commit 938c998cf34985dda6dbe7bc264f46e482f89f8e Author: Your Name Date: Sat Oct 25 00:48:06 2025 +0900 Initial commit: IMG Worker project setup diff --git a/.env b/.env new file mode 100644 index 0000000..d43bde4 --- /dev/null +++ b/.env @@ -0,0 +1,22 @@ +# 큐 대기 한도(가득 차면 429) +IMGWK_MAX_PENDING=400 + +IMGWK_WORKER_READY_TIMEOUT_SEC = 120 # 워커 작시 READY 타임아웃 + +# 워커 롤링 임계치 +IMGWK_ROLL_MAX_RSS_MB=3600 +IMGWK_ROLL_MAX_JOBS=500 +IMGWK_ROLL_MAX_UPTIME_SEC=3600 + +# 잡 타임아웃(초) +IMGWK_JOB_TIMEOUT_SEC=600 + +# 테스트: 2건마다 워커 재시작 (1=활성화) +IMGWK_TEST_ROLL_EVERY_2=0 + +IMGWK_OCR_PROVIDER=auto # auto|dml|cpu +IMGWK_MIGAN_PROVIDER=auto # auto|dml|cpu +IMGWK_REMBG_PROVIDER=auto # auto|dml|cpu + +IMGWK_NO_TRAY = 0 # IMGWK_NO_TRAY=1 설정 시 트레이 비활성화 + diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..3b9f2fa --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +Lib/ +Scripts/ +pyvenv.cfg +include/ +share/ +pyvenv \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..974971a --- /dev/null +++ b/README.md @@ -0,0 +1,77 @@ +## Image Worker API (FastAPI) + +### 개요 +- 이미지 번역 파이프라인을 별도 프로세스로 격리한 로컬 API 서버입니다. +- 단일 워커 프로세스가 순차 처리하며, OCR 이후에는 번역(I/O)과 마스크 생성(CPU)을 동시 실행해 성능을 높입니다. +- 재시작/메모리 이슈를 대비해 워커 롤링(메모리/건수/업타임), 잡 타임아웃, 롤링 중 버퍼링, 429 백프레셔를 제공합니다. + +### 아키텍처 +- FastAPI 서버(가벼움) + 워커 프로세스(무거운 모듈) +- 모듈별 프로바이더 전략(DirectML 우선 → 실패 시 CPU 폴백) 및 성공 프로바이더 캐시 + - OCR: `modules/onnx_ocr_module/src/onnx_ocr_wrapper.py` + - MIGAN: `modules/migan_module.py` + - Rembg(BriaAI): `modules/bria_background_removal_module.py` +- 데이터 전달은 파일 경로 기반(대용량 base64 불사용) +- ProgramData 경로 표준화 + - 작업/임시: `C:\ProgramData\ImgWorker\work` + - 출력: `C:\ProgramData\ImgWorker\outputs` + +### 환경변수(.env) +- IMGWK_MAX_PENDING=200 # 큐 대기 한도(가득 차면 429) +- IMGWK_ROLL_MAX_RSS_MB=1800 # 워커 RSS 임계(MB) +- IMGWK_ROLL_MAX_JOBS=500 # 워커 재시작 처리 건수 임계 +- IMGWK_ROLL_MAX_UPTIME_SEC=7200 # 워커 업타임 임계(초) +- IMGWK_JOB_TIMEOUT_SEC=600 # 잡 타임아웃(초) +- IMGWK_TEST_ROLL_EVERY_2=0 # (테스트) 2건마다 롤링 +- IMGWK_OCR_PROVIDER=auto # auto|dml|cpu (강제 가능) +- IMGWK_REMBG_PROVIDER=auto # rembg용 provider 강제(auto|dml|cpu) +- (선택) 기타 모델 경로/스레드 설정은 토글로 전달 + +### 기본 토글(default) +- Rembg: 자체 로컬 사용(use_local_rembg=True) +- Inpaint: MIGAN을 기본(GPU 시도; DirectML→CPU 폴백) +- OCR provider override: `.env` IMGWK_OCR_PROVIDER(없으면 auto) + +### 프로바이더 선택/폴백/캐시 +- 순서: DirectML 우선 → 실패 시 CPU 폴백 +- 성공한 프로바이더는 캐시 파일에 저장되어 이후 재초기화 시 재검증 생략 + - OCR: `user_data/ocr_provider.json` + - MIGAN: `user_data/migan_provider.json` + - Rembg: `user_data/rembg_provider.json` +- 모델별 강제 설정 + - OCR: 토글 `ocr_provider_override` = auto|dml|cpu + - MIGAN: 토글 `migan_provider_override` = auto|dml|cpu + - Rembg: 환경변수 IMGWK_REMBG_PROVIDER = auto|dml|cpu + +### 엔드포인트 +- GET `/health` + - ready 여부, 워커 PID +- GET `/info` + - 환경/경로/워커 상태 정보 +- POST `/v1/process-image` + - Body: `{ file_path, index, file_prefix?, toggle_overrides?, group_id?, seq? }` + - Return: `{ accepted: true, job_id }` +- POST `/v1/remove-background` + - Body: `{ file_path, file_prefix?, toggle_overrides? }` + - Return: `{ accepted: true, job_id }` +- GET `/v1/jobs/{job_id}` + - 상태: queued|running|done|error|cancelled, 결과/오류 포함 +- DELETE `/v1/jobs/{job_id}` + - queued 상태일 때만 취소 +- 제어 엔드포인트 + - POST `/v1/ocr/reinit` `{ provider?: 'auto'|'dml'|'cpu' }` → OCR 재초기화(프로바이더 반영/캐시) + - POST `/v1/migan/reset` `{ use_cuda?: true|false }` → MIGAN 세션 재설정(DirectML 시도/폴백) + +### 파이프라인(1 이미지) +1) 로드/검증 → 2) OCR → 3) [번역, 마스크] 병렬 → 4) 인페인트(MIGAN 기본) → 5) 렌더링 → 6) 저장 +- 그룹 순서 보장: 클라이언트는 `group_id + seq`로 정렬, 서버는 무순서 완료 허용 + +### 런타임 변경 +- OCR 프로바이더: `/v1/ocr/reinit` 로 dml/cpu/auto 적용 가능(재초기화) +- MIGAN: `/v1/migan/reset` 으로 DirectML 사용 여부 전환 가능 + +### 운영 팁 +- DML 미지원 VM/환경에서는 자동으로 CPU 폴백 +- 큐 가득 차면 429 반환 → 재시도 로직 구현 권장 +- 롤링 임계는 .env 로 조정 +- 로그는 `logs/`에서 롤링 저장, 자세한 타이밍/프로바이더 사용 내역 확인 가능 diff --git a/__pycache__/loggerModule.cpython-311.pyc b/__pycache__/loggerModule.cpython-311.pyc new file mode 100644 index 0000000..96e5a96 Binary files /dev/null and b/__pycache__/loggerModule.cpython-311.pyc differ diff --git a/__pycache__/main.cpython-311.pyc b/__pycache__/main.cpython-311.pyc new file mode 100644 index 0000000..df75758 Binary files /dev/null and b/__pycache__/main.cpython-311.pyc differ diff --git a/build/exe.win-amd64-3.11/ImgWorker.exe b/build/exe.win-amd64-3.11/ImgWorker.exe new file mode 100644 index 0000000..f6e64e5 Binary files /dev/null and b/build/exe.win-amd64-3.11/ImgWorker.exe differ diff --git a/build/exe.win-amd64-3.11/ImgWorker.zip b/build/exe.win-amd64-3.11/ImgWorker.zip new file mode 100644 index 0000000..5f5bedb Binary files /dev/null and b/build/exe.win-amd64-3.11/ImgWorker.zip differ diff --git a/build/exe.win-amd64-3.11/ImgWorkerTray.exe b/build/exe.win-amd64-3.11/ImgWorkerTray.exe new file mode 100644 index 0000000..8cabc26 Binary files /dev/null and b/build/exe.win-amd64-3.11/ImgWorkerTray.exe differ diff --git a/build/exe.win-amd64-3.11/concrt140.dll b/build/exe.win-amd64-3.11/concrt140.dll new file mode 100644 index 0000000..ec4000d Binary files /dev/null and b/build/exe.win-amd64-3.11/concrt140.dll differ diff --git a/build/exe.win-amd64-3.11/frozen_application_license.txt b/build/exe.win-amd64-3.11/frozen_application_license.txt new file mode 100644 index 0000000..0a8ea37 --- /dev/null +++ b/build/exe.win-amd64-3.11/frozen_application_license.txt @@ -0,0 +1,63 @@ +## Why this file is included + +This program has been frozen with cx_Freeze. The freezing process +resulted in certain components from the cx_Freeze software being included +in the frozen application, in particular bootstrap code for launching +the frozen python script. The cx_Freeze software is subject to the +license set out below. + +# Licensing + +- Copyright © 2020-2025, Marcelo Duarte. +- Copyright © 2007-2019, Anthony Tuininga. +- Copyright © 2001-2006, Computronix (Canada) Ltd., Edmonton, Alberta, + Canada. +- All rights reserved. + +NOTE: This license is derived from the Python Software Foundation +License which can be found at + + +## License for cx_Freeze + +1. This LICENSE AGREEMENT is between the copyright holders and the + Individual or Organization ("Licensee") accessing and otherwise + using cx_Freeze software in source or binary form and its associated + documentation. +2. Subject to the terms and conditions of this License Agreement, the + copyright holders hereby grant Licensee a nonexclusive, + royalty-free, world-wide license to reproduce, analyze, test, + perform and/or display publicly, prepare derivative works, + distribute, and otherwise use cx_Freeze alone or in any derivative + version, provided, however, that this License Agreement and this + notice of copyright are retained in cx_Freeze alone or in any + derivative version prepared by Licensee. +3. In the event Licensee prepares a derivative work that is based on or + incorporates cx_Freeze or any part thereof, and wants to make the + derivative work available to others as provided herein, then + Licensee hereby agrees to include in any such work a brief summary + of the changes made to cx_Freeze. +4. The copyright holders are making cx_Freeze available to Licensee on + an "AS IS" basis. THE COPYRIGHT HOLDERS MAKE NO REPRESENTATIONS OR + WARRANTIES, EXPRESS OR IMPLIED. BY WAY OF EXAMPLE, BUT NOT + LIMITATION, THE COPYRIGHT HOLDERS MAKE NO AND DISCLAIM ANY + REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS FOR ANY + PARTICULAR PURPOSE OR THAT THE USE OF CX_FREEZE WILL NOT INFRINGE + ANY THIRD PARTY RIGHTS. +5. THE COPYRIGHT HOLDERS SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER + USERS OF CX_FREEZE FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL + DAMAGES OR LOSS AS A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE + USING CX_FREEZE, OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE + POSSIBILITY THEREOF. +6. This License Agreement will automatically terminate upon a material + breach of its terms and conditions. +7. Nothing in this License Agreement shall be deemed to create any + relationship of agency, partnership, or joint venture between the + copyright holders and Licensee. This License Agreement does not + grant permission to use copyright holder's trademarks or trade name + in a trademark sense to endorse or promote products or services of + Licensee, or any third party. +8. By copying, installing or otherwise using cx_Freeze, Licensee agrees + to be bound by the terms and conditions of this License Agreement. + +Computronix® is a registered trademark of Computronix (Canada) Ltd. diff --git a/build/exe.win-amd64-3.11/msvcp140.dll b/build/exe.win-amd64-3.11/msvcp140.dll new file mode 100644 index 0000000..bea9c37 Binary files /dev/null and b/build/exe.win-amd64-3.11/msvcp140.dll differ diff --git a/build/exe.win-amd64-3.11/msvcp140_1.dll b/build/exe.win-amd64-3.11/msvcp140_1.dll new file mode 100644 index 0000000..e1913ef Binary files /dev/null and b/build/exe.win-amd64-3.11/msvcp140_1.dll differ diff --git a/build/exe.win-amd64-3.11/msvcp140_2.dll b/build/exe.win-amd64-3.11/msvcp140_2.dll new file mode 100644 index 0000000..de91706 Binary files /dev/null and b/build/exe.win-amd64-3.11/msvcp140_2.dll differ diff --git a/build/exe.win-amd64-3.11/msvcp140_atomic_wait.dll b/build/exe.win-amd64-3.11/msvcp140_atomic_wait.dll new file mode 100644 index 0000000..077981c Binary files /dev/null and b/build/exe.win-amd64-3.11/msvcp140_atomic_wait.dll differ diff --git a/build/exe.win-amd64-3.11/msvcp140_codecvt_ids.dll b/build/exe.win-amd64-3.11/msvcp140_codecvt_ids.dll new file mode 100644 index 0000000..6492087 Binary files /dev/null and b/build/exe.win-amd64-3.11/msvcp140_codecvt_ids.dll differ diff --git a/build/exe.win-amd64-3.11/python3.dll b/build/exe.win-amd64-3.11/python3.dll new file mode 100644 index 0000000..4697e72 Binary files /dev/null and b/build/exe.win-amd64-3.11/python3.dll differ diff --git a/build/exe.win-amd64-3.11/python311.dll b/build/exe.win-amd64-3.11/python311.dll new file mode 100644 index 0000000..701d943 Binary files /dev/null and b/build/exe.win-amd64-3.11/python311.dll differ diff --git a/build/exe.win-amd64-3.11/vcamp140.dll b/build/exe.win-amd64-3.11/vcamp140.dll new file mode 100644 index 0000000..6cbca35 Binary files /dev/null and b/build/exe.win-amd64-3.11/vcamp140.dll differ diff --git a/build/exe.win-amd64-3.11/vccorlib140.dll b/build/exe.win-amd64-3.11/vccorlib140.dll new file mode 100644 index 0000000..9f72ffc Binary files /dev/null and b/build/exe.win-amd64-3.11/vccorlib140.dll differ diff --git a/build/exe.win-amd64-3.11/vcomp140.dll b/build/exe.win-amd64-3.11/vcomp140.dll new file mode 100644 index 0000000..dc2d193 Binary files /dev/null and b/build/exe.win-amd64-3.11/vcomp140.dll differ diff --git a/build/exe.win-amd64-3.11/vcruntime140.dll b/build/exe.win-amd64-3.11/vcruntime140.dll new file mode 100644 index 0000000..5786e93 Binary files /dev/null and b/build/exe.win-amd64-3.11/vcruntime140.dll differ diff --git a/build/exe.win-amd64-3.11/vcruntime140_1.dll b/build/exe.win-amd64-3.11/vcruntime140_1.dll new file mode 100644 index 0000000..0b660f9 Binary files /dev/null and b/build/exe.win-amd64-3.11/vcruntime140_1.dll differ diff --git a/build/exe.win-amd64-3.11/vcruntime140_threads.dll b/build/exe.win-amd64-3.11/vcruntime140_threads.dll new file mode 100644 index 0000000..36323c1 Binary files /dev/null and b/build/exe.win-amd64-3.11/vcruntime140_threads.dll differ diff --git a/libs/cx_Logging.lib b/libs/cx_Logging.lib new file mode 100644 index 0000000..2792f92 Binary files /dev/null and b/libs/cx_Logging.lib differ diff --git a/loggerModule.py b/loggerModule.py new file mode 100644 index 0000000..be40aee --- /dev/null +++ b/loggerModule.py @@ -0,0 +1,393 @@ +""" +서버/CLI 친화 Logger 모듈 - 파일/콘솔 분리 로깅과 자동 정리 + +권장 로그 레벨: +- 개발: logging.DEBUG +- 운영: logging.INFO 또는 logging.WARNING + +사용 예시: +```python +# 기본 사용 +logger = Logger( + log_file="logs/app.log", + file_log_level=logging.DEBUG +) + +logger.info("서버 시작") + +# FastAPI에서 사용 (예) +# from fastapi import FastAPI +# app = FastAPI() +# log = Logger(log_file="logs/api.log") +# @app.get("/") +# def root(): +# log.info("요청 수신") +# return {"ok": True} +``` +""" +import re +import logging +from logging.handlers import TimedRotatingFileHandler +import os +import time +import threading +from datetime import datetime, timedelta +from pathlib import Path +import traceback + +# 로그 레벨 이름 매핑 (안전하고 호환성 좋은 방법) +def get_level_name(level): + """로그 레벨을 이름으로 변환 (호환성 보장)""" + level_names = { + logging.NOTSET: "NOTSET", + logging.DEBUG: "DEBUG", + logging.INFO: "INFO", + logging.WARNING: "WARNING", + logging.ERROR: "ERROR", + logging.CRITICAL: "CRITICAL" + } + return level_names.get(level, f"Level {level}") + +class Logger: + + def __init__(self, gui_logger=None, log_file="Edit_PartTimer_log.log", logger_name="Edit_PartTimer_log", + file_log_level=logging.DEBUG, gui_log_level=logging.INFO, + max_days=3, cleanup_interval=3600): + """ + 개선된 Logger 초기화 (서버/CLI용) + :param gui_logger: 선택적 콜백 함수(메시지 수신용) + :param log_file: 로그 파일 이름 + :param logger_name: 로거 이름 + :param file_log_level: 파일 로거의 로그 레벨 + :param gui_log_level: 콜백 호출 로그 레벨(기본 INFO) + :param max_days: 로그 파일 보관 일수 (기본 3일) + :param cleanup_interval: 정리 작업 간격(초, 기본 1시간) + """ + self.gui_logger = gui_logger + self.file_log_level = file_log_level + self.gui_log_level = gui_log_level + self.max_days = max_days + self.cleanup_interval = cleanup_interval + self.log_dir = Path(log_file).parent + self.log_base_name = Path(log_file).stem + + # 로그 디렉토리 생성 + self.log_dir.mkdir(parents=True, exist_ok=True) + + # 로그 설정 + self.logger = logging.getLogger(logger_name) + self.logger.setLevel(file_log_level) + # 상위 로거로의 전파 방지(중복 출력 방지) + self.logger.propagate = False + + # 기존 핸들러 제거 (중복 방지) + for handler in self.logger.handlers[:]: + self.logger.removeHandler(handler) + + # 포맷 설정 + self.simple_format = "[%(asctime)s] [%(levelname)s] %(message)s" + self.detailed_format = ( + "[%(asctime)s] [%(threadName)s] [%(levelname)s] " + "[%(filename)s:%(funcName)s:%(lineno)d] %(message)s" + ) + # 콜백 전용 간결한 포맷 (시간:분:초 + 메시지만) + # 필요 시 log()에서 Formatter를 생성하여 사용 + + # 핸들러 추가 + self._add_console_handler(file_log_level) + self._add_file_handler(log_file, file_log_level) + + # 자동 정리 스레드 시작 + self._start_cleanup_thread() + + def _add_console_handler(self, level): + """콘솔 핸들러 추가""" + console_handler = logging.StreamHandler() + console_handler.setLevel(level) + formatter = logging.Formatter( + self.detailed_format if level <= logging.DEBUG else self.simple_format + ) + console_handler.setFormatter(formatter) + self.logger.addHandler(console_handler) + + def _add_file_handler(self, log_file, level): + """파일 크기 기반 로테이팅 + 수명 관리""" + from logging.handlers import RotatingFileHandler + # 확장자가 .log가 아니면 .log로 변경 + if not log_file.endswith('.log'): + base_name, _ = os.path.splitext(log_file) + log_file = base_name + '.log' + + # 크기 기반 로테이션: 10MB, 최대 50개(정리 스레드가 3일/일5개로 실제 보존 제한) + file_handler = RotatingFileHandler( + log_file, + maxBytes=10 * 1024 * 1024, + backupCount=50, + encoding="utf-8", + ) + + file_handler.setLevel(level) + formatter = logging.Formatter( + self.detailed_format if level <= logging.DEBUG else self.simple_format + ) + file_handler.setFormatter(formatter) + self.logger.addHandler(file_handler) + + # 핸들러 참조 저장 (정리 작업용) + self.file_handler = file_handler + + def _start_cleanup_thread(self): + """자동 정리 스레드 시작""" + def cleanup_worker(): + while True: + try: + self._cleanup_old_logs() + time.sleep(self.cleanup_interval) + except Exception as e: + # 정리 작업 실패 시에도 계속 동작 + pass + + cleanup_thread = threading.Thread(target=cleanup_worker, daemon=True) + cleanup_thread.start() + + def _cleanup_old_logs(self): + """오래된 로그(> max_days) 및 날짜별 최대 10개 초과분 정리""" + cutoff_date = datetime.now() - timedelta(days=self.max_days) + log_pattern = f"{self.log_base_name}*.log*" # .log, .log.1 등 모두 포함 + + files = [] + for log_file in self.log_dir.glob(log_pattern): + try: + if log_file.name == f"{self.log_base_name}.log": + continue + file_mtime = datetime.fromtimestamp(log_file.stat().st_mtime) + files.append((log_file, file_mtime)) + except Exception: + continue + + # 1) 보존 기간 초과 파일 삭제 + for log_file, mtime in files: + if mtime < cutoff_date: + try: + log_file.unlink() + except Exception: + pass + + # 2) 날짜별 최대 5개 유지 (최신순 보존) + from collections import defaultdict + by_day = defaultdict(list) + for log_file, mtime in files: + day_key = mtime.strftime('%Y-%m-%d') + by_day[day_key].append((log_file, mtime)) + + for day_key, items in by_day.items(): + # 최신순으로 정렬 + items.sort(key=lambda x: x[1], reverse=True) + for (log_file, _mtime) in items[5:]: + try: + if log_file.exists(): + log_file.unlink() + except Exception: + pass + + def log(self, message, level=logging.INFO, exc_info=False): + """로그 메시지 기록""" + if exc_info: + message = f"{message}\n{traceback.format_exc()}" + + # 호출 위치 정보를 동적으로 추출 + caller_frame = logging.currentframe().f_back + record = self.logger.makeRecord( + self.logger.name, level, caller_frame.f_code.co_filename, + caller_frame.f_lineno, message, None, None, caller_frame.f_code.co_name + ) + + # 파일/콘솔 핸들러에 메시지 전달 + if level >= self.file_log_level: + self.logger.handle(record) + + # 선택적 콜백으로 전달 (간결 포맷 적용) + if self.gui_logger and level >= self.gui_log_level: + gui_formatter = logging.Formatter( + fmt="[%(asctime)s] %(message)s", + datefmt="%H:%M:%S" + ) + formatted_message = gui_formatter.format(record) + try: + self.gui_logger(formatted_message) + except Exception: + # 콜백 오류는 로깅 실패로 간주하지 않음 + pass + + def set_gui_logger(self, gui_logger, gui_log_level=None): + """ + 로그 콜백 함수를 설정합니다. + 선택적으로 콜백 로그 레벨도 변경할 수 있습니다. + """ + self.gui_logger = gui_logger + + if gui_log_level is not None: + self.gui_log_level = gui_log_level + + def set_gui_log_level(self, level): + """콜백 로그 레벨을 동적으로 변경합니다""" + self.gui_log_level = level + level_name = get_level_name(level) + self.logger.info(f"콜백 로그 레벨이 {level_name}로 변경되었습니다") + + def set_file_log_level(self, level): + """파일 로그 레벨을 동적으로 변경합니다""" + self.file_log_level = level + self.logger.setLevel(level) + for handler in self.logger.handlers: + if isinstance(handler, (logging.FileHandler, TimedRotatingFileHandler)): + handler.setLevel(level) + level_name = get_level_name(level) + self.logger.info(f"파일 로그 레벨이 {level_name}로 변경되었습니다") + + def get_log_levels(self): + """현재 로그 레벨 정보 반환""" + return { + "file_level": get_level_name(self.file_log_level), + "gui_level": get_level_name(self.gui_log_level), + "logger_level": get_level_name(self.logger.level) + } + + def get_log_info(self): + """로그 파일 정보 반환""" + try: + log_files = list(self.log_dir.glob(f"{self.log_base_name}*.log")) + total_size = sum(f.stat().st_size for f in log_files) + + return { + "log_dir": str(self.log_dir), + "total_files": len(log_files), + "total_size_mb": total_size / (1024 * 1024), + "max_days": self.max_days, + "files": [f.name for f in log_files] + } + except Exception: + return {"error": "로그 정보 조회 실패"} + + def force_cleanup(self): + """수동 정리 실행""" + self._cleanup_old_logs() + + # 파이썬 표준 로깅 인터페이스 지원 + def debug(self, message, *args, **kwargs): + """DEBUG 레벨 로그""" + if args: + message = message % args + self.log(message, level=logging.DEBUG, exc_info=kwargs.get('exc_info', False)) + + def info(self, message, *args, **kwargs): + """INFO 레벨 로그""" + if args: + message = message % args + self.log(message, level=logging.INFO, exc_info=kwargs.get('exc_info', False)) + + def warning(self, message, *args, **kwargs): + """WARNING 레벨 로그""" + if args: + message = message % args + self.log(message, level=logging.WARNING, exc_info=kwargs.get('exc_info', False)) + + def error(self, message, *args, **kwargs): + """ERROR 레벨 로그""" + if args: + message = message % args + self.log(message, level=logging.ERROR, exc_info=kwargs.get('exc_info', False)) + + def critical(self, message, *args, **kwargs): + """CRITICAL 레벨 로그""" + if args: + message = message % args + self.log(message, level=logging.CRITICAL, exc_info=kwargs.get('exc_info', False)) + + def exception(self, message, *args, **kwargs): + """ERROR 레벨로 예외 정보와 함께 로그""" + if args: + message = message % args + self.log(message, level=logging.ERROR, exc_info=True) + +# 전역 기본 로거 인스턴스 +_default_logger = None + +def get_default_logger(): + """기본 로거 인스턴스 반환""" + global _default_logger + if _default_logger is None: + _default_logger = Logger(log_file="logs/default.log") + return _default_logger + +def set_default_logger(logger): + """기본 로거 설정""" + global _default_logger + _default_logger = logger + +# 편의 함수들 - 실제 구현 +def debug(msg, *args, **kwargs): + """편의 함수 - DEBUG 레벨 로그""" + get_default_logger().debug(msg, *args, **kwargs) + +def info(msg, *args, **kwargs): + """편의 함수 - INFO 레벨 로그""" + get_default_logger().info(msg, *args, **kwargs) + +def warning(msg, *args, **kwargs): + """편의 함수 - WARNING 레벨 로그""" + get_default_logger().warning(msg, *args, **kwargs) + +def error(msg, *args, **kwargs): + """편의 함수 - ERROR 레벨 로그""" + get_default_logger().error(msg, *args, **kwargs) + +def critical(msg, *args, **kwargs): + """편의 함수 - CRITICAL 레벨 로그""" + get_default_logger().critical(msg, *args, **kwargs) + +def exception(msg, *args, **kwargs): + """편의 함수 - 예외 정보와 함께 로그""" + get_default_logger().exception(msg, *args, **kwargs) + + +# 구조화된 로깅을 위한 추가 클래스 +class StructuredLogger(Logger): + """JSON 형태의 구조화된 로깅을 지원하는 로거""" + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + + def log_structured(self, event, level=logging.INFO, **context): + """구조화된 로그 기록""" + import json + + log_data = { + "timestamp": datetime.now().isoformat(), + "event": event, + "level": get_level_name(level), + **context + } + + message = json.dumps(log_data, ensure_ascii=False, separators=(',', ':')) + self.log(message, level) + + def log_performance(self, operation, duration, **context): + """성능 로그 기록""" + self.log_structured( + "performance", + level=logging.INFO, + operation=operation, + duration_ms=round(duration * 1000, 2), + **context + ) + + def log_error_with_context(self, error, **context): + """에러와 컨텍스트를 함께 로그""" + self.log_structured( + "error", + level=logging.ERROR, + error_type=type(error).__name__, + error_message=str(error), + **context + ) diff --git a/logs/api_server.log b/logs/api_server.log new file mode 100644 index 0000000..e29e746 --- /dev/null +++ b/logs/api_server.log @@ -0,0 +1,9379 @@ +[2025-10-24 00:04:56,044] [MainThread] [INFO] [loggerModule.py:info:281] API 서버 시작 +[2025-10-24 00:04:56,050] [MainThread] [INFO] [loggerModule.py:info:281] 워커 프로세스 기동: PID=10032 +[2025-10-24 00:04:56,496] [LogListener] [DEBUG] [loggerModule.py:debug:275] ImageWorker 프로세스 기동 (PID=10032, Name=ImageWorkerProcess) +[2025-10-24 00:04:56,497] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔧 ImageProcessor3 초기화 시작... +[2025-10-24 00:04:56,497] [LogListener] [DEBUG] [loggerModule.py:debug:275] tracemalloc 메모리 추적 시작 +[2025-10-24 00:04:56,504] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ DirectML 환경 준비 완료 (Windows DirectX 12 기반) +[2025-10-24 00:04:56,504] [LogListener] [DEBUG] [loggerModule.py:debug:275] === 🚀 DirectML GPU 상태 초기화 시작 🚀 === +[2025-10-24 00:04:56,504] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🎯 사용자 GPU 가속 요청: False +[2025-10-24 00:04:56,504] [LogListener] [DEBUG] [loggerModule.py:debug:275] 💻 현재 운영체제: Windows +[2025-10-24 00:04:56,505] [LogListener] [DEBUG] [loggerModule.py:debug:275] GPU 가속이 비활성화됨 (toggle_states['use_cuda'] = False) +[2025-10-24 00:04:56,505] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔒 안전한 CPU 모드로 모든 GPU 설정 강제 비활성화 +[2025-10-24 00:04:56,505] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔧 ImageProcessor3 GPU 상태 요약: +[2025-10-24 00:04:56,505] [LogListener] [DEBUG] [loggerModule.py:debug:275] - CUDA 사용 가능: False +[2025-10-24 00:04:56,505] [LogListener] [DEBUG] [loggerModule.py:debug:275] - toggle_states['use_cuda']: NOT_SET +[2025-10-24 00:04:56,505] [LogListener] [DEBUG] [loggerModule.py:debug:275] - GPU 하드웨어 정보: {} +[2025-10-24 00:04:56,505] [LogListener] [DEBUG] [loggerModule.py:debug:275] ImageProcessor3 Init toggle_states: {'TEMP_IMAGE_DIR': 'C:\\ProgramData\\ImgWorker\\work', 'output_image_format': 'webp', 'use_local_rembg': True, 'local_rembg_model_path': 'D:\\py\\img_worker\\modules\\rembg_models', 'local_model_name': 'birefnet-general-lite', 'optionIMGTrans_type': 'CPU', 'detail_IMGTrans_type': 'CPU', 'thumb_trans_type': 'CPU', 'migan_use_accel': True, 'migan_onnx_path': 'D:\\py\\img_worker\\modules\\migan_onnx\\migan_pipeline_v2.onnx', 'ocr_provider_override': 'auto', 'migan_provider_override': 'auto'} +[2025-10-24 00:04:56,505] [LogListener] [DEBUG] [loggerModule.py:debug:275] is_member_valid: False +[2025-10-24 00:04:56,505] [LogListener] [DEBUG] [loggerModule.py:debug:275] debug_images 디렉토리 이미 존재: D:\py\img_worker\modules\debug_images +[2025-10-24 00:04:56,506] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.font_path: D:\py\img_worker\modules\HakgyoansimDunggeunmisoTTFB.ttf +[2025-10-24 00:04:56,506] [LogListener] [DEBUG] [loggerModule.py:debug:275] toggle_states font_path: +[2025-10-24 00:04:56,506] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.TEMP_IMAGE_DIR: C:\ProgramData\ImgWorker\work +[2025-10-24 00:04:56,506] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.debugging_save_Dir: D:\py\img_worker\modules\debug_images +[2025-10-24 00:04:56,506] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.unwanted_texts: {} +[2025-10-24 00:04:56,506] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.inpaint_method: migan +[2025-10-24 00:04:56,506] [LogListener] [DEBUG] [loggerModule.py:debug:275] Image.MAX_IMAGE_PIXELS set to 20000000 +[2025-10-24 00:04:56,506] [LogListener] [DEBUG] [loggerModule.py:debug:275] ONNX 모델 타입 설정값: 자동 선택, GPU 정보: {} +[2025-10-24 00:04:56,506] [LogListener] [DEBUG] [loggerModule.py:debug:275] 자동 선택 모드: GPU 추천 모델 simp 사용 +[2025-10-24 00:04:56,506] [LogListener] [DEBUG] [loggerModule.py:debug:275] ONNX OCR 모델 타입 결정: simp (GPU: False) +[2025-10-24 00:04:56,506] [LogListener] [DEBUG] [loggerModule.py:debug:275] ONNX OCR 모듈 CPU 모드로 설정 +[2025-10-24 00:04:56,506] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🚀 ONNX TextSystem 초기화 시작 (CPU 모드) +[2025-10-24 00:04:56,506] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🚀 ONNX TextSystem 초기화 시작 (CPU 모드) +[2025-10-24 00:04:58,248] [LogListener] [DEBUG] [loggerModule.py:debug:275] 문자 사전 파일 UTF-8 확인 완료: D:\py\img_worker\modules\onnx_ocr_module\dict\ppocr_keys_v1.txt +[2025-10-24 00:04:58,334] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ ONNX TextSystem 초기화 완료 (CPU + SIMP (호환성) 모델) +[2025-10-24 00:04:58,335] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ ONNX TextSystem 초기화 완료 (CPU + SIMP 모델) +[2025-10-24 00:04:58,335] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ ONNX OCR 모듈 초기화 성공 (CPU 모드) +[2025-10-24 00:04:58,335] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ ONNX OCR 모듈 초기화 성공 +[2025-10-24 00:04:58,335] [LogListener] [DEBUG] [loggerModule.py:debug:275] 마스크 모듈 초기화 완료 +[2025-10-24 00:04:58,335] [LogListener] [DEBUG] [loggerModule.py:debug:275] MaskModule 초기화 성공 +[2025-10-24 00:04:58,335] [LogListener] [DEBUG] [loggerModule.py:debug:275] 텍스트 렌더링 모듈 초기화 완료 +[2025-10-24 00:04:58,336] [LogListener] [DEBUG] [loggerModule.py:debug:275] 기본 폰트: D:\py\img_worker\modules\HakgyoansimDunggeunmisoTTFB.ttf +[2025-10-24 00:04:58,336] [LogListener] [DEBUG] [loggerModule.py:debug:275] TextRenderingModule 초기화 성공 +[2025-10-24 00:04:58,337] [LogListener] [WARNING] [loggerModule.py:warning:287] 커스텀 폰트 로드 실패 (HakgyoansimDunggeunmisoTTFB.ttf): cannot open resource +[2025-10-24 00:04:58,337] [LogListener] [DEBUG] [loggerModule.py:debug:275] 기본 폰트를 사용합니다. +[2025-10-24 00:04:58,337] [LogListener] [DEBUG] [loggerModule.py:debug:275] PostImageManager 초기화 성공 +[2025-10-24 00:04:58,337] [LogListener] [DEBUG] [loggerModule.py:debug:275] local_rembg_model_path: D:\py\img_worker\modules\rembg_models +[2025-10-24 00:04:58,337] [LogListener] [DEBUG] [loggerModule.py:debug:275] Request_AI_Server GPU 상태: CUDA 사용 가능=False +[2025-10-24 00:04:58,337] [LogListener] [DEBUG] [loggerModule.py:debug:275] Request_AI_Server 초기화 성공 +[2025-10-24 00:04:58,337] [LogListener] [DEBUG] [loggerModule.py:debug:275] GoogleTranslate 초기화 성공 +[2025-10-24 00:04:58,338] [LogListener] [DEBUG] [loggerModule.py:debug:275] MIGAN CUDA 사용 불가 - CPU 모드로 설정 +[2025-10-24 00:04:58,338] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] GPU 관리자 전달: GPUManager, can_use_cuda: False +[2025-10-24 00:04:58,358] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] GPU 관리자 사용 가능: False +[2025-10-24 00:04:58,571] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 사용 가능한 providers: ['DmlExecutionProvider', 'CPUExecutionProvider'] +[2025-10-24 00:04:58,572] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] DirectML 가속 활성화 +[2025-10-24 00:04:58,572] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 최종 providers: [('DmlExecutionProvider', {}), ('CPUExecutionProvider', {})] +[2025-10-24 00:04:58,572] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] DirectML 가속 시도: [('DmlExecutionProvider', {}), ('CPUExecutionProvider', {})] +[2025-10-24 00:04:58,572] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] DirectML 가속 성공! 실제 providers: ['DmlExecutionProvider', 'CPUExecutionProvider'] +[2025-10-24 00:04:58,572] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 입력 0: image, 형태: ['batch_size', 3, 'height', 'width'], 타입: tensor(uint8) +[2025-10-24 00:04:58,572] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 입력 1: mask, 형태: ['batch_size', 1, 'height', 'width'], 타입: tensor(uint8) +[2025-10-24 00:04:58,573] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 출력 0: result, 형태: ['ScatterNDresult_dim_0', 3, 'ScatterNDresult_dim_2', 'ScatterNDresult_dim_3'], 타입: tensor(uint8) +[2025-10-24 00:04:58,573] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 세션 준비 완료. providers=['DmlExecutionProvider', 'CPUExecutionProvider'] +[2025-10-24 00:04:58,573] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] GPU 관리자 연결 완료: GPUManager +[2025-10-24 00:04:58,573] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 파이프라인 gpu_manager 속성: True, 값: +[2025-10-24 00:04:58,573] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 초기화 완료: gpu_manager 속성=True, 값= +[2025-10-24 00:04:58,573] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔰 OCR 모듈 Warm-up 시작... +[2025-10-24 00:04:58,573] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔍 ONNX OCR 감지 방식: polygon +[2025-10-24 00:04:58,577] [LogListener] [DEBUG] [loggerModule.py:debug:275] ⚡ ONNX OCR 추론 완료: 4.0ms +[2025-10-24 00:04:58,578] [LogListener] [DEBUG] [loggerModule.py:debug:275] 📊 세부 시간 - 감지: 3.0ms, 인식: 0.0ms, 분류: 0.0ms +[2025-10-24 00:04:58,578] [LogListener] [WARNING] [loggerModule.py:warning:287] ⚠️ ONNX OCR 결과가 비어있습니다 +[2025-10-24 00:04:58,581] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ OCR 모듈 Warm-up 성공 +[2025-10-24 00:04:58,581] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔰 ImageProcessor Warm‑up 완료 +[2025-10-24 00:04:58,581] [ResultListener] [INFO] [loggerModule.py:info:281] 워커 READY 수신 +[2025-10-24 00:04:58,581] [ResultListener] [INFO] [loggerModule.py:info:281] 워커 READY 수신 +[2025-10-24 00:04:58,582] [LogListener] [DEBUG] [loggerModule.py:debug:275] 📡 추가 READY 신호 전송 완료 +[2025-10-24 00:04:58,582] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 00:04:58,582] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 10032) +[2025-10-24 00:04:58,582] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔥 작업 수신 성공 +[2025-10-24 00:04:58,582] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🚀 작업 처리 시작: cmd=__PING__, uid=14975980-efb4-4794-87c3-549f3a20e61a +[2025-10-24 00:04:58,582] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 실행 직전: cmd=__PING__ +[2025-10-24 00:04:58,582] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 결과 반환 중: uid=14975980-efb4-4794-87c3-549f3a20e61a +[2025-10-24 00:04:58,583] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 결과 반환 완료: uid=14975980-efb4-4794-87c3-549f3a20e61a +[2025-10-24 00:04:58,583] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 00:04:58,583] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 10032) +[2025-10-24 00:05:58,587] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 00:05:58,587] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 00:05:58,587] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 10032) +[2025-10-24 00:06:58,601] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 00:06:58,601] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 00:06:58,601] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 10032) +[2025-10-24 00:07:58,602] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 00:07:58,602] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 00:07:58,602] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 10032) +[2025-10-24 00:08:53,220] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔥 작업 수신 성공 +[2025-10-24 00:08:53,220] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🚀 작업 처리 시작: cmd=process_single_image, uid=a5bb72f2-d0b6-4e05-b4a2-f28ea5f5bee6 +[2025-10-24 00:08:53,220] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 프로세서 toggle_states 업데이트 : {'TEMP_IMAGE_DIR': 'C:\\ProgramData\\ImgWorker\\work', 'output_image_format': 'webp', 'use_local_rembg': True, 'local_rembg_model_path': 'D:\\py\\img_worker\\modules\\rembg_models', 'local_model_name': 'birefnet-general-lite', 'optionIMGTrans_type': 'GPU', 'detail_IMGTrans_type': 'GPU', 'thumb_trans_type': 'GPU', 'migan_use_accel': True, 'migan_onnx_path': 'D:\\py\\img_worker\\modules\\migan_onnx\\migan_pipeline_v2.onnx', 'ocr_provider_override': 'auto', 'migan_provider_override': 'auto'} +[2025-10-24 00:08:53,220] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 실행 직전: cmd=process_single_image +[2025-10-24 00:08:53,221] [LogListener] [DEBUG] [loggerModule.py:debug:275] process_single_image 호출 직전 +[2025-10-24 00:08:53,223] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 번역시작 +[2025-10-24 00:08:53,704] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 2 처리 시작: D:\py\img_worker\modules\test\1.jpg - 전체 번역 모드 +[2025-10-24 00:08:53,704] [LogListener] [DEBUG] [loggerModule.py:debug:275] 로컬 파일 경로 감지, 다운로드 생략: D:\py\img_worker\modules\test\1.jpg +[2025-10-24 00:08:53,706] [LogListener] [DEBUG] [loggerModule.py:debug:275] 상세페이지 이미지 2 원본 크기: 816x1200 +[2025-10-24 00:08:53,711] [LogListener] [DEBUG] [loggerModule.py:debug:275] 상세페이지 이미지 2 가로 크기 조정: 816x1200 → 860x1264 +[2025-10-24 00:08:53,719] [LogListener] [DEBUG] [loggerModule.py:debug:275] 상세페이지 이미지 2 크기 조정 완료: 860x1264 +[2025-10-24 00:08:53,719] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 2 로컬 저장위치(상세페이지 전처리 완료): D:\py\img_worker\modules\test\1_resized.jpg +[2025-10-24 00:08:53,722] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔍 ONNX OCR 감지 방식: polygon +[2025-10-24 00:08:53,886] [LogListener] [DEBUG] [loggerModule.py:debug:275] ⚡ ONNX OCR 추론 완료: 163.4ms +[2025-10-24 00:08:53,886] [LogListener] [DEBUG] [loggerModule.py:debug:275] 📊 세부 시간 - 감지: 85.0ms, 인식: 71.4ms, 분류: 4.0ms +[2025-10-24 00:08:53,907] [LogListener] [DEBUG] [loggerModule.py:debug:275] 메모리 변화 [OCR 처리]: 31931.9MB -> 32090.0MB (+158.0MB, +0.5%) - 이미지 2 +[2025-10-24 00:08:53,920] [LogListener] [DEBUG] [loggerModule.py:debug:275] ocr_results: [{'text': '现代极简风格', 'confidence': 0.9960710406303406, 'polygon': [[232.0, 72.0], [581.0, 72.0], [581.0, 122.0], [232.0, 122.0]], 'bbox': (232, 72, 350, 51), 'method': 'polygon'}, {'text': '更易搭配各种使用场景', 'confidence': 0.9952024221420288, 'polygon': [[160.0, 149.0], [656.0, 149.0], [656.0, 190.0], [160.0, 190.0]], 'bbox': (160, 149, 497, 42), 'method': 'polygon'}, {'text': '半圆两端设计', 'confidence': 0.9976122379302979, 'polygon': [[93.0, 304.0], [488.0, 304.0], [488.0, 363.0], [93.0, 363.0]], 'bbox': (93, 304, 396, 60), 'method': 'polygon'}, {'text': '承载各种欢乐', 'confidence': 0.9930798411369324, 'polygon': [[91.0, 388.0], [486.0, 388.0], [486.0, 448.0], [91.0, 448.0]], 'bbox': (91, 388, 396, 61), 'method': 'polygon'}] +[2025-10-24 00:08:53,920] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 2 OCR raw 데이터 메모리 저장 완료: 4개 텍스트 +[2025-10-24 00:08:53,920] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.6%): '现代极简风格' +[2025-10-24 00:08:53,920] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.5%): '更易搭配各种使用场景' +[2025-10-24 00:08:53,920] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.8%): '半圆两端设计' +[2025-10-24 00:08:53,921] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.3%): '承载各种欢乐' +[2025-10-24 00:08:53,921] [LogListener] [DEBUG] [loggerModule.py:debug:275] 필터링 결과: 4/4개 (신뢰도 + & 중국어) +[2025-10-24 00:08:53,921] [LogListener] [DEBUG] [loggerModule.py:debug:275] filter_ocr_results: [{'text': '现代极简风格', 'confidence': 0.9960710406303406, 'polygon': [[232.0, 72.0], [581.0, 72.0], [581.0, 122.0], [232.0, 122.0]], 'bbox': (232, 72, 350, 51), 'method': 'polygon'}, {'text': '更易搭配各种使用场景', 'confidence': 0.9952024221420288, 'polygon': [[160.0, 149.0], [656.0, 149.0], [656.0, 190.0], [160.0, 190.0]], 'bbox': (160, 149, 497, 42), 'method': 'polygon'}, {'text': '半圆两端设计', 'confidence': 0.9976122379302979, 'polygon': [[93.0, 304.0], [488.0, 304.0], [488.0, 363.0], [93.0, 363.0]], 'bbox': (93, 304, 396, 60), 'method': 'polygon'}, {'text': '承载各种欢乐', 'confidence': 0.9930798411369324, 'polygon': [[91.0, 388.0], [486.0, 388.0], [486.0, 448.0], [91.0, 448.0]], 'bbox': (91, 388, 396, 61), 'method': 'polygon'}] +[2025-10-24 00:08:53,921] [LogListener] [DEBUG] [loggerModule.py:debug:275] 중국어 텍스트 4개 필터링 완료 +[2025-10-24 00:08:53,921] [LogListener] [DEBUG] [loggerModule.py:debug:275] 한글 텍스트 0개 필터링 완료 +[2025-10-24 00:08:56,496] [LogListener] [DEBUG] [loggerModule.py:debug:275] translated_texts: ['모던한 미니멀 스타일', '다양한 사용 시나리오에 더 쉽게 일치', '반원형 끝 디자인', '온갖 기쁨을 안고'] +[2025-10-24 00:08:56,496] [LogListener] [DEBUG] [loggerModule.py:debug:275] 마스크 생성 완료 +[2025-10-24 00:08:56,496] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 2 OCR 권한 없음, 전체 번역 모드 +[2025-10-24 00:08:56,503] [LogListener] [DEBUG] [loggerModule.py:debug:275] [AUTO Inpaint] coverage=0.136, comps=2, min_center_dist=0.174 → request +[2025-10-24 00:08:56,503] [LogListener] [DEBUG] [loggerModule.py:debug:275] ocr_count: 4 +[2025-10-24 00:08:56,503] [LogListener] [DEBUG] [loggerModule.py:debug:275] is_member_valid: False +[2025-10-24 00:08:56,503] [LogListener] [DEBUG] [loggerModule.py:debug:275] [set_inpaint_method] prefix=detail, target_key=detail_IMGTrans_type, trans_type=GPU → inpaint_method=migan +[2025-10-24 00:08:56,504] [LogListener] [DEBUG] [loggerModule.py:debug:275] 최종 inpaint_method: migan +[2025-10-24 00:08:56,504] [LogListener] [DEBUG] [loggerModule.py:debug:275] MIGAN 인페인팅 시도 +[2025-10-24 00:08:56,507] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 입력 형태 - 이미지: (1, 3, 1264, 860), 마스크: (1, 1, 1264, 860) +[2025-10-24 00:08:57,460] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 출력 형태: (1264, 860, 3), dtype: uint8 +[2025-10-24 00:08:57,460] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 추론 완료: 952.95 ms +[2025-10-24 00:08:57,469] [LogListener] [DEBUG] [loggerModule.py:debug:275] MIGAN 인페인팅 성공 +[2025-10-24 00:08:57,469] [LogListener] [DEBUG] [loggerModule.py:debug:275] 메모리 변화 [인페인팅]: 32119.6MB -> 32342.7MB (+223.1MB, +0.7%) - 방법: migan +[2025-10-24 00:08:57,469] [LogListener] [DEBUG] [loggerModule.py:debug:275] 인페인팅 완료 +[2025-10-24 00:08:57,508] [LogListener] [DEBUG] [loggerModule.py:debug:275] 텍스트 렌더링 완료 +[2025-10-24 00:08:57,509] [LogListener] [DEBUG] [loggerModule.py:debug:275] watermark_text: 이미지 저작권 보유 +[2025-10-24 00:08:57,509] [LogListener] [DEBUG] [loggerModule.py:debug:275] is_watermark_enabled: True +[2025-10-24 00:08:57,796] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 저장 완료 : C:\ProgramData\ImgWorker\work\translated_detail_img_2.webp +[2025-10-24 00:08:57,798] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 2 번역 완료: C:\ProgramData\ImgWorker\work\translated_detail_img_2.webp +[2025-10-24 00:08:57,798] [LogListener] [DEBUG] [loggerModule.py:debug:275] ⏱ 이미지 파이프라인 총 4576.1ms | download=0.0ms | ocr=173.4ms | translate=2576.0ms | mask=2576.0ms | inpaint=972.0ms(migan/DirectML) | render=39.0ms | save=256.0ms +[2025-10-24 00:08:57,799] [LogListener] [DEBUG] [loggerModule.py:debug:275] process_single_image 호출 완료 +[2025-10-24 00:08:57,799] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 결과 반환 중: uid=a5bb72f2-d0b6-4e05-b4a2-f28ea5f5bee6 +[2025-10-24 00:08:57,799] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 결과 반환 완료: uid=a5bb72f2-d0b6-4e05-b4a2-f28ea5f5bee6 +[2025-10-24 00:08:57,799] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 00:08:57,799] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 10032) +[2025-10-24 00:09:57,812] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 00:09:57,812] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 00:09:57,812] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 10032) +[2025-10-24 00:10:57,813] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 00:10:57,813] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 00:10:57,813] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 10032) +[2025-10-24 00:11:57,821] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 00:11:57,821] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 00:11:57,821] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 10032) +[2025-10-24 00:12:57,831] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 00:12:57,831] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 00:12:57,831] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 10032) +[2025-10-24 00:13:57,846] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 00:13:57,846] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 00:13:57,846] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 10032) +[2025-10-24 00:14:57,855] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 00:14:57,855] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 00:14:57,855] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 10032) +[2025-10-24 00:15:57,859] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 00:15:57,859] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 00:15:57,859] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 10032) +[2025-10-24 00:16:57,861] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 00:16:57,861] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 00:16:57,861] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 10032) +[2025-10-24 00:17:57,864] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 00:17:57,864] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 00:17:57,864] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 10032) +[2025-10-24 00:18:57,870] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 00:18:57,870] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 00:18:57,870] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 10032) +[2025-10-24 00:19:57,882] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 00:19:57,882] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 00:19:57,882] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 10032) +[2025-10-24 00:20:57,892] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 00:20:57,892] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 00:20:57,892] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 10032) +[2025-10-24 00:21:57,907] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 00:21:57,907] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 00:21:57,907] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 10032) +[2025-10-24 00:22:23,352] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔥 작업 수신 성공 +[2025-10-24 00:22:23,352] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🚀 작업 처리 시작: cmd=process_single_image, uid=15ff72a7-242b-4fab-94ea-2cd1c22576ee +[2025-10-24 00:22:23,352] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 프로세서 toggle_states 업데이트 : {'TEMP_IMAGE_DIR': 'C:\\ProgramData\\ImgWorker\\work', 'output_image_format': 'webp', 'use_local_rembg': True, 'local_rembg_model_path': 'D:\\py\\img_worker\\modules\\rembg_models', 'local_model_name': 'birefnet-general-lite', 'optionIMGTrans_type': 'GPU', 'detail_IMGTrans_type': 'GPU', 'thumb_trans_type': 'GPU', 'migan_use_accel': True, 'migan_onnx_path': 'D:\\py\\img_worker\\modules\\migan_onnx\\migan_pipeline_v2.onnx', 'ocr_provider_override': 'auto', 'migan_provider_override': 'auto'} +[2025-10-24 00:22:23,353] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 실행 직전: cmd=process_single_image +[2025-10-24 00:22:23,353] [LogListener] [DEBUG] [loggerModule.py:debug:275] process_single_image 호출 직전 +[2025-10-24 00:22:23,353] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 번역시작 +[2025-10-24 00:22:23,588] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 2 처리 시작: D:\py\img_worker\modules\test\1.jpg - 전체 번역 모드 +[2025-10-24 00:22:23,588] [LogListener] [DEBUG] [loggerModule.py:debug:275] 로컬 파일 경로 감지, 다운로드 생략: D:\py\img_worker\modules\test\1.jpg +[2025-10-24 00:22:23,590] [LogListener] [DEBUG] [loggerModule.py:debug:275] 상세페이지 이미지 2 원본 크기: 816x1200 +[2025-10-24 00:22:23,592] [LogListener] [DEBUG] [loggerModule.py:debug:275] 상세페이지 이미지 2 가로 크기 조정: 816x1200 → 860x1264 +[2025-10-24 00:22:23,600] [LogListener] [DEBUG] [loggerModule.py:debug:275] 상세페이지 이미지 2 크기 조정 완료: 860x1264 +[2025-10-24 00:22:23,600] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 2 로컬 저장위치(상세페이지 전처리 완료): D:\py\img_worker\modules\test\1_resized.jpg +[2025-10-24 00:22:23,602] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔍 ONNX OCR 감지 방식: polygon +[2025-10-24 00:22:23,767] [LogListener] [DEBUG] [loggerModule.py:debug:275] ⚡ ONNX OCR 추론 완료: 163.0ms +[2025-10-24 00:22:23,776] [LogListener] [DEBUG] [loggerModule.py:debug:275] 📊 세부 시간 - 감지: 88.0ms, 인식: 68.0ms, 분류: 4.0ms +[2025-10-24 00:22:23,778] [LogListener] [DEBUG] [loggerModule.py:debug:275] 메모리 변화 [OCR 처리]: 35091.9MB -> 35183.8MB (+91.8MB, +0.3%) - 이미지 2 +[2025-10-24 00:22:23,779] [LogListener] [DEBUG] [loggerModule.py:debug:275] ocr_results: [{'text': '现代极简风格', 'confidence': 0.9960710406303406, 'polygon': [[232.0, 72.0], [581.0, 72.0], [581.0, 122.0], [232.0, 122.0]], 'bbox': (232, 72, 350, 51), 'method': 'polygon'}, {'text': '更易搭配各种使用场景', 'confidence': 0.9952024221420288, 'polygon': [[160.0, 149.0], [656.0, 149.0], [656.0, 190.0], [160.0, 190.0]], 'bbox': (160, 149, 497, 42), 'method': 'polygon'}, {'text': '半圆两端设计', 'confidence': 0.9976122379302979, 'polygon': [[93.0, 304.0], [488.0, 304.0], [488.0, 363.0], [93.0, 363.0]], 'bbox': (93, 304, 396, 60), 'method': 'polygon'}, {'text': '承载各种欢乐', 'confidence': 0.9930798411369324, 'polygon': [[91.0, 388.0], [486.0, 388.0], [486.0, 448.0], [91.0, 448.0]], 'bbox': (91, 388, 396, 61), 'method': 'polygon'}] +[2025-10-24 00:22:23,779] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 2 OCR raw 데이터 메모리 저장 완료: 4개 텍스트 +[2025-10-24 00:22:23,779] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.6%): '现代极简风格' +[2025-10-24 00:22:23,779] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.5%): '更易搭配各种使用场景' +[2025-10-24 00:22:23,779] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.8%): '半圆两端设计' +[2025-10-24 00:22:23,780] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.3%): '承载各种欢乐' +[2025-10-24 00:22:23,780] [LogListener] [DEBUG] [loggerModule.py:debug:275] 필터링 결과: 4/4개 (신뢰도 + & 중국어) +[2025-10-24 00:22:23,780] [LogListener] [DEBUG] [loggerModule.py:debug:275] filter_ocr_results: [{'text': '现代极简风格', 'confidence': 0.9960710406303406, 'polygon': [[232.0, 72.0], [581.0, 72.0], [581.0, 122.0], [232.0, 122.0]], 'bbox': (232, 72, 350, 51), 'method': 'polygon'}, {'text': '更易搭配各种使用场景', 'confidence': 0.9952024221420288, 'polygon': [[160.0, 149.0], [656.0, 149.0], [656.0, 190.0], [160.0, 190.0]], 'bbox': (160, 149, 497, 42), 'method': 'polygon'}, {'text': '半圆两端设计', 'confidence': 0.9976122379302979, 'polygon': [[93.0, 304.0], [488.0, 304.0], [488.0, 363.0], [93.0, 363.0]], 'bbox': (93, 304, 396, 60), 'method': 'polygon'}, {'text': '承载各种欢乐', 'confidence': 0.9930798411369324, 'polygon': [[91.0, 388.0], [486.0, 388.0], [486.0, 448.0], [91.0, 448.0]], 'bbox': (91, 388, 396, 61), 'method': 'polygon'}] +[2025-10-24 00:22:23,780] [LogListener] [DEBUG] [loggerModule.py:debug:275] 중국어 텍스트 4개 필터링 완료 +[2025-10-24 00:22:23,780] [LogListener] [DEBUG] [loggerModule.py:debug:275] 한글 텍스트 0개 필터링 완료 +[2025-10-24 00:22:23,786] [LogListener] [DEBUG] [loggerModule.py:debug:275] translated_texts: ['모던한 미니멀 스타일', '다양한 사용 시나리오에 더 쉽게 일치', '반원형 끝 디자인', '온갖 기쁨을 안고'] +[2025-10-24 00:22:23,786] [LogListener] [DEBUG] [loggerModule.py:debug:275] 마스크 생성 완료 +[2025-10-24 00:22:23,786] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 2 OCR 권한 없음, 전체 번역 모드 +[2025-10-24 00:22:23,794] [LogListener] [DEBUG] [loggerModule.py:debug:275] [AUTO Inpaint] coverage=0.136, comps=2, min_center_dist=0.174 → request +[2025-10-24 00:22:23,794] [LogListener] [DEBUG] [loggerModule.py:debug:275] ocr_count: 4 +[2025-10-24 00:22:23,795] [LogListener] [DEBUG] [loggerModule.py:debug:275] is_member_valid: False +[2025-10-24 00:22:23,795] [LogListener] [DEBUG] [loggerModule.py:debug:275] [set_inpaint_method] prefix=detail, target_key=detail_IMGTrans_type, trans_type=GPU → inpaint_method=migan +[2025-10-24 00:22:23,795] [LogListener] [DEBUG] [loggerModule.py:debug:275] 최종 inpaint_method: migan +[2025-10-24 00:22:23,795] [LogListener] [DEBUG] [loggerModule.py:debug:275] MIGAN 인페인팅 시도 +[2025-10-24 00:22:23,799] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 입력 형태 - 이미지: (1, 3, 1264, 860), 마스크: (1, 1, 1264, 860) +[2025-10-24 00:22:24,044] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 출력 형태: (1264, 860, 3), dtype: uint8 +[2025-10-24 00:22:24,044] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 추론 완료: 246.00 ms +[2025-10-24 00:22:24,054] [LogListener] [DEBUG] [loggerModule.py:debug:275] MIGAN 인페인팅 성공 +[2025-10-24 00:22:24,054] [LogListener] [DEBUG] [loggerModule.py:debug:275] 메모리 변화 [인페인팅]: 35182.3MB -> 35185.5MB (+3.3MB, +0.0%) - 방법: migan +[2025-10-24 00:22:24,054] [LogListener] [DEBUG] [loggerModule.py:debug:275] 인페인팅 완료 +[2025-10-24 00:22:24,079] [LogListener] [DEBUG] [loggerModule.py:debug:275] 텍스트 렌더링 완료 +[2025-10-24 00:22:24,080] [LogListener] [DEBUG] [loggerModule.py:debug:275] watermark_text: 이미지 저작권 보유 +[2025-10-24 00:22:24,080] [LogListener] [DEBUG] [loggerModule.py:debug:275] is_watermark_enabled: True +[2025-10-24 00:22:24,394] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 저장 완료 : C:\ProgramData\ImgWorker\work\translated_detail_img_2.webp +[2025-10-24 00:22:24,396] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 2 번역 완료: C:\ProgramData\ImgWorker\work\translated_detail_img_2.webp +[2025-10-24 00:22:24,396] [LogListener] [DEBUG] [loggerModule.py:debug:275] ⏱ 이미지 파이프라인 총 1041.5ms | download=0.0ms | ocr=173.0ms | translate=7.0ms | mask=7.0ms | inpaint=266.0ms(migan/DirectML) | render=25.0ms | save=273.7ms +[2025-10-24 00:22:24,397] [LogListener] [DEBUG] [loggerModule.py:debug:275] process_single_image 호출 완료 +[2025-10-24 00:22:24,397] [ResultListener] [WARNING] [loggerModule.py:warning:287] 워커 롤링 스케줄: reason=job-count-threshold +[2025-10-24 00:22:24,397] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 결과 반환 중: uid=15ff72a7-242b-4fab-94ea-2cd1c22576ee +[2025-10-24 00:22:24,398] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 결과 반환 완료: uid=15ff72a7-242b-4fab-94ea-2cd1c22576ee +[2025-10-24 00:22:24,398] [WorkerRoller] [WARNING] [loggerModule.py:warning:287] 워커 롤링 시작: job-count-threshold +[2025-10-24 00:22:24,398] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 00:22:24,398] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 10032) +[2025-10-24 00:22:24,447] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔥 작업 수신 성공 +[2025-10-24 00:22:24,447] [LogListener] [DEBUG] [loggerModule.py:debug:275] Shutdown signal 수신 → 종료 +[2025-10-24 00:22:24,447] [LogListener] [DEBUG] [loggerModule.py:debug:275] OCR 모듈 정리 완료 +[2025-10-24 00:22:24,447] [LogListener] [DEBUG] [loggerModule.py:debug:275] 마스크 모듈 정리 완료 +[2025-10-24 00:22:24,448] [LogListener] [DEBUG] [loggerModule.py:debug:275] 임시 폴더 삭제됨: C:\ProgramData\ImgWorker\work +[2025-10-24 00:22:24,448] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 프로세서 소멸 +[2025-10-24 00:22:25,591] [WorkerRoller] [INFO] [loggerModule.py:info:281] 워커 프로세스 기동: PID=17692 +[2025-10-24 00:22:26,131] [MainThread] [INFO] [loggerModule.py:info:281] API 서버 종료 +[2025-10-24 00:22:26,367] [MainThread] [INFO] [loggerModule.py:info:281] 워커 프로세스 종료 +[2025-10-24 00:22:28,126] [MainThread] [INFO] [loggerModule.py:info:281] API 서버 시작 +[2025-10-24 00:22:28,132] [MainThread] [INFO] [loggerModule.py:info:281] 워커 프로세스 기동: PID=6808 +[2025-10-24 00:22:28,553] [LogListener] [DEBUG] [loggerModule.py:debug:275] ImageWorker 프로세스 기동 (PID=6808, Name=ImageWorkerProcess) +[2025-10-24 00:22:28,554] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔧 ImageProcessor3 초기화 시작... +[2025-10-24 00:22:28,554] [LogListener] [DEBUG] [loggerModule.py:debug:275] tracemalloc 메모리 추적 시작 +[2025-10-24 00:22:28,560] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ DirectML 환경 준비 완료 (Windows DirectX 12 기반) +[2025-10-24 00:22:28,560] [LogListener] [DEBUG] [loggerModule.py:debug:275] === 🚀 DirectML GPU 상태 초기화 시작 🚀 === +[2025-10-24 00:22:28,560] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🎯 사용자 GPU 가속 요청: False +[2025-10-24 00:22:28,560] [LogListener] [DEBUG] [loggerModule.py:debug:275] 💻 현재 운영체제: Windows +[2025-10-24 00:22:28,560] [LogListener] [DEBUG] [loggerModule.py:debug:275] GPU 가속이 비활성화됨 (toggle_states['use_cuda'] = False) +[2025-10-24 00:22:28,560] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔒 안전한 CPU 모드로 모든 GPU 설정 강제 비활성화 +[2025-10-24 00:22:28,560] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔧 ImageProcessor3 GPU 상태 요약: +[2025-10-24 00:22:28,560] [LogListener] [DEBUG] [loggerModule.py:debug:275] - CUDA 사용 가능: False +[2025-10-24 00:22:28,560] [LogListener] [DEBUG] [loggerModule.py:debug:275] - toggle_states['use_cuda']: NOT_SET +[2025-10-24 00:22:28,561] [LogListener] [DEBUG] [loggerModule.py:debug:275] - GPU 하드웨어 정보: {} +[2025-10-24 00:22:28,561] [LogListener] [DEBUG] [loggerModule.py:debug:275] ImageProcessor3 Init toggle_states: {'TEMP_IMAGE_DIR': 'C:\\ProgramData\\ImgWorker\\work', 'output_image_format': 'webp', 'use_local_rembg': True, 'local_rembg_model_path': 'D:\\py\\img_worker\\modules\\rembg_models', 'local_model_name': 'birefnet-general-lite', 'optionIMGTrans_type': 'CPU', 'detail_IMGTrans_type': 'CPU', 'thumb_trans_type': 'CPU', 'migan_use_accel': True, 'migan_onnx_path': 'D:\\py\\img_worker\\modules\\migan_onnx\\migan_pipeline_v2.onnx', 'ocr_provider_override': 'auto', 'migan_provider_override': 'auto'} +[2025-10-24 00:22:28,561] [LogListener] [DEBUG] [loggerModule.py:debug:275] is_member_valid: False +[2025-10-24 00:22:28,561] [LogListener] [DEBUG] [loggerModule.py:debug:275] debug_images 디렉토리 이미 존재: D:\py\img_worker\modules\debug_images +[2025-10-24 00:22:28,561] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.font_path: D:\py\img_worker\modules\fonts\HakgyoansimDunggeunmisoTTFB.ttf +[2025-10-24 00:22:28,561] [LogListener] [DEBUG] [loggerModule.py:debug:275] toggle_states font_path: D:\py\img_worker\modules\fonts\HakgyoansimDunggeunmisoTTFB.ttf +[2025-10-24 00:22:28,561] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.TEMP_IMAGE_DIR: C:\ProgramData\ImgWorker\work +[2025-10-24 00:22:28,561] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.debugging_save_Dir: D:\py\img_worker\modules\debug_images +[2025-10-24 00:22:28,561] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.unwanted_texts: {} +[2025-10-24 00:22:28,561] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.inpaint_method: migan +[2025-10-24 00:22:28,561] [LogListener] [DEBUG] [loggerModule.py:debug:275] Image.MAX_IMAGE_PIXELS set to 20000000 +[2025-10-24 00:22:28,561] [LogListener] [DEBUG] [loggerModule.py:debug:275] ONNX 모델 타입 설정값: 자동 선택, GPU 정보: {} +[2025-10-24 00:22:28,561] [LogListener] [DEBUG] [loggerModule.py:debug:275] 자동 선택 모드: GPU 추천 모델 simp 사용 +[2025-10-24 00:22:28,561] [LogListener] [DEBUG] [loggerModule.py:debug:275] ONNX OCR 모델 타입 결정: simp (GPU: False) +[2025-10-24 00:22:28,562] [LogListener] [DEBUG] [loggerModule.py:debug:275] ONNX OCR 모듈 CPU 모드로 설정 +[2025-10-24 00:22:28,562] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🚀 ONNX TextSystem 초기화 시작 (CPU 모드) +[2025-10-24 00:22:28,562] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🚀 ONNX TextSystem 초기화 시작 (CPU 모드) +[2025-10-24 00:22:30,108] [LogListener] [DEBUG] [loggerModule.py:debug:275] 문자 사전 파일 UTF-8 확인 완료: D:\py\img_worker\modules\onnx_ocr_module\dict\ppocr_keys_v1.txt +[2025-10-24 00:22:30,190] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ ONNX TextSystem 초기화 완료 (CPU + SIMP (호환성) 모델) +[2025-10-24 00:22:30,191] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ ONNX TextSystem 초기화 완료 (CPU + SIMP 모델) +[2025-10-24 00:22:30,191] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ ONNX OCR 모듈 초기화 성공 (CPU 모드) +[2025-10-24 00:22:30,191] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ ONNX OCR 모듈 초기화 성공 +[2025-10-24 00:22:30,191] [LogListener] [DEBUG] [loggerModule.py:debug:275] 마스크 모듈 초기화 완료 +[2025-10-24 00:22:30,191] [LogListener] [DEBUG] [loggerModule.py:debug:275] MaskModule 초기화 성공 +[2025-10-24 00:22:30,192] [LogListener] [DEBUG] [loggerModule.py:debug:275] 텍스트 렌더링 모듈 초기화 완료 +[2025-10-24 00:22:30,192] [LogListener] [DEBUG] [loggerModule.py:debug:275] 기본 폰트: D:\py\img_worker\modules\fonts\HakgyoansimDunggeunmisoTTFB.ttf +[2025-10-24 00:22:30,192] [LogListener] [DEBUG] [loggerModule.py:debug:275] TextRenderingModule 초기화 성공 +[2025-10-24 00:22:30,192] [LogListener] [DEBUG] [loggerModule.py:debug:275] 폰트 로드 성공: D:\py\img_worker\modules\fonts\HakgyoansimDunggeunmisoTTFB.ttf +[2025-10-24 00:22:30,192] [LogListener] [DEBUG] [loggerModule.py:debug:275] PostImageManager 초기화 성공 +[2025-10-24 00:22:30,192] [LogListener] [DEBUG] [loggerModule.py:debug:275] local_rembg_model_path: D:\py\img_worker\modules\rembg_models +[2025-10-24 00:22:30,192] [LogListener] [DEBUG] [loggerModule.py:debug:275] Request_AI_Server GPU 상태: CUDA 사용 가능=False +[2025-10-24 00:22:30,192] [LogListener] [DEBUG] [loggerModule.py:debug:275] Request_AI_Server 초기화 성공 +[2025-10-24 00:22:30,192] [LogListener] [DEBUG] [loggerModule.py:debug:275] GoogleTranslate 초기화 성공 +[2025-10-24 00:22:30,192] [LogListener] [DEBUG] [loggerModule.py:debug:275] MIGAN CUDA 사용 불가 - CPU 모드로 설정 +[2025-10-24 00:22:30,192] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] GPU 관리자 전달: GPUManager, can_use_cuda: False +[2025-10-24 00:22:30,211] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] GPU 관리자 사용 가능: False +[2025-10-24 00:22:30,411] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 사용 가능한 providers: ['DmlExecutionProvider', 'CPUExecutionProvider'] +[2025-10-24 00:22:30,411] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] DirectML 가속 활성화 +[2025-10-24 00:22:30,411] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 최종 providers: [('DmlExecutionProvider', {}), ('CPUExecutionProvider', {})] +[2025-10-24 00:22:30,411] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] DirectML 가속 시도: [('DmlExecutionProvider', {}), ('CPUExecutionProvider', {})] +[2025-10-24 00:22:30,411] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] DirectML 가속 성공! 실제 providers: ['DmlExecutionProvider', 'CPUExecutionProvider'] +[2025-10-24 00:22:30,412] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 입력 0: image, 형태: ['batch_size', 3, 'height', 'width'], 타입: tensor(uint8) +[2025-10-24 00:22:30,412] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 입력 1: mask, 형태: ['batch_size', 1, 'height', 'width'], 타입: tensor(uint8) +[2025-10-24 00:22:30,412] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 출력 0: result, 형태: ['ScatterNDresult_dim_0', 3, 'ScatterNDresult_dim_2', 'ScatterNDresult_dim_3'], 타입: tensor(uint8) +[2025-10-24 00:22:30,412] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 세션 준비 완료. providers=['DmlExecutionProvider', 'CPUExecutionProvider'] +[2025-10-24 00:22:30,412] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] GPU 관리자 연결 완료: GPUManager +[2025-10-24 00:22:30,412] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 파이프라인 gpu_manager 속성: True, 값: +[2025-10-24 00:22:30,412] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 초기화 완료: gpu_manager 속성=True, 값= +[2025-10-24 00:22:30,413] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔰 OCR 모듈 Warm-up 시작... +[2025-10-24 00:22:30,413] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔍 ONNX OCR 감지 방식: polygon +[2025-10-24 00:22:30,417] [LogListener] [DEBUG] [loggerModule.py:debug:275] ⚡ ONNX OCR 추론 완료: 5.0ms +[2025-10-24 00:22:30,417] [LogListener] [DEBUG] [loggerModule.py:debug:275] 📊 세부 시간 - 감지: 3.0ms, 인식: 0.0ms, 분류: 0.0ms +[2025-10-24 00:22:30,417] [LogListener] [WARNING] [loggerModule.py:warning:287] ⚠️ ONNX OCR 결과가 비어있습니다 +[2025-10-24 00:22:30,420] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ OCR 모듈 Warm-up 성공 +[2025-10-24 00:22:30,421] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔰 ImageProcessor Warm‑up 완료 +[2025-10-24 00:22:30,421] [ResultListener] [INFO] [loggerModule.py:info:281] 워커 READY 수신 +[2025-10-24 00:22:30,421] [ResultListener] [INFO] [loggerModule.py:info:281] 워커 READY 수신 +[2025-10-24 00:22:30,421] [LogListener] [DEBUG] [loggerModule.py:debug:275] 📡 추가 READY 신호 전송 완료 +[2025-10-24 00:22:30,422] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 00:22:30,422] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 6808) +[2025-10-24 00:22:30,422] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔥 작업 수신 성공 +[2025-10-24 00:22:30,422] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🚀 작업 처리 시작: cmd=__PING__, uid=7a971343-a409-4ae1-a6a3-80ae6b4abf4e +[2025-10-24 00:22:30,422] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 실행 직전: cmd=__PING__ +[2025-10-24 00:22:30,422] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 결과 반환 중: uid=7a971343-a409-4ae1-a6a3-80ae6b4abf4e +[2025-10-24 00:22:30,422] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 결과 반환 완료: uid=7a971343-a409-4ae1-a6a3-80ae6b4abf4e +[2025-10-24 00:22:30,423] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 00:22:30,423] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 6808) +[2025-10-24 00:23:30,429] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 00:23:30,429] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 00:23:30,429] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 6808) +[2025-10-24 00:24:30,433] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 00:24:30,433] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 00:24:30,433] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 6808) +[2025-10-24 00:25:30,436] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 00:25:30,436] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 00:25:30,436] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 6808) +[2025-10-24 00:26:30,437] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 00:26:30,437] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 00:26:30,437] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 6808) +[2025-10-24 00:27:30,448] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 00:27:30,449] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 00:27:30,449] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 6808) +[2025-10-24 00:28:30,461] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 00:28:30,461] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 00:28:30,461] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 6808) +[2025-10-24 00:29:30,470] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 00:29:30,470] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 00:29:30,470] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 6808) +[2025-10-24 00:30:30,473] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 00:30:30,473] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 00:30:30,473] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 6808) +[2025-10-24 00:31:30,480] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 00:31:30,480] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 00:31:30,480] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 6808) +[2025-10-24 00:32:30,490] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 00:32:30,490] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 00:32:30,490] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 6808) +[2025-10-24 00:32:33,663] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔥 작업 수신 성공 +[2025-10-24 00:32:33,663] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🚀 작업 처리 시작: cmd=process_single_image, uid=fa726b7d-5e57-4e80-b856-00f4835b4594 +[2025-10-24 00:32:33,663] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 프로세서 toggle_states 업데이트 : {'TEMP_IMAGE_DIR': 'C:\\ProgramData\\ImgWorker\\work', 'output_image_format': 'webp', 'use_local_rembg': True, 'local_rembg_model_path': 'D:\\py\\img_worker\\modules\\rembg_models', 'local_model_name': 'birefnet-general-lite', 'optionIMGTrans_type': 'GPU', 'detail_IMGTrans_type': 'GPU', 'thumb_trans_type': 'GPU', 'migan_use_accel': True, 'migan_onnx_path': 'D:\\py\\img_worker\\modules\\migan_onnx\\migan_pipeline_v2.onnx', 'ocr_provider_override': 'auto', 'migan_provider_override': 'auto'} +[2025-10-24 00:32:33,663] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 실행 직전: cmd=process_single_image +[2025-10-24 00:32:33,664] [LogListener] [DEBUG] [loggerModule.py:debug:275] process_single_image 호출 직전 +[2025-10-24 00:32:33,666] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 번역시작 +[2025-10-24 00:32:34,040] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 2 처리 시작: D:\py\img_worker\modules\test\1.jpg - 전체 번역 모드 +[2025-10-24 00:32:34,040] [LogListener] [DEBUG] [loggerModule.py:debug:275] 로컬 파일 경로 감지, 다운로드 생략: D:\py\img_worker\modules\test\1.jpg +[2025-10-24 00:32:34,042] [LogListener] [DEBUG] [loggerModule.py:debug:275] 상세페이지 이미지 2 원본 크기: 816x1200 +[2025-10-24 00:32:34,047] [LogListener] [DEBUG] [loggerModule.py:debug:275] 상세페이지 이미지 2 가로 크기 조정: 816x1200 → 860x1264 +[2025-10-24 00:32:34,054] [LogListener] [DEBUG] [loggerModule.py:debug:275] 상세페이지 이미지 2 크기 조정 완료: 860x1264 +[2025-10-24 00:32:34,054] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 2 로컬 저장위치(상세페이지 전처리 완료): D:\py\img_worker\modules\test\1_resized.jpg +[2025-10-24 00:32:34,057] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔍 ONNX OCR 감지 방식: polygon +[2025-10-24 00:32:34,218] [LogListener] [DEBUG] [loggerModule.py:debug:275] ⚡ ONNX OCR 추론 완료: 160.0ms +[2025-10-24 00:32:34,218] [LogListener] [DEBUG] [loggerModule.py:debug:275] 📊 세부 시간 - 감지: 88.0ms, 인식: 66.0ms, 분류: 3.0ms +[2025-10-24 00:32:34,231] [LogListener] [DEBUG] [loggerModule.py:debug:275] 메모리 변화 [OCR 처리]: 31889.6MB -> 32015.4MB (+125.8MB, +0.4%) - 이미지 2 +[2025-10-24 00:32:34,247] [LogListener] [DEBUG] [loggerModule.py:debug:275] ocr_results: [{'text': '现代极简风格', 'confidence': 0.9960710406303406, 'polygon': [[232.0, 72.0], [581.0, 72.0], [581.0, 122.0], [232.0, 122.0]], 'bbox': (232, 72, 350, 51), 'method': 'polygon'}, {'text': '更易搭配各种使用场景', 'confidence': 0.9952024221420288, 'polygon': [[160.0, 149.0], [656.0, 149.0], [656.0, 190.0], [160.0, 190.0]], 'bbox': (160, 149, 497, 42), 'method': 'polygon'}, {'text': '半圆两端设计', 'confidence': 0.9976122379302979, 'polygon': [[93.0, 304.0], [488.0, 304.0], [488.0, 363.0], [93.0, 363.0]], 'bbox': (93, 304, 396, 60), 'method': 'polygon'}, {'text': '承载各种欢乐', 'confidence': 0.9930798411369324, 'polygon': [[91.0, 388.0], [486.0, 388.0], [486.0, 448.0], [91.0, 448.0]], 'bbox': (91, 388, 396, 61), 'method': 'polygon'}] +[2025-10-24 00:32:34,252] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 2 OCR raw 데이터 메모리 저장 완료: 4개 텍스트 +[2025-10-24 00:32:34,252] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.6%): '现代极简风格' +[2025-10-24 00:32:34,252] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.5%): '更易搭配各种使用场景' +[2025-10-24 00:32:34,252] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.8%): '半圆两端设计' +[2025-10-24 00:32:34,252] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.3%): '承载各种欢乐' +[2025-10-24 00:32:34,252] [LogListener] [DEBUG] [loggerModule.py:debug:275] 필터링 결과: 4/4개 (신뢰도 + & 중국어) +[2025-10-24 00:32:34,252] [LogListener] [DEBUG] [loggerModule.py:debug:275] filter_ocr_results: [{'text': '现代极简风格', 'confidence': 0.9960710406303406, 'polygon': [[232.0, 72.0], [581.0, 72.0], [581.0, 122.0], [232.0, 122.0]], 'bbox': (232, 72, 350, 51), 'method': 'polygon'}, {'text': '更易搭配各种使用场景', 'confidence': 0.9952024221420288, 'polygon': [[160.0, 149.0], [656.0, 149.0], [656.0, 190.0], [160.0, 190.0]], 'bbox': (160, 149, 497, 42), 'method': 'polygon'}, {'text': '半圆两端设计', 'confidence': 0.9976122379302979, 'polygon': [[93.0, 304.0], [488.0, 304.0], [488.0, 363.0], [93.0, 363.0]], 'bbox': (93, 304, 396, 60), 'method': 'polygon'}, {'text': '承载各种欢乐', 'confidence': 0.9930798411369324, 'polygon': [[91.0, 388.0], [486.0, 388.0], [486.0, 448.0], [91.0, 448.0]], 'bbox': (91, 388, 396, 61), 'method': 'polygon'}] +[2025-10-24 00:32:34,253] [LogListener] [DEBUG] [loggerModule.py:debug:275] 중국어 텍스트 4개 필터링 완료 +[2025-10-24 00:32:34,253] [LogListener] [DEBUG] [loggerModule.py:debug:275] 한글 텍스트 0개 필터링 완료 +[2025-10-24 00:32:34,727] [LogListener] [DEBUG] [loggerModule.py:debug:275] translated_texts: ['모던한 미니멀 스타일', '다양한 사용 시나리오에 더 쉽게 일치', '반원형 끝 디자인', '온갖 기쁨을 안고'] +[2025-10-24 00:32:34,727] [LogListener] [DEBUG] [loggerModule.py:debug:275] 마스크 생성 완료 +[2025-10-24 00:32:34,727] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 2 OCR 권한 없음, 전체 번역 모드 +[2025-10-24 00:32:34,733] [LogListener] [DEBUG] [loggerModule.py:debug:275] [AUTO Inpaint] coverage=0.136, comps=2, min_center_dist=0.174 → request +[2025-10-24 00:32:34,734] [LogListener] [DEBUG] [loggerModule.py:debug:275] ocr_count: 4 +[2025-10-24 00:32:34,734] [LogListener] [DEBUG] [loggerModule.py:debug:275] is_member_valid: False +[2025-10-24 00:32:34,734] [LogListener] [DEBUG] [loggerModule.py:debug:275] [set_inpaint_method] prefix=detail, target_key=detail_IMGTrans_type, trans_type=GPU → inpaint_method=migan +[2025-10-24 00:32:34,734] [LogListener] [DEBUG] [loggerModule.py:debug:275] 최종 inpaint_method: migan +[2025-10-24 00:32:34,734] [LogListener] [DEBUG] [loggerModule.py:debug:275] MIGAN 인페인팅 시도 +[2025-10-24 00:32:34,737] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 입력 형태 - 이미지: (1, 3, 1264, 860), 마스크: (1, 1, 1264, 860) +[2025-10-24 00:32:35,377] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 출력 형태: (1264, 860, 3), dtype: uint8 +[2025-10-24 00:32:35,377] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 추론 완료: 638.94 ms +[2025-10-24 00:32:35,391] [LogListener] [DEBUG] [loggerModule.py:debug:275] MIGAN 인페인팅 성공 +[2025-10-24 00:32:35,391] [LogListener] [DEBUG] [loggerModule.py:debug:275] 메모리 변화 [인페인팅]: 32047.5MB -> 32235.9MB (+188.4MB, +0.6%) - 방법: migan +[2025-10-24 00:32:35,391] [LogListener] [DEBUG] [loggerModule.py:debug:275] 인페인팅 완료 +[2025-10-24 00:32:35,438] [LogListener] [DEBUG] [loggerModule.py:debug:275] 텍스트 렌더링 완료 +[2025-10-24 00:32:35,438] [LogListener] [DEBUG] [loggerModule.py:debug:275] watermark_text: 이미지 저작권 보유 +[2025-10-24 00:32:35,438] [LogListener] [DEBUG] [loggerModule.py:debug:275] is_watermark_enabled: True +[2025-10-24 00:32:35,757] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 저장 완료 : C:\ProgramData\ImgWorker\work\translated_detail_img_2.webp +[2025-10-24 00:32:35,758] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 2 번역 완료: C:\ProgramData\ImgWorker\work\translated_detail_img_2.webp +[2025-10-24 00:32:35,758] [LogListener] [DEBUG] [loggerModule.py:debug:275] ⏱ 이미지 파이프라인 총 2093.6ms | download=0.0ms | ocr=169.0ms | translate=474.9ms | mask=474.9ms | inpaint=662.9ms(migan/DirectML) | render=46.0ms | save=281.4ms +[2025-10-24 00:32:35,759] [LogListener] [DEBUG] [loggerModule.py:debug:275] process_single_image 호출 완료 +[2025-10-24 00:32:35,759] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 결과 반환 중: uid=fa726b7d-5e57-4e80-b856-00f4835b4594 +[2025-10-24 00:32:35,759] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 결과 반환 완료: uid=fa726b7d-5e57-4e80-b856-00f4835b4594 +[2025-10-24 00:32:35,759] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 00:32:35,760] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 6808) +[2025-10-24 00:33:31,814] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔥 작업 수신 성공 +[2025-10-24 00:33:31,814] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🚀 작업 처리 시작: cmd=remove_background, uid=0e9d4375-a1fb-409d-903e-1d482dd43ed7 +[2025-10-24 00:33:31,814] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 프로세서 toggle_states 업데이트 : {'TEMP_IMAGE_DIR': 'C:\\ProgramData\\ImgWorker\\work', 'output_image_format': 'webp', 'use_local_rembg': True, 'local_rembg_model_path': 'D:\\py\\img_worker\\modules\\rembg_models', 'local_model_name': 'birefnet-general-lite', 'optionIMGTrans_type': 'GPU', 'detail_IMGTrans_type': 'GPU', 'thumb_trans_type': 'GPU', 'migan_use_accel': True, 'migan_onnx_path': 'D:\\py\\img_worker\\modules\\migan_onnx\\migan_pipeline_v2.onnx', 'ocr_provider_override': 'auto', 'migan_provider_override': 'auto'} +[2025-10-24 00:33:31,814] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 실행 직전: cmd=remove_background +[2025-10-24 00:33:31,814] [LogListener] [DEBUG] [loggerModule.py:debug:275] remove_background 호출 직전 +[2025-10-24 00:33:31,815] [LogListener] [DEBUG] [loggerModule.py:debug:275] request_rembg 호출: use_local_rembg=True, local_model_name=birefnet-general-lite +[2025-10-24 00:33:31,817] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔧 백업 rembg 시작: model_name=birefnet-general-lite, object_ratio=0.75, debug_save=True, force_cpu=False +[2025-10-24 00:33:31,817] [LogListener] [DEBUG] [loggerModule.py:debug:275] 📁 디버그 이미지 저장 디렉토리: C:\Users\ADMINI~1\AppData\Local\Temp\wrong_shape_rembg_debug_cjds1u2w +[2025-10-24 00:33:31,834] [LogListener] [DEBUG] [loggerModule.py:debug:275] 💾 1단계 저장 완료: 입력 이미지 (1200, 816, 3) → C:\Users\ADMINI~1\AppData\Local\Temp\wrong_shape_rembg_debug_cjds1u2w\01_input_image.png +[2025-10-24 00:33:31,835] [LogListener] [DEBUG] [loggerModule.py:debug:275] BriaAI 배경제거 모듈 초기화 시작 +[2025-10-24 00:33:31,835] [LogListener] [DEBUG] [loggerModule.py:debug:275] ONNX Runtime 사용 가능한 프로바이더: ['DmlExecutionProvider', 'CPUExecutionProvider'] +[2025-10-24 00:33:31,836] [LogListener] [DEBUG] [loggerModule.py:debug:275] BriaAI CPU 모드로 설정 +[2025-10-24 00:33:31,836] [LogListener] [DEBUG] [loggerModule.py:debug:275] BriaAI 배경제거 모듈 초기화 완료 +[2025-10-24 00:33:31,836] [LogListener] [DEBUG] [loggerModule.py:debug:275] 백업용 내장 rembg 모듈 초기화됨 +[2025-10-24 00:33:31,836] [LogListener] [WARNING] [loggerModule.py:warning:287] 지원하지 않는 모델명 (birefnet-general-lite). bria-rmbg-1.4로 대체 사용 +[2025-10-24 00:33:31,864] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ 안전한 임시 파일 생성: C:\Users\ADMINI~1\AppData\Local\Temp\rembg_8d817431.png (크기: 736863 bytes) +[2025-10-24 00:33:31,865] [LogListener] [DEBUG] [loggerModule.py:debug:275] 💾 2단계 저장 완료: 임시 파일 복사 → C:\Users\ADMINI~1\AppData\Local\Temp\wrong_shape_rembg_debug_cjds1u2w\02_temp_file_copy.png +[2025-10-24 00:33:31,865] [LogListener] [DEBUG] [loggerModule.py:debug:275] 백업 rembg 모듈로 배경 제거 시작: bria-rmbg-1.4 +[2025-10-24 00:33:31,865] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔥 DirectML GPU 모드로 배경 제거 실행 +[2025-10-24 00:33:31,865] [LogListener] [DEBUG] [loggerModule.py:debug:275] BriaAI ONNX 모델 로딩 시도 (원본 providers): D:\py\img_worker\modules\rembg_models +[2025-10-24 00:33:31,867] [LogListener] [WARNING] [loggerModule.py:warning:287] BriaAI ONNX 모델 로딩 실패 (원본 providers): [ONNXRuntimeError] : 1 : FAIL : Load model from D:\py\img_worker\modules\rembg_models failed:system error number 13 +[2025-10-24 00:33:31,867] [LogListener] [DEBUG] [loggerModule.py:debug:275] BriaAI ONNX 모델 로딩 시도 (CPU 폴백): D:\py\img_worker\modules\rembg_models +[2025-10-24 00:33:31,867] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔒 CPU 안전 모드: 모든 최적화 비활성화 +[2025-10-24 00:33:31,868] [LogListener] [ERROR] [loggerModule.py:error:293] BriaAI ONNX 모델 로딩 실패 (CPU 폴백): [ONNXRuntimeError] : 1 : FAIL : Load model from D:\py\img_worker\modules\rembg_models failed:system error number 13 +[2025-10-24 00:33:31,868] [LogListener] [ERROR] [loggerModule.py:error:293] 모든 provider에서 BriaAI ONNX 모델 로딩 실패 +[2025-10-24 00:33:31,868] [LogListener] [ERROR] [loggerModule.py:error:293] 내장 rembg 모듈(bria-rmbg-1.4) 처리 실패 +[2025-10-24 00:33:31,868] [LogListener] [DEBUG] [loggerModule.py:debug:275] 임시 파일 삭제 완료: C:\Users\ADMINI~1\AppData\Local\Temp\rembg_8d817431.png +[2025-10-24 00:33:31,868] [LogListener] [ERROR] [loggerModule.py:error:293] RemoveBG 실패 +[2025-10-24 00:33:31,869] [LogListener] [DEBUG] [loggerModule.py:debug:275] remove_background 호출 완료 +[2025-10-24 00:33:31,869] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 결과 반환 중: uid=0e9d4375-a1fb-409d-903e-1d482dd43ed7 +[2025-10-24 00:33:31,869] [ResultListener] [WARNING] [loggerModule.py:warning:287] 워커 롤링 스케줄: reason=job-count-threshold +[2025-10-24 00:33:31,869] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 결과 반환 완료: uid=0e9d4375-a1fb-409d-903e-1d482dd43ed7 +[2025-10-24 00:33:31,869] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 00:33:31,869] [WorkerRoller] [WARNING] [loggerModule.py:warning:287] 워커 롤링 시작: job-count-threshold +[2025-10-24 00:33:31,869] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 6808) +[2025-10-24 00:33:31,869] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔥 작업 수신 성공 +[2025-10-24 00:33:31,870] [LogListener] [DEBUG] [loggerModule.py:debug:275] Shutdown signal 수신 → 종료 +[2025-10-24 00:33:33,196] [WorkerRoller] [INFO] [loggerModule.py:info:281] 워커 프로세스 기동: PID=14840 +[2025-10-24 00:33:33,658] [LogListener] [DEBUG] [loggerModule.py:debug:275] ImageWorker 프로세스 기동 (PID=14840, Name=ImageWorkerProcess) +[2025-10-24 00:33:33,659] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔧 ImageProcessor3 초기화 시작... +[2025-10-24 00:33:33,659] [LogListener] [DEBUG] [loggerModule.py:debug:275] tracemalloc 메모리 추적 시작 +[2025-10-24 00:33:33,665] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ DirectML 환경 준비 완료 (Windows DirectX 12 기반) +[2025-10-24 00:33:33,665] [LogListener] [DEBUG] [loggerModule.py:debug:275] === 🚀 DirectML GPU 상태 초기화 시작 🚀 === +[2025-10-24 00:33:33,665] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🎯 사용자 GPU 가속 요청: False +[2025-10-24 00:33:33,666] [LogListener] [DEBUG] [loggerModule.py:debug:275] 💻 현재 운영체제: Windows +[2025-10-24 00:33:33,666] [LogListener] [DEBUG] [loggerModule.py:debug:275] GPU 가속이 비활성화됨 (toggle_states['use_cuda'] = False) +[2025-10-24 00:33:33,666] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔒 안전한 CPU 모드로 모든 GPU 설정 강제 비활성화 +[2025-10-24 00:33:33,666] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔧 ImageProcessor3 GPU 상태 요약: +[2025-10-24 00:33:33,666] [LogListener] [DEBUG] [loggerModule.py:debug:275] - CUDA 사용 가능: False +[2025-10-24 00:33:33,666] [LogListener] [DEBUG] [loggerModule.py:debug:275] - toggle_states['use_cuda']: NOT_SET +[2025-10-24 00:33:33,666] [LogListener] [DEBUG] [loggerModule.py:debug:275] - GPU 하드웨어 정보: {} +[2025-10-24 00:33:33,666] [LogListener] [DEBUG] [loggerModule.py:debug:275] ImageProcessor3 Init toggle_states: {'TEMP_IMAGE_DIR': 'C:\\ProgramData\\ImgWorker\\work', 'output_image_format': 'webp', 'use_local_rembg': True, 'local_rembg_model_path': 'D:\\py\\img_worker\\modules\\rembg_models', 'local_model_name': 'birefnet-general-lite', 'optionIMGTrans_type': 'CPU', 'detail_IMGTrans_type': 'CPU', 'thumb_trans_type': 'CPU', 'migan_use_accel': True, 'migan_onnx_path': 'D:\\py\\img_worker\\modules\\migan_onnx\\migan_pipeline_v2.onnx', 'ocr_provider_override': 'auto', 'migan_provider_override': 'auto'} +[2025-10-24 00:33:33,666] [LogListener] [DEBUG] [loggerModule.py:debug:275] is_member_valid: False +[2025-10-24 00:33:33,666] [LogListener] [DEBUG] [loggerModule.py:debug:275] debug_images 디렉토리 이미 존재: D:\py\img_worker\modules\debug_images +[2025-10-24 00:33:33,667] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.font_path: D:\py\img_worker\modules\fonts\HakgyoansimDunggeunmisoTTFB.ttf +[2025-10-24 00:33:33,667] [LogListener] [DEBUG] [loggerModule.py:debug:275] toggle_states font_path: D:\py\img_worker\modules\fonts\HakgyoansimDunggeunmisoTTFB.ttf +[2025-10-24 00:33:33,667] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.TEMP_IMAGE_DIR: C:\ProgramData\ImgWorker\work +[2025-10-24 00:33:33,667] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.debugging_save_Dir: D:\py\img_worker\modules\debug_images +[2025-10-24 00:33:33,667] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.unwanted_texts: {} +[2025-10-24 00:33:33,667] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.inpaint_method: migan +[2025-10-24 00:33:33,667] [LogListener] [DEBUG] [loggerModule.py:debug:275] Image.MAX_IMAGE_PIXELS set to 20000000 +[2025-10-24 00:33:33,667] [LogListener] [DEBUG] [loggerModule.py:debug:275] ONNX 모델 타입 설정값: 자동 선택, GPU 정보: {} +[2025-10-24 00:33:33,667] [LogListener] [DEBUG] [loggerModule.py:debug:275] 자동 선택 모드: GPU 추천 모델 simp 사용 +[2025-10-24 00:33:33,667] [LogListener] [DEBUG] [loggerModule.py:debug:275] ONNX OCR 모델 타입 결정: simp (GPU: False) +[2025-10-24 00:33:33,667] [LogListener] [DEBUG] [loggerModule.py:debug:275] ONNX OCR 모듈 CPU 모드로 설정 +[2025-10-24 00:33:33,667] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🚀 ONNX TextSystem 초기화 시작 (CPU 모드) +[2025-10-24 00:33:33,667] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🚀 ONNX TextSystem 초기화 시작 (CPU 모드) +[2025-10-24 00:33:35,224] [LogListener] [DEBUG] [loggerModule.py:debug:275] 문자 사전 파일 UTF-8 확인 완료: D:\py\img_worker\modules\onnx_ocr_module\dict\ppocr_keys_v1.txt +[2025-10-24 00:33:35,307] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ ONNX TextSystem 초기화 완료 (CPU + SIMP (호환성) 모델) +[2025-10-24 00:33:35,307] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ ONNX TextSystem 초기화 완료 (CPU + SIMP 모델) +[2025-10-24 00:33:35,307] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ ONNX OCR 모듈 초기화 성공 (CPU 모드) +[2025-10-24 00:33:35,308] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ ONNX OCR 모듈 초기화 성공 +[2025-10-24 00:33:35,308] [LogListener] [DEBUG] [loggerModule.py:debug:275] 마스크 모듈 초기화 완료 +[2025-10-24 00:33:35,308] [LogListener] [DEBUG] [loggerModule.py:debug:275] MaskModule 초기화 성공 +[2025-10-24 00:33:35,308] [LogListener] [DEBUG] [loggerModule.py:debug:275] 텍스트 렌더링 모듈 초기화 완료 +[2025-10-24 00:33:35,308] [LogListener] [DEBUG] [loggerModule.py:debug:275] 기본 폰트: D:\py\img_worker\modules\fonts\HakgyoansimDunggeunmisoTTFB.ttf +[2025-10-24 00:33:35,308] [LogListener] [DEBUG] [loggerModule.py:debug:275] TextRenderingModule 초기화 성공 +[2025-10-24 00:33:35,308] [LogListener] [DEBUG] [loggerModule.py:debug:275] 폰트 로드 성공: D:\py\img_worker\modules\fonts\HakgyoansimDunggeunmisoTTFB.ttf +[2025-10-24 00:33:35,309] [LogListener] [DEBUG] [loggerModule.py:debug:275] PostImageManager 초기화 성공 +[2025-10-24 00:33:35,309] [LogListener] [DEBUG] [loggerModule.py:debug:275] local_rembg_model_path: D:\py\img_worker\modules\rembg_models +[2025-10-24 00:33:35,309] [LogListener] [DEBUG] [loggerModule.py:debug:275] Request_AI_Server GPU 상태: CUDA 사용 가능=False +[2025-10-24 00:33:35,309] [LogListener] [DEBUG] [loggerModule.py:debug:275] Request_AI_Server 초기화 성공 +[2025-10-24 00:33:35,309] [LogListener] [DEBUG] [loggerModule.py:debug:275] GoogleTranslate 초기화 성공 +[2025-10-24 00:33:35,309] [LogListener] [DEBUG] [loggerModule.py:debug:275] MIGAN CUDA 사용 불가 - CPU 모드로 설정 +[2025-10-24 00:33:35,309] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] GPU 관리자 전달: GPUManager, can_use_cuda: False +[2025-10-24 00:33:35,554] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] GPU 관리자 사용 가능: False +[2025-10-24 00:33:35,555] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 사용 가능한 providers: ['DmlExecutionProvider', 'CPUExecutionProvider'] +[2025-10-24 00:33:35,555] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] DirectML 가속 활성화 +[2025-10-24 00:33:35,555] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 최종 providers: [('DmlExecutionProvider', {}), ('CPUExecutionProvider', {})] +[2025-10-24 00:33:35,555] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] DirectML 가속 시도: [('DmlExecutionProvider', {}), ('CPUExecutionProvider', {})] +[2025-10-24 00:33:35,555] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] DirectML 가속 성공! 실제 providers: ['DmlExecutionProvider', 'CPUExecutionProvider'] +[2025-10-24 00:33:35,555] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 입력 0: image, 형태: ['batch_size', 3, 'height', 'width'], 타입: tensor(uint8) +[2025-10-24 00:33:35,556] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 입력 1: mask, 형태: ['batch_size', 1, 'height', 'width'], 타입: tensor(uint8) +[2025-10-24 00:33:35,556] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 출력 0: result, 형태: ['ScatterNDresult_dim_0', 3, 'ScatterNDresult_dim_2', 'ScatterNDresult_dim_3'], 타입: tensor(uint8) +[2025-10-24 00:33:35,556] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 세션 준비 완료. providers=['DmlExecutionProvider', 'CPUExecutionProvider'] +[2025-10-24 00:33:35,556] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] GPU 관리자 연결 완료: GPUManager +[2025-10-24 00:33:35,556] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 파이프라인 gpu_manager 속성: True, 값: +[2025-10-24 00:33:35,556] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 초기화 완료: gpu_manager 속성=True, 값= +[2025-10-24 00:33:35,556] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔰 OCR 모듈 Warm-up 시작... +[2025-10-24 00:33:35,556] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔍 ONNX OCR 감지 방식: polygon +[2025-10-24 00:33:35,560] [LogListener] [DEBUG] [loggerModule.py:debug:275] ⚡ ONNX OCR 추론 완료: 4.7ms +[2025-10-24 00:33:35,560] [LogListener] [DEBUG] [loggerModule.py:debug:275] 📊 세부 시간 - 감지: 3.1ms, 인식: 0.0ms, 분류: 0.0ms +[2025-10-24 00:33:35,561] [LogListener] [WARNING] [loggerModule.py:warning:287] ⚠️ ONNX OCR 결과가 비어있습니다 +[2025-10-24 00:33:35,563] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ OCR 모듈 Warm-up 성공 +[2025-10-24 00:33:35,564] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔰 ImageProcessor Warm‑up 완료 +[2025-10-24 00:33:35,564] [ResultListener] [INFO] [loggerModule.py:info:281] 워커 READY 수신 +[2025-10-24 00:33:35,564] [ResultListener] [INFO] [loggerModule.py:info:281] 워커 READY 수신 +[2025-10-24 00:33:35,564] [LogListener] [DEBUG] [loggerModule.py:debug:275] 📡 추가 READY 신호 전송 완료 +[2025-10-24 00:33:35,564] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 00:33:35,564] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 14840) +[2025-10-24 00:33:56,204] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔥 작업 수신 성공 +[2025-10-24 00:33:56,205] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🚀 작업 처리 시작: cmd=process_single_image, uid=8c6e84e5-2e73-4af6-b3e8-c88181c463b4 +[2025-10-24 00:33:56,205] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 프로세서 toggle_states 업데이트 : {'TEMP_IMAGE_DIR': 'C:\\ProgramData\\ImgWorker\\work', 'output_image_format': 'webp', 'use_local_rembg': True, 'local_rembg_model_path': 'D:\\py\\img_worker\\modules\\rembg_models', 'local_model_name': 'birefnet-general-lite', 'optionIMGTrans_type': 'GPU', 'detail_IMGTrans_type': 'GPU', 'thumb_trans_type': 'GPU', 'migan_use_accel': True, 'migan_onnx_path': 'D:\\py\\img_worker\\modules\\migan_onnx\\migan_pipeline_v2.onnx', 'ocr_provider_override': 'auto', 'migan_provider_override': 'auto'} +[2025-10-24 00:33:56,205] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 실행 직전: cmd=process_single_image +[2025-10-24 00:33:56,205] [LogListener] [DEBUG] [loggerModule.py:debug:275] process_single_image 호출 직전 +[2025-10-24 00:33:56,207] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 번역시작 +[2025-10-24 00:33:56,435] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 2 처리 시작: D:\py\img_worker\modules\test\1.jpg - 전체 번역 모드 +[2025-10-24 00:33:56,435] [LogListener] [DEBUG] [loggerModule.py:debug:275] 로컬 파일 경로 감지, 다운로드 생략: D:\py\img_worker\modules\test\1.jpg +[2025-10-24 00:33:56,437] [LogListener] [DEBUG] [loggerModule.py:debug:275] 상세페이지 이미지 2 원본 크기: 816x1200 +[2025-10-24 00:33:56,442] [LogListener] [DEBUG] [loggerModule.py:debug:275] 상세페이지 이미지 2 가로 크기 조정: 816x1200 → 860x1264 +[2025-10-24 00:33:56,449] [LogListener] [DEBUG] [loggerModule.py:debug:275] 상세페이지 이미지 2 크기 조정 완료: 860x1264 +[2025-10-24 00:33:56,450] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 2 로컬 저장위치(상세페이지 전처리 완료): D:\py\img_worker\modules\test\1_resized.jpg +[2025-10-24 00:33:56,452] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔍 ONNX OCR 감지 방식: polygon +[2025-10-24 00:33:56,625] [LogListener] [DEBUG] [loggerModule.py:debug:275] ⚡ ONNX OCR 추론 완료: 171.0ms +[2025-10-24 00:33:56,638] [LogListener] [DEBUG] [loggerModule.py:debug:275] 📊 세부 시간 - 감지: 85.0ms, 인식: 80.0ms, 분류: 4.0ms +[2025-10-24 00:33:56,654] [LogListener] [DEBUG] [loggerModule.py:debug:275] 메모리 변화 [OCR 처리]: 33736.0MB -> 33871.0MB (+134.9MB, +0.4%) - 이미지 2 +[2025-10-24 00:33:56,654] [LogListener] [DEBUG] [loggerModule.py:debug:275] ocr_results: [{'text': '现代极简风格', 'confidence': 0.9960710406303406, 'polygon': [[232.0, 72.0], [581.0, 72.0], [581.0, 122.0], [232.0, 122.0]], 'bbox': (232, 72, 350, 51), 'method': 'polygon'}, {'text': '更易搭配各种使用场景', 'confidence': 0.9952024221420288, 'polygon': [[160.0, 149.0], [656.0, 149.0], [656.0, 190.0], [160.0, 190.0]], 'bbox': (160, 149, 497, 42), 'method': 'polygon'}, {'text': '半圆两端设计', 'confidence': 0.9976122379302979, 'polygon': [[93.0, 304.0], [488.0, 304.0], [488.0, 363.0], [93.0, 363.0]], 'bbox': (93, 304, 396, 60), 'method': 'polygon'}, {'text': '承载各种欢乐', 'confidence': 0.9930798411369324, 'polygon': [[91.0, 388.0], [486.0, 388.0], [486.0, 448.0], [91.0, 448.0]], 'bbox': (91, 388, 396, 61), 'method': 'polygon'}] +[2025-10-24 00:33:56,660] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 2 OCR raw 데이터 메모리 저장 완료: 4개 텍스트 +[2025-10-24 00:33:56,660] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.6%): '现代极简风格' +[2025-10-24 00:33:56,660] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.5%): '更易搭配各种使用场景' +[2025-10-24 00:33:56,661] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.8%): '半圆两端设计' +[2025-10-24 00:33:56,661] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.3%): '承载各种欢乐' +[2025-10-24 00:33:56,661] [LogListener] [DEBUG] [loggerModule.py:debug:275] 필터링 결과: 4/4개 (신뢰도 + & 중국어) +[2025-10-24 00:33:56,661] [LogListener] [DEBUG] [loggerModule.py:debug:275] filter_ocr_results: [{'text': '现代极简风格', 'confidence': 0.9960710406303406, 'polygon': [[232.0, 72.0], [581.0, 72.0], [581.0, 122.0], [232.0, 122.0]], 'bbox': (232, 72, 350, 51), 'method': 'polygon'}, {'text': '更易搭配各种使用场景', 'confidence': 0.9952024221420288, 'polygon': [[160.0, 149.0], [656.0, 149.0], [656.0, 190.0], [160.0, 190.0]], 'bbox': (160, 149, 497, 42), 'method': 'polygon'}, {'text': '半圆两端设计', 'confidence': 0.9976122379302979, 'polygon': [[93.0, 304.0], [488.0, 304.0], [488.0, 363.0], [93.0, 363.0]], 'bbox': (93, 304, 396, 60), 'method': 'polygon'}, {'text': '承载各种欢乐', 'confidence': 0.9930798411369324, 'polygon': [[91.0, 388.0], [486.0, 388.0], [486.0, 448.0], [91.0, 448.0]], 'bbox': (91, 388, 396, 61), 'method': 'polygon'}] +[2025-10-24 00:33:56,661] [LogListener] [DEBUG] [loggerModule.py:debug:275] 중국어 텍스트 4개 필터링 완료 +[2025-10-24 00:33:56,661] [LogListener] [DEBUG] [loggerModule.py:debug:275] 한글 텍스트 0개 필터링 완료 +[2025-10-24 00:33:57,190] [LogListener] [DEBUG] [loggerModule.py:debug:275] translated_texts: ['모던한 미니멀 스타일', '다양한 사용 시나리오에 더 쉽게 일치', '반원형 끝 디자인', '온갖 기쁨을 안고'] +[2025-10-24 00:33:57,190] [LogListener] [DEBUG] [loggerModule.py:debug:275] 마스크 생성 완료 +[2025-10-24 00:33:57,190] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 2 OCR 권한 없음, 전체 번역 모드 +[2025-10-24 00:33:57,197] [LogListener] [DEBUG] [loggerModule.py:debug:275] [AUTO Inpaint] coverage=0.136, comps=2, min_center_dist=0.174 → request +[2025-10-24 00:33:57,197] [LogListener] [DEBUG] [loggerModule.py:debug:275] ocr_count: 4 +[2025-10-24 00:33:57,197] [LogListener] [DEBUG] [loggerModule.py:debug:275] is_member_valid: False +[2025-10-24 00:33:57,198] [LogListener] [DEBUG] [loggerModule.py:debug:275] [set_inpaint_method] prefix=detail, target_key=detail_IMGTrans_type, trans_type=GPU → inpaint_method=migan +[2025-10-24 00:33:57,198] [LogListener] [DEBUG] [loggerModule.py:debug:275] 최종 inpaint_method: migan +[2025-10-24 00:33:57,198] [LogListener] [DEBUG] [loggerModule.py:debug:275] MIGAN 인페인팅 시도 +[2025-10-24 00:33:57,201] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 입력 형태 - 이미지: (1, 3, 1264, 860), 마스크: (1, 1, 1264, 860) +[2025-10-24 00:33:57,661] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 출력 형태: (1264, 860, 3), dtype: uint8 +[2025-10-24 00:33:57,661] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 추론 완료: 459.25 ms +[2025-10-24 00:33:57,669] [LogListener] [DEBUG] [loggerModule.py:debug:275] MIGAN 인페인팅 성공 +[2025-10-24 00:33:57,669] [LogListener] [DEBUG] [loggerModule.py:debug:275] 메모리 변화 [인페인팅]: 33893.7MB -> 34077.5MB (+183.8MB, +0.5%) - 방법: migan +[2025-10-24 00:33:57,669] [LogListener] [DEBUG] [loggerModule.py:debug:275] 인페인팅 완료 +[2025-10-24 00:33:57,714] [LogListener] [DEBUG] [loggerModule.py:debug:275] 텍스트 렌더링 완료 +[2025-10-24 00:33:57,715] [LogListener] [DEBUG] [loggerModule.py:debug:275] watermark_text: 이미지 저작권 보유 +[2025-10-24 00:33:57,715] [LogListener] [DEBUG] [loggerModule.py:debug:275] is_watermark_enabled: True +[2025-10-24 00:33:58,028] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 저장 완료 : C:\ProgramData\ImgWorker\work\translated_detail_img_2.webp +[2025-10-24 00:33:58,029] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 2 번역 완료: C:\ProgramData\ImgWorker\work\translated_detail_img_2.webp +[2025-10-24 00:33:58,029] [LogListener] [DEBUG] [loggerModule.py:debug:275] ⏱ 이미지 파이프라인 총 1823.5ms | download=0.3ms | ocr=181.0ms | translate=528.5ms | mask=528.5ms | inpaint=477.2ms(migan/DirectML) | render=45.0ms | save=281.0ms +[2025-10-24 00:33:58,031] [LogListener] [DEBUG] [loggerModule.py:debug:275] process_single_image 호출 완료 +[2025-10-24 00:33:58,031] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 결과 반환 중: uid=8c6e84e5-2e73-4af6-b3e8-c88181c463b4 +[2025-10-24 00:33:58,031] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 결과 반환 완료: uid=8c6e84e5-2e73-4af6-b3e8-c88181c463b4 +[2025-10-24 00:33:58,031] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 00:33:58,031] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 14840) +[2025-10-24 00:34:31,995] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔥 작업 수신 성공 +[2025-10-24 00:34:31,995] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🚀 작업 처리 시작: cmd=remove_background, uid=32bb78ef-42f8-4bbc-a295-9452e2c1446c +[2025-10-24 00:34:31,996] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 프로세서 toggle_states 업데이트 : {'TEMP_IMAGE_DIR': 'C:\\ProgramData\\ImgWorker\\work', 'output_image_format': 'webp', 'use_local_rembg': True, 'local_rembg_model_path': 'D:\\py\\img_worker\\modules\\rembg_models', 'local_model_name': 'birefnet-general-lite', 'optionIMGTrans_type': 'GPU', 'detail_IMGTrans_type': 'GPU', 'thumb_trans_type': 'GPU', 'migan_use_accel': True, 'migan_onnx_path': 'D:\\py\\img_worker\\modules\\migan_onnx\\migan_pipeline_v2.onnx', 'ocr_provider_override': 'auto', 'migan_provider_override': 'auto'} +[2025-10-24 00:34:31,996] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 실행 직전: cmd=remove_background +[2025-10-24 00:34:31,996] [LogListener] [DEBUG] [loggerModule.py:debug:275] remove_background 호출 직전 +[2025-10-24 00:34:31,996] [LogListener] [DEBUG] [loggerModule.py:debug:275] request_rembg 호출: use_local_rembg=True, local_model_name=birefnet-general-lite +[2025-10-24 00:34:31,998] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔧 백업 rembg 시작: model_name=birefnet-general-lite, object_ratio=0.75, debug_save=True, force_cpu=False +[2025-10-24 00:34:31,999] [LogListener] [DEBUG] [loggerModule.py:debug:275] 📁 디버그 이미지 저장 디렉토리: C:\Users\ADMINI~1\AppData\Local\Temp\wrong_shape_rembg_debug_3_rs6gc7 +[2025-10-24 00:34:32,016] [LogListener] [DEBUG] [loggerModule.py:debug:275] 💾 1단계 저장 완료: 입력 이미지 (1200, 816, 3) → C:\Users\ADMINI~1\AppData\Local\Temp\wrong_shape_rembg_debug_3_rs6gc7\01_input_image.png +[2025-10-24 00:34:32,016] [LogListener] [DEBUG] [loggerModule.py:debug:275] BriaAI 배경제거 모듈 초기화 시작 +[2025-10-24 00:34:32,016] [LogListener] [DEBUG] [loggerModule.py:debug:275] ONNX Runtime 사용 가능한 프로바이더: ['DmlExecutionProvider', 'CPUExecutionProvider'] +[2025-10-24 00:34:32,016] [LogListener] [DEBUG] [loggerModule.py:debug:275] BriaAI CPU 모드로 설정 +[2025-10-24 00:34:32,017] [LogListener] [DEBUG] [loggerModule.py:debug:275] BriaAI 배경제거 모듈 초기화 완료 +[2025-10-24 00:34:32,017] [LogListener] [DEBUG] [loggerModule.py:debug:275] 백업용 내장 rembg 모듈 초기화됨 +[2025-10-24 00:34:32,017] [LogListener] [WARNING] [loggerModule.py:warning:287] 지원하지 않는 모델명 (birefnet-general-lite). bria-rmbg-1.4로 대체 사용 +[2025-10-24 00:34:32,044] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ 안전한 임시 파일 생성: C:\Users\ADMINI~1\AppData\Local\Temp\rembg_84e224b3.png (크기: 736863 bytes) +[2025-10-24 00:34:32,045] [LogListener] [DEBUG] [loggerModule.py:debug:275] 💾 2단계 저장 완료: 임시 파일 복사 → C:\Users\ADMINI~1\AppData\Local\Temp\wrong_shape_rembg_debug_3_rs6gc7\02_temp_file_copy.png +[2025-10-24 00:34:32,045] [LogListener] [DEBUG] [loggerModule.py:debug:275] 백업 rembg 모듈로 배경 제거 시작: bria-rmbg-1.4 +[2025-10-24 00:34:32,045] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔥 DirectML GPU 모드로 배경 제거 실행 +[2025-10-24 00:34:32,045] [LogListener] [DEBUG] [loggerModule.py:debug:275] BriaAI ONNX 모델 로딩 시도 (원본 providers): D:\py\img_worker\modules\rembg_models +[2025-10-24 00:34:32,047] [LogListener] [WARNING] [loggerModule.py:warning:287] BriaAI ONNX 모델 로딩 실패 (원본 providers): [ONNXRuntimeError] : 1 : FAIL : Load model from D:\py\img_worker\modules\rembg_models failed:system error number 13 +[2025-10-24 00:34:32,047] [LogListener] [DEBUG] [loggerModule.py:debug:275] BriaAI ONNX 모델 로딩 시도 (CPU 폴백): D:\py\img_worker\modules\rembg_models +[2025-10-24 00:34:32,047] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔒 CPU 안전 모드: 모든 최적화 비활성화 +[2025-10-24 00:34:32,048] [LogListener] [ERROR] [loggerModule.py:error:293] BriaAI ONNX 모델 로딩 실패 (CPU 폴백): [ONNXRuntimeError] : 1 : FAIL : Load model from D:\py\img_worker\modules\rembg_models failed:system error number 13 +[2025-10-24 00:34:32,048] [LogListener] [ERROR] [loggerModule.py:error:293] 모든 provider에서 BriaAI ONNX 모델 로딩 실패 +[2025-10-24 00:34:32,048] [LogListener] [ERROR] [loggerModule.py:error:293] 내장 rembg 모듈(bria-rmbg-1.4) 처리 실패 +[2025-10-24 00:34:32,048] [LogListener] [DEBUG] [loggerModule.py:debug:275] 임시 파일 삭제 완료: C:\Users\ADMINI~1\AppData\Local\Temp\rembg_84e224b3.png +[2025-10-24 00:34:32,048] [LogListener] [ERROR] [loggerModule.py:error:293] RemoveBG 실패 +[2025-10-24 00:34:32,049] [LogListener] [DEBUG] [loggerModule.py:debug:275] remove_background 호출 완료 +[2025-10-24 00:34:32,049] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 결과 반환 중: uid=32bb78ef-42f8-4bbc-a295-9452e2c1446c +[2025-10-24 00:34:32,049] [ResultListener] [WARNING] [loggerModule.py:warning:287] 워커 롤링 스케줄: reason=job-count-threshold +[2025-10-24 00:34:32,049] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 결과 반환 완료: uid=32bb78ef-42f8-4bbc-a295-9452e2c1446c +[2025-10-24 00:34:32,049] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 00:34:32,049] [WorkerRoller] [WARNING] [loggerModule.py:warning:287] 워커 롤링 시작: job-count-threshold +[2025-10-24 00:34:32,049] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 14840) +[2025-10-24 00:34:32,050] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔥 작업 수신 성공 +[2025-10-24 00:34:32,050] [LogListener] [DEBUG] [loggerModule.py:debug:275] Shutdown signal 수신 → 종료 +[2025-10-24 00:34:33,346] [WorkerRoller] [INFO] [loggerModule.py:info:281] 워커 프로세스 기동: PID=13068 +[2025-10-24 00:34:33,789] [LogListener] [DEBUG] [loggerModule.py:debug:275] ImageWorker 프로세스 기동 (PID=13068, Name=ImageWorkerProcess) +[2025-10-24 00:34:33,789] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔧 ImageProcessor3 초기화 시작... +[2025-10-24 00:34:33,789] [LogListener] [DEBUG] [loggerModule.py:debug:275] tracemalloc 메모리 추적 시작 +[2025-10-24 00:34:33,795] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ DirectML 환경 준비 완료 (Windows DirectX 12 기반) +[2025-10-24 00:34:33,795] [LogListener] [DEBUG] [loggerModule.py:debug:275] === 🚀 DirectML GPU 상태 초기화 시작 🚀 === +[2025-10-24 00:34:33,795] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🎯 사용자 GPU 가속 요청: False +[2025-10-24 00:34:33,795] [LogListener] [DEBUG] [loggerModule.py:debug:275] 💻 현재 운영체제: Windows +[2025-10-24 00:34:33,796] [LogListener] [DEBUG] [loggerModule.py:debug:275] GPU 가속이 비활성화됨 (toggle_states['use_cuda'] = False) +[2025-10-24 00:34:33,796] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔒 안전한 CPU 모드로 모든 GPU 설정 강제 비활성화 +[2025-10-24 00:34:33,796] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔧 ImageProcessor3 GPU 상태 요약: +[2025-10-24 00:34:33,796] [LogListener] [DEBUG] [loggerModule.py:debug:275] - CUDA 사용 가능: False +[2025-10-24 00:34:33,796] [LogListener] [DEBUG] [loggerModule.py:debug:275] - toggle_states['use_cuda']: NOT_SET +[2025-10-24 00:34:33,796] [LogListener] [DEBUG] [loggerModule.py:debug:275] - GPU 하드웨어 정보: {} +[2025-10-24 00:34:33,796] [LogListener] [DEBUG] [loggerModule.py:debug:275] ImageProcessor3 Init toggle_states: {'TEMP_IMAGE_DIR': 'C:\\ProgramData\\ImgWorker\\work', 'output_image_format': 'webp', 'use_local_rembg': True, 'local_rembg_model_path': 'D:\\py\\img_worker\\modules\\rembg_models', 'local_model_name': 'birefnet-general-lite', 'optionIMGTrans_type': 'CPU', 'detail_IMGTrans_type': 'CPU', 'thumb_trans_type': 'CPU', 'migan_use_accel': True, 'migan_onnx_path': 'D:\\py\\img_worker\\modules\\migan_onnx\\migan_pipeline_v2.onnx', 'ocr_provider_override': 'auto', 'migan_provider_override': 'auto'} +[2025-10-24 00:34:33,796] [LogListener] [DEBUG] [loggerModule.py:debug:275] is_member_valid: False +[2025-10-24 00:34:33,796] [LogListener] [DEBUG] [loggerModule.py:debug:275] debug_images 디렉토리 이미 존재: D:\py\img_worker\modules\debug_images +[2025-10-24 00:34:33,797] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.font_path: D:\py\img_worker\modules\fonts\HakgyoansimDunggeunmisoTTFB.ttf +[2025-10-24 00:34:33,797] [LogListener] [DEBUG] [loggerModule.py:debug:275] toggle_states font_path: D:\py\img_worker\modules\fonts\HakgyoansimDunggeunmisoTTFB.ttf +[2025-10-24 00:34:33,797] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.TEMP_IMAGE_DIR: C:\ProgramData\ImgWorker\work +[2025-10-24 00:34:33,797] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.debugging_save_Dir: D:\py\img_worker\modules\debug_images +[2025-10-24 00:34:33,797] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.unwanted_texts: {} +[2025-10-24 00:34:33,797] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.inpaint_method: migan +[2025-10-24 00:34:33,797] [LogListener] [DEBUG] [loggerModule.py:debug:275] Image.MAX_IMAGE_PIXELS set to 20000000 +[2025-10-24 00:34:33,797] [LogListener] [DEBUG] [loggerModule.py:debug:275] ONNX 모델 타입 설정값: 자동 선택, GPU 정보: {} +[2025-10-24 00:34:33,797] [LogListener] [DEBUG] [loggerModule.py:debug:275] 자동 선택 모드: GPU 추천 모델 simp 사용 +[2025-10-24 00:34:33,797] [LogListener] [DEBUG] [loggerModule.py:debug:275] ONNX OCR 모델 타입 결정: simp (GPU: False) +[2025-10-24 00:34:33,797] [LogListener] [DEBUG] [loggerModule.py:debug:275] ONNX OCR 모듈 CPU 모드로 설정 +[2025-10-24 00:34:33,797] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🚀 ONNX TextSystem 초기화 시작 (CPU 모드) +[2025-10-24 00:34:33,797] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🚀 ONNX TextSystem 초기화 시작 (CPU 모드) +[2025-10-24 00:34:35,316] [LogListener] [DEBUG] [loggerModule.py:debug:275] 문자 사전 파일 UTF-8 확인 완료: D:\py\img_worker\modules\onnx_ocr_module\dict\ppocr_keys_v1.txt +[2025-10-24 00:34:35,401] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ ONNX TextSystem 초기화 완료 (CPU + SIMP (호환성) 모델) +[2025-10-24 00:34:35,401] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ ONNX TextSystem 초기화 완료 (CPU + SIMP 모델) +[2025-10-24 00:34:35,401] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ ONNX OCR 모듈 초기화 성공 (CPU 모드) +[2025-10-24 00:34:35,402] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ ONNX OCR 모듈 초기화 성공 +[2025-10-24 00:34:35,402] [LogListener] [DEBUG] [loggerModule.py:debug:275] 마스크 모듈 초기화 완료 +[2025-10-24 00:34:35,402] [LogListener] [DEBUG] [loggerModule.py:debug:275] MaskModule 초기화 성공 +[2025-10-24 00:34:35,402] [LogListener] [DEBUG] [loggerModule.py:debug:275] 텍스트 렌더링 모듈 초기화 완료 +[2025-10-24 00:34:35,402] [LogListener] [DEBUG] [loggerModule.py:debug:275] 기본 폰트: D:\py\img_worker\modules\fonts\HakgyoansimDunggeunmisoTTFB.ttf +[2025-10-24 00:34:35,402] [LogListener] [DEBUG] [loggerModule.py:debug:275] TextRenderingModule 초기화 성공 +[2025-10-24 00:34:35,402] [LogListener] [DEBUG] [loggerModule.py:debug:275] 폰트 로드 성공: D:\py\img_worker\modules\fonts\HakgyoansimDunggeunmisoTTFB.ttf +[2025-10-24 00:34:35,402] [LogListener] [DEBUG] [loggerModule.py:debug:275] PostImageManager 초기화 성공 +[2025-10-24 00:34:35,402] [LogListener] [DEBUG] [loggerModule.py:debug:275] local_rembg_model_path: D:\py\img_worker\modules\rembg_models +[2025-10-24 00:34:35,403] [LogListener] [DEBUG] [loggerModule.py:debug:275] Request_AI_Server GPU 상태: CUDA 사용 가능=False +[2025-10-24 00:34:35,403] [LogListener] [DEBUG] [loggerModule.py:debug:275] Request_AI_Server 초기화 성공 +[2025-10-24 00:34:35,403] [LogListener] [DEBUG] [loggerModule.py:debug:275] GoogleTranslate 초기화 성공 +[2025-10-24 00:34:35,403] [LogListener] [DEBUG] [loggerModule.py:debug:275] MIGAN CUDA 사용 불가 - CPU 모드로 설정 +[2025-10-24 00:34:35,403] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] GPU 관리자 전달: GPUManager, can_use_cuda: False +[2025-10-24 00:34:35,636] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] GPU 관리자 사용 가능: False +[2025-10-24 00:34:35,636] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 사용 가능한 providers: ['DmlExecutionProvider', 'CPUExecutionProvider'] +[2025-10-24 00:34:35,636] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] DirectML 가속 활성화 +[2025-10-24 00:34:35,637] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 최종 providers: [('DmlExecutionProvider', {}), ('CPUExecutionProvider', {})] +[2025-10-24 00:34:35,637] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] DirectML 가속 시도: [('DmlExecutionProvider', {}), ('CPUExecutionProvider', {})] +[2025-10-24 00:34:35,637] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] DirectML 가속 성공! 실제 providers: ['DmlExecutionProvider', 'CPUExecutionProvider'] +[2025-10-24 00:34:35,637] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 입력 0: image, 형태: ['batch_size', 3, 'height', 'width'], 타입: tensor(uint8) +[2025-10-24 00:34:35,637] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 입력 1: mask, 형태: ['batch_size', 1, 'height', 'width'], 타입: tensor(uint8) +[2025-10-24 00:34:35,637] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 출력 0: result, 형태: ['ScatterNDresult_dim_0', 3, 'ScatterNDresult_dim_2', 'ScatterNDresult_dim_3'], 타입: tensor(uint8) +[2025-10-24 00:34:35,637] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 세션 준비 완료. providers=['DmlExecutionProvider', 'CPUExecutionProvider'] +[2025-10-24 00:34:35,637] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] GPU 관리자 연결 완료: GPUManager +[2025-10-24 00:34:35,637] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 파이프라인 gpu_manager 속성: True, 값: +[2025-10-24 00:34:35,637] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 초기화 완료: gpu_manager 속성=True, 값= +[2025-10-24 00:34:35,637] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔰 OCR 모듈 Warm-up 시작... +[2025-10-24 00:34:35,638] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔍 ONNX OCR 감지 방식: polygon +[2025-10-24 00:34:35,642] [LogListener] [DEBUG] [loggerModule.py:debug:275] ⚡ ONNX OCR 추론 완료: 5.0ms +[2025-10-24 00:34:35,642] [LogListener] [DEBUG] [loggerModule.py:debug:275] 📊 세부 시간 - 감지: 2.0ms, 인식: 0.0ms, 분류: 0.0ms +[2025-10-24 00:34:35,642] [LogListener] [WARNING] [loggerModule.py:warning:287] ⚠️ ONNX OCR 결과가 비어있습니다 +[2025-10-24 00:34:35,645] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ OCR 모듈 Warm-up 성공 +[2025-10-24 00:34:35,646] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔰 ImageProcessor Warm‑up 완료 +[2025-10-24 00:34:35,646] [ResultListener] [INFO] [loggerModule.py:info:281] 워커 READY 수신 +[2025-10-24 00:34:35,646] [ResultListener] [INFO] [loggerModule.py:info:281] 워커 READY 수신 +[2025-10-24 00:34:35,646] [LogListener] [DEBUG] [loggerModule.py:debug:275] 📡 추가 READY 신호 전송 완료 +[2025-10-24 00:34:35,646] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 00:34:35,646] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 13068) +[2025-10-24 00:35:35,654] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 00:35:35,654] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 00:35:35,654] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 13068) +[2025-10-24 00:36:35,657] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 00:36:35,657] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 00:36:35,657] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 13068) +[2025-10-24 00:37:35,671] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 00:37:35,671] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 00:37:35,671] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 13068) +[2025-10-24 00:38:35,679] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 00:38:35,679] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 00:38:35,679] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 13068) +[2025-10-24 00:39:35,681] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 00:39:35,681] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 00:39:35,682] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 13068) +[2025-10-24 00:40:25,766] [MainThread] [INFO] [loggerModule.py:info:281] API 서버 종료 +[2025-10-24 00:40:26,214] [MainThread] [INFO] [loggerModule.py:info:281] 워커 프로세스 종료 +[2025-10-24 00:40:28,078] [MainThread] [INFO] [loggerModule.py:info:281] API 서버 시작 +[2025-10-24 00:40:28,084] [MainThread] [INFO] [loggerModule.py:info:281] 워커 프로세스 기동: PID=8648 +[2025-10-24 00:40:28,521] [LogListener] [DEBUG] [loggerModule.py:debug:275] ImageWorker 프로세스 기동 (PID=8648, Name=ImageWorkerProcess) +[2025-10-24 00:40:28,521] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔧 ImageProcessor3 초기화 시작... +[2025-10-24 00:40:28,521] [LogListener] [DEBUG] [loggerModule.py:debug:275] tracemalloc 메모리 추적 시작 +[2025-10-24 00:40:28,528] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ DirectML 환경 준비 완료 (Windows DirectX 12 기반) +[2025-10-24 00:40:28,528] [LogListener] [DEBUG] [loggerModule.py:debug:275] === 🚀 DirectML GPU 상태 초기화 시작 🚀 === +[2025-10-24 00:40:28,528] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🎯 사용자 GPU 가속 요청: False +[2025-10-24 00:40:28,528] [LogListener] [DEBUG] [loggerModule.py:debug:275] 💻 현재 운영체제: Windows +[2025-10-24 00:40:28,528] [LogListener] [DEBUG] [loggerModule.py:debug:275] GPU 가속이 비활성화됨 (toggle_states['use_cuda'] = False) +[2025-10-24 00:40:28,529] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔒 안전한 CPU 모드로 모든 GPU 설정 강제 비활성화 +[2025-10-24 00:40:28,529] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔧 ImageProcessor3 GPU 상태 요약: +[2025-10-24 00:40:28,529] [LogListener] [DEBUG] [loggerModule.py:debug:275] - CUDA 사용 가능: False +[2025-10-24 00:40:28,529] [LogListener] [DEBUG] [loggerModule.py:debug:275] - toggle_states['use_cuda']: NOT_SET +[2025-10-24 00:40:28,529] [LogListener] [DEBUG] [loggerModule.py:debug:275] - GPU 하드웨어 정보: {} +[2025-10-24 00:40:28,529] [LogListener] [DEBUG] [loggerModule.py:debug:275] ImageProcessor3 Init toggle_states: {'TEMP_IMAGE_DIR': 'C:\\ProgramData\\ImgWorker\\work', 'output_image_format': 'webp', 'use_local_rembg': True, 'local_rembg_model_path': 'D:\\py\\img_worker\\modules\\rembg_models', 'local_model_name': 'birefnet-general-lite', 'optionIMGTrans_type': 'CPU', 'detail_IMGTrans_type': 'CPU', 'thumb_trans_type': 'CPU', 'migan_use_accel': True, 'migan_onnx_path': 'D:\\py\\img_worker\\modules\\migan_onnx\\migan_pipeline_v2.onnx', 'ocr_provider_override': 'auto', 'migan_provider_override': 'auto'} +[2025-10-24 00:40:28,530] [LogListener] [DEBUG] [loggerModule.py:debug:275] is_member_valid: False +[2025-10-24 00:40:28,530] [LogListener] [DEBUG] [loggerModule.py:debug:275] debug_images 디렉토리 이미 존재: D:\py\img_worker\modules\debug_images +[2025-10-24 00:40:28,530] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.font_path: D:\py\img_worker\modules\fonts\HakgyoansimDunggeunmisoTTFB.ttf +[2025-10-24 00:40:28,530] [LogListener] [DEBUG] [loggerModule.py:debug:275] toggle_states font_path: D:\py\img_worker\modules\fonts\HakgyoansimDunggeunmisoTTFB.ttf +[2025-10-24 00:40:28,530] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.TEMP_IMAGE_DIR: C:\ProgramData\ImgWorker\work +[2025-10-24 00:40:28,530] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.debugging_save_Dir: D:\py\img_worker\modules\debug_images +[2025-10-24 00:40:28,530] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.unwanted_texts: {} +[2025-10-24 00:40:28,530] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.inpaint_method: migan +[2025-10-24 00:40:28,530] [LogListener] [DEBUG] [loggerModule.py:debug:275] Image.MAX_IMAGE_PIXELS set to 20000000 +[2025-10-24 00:40:28,531] [LogListener] [DEBUG] [loggerModule.py:debug:275] ONNX 모델 타입 설정값: 자동 선택, GPU 정보: {} +[2025-10-24 00:40:28,531] [LogListener] [DEBUG] [loggerModule.py:debug:275] 자동 선택 모드: GPU 추천 모델 simp 사용 +[2025-10-24 00:40:28,531] [LogListener] [DEBUG] [loggerModule.py:debug:275] ONNX OCR 모델 타입 결정: simp (GPU: False) +[2025-10-24 00:40:28,531] [LogListener] [DEBUG] [loggerModule.py:debug:275] ONNX OCR 모듈 CPU 모드로 설정 +[2025-10-24 00:40:28,531] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🚀 ONNX TextSystem 초기화 시작 (CPU 모드) +[2025-10-24 00:40:28,531] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🚀 ONNX TextSystem 초기화 시작 (CPU 모드) +[2025-10-24 00:40:30,046] [LogListener] [DEBUG] [loggerModule.py:debug:275] 문자 사전 파일 UTF-8 확인 완료: D:\py\img_worker\modules\onnx_ocr_module\dict\ppocr_keys_v1.txt +[2025-10-24 00:40:30,131] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ ONNX TextSystem 초기화 완료 (CPU + SIMP (호환성) 모델) +[2025-10-24 00:40:30,132] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ ONNX TextSystem 초기화 완료 (CPU + SIMP 모델) +[2025-10-24 00:40:30,132] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ ONNX OCR 모듈 초기화 성공 (CPU 모드) +[2025-10-24 00:40:30,132] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ ONNX OCR 모듈 초기화 성공 +[2025-10-24 00:40:30,132] [LogListener] [DEBUG] [loggerModule.py:debug:275] 마스크 모듈 초기화 완료 +[2025-10-24 00:40:30,133] [LogListener] [DEBUG] [loggerModule.py:debug:275] MaskModule 초기화 성공 +[2025-10-24 00:40:30,133] [LogListener] [DEBUG] [loggerModule.py:debug:275] 텍스트 렌더링 모듈 초기화 완료 +[2025-10-24 00:40:30,133] [LogListener] [DEBUG] [loggerModule.py:debug:275] 기본 폰트: D:\py\img_worker\modules\fonts\HakgyoansimDunggeunmisoTTFB.ttf +[2025-10-24 00:40:30,133] [LogListener] [DEBUG] [loggerModule.py:debug:275] TextRenderingModule 초기화 성공 +[2025-10-24 00:40:30,133] [LogListener] [DEBUG] [loggerModule.py:debug:275] 폰트 로드 성공: D:\py\img_worker\modules\fonts\HakgyoansimDunggeunmisoTTFB.ttf +[2025-10-24 00:40:30,133] [LogListener] [DEBUG] [loggerModule.py:debug:275] PostImageManager 초기화 성공 +[2025-10-24 00:40:30,133] [LogListener] [DEBUG] [loggerModule.py:debug:275] Request_AI_Server GPU 상태: CUDA 사용 가능=False +[2025-10-24 00:40:30,133] [LogListener] [DEBUG] [loggerModule.py:debug:275] Request_AI_Server 초기화 성공 +[2025-10-24 00:40:30,133] [LogListener] [DEBUG] [loggerModule.py:debug:275] GoogleTranslate 초기화 성공 +[2025-10-24 00:40:30,133] [LogListener] [DEBUG] [loggerModule.py:debug:275] MIGAN CUDA 사용 불가 - CPU 모드로 설정 +[2025-10-24 00:40:30,133] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] GPU 관리자 전달: GPUManager, can_use_cuda: False +[2025-10-24 00:40:30,151] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] GPU 관리자 사용 가능: False +[2025-10-24 00:40:30,353] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 사용 가능한 providers: ['DmlExecutionProvider', 'CPUExecutionProvider'] +[2025-10-24 00:40:30,354] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] DirectML 가속 활성화 +[2025-10-24 00:40:30,354] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 최종 providers: [('DmlExecutionProvider', {}), ('CPUExecutionProvider', {})] +[2025-10-24 00:40:30,354] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] DirectML 가속 시도: [('DmlExecutionProvider', {}), ('CPUExecutionProvider', {})] +[2025-10-24 00:40:30,354] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] DirectML 가속 성공! 실제 providers: ['DmlExecutionProvider', 'CPUExecutionProvider'] +[2025-10-24 00:40:30,354] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 입력 0: image, 형태: ['batch_size', 3, 'height', 'width'], 타입: tensor(uint8) +[2025-10-24 00:40:30,354] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 입력 1: mask, 형태: ['batch_size', 1, 'height', 'width'], 타입: tensor(uint8) +[2025-10-24 00:40:30,354] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 출력 0: result, 형태: ['ScatterNDresult_dim_0', 3, 'ScatterNDresult_dim_2', 'ScatterNDresult_dim_3'], 타입: tensor(uint8) +[2025-10-24 00:40:30,354] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 세션 준비 완료. providers=['DmlExecutionProvider', 'CPUExecutionProvider'] +[2025-10-24 00:40:30,354] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] GPU 관리자 연결 완료: GPUManager +[2025-10-24 00:40:30,354] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 파이프라인 gpu_manager 속성: True, 값: +[2025-10-24 00:40:30,355] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 초기화 완료: gpu_manager 속성=True, 값= +[2025-10-24 00:40:30,355] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔰 OCR 모듈 Warm-up 시작... +[2025-10-24 00:40:30,355] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔍 ONNX OCR 감지 방식: polygon +[2025-10-24 00:40:30,359] [LogListener] [DEBUG] [loggerModule.py:debug:275] ⚡ ONNX OCR 추론 완료: 4.0ms +[2025-10-24 00:40:30,359] [LogListener] [DEBUG] [loggerModule.py:debug:275] 📊 세부 시간 - 감지: 3.0ms, 인식: 0.0ms, 분류: 0.0ms +[2025-10-24 00:40:30,360] [LogListener] [WARNING] [loggerModule.py:warning:287] ⚠️ ONNX OCR 결과가 비어있습니다 +[2025-10-24 00:40:30,363] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ OCR 모듈 Warm-up 성공 +[2025-10-24 00:40:30,363] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔰 ImageProcessor Warm‑up 완료 +[2025-10-24 00:40:30,363] [ResultListener] [INFO] [loggerModule.py:info:281] 워커 READY 수신 +[2025-10-24 00:40:30,363] [ResultListener] [INFO] [loggerModule.py:info:281] 워커 READY 수신 +[2025-10-24 00:40:30,364] [LogListener] [DEBUG] [loggerModule.py:debug:275] 📡 추가 READY 신호 전송 완료 +[2025-10-24 00:40:30,364] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 00:40:30,364] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 8648) +[2025-10-24 00:40:30,364] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔥 작업 수신 성공 +[2025-10-24 00:40:30,364] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🚀 작업 처리 시작: cmd=__PING__, uid=949fa4bd-49d4-487f-a7e0-066e5a1b6292 +[2025-10-24 00:40:30,364] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 실행 직전: cmd=__PING__ +[2025-10-24 00:40:30,365] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 결과 반환 중: uid=949fa4bd-49d4-487f-a7e0-066e5a1b6292 +[2025-10-24 00:40:30,365] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 결과 반환 완료: uid=949fa4bd-49d4-487f-a7e0-066e5a1b6292 +[2025-10-24 00:40:30,365] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 00:40:30,365] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 8648) +[2025-10-24 00:40:31,635] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔥 작업 수신 성공 +[2025-10-24 00:40:31,635] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🚀 작업 처리 시작: cmd=remove_background, uid=60d8cd56-db30-431b-8958-3467fde93bb9 +[2025-10-24 00:40:31,635] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 프로세서 toggle_states 업데이트 : {'TEMP_IMAGE_DIR': 'C:\\ProgramData\\ImgWorker\\work', 'output_image_format': 'webp', 'use_local_rembg': True, 'local_rembg_model_path': 'D:\\py\\img_worker\\modules\\rembg_models', 'local_model_name': 'birefnet-general-lite', 'optionIMGTrans_type': 'GPU', 'detail_IMGTrans_type': 'GPU', 'thumb_trans_type': 'GPU', 'migan_use_accel': True, 'migan_onnx_path': 'D:\\py\\img_worker\\modules\\migan_onnx\\migan_pipeline_v2.onnx', 'ocr_provider_override': 'auto', 'migan_provider_override': 'auto'} +[2025-10-24 00:40:31,635] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 실행 직전: cmd=remove_background +[2025-10-24 00:40:31,635] [LogListener] [DEBUG] [loggerModule.py:debug:275] remove_background 호출 직전 +[2025-10-24 00:40:31,635] [LogListener] [DEBUG] [loggerModule.py:debug:275] request_rembg 호출: use_local_rembg=True, local_model_name=birefnet-general-lite +[2025-10-24 00:40:31,638] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔧 백업 rembg 시작: model_name=birefnet-general-lite, object_ratio=0.75, debug_save=True, force_cpu=False +[2025-10-24 00:40:31,638] [LogListener] [DEBUG] [loggerModule.py:debug:275] 📁 디버그 이미지 저장 디렉토리: C:\Users\ADMINI~1\AppData\Local\Temp\wrong_shape_rembg_debug_sp6854pc +[2025-10-24 00:40:31,655] [LogListener] [DEBUG] [loggerModule.py:debug:275] 💾 1단계 저장 완료: 입력 이미지 (1200, 816, 3) → C:\Users\ADMINI~1\AppData\Local\Temp\wrong_shape_rembg_debug_sp6854pc\01_input_image.png +[2025-10-24 00:40:31,655] [LogListener] [DEBUG] [loggerModule.py:debug:275] BriaAI 배경제거 모듈 초기화 시작 +[2025-10-24 00:40:31,655] [LogListener] [DEBUG] [loggerModule.py:debug:275] ONNX Runtime 사용 가능한 프로바이더: ['DmlExecutionProvider', 'CPUExecutionProvider'] +[2025-10-24 00:40:31,655] [LogListener] [DEBUG] [loggerModule.py:debug:275] BriaAI CPU 모드로 설정 +[2025-10-24 00:40:31,656] [LogListener] [DEBUG] [loggerModule.py:debug:275] BriaAI 배경제거 모듈 초기화 완료 +[2025-10-24 00:40:31,656] [LogListener] [DEBUG] [loggerModule.py:debug:275] 백업용 내장 rembg 모듈 초기화됨 +[2025-10-24 00:40:31,656] [LogListener] [WARNING] [loggerModule.py:warning:287] 지원하지 않는 모델명 (birefnet-general-lite). bria-rmbg-1.4로 대체 사용 +[2025-10-24 00:40:31,683] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ 안전한 임시 파일 생성: C:\Users\ADMINI~1\AppData\Local\Temp\rembg_729bfa7f.png (크기: 736863 bytes) +[2025-10-24 00:40:31,684] [LogListener] [DEBUG] [loggerModule.py:debug:275] 💾 2단계 저장 완료: 임시 파일 복사 → C:\Users\ADMINI~1\AppData\Local\Temp\wrong_shape_rembg_debug_sp6854pc\02_temp_file_copy.png +[2025-10-24 00:40:31,685] [LogListener] [DEBUG] [loggerModule.py:debug:275] 백업 rembg 모듈로 배경 제거 시작: bria-rmbg-1.4 +[2025-10-24 00:40:31,685] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔥 DirectML GPU 모드로 배경 제거 실행 +[2025-10-24 00:40:31,685] [LogListener] [DEBUG] [loggerModule.py:debug:275] BriaAI ONNX 모델 로딩 시도 (원본 providers): D:\py\img_worker\modules\rembg_models +[2025-10-24 00:40:31,687] [LogListener] [WARNING] [loggerModule.py:warning:287] BriaAI ONNX 모델 로딩 실패 (원본 providers): [ONNXRuntimeError] : 1 : FAIL : Load model from D:\py\img_worker\modules\rembg_models failed:system error number 13 +[2025-10-24 00:40:31,688] [LogListener] [DEBUG] [loggerModule.py:debug:275] BriaAI ONNX 모델 로딩 시도 (CPU 폴백): D:\py\img_worker\modules\rembg_models +[2025-10-24 00:40:31,688] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔒 CPU 안전 모드: 모든 최적화 비활성화 +[2025-10-24 00:40:31,689] [LogListener] [ERROR] [loggerModule.py:error:293] BriaAI ONNX 모델 로딩 실패 (CPU 폴백): [ONNXRuntimeError] : 1 : FAIL : Load model from D:\py\img_worker\modules\rembg_models failed:system error number 13 +[2025-10-24 00:40:31,689] [LogListener] [ERROR] [loggerModule.py:error:293] 모든 provider에서 BriaAI ONNX 모델 로딩 실패 +[2025-10-24 00:40:31,689] [LogListener] [ERROR] [loggerModule.py:error:293] 내장 rembg 모듈(bria-rmbg-1.4) 처리 실패 +[2025-10-24 00:40:31,689] [LogListener] [DEBUG] [loggerModule.py:debug:275] 임시 파일 삭제 완료: C:\Users\ADMINI~1\AppData\Local\Temp\rembg_729bfa7f.png +[2025-10-24 00:40:31,689] [LogListener] [ERROR] [loggerModule.py:error:293] RemoveBG 실패 +[2025-10-24 00:40:31,689] [LogListener] [DEBUG] [loggerModule.py:debug:275] remove_background 호출 완료 +[2025-10-24 00:40:31,690] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 결과 반환 중: uid=60d8cd56-db30-431b-8958-3467fde93bb9 +[2025-10-24 00:40:31,690] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 결과 반환 완료: uid=60d8cd56-db30-431b-8958-3467fde93bb9 +[2025-10-24 00:40:31,690] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 00:40:31,690] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 8648) +[2025-10-24 00:41:24,807] [MainThread] [INFO] [loggerModule.py:info:281] API 서버 종료 +[2025-10-24 00:41:25,223] [MainThread] [INFO] [loggerModule.py:info:281] 워커 프로세스 종료 +[2025-10-24 00:41:26,891] [MainThread] [INFO] [loggerModule.py:info:281] API 서버 시작 +[2025-10-24 00:41:26,896] [MainThread] [INFO] [loggerModule.py:info:281] 워커 프로세스 기동: PID=10156 +[2025-10-24 00:41:27,357] [LogListener] [DEBUG] [loggerModule.py:debug:275] ImageWorker 프로세스 기동 (PID=10156, Name=ImageWorkerProcess) +[2025-10-24 00:41:27,357] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔧 ImageProcessor3 초기화 시작... +[2025-10-24 00:41:27,357] [LogListener] [DEBUG] [loggerModule.py:debug:275] tracemalloc 메모리 추적 시작 +[2025-10-24 00:41:27,365] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ DirectML 환경 준비 완료 (Windows DirectX 12 기반) +[2025-10-24 00:41:27,366] [LogListener] [DEBUG] [loggerModule.py:debug:275] === 🚀 DirectML GPU 상태 초기화 시작 🚀 === +[2025-10-24 00:41:27,366] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🎯 사용자 GPU 가속 요청: False +[2025-10-24 00:41:27,366] [LogListener] [DEBUG] [loggerModule.py:debug:275] 💻 현재 운영체제: Windows +[2025-10-24 00:41:27,366] [LogListener] [DEBUG] [loggerModule.py:debug:275] GPU 가속이 비활성화됨 (toggle_states['use_cuda'] = False) +[2025-10-24 00:41:27,366] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔒 안전한 CPU 모드로 모든 GPU 설정 강제 비활성화 +[2025-10-24 00:41:27,366] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔧 ImageProcessor3 GPU 상태 요약: +[2025-10-24 00:41:27,366] [LogListener] [DEBUG] [loggerModule.py:debug:275] - CUDA 사용 가능: False +[2025-10-24 00:41:27,366] [LogListener] [DEBUG] [loggerModule.py:debug:275] - toggle_states['use_cuda']: NOT_SET +[2025-10-24 00:41:27,366] [LogListener] [DEBUG] [loggerModule.py:debug:275] - GPU 하드웨어 정보: {} +[2025-10-24 00:41:27,366] [LogListener] [DEBUG] [loggerModule.py:debug:275] ImageProcessor3 Init toggle_states: {'TEMP_IMAGE_DIR': 'C:\\ProgramData\\ImgWorker\\work', 'output_image_format': 'webp', 'use_local_rembg': True, 'local_rembg_model_path': 'D:\\py\\img_worker\\modules\\rembg_models', 'local_model_name': 'birefnet-general-lite', 'optionIMGTrans_type': 'CPU', 'detail_IMGTrans_type': 'CPU', 'thumb_trans_type': 'CPU', 'migan_use_accel': True, 'migan_onnx_path': 'D:\\py\\img_worker\\modules\\migan_onnx\\migan_pipeline_v2.onnx', 'ocr_provider_override': 'auto', 'migan_provider_override': 'auto'} +[2025-10-24 00:41:27,367] [LogListener] [DEBUG] [loggerModule.py:debug:275] is_member_valid: False +[2025-10-24 00:41:27,367] [LogListener] [DEBUG] [loggerModule.py:debug:275] debug_images 디렉토리 이미 존재: D:\py\img_worker\modules\debug_images +[2025-10-24 00:41:27,367] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.font_path: D:\py\img_worker\modules\fonts\HakgyoansimDunggeunmisoTTFB.ttf +[2025-10-24 00:41:27,367] [LogListener] [DEBUG] [loggerModule.py:debug:275] toggle_states font_path: D:\py\img_worker\modules\fonts\HakgyoansimDunggeunmisoTTFB.ttf +[2025-10-24 00:41:27,367] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.TEMP_IMAGE_DIR: C:\ProgramData\ImgWorker\work +[2025-10-24 00:41:27,367] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.debugging_save_Dir: D:\py\img_worker\modules\debug_images +[2025-10-24 00:41:27,367] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.unwanted_texts: {} +[2025-10-24 00:41:27,367] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.inpaint_method: migan +[2025-10-24 00:41:27,368] [LogListener] [DEBUG] [loggerModule.py:debug:275] Image.MAX_IMAGE_PIXELS set to 20000000 +[2025-10-24 00:41:27,368] [LogListener] [DEBUG] [loggerModule.py:debug:275] ONNX 모델 타입 설정값: 자동 선택, GPU 정보: {} +[2025-10-24 00:41:27,368] [LogListener] [DEBUG] [loggerModule.py:debug:275] 자동 선택 모드: GPU 추천 모델 simp 사용 +[2025-10-24 00:41:27,368] [LogListener] [DEBUG] [loggerModule.py:debug:275] ONNX OCR 모델 타입 결정: simp (GPU: False) +[2025-10-24 00:41:27,368] [LogListener] [DEBUG] [loggerModule.py:debug:275] ONNX OCR 모듈 CPU 모드로 설정 +[2025-10-24 00:41:27,368] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🚀 ONNX TextSystem 초기화 시작 (CPU 모드) +[2025-10-24 00:41:27,368] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🚀 ONNX TextSystem 초기화 시작 (CPU 모드) +[2025-10-24 00:41:28,908] [LogListener] [DEBUG] [loggerModule.py:debug:275] 문자 사전 파일 UTF-8 확인 완료: D:\py\img_worker\modules\onnx_ocr_module\dict\ppocr_keys_v1.txt +[2025-10-24 00:41:28,993] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ ONNX TextSystem 초기화 완료 (CPU + SIMP (호환성) 모델) +[2025-10-24 00:41:28,994] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ ONNX TextSystem 초기화 완료 (CPU + SIMP 모델) +[2025-10-24 00:41:28,994] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ ONNX OCR 모듈 초기화 성공 (CPU 모드) +[2025-10-24 00:41:28,994] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ ONNX OCR 모듈 초기화 성공 +[2025-10-24 00:41:28,994] [LogListener] [DEBUG] [loggerModule.py:debug:275] 마스크 모듈 초기화 완료 +[2025-10-24 00:41:28,994] [LogListener] [DEBUG] [loggerModule.py:debug:275] MaskModule 초기화 성공 +[2025-10-24 00:41:28,995] [LogListener] [DEBUG] [loggerModule.py:debug:275] 텍스트 렌더링 모듈 초기화 완료 +[2025-10-24 00:41:28,995] [LogListener] [DEBUG] [loggerModule.py:debug:275] 기본 폰트: D:\py\img_worker\modules\fonts\HakgyoansimDunggeunmisoTTFB.ttf +[2025-10-24 00:41:28,995] [LogListener] [DEBUG] [loggerModule.py:debug:275] TextRenderingModule 초기화 성공 +[2025-10-24 00:41:28,995] [LogListener] [DEBUG] [loggerModule.py:debug:275] 폰트 로드 성공: D:\py\img_worker\modules\fonts\HakgyoansimDunggeunmisoTTFB.ttf +[2025-10-24 00:41:28,995] [LogListener] [DEBUG] [loggerModule.py:debug:275] PostImageManager 초기화 성공 +[2025-10-24 00:41:28,995] [LogListener] [DEBUG] [loggerModule.py:debug:275] Request_AI_Server GPU 상태: CUDA 사용 가능=False +[2025-10-24 00:41:28,995] [LogListener] [DEBUG] [loggerModule.py:debug:275] Request_AI_Server 초기화 성공 +[2025-10-24 00:41:28,995] [LogListener] [DEBUG] [loggerModule.py:debug:275] GoogleTranslate 초기화 성공 +[2025-10-24 00:41:28,995] [LogListener] [DEBUG] [loggerModule.py:debug:275] MIGAN CUDA 사용 불가 - CPU 모드로 설정 +[2025-10-24 00:41:28,995] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] GPU 관리자 전달: GPUManager, can_use_cuda: False +[2025-10-24 00:41:29,017] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] GPU 관리자 사용 가능: False +[2025-10-24 00:41:29,238] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 사용 가능한 providers: ['DmlExecutionProvider', 'CPUExecutionProvider'] +[2025-10-24 00:41:29,239] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] DirectML 가속 활성화 +[2025-10-24 00:41:29,239] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 최종 providers: [('DmlExecutionProvider', {}), ('CPUExecutionProvider', {})] +[2025-10-24 00:41:29,239] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] DirectML 가속 시도: [('DmlExecutionProvider', {}), ('CPUExecutionProvider', {})] +[2025-10-24 00:41:29,239] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] DirectML 가속 성공! 실제 providers: ['DmlExecutionProvider', 'CPUExecutionProvider'] +[2025-10-24 00:41:29,239] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 입력 0: image, 형태: ['batch_size', 3, 'height', 'width'], 타입: tensor(uint8) +[2025-10-24 00:41:29,239] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 입력 1: mask, 형태: ['batch_size', 1, 'height', 'width'], 타입: tensor(uint8) +[2025-10-24 00:41:29,239] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 출력 0: result, 형태: ['ScatterNDresult_dim_0', 3, 'ScatterNDresult_dim_2', 'ScatterNDresult_dim_3'], 타입: tensor(uint8) +[2025-10-24 00:41:29,239] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 세션 준비 완료. providers=['DmlExecutionProvider', 'CPUExecutionProvider'] +[2025-10-24 00:41:29,240] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] GPU 관리자 연결 완료: GPUManager +[2025-10-24 00:41:29,240] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 파이프라인 gpu_manager 속성: True, 값: +[2025-10-24 00:41:29,240] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 초기화 완료: gpu_manager 속성=True, 값= +[2025-10-24 00:41:29,240] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔰 OCR 모듈 Warm-up 시작... +[2025-10-24 00:41:29,240] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔍 ONNX OCR 감지 방식: polygon +[2025-10-24 00:41:29,245] [LogListener] [DEBUG] [loggerModule.py:debug:275] ⚡ ONNX OCR 추론 완료: 4.0ms +[2025-10-24 00:41:29,245] [LogListener] [DEBUG] [loggerModule.py:debug:275] 📊 세부 시간 - 감지: 3.0ms, 인식: 0.0ms, 분류: 0.0ms +[2025-10-24 00:41:29,245] [LogListener] [WARNING] [loggerModule.py:warning:287] ⚠️ ONNX OCR 결과가 비어있습니다 +[2025-10-24 00:41:29,248] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ OCR 모듈 Warm-up 성공 +[2025-10-24 00:41:29,248] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔰 ImageProcessor Warm‑up 완료 +[2025-10-24 00:41:29,248] [ResultListener] [INFO] [loggerModule.py:info:281] 워커 READY 수신 +[2025-10-24 00:41:29,249] [ResultListener] [INFO] [loggerModule.py:info:281] 워커 READY 수신 +[2025-10-24 00:41:29,249] [LogListener] [DEBUG] [loggerModule.py:debug:275] 📡 추가 READY 신호 전송 완료 +[2025-10-24 00:41:29,249] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 00:41:29,249] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 10156) +[2025-10-24 00:41:29,250] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔥 작업 수신 성공 +[2025-10-24 00:41:29,250] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🚀 작업 처리 시작: cmd=__PING__, uid=c1bdaa2a-14ce-4d0b-a57b-9278e393bb94 +[2025-10-24 00:41:29,250] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 실행 직전: cmd=__PING__ +[2025-10-24 00:41:29,250] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 결과 반환 중: uid=c1bdaa2a-14ce-4d0b-a57b-9278e393bb94 +[2025-10-24 00:41:29,250] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 결과 반환 완료: uid=c1bdaa2a-14ce-4d0b-a57b-9278e393bb94 +[2025-10-24 00:41:29,250] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 00:41:29,250] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 10156) +[2025-10-24 00:41:29,902] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔥 작업 수신 성공 +[2025-10-24 00:41:29,902] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🚀 작업 처리 시작: cmd=remove_background, uid=52608b8c-1f3b-4e93-aca4-4949d943051b +[2025-10-24 00:41:29,902] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 프로세서 toggle_states 업데이트 : {'TEMP_IMAGE_DIR': 'C:\\ProgramData\\ImgWorker\\work', 'output_image_format': 'webp', 'use_local_rembg': True, 'local_rembg_model_path': 'D:\\py\\img_worker\\modules\\rembg_models', 'local_model_name': 'birefnet-general-lite', 'optionIMGTrans_type': 'GPU', 'detail_IMGTrans_type': 'GPU', 'thumb_trans_type': 'GPU', 'migan_use_accel': True, 'migan_onnx_path': 'D:\\py\\img_worker\\modules\\migan_onnx\\migan_pipeline_v2.onnx', 'ocr_provider_override': 'auto', 'migan_provider_override': 'auto'} +[2025-10-24 00:41:29,902] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 실행 직전: cmd=remove_background +[2025-10-24 00:41:29,903] [LogListener] [DEBUG] [loggerModule.py:debug:275] remove_background 호출 직전 +[2025-10-24 00:41:29,903] [LogListener] [DEBUG] [loggerModule.py:debug:275] request_rembg 호출: use_local_rembg=True, local_model_name=birefnet-general-lite +[2025-10-24 00:41:29,905] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔧 백업 rembg 시작: model_name=birefnet-general-lite, object_ratio=0.75, debug_save=True, force_cpu=False +[2025-10-24 00:41:29,905] [LogListener] [DEBUG] [loggerModule.py:debug:275] 📁 디버그 이미지 저장 디렉토리: C:\Users\ADMINI~1\AppData\Local\Temp\wrong_shape_rembg_debug_p9jvrk4k +[2025-10-24 00:41:29,923] [LogListener] [DEBUG] [loggerModule.py:debug:275] 💾 1단계 저장 완료: 입력 이미지 (1200, 816, 3) → C:\Users\ADMINI~1\AppData\Local\Temp\wrong_shape_rembg_debug_p9jvrk4k\01_input_image.png +[2025-10-24 00:41:29,923] [LogListener] [DEBUG] [loggerModule.py:debug:275] BriaAI 배경제거 모듈 초기화 시작 +[2025-10-24 00:41:29,923] [LogListener] [DEBUG] [loggerModule.py:debug:275] ONNX Runtime 사용 가능한 프로바이더: ['DmlExecutionProvider', 'CPUExecutionProvider'] +[2025-10-24 00:41:29,923] [LogListener] [DEBUG] [loggerModule.py:debug:275] BriaAI CPU 모드로 설정 +[2025-10-24 00:41:29,923] [LogListener] [DEBUG] [loggerModule.py:debug:275] BriaAI 배경제거 모듈 초기화 완료 +[2025-10-24 00:41:29,924] [LogListener] [DEBUG] [loggerModule.py:debug:275] 백업용 내장 rembg 모듈 초기화됨 +[2025-10-24 00:41:29,924] [LogListener] [WARNING] [loggerModule.py:warning:287] 지원하지 않는 모델명 (birefnet-general-lite). bria-rmbg-1.4로 대체 사용 +[2025-10-24 00:41:29,951] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ 안전한 임시 파일 생성: C:\Users\ADMINI~1\AppData\Local\Temp\rembg_91cc4e38.png (크기: 736863 bytes) +[2025-10-24 00:41:29,952] [LogListener] [DEBUG] [loggerModule.py:debug:275] 💾 2단계 저장 완료: 임시 파일 복사 → C:\Users\ADMINI~1\AppData\Local\Temp\wrong_shape_rembg_debug_p9jvrk4k\02_temp_file_copy.png +[2025-10-24 00:41:29,952] [LogListener] [DEBUG] [loggerModule.py:debug:275] 백업 rembg 모듈로 배경 제거 시작: bria-rmbg-1.4 +[2025-10-24 00:41:29,952] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔥 DirectML GPU 모드로 배경 제거 실행 +[2025-10-24 00:41:29,952] [LogListener] [DEBUG] [loggerModule.py:debug:275] BriaAI ONNX 모델 로딩 시도 (원본 providers): D:\py\img_worker\modules\rembg_models\BriaRMBG1.4_model_fp16.onnx +[2025-10-24 00:41:30,070] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ BriaAI ONNX 모델 로딩 완료 (원본 providers) | Providers: ['CPUExecutionProvider'] | Input: input | Output: output +[2025-10-24 00:41:30,081] [LogListener] [DEBUG] [loggerModule.py:debug:275] BriaAI 배경제거 시작: bria-rmbg-1.4 (aggressiveness=0.5) +[2025-10-24 00:41:30,093] [LogListener] [DEBUG] [loggerModule.py:debug:275] 전처리 완료: (1200, 816, 3) -> (1, 3, 1024, 1024), 값 범위: [-0.500, 0.500] +[2025-10-24 00:41:30,495] [LogListener] [DEBUG] [loggerModule.py:debug:275] ONNX 모델 출력 개수: 1, 첫 번째 출력 shape: (1, 1, 1024, 1024) +[2025-10-24 00:41:30,495] [LogListener] [DEBUG] [loggerModule.py:debug:275] 추론 완료: (1024, 1024), 값 범위: [0.000, 1.000] +[2025-10-24 00:41:30,514] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ BriaAI 배경제거 성공: bria-rmbg-1.4 (CPU, 0.43초) +[2025-10-24 00:41:30,516] [LogListener] [DEBUG] [loggerModule.py:debug:275] BriaAI 마스크 통계: {'min': 0, 'max': 255, 'mean': 39.34437806372549, 'nonzero_count': 163870} +[2025-10-24 00:41:30,516] [LogListener] [DEBUG] [loggerModule.py:debug:275] DirectML GPU 모드로 배경 제거 성공 +[2025-10-24 00:41:30,516] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ 백업 rembg 처리 성공: RGB, 크기: (816, 1200) +[2025-10-24 00:41:30,546] [LogListener] [DEBUG] [loggerModule.py:debug:275] 💾 3단계 저장 완료: PIL 결과 RGB (816, 1200) → C:\Users\ADMINI~1\AppData\Local\Temp\wrong_shape_rembg_debug_p9jvrk4k\03_pil_result.png +[2025-10-24 00:41:30,546] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔍 PIL 결과 분석: mode=RGB, size=(816, 1200), format=None +[2025-10-24 00:41:30,546] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔍 PIL 밴드: ('R', 'G', 'B') +[2025-10-24 00:41:30,547] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔄 RGB → BGR 변환: (1200, 816, 3) +[2025-10-24 00:41:30,554] [LogListener] [DEBUG] [loggerModule.py:debug:275] 💾 4단계 저장 완료: numpy 변환 결과 (1200, 816, 3) (흰배경 합성 완료) → C:\Users\ADMINI~1\AppData\Local\Temp\wrong_shape_rembg_debug_p9jvrk4k\04_numpy_result.png +[2025-10-24 00:41:30,554] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔧 후처리 시작: object_ratio=0.75 +[2025-10-24 00:41:30,555] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🎨 후처리 시작: 입력 이미지 크기 (1200, 816, 3), object_ratio=0.75 +[2025-10-24 00:41:30,556] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ BGR 이미지 처리: 그레이 < 250인 픽셀 수: 158674 +[2025-10-24 00:41:30,566] [LogListener] [DEBUG] [loggerModule.py:debug:275] 💾 5단계 저장: 초기 마스크 (158674개 픽셀) → C:\Users\ADMINI~1\AppData\Local\Temp\wrong_shape_rembg_debug_p9jvrk4k\05_initial_mask.png +[2025-10-24 00:41:30,567] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔧 마스크 정제: 정제 전 158674개 → 정제 후 168500개 픽셀 +[2025-10-24 00:41:30,569] [LogListener] [DEBUG] [loggerModule.py:debug:275] 💾 6단계 저장: 정제된 마스크 (168500개 픽셀) → C:\Users\ADMINI~1\AppData\Local\Temp\wrong_shape_rembg_debug_p9jvrk4k\06_refined_mask.png +[2025-10-24 00:41:30,570] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔍 연결 요소 분석: 1개 객체 발견 +[2025-10-24 00:41:30,572] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🎯 최대 연결 요소 선택: 168500개 픽셀 +[2025-10-24 00:41:30,574] [LogListener] [DEBUG] [loggerModule.py:debug:275] 💾 7단계 저장: 연결 요소 마스크 (168500개 픽셀) → C:\Users\ADMINI~1\AppData\Local\Temp\wrong_shape_rembg_debug_p9jvrk4k\07_component_mask.png +[2025-10-24 00:41:30,576] [LogListener] [DEBUG] [loggerModule.py:debug:275] 📐 바운딩 박스 계산: 유효 픽셀 168500개 +[2025-10-24 00:41:30,576] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✂️ 크롭 영역: (33,635) ~ (761,1179), 크기: (545, 729, 3) +[2025-10-24 00:41:30,581] [LogListener] [DEBUG] [loggerModule.py:debug:275] 💾 9단계 저장: 바운딩 박스 크롭 (545, 729, 3) → C:\Users\ADMINI~1\AppData\Local\Temp\wrong_shape_rembg_debug_p9jvrk4k\09_bbox_crop.png +[2025-10-24 00:41:30,582] [LogListener] [DEBUG] [loggerModule.py:debug:275] 📏 마진 추가: 7px, 최종 크기: (559, 743, 3) +[2025-10-24 00:41:30,587] [LogListener] [DEBUG] [loggerModule.py:debug:275] 💾 10단계 저장: 마진 추가 (559, 743, 3) → C:\Users\ADMINI~1\AppData\Local\Temp\wrong_shape_rembg_debug_p9jvrk4k\10_with_margin.png +[2025-10-24 00:41:30,587] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ 이미지 처리 완료 (이미 흰배경 합성됨): (559, 743, 3) +[2025-10-24 00:41:30,592] [LogListener] [DEBUG] [loggerModule.py:debug:275] 💾 11단계 저장: 흰 배경 합성 (559, 743, 3) → C:\Users\ADMINI~1\AppData\Local\Temp\wrong_shape_rembg_debug_p9jvrk4k\11_white_background.png +[2025-10-24 00:41:30,593] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🎯 1000x1000 리사이즈 시작: object_ratio=0.75, final_img.shape=(559, 743, 3) +[2025-10-24 00:41:30,593] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🎯 정사각형 캔버스 생성 시작: 입력=559x743, 목표=1000x1000, 객체비율=0.75 +[2025-10-24 00:41:30,594] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ 이미지 리사이즈 완료: 559x743 → 564x750 (scale_factor=1.009) +[2025-10-24 00:41:30,594] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ 정사각형 캔버스 생성: (1000, 1000, 3) +[2025-10-24 00:41:30,594] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ 이미지 배치 완료: offset=(125,218), size=(750,564) +[2025-10-24 00:41:30,599] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ 정사각형 캔버스 완성: 75% 스케일링, 1000x1000, DPI 72 +[2025-10-24 00:41:30,599] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ 1000x1000 리사이즈 완료: (1000, 1000, 3) +[2025-10-24 00:41:30,599] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ 백업 rembg 후처리 완료: (1000, 1000, 3) +[2025-10-24 00:41:30,607] [LogListener] [DEBUG] [loggerModule.py:debug:275] 💾 최종 결과 저장: (1000, 1000, 3) → C:\Users\ADMINI~1\AppData\Local\Temp\wrong_shape_rembg_debug_p9jvrk4k\99_final_result.png +[2025-10-24 00:41:30,607] [LogListener] [DEBUG] [loggerModule.py:debug:275] 임시 파일 삭제 완료: C:\Users\ADMINI~1\AppData\Local\Temp\rembg_91cc4e38.png +[2025-10-24 00:41:30,615] [LogListener] [DEBUG] [loggerModule.py:debug:275] 배경제거 이미지 저장: C:\ProgramData\ImgWorker\work\nobg_thumb_1.png +[2025-10-24 00:41:30,622] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔍 ONNX OCR 감지 방식: polygon +[2025-10-24 00:41:30,752] [LogListener] [DEBUG] [loggerModule.py:debug:275] ⚡ ONNX OCR 추론 완료: 130.0ms +[2025-10-24 00:41:30,771] [LogListener] [DEBUG] [loggerModule.py:debug:275] 📊 세부 시간 - 감지: 128.0ms, 인식: 0.0ms, 분류: 0.0ms +[2025-10-24 00:41:30,777] [LogListener] [WARNING] [loggerModule.py:warning:287] ⚠️ ONNX OCR 결과가 비어있습니다 +[2025-10-24 00:41:30,777] [LogListener] [DEBUG] [loggerModule.py:debug:275] OCR 결과 없음 - 정상 케이스 (NO_TEXT): C:\ProgramData\ImgWorker\work\nobg_thumb_1.png +[2025-10-24 00:41:30,777] [LogListener] [DEBUG] [loggerModule.py:debug:275] 필터링 결과: 0/0개 (신뢰도 + & 중국어) +[2025-10-24 00:41:30,777] [LogListener] [DEBUG] [loggerModule.py:debug:275] 중국어 텍스트 0개 필터링 완료 +[2025-10-24 00:41:30,777] [LogListener] [DEBUG] [loggerModule.py:debug:275] remove_background 호출 완료 +[2025-10-24 00:41:30,778] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 결과 반환 중: uid=52608b8c-1f3b-4e93-aca4-4949d943051b +[2025-10-24 00:41:30,778] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 결과 반환 완료: uid=52608b8c-1f3b-4e93-aca4-4949d943051b +[2025-10-24 00:41:30,778] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 00:41:30,778] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 10156) +[2025-10-24 00:41:46,329] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔥 작업 수신 성공 +[2025-10-24 00:41:46,329] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🚀 작업 처리 시작: cmd=remove_background, uid=c5c964f0-1b07-4c93-a100-2e561efd3753 +[2025-10-24 00:41:46,329] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 프로세서 toggle_states 업데이트 : {'TEMP_IMAGE_DIR': 'C:\\ProgramData\\ImgWorker\\work', 'output_image_format': 'webp', 'use_local_rembg': True, 'local_rembg_model_path': 'D:\\py\\img_worker\\modules\\rembg_models', 'local_model_name': 'birefnet-general-lite', 'optionIMGTrans_type': 'GPU', 'detail_IMGTrans_type': 'GPU', 'thumb_trans_type': 'GPU', 'migan_use_accel': True, 'migan_onnx_path': 'D:\\py\\img_worker\\modules\\migan_onnx\\migan_pipeline_v2.onnx', 'ocr_provider_override': 'auto', 'migan_provider_override': 'auto'} +[2025-10-24 00:41:46,329] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 실행 직전: cmd=remove_background +[2025-10-24 00:41:46,329] [LogListener] [DEBUG] [loggerModule.py:debug:275] remove_background 호출 직전 +[2025-10-24 00:41:46,329] [LogListener] [DEBUG] [loggerModule.py:debug:275] request_rembg 호출: use_local_rembg=True, local_model_name=birefnet-general-lite +[2025-10-24 00:41:46,331] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔧 백업 rembg 시작: model_name=birefnet-general-lite, object_ratio=0.75, debug_save=True, force_cpu=False +[2025-10-24 00:41:46,332] [LogListener] [DEBUG] [loggerModule.py:debug:275] 📁 디버그 이미지 저장 디렉토리: C:\Users\ADMINI~1\AppData\Local\Temp\wrong_shape_rembg_debug_s814ku47 +[2025-10-24 00:41:46,348] [LogListener] [DEBUG] [loggerModule.py:debug:275] 💾 1단계 저장 완료: 입력 이미지 (1200, 816, 3) → C:\Users\ADMINI~1\AppData\Local\Temp\wrong_shape_rembg_debug_s814ku47\01_input_image.png +[2025-10-24 00:41:46,348] [LogListener] [WARNING] [loggerModule.py:warning:287] 지원하지 않는 모델명 (birefnet-general-lite). bria-rmbg-1.4로 대체 사용 +[2025-10-24 00:41:46,376] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ 안전한 임시 파일 생성: C:\Users\ADMINI~1\AppData\Local\Temp\rembg_db2a6c03.png (크기: 736863 bytes) +[2025-10-24 00:41:46,377] [LogListener] [DEBUG] [loggerModule.py:debug:275] 💾 2단계 저장 완료: 임시 파일 복사 → C:\Users\ADMINI~1\AppData\Local\Temp\wrong_shape_rembg_debug_s814ku47\02_temp_file_copy.png +[2025-10-24 00:41:46,377] [LogListener] [DEBUG] [loggerModule.py:debug:275] 백업 rembg 모듈로 배경 제거 시작: bria-rmbg-1.4 +[2025-10-24 00:41:46,377] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔥 DirectML GPU 모드로 배경 제거 실행 +[2025-10-24 00:41:46,387] [LogListener] [DEBUG] [loggerModule.py:debug:275] BriaAI 배경제거 시작: bria-rmbg-1.4 (aggressiveness=0.5) +[2025-10-24 00:41:46,395] [LogListener] [DEBUG] [loggerModule.py:debug:275] 전처리 완료: (1200, 816, 3) -> (1, 3, 1024, 1024), 값 범위: [-0.500, 0.500] +[2025-10-24 00:41:46,785] [LogListener] [DEBUG] [loggerModule.py:debug:275] ONNX 모델 출력 개수: 1, 첫 번째 출력 shape: (1, 1, 1024, 1024) +[2025-10-24 00:41:46,785] [LogListener] [DEBUG] [loggerModule.py:debug:275] 추론 완료: (1024, 1024), 값 범위: [0.000, 1.000] +[2025-10-24 00:41:46,804] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ BriaAI 배경제거 성공: bria-rmbg-1.4 (CPU, 0.42초) +[2025-10-24 00:41:46,806] [LogListener] [DEBUG] [loggerModule.py:debug:275] BriaAI 마스크 통계: {'min': 0, 'max': 255, 'mean': 39.34437806372549, 'nonzero_count': 163870} +[2025-10-24 00:41:46,806] [LogListener] [DEBUG] [loggerModule.py:debug:275] DirectML GPU 모드로 배경 제거 성공 +[2025-10-24 00:41:46,806] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ 백업 rembg 처리 성공: RGB, 크기: (816, 1200) +[2025-10-24 00:41:46,821] [LogListener] [DEBUG] [loggerModule.py:debug:275] 💾 3단계 저장 완료: PIL 결과 RGB (816, 1200) → C:\Users\ADMINI~1\AppData\Local\Temp\wrong_shape_rembg_debug_s814ku47\03_pil_result.png +[2025-10-24 00:41:46,821] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔍 PIL 결과 분석: mode=RGB, size=(816, 1200), format=None +[2025-10-24 00:41:46,821] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔍 PIL 밴드: ('R', 'G', 'B') +[2025-10-24 00:41:46,822] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔄 RGB → BGR 변환: (1200, 816, 3) +[2025-10-24 00:41:46,829] [LogListener] [DEBUG] [loggerModule.py:debug:275] 💾 4단계 저장 완료: numpy 변환 결과 (1200, 816, 3) (흰배경 합성 완료) → C:\Users\ADMINI~1\AppData\Local\Temp\wrong_shape_rembg_debug_s814ku47\04_numpy_result.png +[2025-10-24 00:41:46,829] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔧 후처리 시작: object_ratio=0.75 +[2025-10-24 00:41:46,830] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🎨 후처리 시작: 입력 이미지 크기 (1200, 816, 3), object_ratio=0.75 +[2025-10-24 00:41:46,830] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ BGR 이미지 처리: 그레이 < 250인 픽셀 수: 158674 +[2025-10-24 00:41:46,839] [LogListener] [DEBUG] [loggerModule.py:debug:275] 💾 5단계 저장: 초기 마스크 (158674개 픽셀) → C:\Users\ADMINI~1\AppData\Local\Temp\wrong_shape_rembg_debug_s814ku47\05_initial_mask.png +[2025-10-24 00:41:46,840] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔧 마스크 정제: 정제 전 158674개 → 정제 후 168500개 픽셀 +[2025-10-24 00:41:46,842] [LogListener] [DEBUG] [loggerModule.py:debug:275] 💾 6단계 저장: 정제된 마스크 (168500개 픽셀) → C:\Users\ADMINI~1\AppData\Local\Temp\wrong_shape_rembg_debug_s814ku47\06_refined_mask.png +[2025-10-24 00:41:46,843] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔍 연결 요소 분석: 1개 객체 발견 +[2025-10-24 00:41:46,844] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🎯 최대 연결 요소 선택: 168500개 픽셀 +[2025-10-24 00:41:46,846] [LogListener] [DEBUG] [loggerModule.py:debug:275] 💾 7단계 저장: 연결 요소 마스크 (168500개 픽셀) → C:\Users\ADMINI~1\AppData\Local\Temp\wrong_shape_rembg_debug_s814ku47\07_component_mask.png +[2025-10-24 00:41:46,848] [LogListener] [DEBUG] [loggerModule.py:debug:275] 📐 바운딩 박스 계산: 유효 픽셀 168500개 +[2025-10-24 00:41:46,848] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✂️ 크롭 영역: (33,635) ~ (761,1179), 크기: (545, 729, 3) +[2025-10-24 00:41:46,854] [LogListener] [DEBUG] [loggerModule.py:debug:275] 💾 9단계 저장: 바운딩 박스 크롭 (545, 729, 3) → C:\Users\ADMINI~1\AppData\Local\Temp\wrong_shape_rembg_debug_s814ku47\09_bbox_crop.png +[2025-10-24 00:41:46,854] [LogListener] [DEBUG] [loggerModule.py:debug:275] 📏 마진 추가: 7px, 최종 크기: (559, 743, 3) +[2025-10-24 00:41:46,859] [LogListener] [DEBUG] [loggerModule.py:debug:275] 💾 10단계 저장: 마진 추가 (559, 743, 3) → C:\Users\ADMINI~1\AppData\Local\Temp\wrong_shape_rembg_debug_s814ku47\10_with_margin.png +[2025-10-24 00:41:46,860] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ 이미지 처리 완료 (이미 흰배경 합성됨): (559, 743, 3) +[2025-10-24 00:41:46,866] [LogListener] [DEBUG] [loggerModule.py:debug:275] 💾 11단계 저장: 흰 배경 합성 (559, 743, 3) → C:\Users\ADMINI~1\AppData\Local\Temp\wrong_shape_rembg_debug_s814ku47\11_white_background.png +[2025-10-24 00:41:46,866] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🎯 1000x1000 리사이즈 시작: object_ratio=0.75, final_img.shape=(559, 743, 3) +[2025-10-24 00:41:46,866] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🎯 정사각형 캔버스 생성 시작: 입력=559x743, 목표=1000x1000, 객체비율=0.75 +[2025-10-24 00:41:46,867] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ 이미지 리사이즈 완료: 559x743 → 564x750 (scale_factor=1.009) +[2025-10-24 00:41:46,868] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ 정사각형 캔버스 생성: (1000, 1000, 3) +[2025-10-24 00:41:46,868] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ 이미지 배치 완료: offset=(125,218), size=(750,564) +[2025-10-24 00:41:46,872] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ 정사각형 캔버스 완성: 75% 스케일링, 1000x1000, DPI 72 +[2025-10-24 00:41:46,872] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ 1000x1000 리사이즈 완료: (1000, 1000, 3) +[2025-10-24 00:41:46,872] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ 백업 rembg 후처리 완료: (1000, 1000, 3) +[2025-10-24 00:41:46,881] [LogListener] [DEBUG] [loggerModule.py:debug:275] 💾 최종 결과 저장: (1000, 1000, 3) → C:\Users\ADMINI~1\AppData\Local\Temp\wrong_shape_rembg_debug_s814ku47\99_final_result.png +[2025-10-24 00:41:46,881] [LogListener] [DEBUG] [loggerModule.py:debug:275] 임시 파일 삭제 완료: C:\Users\ADMINI~1\AppData\Local\Temp\rembg_db2a6c03.png +[2025-10-24 00:41:46,889] [LogListener] [DEBUG] [loggerModule.py:debug:275] 배경제거 이미지 저장: C:\ProgramData\ImgWorker\work\nobg_thumb_1.png +[2025-10-24 00:41:46,896] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔍 ONNX OCR 감지 방식: polygon +[2025-10-24 00:41:47,020] [LogListener] [DEBUG] [loggerModule.py:debug:275] ⚡ ONNX OCR 추론 완료: 124.0ms +[2025-10-24 00:41:47,021] [LogListener] [DEBUG] [loggerModule.py:debug:275] 📊 세부 시간 - 감지: 122.0ms, 인식: 0.0ms, 분류: 0.0ms +[2025-10-24 00:41:47,021] [LogListener] [WARNING] [loggerModule.py:warning:287] ⚠️ ONNX OCR 결과가 비어있습니다 +[2025-10-24 00:41:47,021] [LogListener] [DEBUG] [loggerModule.py:debug:275] OCR 결과 없음 - 정상 케이스 (NO_TEXT): C:\ProgramData\ImgWorker\work\nobg_thumb_1.png +[2025-10-24 00:41:47,021] [LogListener] [DEBUG] [loggerModule.py:debug:275] 필터링 결과: 0/0개 (신뢰도 + & 중국어) +[2025-10-24 00:41:47,021] [LogListener] [DEBUG] [loggerModule.py:debug:275] 중국어 텍스트 0개 필터링 완료 +[2025-10-24 00:41:47,021] [LogListener] [DEBUG] [loggerModule.py:debug:275] remove_background 호출 완료 +[2025-10-24 00:41:47,021] [ResultListener] [WARNING] [loggerModule.py:warning:287] 워커 롤링 스케줄: reason=job-count-threshold +[2025-10-24 00:41:47,021] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 결과 반환 중: uid=c5c964f0-1b07-4c93-a100-2e561efd3753 +[2025-10-24 00:41:47,022] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 결과 반환 완료: uid=c5c964f0-1b07-4c93-a100-2e561efd3753 +[2025-10-24 00:41:47,022] [WorkerRoller] [WARNING] [loggerModule.py:warning:287] 워커 롤링 시작: job-count-threshold +[2025-10-24 00:41:47,022] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 00:41:47,022] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 10156) +[2025-10-24 00:41:47,071] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔥 작업 수신 성공 +[2025-10-24 00:41:47,071] [LogListener] [DEBUG] [loggerModule.py:debug:275] Shutdown signal 수신 → 종료 +[2025-10-24 00:41:47,071] [LogListener] [DEBUG] [loggerModule.py:debug:275] OCR 모듈 정리 완료 +[2025-10-24 00:41:47,071] [LogListener] [DEBUG] [loggerModule.py:debug:275] 마스크 모듈 정리 완료 +[2025-10-24 00:41:47,105] [LogListener] [DEBUG] [loggerModule.py:debug:275] 임시 폴더 삭제됨: C:\ProgramData\ImgWorker\work +[2025-10-24 00:41:47,105] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 프로세서 소멸 +[2025-10-24 00:41:48,316] [WorkerRoller] [INFO] [loggerModule.py:info:281] 워커 프로세스 기동: PID=30640 +[2025-10-24 00:41:48,755] [LogListener] [DEBUG] [loggerModule.py:debug:275] ImageWorker 프로세스 기동 (PID=30640, Name=ImageWorkerProcess) +[2025-10-24 00:41:48,755] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔧 ImageProcessor3 초기화 시작... +[2025-10-24 00:41:48,755] [LogListener] [DEBUG] [loggerModule.py:debug:275] tracemalloc 메모리 추적 시작 +[2025-10-24 00:41:48,762] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ DirectML 환경 준비 완료 (Windows DirectX 12 기반) +[2025-10-24 00:41:48,762] [LogListener] [DEBUG] [loggerModule.py:debug:275] === 🚀 DirectML GPU 상태 초기화 시작 🚀 === +[2025-10-24 00:41:48,762] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🎯 사용자 GPU 가속 요청: False +[2025-10-24 00:41:48,762] [LogListener] [DEBUG] [loggerModule.py:debug:275] 💻 현재 운영체제: Windows +[2025-10-24 00:41:48,762] [LogListener] [DEBUG] [loggerModule.py:debug:275] GPU 가속이 비활성화됨 (toggle_states['use_cuda'] = False) +[2025-10-24 00:41:48,763] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔒 안전한 CPU 모드로 모든 GPU 설정 강제 비활성화 +[2025-10-24 00:41:48,763] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔧 ImageProcessor3 GPU 상태 요약: +[2025-10-24 00:41:48,763] [LogListener] [DEBUG] [loggerModule.py:debug:275] - CUDA 사용 가능: False +[2025-10-24 00:41:48,763] [LogListener] [DEBUG] [loggerModule.py:debug:275] - toggle_states['use_cuda']: NOT_SET +[2025-10-24 00:41:48,763] [LogListener] [DEBUG] [loggerModule.py:debug:275] - GPU 하드웨어 정보: {} +[2025-10-24 00:41:48,763] [LogListener] [DEBUG] [loggerModule.py:debug:275] ImageProcessor3 Init toggle_states: {'TEMP_IMAGE_DIR': 'C:\\ProgramData\\ImgWorker\\work', 'output_image_format': 'webp', 'use_local_rembg': True, 'local_rembg_model_path': 'D:\\py\\img_worker\\modules\\rembg_models', 'local_model_name': 'birefnet-general-lite', 'optionIMGTrans_type': 'CPU', 'detail_IMGTrans_type': 'CPU', 'thumb_trans_type': 'CPU', 'migan_use_accel': True, 'migan_onnx_path': 'D:\\py\\img_worker\\modules\\migan_onnx\\migan_pipeline_v2.onnx', 'ocr_provider_override': 'auto', 'migan_provider_override': 'auto'} +[2025-10-24 00:41:48,763] [LogListener] [DEBUG] [loggerModule.py:debug:275] is_member_valid: False +[2025-10-24 00:41:48,763] [LogListener] [DEBUG] [loggerModule.py:debug:275] debug_images 디렉토리 이미 존재: D:\py\img_worker\modules\debug_images +[2025-10-24 00:41:48,763] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.font_path: D:\py\img_worker\modules\fonts\HakgyoansimDunggeunmisoTTFB.ttf +[2025-10-24 00:41:48,763] [LogListener] [DEBUG] [loggerModule.py:debug:275] toggle_states font_path: D:\py\img_worker\modules\fonts\HakgyoansimDunggeunmisoTTFB.ttf +[2025-10-24 00:41:48,763] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.TEMP_IMAGE_DIR: C:\ProgramData\ImgWorker\work +[2025-10-24 00:41:48,763] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.debugging_save_Dir: D:\py\img_worker\modules\debug_images +[2025-10-24 00:41:48,763] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.unwanted_texts: {} +[2025-10-24 00:41:48,763] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.inpaint_method: migan +[2025-10-24 00:41:48,764] [LogListener] [DEBUG] [loggerModule.py:debug:275] Image.MAX_IMAGE_PIXELS set to 20000000 +[2025-10-24 00:41:48,764] [LogListener] [DEBUG] [loggerModule.py:debug:275] ONNX 모델 타입 설정값: 자동 선택, GPU 정보: {} +[2025-10-24 00:41:48,764] [LogListener] [DEBUG] [loggerModule.py:debug:275] 자동 선택 모드: GPU 추천 모델 simp 사용 +[2025-10-24 00:41:48,764] [LogListener] [DEBUG] [loggerModule.py:debug:275] ONNX OCR 모델 타입 결정: simp (GPU: False) +[2025-10-24 00:41:48,764] [LogListener] [DEBUG] [loggerModule.py:debug:275] ONNX OCR 모듈 CPU 모드로 설정 +[2025-10-24 00:41:48,764] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🚀 ONNX TextSystem 초기화 시작 (CPU 모드) +[2025-10-24 00:41:48,764] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🚀 ONNX TextSystem 초기화 시작 (CPU 모드) +[2025-10-24 00:41:50,270] [LogListener] [DEBUG] [loggerModule.py:debug:275] 문자 사전 파일 UTF-8 확인 완료: D:\py\img_worker\modules\onnx_ocr_module\dict\ppocr_keys_v1.txt +[2025-10-24 00:41:50,354] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ ONNX TextSystem 초기화 완료 (CPU + SIMP (호환성) 모델) +[2025-10-24 00:41:50,354] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ ONNX TextSystem 초기화 완료 (CPU + SIMP 모델) +[2025-10-24 00:41:50,354] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ ONNX OCR 모듈 초기화 성공 (CPU 모드) +[2025-10-24 00:41:50,355] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ ONNX OCR 모듈 초기화 성공 +[2025-10-24 00:41:50,355] [LogListener] [DEBUG] [loggerModule.py:debug:275] 마스크 모듈 초기화 완료 +[2025-10-24 00:41:50,355] [LogListener] [DEBUG] [loggerModule.py:debug:275] MaskModule 초기화 성공 +[2025-10-24 00:41:50,355] [LogListener] [DEBUG] [loggerModule.py:debug:275] 텍스트 렌더링 모듈 초기화 완료 +[2025-10-24 00:41:50,355] [LogListener] [DEBUG] [loggerModule.py:debug:275] 기본 폰트: D:\py\img_worker\modules\fonts\HakgyoansimDunggeunmisoTTFB.ttf +[2025-10-24 00:41:50,355] [LogListener] [DEBUG] [loggerModule.py:debug:275] TextRenderingModule 초기화 성공 +[2025-10-24 00:41:50,355] [LogListener] [DEBUG] [loggerModule.py:debug:275] 폰트 로드 성공: D:\py\img_worker\modules\fonts\HakgyoansimDunggeunmisoTTFB.ttf +[2025-10-24 00:41:50,356] [LogListener] [DEBUG] [loggerModule.py:debug:275] PostImageManager 초기화 성공 +[2025-10-24 00:41:50,356] [LogListener] [DEBUG] [loggerModule.py:debug:275] Request_AI_Server GPU 상태: CUDA 사용 가능=False +[2025-10-24 00:41:50,356] [LogListener] [DEBUG] [loggerModule.py:debug:275] Request_AI_Server 초기화 성공 +[2025-10-24 00:41:50,356] [LogListener] [DEBUG] [loggerModule.py:debug:275] GoogleTranslate 초기화 성공 +[2025-10-24 00:41:50,356] [LogListener] [DEBUG] [loggerModule.py:debug:275] MIGAN CUDA 사용 불가 - CPU 모드로 설정 +[2025-10-24 00:41:50,356] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] GPU 관리자 전달: GPUManager, can_use_cuda: False +[2025-10-24 00:41:50,376] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] GPU 관리자 사용 가능: False +[2025-10-24 00:41:50,631] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 사용 가능한 providers: ['DmlExecutionProvider', 'CPUExecutionProvider'] +[2025-10-24 00:41:50,631] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] DirectML 가속 활성화 +[2025-10-24 00:41:50,632] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 최종 providers: [('DmlExecutionProvider', {}), ('CPUExecutionProvider', {})] +[2025-10-24 00:41:50,632] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] DirectML 가속 시도: [('DmlExecutionProvider', {}), ('CPUExecutionProvider', {})] +[2025-10-24 00:41:50,632] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] DirectML 가속 성공! 실제 providers: ['DmlExecutionProvider', 'CPUExecutionProvider'] +[2025-10-24 00:41:50,632] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 입력 0: image, 형태: ['batch_size', 3, 'height', 'width'], 타입: tensor(uint8) +[2025-10-24 00:41:50,632] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 입력 1: mask, 형태: ['batch_size', 1, 'height', 'width'], 타입: tensor(uint8) +[2025-10-24 00:41:50,632] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 출력 0: result, 형태: ['ScatterNDresult_dim_0', 3, 'ScatterNDresult_dim_2', 'ScatterNDresult_dim_3'], 타입: tensor(uint8) +[2025-10-24 00:41:50,632] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 세션 준비 완료. providers=['DmlExecutionProvider', 'CPUExecutionProvider'] +[2025-10-24 00:41:50,632] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] GPU 관리자 연결 완료: GPUManager +[2025-10-24 00:41:50,632] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 파이프라인 gpu_manager 속성: True, 값: +[2025-10-24 00:41:50,632] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 초기화 완료: gpu_manager 속성=True, 값= +[2025-10-24 00:41:50,632] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔰 OCR 모듈 Warm-up 시작... +[2025-10-24 00:41:50,633] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔍 ONNX OCR 감지 방식: polygon +[2025-10-24 00:41:50,637] [LogListener] [DEBUG] [loggerModule.py:debug:275] ⚡ ONNX OCR 추론 완료: 5.0ms +[2025-10-24 00:41:50,637] [LogListener] [DEBUG] [loggerModule.py:debug:275] 📊 세부 시간 - 감지: 3.0ms, 인식: 0.0ms, 분류: 0.0ms +[2025-10-24 00:41:50,637] [LogListener] [WARNING] [loggerModule.py:warning:287] ⚠️ ONNX OCR 결과가 비어있습니다 +[2025-10-24 00:41:50,641] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ OCR 모듈 Warm-up 성공 +[2025-10-24 00:41:50,641] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔰 ImageProcessor Warm‑up 완료 +[2025-10-24 00:41:50,642] [ResultListener] [INFO] [loggerModule.py:info:281] 워커 READY 수신 +[2025-10-24 00:41:50,642] [ResultListener] [INFO] [loggerModule.py:info:281] 워커 READY 수신 +[2025-10-24 00:41:50,642] [LogListener] [DEBUG] [loggerModule.py:debug:275] 📡 추가 READY 신호 전송 완료 +[2025-10-24 00:41:50,642] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 00:41:50,642] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 30640) +[2025-10-24 00:42:40,877] [MainThread] [INFO] [loggerModule.py:info:281] API 서버 종료 +[2025-10-24 00:42:41,335] [MainThread] [INFO] [loggerModule.py:info:281] 워커 프로세스 종료 +[2025-10-24 00:42:43,035] [MainThread] [INFO] [loggerModule.py:info:281] API 서버 시작 +[2025-10-24 00:42:43,040] [MainThread] [INFO] [loggerModule.py:info:281] 워커 프로세스 기동: PID=13224 +[2025-10-24 00:42:43,475] [LogListener] [DEBUG] [loggerModule.py:debug:275] ImageWorker 프로세스 기동 (PID=13224, Name=ImageWorkerProcess) +[2025-10-24 00:42:43,475] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔧 ImageProcessor3 초기화 시작... +[2025-10-24 00:42:43,475] [LogListener] [DEBUG] [loggerModule.py:debug:275] tracemalloc 메모리 추적 시작 +[2025-10-24 00:42:43,482] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ DirectML 환경 준비 완료 (Windows DirectX 12 기반) +[2025-10-24 00:42:43,482] [LogListener] [DEBUG] [loggerModule.py:debug:275] === 🚀 DirectML GPU 상태 초기화 시작 🚀 === +[2025-10-24 00:42:43,482] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🎯 사용자 GPU 가속 요청: False +[2025-10-24 00:42:43,482] [LogListener] [DEBUG] [loggerModule.py:debug:275] 💻 현재 운영체제: Windows +[2025-10-24 00:42:43,482] [LogListener] [DEBUG] [loggerModule.py:debug:275] GPU 가속이 비활성화됨 (toggle_states['use_cuda'] = False) +[2025-10-24 00:42:43,482] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔒 안전한 CPU 모드로 모든 GPU 설정 강제 비활성화 +[2025-10-24 00:42:43,482] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔧 ImageProcessor3 GPU 상태 요약: +[2025-10-24 00:42:43,482] [LogListener] [DEBUG] [loggerModule.py:debug:275] - CUDA 사용 가능: False +[2025-10-24 00:42:43,483] [LogListener] [DEBUG] [loggerModule.py:debug:275] - toggle_states['use_cuda']: NOT_SET +[2025-10-24 00:42:43,483] [LogListener] [DEBUG] [loggerModule.py:debug:275] - GPU 하드웨어 정보: {} +[2025-10-24 00:42:43,483] [LogListener] [DEBUG] [loggerModule.py:debug:275] ImageProcessor3 Init toggle_states: {'TEMP_IMAGE_DIR': 'C:\\ProgramData\\ImgWorker\\work', 'output_image_format': 'webp', 'use_local_rembg': True, 'local_rembg_model_path': 'D:\\py\\img_worker\\modules\\rembg_models', 'local_model_name': 'birefnet-general-lite', 'optionIMGTrans_type': 'CPU', 'detail_IMGTrans_type': 'CPU', 'thumb_trans_type': 'CPU', 'migan_use_accel': True, 'migan_onnx_path': 'D:\\py\\img_worker\\modules\\migan_onnx\\migan_pipeline_v2.onnx', 'ocr_provider_override': 'auto', 'migan_provider_override': 'auto'} +[2025-10-24 00:42:43,483] [LogListener] [DEBUG] [loggerModule.py:debug:275] is_member_valid: False +[2025-10-24 00:42:43,483] [LogListener] [DEBUG] [loggerModule.py:debug:275] debug_images 디렉토리 이미 존재: D:\py\img_worker\modules\debug_images +[2025-10-24 00:42:43,483] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.font_path: D:\py\img_worker\modules\fonts\HakgyoansimDunggeunmisoTTFB.ttf +[2025-10-24 00:42:43,483] [LogListener] [DEBUG] [loggerModule.py:debug:275] toggle_states font_path: D:\py\img_worker\modules\fonts\HakgyoansimDunggeunmisoTTFB.ttf +[2025-10-24 00:42:43,483] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.TEMP_IMAGE_DIR: C:\ProgramData\ImgWorker\work +[2025-10-24 00:42:43,483] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.debugging_save_Dir: D:\py\img_worker\modules\debug_images +[2025-10-24 00:42:43,483] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.unwanted_texts: {} +[2025-10-24 00:42:43,484] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.inpaint_method: migan +[2025-10-24 00:42:43,484] [LogListener] [DEBUG] [loggerModule.py:debug:275] Image.MAX_IMAGE_PIXELS set to 20000000 +[2025-10-24 00:42:43,484] [LogListener] [DEBUG] [loggerModule.py:debug:275] ONNX 모델 타입 설정값: 자동 선택, GPU 정보: {} +[2025-10-24 00:42:43,484] [LogListener] [DEBUG] [loggerModule.py:debug:275] 자동 선택 모드: GPU 추천 모델 simp 사용 +[2025-10-24 00:42:43,484] [LogListener] [DEBUG] [loggerModule.py:debug:275] ONNX OCR 모델 타입 결정: simp (GPU: False) +[2025-10-24 00:42:43,484] [LogListener] [DEBUG] [loggerModule.py:debug:275] ONNX OCR 모듈 CPU 모드로 설정 +[2025-10-24 00:42:43,484] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🚀 ONNX TextSystem 초기화 시작 (CPU 모드) +[2025-10-24 00:42:43,484] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🚀 ONNX TextSystem 초기화 시작 (CPU 모드) +[2025-10-24 00:42:45,008] [LogListener] [DEBUG] [loggerModule.py:debug:275] 문자 사전 파일 UTF-8 확인 완료: D:\py\img_worker\modules\onnx_ocr_module\dict\ppocr_keys_v1.txt +[2025-10-24 00:42:45,094] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ ONNX TextSystem 초기화 완료 (CPU + SIMP (호환성) 모델) +[2025-10-24 00:42:45,094] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ ONNX TextSystem 초기화 완료 (CPU + SIMP 모델) +[2025-10-24 00:42:45,094] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ ONNX OCR 모듈 초기화 성공 (CPU 모드) +[2025-10-24 00:42:45,094] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ ONNX OCR 모듈 초기화 성공 +[2025-10-24 00:42:45,095] [LogListener] [DEBUG] [loggerModule.py:debug:275] 마스크 모듈 초기화 완료 +[2025-10-24 00:42:45,095] [LogListener] [DEBUG] [loggerModule.py:debug:275] MaskModule 초기화 성공 +[2025-10-24 00:42:45,095] [LogListener] [DEBUG] [loggerModule.py:debug:275] 텍스트 렌더링 모듈 초기화 완료 +[2025-10-24 00:42:45,095] [LogListener] [DEBUG] [loggerModule.py:debug:275] 기본 폰트: D:\py\img_worker\modules\fonts\HakgyoansimDunggeunmisoTTFB.ttf +[2025-10-24 00:42:45,095] [LogListener] [DEBUG] [loggerModule.py:debug:275] TextRenderingModule 초기화 성공 +[2025-10-24 00:42:45,095] [LogListener] [DEBUG] [loggerModule.py:debug:275] 폰트 로드 성공: D:\py\img_worker\modules\fonts\HakgyoansimDunggeunmisoTTFB.ttf +[2025-10-24 00:42:45,095] [LogListener] [DEBUG] [loggerModule.py:debug:275] PostImageManager 초기화 성공 +[2025-10-24 00:42:45,095] [LogListener] [DEBUG] [loggerModule.py:debug:275] Request_AI_Server GPU 상태: CUDA 사용 가능=False +[2025-10-24 00:42:45,095] [LogListener] [DEBUG] [loggerModule.py:debug:275] Request_AI_Server 초기화 성공 +[2025-10-24 00:42:45,095] [LogListener] [DEBUG] [loggerModule.py:debug:275] GoogleTranslate 초기화 성공 +[2025-10-24 00:42:45,095] [LogListener] [DEBUG] [loggerModule.py:debug:275] MIGAN CUDA 사용 불가 - CPU 모드로 설정 +[2025-10-24 00:42:45,096] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] GPU 관리자 전달: GPUManager, can_use_cuda: False +[2025-10-24 00:42:45,115] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] GPU 관리자 사용 가능: False +[2025-10-24 00:42:45,334] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 사용 가능한 providers: ['DmlExecutionProvider', 'CPUExecutionProvider'] +[2025-10-24 00:42:45,335] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] DirectML 가속 활성화 +[2025-10-24 00:42:45,335] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 최종 providers: [('DmlExecutionProvider', {}), ('CPUExecutionProvider', {})] +[2025-10-24 00:42:45,335] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] DirectML 가속 시도: [('DmlExecutionProvider', {}), ('CPUExecutionProvider', {})] +[2025-10-24 00:42:45,335] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] DirectML 가속 성공! 실제 providers: ['DmlExecutionProvider', 'CPUExecutionProvider'] +[2025-10-24 00:42:45,335] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 입력 0: image, 형태: ['batch_size', 3, 'height', 'width'], 타입: tensor(uint8) +[2025-10-24 00:42:45,335] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 입력 1: mask, 형태: ['batch_size', 1, 'height', 'width'], 타입: tensor(uint8) +[2025-10-24 00:42:45,335] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 출력 0: result, 형태: ['ScatterNDresult_dim_0', 3, 'ScatterNDresult_dim_2', 'ScatterNDresult_dim_3'], 타입: tensor(uint8) +[2025-10-24 00:42:45,336] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 세션 준비 완료. providers=['DmlExecutionProvider', 'CPUExecutionProvider'] +[2025-10-24 00:42:45,336] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] GPU 관리자 연결 완료: GPUManager +[2025-10-24 00:42:45,336] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 파이프라인 gpu_manager 속성: True, 값: +[2025-10-24 00:42:45,336] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 초기화 완료: gpu_manager 속성=True, 값= +[2025-10-24 00:42:45,336] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔰 OCR 모듈 Warm-up 시작... +[2025-10-24 00:42:45,336] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔍 ONNX OCR 감지 방식: polygon +[2025-10-24 00:42:45,340] [LogListener] [DEBUG] [loggerModule.py:debug:275] ⚡ ONNX OCR 추론 완료: 4.0ms +[2025-10-24 00:42:45,340] [LogListener] [DEBUG] [loggerModule.py:debug:275] 📊 세부 시간 - 감지: 3.0ms, 인식: 0.0ms, 분류: 0.0ms +[2025-10-24 00:42:45,340] [LogListener] [WARNING] [loggerModule.py:warning:287] ⚠️ ONNX OCR 결과가 비어있습니다 +[2025-10-24 00:42:45,344] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ OCR 모듈 Warm-up 성공 +[2025-10-24 00:42:45,344] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔰 ImageProcessor Warm‑up 완료 +[2025-10-24 00:42:45,344] [ResultListener] [INFO] [loggerModule.py:info:281] 워커 READY 수신 +[2025-10-24 00:42:45,344] [ResultListener] [INFO] [loggerModule.py:info:281] 워커 READY 수신 +[2025-10-24 00:42:45,345] [LogListener] [DEBUG] [loggerModule.py:debug:275] 📡 추가 READY 신호 전송 완료 +[2025-10-24 00:42:45,345] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 00:42:45,345] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 13224) +[2025-10-24 00:42:45,345] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔥 작업 수신 성공 +[2025-10-24 00:42:45,345] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🚀 작업 처리 시작: cmd=__PING__, uid=50a56506-b66d-4f07-9013-4f2186e27401 +[2025-10-24 00:42:45,345] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 실행 직전: cmd=__PING__ +[2025-10-24 00:42:45,345] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 결과 반환 중: uid=50a56506-b66d-4f07-9013-4f2186e27401 +[2025-10-24 00:42:45,346] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 결과 반환 완료: uid=50a56506-b66d-4f07-9013-4f2186e27401 +[2025-10-24 00:42:45,346] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 00:42:45,346] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 13224) +[2025-10-24 00:42:45,608] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔥 작업 수신 성공 +[2025-10-24 00:42:45,609] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🚀 작업 처리 시작: cmd=remove_background, uid=94eb59f4-2250-486b-88af-27d30fc6c57c +[2025-10-24 00:42:45,609] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 프로세서 toggle_states 업데이트 : {'TEMP_IMAGE_DIR': 'C:\\ProgramData\\ImgWorker\\work', 'output_image_format': 'webp', 'use_local_rembg': True, 'local_rembg_model_path': 'D:\\py\\img_worker\\modules\\rembg_models', 'local_model_name': 'birefnet-general-lite', 'optionIMGTrans_type': 'GPU', 'detail_IMGTrans_type': 'GPU', 'thumb_trans_type': 'GPU', 'migan_use_accel': True, 'migan_onnx_path': 'D:\\py\\img_worker\\modules\\migan_onnx\\migan_pipeline_v2.onnx', 'ocr_provider_override': 'auto', 'migan_provider_override': 'auto'} +[2025-10-24 00:42:45,609] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 실행 직전: cmd=remove_background +[2025-10-24 00:42:45,609] [LogListener] [DEBUG] [loggerModule.py:debug:275] remove_background 호출 직전 +[2025-10-24 00:42:45,609] [LogListener] [DEBUG] [loggerModule.py:debug:275] request_rembg 호출: use_local_rembg=True, local_model_name=birefnet-general-lite +[2025-10-24 00:42:45,611] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔧 백업 rembg 시작: model_name=birefnet-general-lite, object_ratio=0.95, debug_save=True, force_cpu=False +[2025-10-24 00:42:45,612] [LogListener] [DEBUG] [loggerModule.py:debug:275] 📁 디버그 이미지 저장 디렉토리: C:\Users\ADMINI~1\AppData\Local\Temp\wrong_shape_rembg_debug_vn2hi9qy +[2025-10-24 00:42:45,628] [LogListener] [DEBUG] [loggerModule.py:debug:275] 💾 1단계 저장 완료: 입력 이미지 (1200, 816, 3) → C:\Users\ADMINI~1\AppData\Local\Temp\wrong_shape_rembg_debug_vn2hi9qy\01_input_image.png +[2025-10-24 00:42:45,629] [LogListener] [DEBUG] [loggerModule.py:debug:275] BriaAI 배경제거 모듈 초기화 시작 +[2025-10-24 00:42:45,629] [LogListener] [DEBUG] [loggerModule.py:debug:275] ONNX Runtime 사용 가능한 프로바이더: ['DmlExecutionProvider', 'CPUExecutionProvider'] +[2025-10-24 00:42:45,629] [LogListener] [DEBUG] [loggerModule.py:debug:275] BriaAI CPU 모드로 설정 +[2025-10-24 00:42:45,629] [LogListener] [DEBUG] [loggerModule.py:debug:275] BriaAI 배경제거 모듈 초기화 완료 +[2025-10-24 00:42:45,629] [LogListener] [DEBUG] [loggerModule.py:debug:275] 백업용 내장 rembg 모듈 초기화됨 +[2025-10-24 00:42:45,629] [LogListener] [WARNING] [loggerModule.py:warning:287] 지원하지 않는 모델명 (birefnet-general-lite). bria-rmbg-1.4로 대체 사용 +[2025-10-24 00:42:45,657] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ 안전한 임시 파일 생성: C:\Users\ADMINI~1\AppData\Local\Temp\rembg_31f07fc3.png (크기: 736863 bytes) +[2025-10-24 00:42:45,658] [LogListener] [DEBUG] [loggerModule.py:debug:275] 💾 2단계 저장 완료: 임시 파일 복사 → C:\Users\ADMINI~1\AppData\Local\Temp\wrong_shape_rembg_debug_vn2hi9qy\02_temp_file_copy.png +[2025-10-24 00:42:45,658] [LogListener] [DEBUG] [loggerModule.py:debug:275] 백업 rembg 모듈로 배경 제거 시작: bria-rmbg-1.4 +[2025-10-24 00:42:45,658] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔥 DirectML GPU 모드로 배경 제거 실행 +[2025-10-24 00:42:45,658] [LogListener] [DEBUG] [loggerModule.py:debug:275] BriaAI ONNX 모델 로딩 시도 (원본 providers): D:\py\img_worker\modules\rembg_models\BriaRMBG1.4_model_fp16.onnx +[2025-10-24 00:42:45,776] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ BriaAI ONNX 모델 로딩 완료 (원본 providers) | Providers: ['CPUExecutionProvider'] | Input: input | Output: output +[2025-10-24 00:42:45,787] [LogListener] [DEBUG] [loggerModule.py:debug:275] BriaAI 배경제거 시작: bria-rmbg-1.4 (aggressiveness=0.5) +[2025-10-24 00:42:45,798] [LogListener] [DEBUG] [loggerModule.py:debug:275] 전처리 완료: (1200, 816, 3) -> (1, 3, 1024, 1024), 값 범위: [-0.500, 0.500] +[2025-10-24 00:42:46,212] [LogListener] [DEBUG] [loggerModule.py:debug:275] ONNX 모델 출력 개수: 1, 첫 번째 출력 shape: (1, 1, 1024, 1024) +[2025-10-24 00:42:46,213] [LogListener] [DEBUG] [loggerModule.py:debug:275] 추론 완료: (1024, 1024), 값 범위: [0.000, 1.000] +[2025-10-24 00:42:46,233] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ BriaAI 배경제거 성공: bria-rmbg-1.4 (CPU, 0.45초) +[2025-10-24 00:42:46,234] [LogListener] [DEBUG] [loggerModule.py:debug:275] BriaAI 마스크 통계: {'min': 0, 'max': 255, 'mean': 39.34437806372549, 'nonzero_count': 163870} +[2025-10-24 00:42:46,234] [LogListener] [DEBUG] [loggerModule.py:debug:275] DirectML GPU 모드로 배경 제거 성공 +[2025-10-24 00:42:46,234] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ 백업 rembg 처리 성공: RGB, 크기: (816, 1200) +[2025-10-24 00:42:46,264] [LogListener] [DEBUG] [loggerModule.py:debug:275] 💾 3단계 저장 완료: PIL 결과 RGB (816, 1200) → C:\Users\ADMINI~1\AppData\Local\Temp\wrong_shape_rembg_debug_vn2hi9qy\03_pil_result.png +[2025-10-24 00:42:46,264] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔍 PIL 결과 분석: mode=RGB, size=(816, 1200), format=None +[2025-10-24 00:42:46,264] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔍 PIL 밴드: ('R', 'G', 'B') +[2025-10-24 00:42:46,265] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔄 RGB → BGR 변환: (1200, 816, 3) +[2025-10-24 00:42:46,272] [LogListener] [DEBUG] [loggerModule.py:debug:275] 💾 4단계 저장 완료: numpy 변환 결과 (1200, 816, 3) (흰배경 합성 완료) → C:\Users\ADMINI~1\AppData\Local\Temp\wrong_shape_rembg_debug_vn2hi9qy\04_numpy_result.png +[2025-10-24 00:42:46,272] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔧 후처리 시작: object_ratio=0.95 +[2025-10-24 00:42:46,272] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🎨 후처리 시작: 입력 이미지 크기 (1200, 816, 3), object_ratio=0.95 +[2025-10-24 00:42:46,273] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ BGR 이미지 처리: 그레이 < 250인 픽셀 수: 158674 +[2025-10-24 00:42:46,282] [LogListener] [DEBUG] [loggerModule.py:debug:275] 💾 5단계 저장: 초기 마스크 (158674개 픽셀) → C:\Users\ADMINI~1\AppData\Local\Temp\wrong_shape_rembg_debug_vn2hi9qy\05_initial_mask.png +[2025-10-24 00:42:46,283] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔧 마스크 정제: 정제 전 158674개 → 정제 후 168500개 픽셀 +[2025-10-24 00:42:46,285] [LogListener] [DEBUG] [loggerModule.py:debug:275] 💾 6단계 저장: 정제된 마스크 (168500개 픽셀) → C:\Users\ADMINI~1\AppData\Local\Temp\wrong_shape_rembg_debug_vn2hi9qy\06_refined_mask.png +[2025-10-24 00:42:46,286] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔍 연결 요소 분석: 1개 객체 발견 +[2025-10-24 00:42:46,288] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🎯 최대 연결 요소 선택: 168500개 픽셀 +[2025-10-24 00:42:46,290] [LogListener] [DEBUG] [loggerModule.py:debug:275] 💾 7단계 저장: 연결 요소 마스크 (168500개 픽셀) → C:\Users\ADMINI~1\AppData\Local\Temp\wrong_shape_rembg_debug_vn2hi9qy\07_component_mask.png +[2025-10-24 00:42:46,291] [LogListener] [DEBUG] [loggerModule.py:debug:275] 📐 바운딩 박스 계산: 유효 픽셀 168500개 +[2025-10-24 00:42:46,292] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✂️ 크롭 영역: (33,635) ~ (761,1179), 크기: (545, 729, 3) +[2025-10-24 00:42:46,297] [LogListener] [DEBUG] [loggerModule.py:debug:275] 💾 9단계 저장: 바운딩 박스 크롭 (545, 729, 3) → C:\Users\ADMINI~1\AppData\Local\Temp\wrong_shape_rembg_debug_vn2hi9qy\09_bbox_crop.png +[2025-10-24 00:42:46,298] [LogListener] [DEBUG] [loggerModule.py:debug:275] 📏 마진 추가: 7px, 최종 크기: (559, 743, 3) +[2025-10-24 00:42:46,304] [LogListener] [DEBUG] [loggerModule.py:debug:275] 💾 10단계 저장: 마진 추가 (559, 743, 3) → C:\Users\ADMINI~1\AppData\Local\Temp\wrong_shape_rembg_debug_vn2hi9qy\10_with_margin.png +[2025-10-24 00:42:46,304] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ 이미지 처리 완료 (이미 흰배경 합성됨): (559, 743, 3) +[2025-10-24 00:42:46,309] [LogListener] [DEBUG] [loggerModule.py:debug:275] 💾 11단계 저장: 흰 배경 합성 (559, 743, 3) → C:\Users\ADMINI~1\AppData\Local\Temp\wrong_shape_rembg_debug_vn2hi9qy\11_white_background.png +[2025-10-24 00:42:46,309] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🎯 1000x1000 리사이즈 시작: object_ratio=0.95, final_img.shape=(559, 743, 3) +[2025-10-24 00:42:46,309] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🎯 정사각형 캔버스 생성 시작: 입력=559x743, 목표=1000x1000, 객체비율=0.95 +[2025-10-24 00:42:46,310] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ 이미지 리사이즈 완료: 559x743 → 714x949 (scale_factor=1.279) +[2025-10-24 00:42:46,311] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ 정사각형 캔버스 생성: (1000, 1000, 3) +[2025-10-24 00:42:46,311] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ 이미지 배치 완료: offset=(25,143), size=(949,714) +[2025-10-24 00:42:46,316] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ 정사각형 캔버스 완성: 95% 스케일링, 1000x1000, DPI 72 +[2025-10-24 00:42:46,316] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ 1000x1000 리사이즈 완료: (1000, 1000, 3) +[2025-10-24 00:42:46,316] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ 백업 rembg 후처리 완료: (1000, 1000, 3) +[2025-10-24 00:42:46,326] [LogListener] [DEBUG] [loggerModule.py:debug:275] 💾 최종 결과 저장: (1000, 1000, 3) → C:\Users\ADMINI~1\AppData\Local\Temp\wrong_shape_rembg_debug_vn2hi9qy\99_final_result.png +[2025-10-24 00:42:46,326] [LogListener] [DEBUG] [loggerModule.py:debug:275] 임시 파일 삭제 완료: C:\Users\ADMINI~1\AppData\Local\Temp\rembg_31f07fc3.png +[2025-10-24 00:42:46,336] [LogListener] [DEBUG] [loggerModule.py:debug:275] 배경제거 이미지 저장: C:\ProgramData\ImgWorker\work\nobg_thumb_1.png +[2025-10-24 00:42:46,344] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔍 ONNX OCR 감지 방식: polygon +[2025-10-24 00:42:46,504] [LogListener] [DEBUG] [loggerModule.py:debug:275] ⚡ ONNX OCR 추론 완료: 159.6ms +[2025-10-24 00:42:46,528] [LogListener] [DEBUG] [loggerModule.py:debug:275] 📊 세부 시간 - 감지: 134.6ms, 인식: 20.0ms, 분류: 2.0ms +[2025-10-24 00:42:46,528] [LogListener] [WARNING] [loggerModule.py:warning:287] ⚠️ ONNX OCR 결과가 비어있습니다 +[2025-10-24 00:42:46,528] [LogListener] [DEBUG] [loggerModule.py:debug:275] OCR 결과 없음 - 정상 케이스 (NO_TEXT): C:\ProgramData\ImgWorker\work\nobg_thumb_1.png +[2025-10-24 00:42:46,528] [LogListener] [DEBUG] [loggerModule.py:debug:275] 필터링 결과: 0/0개 (신뢰도 + & 중국어) +[2025-10-24 00:42:46,528] [LogListener] [DEBUG] [loggerModule.py:debug:275] 중국어 텍스트 0개 필터링 완료 +[2025-10-24 00:42:46,528] [LogListener] [DEBUG] [loggerModule.py:debug:275] remove_background 호출 완료 +[2025-10-24 00:42:46,528] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 결과 반환 중: uid=94eb59f4-2250-486b-88af-27d30fc6c57c +[2025-10-24 00:42:46,529] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 결과 반환 완료: uid=94eb59f4-2250-486b-88af-27d30fc6c57c +[2025-10-24 00:42:46,529] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 00:42:46,529] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 13224) +[2025-10-24 00:43:41,951] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔥 작업 수신 성공 +[2025-10-24 00:43:41,951] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🚀 작업 처리 시작: cmd=process_single_image, uid=0f69b9e1-7f89-4a54-8985-241126b9b2a1 +[2025-10-24 00:43:41,951] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 프로세서 toggle_states 업데이트 : {'TEMP_IMAGE_DIR': 'C:\\ProgramData\\ImgWorker\\work', 'output_image_format': 'webp', 'use_local_rembg': True, 'local_rembg_model_path': 'D:\\py\\img_worker\\modules\\rembg_models', 'local_model_name': 'birefnet-general-lite', 'optionIMGTrans_type': 'GPU', 'detail_IMGTrans_type': 'GPU', 'thumb_trans_type': 'GPU', 'migan_use_accel': True, 'migan_onnx_path': 'D:\\py\\img_worker\\modules\\migan_onnx\\migan_pipeline_v2.onnx', 'ocr_provider_override': 'auto', 'migan_provider_override': 'auto'} +[2025-10-24 00:43:41,951] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 실행 직전: cmd=process_single_image +[2025-10-24 00:43:41,952] [LogListener] [DEBUG] [loggerModule.py:debug:275] process_single_image 호출 직전 +[2025-10-24 00:43:41,952] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 번역시작 +[2025-10-24 00:43:42,368] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 2 처리 시작: D:\py\img_worker\modules\test\1.jpg - 전체 번역 모드 +[2025-10-24 00:43:42,368] [LogListener] [DEBUG] [loggerModule.py:debug:275] 로컬 파일 경로 감지, 다운로드 생략: D:\py\img_worker\modules\test\1.jpg +[2025-10-24 00:43:42,369] [LogListener] [DEBUG] [loggerModule.py:debug:275] 상세페이지 이미지 2 원본 크기: 816x1200 +[2025-10-24 00:43:42,371] [LogListener] [DEBUG] [loggerModule.py:debug:275] 상세페이지 이미지 2 가로 크기 조정: 816x1200 → 860x1264 +[2025-10-24 00:43:42,379] [LogListener] [DEBUG] [loggerModule.py:debug:275] 상세페이지 이미지 2 크기 조정 완료: 860x1264 +[2025-10-24 00:43:42,379] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 2 로컬 저장위치(상세페이지 전처리 완료): D:\py\img_worker\modules\test\1_resized.jpg +[2025-10-24 00:43:42,382] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔍 ONNX OCR 감지 방식: polygon +[2025-10-24 00:43:42,541] [LogListener] [DEBUG] [loggerModule.py:debug:275] ⚡ ONNX OCR 추론 완료: 158.0ms +[2025-10-24 00:43:42,541] [LogListener] [DEBUG] [loggerModule.py:debug:275] 📊 세부 시간 - 감지: 83.0ms, 인식: 69.0ms, 분류: 4.0ms +[2025-10-24 00:43:42,551] [LogListener] [DEBUG] [loggerModule.py:debug:275] 메모리 변화 [OCR 처리]: 33892.8MB -> 33914.1MB (+21.2MB, +0.1%) - 이미지 2 +[2025-10-24 00:43:42,552] [LogListener] [DEBUG] [loggerModule.py:debug:275] ocr_results: [{'text': '现代极简风格', 'confidence': 0.9960710406303406, 'polygon': [[232.0, 72.0], [581.0, 72.0], [581.0, 122.0], [232.0, 122.0]], 'bbox': (232, 72, 350, 51), 'method': 'polygon'}, {'text': '更易搭配各种使用场景', 'confidence': 0.9952024221420288, 'polygon': [[160.0, 149.0], [656.0, 149.0], [656.0, 190.0], [160.0, 190.0]], 'bbox': (160, 149, 497, 42), 'method': 'polygon'}, {'text': '半圆两端设计', 'confidence': 0.9976122379302979, 'polygon': [[93.0, 304.0], [488.0, 304.0], [488.0, 363.0], [93.0, 363.0]], 'bbox': (93, 304, 396, 60), 'method': 'polygon'}, {'text': '承载各种欢乐', 'confidence': 0.9930798411369324, 'polygon': [[91.0, 388.0], [486.0, 388.0], [486.0, 448.0], [91.0, 448.0]], 'bbox': (91, 388, 396, 61), 'method': 'polygon'}] +[2025-10-24 00:43:42,552] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 2 OCR raw 데이터 메모리 저장 완료: 4개 텍스트 +[2025-10-24 00:43:42,552] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.6%): '现代极简风格' +[2025-10-24 00:43:42,552] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.5%): '更易搭配各种使用场景' +[2025-10-24 00:43:42,552] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.8%): '半圆两端设计' +[2025-10-24 00:43:42,552] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.3%): '承载各种欢乐' +[2025-10-24 00:43:42,552] [LogListener] [DEBUG] [loggerModule.py:debug:275] 필터링 결과: 4/4개 (신뢰도 + & 중국어) +[2025-10-24 00:43:42,553] [LogListener] [DEBUG] [loggerModule.py:debug:275] filter_ocr_results: [{'text': '现代极简风格', 'confidence': 0.9960710406303406, 'polygon': [[232.0, 72.0], [581.0, 72.0], [581.0, 122.0], [232.0, 122.0]], 'bbox': (232, 72, 350, 51), 'method': 'polygon'}, {'text': '更易搭配各种使用场景', 'confidence': 0.9952024221420288, 'polygon': [[160.0, 149.0], [656.0, 149.0], [656.0, 190.0], [160.0, 190.0]], 'bbox': (160, 149, 497, 42), 'method': 'polygon'}, {'text': '半圆两端设计', 'confidence': 0.9976122379302979, 'polygon': [[93.0, 304.0], [488.0, 304.0], [488.0, 363.0], [93.0, 363.0]], 'bbox': (93, 304, 396, 60), 'method': 'polygon'}, {'text': '承载各种欢乐', 'confidence': 0.9930798411369324, 'polygon': [[91.0, 388.0], [486.0, 388.0], [486.0, 448.0], [91.0, 448.0]], 'bbox': (91, 388, 396, 61), 'method': 'polygon'}] +[2025-10-24 00:43:42,553] [LogListener] [DEBUG] [loggerModule.py:debug:275] 중국어 텍스트 4개 필터링 완료 +[2025-10-24 00:43:42,553] [LogListener] [DEBUG] [loggerModule.py:debug:275] 한글 텍스트 0개 필터링 완료 +[2025-10-24 00:43:43,019] [LogListener] [DEBUG] [loggerModule.py:debug:275] translated_texts: ['모던한 미니멀 스타일', '다양한 사용 시나리오에 더 쉽게 일치', '반원형 끝 디자인', '온갖 기쁨을 안고'] +[2025-10-24 00:43:43,020] [LogListener] [DEBUG] [loggerModule.py:debug:275] 마스크 생성 완료 +[2025-10-24 00:43:43,020] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 2 OCR 권한 없음, 전체 번역 모드 +[2025-10-24 00:43:43,026] [LogListener] [DEBUG] [loggerModule.py:debug:275] [AUTO Inpaint] coverage=0.136, comps=2, min_center_dist=0.174 → request +[2025-10-24 00:43:43,027] [LogListener] [DEBUG] [loggerModule.py:debug:275] ocr_count: 4 +[2025-10-24 00:43:43,027] [LogListener] [DEBUG] [loggerModule.py:debug:275] is_member_valid: False +[2025-10-24 00:43:43,027] [LogListener] [DEBUG] [loggerModule.py:debug:275] [set_inpaint_method] prefix=detail, target_key=detail_IMGTrans_type, trans_type=GPU → inpaint_method=migan +[2025-10-24 00:43:43,027] [LogListener] [DEBUG] [loggerModule.py:debug:275] 최종 inpaint_method: migan +[2025-10-24 00:43:43,027] [LogListener] [DEBUG] [loggerModule.py:debug:275] MIGAN 인페인팅 시도 +[2025-10-24 00:43:43,030] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 입력 형태 - 이미지: (1, 3, 1264, 860), 마스크: (1, 1, 1264, 860) +[2025-10-24 00:43:43,356] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 출력 형태: (1264, 860, 3), dtype: uint8 +[2025-10-24 00:43:43,357] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 추론 완료: 325.66 ms +[2025-10-24 00:43:43,365] [LogListener] [DEBUG] [loggerModule.py:debug:275] MIGAN 인페인팅 성공 +[2025-10-24 00:43:43,365] [LogListener] [DEBUG] [loggerModule.py:debug:275] 메모리 변화 [인페인팅]: 33934.6MB -> 34152.7MB (+218.1MB, +0.6%) - 방법: migan +[2025-10-24 00:43:43,365] [LogListener] [DEBUG] [loggerModule.py:debug:275] 인페인팅 완료 +[2025-10-24 00:43:43,407] [LogListener] [DEBUG] [loggerModule.py:debug:275] 텍스트 렌더링 완료 +[2025-10-24 00:43:43,408] [LogListener] [DEBUG] [loggerModule.py:debug:275] watermark_text: 이미지 저작권 보유 +[2025-10-24 00:43:43,408] [LogListener] [DEBUG] [loggerModule.py:debug:275] is_watermark_enabled: True +[2025-10-24 00:43:43,717] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 저장 완료 : C:\ProgramData\ImgWorker\work\translated_detail_img_2.webp +[2025-10-24 00:43:43,718] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 2 번역 완료: C:\ProgramData\ImgWorker\work\translated_detail_img_2.webp +[2025-10-24 00:43:43,719] [LogListener] [DEBUG] [loggerModule.py:debug:275] ⏱ 이미지 파이프라인 총 1765.9ms | download=0.0ms | ocr=167.0ms | translate=468.0ms | mask=468.0ms | inpaint=343.7ms(migan/DirectML) | render=41.5ms | save=275.6ms +[2025-10-24 00:43:43,719] [LogListener] [DEBUG] [loggerModule.py:debug:275] process_single_image 호출 완료 +[2025-10-24 00:43:43,720] [ResultListener] [WARNING] [loggerModule.py:warning:287] 워커 롤링 스케줄: reason=job-count-threshold +[2025-10-24 00:43:43,720] [WorkerRoller] [WARNING] [loggerModule.py:warning:287] 워커 롤링 시작: job-count-threshold +[2025-10-24 00:43:43,720] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 결과 반환 중: uid=0f69b9e1-7f89-4a54-8985-241126b9b2a1 +[2025-10-24 00:43:43,720] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 결과 반환 완료: uid=0f69b9e1-7f89-4a54-8985-241126b9b2a1 +[2025-10-24 00:43:43,720] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 00:43:43,720] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 13224) +[2025-10-24 00:43:43,720] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔥 작업 수신 성공 +[2025-10-24 00:43:43,720] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🚀 작업 처리 시작: cmd=process_single_image, uid=e7cf1f9a-ad17-4b8c-9c51-8548de9cfd0f +[2025-10-24 00:43:43,720] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 프로세서 toggle_states 업데이트 : {'TEMP_IMAGE_DIR': 'C:\\ProgramData\\ImgWorker\\work', 'output_image_format': 'webp', 'use_local_rembg': True, 'local_rembg_model_path': 'D:\\py\\img_worker\\modules\\rembg_models', 'local_model_name': 'birefnet-general-lite', 'optionIMGTrans_type': 'GPU', 'detail_IMGTrans_type': 'GPU', 'thumb_trans_type': 'GPU', 'migan_use_accel': True, 'migan_onnx_path': 'D:\\py\\img_worker\\modules\\migan_onnx\\migan_pipeline_v2.onnx', 'ocr_provider_override': 'auto', 'migan_provider_override': 'auto'} +[2025-10-24 00:43:43,721] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 실행 직전: cmd=process_single_image +[2025-10-24 00:43:43,721] [LogListener] [DEBUG] [loggerModule.py:debug:275] process_single_image 호출 직전 +[2025-10-24 00:43:43,721] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 번역시작 +[2025-10-24 00:43:43,988] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 2 처리 시작: D:\py\img_worker\modules\test\2.jpg - 전체 번역 모드 +[2025-10-24 00:43:43,988] [LogListener] [DEBUG] [loggerModule.py:debug:275] 로컬 파일 경로 감지, 다운로드 생략: D:\py\img_worker\modules\test\2.jpg +[2025-10-24 00:43:43,990] [LogListener] [DEBUG] [loggerModule.py:debug:275] 상세페이지 이미지 2 원본 크기: 640x640 +[2025-10-24 00:43:43,991] [LogListener] [DEBUG] [loggerModule.py:debug:275] 상세페이지 이미지 2 가로 크기 조정: 640x640 → 860x860 +[2025-10-24 00:43:43,998] [LogListener] [DEBUG] [loggerModule.py:debug:275] 상세페이지 이미지 2 크기 조정 완료: 860x860 +[2025-10-24 00:43:43,998] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 2 로컬 저장위치(상세페이지 전처리 완료): D:\py\img_worker\modules\test\2_resized.jpg +[2025-10-24 00:43:44,001] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔍 ONNX OCR 감지 방식: polygon +[2025-10-24 00:43:44,915] [LogListener] [DEBUG] [loggerModule.py:debug:275] ⚡ ONNX OCR 추론 완료: 911.3ms +[2025-10-24 00:43:44,935] [LogListener] [DEBUG] [loggerModule.py:debug:275] 📊 세부 시간 - 감지: 114.0ms, 인식: 767.3ms, 분류: 23.0ms +[2025-10-24 00:43:44,945] [LogListener] [DEBUG] [loggerModule.py:debug:275] 메모리 변화 [OCR 처리]: 34166.8MB -> 34376.4MB (+209.7MB, +0.6%) - 이미지 2 +[2025-10-24 00:43:44,945] [LogListener] [DEBUG] [loggerModule.py:debug:275] ocr_results: [{'text': '科尔诺', 'confidence': 0.9992308616638184, 'polygon': [[96.0, 27.0], [211.0, 25.0], [212.0, 63.0], [97.0, 66.0]], 'bbox': (96, 25, 117, 42), 'method': 'polygon'}, {'text': 'PA', 'confidence': 0.9930515289306641, 'polygon': [[414.0, 28.0], [470.0, 32.0], [468.0, 59.0], [413.0, 55.0]], 'bbox': (413, 28, 58, 32), 'method': 'polygon'}, {'text': 'CE', 'confidence': 0.9594120979309082, 'polygon': [[665.0, 24.0], [728.0, 24.0], [728.0, 66.0], [665.0, 66.0]], 'bbox': (665, 24, 64, 43), 'method': 'polygon'}, {'text': 'SGS', 'confidence': 0.9917036890983582, 'polygon': [[751.0, 24.0], [821.0, 24.0], [821.0, 61.0], [751.0, 61.0]], 'bbox': (751, 24, 71, 38), 'method': 'polygon'}, {'text': 'CNEX', 'confidence': 0.9931035041809082, 'polygon': [[500.0, 34.0], [569.0, 34.0], [569.0, 55.0], [500.0, 55.0]], 'bbox': (500, 34, 70, 22), 'method': 'polygon'}, {'text': 'S', 'confidence': 0.8341087698936462, 'polygon': [[595.0, 37.0], [618.0, 37.0], [618.0, 48.0], [595.0, 48.0]], 'bbox': (595, 37, 24, 12), 'method': 'polygon'}, {'text': 'KORNO', 'confidence': 0.9965640306472778, 'polygon': [[103.0, 73.0], [211.0, 73.0], [211.0, 98.0], [103.0, 98.0]], 'bbox': (103, 73, 109, 26), 'method': 'polygon'}, {'text': 'CMC认证', 'confidence': 0.9940687417984009, 'polygon': [[321.0, 77.0], [389.0, 77.0], [389.0, 94.0], [321.0, 94.0]], 'bbox': (321, 77, 69, 18), 'method': 'polygon'}, {'text': 'CPA认证', 'confidence': 0.9973182678222656, 'polygon': [[412.0, 78.0], [475.0, 78.0], [475.0, 95.0], [412.0, 95.0]], 'bbox': (412, 78, 64, 18), 'method': 'polygon'}, {'text': '国家防爆', 'confidence': 0.9939618110656738, 'polygon': [[498.0, 79.0], [560.0, 79.0], [560.0, 96.0], [498.0, 96.0]], 'bbox': (498, 79, 63, 18), 'method': 'polygon'}, {'text': 'ISO认证', 'confidence': 0.9970961809158325, 'polygon': [[585.0, 78.0], [644.0, 78.0], [644.0, 95.0], [585.0, 95.0]], 'bbox': (585, 78, 60, 18), 'method': 'polygon'}, {'text': 'CE认证', 'confidence': 0.998458206653595, 'polygon': [[672.0, 77.0], [724.0, 77.0], [724.0, 95.0], [672.0, 95.0]], 'bbox': (672, 77, 53, 19), 'method': 'polygon'}, {'text': 'SGS认证', 'confidence': 0.9989465475082397, 'polygon': [[755.0, 78.0], [816.0, 78.0], [816.0, 96.0], [755.0, 96.0]], 'bbox': (755, 78, 62, 19), 'method': 'polygon'}, {'text': 'GT-1000', 'confidence': 0.9943559765815735, 'polygon': [[60.0, 132.0], [380.0, 135.0], [380.0, 196.0], [60.0, 194.0]], 'bbox': (60, 132, 321, 65), 'method': 'polygon'}, {'text': '激光粉尘检测仪', 'confidence': 0.9930281639099121, 'polygon': [[61.0, 223.0], [546.0, 223.0], [546.0, 284.0], [61.0, 284.0]], 'bbox': (61, 223, 486, 62), 'method': 'polygon'}, {'text': 'MO.5', 'confidence': 0.5840951204299927, 'polygon': [[663.0, 297.0], [685.0, 297.0], [685.0, 304.0], [663.0, 304.0]], 'bbox': (663, 297, 23, 8), 'method': 'polygon'}, {'text': '精度≤±5%F.S', 'confidence': 0.9796085357666016, 'polygon': [[40.0, 322.0], [291.0, 319.0], [292.0, 356.0], [40.0, 358.0]], 'bbox': (40, 319, 253, 40), 'method': 'polygon'}, {'text': 'PMLO', 'confidence': 0.825086236000061, 'polygon': [[659.0, 314.0], [686.0, 314.0], [686.0, 324.0], [659.0, 324.0]], 'bbox': (659, 314, 28, 11), 'method': 'polygon'}, {'text': 'PU2.5', 'confidence': 0.679347813129425, 'polygon': [[658.0, 331.0], [687.0, 331.0], [687.0, 341.0], [658.0, 341.0]], 'bbox': (658, 331, 30, 11), 'method': 'polygon'}, {'text': 'PLSO', 'confidence': 0.6640250086784363, 'polygon': [[658.0, 349.0], [687.0, 349.0], [687.0, 360.0], [658.0, 360.0]], 'bbox': (658, 349, 30, 12), 'method': 'polygon'}, {'text': 'P10', 'confidence': 0.6683804988861084, 'polygon': [[659.0, 368.0], [684.0, 368.0], [684.0, 379.0], [659.0, 379.0]], 'bbox': (659, 368, 26, 12), 'method': 'polygon'}, {'text': '防护等级:IP65', 'confidence': 0.9956157207489014, 'polygon': [[39.0, 399.0], [318.0, 396.0], [319.0, 433.0], [39.0, 436.0]], 'bbox': (39, 396, 281, 41), 'method': 'polygon'}, {'text': '过压保护/声光报警/存储打印', 'confidence': 0.9742894768714905, 'polygon': [[31.0, 478.0], [566.0, 478.0], [566.0, 512.0], [31.0, 512.0]], 'bbox': (31, 478, 536, 35), 'method': 'polygon'}, {'text': 'PM0.3/0.5/1.0/2.5/5.0/10um', 'confidence': 0.9763215780258179, 'polygon': [[29.0, 552.0], [572.0, 555.0], [572.0, 589.0], [29.0, 586.0]], 'bbox': (29, 552, 544, 38), 'method': 'polygon'}, {'text': '可同时监测多种粒径尘埃粒子数', 'confidence': 0.9920802116394043, 'polygon': [[42.0, 631.0], [497.0, 631.0], [497.0, 658.0], [42.0, 658.0]], 'bbox': (42, 631, 456, 28), 'method': 'polygon'}, {'text': '适合十万级以上洁净室', 'confidence': 0.9910793304443359, 'polygon': [[41.0, 679.0], [365.0, 679.0], [365.0, 706.0], [41.0, 706.0]], 'bbox': (41, 679, 325, 28), 'method': 'polygon'}, {'text': '全国', 'confidence': 0.9995953440666199, 'polygon': [[21.0, 720.0], [158.0, 720.0], [158.0, 791.0], [21.0, 791.0]], 'bbox': (21, 720, 138, 72), 'method': 'polygon'}, {'text': '7天无理由退货', 'confidence': 0.9959755539894104, 'polygon': [[263.0, 731.0], [524.0, 733.0], [523.0, 767.0], [263.0, 764.0]], 'bbox': (263, 731, 262, 37), 'method': 'polygon'}, {'text': '赠运险费', 'confidence': 0.9984970688819885, 'polygon': [[571.0, 731.0], [727.0, 731.0], [727.0, 768.0], [571.0, 768.0]], 'bbox': (571, 731, 157, 38), 'method': 'polygon'}, {'text': '包邮', 'confidence': 0.9996557235717773, 'polygon': [[22.0, 789.0], [161.0, 789.0], [161.0, 859.0], [22.0, 859.0]], 'bbox': (22, 789, 140, 71), 'method': 'polygon'}, {'text': '原厂正品/可开发票/质保一年', 'confidence': 0.9892104268074036, 'polygon': [[185.0, 789.0], [837.0, 787.0], [837.0, 828.0], [185.0, 830.0]], 'bbox': (185, 787, 653, 44), 'method': 'polygon'}] +[2025-10-24 00:43:44,946] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 2 OCR raw 데이터 메모리 저장 완료: 31개 텍스트 +[2025-10-24 00:43:44,946] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.9%): '科尔诺' +[2025-10-24 00:43:44,946] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): 'PA' +[2025-10-24 00:43:44,946] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): 'CE' +[2025-10-24 00:43:44,947] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): 'SGS' +[2025-10-24 00:43:44,947] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): 'CNEX' +[2025-10-24 00:43:44,947] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): 'S' +[2025-10-24 00:43:44,947] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): 'KORNO' +[2025-10-24 00:43:44,947] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.4%): 'CMC认证' +[2025-10-24 00:43:44,947] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.7%): 'CPA认证' +[2025-10-24 00:43:44,947] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.4%): '国家防爆' +[2025-10-24 00:43:44,947] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.7%): 'ISO认证' +[2025-10-24 00:43:44,947] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.8%): 'CE认证' +[2025-10-24 00:43:44,947] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.9%): 'SGS认证' +[2025-10-24 00:43:44,947] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): 'GT-1000' +[2025-10-24 00:43:44,947] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.3%): '激光粉尘检测仪' +[2025-10-24 00:43:44,947] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): 'MO.5' +[2025-10-24 00:43:44,947] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 98.0%): '精度≤±5%F.S' +[2025-10-24 00:43:44,948] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): 'PMLO' +[2025-10-24 00:43:44,948] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): 'PU2.5' +[2025-10-24 00:43:44,948] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): 'PLSO' +[2025-10-24 00:43:44,948] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): 'P10' +[2025-10-24 00:43:44,948] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.6%): '防护等级:IP65' +[2025-10-24 00:43:44,948] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 97.4%): '过压保护/声光报警/存储打印' +[2025-10-24 00:43:44,948] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): 'PM0.3/0.5/1.0/2.5/5.0/10um' +[2025-10-24 00:43:44,948] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.2%): '可同时监测多种粒径尘埃粒子数' +[2025-10-24 00:43:44,948] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.1%): '适合十万级以上洁净室' +[2025-10-24 00:43:44,948] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 100.0%): '全国' +[2025-10-24 00:43:44,948] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.6%): '7天无理由退货' +[2025-10-24 00:43:44,949] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.8%): '赠运险费' +[2025-10-24 00:43:44,949] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 100.0%): '包邮' +[2025-10-24 00:43:44,949] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 98.9%): '原厂正品/可开发票/质保一年' +[2025-10-24 00:43:44,949] [LogListener] [DEBUG] [loggerModule.py:debug:275] 필터링 결과: 18/31개 (신뢰도 + & 중국어) +[2025-10-24 00:43:44,949] [LogListener] [DEBUG] [loggerModule.py:debug:275] filter_ocr_results: [{'text': '科尔诺', 'confidence': 0.9992308616638184, 'polygon': [[96.0, 27.0], [211.0, 25.0], [212.0, 63.0], [97.0, 66.0]], 'bbox': (96, 25, 117, 42), 'method': 'polygon'}, {'text': 'CMC认证', 'confidence': 0.9940687417984009, 'polygon': [[321.0, 77.0], [389.0, 77.0], [389.0, 94.0], [321.0, 94.0]], 'bbox': (321, 77, 69, 18), 'method': 'polygon'}, {'text': 'CPA认证', 'confidence': 0.9973182678222656, 'polygon': [[412.0, 78.0], [475.0, 78.0], [475.0, 95.0], [412.0, 95.0]], 'bbox': (412, 78, 64, 18), 'method': 'polygon'}, {'text': '国家防爆', 'confidence': 0.9939618110656738, 'polygon': [[498.0, 79.0], [560.0, 79.0], [560.0, 96.0], [498.0, 96.0]], 'bbox': (498, 79, 63, 18), 'method': 'polygon'}, {'text': 'ISO认证', 'confidence': 0.9970961809158325, 'polygon': [[585.0, 78.0], [644.0, 78.0], [644.0, 95.0], [585.0, 95.0]], 'bbox': (585, 78, 60, 18), 'method': 'polygon'}, {'text': 'CE认证', 'confidence': 0.998458206653595, 'polygon': [[672.0, 77.0], [724.0, 77.0], [724.0, 95.0], [672.0, 95.0]], 'bbox': (672, 77, 53, 19), 'method': 'polygon'}, {'text': 'SGS认证', 'confidence': 0.9989465475082397, 'polygon': [[755.0, 78.0], [816.0, 78.0], [816.0, 96.0], [755.0, 96.0]], 'bbox': (755, 78, 62, 19), 'method': 'polygon'}, {'text': '激光粉尘检测仪', 'confidence': 0.9930281639099121, 'polygon': [[61.0, 223.0], [546.0, 223.0], [546.0, 284.0], [61.0, 284.0]], 'bbox': (61, 223, 486, 62), 'method': 'polygon'}, {'text': '精度≤±5%F.S', 'confidence': 0.9796085357666016, 'polygon': [[40.0, 322.0], [291.0, 319.0], [292.0, 356.0], [40.0, 358.0]], 'bbox': (40, 319, 253, 40), 'method': 'polygon'}, {'text': '防护等级:IP65', 'confidence': 0.9956157207489014, 'polygon': [[39.0, 399.0], [318.0, 396.0], [319.0, 433.0], [39.0, 436.0]], 'bbox': (39, 396, 281, 41), 'method': 'polygon'}, {'text': '过压保护/声光报警/存储打印', 'confidence': 0.9742894768714905, 'polygon': [[31.0, 478.0], [566.0, 478.0], [566.0, 512.0], [31.0, 512.0]], 'bbox': (31, 478, 536, 35), 'method': 'polygon'}, {'text': '可同时监测多种粒径尘埃粒子数', 'confidence': 0.9920802116394043, 'polygon': [[42.0, 631.0], [497.0, 631.0], [497.0, 658.0], [42.0, 658.0]], 'bbox': (42, 631, 456, 28), 'method': 'polygon'}, {'text': '适合十万级以上洁净室', 'confidence': 0.9910793304443359, 'polygon': [[41.0, 679.0], [365.0, 679.0], [365.0, 706.0], [41.0, 706.0]], 'bbox': (41, 679, 325, 28), 'method': 'polygon'}, {'text': '全国', 'confidence': 0.9995953440666199, 'polygon': [[21.0, 720.0], [158.0, 720.0], [158.0, 791.0], [21.0, 791.0]], 'bbox': (21, 720, 138, 72), 'method': 'polygon'}, {'text': '7天无理由退货', 'confidence': 0.9959755539894104, 'polygon': [[263.0, 731.0], [524.0, 733.0], [523.0, 767.0], [263.0, 764.0]], 'bbox': (263, 731, 262, 37), 'method': 'polygon'}, {'text': '赠运险费', 'confidence': 0.9984970688819885, 'polygon': [[571.0, 731.0], [727.0, 731.0], [727.0, 768.0], [571.0, 768.0]], 'bbox': (571, 731, 157, 38), 'method': 'polygon'}, {'text': '包邮', 'confidence': 0.9996557235717773, 'polygon': [[22.0, 789.0], [161.0, 789.0], [161.0, 859.0], [22.0, 859.0]], 'bbox': (22, 789, 140, 71), 'method': 'polygon'}, {'text': '原厂正品/可开发票/质保一年', 'confidence': 0.9892104268074036, 'polygon': [[185.0, 789.0], [837.0, 787.0], [837.0, 828.0], [185.0, 830.0]], 'bbox': (185, 787, 653, 44), 'method': 'polygon'}] +[2025-10-24 00:43:44,949] [LogListener] [DEBUG] [loggerModule.py:debug:275] 중국어 텍스트 18개 필터링 완료 +[2025-10-24 00:43:44,949] [LogListener] [DEBUG] [loggerModule.py:debug:275] 한글 텍스트 0개 필터링 완료 +[2025-10-24 00:43:47,471] [LogListener] [DEBUG] [loggerModule.py:debug:275] translated_texts: ['코르노', 'CMC 인증', 'CPA 자격증', '국가 방폭형', 'ISO 인증', 'CE 인증', 'SGS 인증', '레이저 먼지 감지기', '정확도≤±5%F.S', '보호 수준: IP65', '과전압 보호 / 소리와 빛 경보 / 매장 인쇄', '다양한 입자 크기의 먼지 입자 수를 동시에 모니터링 가능', '클래스 100,000 이상의 클린룸에 적합', '전국', '7일이면 반품할 이유가 없습니다.', '무료 배송 보험', '무료 배송', '오리지널 정품 / 청구 가능 / 1년 보증'] +[2025-10-24 00:43:47,471] [LogListener] [DEBUG] [loggerModule.py:debug:275] 마스크 생성 완료 +[2025-10-24 00:43:47,472] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 2 OCR 권한 없음, 전체 번역 모드 +[2025-10-24 00:43:47,479] [LogListener] [DEBUG] [loggerModule.py:debug:275] [AUTO Inpaint] coverage=0.350, comps=14, min_center_dist=0.064 → request +[2025-10-24 00:43:47,479] [LogListener] [DEBUG] [loggerModule.py:debug:275] ocr_count: 18 +[2025-10-24 00:43:47,479] [LogListener] [DEBUG] [loggerModule.py:debug:275] is_member_valid: False +[2025-10-24 00:43:47,479] [LogListener] [DEBUG] [loggerModule.py:debug:275] [set_inpaint_method] prefix=detail, target_key=detail_IMGTrans_type, trans_type=GPU → inpaint_method=migan +[2025-10-24 00:43:47,479] [LogListener] [DEBUG] [loggerModule.py:debug:275] 최종 inpaint_method: migan +[2025-10-24 00:43:47,479] [LogListener] [DEBUG] [loggerModule.py:debug:275] MIGAN 인페인팅 시도 +[2025-10-24 00:43:47,482] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 입력 형태 - 이미지: (1, 3, 860, 860), 마스크: (1, 1, 860, 860) +[2025-10-24 00:43:47,617] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 출력 형태: (860, 860, 3), dtype: uint8 +[2025-10-24 00:43:47,617] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 추론 완료: 133.66 ms +[2025-10-24 00:43:47,625] [LogListener] [DEBUG] [loggerModule.py:debug:275] MIGAN 인페인팅 성공 +[2025-10-24 00:43:47,625] [LogListener] [DEBUG] [loggerModule.py:debug:275] 메모리 변화 [인페인팅]: 31985.8MB -> 31993.5MB (+7.7MB, +0.0%) - 방법: migan +[2025-10-24 00:43:47,625] [LogListener] [DEBUG] [loggerModule.py:debug:275] 인페인팅 완료 +[2025-10-24 00:43:47,709] [LogListener] [DEBUG] [loggerModule.py:debug:275] 텍스트 렌더링 완료 +[2025-10-24 00:43:47,709] [LogListener] [DEBUG] [loggerModule.py:debug:275] watermark_text: 이미지 저작권 보유 +[2025-10-24 00:43:47,709] [LogListener] [DEBUG] [loggerModule.py:debug:275] is_watermark_enabled: True +[2025-10-24 00:43:47,940] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 저장 완료 : C:\ProgramData\ImgWorker\work\translated_detail_img_2.webp +[2025-10-24 00:43:47,941] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 2 번역 완료: C:\ProgramData\ImgWorker\work\translated_detail_img_2.webp +[2025-10-24 00:43:47,941] [LogListener] [DEBUG] [loggerModule.py:debug:275] ⏱ 이미지 파이프라인 총 4219.7ms | download=0.0ms | ocr=923.3ms | translate=2525.0ms | mask=2525.0ms | inpaint=150.7ms(migan/DirectML) | render=83.6ms | save=200.0ms +[2025-10-24 00:43:47,942] [LogListener] [DEBUG] [loggerModule.py:debug:275] process_single_image 호출 완료 +[2025-10-24 00:43:47,943] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 결과 반환 중: uid=e7cf1f9a-ad17-4b8c-9c51-8548de9cfd0f +[2025-10-24 00:43:47,943] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 결과 반환 완료: uid=e7cf1f9a-ad17-4b8c-9c51-8548de9cfd0f +[2025-10-24 00:43:47,943] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 00:43:47,943] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 13224) +[2025-10-24 00:43:47,943] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔥 작업 수신 성공 +[2025-10-24 00:43:47,943] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🚀 작업 처리 시작: cmd=process_single_image, uid=4506f4e0-2d3d-4f5a-9280-30d18d8994f7 +[2025-10-24 00:43:47,944] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 프로세서 toggle_states 업데이트 : {'TEMP_IMAGE_DIR': 'C:\\ProgramData\\ImgWorker\\work', 'output_image_format': 'webp', 'use_local_rembg': True, 'local_rembg_model_path': 'D:\\py\\img_worker\\modules\\rembg_models', 'local_model_name': 'birefnet-general-lite', 'optionIMGTrans_type': 'GPU', 'detail_IMGTrans_type': 'GPU', 'thumb_trans_type': 'GPU', 'migan_use_accel': True, 'migan_onnx_path': 'D:\\py\\img_worker\\modules\\migan_onnx\\migan_pipeline_v2.onnx', 'ocr_provider_override': 'auto', 'migan_provider_override': 'auto'} +[2025-10-24 00:43:47,944] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 실행 직전: cmd=process_single_image +[2025-10-24 00:43:47,944] [LogListener] [DEBUG] [loggerModule.py:debug:275] process_single_image 호출 직전 +[2025-10-24 00:43:47,944] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 번역시작 +[2025-10-24 00:43:48,136] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 3 처리 시작: D:\py\img_worker\modules\test\5.jpg - 전체 번역 모드 +[2025-10-24 00:43:48,136] [LogListener] [DEBUG] [loggerModule.py:debug:275] 로컬 파일 경로 감지, 다운로드 생략: D:\py\img_worker\modules\test\5.jpg +[2025-10-24 00:43:48,141] [LogListener] [DEBUG] [loggerModule.py:debug:275] 상세페이지 이미지 3 원본 크기: 1200x1857 +[2025-10-24 00:43:48,144] [LogListener] [DEBUG] [loggerModule.py:debug:275] 상세페이지 이미지 3 가로 크기 조정: 1200x1857 → 860x1330 +[2025-10-24 00:43:48,151] [LogListener] [DEBUG] [loggerModule.py:debug:275] 상세페이지 이미지 3 크기 조정 완료: 860x1330 +[2025-10-24 00:43:48,151] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 3 로컬 저장위치(상세페이지 전처리 완료): D:\py\img_worker\modules\test\5_resized.jpg +[2025-10-24 00:43:48,154] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔍 ONNX OCR 감지 방식: polygon +[2025-10-24 00:43:48,386] [LogListener] [DEBUG] [loggerModule.py:debug:275] ⚡ ONNX OCR 추론 완료: 230.0ms +[2025-10-24 00:43:48,391] [LogListener] [DEBUG] [loggerModule.py:debug:275] 📊 세부 시간 - 감지: 80.0ms, 인식: 142.0ms, 분류: 5.0ms +[2025-10-24 00:43:48,398] [LogListener] [DEBUG] [loggerModule.py:debug:275] 메모리 변화 [OCR 처리]: 31999.3MB -> 31994.1MB (-5.2MB, -0.0%) - 이미지 3 +[2025-10-24 00:43:48,399] [LogListener] [DEBUG] [loggerModule.py:debug:275] ocr_results: [{'text': '现代极简风格', 'confidence': 0.9961185455322266, 'polygon': [[245.0, 75.0], [614.0, 75.0], [614.0, 132.0], [245.0, 132.0]], 'bbox': (245, 75, 370, 58), 'method': 'polygon'}, {'text': '更易搭配各种使用场景', 'confidence': 0.9973940849304199, 'polygon': [[168.0, 157.0], [690.0, 157.0], [690.0, 200.0], [168.0, 200.0]], 'bbox': (168, 157, 523, 44), 'method': 'polygon'}, {'text': '★WELLOME', 'confidence': 0.8410268425941467, 'polygon': [[342.0, 521.0], [465.0, 521.0], [465.0, 546.0], [342.0, 546.0]], 'bbox': (342, 521, 124, 26), 'method': 'polygon'}, {'text': '欢迎光临', 'confidence': 0.9999395608901978, 'polygon': [[291.0, 544.0], [519.0, 544.0], [519.0, 612.0], [291.0, 612.0]], 'bbox': (291, 544, 229, 69), 'method': 'polygon'}, {'text': '聚时促销礼惠全城', 'confidence': 0.9192686676979065, 'polygon': [[339.0, 615.0], [447.0, 618.0], [446.0, 643.0], [338.0, 640.0]], 'bbox': (338, 615, 110, 29), 'method': 'polygon'}, {'text': '满499减200/满999减500', 'confidence': 0.9207867383956909, 'polygon': [[320.0, 678.0], [459.0, 689.0], [457.0, 713.0], [318.0, 702.0]], 'bbox': (318, 678, 142, 36), 'method': 'polygon'}, {'text': '活动的间6.1·6.7', 'confidence': 0.7784561514854431, 'polygon': [[345.0, 702.0], [426.0, 710.0], [425.0, 728.0], [343.0, 720.0]], 'bbox': (343, 702, 84, 27), 'method': 'polygon'}] +[2025-10-24 00:43:48,399] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 3 OCR raw 데이터 메모리 저장 완료: 7개 텍스트 +[2025-10-24 00:43:48,399] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.6%): '现代极简风格' +[2025-10-24 00:43:48,399] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.7%): '更易搭配各种使用场景' +[2025-10-24 00:43:48,399] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): '★WELLOME' +[2025-10-24 00:43:48,399] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 100.0%): '欢迎光临' +[2025-10-24 00:43:48,399] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 91.9%): '聚时促销礼惠全城' +[2025-10-24 00:43:48,399] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 92.1%): '满499减200/满999减500' +[2025-10-24 00:43:48,399] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 77.8%): '活动的间6.1·6.7' +[2025-10-24 00:43:48,399] [LogListener] [DEBUG] [loggerModule.py:debug:275] 필터링 결과: 6/7개 (신뢰도 + & 중국어) +[2025-10-24 00:43:48,399] [LogListener] [DEBUG] [loggerModule.py:debug:275] filter_ocr_results: [{'text': '现代极简风格', 'confidence': 0.9961185455322266, 'polygon': [[245.0, 75.0], [614.0, 75.0], [614.0, 132.0], [245.0, 132.0]], 'bbox': (245, 75, 370, 58), 'method': 'polygon'}, {'text': '更易搭配各种使用场景', 'confidence': 0.9973940849304199, 'polygon': [[168.0, 157.0], [690.0, 157.0], [690.0, 200.0], [168.0, 200.0]], 'bbox': (168, 157, 523, 44), 'method': 'polygon'}, {'text': '欢迎光临', 'confidence': 0.9999395608901978, 'polygon': [[291.0, 544.0], [519.0, 544.0], [519.0, 612.0], [291.0, 612.0]], 'bbox': (291, 544, 229, 69), 'method': 'polygon'}, {'text': '聚时促销礼惠全城', 'confidence': 0.9192686676979065, 'polygon': [[339.0, 615.0], [447.0, 618.0], [446.0, 643.0], [338.0, 640.0]], 'bbox': (338, 615, 110, 29), 'method': 'polygon'}, {'text': '满499减200/满999减500', 'confidence': 0.9207867383956909, 'polygon': [[320.0, 678.0], [459.0, 689.0], [457.0, 713.0], [318.0, 702.0]], 'bbox': (318, 678, 142, 36), 'method': 'polygon'}, {'text': '活动的间6.1·6.7', 'confidence': 0.7784561514854431, 'polygon': [[345.0, 702.0], [426.0, 710.0], [425.0, 728.0], [343.0, 720.0]], 'bbox': (343, 702, 84, 27), 'method': 'polygon'}] +[2025-10-24 00:43:48,400] [LogListener] [DEBUG] [loggerModule.py:debug:275] 중국어 텍스트 6개 필터링 완료 +[2025-10-24 00:43:48,400] [LogListener] [DEBUG] [loggerModule.py:debug:275] 한글 텍스트 0개 필터링 완료 +[2025-10-24 00:43:50,572] [LogListener] [DEBUG] [loggerModule.py:debug:275] translated_texts: ['모던한 미니멀 스타일', '다양한 사용 시나리오에 더 쉽게 일치', '환영', '도시 전역에서 판촉 선물 이용 가능', '499/500 이상 지출 시 200 할인 999 이상 지출 시 할인', '액티브룸 6.1 / 6.7'] +[2025-10-24 00:43:50,572] [LogListener] [DEBUG] [loggerModule.py:debug:275] 마스크 생성 완료 +[2025-10-24 00:43:50,572] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 3 OCR 권한 없음, 전체 번역 모드 +[2025-10-24 00:43:50,580] [LogListener] [DEBUG] [loggerModule.py:debug:275] [AUTO Inpaint] coverage=0.100, comps=3, min_center_dist=0.073 → request +[2025-10-24 00:43:50,580] [LogListener] [DEBUG] [loggerModule.py:debug:275] ocr_count: 6 +[2025-10-24 00:43:50,580] [LogListener] [DEBUG] [loggerModule.py:debug:275] is_member_valid: False +[2025-10-24 00:43:50,580] [LogListener] [DEBUG] [loggerModule.py:debug:275] [set_inpaint_method] prefix=detail, target_key=detail_IMGTrans_type, trans_type=GPU → inpaint_method=migan +[2025-10-24 00:43:50,581] [LogListener] [DEBUG] [loggerModule.py:debug:275] 최종 inpaint_method: migan +[2025-10-24 00:43:50,581] [LogListener] [DEBUG] [loggerModule.py:debug:275] MIGAN 인페인팅 시도 +[2025-10-24 00:43:50,584] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 입력 형태 - 이미지: (1, 3, 1330, 860), 마스크: (1, 1, 1330, 860) +[2025-10-24 00:43:50,670] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 출력 형태: (1330, 860, 3), dtype: uint8 +[2025-10-24 00:43:50,671] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 추론 완료: 87.00 ms +[2025-10-24 00:43:50,680] [LogListener] [DEBUG] [loggerModule.py:debug:275] MIGAN 인페인팅 성공 +[2025-10-24 00:43:50,680] [LogListener] [DEBUG] [loggerModule.py:debug:275] 메모리 변화 [인페인팅]: 32027.0MB -> 32047.0MB (+20.0MB, +0.1%) - 방법: migan +[2025-10-24 00:43:50,681] [LogListener] [DEBUG] [loggerModule.py:debug:275] 인페인팅 완료 +[2025-10-24 00:43:50,715] [LogListener] [DEBUG] [loggerModule.py:debug:275] 텍스트 렌더링 완료 +[2025-10-24 00:43:50,715] [LogListener] [DEBUG] [loggerModule.py:debug:275] watermark_text: 이미지 저작권 보유 +[2025-10-24 00:43:50,715] [LogListener] [DEBUG] [loggerModule.py:debug:275] is_watermark_enabled: True +[2025-10-24 00:43:51,033] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 저장 완료 : C:\ProgramData\ImgWorker\work\translated_detail_img_3.webp +[2025-10-24 00:43:51,034] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 3 번역 완료: C:\ProgramData\ImgWorker\work\translated_detail_img_3.webp +[2025-10-24 00:43:51,034] [LogListener] [DEBUG] [loggerModule.py:debug:275] ⏱ 이미지 파이프라인 총 3089.2ms | download=0.0ms | ocr=240.0ms | translate=2172.4ms | mask=2172.4ms | inpaint=106.0ms(migan/DirectML) | render=34.0ms | save=286.3ms +[2025-10-24 00:43:51,035] [LogListener] [DEBUG] [loggerModule.py:debug:275] process_single_image 호출 완료 +[2025-10-24 00:43:51,035] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 결과 반환 중: uid=4506f4e0-2d3d-4f5a-9280-30d18d8994f7 +[2025-10-24 00:43:51,083] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 결과 반환 완료: uid=4506f4e0-2d3d-4f5a-9280-30d18d8994f7 +[2025-10-24 00:43:51,084] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 00:43:51,084] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 13224) +[2025-10-24 00:43:51,084] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔥 작업 수신 성공 +[2025-10-24 00:43:51,084] [LogListener] [DEBUG] [loggerModule.py:debug:275] Shutdown signal 수신 → 종료 +[2025-10-24 00:43:51,084] [LogListener] [DEBUG] [loggerModule.py:debug:275] OCR 모듈 정리 완료 +[2025-10-24 00:43:51,084] [LogListener] [DEBUG] [loggerModule.py:debug:275] 마스크 모듈 정리 완료 +[2025-10-24 00:43:51,108] [LogListener] [DEBUG] [loggerModule.py:debug:275] 임시 폴더 삭제됨: C:\ProgramData\ImgWorker\work +[2025-10-24 00:43:51,109] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 프로세서 소멸 +[2025-10-24 00:43:52,318] [WorkerRoller] [INFO] [loggerModule.py:info:281] 워커 프로세스 기동: PID=29272 +[2025-10-24 00:43:52,748] [LogListener] [DEBUG] [loggerModule.py:debug:275] ImageWorker 프로세스 기동 (PID=29272, Name=ImageWorkerProcess) +[2025-10-24 00:43:52,748] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔧 ImageProcessor3 초기화 시작... +[2025-10-24 00:43:52,748] [LogListener] [DEBUG] [loggerModule.py:debug:275] tracemalloc 메모리 추적 시작 +[2025-10-24 00:43:52,755] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ DirectML 환경 준비 완료 (Windows DirectX 12 기반) +[2025-10-24 00:43:52,755] [LogListener] [DEBUG] [loggerModule.py:debug:275] === 🚀 DirectML GPU 상태 초기화 시작 🚀 === +[2025-10-24 00:43:52,755] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🎯 사용자 GPU 가속 요청: False +[2025-10-24 00:43:52,755] [LogListener] [DEBUG] [loggerModule.py:debug:275] 💻 현재 운영체제: Windows +[2025-10-24 00:43:52,755] [LogListener] [DEBUG] [loggerModule.py:debug:275] GPU 가속이 비활성화됨 (toggle_states['use_cuda'] = False) +[2025-10-24 00:43:52,756] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔒 안전한 CPU 모드로 모든 GPU 설정 강제 비활성화 +[2025-10-24 00:43:52,756] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔧 ImageProcessor3 GPU 상태 요약: +[2025-10-24 00:43:52,756] [LogListener] [DEBUG] [loggerModule.py:debug:275] - CUDA 사용 가능: False +[2025-10-24 00:43:52,756] [LogListener] [DEBUG] [loggerModule.py:debug:275] - toggle_states['use_cuda']: NOT_SET +[2025-10-24 00:43:52,756] [LogListener] [DEBUG] [loggerModule.py:debug:275] - GPU 하드웨어 정보: {} +[2025-10-24 00:43:52,756] [LogListener] [DEBUG] [loggerModule.py:debug:275] ImageProcessor3 Init toggle_states: {'TEMP_IMAGE_DIR': 'C:\\ProgramData\\ImgWorker\\work', 'output_image_format': 'webp', 'use_local_rembg': True, 'local_rembg_model_path': 'D:\\py\\img_worker\\modules\\rembg_models', 'local_model_name': 'birefnet-general-lite', 'optionIMGTrans_type': 'CPU', 'detail_IMGTrans_type': 'CPU', 'thumb_trans_type': 'CPU', 'migan_use_accel': True, 'migan_onnx_path': 'D:\\py\\img_worker\\modules\\migan_onnx\\migan_pipeline_v2.onnx', 'ocr_provider_override': 'auto', 'migan_provider_override': 'auto'} +[2025-10-24 00:43:52,756] [LogListener] [DEBUG] [loggerModule.py:debug:275] is_member_valid: False +[2025-10-24 00:43:52,756] [LogListener] [DEBUG] [loggerModule.py:debug:275] debug_images 디렉토리 이미 존재: D:\py\img_worker\modules\debug_images +[2025-10-24 00:43:52,757] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.font_path: D:\py\img_worker\modules\fonts\HakgyoansimDunggeunmisoTTFB.ttf +[2025-10-24 00:43:52,757] [LogListener] [DEBUG] [loggerModule.py:debug:275] toggle_states font_path: D:\py\img_worker\modules\fonts\HakgyoansimDunggeunmisoTTFB.ttf +[2025-10-24 00:43:52,757] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.TEMP_IMAGE_DIR: C:\ProgramData\ImgWorker\work +[2025-10-24 00:43:52,757] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.debugging_save_Dir: D:\py\img_worker\modules\debug_images +[2025-10-24 00:43:52,757] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.unwanted_texts: {} +[2025-10-24 00:43:52,757] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.inpaint_method: migan +[2025-10-24 00:43:52,757] [LogListener] [DEBUG] [loggerModule.py:debug:275] Image.MAX_IMAGE_PIXELS set to 20000000 +[2025-10-24 00:43:52,757] [LogListener] [DEBUG] [loggerModule.py:debug:275] ONNX 모델 타입 설정값: 자동 선택, GPU 정보: {} +[2025-10-24 00:43:52,757] [LogListener] [DEBUG] [loggerModule.py:debug:275] 자동 선택 모드: GPU 추천 모델 simp 사용 +[2025-10-24 00:43:52,757] [LogListener] [DEBUG] [loggerModule.py:debug:275] ONNX OCR 모델 타입 결정: simp (GPU: False) +[2025-10-24 00:43:52,758] [LogListener] [DEBUG] [loggerModule.py:debug:275] ONNX OCR 모듈 CPU 모드로 설정 +[2025-10-24 00:43:52,758] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🚀 ONNX TextSystem 초기화 시작 (CPU 모드) +[2025-10-24 00:43:52,758] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🚀 ONNX TextSystem 초기화 시작 (CPU 모드) +[2025-10-24 00:43:54,315] [LogListener] [DEBUG] [loggerModule.py:debug:275] 문자 사전 파일 UTF-8 확인 완료: D:\py\img_worker\modules\onnx_ocr_module\dict\ppocr_keys_v1.txt +[2025-10-24 00:43:54,401] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ ONNX TextSystem 초기화 완료 (CPU + SIMP (호환성) 모델) +[2025-10-24 00:43:54,401] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ ONNX TextSystem 초기화 완료 (CPU + SIMP 모델) +[2025-10-24 00:43:54,401] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ ONNX OCR 모듈 초기화 성공 (CPU 모드) +[2025-10-24 00:43:54,402] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ ONNX OCR 모듈 초기화 성공 +[2025-10-24 00:43:54,402] [LogListener] [DEBUG] [loggerModule.py:debug:275] 마스크 모듈 초기화 완료 +[2025-10-24 00:43:54,402] [LogListener] [DEBUG] [loggerModule.py:debug:275] MaskModule 초기화 성공 +[2025-10-24 00:43:54,402] [LogListener] [DEBUG] [loggerModule.py:debug:275] 텍스트 렌더링 모듈 초기화 완료 +[2025-10-24 00:43:54,402] [LogListener] [DEBUG] [loggerModule.py:debug:275] 기본 폰트: D:\py\img_worker\modules\fonts\HakgyoansimDunggeunmisoTTFB.ttf +[2025-10-24 00:43:54,402] [LogListener] [DEBUG] [loggerModule.py:debug:275] TextRenderingModule 초기화 성공 +[2025-10-24 00:43:54,402] [LogListener] [DEBUG] [loggerModule.py:debug:275] 폰트 로드 성공: D:\py\img_worker\modules\fonts\HakgyoansimDunggeunmisoTTFB.ttf +[2025-10-24 00:43:54,402] [LogListener] [DEBUG] [loggerModule.py:debug:275] PostImageManager 초기화 성공 +[2025-10-24 00:43:54,402] [LogListener] [DEBUG] [loggerModule.py:debug:275] Request_AI_Server GPU 상태: CUDA 사용 가능=False +[2025-10-24 00:43:54,402] [LogListener] [DEBUG] [loggerModule.py:debug:275] Request_AI_Server 초기화 성공 +[2025-10-24 00:43:54,403] [LogListener] [DEBUG] [loggerModule.py:debug:275] GoogleTranslate 초기화 성공 +[2025-10-24 00:43:54,403] [LogListener] [DEBUG] [loggerModule.py:debug:275] MIGAN CUDA 사용 불가 - CPU 모드로 설정 +[2025-10-24 00:43:54,403] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] GPU 관리자 전달: GPUManager, can_use_cuda: False +[2025-10-24 00:43:54,422] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] GPU 관리자 사용 가능: False +[2025-10-24 00:43:54,633] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 사용 가능한 providers: ['DmlExecutionProvider', 'CPUExecutionProvider'] +[2025-10-24 00:43:54,634] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] DirectML 가속 활성화 +[2025-10-24 00:43:54,634] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 최종 providers: [('DmlExecutionProvider', {}), ('CPUExecutionProvider', {})] +[2025-10-24 00:43:54,634] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] DirectML 가속 시도: [('DmlExecutionProvider', {}), ('CPUExecutionProvider', {})] +[2025-10-24 00:43:54,634] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] DirectML 가속 성공! 실제 providers: ['DmlExecutionProvider', 'CPUExecutionProvider'] +[2025-10-24 00:43:54,634] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 입력 0: image, 형태: ['batch_size', 3, 'height', 'width'], 타입: tensor(uint8) +[2025-10-24 00:43:54,635] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 입력 1: mask, 형태: ['batch_size', 1, 'height', 'width'], 타입: tensor(uint8) +[2025-10-24 00:43:54,635] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 출력 0: result, 형태: ['ScatterNDresult_dim_0', 3, 'ScatterNDresult_dim_2', 'ScatterNDresult_dim_3'], 타입: tensor(uint8) +[2025-10-24 00:43:54,635] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 세션 준비 완료. providers=['DmlExecutionProvider', 'CPUExecutionProvider'] +[2025-10-24 00:43:54,635] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] GPU 관리자 연결 완료: GPUManager +[2025-10-24 00:43:54,635] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 파이프라인 gpu_manager 속성: True, 값: +[2025-10-24 00:43:54,635] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 초기화 완료: gpu_manager 속성=True, 값= +[2025-10-24 00:43:54,635] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔰 OCR 모듈 Warm-up 시작... +[2025-10-24 00:43:54,635] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔍 ONNX OCR 감지 방식: polygon +[2025-10-24 00:43:54,640] [LogListener] [DEBUG] [loggerModule.py:debug:275] ⚡ ONNX OCR 추론 완료: 4.0ms +[2025-10-24 00:43:54,640] [LogListener] [DEBUG] [loggerModule.py:debug:275] 📊 세부 시간 - 감지: 3.0ms, 인식: 0.0ms, 분류: 0.0ms +[2025-10-24 00:43:54,640] [LogListener] [WARNING] [loggerModule.py:warning:287] ⚠️ ONNX OCR 결과가 비어있습니다 +[2025-10-24 00:43:54,643] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ OCR 모듈 Warm-up 성공 +[2025-10-24 00:43:54,643] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔰 ImageProcessor Warm‑up 완료 +[2025-10-24 00:43:54,644] [ResultListener] [INFO] [loggerModule.py:info:281] 워커 READY 수신 +[2025-10-24 00:43:54,644] [ResultListener] [INFO] [loggerModule.py:info:281] 워커 READY 수신 +[2025-10-24 00:43:54,644] [LogListener] [DEBUG] [loggerModule.py:debug:275] 📡 추가 READY 신호 전송 완료 +[2025-10-24 00:43:54,644] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 00:43:54,644] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 29272) +[2025-10-24 00:44:30,653] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔥 작업 수신 성공 +[2025-10-24 00:44:30,653] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🚀 작업 처리 시작: cmd=process_single_image, uid=825918eb-0f01-41dd-ae26-4b37c6b06865 +[2025-10-24 00:44:30,653] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 프로세서 toggle_states 업데이트 : {'TEMP_IMAGE_DIR': 'C:\\ProgramData\\ImgWorker\\work', 'output_image_format': 'webp', 'use_local_rembg': True, 'local_rembg_model_path': 'D:\\py\\img_worker\\modules\\rembg_models', 'local_model_name': 'birefnet-general-lite', 'optionIMGTrans_type': 'GPU', 'detail_IMGTrans_type': 'GPU', 'thumb_trans_type': 'GPU', 'migan_use_accel': True, 'migan_onnx_path': 'D:\\py\\img_worker\\modules\\migan_onnx\\migan_pipeline_v2.onnx', 'ocr_provider_override': 'auto', 'migan_provider_override': 'auto'} +[2025-10-24 00:44:30,653] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 실행 직전: cmd=process_single_image +[2025-10-24 00:44:30,653] [LogListener] [DEBUG] [loggerModule.py:debug:275] process_single_image 호출 직전 +[2025-10-24 00:44:30,655] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 번역시작 +[2025-10-24 00:44:30,966] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 2 처리 시작: D:\py\img_worker\modules\test\1.jpg - 전체 번역 모드 +[2025-10-24 00:44:30,966] [LogListener] [DEBUG] [loggerModule.py:debug:275] 로컬 파일 경로 감지, 다운로드 생략: D:\py\img_worker\modules\test\1.jpg +[2025-10-24 00:44:30,968] [LogListener] [DEBUG] [loggerModule.py:debug:275] 상세페이지 이미지 2 원본 크기: 816x1200 +[2025-10-24 00:44:30,973] [LogListener] [DEBUG] [loggerModule.py:debug:275] 상세페이지 이미지 2 가로 크기 조정: 816x1200 → 860x1264 +[2025-10-24 00:44:30,981] [LogListener] [DEBUG] [loggerModule.py:debug:275] 상세페이지 이미지 2 크기 조정 완료: 860x1264 +[2025-10-24 00:44:30,981] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 2 로컬 저장위치(상세페이지 전처리 완료): D:\py\img_worker\modules\test\1_resized.jpg +[2025-10-24 00:44:30,983] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔍 ONNX OCR 감지 방식: polygon +[2025-10-24 00:44:31,170] [LogListener] [DEBUG] [loggerModule.py:debug:275] ⚡ ONNX OCR 추론 완료: 186.0ms +[2025-10-24 00:44:31,171] [LogListener] [DEBUG] [loggerModule.py:debug:275] 📊 세부 시간 - 감지: 85.0ms, 인식: 93.0ms, 분류: 4.0ms +[2025-10-24 00:44:31,189] [LogListener] [DEBUG] [loggerModule.py:debug:275] 메모리 변화 [OCR 처리]: 31809.1MB -> 31964.8MB (+155.7MB, +0.5%) - 이미지 2 +[2025-10-24 00:44:31,210] [LogListener] [DEBUG] [loggerModule.py:debug:275] ocr_results: [{'text': '现代极简风格', 'confidence': 0.9960710406303406, 'polygon': [[232.0, 72.0], [581.0, 72.0], [581.0, 122.0], [232.0, 122.0]], 'bbox': (232, 72, 350, 51), 'method': 'polygon'}, {'text': '更易搭配各种使用场景', 'confidence': 0.9952024221420288, 'polygon': [[160.0, 149.0], [656.0, 149.0], [656.0, 190.0], [160.0, 190.0]], 'bbox': (160, 149, 497, 42), 'method': 'polygon'}, {'text': '半圆两端设计', 'confidence': 0.9976122379302979, 'polygon': [[93.0, 304.0], [488.0, 304.0], [488.0, 363.0], [93.0, 363.0]], 'bbox': (93, 304, 396, 60), 'method': 'polygon'}, {'text': '承载各种欢乐', 'confidence': 0.9930798411369324, 'polygon': [[91.0, 388.0], [486.0, 388.0], [486.0, 448.0], [91.0, 448.0]], 'bbox': (91, 388, 396, 61), 'method': 'polygon'}] +[2025-10-24 00:44:31,211] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 2 OCR raw 데이터 메모리 저장 완료: 4개 텍스트 +[2025-10-24 00:44:31,211] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.6%): '现代极简风格' +[2025-10-24 00:44:31,211] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.5%): '更易搭配各种使用场景' +[2025-10-24 00:44:31,211] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.8%): '半圆两端设计' +[2025-10-24 00:44:31,211] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.3%): '承载各种欢乐' +[2025-10-24 00:44:31,211] [LogListener] [DEBUG] [loggerModule.py:debug:275] 필터링 결과: 4/4개 (신뢰도 + & 중국어) +[2025-10-24 00:44:31,211] [LogListener] [DEBUG] [loggerModule.py:debug:275] filter_ocr_results: [{'text': '现代极简风格', 'confidence': 0.9960710406303406, 'polygon': [[232.0, 72.0], [581.0, 72.0], [581.0, 122.0], [232.0, 122.0]], 'bbox': (232, 72, 350, 51), 'method': 'polygon'}, {'text': '更易搭配各种使用场景', 'confidence': 0.9952024221420288, 'polygon': [[160.0, 149.0], [656.0, 149.0], [656.0, 190.0], [160.0, 190.0]], 'bbox': (160, 149, 497, 42), 'method': 'polygon'}, {'text': '半圆两端设计', 'confidence': 0.9976122379302979, 'polygon': [[93.0, 304.0], [488.0, 304.0], [488.0, 363.0], [93.0, 363.0]], 'bbox': (93, 304, 396, 60), 'method': 'polygon'}, {'text': '承载各种欢乐', 'confidence': 0.9930798411369324, 'polygon': [[91.0, 388.0], [486.0, 388.0], [486.0, 448.0], [91.0, 448.0]], 'bbox': (91, 388, 396, 61), 'method': 'polygon'}] +[2025-10-24 00:44:31,212] [LogListener] [DEBUG] [loggerModule.py:debug:275] 중국어 텍스트 4개 필터링 완료 +[2025-10-24 00:44:31,212] [LogListener] [DEBUG] [loggerModule.py:debug:275] 한글 텍스트 0개 필터링 완료 +[2025-10-24 00:44:31,595] [LogListener] [DEBUG] [loggerModule.py:debug:275] translated_texts: ['모던한 미니멀 스타일', '다양한 사용 시나리오에 더 쉽게 일치', '반원형 끝 디자인', '온갖 기쁨을 안고'] +[2025-10-24 00:44:31,595] [LogListener] [DEBUG] [loggerModule.py:debug:275] 마스크 생성 완료 +[2025-10-24 00:44:31,595] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 2 OCR 권한 없음, 전체 번역 모드 +[2025-10-24 00:44:31,601] [LogListener] [DEBUG] [loggerModule.py:debug:275] [AUTO Inpaint] coverage=0.136, comps=2, min_center_dist=0.174 → request +[2025-10-24 00:44:31,602] [LogListener] [DEBUG] [loggerModule.py:debug:275] ocr_count: 4 +[2025-10-24 00:44:31,602] [LogListener] [DEBUG] [loggerModule.py:debug:275] is_member_valid: False +[2025-10-24 00:44:31,602] [LogListener] [DEBUG] [loggerModule.py:debug:275] [set_inpaint_method] prefix=detail, target_key=detail_IMGTrans_type, trans_type=GPU → inpaint_method=migan +[2025-10-24 00:44:31,602] [LogListener] [DEBUG] [loggerModule.py:debug:275] 최종 inpaint_method: migan +[2025-10-24 00:44:31,602] [LogListener] [DEBUG] [loggerModule.py:debug:275] MIGAN 인페인팅 시도 +[2025-10-24 00:44:31,605] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 입력 형태 - 이미지: (1, 3, 1264, 860), 마스크: (1, 1, 1264, 860) +[2025-10-24 00:44:31,937] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 출력 형태: (1264, 860, 3), dtype: uint8 +[2025-10-24 00:44:31,937] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 추론 완료: 330.54 ms +[2025-10-24 00:44:31,946] [LogListener] [DEBUG] [loggerModule.py:debug:275] MIGAN 인페인팅 성공 +[2025-10-24 00:44:31,946] [LogListener] [DEBUG] [loggerModule.py:debug:275] 메모리 변화 [인페인팅]: 31946.1MB -> 32136.6MB (+190.6MB, +0.6%) - 방법: migan +[2025-10-24 00:44:31,947] [LogListener] [DEBUG] [loggerModule.py:debug:275] 인페인팅 완료 +[2025-10-24 00:44:31,992] [LogListener] [DEBUG] [loggerModule.py:debug:275] 텍스트 렌더링 완료 +[2025-10-24 00:44:31,993] [LogListener] [DEBUG] [loggerModule.py:debug:275] watermark_text: 이미지 저작권 보유 +[2025-10-24 00:44:31,993] [LogListener] [DEBUG] [loggerModule.py:debug:275] is_watermark_enabled: True +[2025-10-24 00:44:32,302] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 저장 완료 : C:\ProgramData\ImgWorker\work\translated_detail_img_2.webp +[2025-10-24 00:44:32,303] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 2 번역 완료: C:\ProgramData\ImgWorker\work\translated_detail_img_2.webp +[2025-10-24 00:44:32,304] [LogListener] [DEBUG] [loggerModule.py:debug:275] ⏱ 이미지 파이프라인 총 1649.5ms | download=0.0ms | ocr=195.0ms | translate=384.1ms | mask=384.1ms | inpaint=348.5ms(migan/DirectML) | render=46.8ms | save=280.0ms +[2025-10-24 00:44:32,305] [LogListener] [DEBUG] [loggerModule.py:debug:275] process_single_image 호출 완료 +[2025-10-24 00:44:32,305] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 결과 반환 중: uid=825918eb-0f01-41dd-ae26-4b37c6b06865 +[2025-10-24 00:44:32,305] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 결과 반환 완료: uid=825918eb-0f01-41dd-ae26-4b37c6b06865 +[2025-10-24 00:44:32,305] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 00:44:32,305] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 29272) +[2025-10-24 00:44:45,309] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔥 작업 수신 성공 +[2025-10-24 00:44:45,309] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🚀 작업 처리 시작: cmd=process_single_image, uid=195b346e-8af0-4212-9402-019454522bc9 +[2025-10-24 00:44:45,309] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 프로세서 toggle_states 업데이트 : {'TEMP_IMAGE_DIR': 'C:\\ProgramData\\ImgWorker\\work', 'output_image_format': 'webp', 'use_local_rembg': True, 'local_rembg_model_path': 'D:\\py\\img_worker\\modules\\rembg_models', 'local_model_name': 'birefnet-general-lite', 'optionIMGTrans_type': 'GPU', 'detail_IMGTrans_type': 'GPU', 'thumb_trans_type': 'GPU', 'migan_use_accel': True, 'migan_onnx_path': 'D:\\py\\img_worker\\modules\\migan_onnx\\migan_pipeline_v2.onnx', 'ocr_provider_override': 'auto', 'migan_provider_override': 'auto'} +[2025-10-24 00:44:45,309] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 실행 직전: cmd=process_single_image +[2025-10-24 00:44:45,309] [LogListener] [DEBUG] [loggerModule.py:debug:275] process_single_image 호출 직전 +[2025-10-24 00:44:45,309] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 번역시작 +[2025-10-24 00:44:45,768] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 2 처리 시작: D:\py\img_worker\modules\test\2.jpg - 전체 번역 모드 +[2025-10-24 00:44:45,768] [LogListener] [DEBUG] [loggerModule.py:debug:275] 로컬 파일 경로 감지, 다운로드 생략: D:\py\img_worker\modules\test\2.jpg +[2025-10-24 00:44:45,770] [LogListener] [DEBUG] [loggerModule.py:debug:275] 상세페이지 이미지 2 원본 크기: 640x640 +[2025-10-24 00:44:45,771] [LogListener] [DEBUG] [loggerModule.py:debug:275] 상세페이지 이미지 2 가로 크기 조정: 640x640 → 860x860 +[2025-10-24 00:44:45,778] [LogListener] [DEBUG] [loggerModule.py:debug:275] 상세페이지 이미지 2 크기 조정 완료: 860x860 +[2025-10-24 00:44:45,778] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 2 로컬 저장위치(상세페이지 전처리 완료): D:\py\img_worker\modules\test\2_resized.jpg +[2025-10-24 00:44:45,780] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔍 ONNX OCR 감지 방식: polygon +[2025-10-24 00:44:46,670] [LogListener] [DEBUG] [loggerModule.py:debug:275] ⚡ ONNX OCR 추론 완료: 885.4ms +[2025-10-24 00:44:46,689] [LogListener] [DEBUG] [loggerModule.py:debug:275] 📊 세부 시간 - 감지: 112.0ms, 인식: 743.4ms, 분류: 22.0ms +[2025-10-24 00:44:46,699] [LogListener] [DEBUG] [loggerModule.py:debug:275] 메모리 변화 [OCR 처리]: 32355.4MB -> 32550.0MB (+194.5MB, +0.6%) - 이미지 2 +[2025-10-24 00:44:46,699] [LogListener] [DEBUG] [loggerModule.py:debug:275] ocr_results: [{'text': '科尔诺', 'confidence': 0.9992308616638184, 'polygon': [[96.0, 27.0], [211.0, 25.0], [212.0, 63.0], [97.0, 66.0]], 'bbox': (96, 25, 117, 42), 'method': 'polygon'}, {'text': 'PA', 'confidence': 0.9930515289306641, 'polygon': [[414.0, 28.0], [470.0, 32.0], [468.0, 59.0], [413.0, 55.0]], 'bbox': (413, 28, 58, 32), 'method': 'polygon'}, {'text': 'CE', 'confidence': 0.9594120979309082, 'polygon': [[665.0, 24.0], [728.0, 24.0], [728.0, 66.0], [665.0, 66.0]], 'bbox': (665, 24, 64, 43), 'method': 'polygon'}, {'text': 'SGS', 'confidence': 0.9917036890983582, 'polygon': [[751.0, 24.0], [821.0, 24.0], [821.0, 61.0], [751.0, 61.0]], 'bbox': (751, 24, 71, 38), 'method': 'polygon'}, {'text': 'CNEX', 'confidence': 0.9931035041809082, 'polygon': [[500.0, 34.0], [569.0, 34.0], [569.0, 55.0], [500.0, 55.0]], 'bbox': (500, 34, 70, 22), 'method': 'polygon'}, {'text': 'S', 'confidence': 0.8341087698936462, 'polygon': [[595.0, 37.0], [618.0, 37.0], [618.0, 48.0], [595.0, 48.0]], 'bbox': (595, 37, 24, 12), 'method': 'polygon'}, {'text': 'KORNO', 'confidence': 0.9965640306472778, 'polygon': [[103.0, 73.0], [211.0, 73.0], [211.0, 98.0], [103.0, 98.0]], 'bbox': (103, 73, 109, 26), 'method': 'polygon'}, {'text': 'CMC认证', 'confidence': 0.9940687417984009, 'polygon': [[321.0, 77.0], [389.0, 77.0], [389.0, 94.0], [321.0, 94.0]], 'bbox': (321, 77, 69, 18), 'method': 'polygon'}, {'text': 'CPA认证', 'confidence': 0.9973182678222656, 'polygon': [[412.0, 78.0], [475.0, 78.0], [475.0, 95.0], [412.0, 95.0]], 'bbox': (412, 78, 64, 18), 'method': 'polygon'}, {'text': '国家防爆', 'confidence': 0.9939618110656738, 'polygon': [[498.0, 79.0], [560.0, 79.0], [560.0, 96.0], [498.0, 96.0]], 'bbox': (498, 79, 63, 18), 'method': 'polygon'}, {'text': 'ISO认证', 'confidence': 0.9970961809158325, 'polygon': [[585.0, 78.0], [644.0, 78.0], [644.0, 95.0], [585.0, 95.0]], 'bbox': (585, 78, 60, 18), 'method': 'polygon'}, {'text': 'CE认证', 'confidence': 0.998458206653595, 'polygon': [[672.0, 77.0], [724.0, 77.0], [724.0, 95.0], [672.0, 95.0]], 'bbox': (672, 77, 53, 19), 'method': 'polygon'}, {'text': 'SGS认证', 'confidence': 0.9989465475082397, 'polygon': [[755.0, 78.0], [816.0, 78.0], [816.0, 96.0], [755.0, 96.0]], 'bbox': (755, 78, 62, 19), 'method': 'polygon'}, {'text': 'GT-1000', 'confidence': 0.9943559765815735, 'polygon': [[60.0, 132.0], [380.0, 135.0], [380.0, 196.0], [60.0, 194.0]], 'bbox': (60, 132, 321, 65), 'method': 'polygon'}, {'text': '激光粉尘检测仪', 'confidence': 0.9930281639099121, 'polygon': [[61.0, 223.0], [546.0, 223.0], [546.0, 284.0], [61.0, 284.0]], 'bbox': (61, 223, 486, 62), 'method': 'polygon'}, {'text': 'MO.5', 'confidence': 0.5840951204299927, 'polygon': [[663.0, 297.0], [685.0, 297.0], [685.0, 304.0], [663.0, 304.0]], 'bbox': (663, 297, 23, 8), 'method': 'polygon'}, {'text': '精度≤±5%F.S', 'confidence': 0.9796085357666016, 'polygon': [[40.0, 322.0], [291.0, 319.0], [292.0, 356.0], [40.0, 358.0]], 'bbox': (40, 319, 253, 40), 'method': 'polygon'}, {'text': 'PMLO', 'confidence': 0.825086236000061, 'polygon': [[659.0, 314.0], [686.0, 314.0], [686.0, 324.0], [659.0, 324.0]], 'bbox': (659, 314, 28, 11), 'method': 'polygon'}, {'text': 'PU2.5', 'confidence': 0.679347813129425, 'polygon': [[658.0, 331.0], [687.0, 331.0], [687.0, 341.0], [658.0, 341.0]], 'bbox': (658, 331, 30, 11), 'method': 'polygon'}, {'text': 'PLSO', 'confidence': 0.6640250086784363, 'polygon': [[658.0, 349.0], [687.0, 349.0], [687.0, 360.0], [658.0, 360.0]], 'bbox': (658, 349, 30, 12), 'method': 'polygon'}, {'text': 'P10', 'confidence': 0.6683804988861084, 'polygon': [[659.0, 368.0], [684.0, 368.0], [684.0, 379.0], [659.0, 379.0]], 'bbox': (659, 368, 26, 12), 'method': 'polygon'}, {'text': '防护等级:IP65', 'confidence': 0.9956157207489014, 'polygon': [[39.0, 399.0], [318.0, 396.0], [319.0, 433.0], [39.0, 436.0]], 'bbox': (39, 396, 281, 41), 'method': 'polygon'}, {'text': '过压保护/声光报警/存储打印', 'confidence': 0.9742894768714905, 'polygon': [[31.0, 478.0], [566.0, 478.0], [566.0, 512.0], [31.0, 512.0]], 'bbox': (31, 478, 536, 35), 'method': 'polygon'}, {'text': 'PM0.3/0.5/1.0/2.5/5.0/10um', 'confidence': 0.9763215780258179, 'polygon': [[29.0, 552.0], [572.0, 555.0], [572.0, 589.0], [29.0, 586.0]], 'bbox': (29, 552, 544, 38), 'method': 'polygon'}, {'text': '可同时监测多种粒径尘埃粒子数', 'confidence': 0.9920802116394043, 'polygon': [[42.0, 631.0], [497.0, 631.0], [497.0, 658.0], [42.0, 658.0]], 'bbox': (42, 631, 456, 28), 'method': 'polygon'}, {'text': '适合十万级以上洁净室', 'confidence': 0.9910793304443359, 'polygon': [[41.0, 679.0], [365.0, 679.0], [365.0, 706.0], [41.0, 706.0]], 'bbox': (41, 679, 325, 28), 'method': 'polygon'}, {'text': '全国', 'confidence': 0.9995953440666199, 'polygon': [[21.0, 720.0], [158.0, 720.0], [158.0, 791.0], [21.0, 791.0]], 'bbox': (21, 720, 138, 72), 'method': 'polygon'}, {'text': '7天无理由退货', 'confidence': 0.9959755539894104, 'polygon': [[263.0, 731.0], [524.0, 733.0], [523.0, 767.0], [263.0, 764.0]], 'bbox': (263, 731, 262, 37), 'method': 'polygon'}, {'text': '赠运险费', 'confidence': 0.9984970688819885, 'polygon': [[571.0, 731.0], [727.0, 731.0], [727.0, 768.0], [571.0, 768.0]], 'bbox': (571, 731, 157, 38), 'method': 'polygon'}, {'text': '包邮', 'confidence': 0.9996557235717773, 'polygon': [[22.0, 789.0], [161.0, 789.0], [161.0, 859.0], [22.0, 859.0]], 'bbox': (22, 789, 140, 71), 'method': 'polygon'}, {'text': '原厂正品/可开发票/质保一年', 'confidence': 0.9892104268074036, 'polygon': [[185.0, 789.0], [837.0, 787.0], [837.0, 828.0], [185.0, 830.0]], 'bbox': (185, 787, 653, 44), 'method': 'polygon'}] +[2025-10-24 00:44:46,700] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 2 OCR raw 데이터 메모리 저장 완료: 31개 텍스트 +[2025-10-24 00:44:46,700] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.9%): '科尔诺' +[2025-10-24 00:44:46,700] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): 'PA' +[2025-10-24 00:44:46,700] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): 'CE' +[2025-10-24 00:44:46,700] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): 'SGS' +[2025-10-24 00:44:46,700] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): 'CNEX' +[2025-10-24 00:44:46,700] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): 'S' +[2025-10-24 00:44:46,700] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): 'KORNO' +[2025-10-24 00:44:46,700] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.4%): 'CMC认证' +[2025-10-24 00:44:46,700] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.7%): 'CPA认证' +[2025-10-24 00:44:46,701] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.4%): '国家防爆' +[2025-10-24 00:44:46,701] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.7%): 'ISO认证' +[2025-10-24 00:44:46,701] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.8%): 'CE认证' +[2025-10-24 00:44:46,701] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.9%): 'SGS认证' +[2025-10-24 00:44:46,701] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): 'GT-1000' +[2025-10-24 00:44:46,701] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.3%): '激光粉尘检测仪' +[2025-10-24 00:44:46,701] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): 'MO.5' +[2025-10-24 00:44:46,701] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 98.0%): '精度≤±5%F.S' +[2025-10-24 00:44:46,701] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): 'PMLO' +[2025-10-24 00:44:46,701] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): 'PU2.5' +[2025-10-24 00:44:46,701] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): 'PLSO' +[2025-10-24 00:44:46,701] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): 'P10' +[2025-10-24 00:44:46,701] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.6%): '防护等级:IP65' +[2025-10-24 00:44:46,701] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 97.4%): '过压保护/声光报警/存储打印' +[2025-10-24 00:44:46,701] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): 'PM0.3/0.5/1.0/2.5/5.0/10um' +[2025-10-24 00:44:46,701] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.2%): '可同时监测多种粒径尘埃粒子数' +[2025-10-24 00:44:46,702] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.1%): '适合十万级以上洁净室' +[2025-10-24 00:44:46,702] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 100.0%): '全国' +[2025-10-24 00:44:46,702] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.6%): '7天无理由退货' +[2025-10-24 00:44:46,702] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.8%): '赠运险费' +[2025-10-24 00:44:46,702] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 100.0%): '包邮' +[2025-10-24 00:44:46,702] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 98.9%): '原厂正品/可开发票/质保一年' +[2025-10-24 00:44:46,702] [LogListener] [DEBUG] [loggerModule.py:debug:275] 필터링 결과: 18/31개 (신뢰도 + & 중국어) +[2025-10-24 00:44:46,702] [LogListener] [DEBUG] [loggerModule.py:debug:275] filter_ocr_results: [{'text': '科尔诺', 'confidence': 0.9992308616638184, 'polygon': [[96.0, 27.0], [211.0, 25.0], [212.0, 63.0], [97.0, 66.0]], 'bbox': (96, 25, 117, 42), 'method': 'polygon'}, {'text': 'CMC认证', 'confidence': 0.9940687417984009, 'polygon': [[321.0, 77.0], [389.0, 77.0], [389.0, 94.0], [321.0, 94.0]], 'bbox': (321, 77, 69, 18), 'method': 'polygon'}, {'text': 'CPA认证', 'confidence': 0.9973182678222656, 'polygon': [[412.0, 78.0], [475.0, 78.0], [475.0, 95.0], [412.0, 95.0]], 'bbox': (412, 78, 64, 18), 'method': 'polygon'}, {'text': '国家防爆', 'confidence': 0.9939618110656738, 'polygon': [[498.0, 79.0], [560.0, 79.0], [560.0, 96.0], [498.0, 96.0]], 'bbox': (498, 79, 63, 18), 'method': 'polygon'}, {'text': 'ISO认证', 'confidence': 0.9970961809158325, 'polygon': [[585.0, 78.0], [644.0, 78.0], [644.0, 95.0], [585.0, 95.0]], 'bbox': (585, 78, 60, 18), 'method': 'polygon'}, {'text': 'CE认证', 'confidence': 0.998458206653595, 'polygon': [[672.0, 77.0], [724.0, 77.0], [724.0, 95.0], [672.0, 95.0]], 'bbox': (672, 77, 53, 19), 'method': 'polygon'}, {'text': 'SGS认证', 'confidence': 0.9989465475082397, 'polygon': [[755.0, 78.0], [816.0, 78.0], [816.0, 96.0], [755.0, 96.0]], 'bbox': (755, 78, 62, 19), 'method': 'polygon'}, {'text': '激光粉尘检测仪', 'confidence': 0.9930281639099121, 'polygon': [[61.0, 223.0], [546.0, 223.0], [546.0, 284.0], [61.0, 284.0]], 'bbox': (61, 223, 486, 62), 'method': 'polygon'}, {'text': '精度≤±5%F.S', 'confidence': 0.9796085357666016, 'polygon': [[40.0, 322.0], [291.0, 319.0], [292.0, 356.0], [40.0, 358.0]], 'bbox': (40, 319, 253, 40), 'method': 'polygon'}, {'text': '防护等级:IP65', 'confidence': 0.9956157207489014, 'polygon': [[39.0, 399.0], [318.0, 396.0], [319.0, 433.0], [39.0, 436.0]], 'bbox': (39, 396, 281, 41), 'method': 'polygon'}, {'text': '过压保护/声光报警/存储打印', 'confidence': 0.9742894768714905, 'polygon': [[31.0, 478.0], [566.0, 478.0], [566.0, 512.0], [31.0, 512.0]], 'bbox': (31, 478, 536, 35), 'method': 'polygon'}, {'text': '可同时监测多种粒径尘埃粒子数', 'confidence': 0.9920802116394043, 'polygon': [[42.0, 631.0], [497.0, 631.0], [497.0, 658.0], [42.0, 658.0]], 'bbox': (42, 631, 456, 28), 'method': 'polygon'}, {'text': '适合十万级以上洁净室', 'confidence': 0.9910793304443359, 'polygon': [[41.0, 679.0], [365.0, 679.0], [365.0, 706.0], [41.0, 706.0]], 'bbox': (41, 679, 325, 28), 'method': 'polygon'}, {'text': '全国', 'confidence': 0.9995953440666199, 'polygon': [[21.0, 720.0], [158.0, 720.0], [158.0, 791.0], [21.0, 791.0]], 'bbox': (21, 720, 138, 72), 'method': 'polygon'}, {'text': '7天无理由退货', 'confidence': 0.9959755539894104, 'polygon': [[263.0, 731.0], [524.0, 733.0], [523.0, 767.0], [263.0, 764.0]], 'bbox': (263, 731, 262, 37), 'method': 'polygon'}, {'text': '赠运险费', 'confidence': 0.9984970688819885, 'polygon': [[571.0, 731.0], [727.0, 731.0], [727.0, 768.0], [571.0, 768.0]], 'bbox': (571, 731, 157, 38), 'method': 'polygon'}, {'text': '包邮', 'confidence': 0.9996557235717773, 'polygon': [[22.0, 789.0], [161.0, 789.0], [161.0, 859.0], [22.0, 859.0]], 'bbox': (22, 789, 140, 71), 'method': 'polygon'}, {'text': '原厂正品/可开发票/质保一年', 'confidence': 0.9892104268074036, 'polygon': [[185.0, 789.0], [837.0, 787.0], [837.0, 828.0], [185.0, 830.0]], 'bbox': (185, 787, 653, 44), 'method': 'polygon'}] +[2025-10-24 00:44:46,702] [LogListener] [DEBUG] [loggerModule.py:debug:275] 중국어 텍스트 18개 필터링 완료 +[2025-10-24 00:44:46,702] [LogListener] [DEBUG] [loggerModule.py:debug:275] 한글 텍스트 0개 필터링 완료 +[2025-10-24 00:44:46,854] [LogListener] [DEBUG] [loggerModule.py:debug:275] translated_texts: ['코르노', 'CMC 인증', 'CPA 자격증', '국가 방폭형', 'ISO 인증', 'CE 인증', 'SGS 인증', '레이저 먼지 감지기', '정확도≤±5%F.S', '보호 수준: IP65', '과전압 보호 / 소리와 빛 경보 / 매장 인쇄', '다양한 입자 크기의 먼지 입자 수를 동시에 모니터링 가능', '클래스 100,000 이상의 클린룸에 적합', '전국', '7일이면 반품할 이유가 없습니다.', '무료 배송 보험', '무료 배송', '오리지널 정품 / 청구 가능 / 1년 보증'] +[2025-10-24 00:44:46,855] [LogListener] [DEBUG] [loggerModule.py:debug:275] 마스크 생성 완료 +[2025-10-24 00:44:46,855] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 2 OCR 권한 없음, 전체 번역 모드 +[2025-10-24 00:44:46,862] [LogListener] [DEBUG] [loggerModule.py:debug:275] [AUTO Inpaint] coverage=0.350, comps=14, min_center_dist=0.064 → request +[2025-10-24 00:44:46,862] [LogListener] [DEBUG] [loggerModule.py:debug:275] ocr_count: 18 +[2025-10-24 00:44:46,862] [LogListener] [DEBUG] [loggerModule.py:debug:275] is_member_valid: False +[2025-10-24 00:44:46,863] [LogListener] [DEBUG] [loggerModule.py:debug:275] [set_inpaint_method] prefix=detail, target_key=detail_IMGTrans_type, trans_type=GPU → inpaint_method=migan +[2025-10-24 00:44:46,863] [LogListener] [DEBUG] [loggerModule.py:debug:275] 최종 inpaint_method: migan +[2025-10-24 00:44:46,863] [LogListener] [DEBUG] [loggerModule.py:debug:275] MIGAN 인페인팅 시도 +[2025-10-24 00:44:46,865] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 입력 형태 - 이미지: (1, 3, 860, 860), 마스크: (1, 1, 860, 860) +[2025-10-24 00:44:46,949] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 출력 형태: (860, 860, 3), dtype: uint8 +[2025-10-24 00:44:46,949] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 추론 완료: 83.00 ms +[2025-10-24 00:44:46,956] [LogListener] [DEBUG] [loggerModule.py:debug:275] MIGAN 인페인팅 성공 +[2025-10-24 00:44:46,956] [LogListener] [DEBUG] [loggerModule.py:debug:275] 메모리 변화 [인페인팅]: 32532.8MB -> 32536.6MB (+3.8MB, +0.0%) - 방법: migan +[2025-10-24 00:44:46,957] [LogListener] [DEBUG] [loggerModule.py:debug:275] 인페인팅 완료 +[2025-10-24 00:44:47,040] [LogListener] [DEBUG] [loggerModule.py:debug:275] 텍스트 렌더링 완료 +[2025-10-24 00:44:47,040] [LogListener] [DEBUG] [loggerModule.py:debug:275] watermark_text: 이미지 저작권 보유 +[2025-10-24 00:44:47,040] [LogListener] [DEBUG] [loggerModule.py:debug:275] is_watermark_enabled: True +[2025-10-24 00:44:47,278] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 저장 완료 : C:\ProgramData\ImgWorker\work\translated_detail_img_2.webp +[2025-10-24 00:44:47,279] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 2 번역 완료: C:\ProgramData\ImgWorker\work\translated_detail_img_2.webp +[2025-10-24 00:44:47,279] [LogListener] [DEBUG] [loggerModule.py:debug:275] ⏱ 이미지 파이프라인 총 1968.8ms | download=0.0ms | ocr=896.4ms | translate=156.0ms | mask=156.0ms | inpaint=99.0ms(migan/DirectML) | render=83.5ms | save=201.5ms +[2025-10-24 00:44:47,280] [LogListener] [DEBUG] [loggerModule.py:debug:275] process_single_image 호출 완료 +[2025-10-24 00:44:47,280] [ResultListener] [WARNING] [loggerModule.py:warning:287] 워커 롤링 스케줄: reason=job-count-threshold +[2025-10-24 00:44:47,281] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 결과 반환 중: uid=195b346e-8af0-4212-9402-019454522bc9 +[2025-10-24 00:44:47,281] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 결과 반환 완료: uid=195b346e-8af0-4212-9402-019454522bc9 +[2025-10-24 00:44:47,281] [WorkerRoller] [WARNING] [loggerModule.py:warning:287] 워커 롤링 시작: job-count-threshold +[2025-10-24 00:44:47,281] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 00:44:47,281] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 29272) +[2025-10-24 00:44:47,298] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔥 작업 수신 성공 +[2025-10-24 00:44:47,331] [LogListener] [DEBUG] [loggerModule.py:debug:275] Shutdown signal 수신 → 종료 +[2025-10-24 00:44:47,332] [LogListener] [DEBUG] [loggerModule.py:debug:275] OCR 모듈 정리 완료 +[2025-10-24 00:44:47,332] [LogListener] [DEBUG] [loggerModule.py:debug:275] 마스크 모듈 정리 완료 +[2025-10-24 00:44:47,333] [LogListener] [DEBUG] [loggerModule.py:debug:275] 임시 폴더 삭제됨: C:\ProgramData\ImgWorker\work +[2025-10-24 00:44:47,333] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 프로세서 소멸 +[2025-10-24 00:44:48,510] [WorkerRoller] [INFO] [loggerModule.py:info:281] 워커 프로세스 기동: PID=10156 +[2025-10-24 00:44:48,945] [LogListener] [DEBUG] [loggerModule.py:debug:275] ImageWorker 프로세스 기동 (PID=10156, Name=ImageWorkerProcess) +[2025-10-24 00:44:48,945] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔧 ImageProcessor3 초기화 시작... +[2025-10-24 00:44:48,945] [LogListener] [DEBUG] [loggerModule.py:debug:275] tracemalloc 메모리 추적 시작 +[2025-10-24 00:44:48,951] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ DirectML 환경 준비 완료 (Windows DirectX 12 기반) +[2025-10-24 00:44:48,951] [LogListener] [DEBUG] [loggerModule.py:debug:275] === 🚀 DirectML GPU 상태 초기화 시작 🚀 === +[2025-10-24 00:44:48,952] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🎯 사용자 GPU 가속 요청: False +[2025-10-24 00:44:48,952] [LogListener] [DEBUG] [loggerModule.py:debug:275] 💻 현재 운영체제: Windows +[2025-10-24 00:44:48,952] [LogListener] [DEBUG] [loggerModule.py:debug:275] GPU 가속이 비활성화됨 (toggle_states['use_cuda'] = False) +[2025-10-24 00:44:48,952] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔒 안전한 CPU 모드로 모든 GPU 설정 강제 비활성화 +[2025-10-24 00:44:48,952] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔧 ImageProcessor3 GPU 상태 요약: +[2025-10-24 00:44:48,952] [LogListener] [DEBUG] [loggerModule.py:debug:275] - CUDA 사용 가능: False +[2025-10-24 00:44:48,952] [LogListener] [DEBUG] [loggerModule.py:debug:275] - toggle_states['use_cuda']: NOT_SET +[2025-10-24 00:44:48,952] [LogListener] [DEBUG] [loggerModule.py:debug:275] - GPU 하드웨어 정보: {} +[2025-10-24 00:44:48,952] [LogListener] [DEBUG] [loggerModule.py:debug:275] ImageProcessor3 Init toggle_states: {'TEMP_IMAGE_DIR': 'C:\\ProgramData\\ImgWorker\\work', 'output_image_format': 'webp', 'use_local_rembg': True, 'local_rembg_model_path': 'D:\\py\\img_worker\\modules\\rembg_models', 'local_model_name': 'birefnet-general-lite', 'optionIMGTrans_type': 'CPU', 'detail_IMGTrans_type': 'CPU', 'thumb_trans_type': 'CPU', 'migan_use_accel': True, 'migan_onnx_path': 'D:\\py\\img_worker\\modules\\migan_onnx\\migan_pipeline_v2.onnx', 'ocr_provider_override': 'auto', 'migan_provider_override': 'auto'} +[2025-10-24 00:44:48,952] [LogListener] [DEBUG] [loggerModule.py:debug:275] is_member_valid: False +[2025-10-24 00:44:48,952] [LogListener] [DEBUG] [loggerModule.py:debug:275] debug_images 디렉토리 이미 존재: D:\py\img_worker\modules\debug_images +[2025-10-24 00:44:48,953] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.font_path: D:\py\img_worker\modules\fonts\HakgyoansimDunggeunmisoTTFB.ttf +[2025-10-24 00:44:48,953] [LogListener] [DEBUG] [loggerModule.py:debug:275] toggle_states font_path: D:\py\img_worker\modules\fonts\HakgyoansimDunggeunmisoTTFB.ttf +[2025-10-24 00:44:48,953] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.TEMP_IMAGE_DIR: C:\ProgramData\ImgWorker\work +[2025-10-24 00:44:48,953] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.debugging_save_Dir: D:\py\img_worker\modules\debug_images +[2025-10-24 00:44:48,953] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.unwanted_texts: {} +[2025-10-24 00:44:48,953] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.inpaint_method: migan +[2025-10-24 00:44:48,953] [LogListener] [DEBUG] [loggerModule.py:debug:275] Image.MAX_IMAGE_PIXELS set to 20000000 +[2025-10-24 00:44:48,953] [LogListener] [DEBUG] [loggerModule.py:debug:275] ONNX 모델 타입 설정값: 자동 선택, GPU 정보: {} +[2025-10-24 00:44:48,953] [LogListener] [DEBUG] [loggerModule.py:debug:275] 자동 선택 모드: GPU 추천 모델 simp 사용 +[2025-10-24 00:44:48,953] [LogListener] [DEBUG] [loggerModule.py:debug:275] ONNX OCR 모델 타입 결정: simp (GPU: False) +[2025-10-24 00:44:48,953] [LogListener] [DEBUG] [loggerModule.py:debug:275] ONNX OCR 모듈 CPU 모드로 설정 +[2025-10-24 00:44:48,953] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🚀 ONNX TextSystem 초기화 시작 (CPU 모드) +[2025-10-24 00:44:48,953] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🚀 ONNX TextSystem 초기화 시작 (CPU 모드) +[2025-10-24 00:44:50,444] [LogListener] [DEBUG] [loggerModule.py:debug:275] 문자 사전 파일 UTF-8 확인 완료: D:\py\img_worker\modules\onnx_ocr_module\dict\ppocr_keys_v1.txt +[2025-10-24 00:44:50,527] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ ONNX TextSystem 초기화 완료 (CPU + SIMP (호환성) 모델) +[2025-10-24 00:44:50,527] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ ONNX TextSystem 초기화 완료 (CPU + SIMP 모델) +[2025-10-24 00:44:50,527] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ ONNX OCR 모듈 초기화 성공 (CPU 모드) +[2025-10-24 00:44:50,528] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ ONNX OCR 모듈 초기화 성공 +[2025-10-24 00:44:50,528] [LogListener] [DEBUG] [loggerModule.py:debug:275] 마스크 모듈 초기화 완료 +[2025-10-24 00:44:50,528] [LogListener] [DEBUG] [loggerModule.py:debug:275] MaskModule 초기화 성공 +[2025-10-24 00:44:50,528] [LogListener] [DEBUG] [loggerModule.py:debug:275] 텍스트 렌더링 모듈 초기화 완료 +[2025-10-24 00:44:50,528] [LogListener] [DEBUG] [loggerModule.py:debug:275] 기본 폰트: D:\py\img_worker\modules\fonts\HakgyoansimDunggeunmisoTTFB.ttf +[2025-10-24 00:44:50,529] [LogListener] [DEBUG] [loggerModule.py:debug:275] TextRenderingModule 초기화 성공 +[2025-10-24 00:44:50,529] [LogListener] [DEBUG] [loggerModule.py:debug:275] 폰트 로드 성공: D:\py\img_worker\modules\fonts\HakgyoansimDunggeunmisoTTFB.ttf +[2025-10-24 00:44:50,529] [LogListener] [DEBUG] [loggerModule.py:debug:275] PostImageManager 초기화 성공 +[2025-10-24 00:44:50,529] [LogListener] [DEBUG] [loggerModule.py:debug:275] Request_AI_Server GPU 상태: CUDA 사용 가능=False +[2025-10-24 00:44:50,529] [LogListener] [DEBUG] [loggerModule.py:debug:275] Request_AI_Server 초기화 성공 +[2025-10-24 00:44:50,529] [LogListener] [DEBUG] [loggerModule.py:debug:275] GoogleTranslate 초기화 성공 +[2025-10-24 00:44:50,529] [LogListener] [DEBUG] [loggerModule.py:debug:275] MIGAN CUDA 사용 불가 - CPU 모드로 설정 +[2025-10-24 00:44:50,529] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] GPU 관리자 전달: GPUManager, can_use_cuda: False +[2025-10-24 00:44:50,548] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] GPU 관리자 사용 가능: False +[2025-10-24 00:44:50,762] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 사용 가능한 providers: ['DmlExecutionProvider', 'CPUExecutionProvider'] +[2025-10-24 00:44:50,763] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] DirectML 가속 활성화 +[2025-10-24 00:44:50,763] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 최종 providers: [('DmlExecutionProvider', {}), ('CPUExecutionProvider', {})] +[2025-10-24 00:44:50,763] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] DirectML 가속 시도: [('DmlExecutionProvider', {}), ('CPUExecutionProvider', {})] +[2025-10-24 00:44:50,763] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] DirectML 가속 성공! 실제 providers: ['DmlExecutionProvider', 'CPUExecutionProvider'] +[2025-10-24 00:44:50,763] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 입력 0: image, 형태: ['batch_size', 3, 'height', 'width'], 타입: tensor(uint8) +[2025-10-24 00:44:50,764] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 입력 1: mask, 형태: ['batch_size', 1, 'height', 'width'], 타입: tensor(uint8) +[2025-10-24 00:44:50,764] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 출력 0: result, 형태: ['ScatterNDresult_dim_0', 3, 'ScatterNDresult_dim_2', 'ScatterNDresult_dim_3'], 타입: tensor(uint8) +[2025-10-24 00:44:50,764] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 세션 준비 완료. providers=['DmlExecutionProvider', 'CPUExecutionProvider'] +[2025-10-24 00:44:50,764] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] GPU 관리자 연결 완료: GPUManager +[2025-10-24 00:44:50,764] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 파이프라인 gpu_manager 속성: True, 값: +[2025-10-24 00:44:50,764] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 초기화 완료: gpu_manager 속성=True, 값= +[2025-10-24 00:44:50,764] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔰 OCR 모듈 Warm-up 시작... +[2025-10-24 00:44:50,764] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔍 ONNX OCR 감지 방식: polygon +[2025-10-24 00:44:50,768] [LogListener] [DEBUG] [loggerModule.py:debug:275] ⚡ ONNX OCR 추론 완료: 4.0ms +[2025-10-24 00:44:50,768] [LogListener] [DEBUG] [loggerModule.py:debug:275] 📊 세부 시간 - 감지: 3.0ms, 인식: 0.0ms, 분류: 0.0ms +[2025-10-24 00:44:50,769] [LogListener] [WARNING] [loggerModule.py:warning:287] ⚠️ ONNX OCR 결과가 비어있습니다 +[2025-10-24 00:44:50,772] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ OCR 모듈 Warm-up 성공 +[2025-10-24 00:44:50,772] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔰 ImageProcessor Warm‑up 완료 +[2025-10-24 00:44:50,772] [ResultListener] [INFO] [loggerModule.py:info:281] 워커 READY 수신 +[2025-10-24 00:44:50,773] [ResultListener] [INFO] [loggerModule.py:info:281] 워커 READY 수신 +[2025-10-24 00:44:50,773] [LogListener] [DEBUG] [loggerModule.py:debug:275] 📡 추가 READY 신호 전송 완료 +[2025-10-24 00:44:50,773] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 00:44:50,773] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 10156) +[2025-10-24 00:45:04,423] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔥 작업 수신 성공 +[2025-10-24 00:45:04,423] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🚀 작업 처리 시작: cmd=process_single_image, uid=a05535ea-073d-4dcc-8336-6e419eb2fa8f +[2025-10-24 00:45:04,424] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 프로세서 toggle_states 업데이트 : {'TEMP_IMAGE_DIR': 'C:\\ProgramData\\ImgWorker\\work', 'output_image_format': 'webp', 'use_local_rembg': True, 'local_rembg_model_path': 'D:\\py\\img_worker\\modules\\rembg_models', 'local_model_name': 'birefnet-general-lite', 'optionIMGTrans_type': 'GPU', 'detail_IMGTrans_type': 'GPU', 'thumb_trans_type': 'GPU', 'migan_use_accel': True, 'migan_onnx_path': 'D:\\py\\img_worker\\modules\\migan_onnx\\migan_pipeline_v2.onnx', 'ocr_provider_override': 'auto', 'migan_provider_override': 'auto'} +[2025-10-24 00:45:04,424] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 실행 직전: cmd=process_single_image +[2025-10-24 00:45:04,424] [LogListener] [DEBUG] [loggerModule.py:debug:275] process_single_image 호출 직전 +[2025-10-24 00:45:04,426] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 번역시작 +[2025-10-24 00:45:04,840] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 2 처리 시작: D:\py\img_worker\modules\test\2.jpg - 전체 번역 모드 +[2025-10-24 00:45:04,840] [LogListener] [DEBUG] [loggerModule.py:debug:275] 로컬 파일 경로 감지, 다운로드 생략: D:\py\img_worker\modules\test\2.jpg +[2025-10-24 00:45:04,841] [LogListener] [DEBUG] [loggerModule.py:debug:275] 상세페이지 이미지 2 원본 크기: 640x640 +[2025-10-24 00:45:04,845] [LogListener] [DEBUG] [loggerModule.py:debug:275] 상세페이지 이미지 2 가로 크기 조정: 640x640 → 860x860 +[2025-10-24 00:45:04,852] [LogListener] [DEBUG] [loggerModule.py:debug:275] 상세페이지 이미지 2 크기 조정 완료: 860x860 +[2025-10-24 00:45:04,852] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 2 로컬 저장위치(상세페이지 전처리 완료): D:\py\img_worker\modules\test\2_resized.jpg +[2025-10-24 00:45:04,855] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔍 ONNX OCR 감지 방식: polygon +[2025-10-24 00:45:05,797] [LogListener] [DEBUG] [loggerModule.py:debug:275] ⚡ ONNX OCR 추론 완료: 938.3ms +[2025-10-24 00:45:05,798] [LogListener] [DEBUG] [loggerModule.py:debug:275] 📊 세부 시간 - 감지: 129.0ms, 인식: 778.3ms, 분류: 24.0ms +[2025-10-24 00:45:05,821] [LogListener] [DEBUG] [loggerModule.py:debug:275] 메모리 변화 [OCR 처리]: 32436.8MB -> 32778.2MB (+341.4MB, +1.1%) - 이미지 2 +[2025-10-24 00:45:05,837] [LogListener] [DEBUG] [loggerModule.py:debug:275] ocr_results: [{'text': '科尔诺', 'confidence': 0.9992308616638184, 'polygon': [[96.0, 27.0], [211.0, 25.0], [212.0, 63.0], [97.0, 66.0]], 'bbox': (96, 25, 117, 42), 'method': 'polygon'}, {'text': 'PA', 'confidence': 0.9930515289306641, 'polygon': [[414.0, 28.0], [470.0, 32.0], [468.0, 59.0], [413.0, 55.0]], 'bbox': (413, 28, 58, 32), 'method': 'polygon'}, {'text': 'CE', 'confidence': 0.9594120979309082, 'polygon': [[665.0, 24.0], [728.0, 24.0], [728.0, 66.0], [665.0, 66.0]], 'bbox': (665, 24, 64, 43), 'method': 'polygon'}, {'text': 'SGS', 'confidence': 0.9917036890983582, 'polygon': [[751.0, 24.0], [821.0, 24.0], [821.0, 61.0], [751.0, 61.0]], 'bbox': (751, 24, 71, 38), 'method': 'polygon'}, {'text': 'CNEX', 'confidence': 0.9931035041809082, 'polygon': [[500.0, 34.0], [569.0, 34.0], [569.0, 55.0], [500.0, 55.0]], 'bbox': (500, 34, 70, 22), 'method': 'polygon'}, {'text': 'S', 'confidence': 0.8341087698936462, 'polygon': [[595.0, 37.0], [618.0, 37.0], [618.0, 48.0], [595.0, 48.0]], 'bbox': (595, 37, 24, 12), 'method': 'polygon'}, {'text': 'KORNO', 'confidence': 0.9965640306472778, 'polygon': [[103.0, 73.0], [211.0, 73.0], [211.0, 98.0], [103.0, 98.0]], 'bbox': (103, 73, 109, 26), 'method': 'polygon'}, {'text': 'CMC认证', 'confidence': 0.9940687417984009, 'polygon': [[321.0, 77.0], [389.0, 77.0], [389.0, 94.0], [321.0, 94.0]], 'bbox': (321, 77, 69, 18), 'method': 'polygon'}, {'text': 'CPA认证', 'confidence': 0.9973182678222656, 'polygon': [[412.0, 78.0], [475.0, 78.0], [475.0, 95.0], [412.0, 95.0]], 'bbox': (412, 78, 64, 18), 'method': 'polygon'}, {'text': '国家防爆', 'confidence': 0.9939618110656738, 'polygon': [[498.0, 79.0], [560.0, 79.0], [560.0, 96.0], [498.0, 96.0]], 'bbox': (498, 79, 63, 18), 'method': 'polygon'}, {'text': 'ISO认证', 'confidence': 0.9970961809158325, 'polygon': [[585.0, 78.0], [644.0, 78.0], [644.0, 95.0], [585.0, 95.0]], 'bbox': (585, 78, 60, 18), 'method': 'polygon'}, {'text': 'CE认证', 'confidence': 0.998458206653595, 'polygon': [[672.0, 77.0], [724.0, 77.0], [724.0, 95.0], [672.0, 95.0]], 'bbox': (672, 77, 53, 19), 'method': 'polygon'}, {'text': 'SGS认证', 'confidence': 0.9989465475082397, 'polygon': [[755.0, 78.0], [816.0, 78.0], [816.0, 96.0], [755.0, 96.0]], 'bbox': (755, 78, 62, 19), 'method': 'polygon'}, {'text': 'GT-1000', 'confidence': 0.9943559765815735, 'polygon': [[60.0, 132.0], [380.0, 135.0], [380.0, 196.0], [60.0, 194.0]], 'bbox': (60, 132, 321, 65), 'method': 'polygon'}, {'text': '激光粉尘检测仪', 'confidence': 0.9930281639099121, 'polygon': [[61.0, 223.0], [546.0, 223.0], [546.0, 284.0], [61.0, 284.0]], 'bbox': (61, 223, 486, 62), 'method': 'polygon'}, {'text': 'MO.5', 'confidence': 0.5840951204299927, 'polygon': [[663.0, 297.0], [685.0, 297.0], [685.0, 304.0], [663.0, 304.0]], 'bbox': (663, 297, 23, 8), 'method': 'polygon'}, {'text': '精度≤±5%F.S', 'confidence': 0.9796085357666016, 'polygon': [[40.0, 322.0], [291.0, 319.0], [292.0, 356.0], [40.0, 358.0]], 'bbox': (40, 319, 253, 40), 'method': 'polygon'}, {'text': 'PMLO', 'confidence': 0.825086236000061, 'polygon': [[659.0, 314.0], [686.0, 314.0], [686.0, 324.0], [659.0, 324.0]], 'bbox': (659, 314, 28, 11), 'method': 'polygon'}, {'text': 'PU2.5', 'confidence': 0.679347813129425, 'polygon': [[658.0, 331.0], [687.0, 331.0], [687.0, 341.0], [658.0, 341.0]], 'bbox': (658, 331, 30, 11), 'method': 'polygon'}, {'text': 'PLSO', 'confidence': 0.6640250086784363, 'polygon': [[658.0, 349.0], [687.0, 349.0], [687.0, 360.0], [658.0, 360.0]], 'bbox': (658, 349, 30, 12), 'method': 'polygon'}, {'text': 'P10', 'confidence': 0.6683804988861084, 'polygon': [[659.0, 368.0], [684.0, 368.0], [684.0, 379.0], [659.0, 379.0]], 'bbox': (659, 368, 26, 12), 'method': 'polygon'}, {'text': '防护等级:IP65', 'confidence': 0.9956157207489014, 'polygon': [[39.0, 399.0], [318.0, 396.0], [319.0, 433.0], [39.0, 436.0]], 'bbox': (39, 396, 281, 41), 'method': 'polygon'}, {'text': '过压保护/声光报警/存储打印', 'confidence': 0.9742894768714905, 'polygon': [[31.0, 478.0], [566.0, 478.0], [566.0, 512.0], [31.0, 512.0]], 'bbox': (31, 478, 536, 35), 'method': 'polygon'}, {'text': 'PM0.3/0.5/1.0/2.5/5.0/10um', 'confidence': 0.9763215780258179, 'polygon': [[29.0, 552.0], [572.0, 555.0], [572.0, 589.0], [29.0, 586.0]], 'bbox': (29, 552, 544, 38), 'method': 'polygon'}, {'text': '可同时监测多种粒径尘埃粒子数', 'confidence': 0.9920802116394043, 'polygon': [[42.0, 631.0], [497.0, 631.0], [497.0, 658.0], [42.0, 658.0]], 'bbox': (42, 631, 456, 28), 'method': 'polygon'}, {'text': '适合十万级以上洁净室', 'confidence': 0.9910793304443359, 'polygon': [[41.0, 679.0], [365.0, 679.0], [365.0, 706.0], [41.0, 706.0]], 'bbox': (41, 679, 325, 28), 'method': 'polygon'}, {'text': '全国', 'confidence': 0.9995953440666199, 'polygon': [[21.0, 720.0], [158.0, 720.0], [158.0, 791.0], [21.0, 791.0]], 'bbox': (21, 720, 138, 72), 'method': 'polygon'}, {'text': '7天无理由退货', 'confidence': 0.9959755539894104, 'polygon': [[263.0, 731.0], [524.0, 733.0], [523.0, 767.0], [263.0, 764.0]], 'bbox': (263, 731, 262, 37), 'method': 'polygon'}, {'text': '赠运险费', 'confidence': 0.9984970688819885, 'polygon': [[571.0, 731.0], [727.0, 731.0], [727.0, 768.0], [571.0, 768.0]], 'bbox': (571, 731, 157, 38), 'method': 'polygon'}, {'text': '包邮', 'confidence': 0.9996557235717773, 'polygon': [[22.0, 789.0], [161.0, 789.0], [161.0, 859.0], [22.0, 859.0]], 'bbox': (22, 789, 140, 71), 'method': 'polygon'}, {'text': '原厂正品/可开发票/质保一年', 'confidence': 0.9892104268074036, 'polygon': [[185.0, 789.0], [837.0, 787.0], [837.0, 828.0], [185.0, 830.0]], 'bbox': (185, 787, 653, 44), 'method': 'polygon'}] +[2025-10-24 00:45:05,849] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 2 OCR raw 데이터 메모리 저장 완료: 31개 텍스트 +[2025-10-24 00:45:05,849] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.9%): '科尔诺' +[2025-10-24 00:45:05,850] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): 'PA' +[2025-10-24 00:45:05,850] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): 'CE' +[2025-10-24 00:45:05,850] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): 'SGS' +[2025-10-24 00:45:05,850] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): 'CNEX' +[2025-10-24 00:45:05,850] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): 'S' +[2025-10-24 00:45:05,850] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): 'KORNO' +[2025-10-24 00:45:05,850] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.4%): 'CMC认证' +[2025-10-24 00:45:05,850] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.7%): 'CPA认证' +[2025-10-24 00:45:05,850] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.4%): '国家防爆' +[2025-10-24 00:45:05,850] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.7%): 'ISO认证' +[2025-10-24 00:45:05,850] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.8%): 'CE认证' +[2025-10-24 00:45:05,851] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.9%): 'SGS认证' +[2025-10-24 00:45:05,851] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): 'GT-1000' +[2025-10-24 00:45:05,851] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.3%): '激光粉尘检测仪' +[2025-10-24 00:45:05,851] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): 'MO.5' +[2025-10-24 00:45:05,851] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 98.0%): '精度≤±5%F.S' +[2025-10-24 00:45:05,851] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): 'PMLO' +[2025-10-24 00:45:05,851] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): 'PU2.5' +[2025-10-24 00:45:05,851] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): 'PLSO' +[2025-10-24 00:45:05,851] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): 'P10' +[2025-10-24 00:45:05,851] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.6%): '防护等级:IP65' +[2025-10-24 00:45:05,851] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 97.4%): '过压保护/声光报警/存储打印' +[2025-10-24 00:45:05,851] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): 'PM0.3/0.5/1.0/2.5/5.0/10um' +[2025-10-24 00:45:05,851] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.2%): '可同时监测多种粒径尘埃粒子数' +[2025-10-24 00:45:05,851] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.1%): '适合十万级以上洁净室' +[2025-10-24 00:45:05,851] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 100.0%): '全国' +[2025-10-24 00:45:05,851] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.6%): '7天无理由退货' +[2025-10-24 00:45:05,852] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.8%): '赠运险费' +[2025-10-24 00:45:05,852] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 100.0%): '包邮' +[2025-10-24 00:45:05,852] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 98.9%): '原厂正品/可开发票/质保一年' +[2025-10-24 00:45:05,852] [LogListener] [DEBUG] [loggerModule.py:debug:275] 필터링 결과: 18/31개 (신뢰도 + & 중국어) +[2025-10-24 00:45:05,852] [LogListener] [DEBUG] [loggerModule.py:debug:275] filter_ocr_results: [{'text': '科尔诺', 'confidence': 0.9992308616638184, 'polygon': [[96.0, 27.0], [211.0, 25.0], [212.0, 63.0], [97.0, 66.0]], 'bbox': (96, 25, 117, 42), 'method': 'polygon'}, {'text': 'CMC认证', 'confidence': 0.9940687417984009, 'polygon': [[321.0, 77.0], [389.0, 77.0], [389.0, 94.0], [321.0, 94.0]], 'bbox': (321, 77, 69, 18), 'method': 'polygon'}, {'text': 'CPA认证', 'confidence': 0.9973182678222656, 'polygon': [[412.0, 78.0], [475.0, 78.0], [475.0, 95.0], [412.0, 95.0]], 'bbox': (412, 78, 64, 18), 'method': 'polygon'}, {'text': '国家防爆', 'confidence': 0.9939618110656738, 'polygon': [[498.0, 79.0], [560.0, 79.0], [560.0, 96.0], [498.0, 96.0]], 'bbox': (498, 79, 63, 18), 'method': 'polygon'}, {'text': 'ISO认证', 'confidence': 0.9970961809158325, 'polygon': [[585.0, 78.0], [644.0, 78.0], [644.0, 95.0], [585.0, 95.0]], 'bbox': (585, 78, 60, 18), 'method': 'polygon'}, {'text': 'CE认证', 'confidence': 0.998458206653595, 'polygon': [[672.0, 77.0], [724.0, 77.0], [724.0, 95.0], [672.0, 95.0]], 'bbox': (672, 77, 53, 19), 'method': 'polygon'}, {'text': 'SGS认证', 'confidence': 0.9989465475082397, 'polygon': [[755.0, 78.0], [816.0, 78.0], [816.0, 96.0], [755.0, 96.0]], 'bbox': (755, 78, 62, 19), 'method': 'polygon'}, {'text': '激光粉尘检测仪', 'confidence': 0.9930281639099121, 'polygon': [[61.0, 223.0], [546.0, 223.0], [546.0, 284.0], [61.0, 284.0]], 'bbox': (61, 223, 486, 62), 'method': 'polygon'}, {'text': '精度≤±5%F.S', 'confidence': 0.9796085357666016, 'polygon': [[40.0, 322.0], [291.0, 319.0], [292.0, 356.0], [40.0, 358.0]], 'bbox': (40, 319, 253, 40), 'method': 'polygon'}, {'text': '防护等级:IP65', 'confidence': 0.9956157207489014, 'polygon': [[39.0, 399.0], [318.0, 396.0], [319.0, 433.0], [39.0, 436.0]], 'bbox': (39, 396, 281, 41), 'method': 'polygon'}, {'text': '过压保护/声光报警/存储打印', 'confidence': 0.9742894768714905, 'polygon': [[31.0, 478.0], [566.0, 478.0], [566.0, 512.0], [31.0, 512.0]], 'bbox': (31, 478, 536, 35), 'method': 'polygon'}, {'text': '可同时监测多种粒径尘埃粒子数', 'confidence': 0.9920802116394043, 'polygon': [[42.0, 631.0], [497.0, 631.0], [497.0, 658.0], [42.0, 658.0]], 'bbox': (42, 631, 456, 28), 'method': 'polygon'}, {'text': '适合十万级以上洁净室', 'confidence': 0.9910793304443359, 'polygon': [[41.0, 679.0], [365.0, 679.0], [365.0, 706.0], [41.0, 706.0]], 'bbox': (41, 679, 325, 28), 'method': 'polygon'}, {'text': '全国', 'confidence': 0.9995953440666199, 'polygon': [[21.0, 720.0], [158.0, 720.0], [158.0, 791.0], [21.0, 791.0]], 'bbox': (21, 720, 138, 72), 'method': 'polygon'}, {'text': '7天无理由退货', 'confidence': 0.9959755539894104, 'polygon': [[263.0, 731.0], [524.0, 733.0], [523.0, 767.0], [263.0, 764.0]], 'bbox': (263, 731, 262, 37), 'method': 'polygon'}, {'text': '赠运险费', 'confidence': 0.9984970688819885, 'polygon': [[571.0, 731.0], [727.0, 731.0], [727.0, 768.0], [571.0, 768.0]], 'bbox': (571, 731, 157, 38), 'method': 'polygon'}, {'text': '包邮', 'confidence': 0.9996557235717773, 'polygon': [[22.0, 789.0], [161.0, 789.0], [161.0, 859.0], [22.0, 859.0]], 'bbox': (22, 789, 140, 71), 'method': 'polygon'}, {'text': '原厂正品/可开发票/质保一年', 'confidence': 0.9892104268074036, 'polygon': [[185.0, 789.0], [837.0, 787.0], [837.0, 828.0], [185.0, 830.0]], 'bbox': (185, 787, 653, 44), 'method': 'polygon'}] +[2025-10-24 00:45:05,886] [LogListener] [DEBUG] [loggerModule.py:debug:275] 중국어 텍스트 18개 필터링 완료 +[2025-10-24 00:45:05,887] [LogListener] [DEBUG] [loggerModule.py:debug:275] 한글 텍스트 0개 필터링 완료 +[2025-10-24 00:45:06,352] [LogListener] [DEBUG] [loggerModule.py:debug:275] translated_texts: ['코르노', 'CMC 인증', 'CPA 자격증', '국가 방폭형', 'ISO 인증', 'CE 인증', 'SGS 인증', '레이저 먼지 감지기', '정확도≤±5%F.S', '보호 수준: IP65', '과전압 보호 / 소리와 빛 경보 / 매장 인쇄', '다양한 입자 크기의 먼지 입자 수를 동시에 모니터링 가능', '클래스 100,000 이상의 클린룸에 적합', '전국', '7일이면 반품할 이유가 없습니다.', '무료 배송 보험', '무료 배송', '오리지널 정품 / 청구 가능 / 1년 보증'] +[2025-10-24 00:45:06,352] [LogListener] [DEBUG] [loggerModule.py:debug:275] 마스크 생성 완료 +[2025-10-24 00:45:06,352] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 2 OCR 권한 없음, 전체 번역 모드 +[2025-10-24 00:45:06,360] [LogListener] [DEBUG] [loggerModule.py:debug:275] [AUTO Inpaint] coverage=0.350, comps=14, min_center_dist=0.064 → request +[2025-10-24 00:45:06,360] [LogListener] [DEBUG] [loggerModule.py:debug:275] ocr_count: 18 +[2025-10-24 00:45:06,360] [LogListener] [DEBUG] [loggerModule.py:debug:275] is_member_valid: False +[2025-10-24 00:45:06,360] [LogListener] [DEBUG] [loggerModule.py:debug:275] [set_inpaint_method] prefix=detail, target_key=detail_IMGTrans_type, trans_type=GPU → inpaint_method=migan +[2025-10-24 00:45:06,360] [LogListener] [DEBUG] [loggerModule.py:debug:275] 최종 inpaint_method: migan +[2025-10-24 00:45:06,360] [LogListener] [DEBUG] [loggerModule.py:debug:275] MIGAN 인페인팅 시도 +[2025-10-24 00:45:06,363] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 입력 형태 - 이미지: (1, 3, 860, 860), 마스크: (1, 1, 860, 860) +[2025-10-24 00:45:06,928] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 출력 형태: (860, 860, 3), dtype: uint8 +[2025-10-24 00:45:06,928] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 추론 완료: 564.81 ms +[2025-10-24 00:45:06,935] [LogListener] [DEBUG] [loggerModule.py:debug:275] MIGAN 인페인팅 성공 +[2025-10-24 00:45:06,935] [LogListener] [DEBUG] [loggerModule.py:debug:275] 메모리 변화 [인페인팅]: 32807.7MB -> 33003.4MB (+195.7MB, +0.6%) - 방법: migan +[2025-10-24 00:45:06,935] [LogListener] [DEBUG] [loggerModule.py:debug:275] 인페인팅 완료 +[2025-10-24 00:45:07,030] [LogListener] [DEBUG] [loggerModule.py:debug:275] 텍스트 렌더링 완료 +[2025-10-24 00:45:07,031] [LogListener] [DEBUG] [loggerModule.py:debug:275] watermark_text: 이미지 저작권 보유 +[2025-10-24 00:45:07,031] [LogListener] [DEBUG] [loggerModule.py:debug:275] is_watermark_enabled: True +[2025-10-24 00:45:07,262] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 저장 완료 : C:\ProgramData\ImgWorker\work\translated_detail_img_2.webp +[2025-10-24 00:45:07,263] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 2 번역 완료: C:\ProgramData\ImgWorker\work\translated_detail_img_2.webp +[2025-10-24 00:45:07,263] [LogListener] [DEBUG] [loggerModule.py:debug:275] ⏱ 이미지 파이프라인 총 2837.4ms | download=0.0ms | ocr=950.3ms | translate=502.0ms | mask=502.0ms | inpaint=580.8ms(migan/DirectML) | render=94.0ms | save=200.0ms +[2025-10-24 00:45:07,264] [LogListener] [DEBUG] [loggerModule.py:debug:275] process_single_image 호출 완료 +[2025-10-24 00:45:07,264] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 결과 반환 중: uid=a05535ea-073d-4dcc-8336-6e419eb2fa8f +[2025-10-24 00:45:07,264] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 결과 반환 완료: uid=a05535ea-073d-4dcc-8336-6e419eb2fa8f +[2025-10-24 00:45:07,264] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 00:45:07,264] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 10156) +[2025-10-24 00:45:07,264] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔥 작업 수신 성공 +[2025-10-24 00:45:07,265] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🚀 작업 처리 시작: cmd=process_single_image, uid=024282c9-a0fa-43a7-9392-55af0d636164 +[2025-10-24 00:45:07,265] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 프로세서 toggle_states 업데이트 : {'TEMP_IMAGE_DIR': 'C:\\ProgramData\\ImgWorker\\work', 'output_image_format': 'webp', 'use_local_rembg': True, 'local_rembg_model_path': 'D:\\py\\img_worker\\modules\\rembg_models', 'local_model_name': 'birefnet-general-lite', 'optionIMGTrans_type': 'GPU', 'detail_IMGTrans_type': 'GPU', 'thumb_trans_type': 'GPU', 'migan_use_accel': True, 'migan_onnx_path': 'D:\\py\\img_worker\\modules\\migan_onnx\\migan_pipeline_v2.onnx', 'ocr_provider_override': 'auto', 'migan_provider_override': 'auto'} +[2025-10-24 00:45:07,265] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 실행 직전: cmd=process_single_image +[2025-10-24 00:45:07,265] [LogListener] [DEBUG] [loggerModule.py:debug:275] process_single_image 호출 직전 +[2025-10-24 00:45:07,265] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 번역시작 +[2025-10-24 00:45:07,595] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 2 처리 시작: D:\py\img_worker\modules\test\1.jpg - 전체 번역 모드 +[2025-10-24 00:45:07,595] [LogListener] [DEBUG] [loggerModule.py:debug:275] 로컬 파일 경로 감지, 다운로드 생략: D:\py\img_worker\modules\test\1.jpg +[2025-10-24 00:45:07,597] [LogListener] [DEBUG] [loggerModule.py:debug:275] 상세페이지 이미지 2 원본 크기: 816x1200 +[2025-10-24 00:45:07,599] [LogListener] [DEBUG] [loggerModule.py:debug:275] 상세페이지 이미지 2 가로 크기 조정: 816x1200 → 860x1264 +[2025-10-24 00:45:07,606] [LogListener] [DEBUG] [loggerModule.py:debug:275] 상세페이지 이미지 2 크기 조정 완료: 860x1264 +[2025-10-24 00:45:07,606] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 2 로컬 저장위치(상세페이지 전처리 완료): D:\py\img_worker\modules\test\1_resized.jpg +[2025-10-24 00:45:07,609] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔍 ONNX OCR 감지 방식: polygon +[2025-10-24 00:45:07,776] [LogListener] [DEBUG] [loggerModule.py:debug:275] ⚡ ONNX OCR 추론 완료: 166.0ms +[2025-10-24 00:45:07,783] [LogListener] [DEBUG] [loggerModule.py:debug:275] 📊 세부 시간 - 감지: 88.0ms, 인식: 72.0ms, 분류: 3.0ms +[2025-10-24 00:45:07,786] [LogListener] [DEBUG] [loggerModule.py:debug:275] 메모리 변화 [OCR 처리]: 33063.3MB -> 33061.1MB (-2.2MB, -0.0%) - 이미지 2 +[2025-10-24 00:45:07,786] [LogListener] [DEBUG] [loggerModule.py:debug:275] ocr_results: [{'text': '现代极简风格', 'confidence': 0.9960710406303406, 'polygon': [[232.0, 72.0], [581.0, 72.0], [581.0, 122.0], [232.0, 122.0]], 'bbox': (232, 72, 350, 51), 'method': 'polygon'}, {'text': '更易搭配各种使用场景', 'confidence': 0.9952024221420288, 'polygon': [[160.0, 149.0], [656.0, 149.0], [656.0, 190.0], [160.0, 190.0]], 'bbox': (160, 149, 497, 42), 'method': 'polygon'}, {'text': '半圆两端设计', 'confidence': 0.9976122379302979, 'polygon': [[93.0, 304.0], [488.0, 304.0], [488.0, 363.0], [93.0, 363.0]], 'bbox': (93, 304, 396, 60), 'method': 'polygon'}, {'text': '承载各种欢乐', 'confidence': 0.9930798411369324, 'polygon': [[91.0, 388.0], [486.0, 388.0], [486.0, 448.0], [91.0, 448.0]], 'bbox': (91, 388, 396, 61), 'method': 'polygon'}] +[2025-10-24 00:45:07,786] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 2 OCR raw 데이터 메모리 저장 완료: 4개 텍스트 +[2025-10-24 00:45:07,786] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.6%): '现代极简风格' +[2025-10-24 00:45:07,786] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.5%): '更易搭配各种使用场景' +[2025-10-24 00:45:07,786] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.8%): '半圆两端设计' +[2025-10-24 00:45:07,787] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.3%): '承载各种欢乐' +[2025-10-24 00:45:07,787] [LogListener] [DEBUG] [loggerModule.py:debug:275] 필터링 결과: 4/4개 (신뢰도 + & 중국어) +[2025-10-24 00:45:07,787] [LogListener] [DEBUG] [loggerModule.py:debug:275] filter_ocr_results: [{'text': '现代极简风格', 'confidence': 0.9960710406303406, 'polygon': [[232.0, 72.0], [581.0, 72.0], [581.0, 122.0], [232.0, 122.0]], 'bbox': (232, 72, 350, 51), 'method': 'polygon'}, {'text': '更易搭配各种使用场景', 'confidence': 0.9952024221420288, 'polygon': [[160.0, 149.0], [656.0, 149.0], [656.0, 190.0], [160.0, 190.0]], 'bbox': (160, 149, 497, 42), 'method': 'polygon'}, {'text': '半圆两端设计', 'confidence': 0.9976122379302979, 'polygon': [[93.0, 304.0], [488.0, 304.0], [488.0, 363.0], [93.0, 363.0]], 'bbox': (93, 304, 396, 60), 'method': 'polygon'}, {'text': '承载各种欢乐', 'confidence': 0.9930798411369324, 'polygon': [[91.0, 388.0], [486.0, 388.0], [486.0, 448.0], [91.0, 448.0]], 'bbox': (91, 388, 396, 61), 'method': 'polygon'}] +[2025-10-24 00:45:07,787] [LogListener] [DEBUG] [loggerModule.py:debug:275] 중국어 텍스트 4개 필터링 완료 +[2025-10-24 00:45:07,787] [LogListener] [DEBUG] [loggerModule.py:debug:275] 한글 텍스트 0개 필터링 완료 +[2025-10-24 00:45:07,939] [LogListener] [DEBUG] [loggerModule.py:debug:275] translated_texts: ['모던한 미니멀 스타일', '다양한 사용 시나리오에 더 쉽게 일치', '반원형 끝 디자인', '온갖 기쁨을 안고'] +[2025-10-24 00:45:07,939] [LogListener] [DEBUG] [loggerModule.py:debug:275] 마스크 생성 완료 +[2025-10-24 00:45:07,939] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 2 OCR 권한 없음, 전체 번역 모드 +[2025-10-24 00:45:07,945] [LogListener] [DEBUG] [loggerModule.py:debug:275] [AUTO Inpaint] coverage=0.136, comps=2, min_center_dist=0.174 → request +[2025-10-24 00:45:07,946] [LogListener] [DEBUG] [loggerModule.py:debug:275] ocr_count: 4 +[2025-10-24 00:45:07,946] [LogListener] [DEBUG] [loggerModule.py:debug:275] is_member_valid: False +[2025-10-24 00:45:07,946] [LogListener] [DEBUG] [loggerModule.py:debug:275] [set_inpaint_method] prefix=detail, target_key=detail_IMGTrans_type, trans_type=GPU → inpaint_method=migan +[2025-10-24 00:45:07,946] [LogListener] [DEBUG] [loggerModule.py:debug:275] 최종 inpaint_method: migan +[2025-10-24 00:45:07,946] [LogListener] [DEBUG] [loggerModule.py:debug:275] MIGAN 인페인팅 시도 +[2025-10-24 00:45:07,949] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 입력 형태 - 이미지: (1, 3, 1264, 860), 마스크: (1, 1, 1264, 860) +[2025-10-24 00:45:08,030] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 출력 형태: (1264, 860, 3), dtype: uint8 +[2025-10-24 00:45:08,031] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 추론 완료: 80.60 ms +[2025-10-24 00:45:08,039] [LogListener] [DEBUG] [loggerModule.py:debug:275] MIGAN 인페인팅 성공 +[2025-10-24 00:45:08,039] [LogListener] [DEBUG] [loggerModule.py:debug:275] 메모리 변화 [인페인팅]: 33068.3MB -> 33070.2MB (+1.9MB, +0.0%) - 방법: migan +[2025-10-24 00:45:08,039] [LogListener] [DEBUG] [loggerModule.py:debug:275] 인페인팅 완료 +[2025-10-24 00:45:08,067] [LogListener] [DEBUG] [loggerModule.py:debug:275] 텍스트 렌더링 완료 +[2025-10-24 00:45:08,067] [LogListener] [DEBUG] [loggerModule.py:debug:275] watermark_text: 이미지 저작권 보유 +[2025-10-24 00:45:08,068] [LogListener] [DEBUG] [loggerModule.py:debug:275] is_watermark_enabled: True +[2025-10-24 00:45:08,373] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 저장 완료 : C:\ProgramData\ImgWorker\work\translated_detail_img_2.webp +[2025-10-24 00:45:08,374] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 2 번역 완료: C:\ProgramData\ImgWorker\work\translated_detail_img_2.webp +[2025-10-24 00:45:08,374] [LogListener] [DEBUG] [loggerModule.py:debug:275] ⏱ 이미지 파이프라인 총 1108.0ms | download=0.0ms | ocr=175.0ms | translate=151.9ms | mask=151.9ms | inpaint=98.6ms(migan/DirectML) | render=28.0ms | save=276.0ms +[2025-10-24 00:45:08,375] [LogListener] [DEBUG] [loggerModule.py:debug:275] process_single_image 호출 완료 +[2025-10-24 00:45:08,375] [ResultListener] [WARNING] [loggerModule.py:warning:287] 워커 롤링 스케줄: reason=job-count-threshold +[2025-10-24 00:45:08,375] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 결과 반환 중: uid=024282c9-a0fa-43a7-9392-55af0d636164 +[2025-10-24 00:45:08,376] [WorkerRoller] [WARNING] [loggerModule.py:warning:287] 워커 롤링 시작: job-count-threshold +[2025-10-24 00:45:08,376] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 결과 반환 완료: uid=024282c9-a0fa-43a7-9392-55af0d636164 +[2025-10-24 00:45:08,376] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 00:45:08,376] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 10156) +[2025-10-24 00:45:08,376] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔥 작업 수신 성공 +[2025-10-24 00:45:08,376] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🚀 작업 처리 시작: cmd=process_single_image, uid=758a6e8c-013a-4992-a09f-88bfb4086403 +[2025-10-24 00:45:08,376] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 프로세서 toggle_states 업데이트 : {'TEMP_IMAGE_DIR': 'C:\\ProgramData\\ImgWorker\\work', 'output_image_format': 'webp', 'use_local_rembg': True, 'local_rembg_model_path': 'D:\\py\\img_worker\\modules\\rembg_models', 'local_model_name': 'birefnet-general-lite', 'optionIMGTrans_type': 'GPU', 'detail_IMGTrans_type': 'GPU', 'thumb_trans_type': 'GPU', 'migan_use_accel': True, 'migan_onnx_path': 'D:\\py\\img_worker\\modules\\migan_onnx\\migan_pipeline_v2.onnx', 'ocr_provider_override': 'auto', 'migan_provider_override': 'auto'} +[2025-10-24 00:45:08,376] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 실행 직전: cmd=process_single_image +[2025-10-24 00:45:08,376] [LogListener] [DEBUG] [loggerModule.py:debug:275] process_single_image 호출 직전 +[2025-10-24 00:45:08,377] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 번역시작 +[2025-10-24 00:45:08,800] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 3 처리 시작: D:\py\img_worker\modules\test\5.jpg - 전체 번역 모드 +[2025-10-24 00:45:08,800] [LogListener] [DEBUG] [loggerModule.py:debug:275] 로컬 파일 경로 감지, 다운로드 생략: D:\py\img_worker\modules\test\5.jpg +[2025-10-24 00:45:08,804] [LogListener] [DEBUG] [loggerModule.py:debug:275] 상세페이지 이미지 3 원본 크기: 1200x1857 +[2025-10-24 00:45:08,807] [LogListener] [DEBUG] [loggerModule.py:debug:275] 상세페이지 이미지 3 가로 크기 조정: 1200x1857 → 860x1330 +[2025-10-24 00:45:08,814] [LogListener] [DEBUG] [loggerModule.py:debug:275] 상세페이지 이미지 3 크기 조정 완료: 860x1330 +[2025-10-24 00:45:08,815] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 3 로컬 저장위치(상세페이지 전처리 완료): D:\py\img_worker\modules\test\5_resized.jpg +[2025-10-24 00:45:08,817] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔍 ONNX OCR 감지 방식: polygon +[2025-10-24 00:45:09,056] [LogListener] [DEBUG] [loggerModule.py:debug:275] ⚡ ONNX OCR 추론 완료: 238.0ms +[2025-10-24 00:45:09,069] [LogListener] [DEBUG] [loggerModule.py:debug:275] 📊 세부 시간 - 감지: 84.0ms, 인식: 145.0ms, 분류: 6.0ms +[2025-10-24 00:45:09,069] [LogListener] [DEBUG] [loggerModule.py:debug:275] 메모리 변화 [OCR 처리]: 33097.2MB -> 33090.9MB (-6.3MB, -0.0%) - 이미지 3 +[2025-10-24 00:45:09,069] [LogListener] [DEBUG] [loggerModule.py:debug:275] ocr_results: [{'text': '现代极简风格', 'confidence': 0.9961185455322266, 'polygon': [[245.0, 75.0], [614.0, 75.0], [614.0, 132.0], [245.0, 132.0]], 'bbox': (245, 75, 370, 58), 'method': 'polygon'}, {'text': '更易搭配各种使用场景', 'confidence': 0.9973940849304199, 'polygon': [[168.0, 157.0], [690.0, 157.0], [690.0, 200.0], [168.0, 200.0]], 'bbox': (168, 157, 523, 44), 'method': 'polygon'}, {'text': '★WELLOME', 'confidence': 0.8410268425941467, 'polygon': [[342.0, 521.0], [465.0, 521.0], [465.0, 546.0], [342.0, 546.0]], 'bbox': (342, 521, 124, 26), 'method': 'polygon'}, {'text': '欢迎光临', 'confidence': 0.9999395608901978, 'polygon': [[291.0, 544.0], [519.0, 544.0], [519.0, 612.0], [291.0, 612.0]], 'bbox': (291, 544, 229, 69), 'method': 'polygon'}, {'text': '聚时促销礼惠全城', 'confidence': 0.9192686676979065, 'polygon': [[339.0, 615.0], [447.0, 618.0], [446.0, 643.0], [338.0, 640.0]], 'bbox': (338, 615, 110, 29), 'method': 'polygon'}, {'text': '满499减200/满999减500', 'confidence': 0.9207867383956909, 'polygon': [[320.0, 678.0], [459.0, 689.0], [457.0, 713.0], [318.0, 702.0]], 'bbox': (318, 678, 142, 36), 'method': 'polygon'}, {'text': '活动的间6.1·6.7', 'confidence': 0.7784561514854431, 'polygon': [[345.0, 702.0], [426.0, 710.0], [425.0, 728.0], [343.0, 720.0]], 'bbox': (343, 702, 84, 27), 'method': 'polygon'}] +[2025-10-24 00:45:09,069] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 3 OCR raw 데이터 메모리 저장 완료: 7개 텍스트 +[2025-10-24 00:45:09,070] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.6%): '现代极简风格' +[2025-10-24 00:45:09,070] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.7%): '更易搭配各种使用场景' +[2025-10-24 00:45:09,070] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): '★WELLOME' +[2025-10-24 00:45:09,070] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 100.0%): '欢迎光临' +[2025-10-24 00:45:09,070] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 91.9%): '聚时促销礼惠全城' +[2025-10-24 00:45:09,070] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 92.1%): '满499减200/满999减500' +[2025-10-24 00:45:09,070] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 77.8%): '活动的间6.1·6.7' +[2025-10-24 00:45:09,070] [LogListener] [DEBUG] [loggerModule.py:debug:275] 필터링 결과: 6/7개 (신뢰도 + & 중국어) +[2025-10-24 00:45:09,071] [LogListener] [DEBUG] [loggerModule.py:debug:275] filter_ocr_results: [{'text': '现代极简风格', 'confidence': 0.9961185455322266, 'polygon': [[245.0, 75.0], [614.0, 75.0], [614.0, 132.0], [245.0, 132.0]], 'bbox': (245, 75, 370, 58), 'method': 'polygon'}, {'text': '更易搭配各种使用场景', 'confidence': 0.9973940849304199, 'polygon': [[168.0, 157.0], [690.0, 157.0], [690.0, 200.0], [168.0, 200.0]], 'bbox': (168, 157, 523, 44), 'method': 'polygon'}, {'text': '欢迎光临', 'confidence': 0.9999395608901978, 'polygon': [[291.0, 544.0], [519.0, 544.0], [519.0, 612.0], [291.0, 612.0]], 'bbox': (291, 544, 229, 69), 'method': 'polygon'}, {'text': '聚时促销礼惠全城', 'confidence': 0.9192686676979065, 'polygon': [[339.0, 615.0], [447.0, 618.0], [446.0, 643.0], [338.0, 640.0]], 'bbox': (338, 615, 110, 29), 'method': 'polygon'}, {'text': '满499减200/满999减500', 'confidence': 0.9207867383956909, 'polygon': [[320.0, 678.0], [459.0, 689.0], [457.0, 713.0], [318.0, 702.0]], 'bbox': (318, 678, 142, 36), 'method': 'polygon'}, {'text': '活动的间6.1·6.7', 'confidence': 0.7784561514854431, 'polygon': [[345.0, 702.0], [426.0, 710.0], [425.0, 728.0], [343.0, 720.0]], 'bbox': (343, 702, 84, 27), 'method': 'polygon'}] +[2025-10-24 00:45:09,071] [LogListener] [DEBUG] [loggerModule.py:debug:275] 중국어 텍스트 6개 필터링 완료 +[2025-10-24 00:45:09,071] [LogListener] [DEBUG] [loggerModule.py:debug:275] 한글 텍스트 0개 필터링 완료 +[2025-10-24 00:45:11,541] [LogListener] [DEBUG] [loggerModule.py:debug:275] translated_texts: ['모던한 미니멀 스타일', '다양한 사용 시나리오에 더 쉽게 일치', '환영', '도시 전역에서 판촉 선물 이용 가능', '499/500 이상 지출 시 200 할인 999 이상 지출 시 할인', '액티브룸 6.1 / 6.7'] +[2025-10-24 00:45:11,541] [LogListener] [DEBUG] [loggerModule.py:debug:275] 마스크 생성 완료 +[2025-10-24 00:45:11,541] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 3 OCR 권한 없음, 전체 번역 모드 +[2025-10-24 00:45:11,548] [LogListener] [DEBUG] [loggerModule.py:debug:275] [AUTO Inpaint] coverage=0.100, comps=3, min_center_dist=0.073 → request +[2025-10-24 00:45:11,549] [LogListener] [DEBUG] [loggerModule.py:debug:275] ocr_count: 6 +[2025-10-24 00:45:11,549] [LogListener] [DEBUG] [loggerModule.py:debug:275] is_member_valid: False +[2025-10-24 00:45:11,549] [LogListener] [DEBUG] [loggerModule.py:debug:275] [set_inpaint_method] prefix=detail, target_key=detail_IMGTrans_type, trans_type=GPU → inpaint_method=migan +[2025-10-24 00:45:11,549] [LogListener] [DEBUG] [loggerModule.py:debug:275] 최종 inpaint_method: migan +[2025-10-24 00:45:11,549] [LogListener] [DEBUG] [loggerModule.py:debug:275] MIGAN 인페인팅 시도 +[2025-10-24 00:45:11,552] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 입력 형태 - 이미지: (1, 3, 1330, 860), 마스크: (1, 1, 1330, 860) +[2025-10-24 00:45:11,793] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 출력 형태: (1330, 860, 3), dtype: uint8 +[2025-10-24 00:45:11,793] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 추론 완료: 241.00 ms +[2025-10-24 00:45:11,802] [LogListener] [DEBUG] [loggerModule.py:debug:275] MIGAN 인페인팅 성공 +[2025-10-24 00:45:11,802] [LogListener] [DEBUG] [loggerModule.py:debug:275] 메모리 변화 [인페인팅]: 33131.4MB -> 33140.5MB (+9.1MB, +0.0%) - 방법: migan +[2025-10-24 00:45:11,802] [LogListener] [DEBUG] [loggerModule.py:debug:275] 인페인팅 완료 +[2025-10-24 00:45:11,835] [LogListener] [DEBUG] [loggerModule.py:debug:275] 텍스트 렌더링 완료 +[2025-10-24 00:45:11,835] [LogListener] [DEBUG] [loggerModule.py:debug:275] watermark_text: 이미지 저작권 보유 +[2025-10-24 00:45:11,835] [LogListener] [DEBUG] [loggerModule.py:debug:275] is_watermark_enabled: True +[2025-10-24 00:45:12,153] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 저장 완료 : C:\ProgramData\ImgWorker\work\translated_detail_img_3.webp +[2025-10-24 00:45:12,154] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 3 번역 완료: C:\ProgramData\ImgWorker\work\translated_detail_img_3.webp +[2025-10-24 00:45:12,154] [LogListener] [DEBUG] [loggerModule.py:debug:275] ⏱ 이미지 파이프라인 총 3777.5ms | download=0.0ms | ocr=247.0ms | translate=2471.8ms | mask=2471.8ms | inpaint=259.0ms(migan/DirectML) | render=33.0ms | save=287.0ms +[2025-10-24 00:45:12,155] [LogListener] [DEBUG] [loggerModule.py:debug:275] process_single_image 호출 완료 +[2025-10-24 00:45:12,156] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 결과 반환 중: uid=758a6e8c-013a-4992-a09f-88bfb4086403 +[2025-10-24 00:45:12,171] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 결과 반환 완료: uid=758a6e8c-013a-4992-a09f-88bfb4086403 +[2025-10-24 00:45:12,171] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 00:45:12,171] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 10156) +[2025-10-24 00:45:12,171] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔥 작업 수신 성공 +[2025-10-24 00:45:12,171] [LogListener] [DEBUG] [loggerModule.py:debug:275] Shutdown signal 수신 → 종료 +[2025-10-24 00:45:12,205] [LogListener] [DEBUG] [loggerModule.py:debug:275] OCR 모듈 정리 완료 +[2025-10-24 00:45:12,205] [LogListener] [DEBUG] [loggerModule.py:debug:275] 마스크 모듈 정리 완료 +[2025-10-24 00:45:12,206] [LogListener] [DEBUG] [loggerModule.py:debug:275] 임시 폴더 삭제됨: C:\ProgramData\ImgWorker\work +[2025-10-24 00:45:12,206] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 프로세서 소멸 +[2025-10-24 00:45:13,361] [WorkerRoller] [INFO] [loggerModule.py:info:281] 워커 프로세스 기동: PID=26488 +[2025-10-24 00:45:13,800] [LogListener] [DEBUG] [loggerModule.py:debug:275] ImageWorker 프로세스 기동 (PID=26488, Name=ImageWorkerProcess) +[2025-10-24 00:45:13,801] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔧 ImageProcessor3 초기화 시작... +[2025-10-24 00:45:13,801] [LogListener] [DEBUG] [loggerModule.py:debug:275] tracemalloc 메모리 추적 시작 +[2025-10-24 00:45:13,807] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ DirectML 환경 준비 완료 (Windows DirectX 12 기반) +[2025-10-24 00:45:13,807] [LogListener] [DEBUG] [loggerModule.py:debug:275] === 🚀 DirectML GPU 상태 초기화 시작 🚀 === +[2025-10-24 00:45:13,807] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🎯 사용자 GPU 가속 요청: False +[2025-10-24 00:45:13,807] [LogListener] [DEBUG] [loggerModule.py:debug:275] 💻 현재 운영체제: Windows +[2025-10-24 00:45:13,807] [LogListener] [DEBUG] [loggerModule.py:debug:275] GPU 가속이 비활성화됨 (toggle_states['use_cuda'] = False) +[2025-10-24 00:45:13,808] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔒 안전한 CPU 모드로 모든 GPU 설정 강제 비활성화 +[2025-10-24 00:45:13,808] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔧 ImageProcessor3 GPU 상태 요약: +[2025-10-24 00:45:13,808] [LogListener] [DEBUG] [loggerModule.py:debug:275] - CUDA 사용 가능: False +[2025-10-24 00:45:13,808] [LogListener] [DEBUG] [loggerModule.py:debug:275] - toggle_states['use_cuda']: NOT_SET +[2025-10-24 00:45:13,808] [LogListener] [DEBUG] [loggerModule.py:debug:275] - GPU 하드웨어 정보: {} +[2025-10-24 00:45:13,808] [LogListener] [DEBUG] [loggerModule.py:debug:275] ImageProcessor3 Init toggle_states: {'TEMP_IMAGE_DIR': 'C:\\ProgramData\\ImgWorker\\work', 'output_image_format': 'webp', 'use_local_rembg': True, 'local_rembg_model_path': 'D:\\py\\img_worker\\modules\\rembg_models', 'local_model_name': 'birefnet-general-lite', 'optionIMGTrans_type': 'CPU', 'detail_IMGTrans_type': 'CPU', 'thumb_trans_type': 'CPU', 'migan_use_accel': True, 'migan_onnx_path': 'D:\\py\\img_worker\\modules\\migan_onnx\\migan_pipeline_v2.onnx', 'ocr_provider_override': 'auto', 'migan_provider_override': 'auto'} +[2025-10-24 00:45:13,808] [LogListener] [DEBUG] [loggerModule.py:debug:275] is_member_valid: False +[2025-10-24 00:45:13,809] [LogListener] [DEBUG] [loggerModule.py:debug:275] debug_images 디렉토리 이미 존재: D:\py\img_worker\modules\debug_images +[2025-10-24 00:45:13,809] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.font_path: D:\py\img_worker\modules\fonts\HakgyoansimDunggeunmisoTTFB.ttf +[2025-10-24 00:45:13,809] [LogListener] [DEBUG] [loggerModule.py:debug:275] toggle_states font_path: D:\py\img_worker\modules\fonts\HakgyoansimDunggeunmisoTTFB.ttf +[2025-10-24 00:45:13,809] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.TEMP_IMAGE_DIR: C:\ProgramData\ImgWorker\work +[2025-10-24 00:45:13,809] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.debugging_save_Dir: D:\py\img_worker\modules\debug_images +[2025-10-24 00:45:13,809] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.unwanted_texts: {} +[2025-10-24 00:45:13,809] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.inpaint_method: migan +[2025-10-24 00:45:13,809] [LogListener] [DEBUG] [loggerModule.py:debug:275] Image.MAX_IMAGE_PIXELS set to 20000000 +[2025-10-24 00:45:13,809] [LogListener] [DEBUG] [loggerModule.py:debug:275] ONNX 모델 타입 설정값: 자동 선택, GPU 정보: {} +[2025-10-24 00:45:13,810] [LogListener] [DEBUG] [loggerModule.py:debug:275] 자동 선택 모드: GPU 추천 모델 simp 사용 +[2025-10-24 00:45:13,810] [LogListener] [DEBUG] [loggerModule.py:debug:275] ONNX OCR 모델 타입 결정: simp (GPU: False) +[2025-10-24 00:45:13,810] [LogListener] [DEBUG] [loggerModule.py:debug:275] ONNX OCR 모듈 CPU 모드로 설정 +[2025-10-24 00:45:13,810] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🚀 ONNX TextSystem 초기화 시작 (CPU 모드) +[2025-10-24 00:45:13,810] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🚀 ONNX TextSystem 초기화 시작 (CPU 모드) +[2025-10-24 00:45:15,371] [LogListener] [DEBUG] [loggerModule.py:debug:275] 문자 사전 파일 UTF-8 확인 완료: D:\py\img_worker\modules\onnx_ocr_module\dict\ppocr_keys_v1.txt +[2025-10-24 00:45:15,463] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ ONNX TextSystem 초기화 완료 (CPU + SIMP (호환성) 모델) +[2025-10-24 00:45:15,463] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ ONNX TextSystem 초기화 완료 (CPU + SIMP 모델) +[2025-10-24 00:45:15,463] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ ONNX OCR 모듈 초기화 성공 (CPU 모드) +[2025-10-24 00:45:15,464] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ ONNX OCR 모듈 초기화 성공 +[2025-10-24 00:45:15,464] [LogListener] [DEBUG] [loggerModule.py:debug:275] 마스크 모듈 초기화 완료 +[2025-10-24 00:45:15,464] [LogListener] [DEBUG] [loggerModule.py:debug:275] MaskModule 초기화 성공 +[2025-10-24 00:45:15,464] [LogListener] [DEBUG] [loggerModule.py:debug:275] 텍스트 렌더링 모듈 초기화 완료 +[2025-10-24 00:45:15,464] [LogListener] [DEBUG] [loggerModule.py:debug:275] 기본 폰트: D:\py\img_worker\modules\fonts\HakgyoansimDunggeunmisoTTFB.ttf +[2025-10-24 00:45:15,464] [LogListener] [DEBUG] [loggerModule.py:debug:275] TextRenderingModule 초기화 성공 +[2025-10-24 00:45:15,464] [LogListener] [DEBUG] [loggerModule.py:debug:275] 폰트 로드 성공: D:\py\img_worker\modules\fonts\HakgyoansimDunggeunmisoTTFB.ttf +[2025-10-24 00:45:15,464] [LogListener] [DEBUG] [loggerModule.py:debug:275] PostImageManager 초기화 성공 +[2025-10-24 00:45:15,464] [LogListener] [DEBUG] [loggerModule.py:debug:275] Request_AI_Server GPU 상태: CUDA 사용 가능=False +[2025-10-24 00:45:15,465] [LogListener] [DEBUG] [loggerModule.py:debug:275] Request_AI_Server 초기화 성공 +[2025-10-24 00:45:15,465] [LogListener] [DEBUG] [loggerModule.py:debug:275] GoogleTranslate 초기화 성공 +[2025-10-24 00:45:15,465] [LogListener] [DEBUG] [loggerModule.py:debug:275] MIGAN CUDA 사용 불가 - CPU 모드로 설정 +[2025-10-24 00:45:15,465] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] GPU 관리자 전달: GPUManager, can_use_cuda: False +[2025-10-24 00:45:15,486] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] GPU 관리자 사용 가능: False +[2025-10-24 00:45:15,720] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 사용 가능한 providers: ['DmlExecutionProvider', 'CPUExecutionProvider'] +[2025-10-24 00:45:15,721] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] DirectML 가속 활성화 +[2025-10-24 00:45:15,721] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 최종 providers: [('DmlExecutionProvider', {}), ('CPUExecutionProvider', {})] +[2025-10-24 00:45:15,721] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] DirectML 가속 시도: [('DmlExecutionProvider', {}), ('CPUExecutionProvider', {})] +[2025-10-24 00:45:15,721] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] DirectML 가속 성공! 실제 providers: ['DmlExecutionProvider', 'CPUExecutionProvider'] +[2025-10-24 00:45:15,721] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 입력 0: image, 형태: ['batch_size', 3, 'height', 'width'], 타입: tensor(uint8) +[2025-10-24 00:45:15,721] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 입력 1: mask, 형태: ['batch_size', 1, 'height', 'width'], 타입: tensor(uint8) +[2025-10-24 00:45:15,722] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 출력 0: result, 형태: ['ScatterNDresult_dim_0', 3, 'ScatterNDresult_dim_2', 'ScatterNDresult_dim_3'], 타입: tensor(uint8) +[2025-10-24 00:45:15,722] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 세션 준비 완료. providers=['DmlExecutionProvider', 'CPUExecutionProvider'] +[2025-10-24 00:45:15,722] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] GPU 관리자 연결 완료: GPUManager +[2025-10-24 00:45:15,722] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 파이프라인 gpu_manager 속성: True, 값: +[2025-10-24 00:45:15,722] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 초기화 완료: gpu_manager 속성=True, 값= +[2025-10-24 00:45:15,722] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔰 OCR 모듈 Warm-up 시작... +[2025-10-24 00:45:15,722] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔍 ONNX OCR 감지 방식: polygon +[2025-10-24 00:45:15,727] [LogListener] [DEBUG] [loggerModule.py:debug:275] ⚡ ONNX OCR 추론 완료: 5.0ms +[2025-10-24 00:45:15,727] [LogListener] [DEBUG] [loggerModule.py:debug:275] 📊 세부 시간 - 감지: 3.0ms, 인식: 0.0ms, 분류: 0.0ms +[2025-10-24 00:45:15,727] [LogListener] [WARNING] [loggerModule.py:warning:287] ⚠️ ONNX OCR 결과가 비어있습니다 +[2025-10-24 00:45:15,731] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ OCR 모듈 Warm-up 성공 +[2025-10-24 00:45:15,731] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔰 ImageProcessor Warm‑up 완료 +[2025-10-24 00:45:15,731] [ResultListener] [INFO] [loggerModule.py:info:281] 워커 READY 수신 +[2025-10-24 00:45:15,731] [ResultListener] [INFO] [loggerModule.py:info:281] 워커 READY 수신 +[2025-10-24 00:45:15,731] [LogListener] [DEBUG] [loggerModule.py:debug:275] 📡 추가 READY 신호 전송 완료 +[2025-10-24 00:45:15,732] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 00:45:15,732] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 26488) +[2025-10-24 00:45:48,423] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔥 작업 수신 성공 +[2025-10-24 00:45:48,423] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🚀 작업 처리 시작: cmd=process_single_image, uid=7e676549-f091-44e5-aeef-5942d0cc4e49 +[2025-10-24 00:45:48,423] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 프로세서 toggle_states 업데이트 : {'TEMP_IMAGE_DIR': 'C:\\ProgramData\\ImgWorker\\work', 'output_image_format': 'webp', 'use_local_rembg': True, 'local_rembg_model_path': 'D:\\py\\img_worker\\modules\\rembg_models', 'local_model_name': 'birefnet-general-lite', 'optionIMGTrans_type': 'GPU', 'detail_IMGTrans_type': 'GPU', 'thumb_trans_type': 'GPU', 'migan_use_accel': True, 'migan_onnx_path': 'D:\\py\\img_worker\\modules\\migan_onnx\\migan_pipeline_v2.onnx', 'ocr_provider_override': 'auto', 'migan_provider_override': 'auto'} +[2025-10-24 00:45:48,423] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 실행 직전: cmd=process_single_image +[2025-10-24 00:45:48,423] [LogListener] [DEBUG] [loggerModule.py:debug:275] process_single_image 호출 직전 +[2025-10-24 00:45:48,425] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 번역시작 +[2025-10-24 00:45:48,757] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 2 처리 시작: D:\py\img_worker\modules\test\2.jpg - 전체 번역 모드 +[2025-10-24 00:45:48,757] [LogListener] [DEBUG] [loggerModule.py:debug:275] 로컬 파일 경로 감지, 다운로드 생략: D:\py\img_worker\modules\test\2.jpg +[2025-10-24 00:45:48,759] [LogListener] [DEBUG] [loggerModule.py:debug:275] 상세페이지 이미지 2 원본 크기: 640x640 +[2025-10-24 00:45:48,763] [LogListener] [DEBUG] [loggerModule.py:debug:275] 상세페이지 이미지 2 가로 크기 조정: 640x640 → 860x860 +[2025-10-24 00:45:48,770] [LogListener] [DEBUG] [loggerModule.py:debug:275] 상세페이지 이미지 2 크기 조정 완료: 860x860 +[2025-10-24 00:45:48,770] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 2 로컬 저장위치(상세페이지 전처리 완료): D:\py\img_worker\modules\test\2_resized.jpg +[2025-10-24 00:45:48,773] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔍 ONNX OCR 감지 방식: polygon +[2025-10-24 00:45:49,674] [LogListener] [DEBUG] [loggerModule.py:debug:275] ⚡ ONNX OCR 추론 완료: 898.0ms +[2025-10-24 00:45:49,675] [LogListener] [DEBUG] [loggerModule.py:debug:275] 📊 세부 시간 - 감지: 121.0ms, 인식: 745.0ms, 분류: 24.0ms +[2025-10-24 00:45:49,694] [LogListener] [DEBUG] [loggerModule.py:debug:275] 메모리 변화 [OCR 처리]: 33538.7MB -> 33859.1MB (+320.4MB, +1.0%) - 이미지 2 +[2025-10-24 00:45:49,704] [LogListener] [DEBUG] [loggerModule.py:debug:275] ocr_results: [{'text': '科尔诺', 'confidence': 0.9992308616638184, 'polygon': [[96.0, 27.0], [211.0, 25.0], [212.0, 63.0], [97.0, 66.0]], 'bbox': (96, 25, 117, 42), 'method': 'polygon'}, {'text': 'PA', 'confidence': 0.9930515289306641, 'polygon': [[414.0, 28.0], [470.0, 32.0], [468.0, 59.0], [413.0, 55.0]], 'bbox': (413, 28, 58, 32), 'method': 'polygon'}, {'text': 'CE', 'confidence': 0.9594120979309082, 'polygon': [[665.0, 24.0], [728.0, 24.0], [728.0, 66.0], [665.0, 66.0]], 'bbox': (665, 24, 64, 43), 'method': 'polygon'}, {'text': 'SGS', 'confidence': 0.9917036890983582, 'polygon': [[751.0, 24.0], [821.0, 24.0], [821.0, 61.0], [751.0, 61.0]], 'bbox': (751, 24, 71, 38), 'method': 'polygon'}, {'text': 'CNEX', 'confidence': 0.9931035041809082, 'polygon': [[500.0, 34.0], [569.0, 34.0], [569.0, 55.0], [500.0, 55.0]], 'bbox': (500, 34, 70, 22), 'method': 'polygon'}, {'text': 'S', 'confidence': 0.8341087698936462, 'polygon': [[595.0, 37.0], [618.0, 37.0], [618.0, 48.0], [595.0, 48.0]], 'bbox': (595, 37, 24, 12), 'method': 'polygon'}, {'text': 'KORNO', 'confidence': 0.9965640306472778, 'polygon': [[103.0, 73.0], [211.0, 73.0], [211.0, 98.0], [103.0, 98.0]], 'bbox': (103, 73, 109, 26), 'method': 'polygon'}, {'text': 'CMC认证', 'confidence': 0.9940687417984009, 'polygon': [[321.0, 77.0], [389.0, 77.0], [389.0, 94.0], [321.0, 94.0]], 'bbox': (321, 77, 69, 18), 'method': 'polygon'}, {'text': 'CPA认证', 'confidence': 0.9973182678222656, 'polygon': [[412.0, 78.0], [475.0, 78.0], [475.0, 95.0], [412.0, 95.0]], 'bbox': (412, 78, 64, 18), 'method': 'polygon'}, {'text': '国家防爆', 'confidence': 0.9939618110656738, 'polygon': [[498.0, 79.0], [560.0, 79.0], [560.0, 96.0], [498.0, 96.0]], 'bbox': (498, 79, 63, 18), 'method': 'polygon'}, {'text': 'ISO认证', 'confidence': 0.9970961809158325, 'polygon': [[585.0, 78.0], [644.0, 78.0], [644.0, 95.0], [585.0, 95.0]], 'bbox': (585, 78, 60, 18), 'method': 'polygon'}, {'text': 'CE认证', 'confidence': 0.998458206653595, 'polygon': [[672.0, 77.0], [724.0, 77.0], [724.0, 95.0], [672.0, 95.0]], 'bbox': (672, 77, 53, 19), 'method': 'polygon'}, {'text': 'SGS认证', 'confidence': 0.9989465475082397, 'polygon': [[755.0, 78.0], [816.0, 78.0], [816.0, 96.0], [755.0, 96.0]], 'bbox': (755, 78, 62, 19), 'method': 'polygon'}, {'text': 'GT-1000', 'confidence': 0.9943559765815735, 'polygon': [[60.0, 132.0], [380.0, 135.0], [380.0, 196.0], [60.0, 194.0]], 'bbox': (60, 132, 321, 65), 'method': 'polygon'}, {'text': '激光粉尘检测仪', 'confidence': 0.9930281639099121, 'polygon': [[61.0, 223.0], [546.0, 223.0], [546.0, 284.0], [61.0, 284.0]], 'bbox': (61, 223, 486, 62), 'method': 'polygon'}, {'text': 'MO.5', 'confidence': 0.5840951204299927, 'polygon': [[663.0, 297.0], [685.0, 297.0], [685.0, 304.0], [663.0, 304.0]], 'bbox': (663, 297, 23, 8), 'method': 'polygon'}, {'text': '精度≤±5%F.S', 'confidence': 0.9796085357666016, 'polygon': [[40.0, 322.0], [291.0, 319.0], [292.0, 356.0], [40.0, 358.0]], 'bbox': (40, 319, 253, 40), 'method': 'polygon'}, {'text': 'PMLO', 'confidence': 0.825086236000061, 'polygon': [[659.0, 314.0], [686.0, 314.0], [686.0, 324.0], [659.0, 324.0]], 'bbox': (659, 314, 28, 11), 'method': 'polygon'}, {'text': 'PU2.5', 'confidence': 0.679347813129425, 'polygon': [[658.0, 331.0], [687.0, 331.0], [687.0, 341.0], [658.0, 341.0]], 'bbox': (658, 331, 30, 11), 'method': 'polygon'}, {'text': 'PLSO', 'confidence': 0.6640250086784363, 'polygon': [[658.0, 349.0], [687.0, 349.0], [687.0, 360.0], [658.0, 360.0]], 'bbox': (658, 349, 30, 12), 'method': 'polygon'}, {'text': 'P10', 'confidence': 0.6683804988861084, 'polygon': [[659.0, 368.0], [684.0, 368.0], [684.0, 379.0], [659.0, 379.0]], 'bbox': (659, 368, 26, 12), 'method': 'polygon'}, {'text': '防护等级:IP65', 'confidence': 0.9956157207489014, 'polygon': [[39.0, 399.0], [318.0, 396.0], [319.0, 433.0], [39.0, 436.0]], 'bbox': (39, 396, 281, 41), 'method': 'polygon'}, {'text': '过压保护/声光报警/存储打印', 'confidence': 0.9742894768714905, 'polygon': [[31.0, 478.0], [566.0, 478.0], [566.0, 512.0], [31.0, 512.0]], 'bbox': (31, 478, 536, 35), 'method': 'polygon'}, {'text': 'PM0.3/0.5/1.0/2.5/5.0/10um', 'confidence': 0.9763215780258179, 'polygon': [[29.0, 552.0], [572.0, 555.0], [572.0, 589.0], [29.0, 586.0]], 'bbox': (29, 552, 544, 38), 'method': 'polygon'}, {'text': '可同时监测多种粒径尘埃粒子数', 'confidence': 0.9920802116394043, 'polygon': [[42.0, 631.0], [497.0, 631.0], [497.0, 658.0], [42.0, 658.0]], 'bbox': (42, 631, 456, 28), 'method': 'polygon'}, {'text': '适合十万级以上洁净室', 'confidence': 0.9910793304443359, 'polygon': [[41.0, 679.0], [365.0, 679.0], [365.0, 706.0], [41.0, 706.0]], 'bbox': (41, 679, 325, 28), 'method': 'polygon'}, {'text': '全国', 'confidence': 0.9995953440666199, 'polygon': [[21.0, 720.0], [158.0, 720.0], [158.0, 791.0], [21.0, 791.0]], 'bbox': (21, 720, 138, 72), 'method': 'polygon'}, {'text': '7天无理由退货', 'confidence': 0.9959755539894104, 'polygon': [[263.0, 731.0], [524.0, 733.0], [523.0, 767.0], [263.0, 764.0]], 'bbox': (263, 731, 262, 37), 'method': 'polygon'}, {'text': '赠运险费', 'confidence': 0.9984970688819885, 'polygon': [[571.0, 731.0], [727.0, 731.0], [727.0, 768.0], [571.0, 768.0]], 'bbox': (571, 731, 157, 38), 'method': 'polygon'}, {'text': '包邮', 'confidence': 0.9996557235717773, 'polygon': [[22.0, 789.0], [161.0, 789.0], [161.0, 859.0], [22.0, 859.0]], 'bbox': (22, 789, 140, 71), 'method': 'polygon'}, {'text': '原厂正品/可开发票/质保一年', 'confidence': 0.9892104268074036, 'polygon': [[185.0, 789.0], [837.0, 787.0], [837.0, 828.0], [185.0, 830.0]], 'bbox': (185, 787, 653, 44), 'method': 'polygon'}] +[2025-10-24 00:45:49,727] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 2 OCR raw 데이터 메모리 저장 완료: 31개 텍스트 +[2025-10-24 00:45:49,727] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.9%): '科尔诺' +[2025-10-24 00:45:49,727] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): 'PA' +[2025-10-24 00:45:49,727] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): 'CE' +[2025-10-24 00:45:49,727] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): 'SGS' +[2025-10-24 00:45:49,727] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): 'CNEX' +[2025-10-24 00:45:49,727] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): 'S' +[2025-10-24 00:45:49,728] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): 'KORNO' +[2025-10-24 00:45:49,728] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.4%): 'CMC认证' +[2025-10-24 00:45:49,728] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.7%): 'CPA认证' +[2025-10-24 00:45:49,728] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.4%): '国家防爆' +[2025-10-24 00:45:49,728] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.7%): 'ISO认证' +[2025-10-24 00:45:49,728] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.8%): 'CE认证' +[2025-10-24 00:45:49,728] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.9%): 'SGS认证' +[2025-10-24 00:45:49,728] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): 'GT-1000' +[2025-10-24 00:45:49,728] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.3%): '激光粉尘检测仪' +[2025-10-24 00:45:49,728] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): 'MO.5' +[2025-10-24 00:45:49,728] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 98.0%): '精度≤±5%F.S' +[2025-10-24 00:45:49,728] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): 'PMLO' +[2025-10-24 00:45:49,728] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): 'PU2.5' +[2025-10-24 00:45:49,728] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): 'PLSO' +[2025-10-24 00:45:49,728] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): 'P10' +[2025-10-24 00:45:49,728] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.6%): '防护等级:IP65' +[2025-10-24 00:45:49,729] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 97.4%): '过压保护/声光报警/存储打印' +[2025-10-24 00:45:49,729] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): 'PM0.3/0.5/1.0/2.5/5.0/10um' +[2025-10-24 00:45:49,729] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.2%): '可同时监测多种粒径尘埃粒子数' +[2025-10-24 00:45:49,729] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.1%): '适合十万级以上洁净室' +[2025-10-24 00:45:49,729] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 100.0%): '全国' +[2025-10-24 00:45:49,735] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.6%): '7天无理由退货' +[2025-10-24 00:45:49,751] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.8%): '赠运险费' +[2025-10-24 00:45:49,764] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 100.0%): '包邮' +[2025-10-24 00:45:49,764] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 98.9%): '原厂正品/可开发票/质保一年' +[2025-10-24 00:45:49,764] [LogListener] [DEBUG] [loggerModule.py:debug:275] 필터링 결과: 18/31개 (신뢰도 + & 중국어) +[2025-10-24 00:45:49,765] [LogListener] [DEBUG] [loggerModule.py:debug:275] filter_ocr_results: [{'text': '科尔诺', 'confidence': 0.9992308616638184, 'polygon': [[96.0, 27.0], [211.0, 25.0], [212.0, 63.0], [97.0, 66.0]], 'bbox': (96, 25, 117, 42), 'method': 'polygon'}, {'text': 'CMC认证', 'confidence': 0.9940687417984009, 'polygon': [[321.0, 77.0], [389.0, 77.0], [389.0, 94.0], [321.0, 94.0]], 'bbox': (321, 77, 69, 18), 'method': 'polygon'}, {'text': 'CPA认证', 'confidence': 0.9973182678222656, 'polygon': [[412.0, 78.0], [475.0, 78.0], [475.0, 95.0], [412.0, 95.0]], 'bbox': (412, 78, 64, 18), 'method': 'polygon'}, {'text': '国家防爆', 'confidence': 0.9939618110656738, 'polygon': [[498.0, 79.0], [560.0, 79.0], [560.0, 96.0], [498.0, 96.0]], 'bbox': (498, 79, 63, 18), 'method': 'polygon'}, {'text': 'ISO认证', 'confidence': 0.9970961809158325, 'polygon': [[585.0, 78.0], [644.0, 78.0], [644.0, 95.0], [585.0, 95.0]], 'bbox': (585, 78, 60, 18), 'method': 'polygon'}, {'text': 'CE认证', 'confidence': 0.998458206653595, 'polygon': [[672.0, 77.0], [724.0, 77.0], [724.0, 95.0], [672.0, 95.0]], 'bbox': (672, 77, 53, 19), 'method': 'polygon'}, {'text': 'SGS认证', 'confidence': 0.9989465475082397, 'polygon': [[755.0, 78.0], [816.0, 78.0], [816.0, 96.0], [755.0, 96.0]], 'bbox': (755, 78, 62, 19), 'method': 'polygon'}, {'text': '激光粉尘检测仪', 'confidence': 0.9930281639099121, 'polygon': [[61.0, 223.0], [546.0, 223.0], [546.0, 284.0], [61.0, 284.0]], 'bbox': (61, 223, 486, 62), 'method': 'polygon'}, {'text': '精度≤±5%F.S', 'confidence': 0.9796085357666016, 'polygon': [[40.0, 322.0], [291.0, 319.0], [292.0, 356.0], [40.0, 358.0]], 'bbox': (40, 319, 253, 40), 'method': 'polygon'}, {'text': '防护等级:IP65', 'confidence': 0.9956157207489014, 'polygon': [[39.0, 399.0], [318.0, 396.0], [319.0, 433.0], [39.0, 436.0]], 'bbox': (39, 396, 281, 41), 'method': 'polygon'}, {'text': '过压保护/声光报警/存储打印', 'confidence': 0.9742894768714905, 'polygon': [[31.0, 478.0], [566.0, 478.0], [566.0, 512.0], [31.0, 512.0]], 'bbox': (31, 478, 536, 35), 'method': 'polygon'}, {'text': '可同时监测多种粒径尘埃粒子数', 'confidence': 0.9920802116394043, 'polygon': [[42.0, 631.0], [497.0, 631.0], [497.0, 658.0], [42.0, 658.0]], 'bbox': (42, 631, 456, 28), 'method': 'polygon'}, {'text': '适合十万级以上洁净室', 'confidence': 0.9910793304443359, 'polygon': [[41.0, 679.0], [365.0, 679.0], [365.0, 706.0], [41.0, 706.0]], 'bbox': (41, 679, 325, 28), 'method': 'polygon'}, {'text': '全国', 'confidence': 0.9995953440666199, 'polygon': [[21.0, 720.0], [158.0, 720.0], [158.0, 791.0], [21.0, 791.0]], 'bbox': (21, 720, 138, 72), 'method': 'polygon'}, {'text': '7天无理由退货', 'confidence': 0.9959755539894104, 'polygon': [[263.0, 731.0], [524.0, 733.0], [523.0, 767.0], [263.0, 764.0]], 'bbox': (263, 731, 262, 37), 'method': 'polygon'}, {'text': '赠运险费', 'confidence': 0.9984970688819885, 'polygon': [[571.0, 731.0], [727.0, 731.0], [727.0, 768.0], [571.0, 768.0]], 'bbox': (571, 731, 157, 38), 'method': 'polygon'}, {'text': '包邮', 'confidence': 0.9996557235717773, 'polygon': [[22.0, 789.0], [161.0, 789.0], [161.0, 859.0], [22.0, 859.0]], 'bbox': (22, 789, 140, 71), 'method': 'polygon'}, {'text': '原厂正品/可开发票/质保一年', 'confidence': 0.9892104268074036, 'polygon': [[185.0, 789.0], [837.0, 787.0], [837.0, 828.0], [185.0, 830.0]], 'bbox': (185, 787, 653, 44), 'method': 'polygon'}] +[2025-10-24 00:45:49,765] [LogListener] [DEBUG] [loggerModule.py:debug:275] 중국어 텍스트 18개 필터링 완료 +[2025-10-24 00:45:49,765] [LogListener] [DEBUG] [loggerModule.py:debug:275] 한글 텍스트 0개 필터링 완료 +[2025-10-24 00:45:51,867] [LogListener] [DEBUG] [loggerModule.py:debug:275] translated_texts: ['코르노', 'CMC 인증', 'CPA 자격증', '국가 방폭형', 'ISO 인증', 'CE 인증', 'SGS 인증', '레이저 먼지 감지기', '정확도≤±5%F.S', '보호 수준: IP65', '과전압 보호 / 소리와 빛 경보 / 매장 인쇄', '다양한 입자 크기의 먼지 입자 수를 동시에 모니터링 가능', '클래스 100,000 이상의 클린룸에 적합', '전국', '7일이면 반품할 이유가 없습니다.', '무료 배송 보험', '무료 배송', '오리지널 정품 / 청구 가능 / 1년 보증'] +[2025-10-24 00:45:51,867] [LogListener] [DEBUG] [loggerModule.py:debug:275] 마스크 생성 완료 +[2025-10-24 00:45:51,867] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 2 OCR 권한 없음, 전체 번역 모드 +[2025-10-24 00:45:51,874] [LogListener] [DEBUG] [loggerModule.py:debug:275] [AUTO Inpaint] coverage=0.350, comps=14, min_center_dist=0.064 → request +[2025-10-24 00:45:51,875] [LogListener] [DEBUG] [loggerModule.py:debug:275] ocr_count: 18 +[2025-10-24 00:45:51,875] [LogListener] [DEBUG] [loggerModule.py:debug:275] is_member_valid: False +[2025-10-24 00:45:51,875] [LogListener] [DEBUG] [loggerModule.py:debug:275] [set_inpaint_method] prefix=detail, target_key=detail_IMGTrans_type, trans_type=GPU → inpaint_method=migan +[2025-10-24 00:45:51,875] [LogListener] [DEBUG] [loggerModule.py:debug:275] 최종 inpaint_method: migan +[2025-10-24 00:45:51,875] [LogListener] [DEBUG] [loggerModule.py:debug:275] MIGAN 인페인팅 시도 +[2025-10-24 00:45:51,878] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 입력 형태 - 이미지: (1, 3, 860, 860), 마스크: (1, 1, 860, 860) +[2025-10-24 00:45:52,506] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 출력 형태: (860, 860, 3), dtype: uint8 +[2025-10-24 00:45:52,507] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 추론 완료: 628.99 ms +[2025-10-24 00:45:52,514] [LogListener] [DEBUG] [loggerModule.py:debug:275] MIGAN 인페인팅 성공 +[2025-10-24 00:45:52,514] [LogListener] [DEBUG] [loggerModule.py:debug:275] 메모리 변화 [인페인팅]: 33918.0MB -> 34173.3MB (+255.3MB, +0.8%) - 방법: migan +[2025-10-24 00:45:52,514] [LogListener] [DEBUG] [loggerModule.py:debug:275] 인페인팅 완료 +[2025-10-24 00:45:52,615] [LogListener] [DEBUG] [loggerModule.py:debug:275] 텍스트 렌더링 완료 +[2025-10-24 00:45:52,617] [LogListener] [DEBUG] [loggerModule.py:debug:275] watermark_text: 이미지 저작권 보유 +[2025-10-24 00:45:52,617] [LogListener] [DEBUG] [loggerModule.py:debug:275] is_watermark_enabled: True +[2025-10-24 00:45:52,852] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 저장 완료 : C:\ProgramData\ImgWorker\work\translated_detail_img_2.webp +[2025-10-24 00:45:52,853] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 2 번역 완료: C:\ProgramData\ImgWorker\work\translated_detail_img_2.webp +[2025-10-24 00:45:52,853] [LogListener] [DEBUG] [loggerModule.py:debug:275] ⏱ 이미지 파이프라인 총 4428.6ms | download=0.0ms | ocr=910.0ms | translate=2138.7ms | mask=2138.7ms | inpaint=644.0ms(migan/DirectML) | render=101.9ms | save=202.5ms +[2025-10-24 00:45:52,854] [LogListener] [DEBUG] [loggerModule.py:debug:275] process_single_image 호출 완료 +[2025-10-24 00:45:52,854] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 결과 반환 중: uid=7e676549-f091-44e5-aeef-5942d0cc4e49 +[2025-10-24 00:45:52,854] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 결과 반환 완료: uid=7e676549-f091-44e5-aeef-5942d0cc4e49 +[2025-10-24 00:45:52,854] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 00:45:52,854] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 26488) +[2025-10-24 00:46:52,861] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 00:46:52,861] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 00:46:52,861] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 26488) +[2025-10-24 00:47:52,873] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 00:47:52,873] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 00:47:52,873] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 26488) +[2025-10-24 00:48:52,876] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 00:48:52,876] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 00:48:52,876] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 26488) +[2025-10-24 00:49:52,878] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 00:49:52,878] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 00:49:52,878] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 26488) +[2025-10-24 00:50:52,891] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 00:50:52,891] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 00:50:52,891] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 26488) +[2025-10-24 00:51:52,892] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 00:51:52,892] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 00:51:52,892] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 26488) +[2025-10-24 00:52:52,898] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 00:52:52,898] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 00:52:52,898] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 26488) +[2025-10-24 00:53:52,908] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 00:53:52,908] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 00:53:52,908] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 26488) +[2025-10-24 00:54:52,923] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 00:54:52,923] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 00:54:52,923] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 26488) +[2025-10-24 00:55:52,931] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 00:55:52,931] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 00:55:52,931] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 26488) +[2025-10-24 00:56:52,935] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 00:56:52,935] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 00:56:52,935] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 26488) +[2025-10-24 00:57:52,938] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 00:57:52,938] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 00:57:52,938] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 26488) +[2025-10-24 00:58:52,947] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 00:58:52,947] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 00:58:52,947] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 26488) +[2025-10-24 00:59:52,960] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 00:59:52,960] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 00:59:52,960] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 26488) +[2025-10-24 01:00:52,963] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 01:00:52,963] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 01:00:52,963] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 26488) +[2025-10-24 01:01:52,969] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 01:01:52,969] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 01:01:52,969] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 26488) +[2025-10-24 01:02:52,972] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 01:02:52,972] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 01:02:52,972] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 26488) +[2025-10-24 01:03:52,978] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 01:03:52,978] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 01:03:52,978] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 26488) +[2025-10-24 01:04:52,981] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 01:04:52,981] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 01:04:52,981] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 26488) +[2025-10-24 01:05:52,993] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 01:05:52,993] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 01:05:52,993] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 26488) +[2025-10-24 01:06:53,009] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 01:06:53,009] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 01:06:53,009] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 26488) +[2025-10-24 01:07:53,012] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 01:07:53,012] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 01:07:53,012] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 26488) +[2025-10-24 01:08:53,021] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 01:08:53,021] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 01:08:53,021] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 26488) +[2025-10-24 01:09:53,033] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 01:09:53,033] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 01:09:53,033] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 26488) +[2025-10-24 01:10:53,035] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 01:10:53,035] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 01:10:53,035] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 26488) +[2025-10-24 01:11:53,037] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 01:11:53,037] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 01:11:53,037] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 26488) +[2025-10-24 01:12:53,038] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 01:12:53,038] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 01:12:53,038] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 26488) +[2025-10-24 01:13:53,042] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 01:13:53,042] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 01:13:53,042] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 26488) +[2025-10-24 01:14:53,042] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 01:14:53,042] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 01:14:53,042] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 26488) +[2025-10-24 01:15:53,055] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 01:15:53,055] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 01:15:53,055] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 26488) +[2025-10-24 01:16:53,065] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 01:16:53,065] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 01:16:53,066] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 26488) +[2025-10-24 01:17:53,069] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 01:17:53,069] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 01:17:53,069] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 26488) +[2025-10-24 01:18:53,078] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 01:18:53,078] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 01:18:53,078] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 26488) +[2025-10-24 01:19:53,081] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 01:19:53,081] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 01:19:53,081] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 26488) +[2025-10-24 01:20:53,091] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 01:20:53,091] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 01:20:53,091] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 26488) +[2025-10-24 01:21:53,101] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 01:21:53,101] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 01:21:53,101] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 26488) +[2025-10-24 01:22:53,101] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 01:22:53,101] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 01:22:53,101] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 26488) +[2025-10-24 01:23:53,102] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 01:23:53,102] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 01:23:53,102] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 26488) +[2025-10-24 01:24:53,116] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 01:24:53,116] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 01:24:53,116] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 26488) +[2025-10-24 01:25:53,130] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 01:25:53,130] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 01:25:53,130] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 26488) +[2025-10-24 01:26:53,136] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 01:26:53,136] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 01:26:53,136] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 26488) +[2025-10-24 01:27:53,141] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 01:27:53,141] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 01:27:53,141] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 26488) +[2025-10-24 01:28:53,156] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 01:28:53,156] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 01:28:53,156] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 26488) +[2025-10-24 01:29:53,171] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 01:29:53,171] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 01:29:53,171] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 26488) +[2025-10-24 01:30:53,179] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 01:30:53,179] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 01:30:53,179] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 26488) +[2025-10-24 01:31:53,179] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 01:31:53,179] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 01:31:53,179] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 26488) +[2025-10-24 01:32:53,189] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 01:32:53,189] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 01:32:53,189] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 26488) +[2025-10-24 01:33:53,204] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 01:33:53,204] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 01:33:53,204] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 26488) +[2025-10-24 01:34:53,206] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 01:34:53,206] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 01:34:53,206] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 26488) +[2025-10-24 01:35:53,207] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 01:35:53,207] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 01:35:53,207] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 26488) +[2025-10-24 01:36:53,213] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 01:36:53,213] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 01:36:53,213] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 26488) +[2025-10-24 01:37:53,219] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 01:37:53,219] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 01:37:53,219] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 26488) +[2025-10-24 01:38:53,225] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 01:38:53,225] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 01:38:53,225] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 26488) +[2025-10-24 01:39:53,240] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 01:39:53,240] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 01:39:53,240] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 26488) +[2025-10-24 01:40:53,254] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 01:40:53,254] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 01:40:53,254] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 26488) +[2025-10-24 01:41:53,261] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 01:41:53,261] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 01:41:53,261] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 26488) +[2025-10-24 01:42:53,263] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 01:42:53,263] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 01:42:53,263] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 26488) +[2025-10-24 01:43:53,270] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 01:43:53,270] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 01:43:53,270] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 26488) +[2025-10-24 01:44:53,273] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 01:44:53,273] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 01:44:53,273] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 26488) +[2025-10-24 01:45:53,288] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 01:45:53,288] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 01:45:53,288] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 26488) +[2025-10-24 01:46:53,303] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 01:46:53,303] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 01:46:53,303] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 26488) +[2025-10-24 01:47:53,312] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 01:47:53,312] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 01:47:53,312] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 26488) +[2025-10-24 01:48:53,325] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 01:48:53,325] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 01:48:53,325] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 26488) +[2025-10-24 01:49:53,330] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 01:49:53,330] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 01:49:53,330] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 26488) +[2025-10-24 01:50:53,331] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 01:50:53,331] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 01:50:53,331] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 26488) +[2025-10-24 01:51:53,333] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 01:51:53,333] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 01:51:53,333] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 26488) +[2025-10-24 01:52:53,334] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 01:52:53,334] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 01:52:53,334] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 26488) +[2025-10-24 01:53:53,338] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 01:53:53,338] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 01:53:53,338] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 26488) +[2025-10-24 01:54:53,340] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 01:54:53,340] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 01:54:53,340] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 26488) +[2025-10-24 01:55:53,346] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 01:55:53,346] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 01:55:53,346] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 26488) +[2025-10-24 01:56:53,352] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 01:56:53,352] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 01:56:53,352] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 26488) +[2025-10-24 01:57:53,357] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 01:57:53,358] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 01:57:53,358] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 26488) +[2025-10-24 01:58:53,362] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 01:58:53,362] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 01:58:53,362] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 26488) +[2025-10-24 01:59:53,370] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 01:59:53,370] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 01:59:53,371] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 26488) +[2025-10-24 02:00:53,373] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 02:00:53,373] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 02:00:53,373] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 26488) +[2025-10-24 02:01:53,386] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 02:01:53,386] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 02:01:53,386] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 26488) +[2025-10-24 02:02:53,399] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 02:02:53,399] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 02:02:53,399] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 26488) +[2025-10-24 02:03:53,413] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 02:03:53,413] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 02:03:53,413] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 26488) +[2025-10-24 02:13:20,664] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 02:13:20,664] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 02:13:20,664] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 26488) +[2025-10-24 02:14:20,677] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 02:14:20,677] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 02:14:20,677] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 26488) +[2025-10-24 02:15:20,685] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 02:15:20,685] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 02:15:20,685] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 26488) +[2025-10-24 02:16:20,687] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 02:16:20,687] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 02:16:20,687] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 26488) +[2025-10-24 02:17:20,696] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 02:17:20,696] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 02:17:20,696] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 26488) +[2025-10-24 02:18:20,697] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 02:18:20,697] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 02:18:20,697] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 26488) +[2025-10-24 02:19:20,711] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 02:19:20,711] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 02:19:20,711] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 26488) +[2025-10-24 02:20:20,724] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 02:20:20,724] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 02:20:20,724] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 26488) +[2025-10-24 02:21:20,739] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 02:21:20,739] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 02:21:20,739] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 26488) +[2025-10-24 02:22:20,749] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 02:22:20,749] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 02:22:20,750] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 26488) +[2025-10-24 02:23:20,756] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 02:23:20,756] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 02:23:20,756] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 26488) +[2025-10-24 02:24:20,768] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 02:24:20,768] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 02:24:20,768] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 26488) +[2025-10-24 02:25:20,773] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 02:25:20,773] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 02:25:20,773] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 26488) +[2025-10-24 02:26:20,782] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 02:26:20,782] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 02:26:20,782] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 26488) +[2025-10-24 02:27:20,783] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 02:27:20,783] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 02:27:20,783] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 26488) +[2025-10-24 02:28:20,796] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 02:28:20,796] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 02:28:20,796] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 26488) +[2025-10-24 02:29:20,803] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 02:29:20,803] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 02:29:20,803] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 26488) +[2025-10-24 02:30:20,806] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 02:30:20,806] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 02:30:20,806] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 26488) +[2025-10-24 02:31:20,812] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 02:31:20,812] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 02:31:20,812] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 26488) +[2025-10-24 02:32:20,823] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 02:32:20,823] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 02:32:20,823] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 26488) +[2025-10-24 02:33:20,829] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 02:33:20,829] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 02:33:20,829] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 26488) +[2025-10-24 02:34:20,837] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 02:34:20,837] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 02:34:20,837] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 26488) +[2025-10-24 02:35:20,845] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 02:35:20,845] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 02:35:20,845] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 26488) +[2025-10-24 02:36:20,852] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 02:36:20,852] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 02:36:20,852] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 26488) +[2025-10-24 02:37:20,865] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 02:37:20,865] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 02:37:20,865] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 26488) +[2025-10-24 02:38:20,876] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 02:38:20,876] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 02:38:20,876] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 26488) +[2025-10-24 02:39:20,878] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 02:39:20,878] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 02:39:20,878] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 26488) +[2025-10-24 02:40:20,891] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 02:40:20,891] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 02:40:20,891] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 26488) +[2025-10-24 02:41:20,893] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 02:41:20,893] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 02:41:20,893] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 26488) +[2025-10-24 02:42:20,898] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 02:42:20,898] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 02:42:20,898] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 26488) +[2025-10-24 02:43:20,906] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 02:43:20,906] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 02:43:20,906] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 26488) +[2025-10-24 02:44:20,920] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 02:44:20,920] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 02:44:20,920] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 26488) +[2025-10-24 02:45:16,868] [MemMonitor] [WARNING] [loggerModule.py:warning:287] 워커 롤링 스케줄: reason=uptime-threshold +[2025-10-24 02:45:16,868] [WorkerRoller] [WARNING] [loggerModule.py:warning:287] 워커 롤링 시작: uptime-threshold +[2025-10-24 02:45:16,885] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔥 작업 수신 성공 +[2025-10-24 02:45:16,923] [LogListener] [DEBUG] [loggerModule.py:debug:275] Shutdown signal 수신 → 종료 +[2025-10-24 02:45:16,923] [LogListener] [DEBUG] [loggerModule.py:debug:275] OCR 모듈 정리 완료 +[2025-10-24 02:45:16,923] [LogListener] [DEBUG] [loggerModule.py:debug:275] 마스크 모듈 정리 완료 +[2025-10-24 02:45:16,925] [LogListener] [DEBUG] [loggerModule.py:debug:275] 임시 폴더 삭제됨: C:\ProgramData\ImgWorker\work +[2025-10-24 02:45:16,925] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 프로세서 소멸 +[2025-10-24 02:45:17,352] [WorkerRoller] [INFO] [loggerModule.py:info:281] 워커 프로세스 기동: PID=33324 +[2025-10-24 02:45:17,955] [LogListener] [DEBUG] [loggerModule.py:debug:275] ImageWorker 프로세스 기동 (PID=33324, Name=ImageWorkerProcess) +[2025-10-24 02:45:17,955] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔧 ImageProcessor3 초기화 시작... +[2025-10-24 02:45:17,955] [LogListener] [DEBUG] [loggerModule.py:debug:275] tracemalloc 메모리 추적 시작 +[2025-10-24 02:45:17,961] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ DirectML 환경 준비 완료 (Windows DirectX 12 기반) +[2025-10-24 02:45:17,961] [LogListener] [DEBUG] [loggerModule.py:debug:275] === 🚀 DirectML GPU 상태 초기화 시작 🚀 === +[2025-10-24 02:45:17,961] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🎯 사용자 GPU 가속 요청: False +[2025-10-24 02:45:17,962] [LogListener] [DEBUG] [loggerModule.py:debug:275] 💻 현재 운영체제: Windows +[2025-10-24 02:45:17,962] [LogListener] [DEBUG] [loggerModule.py:debug:275] GPU 가속이 비활성화됨 (toggle_states['use_cuda'] = False) +[2025-10-24 02:45:17,962] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔒 안전한 CPU 모드로 모든 GPU 설정 강제 비활성화 +[2025-10-24 02:45:17,962] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔧 ImageProcessor3 GPU 상태 요약: +[2025-10-24 02:45:17,962] [LogListener] [DEBUG] [loggerModule.py:debug:275] - CUDA 사용 가능: False +[2025-10-24 02:45:17,962] [LogListener] [DEBUG] [loggerModule.py:debug:275] - toggle_states['use_cuda']: NOT_SET +[2025-10-24 02:45:17,962] [LogListener] [DEBUG] [loggerModule.py:debug:275] - GPU 하드웨어 정보: {} +[2025-10-24 02:45:17,962] [LogListener] [DEBUG] [loggerModule.py:debug:275] ImageProcessor3 Init toggle_states: {'TEMP_IMAGE_DIR': 'C:\\ProgramData\\ImgWorker\\work', 'output_image_format': 'webp', 'use_local_rembg': True, 'local_rembg_model_path': 'D:\\py\\img_worker\\modules\\rembg_models', 'local_model_name': 'birefnet-general-lite', 'optionIMGTrans_type': 'CPU', 'detail_IMGTrans_type': 'CPU', 'thumb_trans_type': 'CPU', 'migan_use_accel': True, 'migan_onnx_path': 'D:\\py\\img_worker\\modules\\migan_onnx\\migan_pipeline_v2.onnx', 'ocr_provider_override': 'auto', 'migan_provider_override': 'auto'} +[2025-10-24 02:45:17,962] [LogListener] [DEBUG] [loggerModule.py:debug:275] is_member_valid: False +[2025-10-24 02:45:17,962] [LogListener] [DEBUG] [loggerModule.py:debug:275] debug_images 디렉토리 이미 존재: D:\py\img_worker\modules\debug_images +[2025-10-24 02:45:17,962] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.font_path: D:\py\img_worker\modules\fonts\HakgyoansimDunggeunmisoTTFB.ttf +[2025-10-24 02:45:17,962] [LogListener] [DEBUG] [loggerModule.py:debug:275] toggle_states font_path: D:\py\img_worker\modules\fonts\HakgyoansimDunggeunmisoTTFB.ttf +[2025-10-24 02:45:17,962] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.TEMP_IMAGE_DIR: C:\ProgramData\ImgWorker\work +[2025-10-24 02:45:17,962] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.debugging_save_Dir: D:\py\img_worker\modules\debug_images +[2025-10-24 02:45:17,963] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.unwanted_texts: {} +[2025-10-24 02:45:17,963] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.inpaint_method: migan +[2025-10-24 02:45:17,963] [LogListener] [DEBUG] [loggerModule.py:debug:275] Image.MAX_IMAGE_PIXELS set to 20000000 +[2025-10-24 02:45:17,963] [LogListener] [DEBUG] [loggerModule.py:debug:275] ONNX 모델 타입 설정값: 자동 선택, GPU 정보: {} +[2025-10-24 02:45:17,963] [LogListener] [DEBUG] [loggerModule.py:debug:275] 자동 선택 모드: GPU 추천 모델 simp 사용 +[2025-10-24 02:45:17,963] [LogListener] [DEBUG] [loggerModule.py:debug:275] ONNX OCR 모델 타입 결정: simp (GPU: False) +[2025-10-24 02:45:17,963] [LogListener] [DEBUG] [loggerModule.py:debug:275] ONNX OCR 모듈 CPU 모드로 설정 +[2025-10-24 02:45:17,963] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🚀 ONNX TextSystem 초기화 시작 (CPU 모드) +[2025-10-24 02:45:17,963] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🚀 ONNX TextSystem 초기화 시작 (CPU 모드) +[2025-10-24 02:45:19,599] [LogListener] [DEBUG] [loggerModule.py:debug:275] 문자 사전 파일 UTF-8 확인 완료: D:\py\img_worker\modules\onnx_ocr_module\dict\ppocr_keys_v1.txt +[2025-10-24 02:45:19,686] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ ONNX TextSystem 초기화 완료 (CPU + SIMP (호환성) 모델) +[2025-10-24 02:45:19,686] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ ONNX TextSystem 초기화 완료 (CPU + SIMP 모델) +[2025-10-24 02:45:19,687] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ ONNX OCR 모듈 초기화 성공 (CPU 모드) +[2025-10-24 02:45:19,687] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ ONNX OCR 모듈 초기화 성공 +[2025-10-24 02:45:19,687] [LogListener] [DEBUG] [loggerModule.py:debug:275] 마스크 모듈 초기화 완료 +[2025-10-24 02:45:19,687] [LogListener] [DEBUG] [loggerModule.py:debug:275] MaskModule 초기화 성공 +[2025-10-24 02:45:19,687] [LogListener] [DEBUG] [loggerModule.py:debug:275] 텍스트 렌더링 모듈 초기화 완료 +[2025-10-24 02:45:19,688] [LogListener] [DEBUG] [loggerModule.py:debug:275] 기본 폰트: D:\py\img_worker\modules\fonts\HakgyoansimDunggeunmisoTTFB.ttf +[2025-10-24 02:45:19,688] [LogListener] [DEBUG] [loggerModule.py:debug:275] TextRenderingModule 초기화 성공 +[2025-10-24 02:45:19,688] [LogListener] [DEBUG] [loggerModule.py:debug:275] 폰트 로드 성공: D:\py\img_worker\modules\fonts\HakgyoansimDunggeunmisoTTFB.ttf +[2025-10-24 02:45:19,688] [LogListener] [DEBUG] [loggerModule.py:debug:275] PostImageManager 초기화 성공 +[2025-10-24 02:45:19,688] [LogListener] [DEBUG] [loggerModule.py:debug:275] Request_AI_Server GPU 상태: CUDA 사용 가능=False +[2025-10-24 02:45:19,688] [LogListener] [DEBUG] [loggerModule.py:debug:275] Request_AI_Server 초기화 성공 +[2025-10-24 02:45:19,688] [LogListener] [DEBUG] [loggerModule.py:debug:275] GoogleTranslate 초기화 성공 +[2025-10-24 02:45:19,688] [LogListener] [DEBUG] [loggerModule.py:debug:275] MIGAN CUDA 사용 불가 - CPU 모드로 설정 +[2025-10-24 02:45:19,689] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] GPU 관리자 전달: GPUManager, can_use_cuda: False +[2025-10-24 02:45:19,711] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] GPU 관리자 사용 가능: False +[2025-10-24 02:45:19,938] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 사용 가능한 providers: ['DmlExecutionProvider', 'CPUExecutionProvider'] +[2025-10-24 02:45:19,938] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] DirectML 가속 활성화 +[2025-10-24 02:45:19,938] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 최종 providers: [('DmlExecutionProvider', {}), ('CPUExecutionProvider', {})] +[2025-10-24 02:45:19,939] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] DirectML 가속 시도: [('DmlExecutionProvider', {}), ('CPUExecutionProvider', {})] +[2025-10-24 02:45:19,939] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] DirectML 가속 성공! 실제 providers: ['DmlExecutionProvider', 'CPUExecutionProvider'] +[2025-10-24 02:45:19,939] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 입력 0: image, 형태: ['batch_size', 3, 'height', 'width'], 타입: tensor(uint8) +[2025-10-24 02:45:19,939] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 입력 1: mask, 형태: ['batch_size', 1, 'height', 'width'], 타입: tensor(uint8) +[2025-10-24 02:45:19,939] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 출력 0: result, 형태: ['ScatterNDresult_dim_0', 3, 'ScatterNDresult_dim_2', 'ScatterNDresult_dim_3'], 타입: tensor(uint8) +[2025-10-24 02:45:19,939] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 세션 준비 완료. providers=['DmlExecutionProvider', 'CPUExecutionProvider'] +[2025-10-24 02:45:19,939] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] GPU 관리자 연결 완료: GPUManager +[2025-10-24 02:45:19,939] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 파이프라인 gpu_manager 속성: True, 값: +[2025-10-24 02:45:19,939] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 초기화 완료: gpu_manager 속성=True, 값= +[2025-10-24 02:45:19,939] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔰 OCR 모듈 Warm-up 시작... +[2025-10-24 02:45:19,940] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔍 ONNX OCR 감지 방식: polygon +[2025-10-24 02:45:19,944] [LogListener] [DEBUG] [loggerModule.py:debug:275] ⚡ ONNX OCR 추론 완료: 4.0ms +[2025-10-24 02:45:19,945] [LogListener] [DEBUG] [loggerModule.py:debug:275] 📊 세부 시간 - 감지: 3.0ms, 인식: 0.0ms, 분류: 0.0ms +[2025-10-24 02:45:19,945] [LogListener] [WARNING] [loggerModule.py:warning:287] ⚠️ ONNX OCR 결과가 비어있습니다 +[2025-10-24 02:45:19,948] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ OCR 모듈 Warm-up 성공 +[2025-10-24 02:45:19,948] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔰 ImageProcessor Warm‑up 완료 +[2025-10-24 02:45:19,948] [ResultListener] [INFO] [loggerModule.py:info:281] 워커 READY 수신 +[2025-10-24 02:45:19,948] [ResultListener] [INFO] [loggerModule.py:info:281] 워커 READY 수신 +[2025-10-24 02:45:19,949] [LogListener] [DEBUG] [loggerModule.py:debug:275] 📡 추가 READY 신호 전송 완료 +[2025-10-24 02:45:19,949] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 02:45:19,949] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 33324) +[2025-10-24 02:46:19,954] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 02:46:19,954] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 02:46:19,954] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 33324) +[2025-10-24 02:47:19,962] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 02:47:19,962] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 02:47:19,962] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 33324) +[2025-10-24 02:48:19,975] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 02:48:19,975] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 02:48:19,975] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 33324) +[2025-10-24 02:49:19,978] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 02:49:19,978] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 02:49:19,978] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 33324) +[2025-10-24 02:50:19,991] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 02:50:19,991] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 02:50:19,991] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 33324) +[2025-10-24 02:51:20,000] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 02:51:20,000] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 02:51:20,000] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 33324) +[2025-10-24 02:52:20,005] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 02:52:20,005] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 02:52:20,005] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 33324) +[2025-10-24 02:53:20,015] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 02:53:20,015] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 02:53:20,015] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 33324) +[2025-10-24 02:54:20,020] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 02:54:20,020] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 02:54:20,020] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 33324) +[2025-10-24 02:55:20,023] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 02:55:20,023] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 02:55:20,023] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 33324) +[2025-10-24 02:56:20,037] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 02:56:20,037] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 02:56:20,037] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 33324) +[2025-10-24 02:57:20,040] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 02:57:20,040] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 02:57:20,040] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 33324) +[2025-10-24 02:58:20,044] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 02:58:20,044] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 02:58:20,044] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 33324) +[2025-10-24 02:59:20,050] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 02:59:20,050] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 02:59:20,050] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 33324) +[2025-10-24 03:00:20,050] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 03:00:20,050] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 03:00:20,050] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 33324) +[2025-10-24 03:01:20,055] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 03:01:20,055] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 03:01:20,055] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 33324) +[2025-10-24 03:02:20,069] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 03:02:20,069] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 03:02:20,069] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 33324) +[2025-10-24 03:03:20,072] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 03:03:20,072] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 03:03:20,072] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 33324) +[2025-10-24 03:04:20,080] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 03:04:20,080] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 03:04:20,080] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 33324) +[2025-10-24 03:05:20,095] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 03:05:20,095] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 03:05:20,095] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 33324) +[2025-10-24 03:06:20,098] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 03:06:20,098] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 03:06:20,098] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 33324) +[2025-10-24 03:07:20,113] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 03:07:20,113] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 03:07:20,113] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 33324) +[2025-10-24 03:08:20,122] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 03:08:20,122] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 03:08:20,122] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 33324) +[2025-10-24 03:09:20,134] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 03:09:20,134] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 03:09:20,134] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 33324) +[2025-10-24 03:10:20,137] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 03:10:20,137] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 03:10:20,137] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 33324) +[2025-10-24 03:11:20,144] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 03:11:20,144] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 03:11:20,144] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 33324) +[2025-10-24 03:12:20,154] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 03:12:20,154] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 03:12:20,154] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 33324) +[2025-10-24 03:13:20,159] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 03:13:20,159] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 03:13:20,159] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 33324) +[2025-10-24 03:14:20,162] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 03:14:20,162] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 03:14:20,162] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 33324) +[2025-10-24 03:15:20,177] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 03:15:20,177] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 03:15:20,177] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 33324) +[2025-10-24 03:16:20,180] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 03:16:20,180] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 03:16:20,180] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 33324) +[2025-10-24 03:17:20,185] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 03:17:20,185] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 03:17:20,185] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 33324) +[2025-10-24 03:18:20,189] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 03:18:20,189] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 03:18:20,189] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 33324) +[2025-10-24 03:19:20,201] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 03:19:20,201] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 03:19:20,201] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 33324) +[2025-10-24 03:20:20,214] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 03:20:20,214] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 03:20:20,214] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 33324) +[2025-10-24 03:21:20,219] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 03:21:20,219] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 03:21:20,219] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 33324) +[2025-10-24 03:22:20,229] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 03:22:20,229] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 03:22:20,229] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 33324) +[2025-10-24 03:23:20,243] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 03:23:20,243] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 03:23:20,243] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 33324) +[2025-10-24 03:24:20,255] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 03:24:20,255] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 03:24:20,255] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 33324) +[2025-10-24 03:25:20,262] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 03:25:20,262] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 03:25:20,262] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 33324) +[2025-10-24 03:26:20,265] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 03:26:20,265] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 03:26:20,265] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 33324) +[2025-10-24 03:27:20,269] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 03:27:20,269] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 03:27:20,269] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 33324) +[2025-10-24 03:28:20,273] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 03:28:20,273] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 03:28:20,273] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 33324) +[2025-10-24 03:29:20,282] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 03:29:20,282] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 03:29:20,282] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 33324) +[2025-10-24 03:30:20,286] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 03:30:20,286] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 03:30:20,286] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 33324) +[2025-10-24 03:31:20,296] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 03:31:20,296] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 03:31:20,296] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 33324) +[2025-10-24 03:32:20,308] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 03:32:20,308] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 03:32:20,308] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 33324) +[2025-10-24 03:33:20,310] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 03:33:20,310] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 03:33:20,310] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 33324) +[2025-10-24 03:34:20,321] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 03:34:20,321] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 03:34:20,321] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 33324) +[2025-10-24 03:35:20,333] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 03:35:20,333] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 03:35:20,333] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 33324) +[2025-10-24 03:36:20,336] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 03:36:20,336] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 03:36:20,336] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 33324) +[2025-10-24 03:37:20,342] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 03:37:20,342] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 03:37:20,342] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 33324) +[2025-10-24 03:38:20,350] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 03:38:20,350] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 03:38:20,350] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 33324) +[2025-10-24 03:39:20,358] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 03:39:20,358] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 03:39:20,358] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 33324) +[2025-10-24 03:40:20,371] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 03:40:20,371] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 03:40:20,371] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 33324) +[2025-10-24 03:41:20,381] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 03:41:20,381] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 03:41:20,381] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 33324) +[2025-10-24 03:42:20,383] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 03:42:20,383] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 03:42:20,383] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 33324) +[2025-10-24 03:43:20,385] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 03:43:20,386] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 03:43:20,386] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 33324) +[2025-10-24 03:44:20,399] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 03:44:20,399] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 03:44:20,399] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 33324) +[2025-10-24 03:45:20,409] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 03:45:20,409] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 03:45:20,409] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 33324) +[2025-10-24 03:46:20,411] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 03:46:20,411] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 03:46:20,411] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 33324) +[2025-10-24 03:47:20,427] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 03:47:20,427] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 03:47:20,427] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 33324) +[2025-10-24 03:48:20,442] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 03:48:20,442] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 03:48:20,442] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 33324) +[2025-10-24 03:49:20,453] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 03:49:20,453] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 03:49:20,453] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 33324) +[2025-10-24 03:50:20,461] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 03:50:20,461] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 03:50:20,461] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 33324) +[2025-10-24 03:51:20,469] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 03:51:20,469] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 03:51:20,469] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 33324) +[2025-10-24 03:52:20,482] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 03:52:20,482] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 03:52:20,482] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 33324) +[2025-10-24 03:53:20,486] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 03:53:20,486] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 03:53:20,486] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 33324) +[2025-10-24 03:54:20,488] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 03:54:20,488] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 03:54:20,488] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 33324) +[2025-10-24 03:55:20,493] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 03:55:20,493] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 03:55:20,493] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 33324) +[2025-10-24 03:56:20,493] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 03:56:20,494] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 03:56:20,494] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 33324) +[2025-10-24 03:57:20,494] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 03:57:20,494] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 03:57:20,494] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 33324) +[2025-10-24 03:58:20,504] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 03:58:20,505] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 03:58:20,505] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 33324) +[2025-10-24 03:59:20,507] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 03:59:20,507] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 03:59:20,507] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 33324) +[2025-10-24 04:00:20,511] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 04:00:20,511] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 04:00:20,511] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 33324) +[2025-10-24 04:01:20,525] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 04:01:20,525] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 04:01:20,525] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 33324) +[2025-10-24 04:02:20,540] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 04:02:20,540] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 04:02:20,540] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 33324) +[2025-10-24 04:03:20,550] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 04:03:20,550] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 04:03:20,550] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 33324) +[2025-10-24 04:04:20,561] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 04:04:20,561] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 04:04:20,561] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 33324) +[2025-10-24 04:05:20,575] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 04:05:20,575] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 04:05:20,575] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 33324) +[2025-10-24 04:06:20,583] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 04:06:20,583] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 04:06:20,583] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 33324) +[2025-10-24 04:07:20,594] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 04:07:20,594] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 04:07:20,594] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 33324) +[2025-10-24 04:08:20,594] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 04:08:20,594] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 04:08:20,594] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 33324) +[2025-10-24 04:09:20,608] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 04:09:20,608] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 04:09:20,608] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 33324) +[2025-10-24 04:10:20,619] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 04:10:20,619] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 04:10:20,619] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 33324) +[2025-10-24 04:11:20,624] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 04:11:20,624] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 04:11:20,624] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 33324) +[2025-10-24 04:12:20,626] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 04:12:20,626] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 04:12:20,626] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 33324) +[2025-10-24 04:13:20,637] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 04:13:20,637] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 04:13:20,637] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 33324) +[2025-10-24 04:14:20,642] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 04:14:20,642] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 04:14:20,642] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 33324) +[2025-10-24 04:15:20,644] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 04:15:20,644] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 04:15:20,644] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 33324) +[2025-10-24 04:16:20,658] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 04:16:20,658] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 04:16:20,658] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 33324) +[2025-10-24 04:17:20,666] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 04:17:20,666] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 04:17:20,666] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 33324) +[2025-10-24 04:18:20,681] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 04:18:20,681] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 04:18:20,681] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 33324) +[2025-10-24 04:19:20,683] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 04:19:20,683] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 04:19:20,683] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 33324) +[2025-10-24 04:20:20,691] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 04:20:20,691] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 04:20:20,691] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 33324) +[2025-10-24 04:21:20,699] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 04:21:20,699] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 04:21:20,699] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 33324) +[2025-10-24 04:22:20,708] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 04:22:20,708] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 04:22:20,708] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 33324) +[2025-10-24 04:23:20,711] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 04:23:20,711] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 04:23:20,711] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 33324) +[2025-10-24 04:24:20,717] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 04:24:20,717] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 04:24:20,717] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 33324) +[2025-10-24 04:25:20,721] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 04:25:20,721] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 04:25:20,721] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 33324) +[2025-10-24 04:26:20,727] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 04:26:20,727] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 04:26:20,727] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 33324) +[2025-10-24 04:27:20,728] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 04:27:20,728] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 04:27:20,728] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 33324) +[2025-10-24 04:28:20,743] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 04:28:20,743] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 04:28:20,743] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 33324) +[2025-10-24 04:29:20,758] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 04:29:20,758] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 04:29:20,758] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 33324) +[2025-10-24 04:30:20,760] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 04:30:20,760] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 04:30:20,760] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 33324) +[2025-10-24 04:31:20,771] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 04:31:20,771] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 04:31:20,771] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 33324) +[2025-10-24 04:32:20,785] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 04:32:20,785] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 04:32:20,785] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 33324) +[2025-10-24 04:33:20,789] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 04:33:20,789] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 04:33:20,789] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 33324) +[2025-10-24 04:34:20,803] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 04:34:20,803] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 04:34:20,803] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 33324) +[2025-10-24 04:35:20,809] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 04:35:20,809] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 04:35:20,809] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 33324) +[2025-10-24 04:36:20,818] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 04:36:20,818] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 04:36:20,818] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 33324) +[2025-10-24 04:37:20,827] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 04:37:20,827] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 04:37:20,827] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 33324) +[2025-10-24 04:38:20,831] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 04:38:20,831] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 04:38:20,831] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 33324) +[2025-10-24 04:39:20,838] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 04:39:20,838] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 04:39:20,838] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 33324) +[2025-10-24 04:40:20,848] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 04:40:20,848] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 04:40:20,848] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 33324) +[2025-10-24 04:41:20,850] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 04:41:20,850] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 04:41:20,850] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 33324) +[2025-10-24 04:42:20,856] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 04:42:20,856] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 04:42:20,857] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 33324) +[2025-10-24 04:43:20,860] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 04:43:20,860] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 04:43:20,860] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 33324) +[2025-10-24 04:44:20,861] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 04:44:20,861] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 04:44:20,861] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 33324) +[2025-10-24 04:45:20,865] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 04:45:20,865] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 04:45:20,865] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 33324) +[2025-10-24 04:45:21,224] [MemMonitor] [WARNING] [loggerModule.py:warning:287] 워커 롤링 스케줄: reason=uptime-threshold +[2025-10-24 04:45:21,224] [WorkerRoller] [WARNING] [loggerModule.py:warning:287] 워커 롤링 시작: uptime-threshold +[2025-10-24 04:45:21,263] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔥 작업 수신 성공 +[2025-10-24 04:45:21,263] [LogListener] [DEBUG] [loggerModule.py:debug:275] Shutdown signal 수신 → 종료 +[2025-10-24 04:45:21,263] [LogListener] [DEBUG] [loggerModule.py:debug:275] OCR 모듈 정리 완료 +[2025-10-24 04:45:21,263] [LogListener] [DEBUG] [loggerModule.py:debug:275] 마스크 모듈 정리 완료 +[2025-10-24 04:45:21,263] [LogListener] [DEBUG] [loggerModule.py:debug:275] 임시 폴더 삭제됨: C:\ProgramData\ImgWorker\work +[2025-10-24 04:45:21,263] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 프로세서 소멸 +[2025-10-24 04:45:21,528] [WorkerRoller] [INFO] [loggerModule.py:info:281] 워커 프로세스 기동: PID=9300 +[2025-10-24 04:45:21,978] [LogListener] [DEBUG] [loggerModule.py:debug:275] ImageWorker 프로세스 기동 (PID=9300, Name=ImageWorkerProcess) +[2025-10-24 04:45:21,979] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔧 ImageProcessor3 초기화 시작... +[2025-10-24 04:45:21,979] [LogListener] [DEBUG] [loggerModule.py:debug:275] tracemalloc 메모리 추적 시작 +[2025-10-24 04:45:21,985] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ DirectML 환경 준비 완료 (Windows DirectX 12 기반) +[2025-10-24 04:45:21,986] [LogListener] [DEBUG] [loggerModule.py:debug:275] === 🚀 DirectML GPU 상태 초기화 시작 🚀 === +[2025-10-24 04:45:21,986] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🎯 사용자 GPU 가속 요청: False +[2025-10-24 04:45:21,986] [LogListener] [DEBUG] [loggerModule.py:debug:275] 💻 현재 운영체제: Windows +[2025-10-24 04:45:21,986] [LogListener] [DEBUG] [loggerModule.py:debug:275] GPU 가속이 비활성화됨 (toggle_states['use_cuda'] = False) +[2025-10-24 04:45:21,986] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔒 안전한 CPU 모드로 모든 GPU 설정 강제 비활성화 +[2025-10-24 04:45:21,986] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔧 ImageProcessor3 GPU 상태 요약: +[2025-10-24 04:45:21,986] [LogListener] [DEBUG] [loggerModule.py:debug:275] - CUDA 사용 가능: False +[2025-10-24 04:45:21,986] [LogListener] [DEBUG] [loggerModule.py:debug:275] - toggle_states['use_cuda']: NOT_SET +[2025-10-24 04:45:21,986] [LogListener] [DEBUG] [loggerModule.py:debug:275] - GPU 하드웨어 정보: {} +[2025-10-24 04:45:21,986] [LogListener] [DEBUG] [loggerModule.py:debug:275] ImageProcessor3 Init toggle_states: {'TEMP_IMAGE_DIR': 'C:\\ProgramData\\ImgWorker\\work', 'output_image_format': 'webp', 'use_local_rembg': True, 'local_rembg_model_path': 'D:\\py\\img_worker\\modules\\rembg_models', 'local_model_name': 'birefnet-general-lite', 'optionIMGTrans_type': 'CPU', 'detail_IMGTrans_type': 'CPU', 'thumb_trans_type': 'CPU', 'migan_use_accel': True, 'migan_onnx_path': 'D:\\py\\img_worker\\modules\\migan_onnx\\migan_pipeline_v2.onnx', 'ocr_provider_override': 'auto', 'migan_provider_override': 'auto'} +[2025-10-24 04:45:21,987] [LogListener] [DEBUG] [loggerModule.py:debug:275] is_member_valid: False +[2025-10-24 04:45:21,987] [LogListener] [DEBUG] [loggerModule.py:debug:275] debug_images 디렉토리 이미 존재: D:\py\img_worker\modules\debug_images +[2025-10-24 04:45:21,987] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.font_path: D:\py\img_worker\modules\fonts\HakgyoansimDunggeunmisoTTFB.ttf +[2025-10-24 04:45:21,987] [LogListener] [DEBUG] [loggerModule.py:debug:275] toggle_states font_path: D:\py\img_worker\modules\fonts\HakgyoansimDunggeunmisoTTFB.ttf +[2025-10-24 04:45:21,987] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.TEMP_IMAGE_DIR: C:\ProgramData\ImgWorker\work +[2025-10-24 04:45:21,987] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.debugging_save_Dir: D:\py\img_worker\modules\debug_images +[2025-10-24 04:45:21,987] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.unwanted_texts: {} +[2025-10-24 04:45:21,987] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.inpaint_method: migan +[2025-10-24 04:45:21,987] [LogListener] [DEBUG] [loggerModule.py:debug:275] Image.MAX_IMAGE_PIXELS set to 20000000 +[2025-10-24 04:45:21,987] [LogListener] [DEBUG] [loggerModule.py:debug:275] ONNX 모델 타입 설정값: 자동 선택, GPU 정보: {} +[2025-10-24 04:45:21,987] [LogListener] [DEBUG] [loggerModule.py:debug:275] 자동 선택 모드: GPU 추천 모델 simp 사용 +[2025-10-24 04:45:21,987] [LogListener] [DEBUG] [loggerModule.py:debug:275] ONNX OCR 모델 타입 결정: simp (GPU: False) +[2025-10-24 04:45:21,987] [LogListener] [DEBUG] [loggerModule.py:debug:275] ONNX OCR 모듈 CPU 모드로 설정 +[2025-10-24 04:45:21,987] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🚀 ONNX TextSystem 초기화 시작 (CPU 모드) +[2025-10-24 04:45:21,987] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🚀 ONNX TextSystem 초기화 시작 (CPU 모드) +[2025-10-24 04:45:23,514] [LogListener] [DEBUG] [loggerModule.py:debug:275] 문자 사전 파일 UTF-8 확인 완료: D:\py\img_worker\modules\onnx_ocr_module\dict\ppocr_keys_v1.txt +[2025-10-24 04:45:23,598] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ ONNX TextSystem 초기화 완료 (CPU + SIMP (호환성) 모델) +[2025-10-24 04:45:23,598] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ ONNX TextSystem 초기화 완료 (CPU + SIMP 모델) +[2025-10-24 04:45:23,599] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ ONNX OCR 모듈 초기화 성공 (CPU 모드) +[2025-10-24 04:45:23,599] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ ONNX OCR 모듈 초기화 성공 +[2025-10-24 04:45:23,599] [LogListener] [DEBUG] [loggerModule.py:debug:275] 마스크 모듈 초기화 완료 +[2025-10-24 04:45:23,599] [LogListener] [DEBUG] [loggerModule.py:debug:275] MaskModule 초기화 성공 +[2025-10-24 04:45:23,600] [LogListener] [DEBUG] [loggerModule.py:debug:275] 텍스트 렌더링 모듈 초기화 완료 +[2025-10-24 04:45:23,600] [LogListener] [DEBUG] [loggerModule.py:debug:275] 기본 폰트: D:\py\img_worker\modules\fonts\HakgyoansimDunggeunmisoTTFB.ttf +[2025-10-24 04:45:23,600] [LogListener] [DEBUG] [loggerModule.py:debug:275] TextRenderingModule 초기화 성공 +[2025-10-24 04:45:23,600] [LogListener] [DEBUG] [loggerModule.py:debug:275] 폰트 로드 성공: D:\py\img_worker\modules\fonts\HakgyoansimDunggeunmisoTTFB.ttf +[2025-10-24 04:45:23,600] [LogListener] [DEBUG] [loggerModule.py:debug:275] PostImageManager 초기화 성공 +[2025-10-24 04:45:23,600] [LogListener] [DEBUG] [loggerModule.py:debug:275] Request_AI_Server GPU 상태: CUDA 사용 가능=False +[2025-10-24 04:45:23,600] [LogListener] [DEBUG] [loggerModule.py:debug:275] Request_AI_Server 초기화 성공 +[2025-10-24 04:45:23,601] [LogListener] [DEBUG] [loggerModule.py:debug:275] GoogleTranslate 초기화 성공 +[2025-10-24 04:45:23,601] [LogListener] [DEBUG] [loggerModule.py:debug:275] MIGAN CUDA 사용 불가 - CPU 모드로 설정 +[2025-10-24 04:45:23,601] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] GPU 관리자 전달: GPUManager, can_use_cuda: False +[2025-10-24 04:45:23,620] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] GPU 관리자 사용 가능: False +[2025-10-24 04:45:23,844] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 사용 가능한 providers: ['DmlExecutionProvider', 'CPUExecutionProvider'] +[2025-10-24 04:45:23,844] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] DirectML 가속 활성화 +[2025-10-24 04:45:23,845] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 최종 providers: [('DmlExecutionProvider', {}), ('CPUExecutionProvider', {})] +[2025-10-24 04:45:23,845] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] DirectML 가속 시도: [('DmlExecutionProvider', {}), ('CPUExecutionProvider', {})] +[2025-10-24 04:45:23,845] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] DirectML 가속 성공! 실제 providers: ['DmlExecutionProvider', 'CPUExecutionProvider'] +[2025-10-24 04:45:23,845] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 입력 0: image, 형태: ['batch_size', 3, 'height', 'width'], 타입: tensor(uint8) +[2025-10-24 04:45:23,845] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 입력 1: mask, 형태: ['batch_size', 1, 'height', 'width'], 타입: tensor(uint8) +[2025-10-24 04:45:23,845] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 출력 0: result, 형태: ['ScatterNDresult_dim_0', 3, 'ScatterNDresult_dim_2', 'ScatterNDresult_dim_3'], 타입: tensor(uint8) +[2025-10-24 04:45:23,845] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 세션 준비 완료. providers=['DmlExecutionProvider', 'CPUExecutionProvider'] +[2025-10-24 04:45:23,845] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] GPU 관리자 연결 완료: GPUManager +[2025-10-24 04:45:23,846] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 파이프라인 gpu_manager 속성: True, 값: +[2025-10-24 04:45:23,846] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 초기화 완료: gpu_manager 속성=True, 값= +[2025-10-24 04:45:23,846] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔰 OCR 모듈 Warm-up 시작... +[2025-10-24 04:45:23,846] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔍 ONNX OCR 감지 방식: polygon +[2025-10-24 04:45:23,851] [LogListener] [DEBUG] [loggerModule.py:debug:275] ⚡ ONNX OCR 추론 완료: 6.0ms +[2025-10-24 04:45:23,851] [LogListener] [DEBUG] [loggerModule.py:debug:275] 📊 세부 시간 - 감지: 3.0ms, 인식: 0.0ms, 분류: 0.0ms +[2025-10-24 04:45:23,851] [LogListener] [WARNING] [loggerModule.py:warning:287] ⚠️ ONNX OCR 결과가 비어있습니다 +[2025-10-24 04:45:23,854] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ OCR 모듈 Warm-up 성공 +[2025-10-24 04:45:23,855] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔰 ImageProcessor Warm‑up 완료 +[2025-10-24 04:45:23,855] [ResultListener] [INFO] [loggerModule.py:info:281] 워커 READY 수신 +[2025-10-24 04:45:23,855] [ResultListener] [INFO] [loggerModule.py:info:281] 워커 READY 수신 +[2025-10-24 04:45:23,855] [LogListener] [DEBUG] [loggerModule.py:debug:275] 📡 추가 READY 신호 전송 완료 +[2025-10-24 04:45:23,855] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 04:45:23,855] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 9300) +[2025-10-24 04:46:23,856] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 04:46:23,856] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 04:46:23,856] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 9300) +[2025-10-24 04:47:23,857] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 04:47:23,857] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 04:47:23,857] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 9300) +[2025-10-24 04:48:23,869] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 04:48:23,869] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 04:48:23,869] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 9300) +[2025-10-24 04:49:23,874] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 04:49:23,874] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 04:49:23,874] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 9300) +[2025-10-24 04:50:23,884] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 04:50:23,885] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 04:50:23,885] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 9300) +[2025-10-24 04:51:23,893] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 04:51:23,893] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 04:51:23,893] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 9300) +[2025-10-24 04:52:23,908] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 04:52:23,908] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 04:52:23,908] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 9300) +[2025-10-24 04:53:23,920] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 04:53:23,920] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 04:53:23,920] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 9300) +[2025-10-24 04:54:23,934] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 04:54:23,934] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 04:54:23,934] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 9300) +[2025-10-24 04:55:23,946] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 04:55:23,946] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 04:55:23,946] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 9300) +[2025-10-24 04:56:23,959] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 04:56:23,959] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 04:56:23,959] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 9300) +[2025-10-24 04:57:23,965] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 04:57:23,965] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 04:57:23,965] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 9300) +[2025-10-24 04:58:23,971] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 04:58:23,971] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 04:58:23,971] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 9300) +[2025-10-24 04:59:23,981] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 04:59:23,981] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 04:59:23,981] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 9300) +[2025-10-24 05:00:23,992] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 05:00:23,992] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 05:00:23,992] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 9300) +[2025-10-24 05:01:23,995] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 05:01:23,995] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 05:01:23,995] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 9300) +[2025-10-24 05:02:24,000] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 05:02:24,000] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 05:02:24,000] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 9300) +[2025-10-24 05:03:24,007] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 05:03:24,007] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 05:03:24,007] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 9300) +[2025-10-24 05:04:24,011] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 05:04:24,011] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 05:04:24,011] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 9300) +[2025-10-24 05:05:24,019] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 05:05:24,019] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 05:05:24,019] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 9300) +[2025-10-24 05:06:24,026] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 05:06:24,026] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 05:06:24,026] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 9300) +[2025-10-24 05:07:24,030] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 05:07:24,030] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 05:07:24,030] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 9300) +[2025-10-24 05:08:24,032] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 05:08:24,032] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 05:08:24,032] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 9300) +[2025-10-24 05:09:24,047] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 05:09:24,047] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 05:09:24,047] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 9300) +[2025-10-24 05:10:24,051] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 05:10:24,051] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 05:10:24,051] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 9300) +[2025-10-24 05:11:24,064] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 05:11:24,064] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 05:11:24,064] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 9300) +[2025-10-24 05:12:24,073] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 05:12:24,073] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 05:12:24,073] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 9300) +[2025-10-24 05:13:24,085] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 05:13:24,085] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 05:13:24,085] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 9300) +[2025-10-24 05:14:24,087] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 05:14:24,087] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 05:14:24,087] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 9300) +[2025-10-24 05:15:24,097] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 05:15:24,097] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 05:15:24,097] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 9300) +[2025-10-24 05:16:24,099] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 05:16:24,099] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 05:16:24,099] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 9300) +[2025-10-24 05:17:24,111] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 05:17:24,111] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 05:17:24,111] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 9300) +[2025-10-24 05:18:24,124] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 05:18:24,124] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 05:18:24,124] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 9300) +[2025-10-24 05:19:24,137] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 05:19:24,137] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 05:19:24,137] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 9300) +[2025-10-24 05:20:24,147] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 05:20:24,147] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 05:20:24,147] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 9300) +[2025-10-24 05:21:24,158] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 05:21:24,158] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 05:21:24,158] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 9300) +[2025-10-24 05:22:24,173] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 05:22:24,173] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 05:22:24,173] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 9300) +[2025-10-24 05:23:24,182] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 05:23:24,182] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 05:23:24,182] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 9300) +[2025-10-24 05:24:24,190] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 05:24:24,190] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 05:24:24,190] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 9300) +[2025-10-24 05:25:24,204] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 05:25:24,204] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 05:25:24,204] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 9300) +[2025-10-24 05:26:24,211] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 05:26:24,211] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 05:26:24,211] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 9300) +[2025-10-24 05:27:24,218] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 05:27:24,218] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 05:27:24,218] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 9300) +[2025-10-24 05:28:24,229] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 05:28:24,229] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 05:28:24,229] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 9300) +[2025-10-24 05:29:24,242] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 05:29:24,242] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 05:29:24,242] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 9300) +[2025-10-24 05:30:24,256] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 05:30:24,256] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 05:30:24,256] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 9300) +[2025-10-24 05:31:24,271] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 05:31:24,271] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 05:31:24,271] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 9300) +[2025-10-24 05:32:24,280] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 05:32:24,280] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 05:32:24,280] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 9300) +[2025-10-24 05:33:24,294] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 05:33:24,294] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 05:33:24,294] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 9300) +[2025-10-24 05:34:24,303] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 05:34:24,303] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 05:34:24,303] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 9300) +[2025-10-24 05:35:24,309] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 05:35:24,309] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 05:35:24,309] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 9300) +[2025-10-24 05:36:24,310] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 05:36:24,310] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 05:36:24,310] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 9300) +[2025-10-24 05:37:24,319] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 05:37:24,319] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 05:37:24,319] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 9300) +[2025-10-24 05:38:24,331] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 05:38:24,331] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 05:38:24,331] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 9300) +[2025-10-24 05:39:24,332] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 05:39:24,332] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 05:39:24,332] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 9300) +[2025-10-24 05:40:24,335] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 05:40:24,335] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 05:40:24,335] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 9300) +[2025-10-24 05:41:24,339] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 05:41:24,339] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 05:41:24,339] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 9300) +[2025-10-24 05:42:24,344] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 05:42:24,344] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 05:42:24,344] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 9300) +[2025-10-24 05:43:24,350] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 05:43:24,350] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 05:43:24,350] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 9300) +[2025-10-24 05:44:24,360] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 05:44:24,360] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 05:44:24,360] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 9300) +[2025-10-24 05:45:24,372] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 05:45:24,372] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 05:45:24,372] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 9300) +[2025-10-24 05:46:24,373] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 05:46:24,373] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 05:46:24,373] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 9300) +[2025-10-24 05:47:24,385] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 05:47:24,385] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 05:47:24,385] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 9300) +[2025-10-24 05:48:24,398] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 05:48:24,398] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 05:48:24,398] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 9300) +[2025-10-24 05:49:24,404] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 05:49:24,404] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 05:49:24,404] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 9300) +[2025-10-24 05:50:24,416] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 05:50:24,416] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 05:50:24,416] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 9300) +[2025-10-24 05:51:24,420] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 05:51:24,420] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 05:51:24,420] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 9300) +[2025-10-24 05:52:24,423] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 05:52:24,423] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 05:52:24,423] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 9300) +[2025-10-24 05:53:24,436] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 05:53:24,436] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 05:53:24,436] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 9300) +[2025-10-24 05:54:24,450] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 05:54:24,450] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 05:54:24,450] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 9300) +[2025-10-24 05:55:24,455] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 05:55:24,455] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 05:55:24,455] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 9300) +[2025-10-24 05:56:24,459] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 05:56:24,459] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 05:56:24,459] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 9300) +[2025-10-24 05:57:24,467] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 05:57:24,467] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 05:57:24,467] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 9300) +[2025-10-24 05:58:24,475] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 05:58:24,475] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 05:58:24,475] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 9300) +[2025-10-24 05:59:24,481] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 05:59:24,481] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 05:59:24,481] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 9300) +[2025-10-24 06:00:24,482] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 06:00:24,482] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 06:00:24,482] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 9300) +[2025-10-24 06:01:24,492] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 06:01:24,492] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 06:01:24,492] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 9300) +[2025-10-24 06:02:24,504] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 06:02:24,504] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 06:02:24,504] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 9300) +[2025-10-24 06:03:24,511] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 06:03:24,511] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 06:03:24,511] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 9300) +[2025-10-24 06:04:24,514] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 06:04:24,514] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 06:04:24,514] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 9300) +[2025-10-24 06:05:24,520] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 06:05:24,520] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 06:05:24,520] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 9300) +[2025-10-24 06:06:24,521] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 06:06:24,521] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 06:06:24,521] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 9300) +[2025-10-24 06:07:24,533] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 06:07:24,533] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 06:07:24,533] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 9300) +[2025-10-24 06:08:24,543] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 06:08:24,543] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 06:08:24,543] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 9300) +[2025-10-24 06:09:24,547] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 06:09:24,547] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 06:09:24,547] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 9300) +[2025-10-24 06:10:24,559] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 06:10:24,559] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 06:10:24,559] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 9300) +[2025-10-24 06:11:24,571] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 06:11:24,571] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 06:11:24,571] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 9300) +[2025-10-24 06:12:24,572] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 06:12:24,572] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 06:12:24,572] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 9300) +[2025-10-24 06:13:24,583] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 06:13:24,583] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 06:13:24,583] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 9300) +[2025-10-24 06:14:24,588] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 06:14:24,588] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 06:14:24,588] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 9300) +[2025-10-24 06:15:24,600] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 06:15:24,600] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 06:15:24,600] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 9300) +[2025-10-24 06:16:24,606] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 06:16:24,606] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 06:16:24,606] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 9300) +[2025-10-24 06:17:24,620] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 06:17:24,620] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 06:17:24,620] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 9300) +[2025-10-24 06:18:24,626] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 06:18:24,626] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 06:18:24,626] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 9300) +[2025-10-24 06:19:24,640] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 06:19:24,640] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 06:19:24,640] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 9300) +[2025-10-24 06:20:24,647] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 06:20:24,647] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 06:20:24,647] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 9300) +[2025-10-24 06:21:24,656] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 06:21:24,656] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 06:21:24,656] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 9300) +[2025-10-24 06:22:24,663] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 06:22:24,663] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 06:22:24,663] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 9300) +[2025-10-24 06:23:24,667] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 06:23:24,667] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 06:23:24,667] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 9300) +[2025-10-24 06:24:24,682] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 06:24:24,682] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 06:24:24,682] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 9300) +[2025-10-24 06:25:24,697] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 06:25:24,697] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 06:25:24,697] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 9300) +[2025-10-24 06:26:24,697] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 06:26:24,697] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 06:26:24,697] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 9300) +[2025-10-24 06:27:24,705] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 06:27:24,705] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 06:27:24,705] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 9300) +[2025-10-24 06:28:24,707] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 06:28:24,707] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 06:28:24,707] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 9300) +[2025-10-24 06:29:24,721] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 06:29:24,721] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 06:29:24,721] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 9300) +[2025-10-24 06:30:24,732] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 06:30:24,732] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 06:30:24,732] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 9300) +[2025-10-24 06:31:24,740] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 06:31:24,740] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 06:31:24,740] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 9300) +[2025-10-24 06:32:24,746] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 06:32:24,746] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 06:32:24,746] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 9300) +[2025-10-24 06:33:24,747] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 06:33:24,747] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 06:33:24,747] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 9300) +[2025-10-24 06:34:24,747] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 06:34:24,747] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 06:34:24,747] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 9300) +[2025-10-24 06:35:24,751] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 06:35:24,751] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 06:35:24,751] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 9300) +[2025-10-24 06:36:24,761] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 06:36:24,761] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 06:36:24,761] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 9300) +[2025-10-24 06:37:24,765] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 06:37:24,765] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 06:37:24,765] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 9300) +[2025-10-24 06:38:24,781] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 06:38:24,781] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 06:38:24,781] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 9300) +[2025-10-24 06:39:24,796] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 06:39:24,796] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 06:39:24,796] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 9300) +[2025-10-24 06:40:24,804] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 06:40:24,804] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 06:40:24,804] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 9300) +[2025-10-24 06:41:24,810] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 06:41:24,810] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 06:41:24,810] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 9300) +[2025-10-24 06:42:24,815] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 06:42:24,815] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 06:42:24,815] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 9300) +[2025-10-24 06:43:24,826] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 06:43:24,826] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 06:43:24,826] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 9300) +[2025-10-24 06:44:24,830] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 06:44:24,830] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 06:44:24,830] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 9300) +[2025-10-24 06:45:24,841] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 06:45:24,841] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 06:45:24,841] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 9300) +[2025-10-24 06:45:25,565] [MemMonitor] [WARNING] [loggerModule.py:warning:287] 워커 롤링 스케줄: reason=uptime-threshold +[2025-10-24 06:45:25,565] [WorkerRoller] [WARNING] [loggerModule.py:warning:287] 워커 롤링 시작: uptime-threshold +[2025-10-24 06:45:25,604] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔥 작업 수신 성공 +[2025-10-24 06:45:25,605] [LogListener] [DEBUG] [loggerModule.py:debug:275] Shutdown signal 수신 → 종료 +[2025-10-24 06:45:25,605] [LogListener] [DEBUG] [loggerModule.py:debug:275] OCR 모듈 정리 완료 +[2025-10-24 06:45:25,605] [LogListener] [DEBUG] [loggerModule.py:debug:275] 마스크 모듈 정리 완료 +[2025-10-24 06:45:25,605] [LogListener] [DEBUG] [loggerModule.py:debug:275] 임시 폴더 삭제됨: C:\ProgramData\ImgWorker\work +[2025-10-24 06:45:25,605] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 프로세서 소멸 +[2025-10-24 06:45:25,854] [WorkerRoller] [INFO] [loggerModule.py:info:281] 워커 프로세스 기동: PID=27968 +[2025-10-24 06:45:26,305] [LogListener] [DEBUG] [loggerModule.py:debug:275] ImageWorker 프로세스 기동 (PID=27968, Name=ImageWorkerProcess) +[2025-10-24 06:45:26,305] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔧 ImageProcessor3 초기화 시작... +[2025-10-24 06:45:26,305] [LogListener] [DEBUG] [loggerModule.py:debug:275] tracemalloc 메모리 추적 시작 +[2025-10-24 06:45:26,311] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ DirectML 환경 준비 완료 (Windows DirectX 12 기반) +[2025-10-24 06:45:26,311] [LogListener] [DEBUG] [loggerModule.py:debug:275] === 🚀 DirectML GPU 상태 초기화 시작 🚀 === +[2025-10-24 06:45:26,311] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🎯 사용자 GPU 가속 요청: False +[2025-10-24 06:45:26,311] [LogListener] [DEBUG] [loggerModule.py:debug:275] 💻 현재 운영체제: Windows +[2025-10-24 06:45:26,312] [LogListener] [DEBUG] [loggerModule.py:debug:275] GPU 가속이 비활성화됨 (toggle_states['use_cuda'] = False) +[2025-10-24 06:45:26,312] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔒 안전한 CPU 모드로 모든 GPU 설정 강제 비활성화 +[2025-10-24 06:45:26,312] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔧 ImageProcessor3 GPU 상태 요약: +[2025-10-24 06:45:26,312] [LogListener] [DEBUG] [loggerModule.py:debug:275] - CUDA 사용 가능: False +[2025-10-24 06:45:26,312] [LogListener] [DEBUG] [loggerModule.py:debug:275] - toggle_states['use_cuda']: NOT_SET +[2025-10-24 06:45:26,312] [LogListener] [DEBUG] [loggerModule.py:debug:275] - GPU 하드웨어 정보: {} +[2025-10-24 06:45:26,312] [LogListener] [DEBUG] [loggerModule.py:debug:275] ImageProcessor3 Init toggle_states: {'TEMP_IMAGE_DIR': 'C:\\ProgramData\\ImgWorker\\work', 'output_image_format': 'webp', 'use_local_rembg': True, 'local_rembg_model_path': 'D:\\py\\img_worker\\modules\\rembg_models', 'local_model_name': 'birefnet-general-lite', 'optionIMGTrans_type': 'CPU', 'detail_IMGTrans_type': 'CPU', 'thumb_trans_type': 'CPU', 'migan_use_accel': True, 'migan_onnx_path': 'D:\\py\\img_worker\\modules\\migan_onnx\\migan_pipeline_v2.onnx', 'ocr_provider_override': 'auto', 'migan_provider_override': 'auto'} +[2025-10-24 06:45:26,312] [LogListener] [DEBUG] [loggerModule.py:debug:275] is_member_valid: False +[2025-10-24 06:45:26,312] [LogListener] [DEBUG] [loggerModule.py:debug:275] debug_images 디렉토리 이미 존재: D:\py\img_worker\modules\debug_images +[2025-10-24 06:45:26,312] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.font_path: D:\py\img_worker\modules\fonts\HakgyoansimDunggeunmisoTTFB.ttf +[2025-10-24 06:45:26,312] [LogListener] [DEBUG] [loggerModule.py:debug:275] toggle_states font_path: D:\py\img_worker\modules\fonts\HakgyoansimDunggeunmisoTTFB.ttf +[2025-10-24 06:45:26,313] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.TEMP_IMAGE_DIR: C:\ProgramData\ImgWorker\work +[2025-10-24 06:45:26,313] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.debugging_save_Dir: D:\py\img_worker\modules\debug_images +[2025-10-24 06:45:26,313] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.unwanted_texts: {} +[2025-10-24 06:45:26,313] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.inpaint_method: migan +[2025-10-24 06:45:26,313] [LogListener] [DEBUG] [loggerModule.py:debug:275] Image.MAX_IMAGE_PIXELS set to 20000000 +[2025-10-24 06:45:26,313] [LogListener] [DEBUG] [loggerModule.py:debug:275] ONNX 모델 타입 설정값: 자동 선택, GPU 정보: {} +[2025-10-24 06:45:26,313] [LogListener] [DEBUG] [loggerModule.py:debug:275] 자동 선택 모드: GPU 추천 모델 simp 사용 +[2025-10-24 06:45:26,313] [LogListener] [DEBUG] [loggerModule.py:debug:275] ONNX OCR 모델 타입 결정: simp (GPU: False) +[2025-10-24 06:45:26,313] [LogListener] [DEBUG] [loggerModule.py:debug:275] ONNX OCR 모듈 CPU 모드로 설정 +[2025-10-24 06:45:26,313] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🚀 ONNX TextSystem 초기화 시작 (CPU 모드) +[2025-10-24 06:45:26,313] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🚀 ONNX TextSystem 초기화 시작 (CPU 모드) +[2025-10-24 06:45:27,850] [LogListener] [DEBUG] [loggerModule.py:debug:275] 문자 사전 파일 UTF-8 확인 완료: D:\py\img_worker\modules\onnx_ocr_module\dict\ppocr_keys_v1.txt +[2025-10-24 06:45:27,933] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ ONNX TextSystem 초기화 완료 (CPU + SIMP (호환성) 모델) +[2025-10-24 06:45:27,934] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ ONNX TextSystem 초기화 완료 (CPU + SIMP 모델) +[2025-10-24 06:45:27,934] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ ONNX OCR 모듈 초기화 성공 (CPU 모드) +[2025-10-24 06:45:27,934] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ ONNX OCR 모듈 초기화 성공 +[2025-10-24 06:45:27,934] [LogListener] [DEBUG] [loggerModule.py:debug:275] 마스크 모듈 초기화 완료 +[2025-10-24 06:45:27,934] [LogListener] [DEBUG] [loggerModule.py:debug:275] MaskModule 초기화 성공 +[2025-10-24 06:45:27,935] [LogListener] [DEBUG] [loggerModule.py:debug:275] 텍스트 렌더링 모듈 초기화 완료 +[2025-10-24 06:45:27,935] [LogListener] [DEBUG] [loggerModule.py:debug:275] 기본 폰트: D:\py\img_worker\modules\fonts\HakgyoansimDunggeunmisoTTFB.ttf +[2025-10-24 06:45:27,935] [LogListener] [DEBUG] [loggerModule.py:debug:275] TextRenderingModule 초기화 성공 +[2025-10-24 06:45:27,935] [LogListener] [DEBUG] [loggerModule.py:debug:275] 폰트 로드 성공: D:\py\img_worker\modules\fonts\HakgyoansimDunggeunmisoTTFB.ttf +[2025-10-24 06:45:27,935] [LogListener] [DEBUG] [loggerModule.py:debug:275] PostImageManager 초기화 성공 +[2025-10-24 06:45:27,935] [LogListener] [DEBUG] [loggerModule.py:debug:275] Request_AI_Server GPU 상태: CUDA 사용 가능=False +[2025-10-24 06:45:27,935] [LogListener] [DEBUG] [loggerModule.py:debug:275] Request_AI_Server 초기화 성공 +[2025-10-24 06:45:27,935] [LogListener] [DEBUG] [loggerModule.py:debug:275] GoogleTranslate 초기화 성공 +[2025-10-24 06:45:27,935] [LogListener] [DEBUG] [loggerModule.py:debug:275] MIGAN CUDA 사용 불가 - CPU 모드로 설정 +[2025-10-24 06:45:27,935] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] GPU 관리자 전달: GPUManager, can_use_cuda: False +[2025-10-24 06:45:27,953] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] GPU 관리자 사용 가능: False +[2025-10-24 06:45:28,168] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 사용 가능한 providers: ['DmlExecutionProvider', 'CPUExecutionProvider'] +[2025-10-24 06:45:28,169] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] DirectML 가속 활성화 +[2025-10-24 06:45:28,169] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 최종 providers: [('DmlExecutionProvider', {}), ('CPUExecutionProvider', {})] +[2025-10-24 06:45:28,169] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] DirectML 가속 시도: [('DmlExecutionProvider', {}), ('CPUExecutionProvider', {})] +[2025-10-24 06:45:28,169] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] DirectML 가속 성공! 실제 providers: ['DmlExecutionProvider', 'CPUExecutionProvider'] +[2025-10-24 06:45:28,169] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 입력 0: image, 형태: ['batch_size', 3, 'height', 'width'], 타입: tensor(uint8) +[2025-10-24 06:45:28,169] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 입력 1: mask, 형태: ['batch_size', 1, 'height', 'width'], 타입: tensor(uint8) +[2025-10-24 06:45:28,169] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 출력 0: result, 형태: ['ScatterNDresult_dim_0', 3, 'ScatterNDresult_dim_2', 'ScatterNDresult_dim_3'], 타입: tensor(uint8) +[2025-10-24 06:45:28,169] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 세션 준비 완료. providers=['DmlExecutionProvider', 'CPUExecutionProvider'] +[2025-10-24 06:45:28,170] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] GPU 관리자 연결 완료: GPUManager +[2025-10-24 06:45:28,170] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 파이프라인 gpu_manager 속성: True, 값: +[2025-10-24 06:45:28,170] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 초기화 완료: gpu_manager 속성=True, 값= +[2025-10-24 06:45:28,170] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔰 OCR 모듈 Warm-up 시작... +[2025-10-24 06:45:28,170] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔍 ONNX OCR 감지 방식: polygon +[2025-10-24 06:45:28,174] [LogListener] [DEBUG] [loggerModule.py:debug:275] ⚡ ONNX OCR 추론 완료: 4.0ms +[2025-10-24 06:45:28,174] [LogListener] [DEBUG] [loggerModule.py:debug:275] 📊 세부 시간 - 감지: 3.0ms, 인식: 0.0ms, 분류: 0.0ms +[2025-10-24 06:45:28,174] [LogListener] [WARNING] [loggerModule.py:warning:287] ⚠️ ONNX OCR 결과가 비어있습니다 +[2025-10-24 06:45:28,178] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ OCR 모듈 Warm-up 성공 +[2025-10-24 06:45:28,178] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔰 ImageProcessor Warm‑up 완료 +[2025-10-24 06:45:28,178] [ResultListener] [INFO] [loggerModule.py:info:281] 워커 READY 수신 +[2025-10-24 06:45:28,178] [ResultListener] [INFO] [loggerModule.py:info:281] 워커 READY 수신 +[2025-10-24 06:45:28,178] [LogListener] [DEBUG] [loggerModule.py:debug:275] 📡 추가 READY 신호 전송 완료 +[2025-10-24 06:45:28,178] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 06:45:28,178] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 27968) +[2025-10-24 06:46:28,185] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 06:46:28,185] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 06:46:28,185] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 27968) +[2025-10-24 06:47:28,188] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 06:47:28,188] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 06:47:28,188] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 27968) +[2025-10-24 06:48:28,193] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 06:48:28,193] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 06:48:28,193] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 27968) +[2025-10-24 06:49:28,208] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 06:49:28,208] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 06:49:28,208] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 27968) +[2025-10-24 06:50:28,222] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 06:50:28,222] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 06:50:28,222] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 27968) +[2025-10-24 06:51:28,230] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 06:51:28,230] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 06:51:28,230] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 27968) +[2025-10-24 06:52:28,235] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 06:52:28,236] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 06:52:28,236] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 27968) +[2025-10-24 06:53:28,236] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 06:53:28,236] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 06:53:28,236] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 27968) +[2025-10-24 06:54:28,239] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 06:54:28,239] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 06:54:28,239] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 27968) +[2025-10-24 06:55:28,242] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 06:55:28,242] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 06:55:28,242] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 27968) +[2025-10-24 06:56:28,254] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 06:56:28,254] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 06:56:28,254] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 27968) +[2025-10-24 06:57:28,263] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 06:57:28,263] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 06:57:28,263] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 27968) +[2025-10-24 06:58:28,264] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 06:58:28,264] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 06:58:28,264] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 27968) +[2025-10-24 06:59:28,265] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 06:59:28,265] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 06:59:28,265] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 27968) +[2025-10-24 07:00:28,278] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 07:00:28,278] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 07:00:28,278] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 27968) +[2025-10-24 07:01:28,292] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 07:01:28,292] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 07:01:28,292] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 27968) +[2025-10-24 07:02:28,300] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 07:02:28,300] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 07:02:28,300] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 27968) +[2025-10-24 07:03:28,313] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 07:03:28,313] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 07:03:28,313] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 27968) +[2025-10-24 07:04:28,321] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 07:04:28,321] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 07:04:28,321] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 27968) +[2025-10-24 07:05:28,335] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 07:05:28,335] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 07:05:28,335] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 27968) +[2025-10-24 07:06:28,350] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 07:06:28,350] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 07:06:28,350] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 27968) +[2025-10-24 07:07:28,365] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 07:07:28,365] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 07:07:28,365] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 27968) +[2025-10-24 07:08:28,372] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 07:08:28,372] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 07:08:28,372] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 27968) +[2025-10-24 07:09:28,375] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 07:09:28,375] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 07:09:28,375] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 27968) +[2025-10-24 07:10:28,383] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 07:10:28,383] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 07:10:28,383] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 27968) +[2025-10-24 07:11:28,389] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 07:11:28,389] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 07:11:28,389] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 27968) +[2025-10-24 07:12:28,389] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 07:12:28,389] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 07:12:28,389] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 27968) +[2025-10-24 07:13:28,394] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 07:13:28,394] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 07:13:28,394] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 27968) +[2025-10-24 07:14:28,395] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 07:14:28,395] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 07:14:28,395] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 27968) +[2025-10-24 07:15:28,403] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 07:15:28,403] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 07:15:28,403] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 27968) +[2025-10-24 07:16:28,414] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 07:16:28,414] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 07:16:28,414] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 27968) +[2025-10-24 07:17:28,417] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 07:17:28,417] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 07:17:28,417] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 27968) +[2025-10-24 07:18:28,425] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 07:18:28,425] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 07:18:28,425] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 27968) +[2025-10-24 07:19:28,437] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 07:19:28,437] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 07:19:28,437] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 27968) +[2025-10-24 07:20:28,446] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 07:20:28,446] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 07:20:28,446] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 27968) +[2025-10-24 07:21:28,447] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 07:21:28,448] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 07:21:28,448] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 27968) +[2025-10-24 07:22:28,454] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 07:22:28,454] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 07:22:28,454] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 27968) +[2025-10-24 07:23:28,462] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 07:23:28,462] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 07:23:28,462] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 27968) +[2025-10-24 07:24:28,474] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 07:24:28,474] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 07:24:28,474] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 27968) +[2025-10-24 07:25:28,484] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 07:25:28,484] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 07:25:28,484] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 27968) +[2025-10-24 07:26:28,490] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 07:26:28,490] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 07:26:28,490] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 27968) +[2025-10-24 07:27:28,492] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 07:27:28,492] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 07:27:28,492] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 27968) +[2025-10-24 07:28:28,507] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 07:28:28,507] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 07:28:28,507] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 27968) +[2025-10-24 07:29:28,521] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 07:29:28,521] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 07:29:28,521] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 27968) +[2025-10-24 07:30:28,526] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 07:30:28,526] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 07:30:28,526] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 27968) +[2025-10-24 07:31:28,526] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 07:31:28,526] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 07:31:28,526] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 27968) +[2025-10-24 07:32:28,529] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 07:32:28,529] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 07:32:28,529] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 27968) +[2025-10-24 07:33:28,535] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 07:33:28,535] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 07:33:28,535] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 27968) +[2025-10-24 07:34:28,536] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 07:34:28,536] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 07:34:28,536] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 27968) +[2025-10-24 07:35:28,537] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 07:35:28,537] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 07:35:28,537] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 27968) +[2025-10-24 07:36:28,539] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 07:36:28,539] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 07:36:28,539] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 27968) +[2025-10-24 07:37:28,543] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 07:37:28,543] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 07:37:28,543] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 27968) +[2025-10-24 07:38:28,554] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 07:38:28,554] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 07:38:28,554] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 27968) +[2025-10-24 07:39:28,567] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 07:39:28,567] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 07:39:28,567] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 27968) +[2025-10-24 07:40:28,575] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 07:40:28,575] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 07:40:28,575] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 27968) +[2025-10-24 07:41:28,584] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 07:41:28,584] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 07:41:28,584] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 27968) +[2025-10-24 07:42:28,586] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 07:42:28,586] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 07:42:28,586] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 27968) +[2025-10-24 07:43:28,600] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 07:43:28,600] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 07:43:28,600] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 27968) +[2025-10-24 07:44:28,602] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 07:44:28,602] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 07:44:28,602] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 27968) +[2025-10-24 07:45:28,604] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 07:45:28,604] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 07:45:28,604] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 27968) +[2025-10-24 07:46:28,615] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 07:46:28,615] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 07:46:28,615] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 27968) +[2025-10-24 07:47:28,627] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 07:47:28,627] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 07:47:28,627] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 27968) +[2025-10-24 07:48:28,633] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 07:48:28,633] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 07:48:28,633] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 27968) +[2025-10-24 07:49:28,645] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 07:49:28,646] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 07:49:28,646] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 27968) +[2025-10-24 07:50:28,647] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 07:50:28,647] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 07:50:28,647] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 27968) +[2025-10-24 07:51:28,648] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 07:51:28,648] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 07:51:28,648] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 27968) +[2025-10-24 07:52:28,656] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 07:52:28,657] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 07:52:28,657] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 27968) +[2025-10-24 07:53:28,672] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 07:53:28,672] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 07:53:28,672] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 27968) +[2025-10-24 07:54:28,687] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 07:54:28,687] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 07:54:28,687] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 27968) +[2025-10-24 07:55:28,691] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 07:55:28,691] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 07:55:28,691] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 27968) +[2025-10-24 07:56:28,700] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 07:56:28,700] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 07:56:28,700] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 27968) +[2025-10-24 07:57:28,700] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 07:57:28,700] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 07:57:28,700] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 27968) +[2025-10-24 07:58:28,710] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 07:58:28,711] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 07:58:28,711] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 27968) +[2025-10-24 07:59:28,714] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 07:59:28,714] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 07:59:28,714] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 27968) +[2025-10-24 08:00:28,728] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 08:00:28,728] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 08:00:28,728] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 27968) +[2025-10-24 08:01:28,732] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 08:01:28,732] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 08:01:28,732] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 27968) +[2025-10-24 08:02:28,741] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 08:02:28,741] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 08:02:28,741] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 27968) +[2025-10-24 08:03:28,756] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 08:03:28,756] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 08:03:28,756] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 27968) +[2025-10-24 08:04:28,769] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 08:04:28,770] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 08:04:28,770] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 27968) +[2025-10-24 08:05:28,780] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 08:05:28,780] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 08:05:28,780] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 27968) +[2025-10-24 08:06:28,781] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 08:06:28,781] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 08:06:28,781] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 27968) +[2025-10-24 08:07:28,785] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 08:07:28,785] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 08:07:28,785] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 27968) +[2025-10-24 08:08:28,786] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 08:08:28,786] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 08:08:28,786] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 27968) +[2025-10-24 08:09:28,800] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 08:09:28,800] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 08:09:28,800] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 27968) +[2025-10-24 08:10:28,816] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 08:10:28,816] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 08:10:28,816] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 27968) +[2025-10-24 08:11:28,822] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 08:11:28,822] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 08:11:28,822] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 27968) +[2025-10-24 08:12:28,826] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 08:12:28,826] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 08:12:28,826] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 27968) +[2025-10-24 08:13:28,829] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 08:13:28,829] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 08:13:28,829] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 27968) +[2025-10-24 08:14:28,840] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 08:14:28,841] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 08:14:28,841] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 27968) +[2025-10-24 08:15:28,849] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 08:15:28,849] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 08:15:28,849] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 27968) +[2025-10-24 08:16:28,863] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 08:16:28,863] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 08:16:28,863] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 27968) +[2025-10-24 08:17:28,869] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 08:17:28,869] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 08:17:28,869] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 27968) +[2025-10-24 08:18:28,879] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 08:18:28,879] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 08:18:28,879] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 27968) +[2025-10-24 08:19:28,883] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 08:19:28,883] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 08:19:28,883] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 27968) +[2025-10-24 08:20:28,895] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 08:20:28,895] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 08:20:28,895] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 27968) +[2025-10-24 08:21:28,897] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 08:21:28,897] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 08:21:28,897] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 27968) +[2025-10-24 08:22:28,901] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 08:22:28,901] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 08:22:28,901] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 27968) +[2025-10-24 08:23:28,904] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 08:23:28,904] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 08:23:28,904] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 27968) +[2025-10-24 08:24:28,906] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 08:24:28,906] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 08:24:28,906] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 27968) +[2025-10-24 08:25:28,913] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 08:25:28,913] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 08:25:28,913] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 27968) +[2025-10-24 08:26:28,920] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 08:26:28,920] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 08:26:28,920] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 27968) +[2025-10-24 08:27:28,922] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 08:27:28,922] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 08:27:28,922] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 27968) +[2025-10-24 08:28:28,922] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 08:28:28,922] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 08:28:28,922] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 27968) +[2025-10-24 08:29:28,930] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 08:29:28,930] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 08:29:28,930] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 27968) +[2025-10-24 08:30:28,942] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 08:30:28,942] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 08:30:28,942] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 27968) +[2025-10-24 08:31:28,945] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 08:31:28,945] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 08:31:28,945] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 27968) +[2025-10-24 08:32:28,954] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 08:32:28,954] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 08:32:28,954] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 27968) +[2025-10-24 08:33:06,992] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔥 작업 수신 성공 +[2025-10-24 08:33:06,992] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🚀 작업 처리 시작: cmd=process_single_image, uid=cd96bcdd-e4c5-472c-8b8c-c67f1eb3c8ff +[2025-10-24 08:33:06,992] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 프로세서 toggle_states 업데이트 : {'TEMP_IMAGE_DIR': 'C:\\ProgramData\\ImgWorker\\work', 'output_image_format': 'webp', 'use_local_rembg': True, 'local_rembg_model_path': 'D:\\py\\img_worker\\modules\\rembg_models', 'local_model_name': 'birefnet-general-lite', 'optionIMGTrans_type': 'GPU', 'detail_IMGTrans_type': 'GPU', 'thumb_trans_type': 'GPU', 'migan_use_accel': True, 'migan_onnx_path': 'D:\\py\\img_worker\\modules\\migan_onnx\\migan_pipeline_v2.onnx', 'ocr_provider_override': 'auto', 'migan_provider_override': 'auto'} +[2025-10-24 08:33:06,993] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 실행 직전: cmd=process_single_image +[2025-10-24 08:33:06,993] [LogListener] [DEBUG] [loggerModule.py:debug:275] process_single_image 호출 직전 +[2025-10-24 08:33:06,993] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 번역시작 +[2025-10-24 08:33:07,218] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 2 처리 시작: D:\py\img_worker\modules\test\2.jpg - 전체 번역 모드 +[2025-10-24 08:33:07,219] [LogListener] [DEBUG] [loggerModule.py:debug:275] 로컬 파일 경로 감지, 다운로드 생략: D:\py\img_worker\modules\test\2.jpg +[2025-10-24 08:33:07,220] [LogListener] [DEBUG] [loggerModule.py:debug:275] 상세페이지 이미지 2 원본 크기: 640x640 +[2025-10-24 08:33:07,226] [LogListener] [DEBUG] [loggerModule.py:debug:275] 상세페이지 이미지 2 가로 크기 조정: 640x640 → 860x860 +[2025-10-24 08:33:07,233] [LogListener] [DEBUG] [loggerModule.py:debug:275] 상세페이지 이미지 2 크기 조정 완료: 860x860 +[2025-10-24 08:33:07,234] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 2 로컬 저장위치(상세페이지 전처리 완료): D:\py\img_worker\modules\test\2_resized.jpg +[2025-10-24 08:33:07,236] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔍 ONNX OCR 감지 방식: polygon +[2025-10-24 08:33:08,218] [LogListener] [DEBUG] [loggerModule.py:debug:275] ⚡ ONNX OCR 추론 완료: 979.0ms +[2025-10-24 08:33:08,218] [LogListener] [DEBUG] [loggerModule.py:debug:275] 📊 세부 시간 - 감지: 126.0ms, 인식: 823.0ms, 분류: 23.0ms +[2025-10-24 08:33:08,237] [LogListener] [DEBUG] [loggerModule.py:debug:275] 메모리 변화 [OCR 처리]: 38935.1MB -> 39276.6MB (+341.5MB, +0.9%) - 이미지 2 +[2025-10-24 08:33:08,245] [LogListener] [DEBUG] [loggerModule.py:debug:275] ocr_results: [{'text': '科尔诺', 'confidence': 0.9992308616638184, 'polygon': [[96.0, 27.0], [211.0, 25.0], [212.0, 63.0], [97.0, 66.0]], 'bbox': (96, 25, 117, 42), 'method': 'polygon'}, {'text': 'PA', 'confidence': 0.9930515289306641, 'polygon': [[414.0, 28.0], [470.0, 32.0], [468.0, 59.0], [413.0, 55.0]], 'bbox': (413, 28, 58, 32), 'method': 'polygon'}, {'text': 'CE', 'confidence': 0.9594120979309082, 'polygon': [[665.0, 24.0], [728.0, 24.0], [728.0, 66.0], [665.0, 66.0]], 'bbox': (665, 24, 64, 43), 'method': 'polygon'}, {'text': 'SGS', 'confidence': 0.9917036890983582, 'polygon': [[751.0, 24.0], [821.0, 24.0], [821.0, 61.0], [751.0, 61.0]], 'bbox': (751, 24, 71, 38), 'method': 'polygon'}, {'text': 'CNEX', 'confidence': 0.9931035041809082, 'polygon': [[500.0, 34.0], [569.0, 34.0], [569.0, 55.0], [500.0, 55.0]], 'bbox': (500, 34, 70, 22), 'method': 'polygon'}, {'text': 'S', 'confidence': 0.8341087698936462, 'polygon': [[595.0, 37.0], [618.0, 37.0], [618.0, 48.0], [595.0, 48.0]], 'bbox': (595, 37, 24, 12), 'method': 'polygon'}, {'text': 'KORNO', 'confidence': 0.9965640306472778, 'polygon': [[103.0, 73.0], [211.0, 73.0], [211.0, 98.0], [103.0, 98.0]], 'bbox': (103, 73, 109, 26), 'method': 'polygon'}, {'text': 'CMC认证', 'confidence': 0.9940687417984009, 'polygon': [[321.0, 77.0], [389.0, 77.0], [389.0, 94.0], [321.0, 94.0]], 'bbox': (321, 77, 69, 18), 'method': 'polygon'}, {'text': 'CPA认证', 'confidence': 0.9973182678222656, 'polygon': [[412.0, 78.0], [475.0, 78.0], [475.0, 95.0], [412.0, 95.0]], 'bbox': (412, 78, 64, 18), 'method': 'polygon'}, {'text': '国家防爆', 'confidence': 0.9939618110656738, 'polygon': [[498.0, 79.0], [560.0, 79.0], [560.0, 96.0], [498.0, 96.0]], 'bbox': (498, 79, 63, 18), 'method': 'polygon'}, {'text': 'ISO认证', 'confidence': 0.9970961809158325, 'polygon': [[585.0, 78.0], [644.0, 78.0], [644.0, 95.0], [585.0, 95.0]], 'bbox': (585, 78, 60, 18), 'method': 'polygon'}, {'text': 'CE认证', 'confidence': 0.998458206653595, 'polygon': [[672.0, 77.0], [724.0, 77.0], [724.0, 95.0], [672.0, 95.0]], 'bbox': (672, 77, 53, 19), 'method': 'polygon'}, {'text': 'SGS认证', 'confidence': 0.9989465475082397, 'polygon': [[755.0, 78.0], [816.0, 78.0], [816.0, 96.0], [755.0, 96.0]], 'bbox': (755, 78, 62, 19), 'method': 'polygon'}, {'text': 'GT-1000', 'confidence': 0.9943559765815735, 'polygon': [[60.0, 132.0], [380.0, 135.0], [380.0, 196.0], [60.0, 194.0]], 'bbox': (60, 132, 321, 65), 'method': 'polygon'}, {'text': '激光粉尘检测仪', 'confidence': 0.9930281639099121, 'polygon': [[61.0, 223.0], [546.0, 223.0], [546.0, 284.0], [61.0, 284.0]], 'bbox': (61, 223, 486, 62), 'method': 'polygon'}, {'text': 'MO.5', 'confidence': 0.5840951204299927, 'polygon': [[663.0, 297.0], [685.0, 297.0], [685.0, 304.0], [663.0, 304.0]], 'bbox': (663, 297, 23, 8), 'method': 'polygon'}, {'text': '精度≤±5%F.S', 'confidence': 0.9796085357666016, 'polygon': [[40.0, 322.0], [291.0, 319.0], [292.0, 356.0], [40.0, 358.0]], 'bbox': (40, 319, 253, 40), 'method': 'polygon'}, {'text': 'PMLO', 'confidence': 0.825086236000061, 'polygon': [[659.0, 314.0], [686.0, 314.0], [686.0, 324.0], [659.0, 324.0]], 'bbox': (659, 314, 28, 11), 'method': 'polygon'}, {'text': 'PU2.5', 'confidence': 0.679347813129425, 'polygon': [[658.0, 331.0], [687.0, 331.0], [687.0, 341.0], [658.0, 341.0]], 'bbox': (658, 331, 30, 11), 'method': 'polygon'}, {'text': 'PLSO', 'confidence': 0.6640250086784363, 'polygon': [[658.0, 349.0], [687.0, 349.0], [687.0, 360.0], [658.0, 360.0]], 'bbox': (658, 349, 30, 12), 'method': 'polygon'}, {'text': 'P10', 'confidence': 0.6683804988861084, 'polygon': [[659.0, 368.0], [684.0, 368.0], [684.0, 379.0], [659.0, 379.0]], 'bbox': (659, 368, 26, 12), 'method': 'polygon'}, {'text': '防护等级:IP65', 'confidence': 0.9956157207489014, 'polygon': [[39.0, 399.0], [318.0, 396.0], [319.0, 433.0], [39.0, 436.0]], 'bbox': (39, 396, 281, 41), 'method': 'polygon'}, {'text': '过压保护/声光报警/存储打印', 'confidence': 0.9742894768714905, 'polygon': [[31.0, 478.0], [566.0, 478.0], [566.0, 512.0], [31.0, 512.0]], 'bbox': (31, 478, 536, 35), 'method': 'polygon'}, {'text': 'PM0.3/0.5/1.0/2.5/5.0/10um', 'confidence': 0.9763215780258179, 'polygon': [[29.0, 552.0], [572.0, 555.0], [572.0, 589.0], [29.0, 586.0]], 'bbox': (29, 552, 544, 38), 'method': 'polygon'}, {'text': '可同时监测多种粒径尘埃粒子数', 'confidence': 0.9920802116394043, 'polygon': [[42.0, 631.0], [497.0, 631.0], [497.0, 658.0], [42.0, 658.0]], 'bbox': (42, 631, 456, 28), 'method': 'polygon'}, {'text': '适合十万级以上洁净室', 'confidence': 0.9910793304443359, 'polygon': [[41.0, 679.0], [365.0, 679.0], [365.0, 706.0], [41.0, 706.0]], 'bbox': (41, 679, 325, 28), 'method': 'polygon'}, {'text': '全国', 'confidence': 0.9995953440666199, 'polygon': [[21.0, 720.0], [158.0, 720.0], [158.0, 791.0], [21.0, 791.0]], 'bbox': (21, 720, 138, 72), 'method': 'polygon'}, {'text': '7天无理由退货', 'confidence': 0.9959755539894104, 'polygon': [[263.0, 731.0], [524.0, 733.0], [523.0, 767.0], [263.0, 764.0]], 'bbox': (263, 731, 262, 37), 'method': 'polygon'}, {'text': '赠运险费', 'confidence': 0.9984970688819885, 'polygon': [[571.0, 731.0], [727.0, 731.0], [727.0, 768.0], [571.0, 768.0]], 'bbox': (571, 731, 157, 38), 'method': 'polygon'}, {'text': '包邮', 'confidence': 0.9996557235717773, 'polygon': [[22.0, 789.0], [161.0, 789.0], [161.0, 859.0], [22.0, 859.0]], 'bbox': (22, 789, 140, 71), 'method': 'polygon'}, {'text': '原厂正品/可开发票/质保一年', 'confidence': 0.9892104268074036, 'polygon': [[185.0, 789.0], [837.0, 787.0], [837.0, 828.0], [185.0, 830.0]], 'bbox': (185, 787, 653, 44), 'method': 'polygon'}] +[2025-10-24 08:33:08,261] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 2 OCR raw 데이터 메모리 저장 완료: 31개 텍스트 +[2025-10-24 08:33:08,270] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.9%): '科尔诺' +[2025-10-24 08:33:08,270] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): 'PA' +[2025-10-24 08:33:08,270] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): 'CE' +[2025-10-24 08:33:08,270] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): 'SGS' +[2025-10-24 08:33:08,270] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): 'CNEX' +[2025-10-24 08:33:08,271] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): 'S' +[2025-10-24 08:33:08,271] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): 'KORNO' +[2025-10-24 08:33:08,271] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.4%): 'CMC认证' +[2025-10-24 08:33:08,271] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.7%): 'CPA认证' +[2025-10-24 08:33:08,271] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.4%): '国家防爆' +[2025-10-24 08:33:08,271] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.7%): 'ISO认证' +[2025-10-24 08:33:08,271] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.8%): 'CE认证' +[2025-10-24 08:33:08,271] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.9%): 'SGS认证' +[2025-10-24 08:33:08,271] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): 'GT-1000' +[2025-10-24 08:33:08,271] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.3%): '激光粉尘检测仪' +[2025-10-24 08:33:08,271] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): 'MO.5' +[2025-10-24 08:33:08,271] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 98.0%): '精度≤±5%F.S' +[2025-10-24 08:33:08,271] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): 'PMLO' +[2025-10-24 08:33:08,271] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): 'PU2.5' +[2025-10-24 08:33:08,271] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): 'PLSO' +[2025-10-24 08:33:08,272] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): 'P10' +[2025-10-24 08:33:08,272] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.6%): '防护等级:IP65' +[2025-10-24 08:33:08,272] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 97.4%): '过压保护/声光报警/存储打印' +[2025-10-24 08:33:08,272] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): 'PM0.3/0.5/1.0/2.5/5.0/10um' +[2025-10-24 08:33:08,272] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.2%): '可同时监测多种粒径尘埃粒子数' +[2025-10-24 08:33:08,272] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.1%): '适合十万级以上洁净室' +[2025-10-24 08:33:08,272] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 100.0%): '全国' +[2025-10-24 08:33:08,272] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.6%): '7天无理由退货' +[2025-10-24 08:33:08,272] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.8%): '赠运险费' +[2025-10-24 08:33:08,272] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 100.0%): '包邮' +[2025-10-24 08:33:08,272] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 98.9%): '原厂正品/可开发票/质保一年' +[2025-10-24 08:33:08,272] [LogListener] [DEBUG] [loggerModule.py:debug:275] 필터링 결과: 18/31개 (신뢰도 + & 중국어) +[2025-10-24 08:33:08,272] [LogListener] [DEBUG] [loggerModule.py:debug:275] filter_ocr_results: [{'text': '科尔诺', 'confidence': 0.9992308616638184, 'polygon': [[96.0, 27.0], [211.0, 25.0], [212.0, 63.0], [97.0, 66.0]], 'bbox': (96, 25, 117, 42), 'method': 'polygon'}, {'text': 'CMC认证', 'confidence': 0.9940687417984009, 'polygon': [[321.0, 77.0], [389.0, 77.0], [389.0, 94.0], [321.0, 94.0]], 'bbox': (321, 77, 69, 18), 'method': 'polygon'}, {'text': 'CPA认证', 'confidence': 0.9973182678222656, 'polygon': [[412.0, 78.0], [475.0, 78.0], [475.0, 95.0], [412.0, 95.0]], 'bbox': (412, 78, 64, 18), 'method': 'polygon'}, {'text': '国家防爆', 'confidence': 0.9939618110656738, 'polygon': [[498.0, 79.0], [560.0, 79.0], [560.0, 96.0], [498.0, 96.0]], 'bbox': (498, 79, 63, 18), 'method': 'polygon'}, {'text': 'ISO认证', 'confidence': 0.9970961809158325, 'polygon': [[585.0, 78.0], [644.0, 78.0], [644.0, 95.0], [585.0, 95.0]], 'bbox': (585, 78, 60, 18), 'method': 'polygon'}, {'text': 'CE认证', 'confidence': 0.998458206653595, 'polygon': [[672.0, 77.0], [724.0, 77.0], [724.0, 95.0], [672.0, 95.0]], 'bbox': (672, 77, 53, 19), 'method': 'polygon'}, {'text': 'SGS认证', 'confidence': 0.9989465475082397, 'polygon': [[755.0, 78.0], [816.0, 78.0], [816.0, 96.0], [755.0, 96.0]], 'bbox': (755, 78, 62, 19), 'method': 'polygon'}, {'text': '激光粉尘检测仪', 'confidence': 0.9930281639099121, 'polygon': [[61.0, 223.0], [546.0, 223.0], [546.0, 284.0], [61.0, 284.0]], 'bbox': (61, 223, 486, 62), 'method': 'polygon'}, {'text': '精度≤±5%F.S', 'confidence': 0.9796085357666016, 'polygon': [[40.0, 322.0], [291.0, 319.0], [292.0, 356.0], [40.0, 358.0]], 'bbox': (40, 319, 253, 40), 'method': 'polygon'}, {'text': '防护等级:IP65', 'confidence': 0.9956157207489014, 'polygon': [[39.0, 399.0], [318.0, 396.0], [319.0, 433.0], [39.0, 436.0]], 'bbox': (39, 396, 281, 41), 'method': 'polygon'}, {'text': '过压保护/声光报警/存储打印', 'confidence': 0.9742894768714905, 'polygon': [[31.0, 478.0], [566.0, 478.0], [566.0, 512.0], [31.0, 512.0]], 'bbox': (31, 478, 536, 35), 'method': 'polygon'}, {'text': '可同时监测多种粒径尘埃粒子数', 'confidence': 0.9920802116394043, 'polygon': [[42.0, 631.0], [497.0, 631.0], [497.0, 658.0], [42.0, 658.0]], 'bbox': (42, 631, 456, 28), 'method': 'polygon'}, {'text': '适合十万级以上洁净室', 'confidence': 0.9910793304443359, 'polygon': [[41.0, 679.0], [365.0, 679.0], [365.0, 706.0], [41.0, 706.0]], 'bbox': (41, 679, 325, 28), 'method': 'polygon'}, {'text': '全国', 'confidence': 0.9995953440666199, 'polygon': [[21.0, 720.0], [158.0, 720.0], [158.0, 791.0], [21.0, 791.0]], 'bbox': (21, 720, 138, 72), 'method': 'polygon'}, {'text': '7天无理由退货', 'confidence': 0.9959755539894104, 'polygon': [[263.0, 731.0], [524.0, 733.0], [523.0, 767.0], [263.0, 764.0]], 'bbox': (263, 731, 262, 37), 'method': 'polygon'}, {'text': '赠运险费', 'confidence': 0.9984970688819885, 'polygon': [[571.0, 731.0], [727.0, 731.0], [727.0, 768.0], [571.0, 768.0]], 'bbox': (571, 731, 157, 38), 'method': 'polygon'}, {'text': '包邮', 'confidence': 0.9996557235717773, 'polygon': [[22.0, 789.0], [161.0, 789.0], [161.0, 859.0], [22.0, 859.0]], 'bbox': (22, 789, 140, 71), 'method': 'polygon'}, {'text': '原厂正品/可开发票/质保一年', 'confidence': 0.9892104268074036, 'polygon': [[185.0, 789.0], [837.0, 787.0], [837.0, 828.0], [185.0, 830.0]], 'bbox': (185, 787, 653, 44), 'method': 'polygon'}] +[2025-10-24 08:33:08,276] [LogListener] [DEBUG] [loggerModule.py:debug:275] 중국어 텍스트 18개 필터링 완료 +[2025-10-24 08:33:08,292] [LogListener] [DEBUG] [loggerModule.py:debug:275] 한글 텍스트 0개 필터링 완료 +[2025-10-24 08:33:09,907] [LogListener] [DEBUG] [loggerModule.py:debug:275] translated_texts: ['코르노', 'CMC 인증', 'CPA 자격증', '국가 방폭형', 'ISO 인증', 'CE 인증', 'SGS 인증', '레이저 먼지 감지기', '정확도≤±5%F.S', '보호 수준: IP65', '과전압 보호 / 소리와 빛 경보 / 매장 인쇄', '다양한 입자 크기의 먼지 입자 수를 동시에 모니터링 가능', '클래스 100,000 이상의 클린룸에 적합', '전국', '7일이면 반품할 이유가 없습니다.', '무료 배송 보험', '무료 배송', '오리지널 정품 / 청구 가능 / 1년 보증'] +[2025-10-24 08:33:09,907] [LogListener] [DEBUG] [loggerModule.py:debug:275] 마스크 생성 완료 +[2025-10-24 08:33:09,907] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 2 OCR 권한 없음, 전체 번역 모드 +[2025-10-24 08:33:09,914] [LogListener] [DEBUG] [loggerModule.py:debug:275] [AUTO Inpaint] coverage=0.350, comps=14, min_center_dist=0.064 → request +[2025-10-24 08:33:09,915] [LogListener] [DEBUG] [loggerModule.py:debug:275] ocr_count: 18 +[2025-10-24 08:33:09,915] [LogListener] [DEBUG] [loggerModule.py:debug:275] is_member_valid: False +[2025-10-24 08:33:09,915] [LogListener] [DEBUG] [loggerModule.py:debug:275] [set_inpaint_method] prefix=detail, target_key=detail_IMGTrans_type, trans_type=GPU → inpaint_method=migan +[2025-10-24 08:33:09,915] [LogListener] [DEBUG] [loggerModule.py:debug:275] 최종 inpaint_method: migan +[2025-10-24 08:33:09,915] [LogListener] [DEBUG] [loggerModule.py:debug:275] MIGAN 인페인팅 시도 +[2025-10-24 08:33:09,918] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 입력 형태 - 이미지: (1, 3, 860, 860), 마스크: (1, 1, 860, 860) +[2025-10-24 08:33:10,361] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 출력 형태: (860, 860, 3), dtype: uint8 +[2025-10-24 08:33:10,361] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 추론 완료: 442.90 ms +[2025-10-24 08:33:10,374] [LogListener] [DEBUG] [loggerModule.py:debug:275] MIGAN 인페인팅 성공 +[2025-10-24 08:33:10,375] [LogListener] [DEBUG] [loggerModule.py:debug:275] 메모리 변화 [인페인팅]: 39260.7MB -> 39479.5MB (+218.8MB, +0.6%) - 방법: migan +[2025-10-24 08:33:10,375] [LogListener] [DEBUG] [loggerModule.py:debug:275] 인페인팅 완료 +[2025-10-24 08:33:10,478] [LogListener] [DEBUG] [loggerModule.py:debug:275] 텍스트 렌더링 완료 +[2025-10-24 08:33:10,479] [LogListener] [DEBUG] [loggerModule.py:debug:275] watermark_text: 이미지 저작권 보유 +[2025-10-24 08:33:10,480] [LogListener] [DEBUG] [loggerModule.py:debug:275] is_watermark_enabled: True +[2025-10-24 08:33:10,713] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 저장 완료 : C:\ProgramData\ImgWorker\work\translated_detail_img_2.webp +[2025-10-24 08:33:10,714] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 2 번역 완료: C:\ProgramData\ImgWorker\work\translated_detail_img_2.webp +[2025-10-24 08:33:10,714] [LogListener] [DEBUG] [loggerModule.py:debug:275] ⏱ 이미지 파이프라인 총 3721.5ms | download=0.0ms | ocr=991.0ms | translate=1636.2ms | mask=1636.2ms | inpaint=465.6ms(migan/DirectML) | render=103.5ms | save=204.6ms +[2025-10-24 08:33:10,715] [LogListener] [DEBUG] [loggerModule.py:debug:275] process_single_image 호출 완료 +[2025-10-24 08:33:10,715] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 결과 반환 중: uid=cd96bcdd-e4c5-472c-8b8c-c67f1eb3c8ff +[2025-10-24 08:33:10,715] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 결과 반환 완료: uid=cd96bcdd-e4c5-472c-8b8c-c67f1eb3c8ff +[2025-10-24 08:33:10,715] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 08:33:10,716] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 27968) +[2025-10-24 08:34:10,721] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 08:34:10,721] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 08:34:10,721] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 27968) +[2025-10-24 08:35:10,734] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 08:35:10,734] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 08:35:10,734] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 27968) +[2025-10-24 08:36:10,743] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 08:36:10,743] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 08:36:10,743] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 27968) +[2025-10-24 08:37:10,747] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 08:37:10,747] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 08:37:10,747] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 27968) +[2025-10-24 08:38:10,750] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 08:38:10,750] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 08:38:10,750] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 27968) +[2025-10-24 08:39:10,762] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 08:39:10,762] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 08:39:10,762] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 27968) +[2025-10-24 08:40:10,777] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 08:40:10,777] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 08:40:10,777] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 27968) +[2025-10-24 08:41:10,782] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 08:41:10,782] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 08:41:10,782] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 27968) +[2025-10-24 08:42:10,785] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 08:42:10,785] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 08:42:10,785] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 27968) +[2025-10-24 08:43:10,789] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 08:43:10,789] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 08:43:10,789] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 27968) +[2025-10-24 08:44:10,802] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 08:44:10,802] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 08:44:10,803] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 27968) +[2025-10-24 08:45:10,812] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 08:45:10,812] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 08:45:10,812] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 27968) +[2025-10-24 08:45:29,923] [MemMonitor] [WARNING] [loggerModule.py:warning:287] 워커 롤링 스케줄: reason=uptime-threshold +[2025-10-24 08:45:29,923] [WorkerRoller] [WARNING] [loggerModule.py:warning:287] 워커 롤링 시작: uptime-threshold +[2025-10-24 08:45:29,939] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔥 작업 수신 성공 +[2025-10-24 08:45:29,974] [LogListener] [DEBUG] [loggerModule.py:debug:275] Shutdown signal 수신 → 종료 +[2025-10-24 08:45:29,975] [LogListener] [DEBUG] [loggerModule.py:debug:275] OCR 모듈 정리 완료 +[2025-10-24 08:45:29,975] [LogListener] [DEBUG] [loggerModule.py:debug:275] 마스크 모듈 정리 완료 +[2025-10-24 08:45:29,976] [LogListener] [DEBUG] [loggerModule.py:debug:275] 임시 폴더 삭제됨: C:\ProgramData\ImgWorker\work +[2025-10-24 08:45:29,976] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 프로세서 소멸 +[2025-10-24 08:45:30,341] [WorkerRoller] [INFO] [loggerModule.py:info:281] 워커 프로세스 기동: PID=33480 +[2025-10-24 08:45:30,793] [LogListener] [DEBUG] [loggerModule.py:debug:275] ImageWorker 프로세스 기동 (PID=33480, Name=ImageWorkerProcess) +[2025-10-24 08:45:30,793] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔧 ImageProcessor3 초기화 시작... +[2025-10-24 08:45:30,793] [LogListener] [DEBUG] [loggerModule.py:debug:275] tracemalloc 메모리 추적 시작 +[2025-10-24 08:45:30,800] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ DirectML 환경 준비 완료 (Windows DirectX 12 기반) +[2025-10-24 08:45:30,800] [LogListener] [DEBUG] [loggerModule.py:debug:275] === 🚀 DirectML GPU 상태 초기화 시작 🚀 === +[2025-10-24 08:45:30,800] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🎯 사용자 GPU 가속 요청: False +[2025-10-24 08:45:30,800] [LogListener] [DEBUG] [loggerModule.py:debug:275] 💻 현재 운영체제: Windows +[2025-10-24 08:45:30,800] [LogListener] [DEBUG] [loggerModule.py:debug:275] GPU 가속이 비활성화됨 (toggle_states['use_cuda'] = False) +[2025-10-24 08:45:30,800] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔒 안전한 CPU 모드로 모든 GPU 설정 강제 비활성화 +[2025-10-24 08:45:30,800] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔧 ImageProcessor3 GPU 상태 요약: +[2025-10-24 08:45:30,800] [LogListener] [DEBUG] [loggerModule.py:debug:275] - CUDA 사용 가능: False +[2025-10-24 08:45:30,800] [LogListener] [DEBUG] [loggerModule.py:debug:275] - toggle_states['use_cuda']: NOT_SET +[2025-10-24 08:45:30,801] [LogListener] [DEBUG] [loggerModule.py:debug:275] - GPU 하드웨어 정보: {} +[2025-10-24 08:45:30,801] [LogListener] [DEBUG] [loggerModule.py:debug:275] ImageProcessor3 Init toggle_states: {'TEMP_IMAGE_DIR': 'C:\\ProgramData\\ImgWorker\\work', 'output_image_format': 'webp', 'use_local_rembg': True, 'local_rembg_model_path': 'D:\\py\\img_worker\\modules\\rembg_models', 'local_model_name': 'birefnet-general-lite', 'optionIMGTrans_type': 'CPU', 'detail_IMGTrans_type': 'CPU', 'thumb_trans_type': 'CPU', 'migan_use_accel': True, 'migan_onnx_path': 'D:\\py\\img_worker\\modules\\migan_onnx\\migan_pipeline_v2.onnx', 'ocr_provider_override': 'auto', 'migan_provider_override': 'auto'} +[2025-10-24 08:45:30,801] [LogListener] [DEBUG] [loggerModule.py:debug:275] is_member_valid: False +[2025-10-24 08:45:30,801] [LogListener] [DEBUG] [loggerModule.py:debug:275] debug_images 디렉토리 이미 존재: D:\py\img_worker\modules\debug_images +[2025-10-24 08:45:30,801] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.font_path: D:\py\img_worker\modules\fonts\HakgyoansimDunggeunmisoTTFB.ttf +[2025-10-24 08:45:30,801] [LogListener] [DEBUG] [loggerModule.py:debug:275] toggle_states font_path: D:\py\img_worker\modules\fonts\HakgyoansimDunggeunmisoTTFB.ttf +[2025-10-24 08:45:30,801] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.TEMP_IMAGE_DIR: C:\ProgramData\ImgWorker\work +[2025-10-24 08:45:30,801] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.debugging_save_Dir: D:\py\img_worker\modules\debug_images +[2025-10-24 08:45:30,801] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.unwanted_texts: {} +[2025-10-24 08:45:30,801] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.inpaint_method: migan +[2025-10-24 08:45:30,801] [LogListener] [DEBUG] [loggerModule.py:debug:275] Image.MAX_IMAGE_PIXELS set to 20000000 +[2025-10-24 08:45:30,801] [LogListener] [DEBUG] [loggerModule.py:debug:275] ONNX 모델 타입 설정값: 자동 선택, GPU 정보: {} +[2025-10-24 08:45:30,801] [LogListener] [DEBUG] [loggerModule.py:debug:275] 자동 선택 모드: GPU 추천 모델 simp 사용 +[2025-10-24 08:45:30,802] [LogListener] [DEBUG] [loggerModule.py:debug:275] ONNX OCR 모델 타입 결정: simp (GPU: False) +[2025-10-24 08:45:30,802] [LogListener] [DEBUG] [loggerModule.py:debug:275] ONNX OCR 모듈 CPU 모드로 설정 +[2025-10-24 08:45:30,802] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🚀 ONNX TextSystem 초기화 시작 (CPU 모드) +[2025-10-24 08:45:30,802] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🚀 ONNX TextSystem 초기화 시작 (CPU 모드) +[2025-10-24 08:45:32,336] [LogListener] [DEBUG] [loggerModule.py:debug:275] 문자 사전 파일 UTF-8 확인 완료: D:\py\img_worker\modules\onnx_ocr_module\dict\ppocr_keys_v1.txt +[2025-10-24 08:45:32,419] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ ONNX TextSystem 초기화 완료 (CPU + SIMP (호환성) 모델) +[2025-10-24 08:45:32,419] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ ONNX TextSystem 초기화 완료 (CPU + SIMP 모델) +[2025-10-24 08:45:32,419] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ ONNX OCR 모듈 초기화 성공 (CPU 모드) +[2025-10-24 08:45:32,420] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ ONNX OCR 모듈 초기화 성공 +[2025-10-24 08:45:32,420] [LogListener] [DEBUG] [loggerModule.py:debug:275] 마스크 모듈 초기화 완료 +[2025-10-24 08:45:32,420] [LogListener] [DEBUG] [loggerModule.py:debug:275] MaskModule 초기화 성공 +[2025-10-24 08:45:32,420] [LogListener] [DEBUG] [loggerModule.py:debug:275] 텍스트 렌더링 모듈 초기화 완료 +[2025-10-24 08:45:32,420] [LogListener] [DEBUG] [loggerModule.py:debug:275] 기본 폰트: D:\py\img_worker\modules\fonts\HakgyoansimDunggeunmisoTTFB.ttf +[2025-10-24 08:45:32,420] [LogListener] [DEBUG] [loggerModule.py:debug:275] TextRenderingModule 초기화 성공 +[2025-10-24 08:45:32,420] [LogListener] [DEBUG] [loggerModule.py:debug:275] 폰트 로드 성공: D:\py\img_worker\modules\fonts\HakgyoansimDunggeunmisoTTFB.ttf +[2025-10-24 08:45:32,420] [LogListener] [DEBUG] [loggerModule.py:debug:275] PostImageManager 초기화 성공 +[2025-10-24 08:45:32,420] [LogListener] [DEBUG] [loggerModule.py:debug:275] Request_AI_Server GPU 상태: CUDA 사용 가능=False +[2025-10-24 08:45:32,420] [LogListener] [DEBUG] [loggerModule.py:debug:275] Request_AI_Server 초기화 성공 +[2025-10-24 08:45:32,421] [LogListener] [DEBUG] [loggerModule.py:debug:275] GoogleTranslate 초기화 성공 +[2025-10-24 08:45:32,421] [LogListener] [DEBUG] [loggerModule.py:debug:275] MIGAN CUDA 사용 불가 - CPU 모드로 설정 +[2025-10-24 08:45:32,421] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] GPU 관리자 전달: GPUManager, can_use_cuda: False +[2025-10-24 08:45:32,439] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] GPU 관리자 사용 가능: False +[2025-10-24 08:45:32,644] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 사용 가능한 providers: ['DmlExecutionProvider', 'CPUExecutionProvider'] +[2025-10-24 08:45:32,644] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] DirectML 가속 활성화 +[2025-10-24 08:45:32,645] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 최종 providers: [('DmlExecutionProvider', {}), ('CPUExecutionProvider', {})] +[2025-10-24 08:45:32,645] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] DirectML 가속 시도: [('DmlExecutionProvider', {}), ('CPUExecutionProvider', {})] +[2025-10-24 08:45:32,645] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] DirectML 가속 성공! 실제 providers: ['DmlExecutionProvider', 'CPUExecutionProvider'] +[2025-10-24 08:45:32,645] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 입력 0: image, 형태: ['batch_size', 3, 'height', 'width'], 타입: tensor(uint8) +[2025-10-24 08:45:32,645] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 입력 1: mask, 형태: ['batch_size', 1, 'height', 'width'], 타입: tensor(uint8) +[2025-10-24 08:45:32,645] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 출력 0: result, 형태: ['ScatterNDresult_dim_0', 3, 'ScatterNDresult_dim_2', 'ScatterNDresult_dim_3'], 타입: tensor(uint8) +[2025-10-24 08:45:32,645] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 세션 준비 완료. providers=['DmlExecutionProvider', 'CPUExecutionProvider'] +[2025-10-24 08:45:32,645] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] GPU 관리자 연결 완료: GPUManager +[2025-10-24 08:45:32,645] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 파이프라인 gpu_manager 속성: True, 값: +[2025-10-24 08:45:32,645] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 초기화 완료: gpu_manager 속성=True, 값= +[2025-10-24 08:45:32,646] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔰 OCR 모듈 Warm-up 시작... +[2025-10-24 08:45:32,646] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔍 ONNX OCR 감지 방식: polygon +[2025-10-24 08:45:32,650] [LogListener] [DEBUG] [loggerModule.py:debug:275] ⚡ ONNX OCR 추론 완료: 4.0ms +[2025-10-24 08:45:32,651] [LogListener] [DEBUG] [loggerModule.py:debug:275] 📊 세부 시간 - 감지: 3.0ms, 인식: 0.0ms, 분류: 0.0ms +[2025-10-24 08:45:32,651] [LogListener] [WARNING] [loggerModule.py:warning:287] ⚠️ ONNX OCR 결과가 비어있습니다 +[2025-10-24 08:45:32,654] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ OCR 모듈 Warm-up 성공 +[2025-10-24 08:45:32,654] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔰 ImageProcessor Warm‑up 완료 +[2025-10-24 08:45:32,654] [ResultListener] [INFO] [loggerModule.py:info:281] 워커 READY 수신 +[2025-10-24 08:45:32,655] [ResultListener] [INFO] [loggerModule.py:info:281] 워커 READY 수신 +[2025-10-24 08:45:32,655] [LogListener] [DEBUG] [loggerModule.py:debug:275] 📡 추가 READY 신호 전송 완료 +[2025-10-24 08:45:32,655] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 08:45:32,655] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 33480) +[2025-10-24 08:46:32,658] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 08:46:32,658] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 08:46:32,658] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 33480) +[2025-10-24 08:47:32,661] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 08:47:32,661] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 08:47:32,661] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 33480) +[2025-10-24 08:48:32,671] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 08:48:32,671] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 08:48:32,671] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 33480) +[2025-10-24 08:49:32,686] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 08:49:32,686] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 08:49:32,686] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 33480) +[2025-10-24 08:50:32,700] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 08:50:32,700] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 08:50:32,700] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 33480) +[2025-10-24 08:51:26,436] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔥 작업 수신 성공 +[2025-10-24 08:51:26,436] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🚀 작업 처리 시작: cmd=process_single_image, uid=f740071c-e962-44b6-bc6e-d0299d8eaff0 +[2025-10-24 08:51:26,436] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 프로세서 toggle_states 업데이트 : {'TEMP_IMAGE_DIR': 'C:\\ProgramData\\ImgWorker\\work', 'output_image_format': 'webp', 'use_local_rembg': True, 'local_rembg_model_path': 'D:\\py\\img_worker\\modules\\rembg_models', 'local_model_name': 'birefnet-general-lite', 'optionIMGTrans_type': 'GPU', 'detail_IMGTrans_type': 'GPU', 'thumb_trans_type': 'GPU', 'migan_use_accel': True, 'migan_onnx_path': 'D:\\py\\img_worker\\modules\\migan_onnx\\migan_pipeline_v2.onnx', 'ocr_provider_override': 'auto', 'migan_provider_override': 'auto', 'font_type': '폰트5', 'unwanted_texts': ['가격설명', '무료배송', '보증', '사은품', '세일', '이벤트', '특가', '품절', '할인', '할인가'], 'is_member_valid': False, 'authenticated_by_admin': True} +[2025-10-24 08:51:26,437] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 실행 직전: cmd=process_single_image +[2025-10-24 08:51:26,437] [LogListener] [DEBUG] [loggerModule.py:debug:275] process_single_image 호출 직전 +[2025-10-24 08:51:26,439] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 번역시작 +[2025-10-24 08:51:26,842] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 1 처리 시작: C:\ProgramData\ImgWorker\incoming\dl_cbb9790c5d8b.jpg - 전체 번역 모드 +[2025-10-24 08:51:26,853] [LogListener] [DEBUG] [loggerModule.py:debug:275] 로컬 파일 경로 감지, 다운로드 생략: C:\ProgramData\ImgWorker\incoming\dl_cbb9790c5d8b.jpg +[2025-10-24 08:51:26,853] [LogListener] [DEBUG] [loggerModule.py:debug:275] 옵션 이미지는 스케일 처리 건너뛰기: option +[2025-10-24 08:51:26,853] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 1 로컬 저장위치(옵션 이미지 원본 유지): C:\ProgramData\ImgWorker\incoming\dl_cbb9790c5d8b.jpg +[2025-10-24 08:51:26,855] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔍 ONNX OCR 감지 방식: polygon +[2025-10-24 08:51:27,726] [LogListener] [DEBUG] [loggerModule.py:debug:275] ⚡ ONNX OCR 추론 완료: 867.9ms +[2025-10-24 08:51:27,726] [LogListener] [DEBUG] [loggerModule.py:debug:275] 📊 세부 시간 - 감지: 137.7ms, 인식: 700.1ms, 분류: 24.0ms +[2025-10-24 08:51:27,746] [LogListener] [DEBUG] [loggerModule.py:debug:275] 메모리 변화 [OCR 처리]: 43423.5MB -> 43783.2MB (+359.7MB, +0.8%) - 이미지 1 +[2025-10-24 08:51:27,760] [LogListener] [DEBUG] [loggerModule.py:debug:275] ocr_results: [{'text': '暴走兔', 'confidence': 0.9030442237854004, 'polygon': [[136.0, 21.0], [270.0, 23.0], [269.0, 63.0], [135.0, 61.0]], 'bbox': (135, 21, 136, 43), 'method': 'polygon'}, {'text': 'BAOZOUTU', 'confidence': 0.9928661584854126, 'polygon': [[134.0, 73.0], [273.0, 73.0], [273.0, 87.0], [134.0, 87.0]], 'bbox': (134, 73, 140, 15), 'method': 'polygon'}, {'text': 'AM', 'confidence': 0.9390634298324585, 'polygon': [[471.0, 146.0], [481.0, 146.0], [481.0, 154.0], [471.0, 154.0]], 'bbox': (471, 146, 11, 9), 'method': 'polygon'}, {'text': '全波段收音机', 'confidence': 0.9978317618370056, 'polygon': [[81.0, 158.0], [288.0, 156.0], [288.0, 184.0], [81.0, 186.0]], 'bbox': (81, 156, 208, 31), 'method': 'polygon'}, {'text': '1500', 'confidence': 0.8749780058860779, 'polygon': [[469.0, 192.0], [482.0, 192.0], [482.0, 209.0], [469.0, 209.0]], 'bbox': (469, 192, 14, 18), 'method': 'polygon'}, {'text': '蓝牙音', 'confidence': 0.9984228610992432, 'polygon': [[79.0, 213.0], [273.0, 213.0], [273.0, 248.0], [79.0, 248.0]], 'bbox': (79, 213, 195, 36), 'method': 'polygon'}, {'text': '响', 'confidence': 0.9996689558029175, 'polygon': [[259.0, 219.0], [290.0, 219.0], [290.0, 244.0], [259.0, 244.0]], 'bbox': (259, 219, 32, 26), 'method': 'polygon'}, {'text': 'R11', 'confidence': 0.9946306347846985, 'polygon': [[457.0, 245.0], [490.0, 245.0], [490.0, 256.0], [457.0, 256.0]], 'bbox': (457, 245, 34, 12), 'method': 'polygon'}, {'text': 'RADIO/BT/MUSIC PLAYER', 'confidence': 0.9764536619186401, 'polygon': [[505.0, 245.0], [633.0, 245.0], [633.0, 255.0], [505.0, 255.0]], 'bbox': (505, 245, 129, 11), 'method': 'polygon'}, {'text': '4000毫安锂电池', 'confidence': 0.9978349208831787, 'polygon': [[82.0, 279.0], [289.0, 279.0], [289.0, 303.0], [82.0, 303.0]], 'bbox': (82, 279, 208, 25), 'method': 'polygon'}, {'text': '手', 'confidence': 0.9999611377716064, 'polygon': [[80.0, 335.0], [113.0, 335.0], [113.0, 365.0], [80.0, 365.0]], 'bbox': (80, 335, 34, 31), 'method': 'polygon'}, {'text': '电', 'confidence': 0.9988621473312378, 'polygon': [[168.0, 336.0], [204.0, 336.0], [204.0, 365.0], [168.0, 365.0]], 'bbox': (168, 336, 37, 30), 'method': 'polygon'}, {'text': '筒', 'confidence': 0.998289167881012, 'polygon': [[263.0, 336.0], [290.0, 336.0], [290.0, 365.0], [263.0, 365.0]], 'bbox': (263, 336, 28, 30), 'method': 'polygon'}, {'text': 'SOS警报/指南针', 'confidence': 0.9654105305671692, 'polygon': [[81.0, 398.0], [288.0, 398.0], [288.0, 425.0], [81.0, 425.0]], 'bbox': (81, 398, 208, 28), 'method': 'polygon'}, {'text': '手摇/太阳能发电', 'confidence': 0.98994380235672, 'polygon': [[83.0, 457.0], [289.0, 457.0], [289.0, 485.0], [83.0, 485.0]], 'bbox': (83, 457, 207, 29), 'method': 'polygon'}, {'text': '可插U盘/TF卡', 'confidence': 0.9858887195587158, 'polygon': [[82.0, 518.0], [290.0, 518.0], [290.0, 545.0], [82.0, 545.0]], 'bbox': (82, 518, 209, 28), 'method': 'polygon'}, {'text': '嘉走兔', 'confidence': 0.5901210904121399, 'polygon': [[538.0, 523.0], [583.0, 523.0], [583.0, 538.0], [538.0, 538.0]], 'bbox': (538, 523, 46, 16), 'method': 'polygon'}, {'text': '限时', 'confidence': 0.9985836148262024, 'polygon': [[18.0, 659.0], [150.0, 661.0], [149.0, 729.0], [17.0, 727.0]], 'bbox': (17, 659, 134, 71), 'method': 'polygon'}, {'text': '赠送', 'confidence': 0.9996605515480042, 'polygon': [[15.0, 728.0], [149.0, 728.0], [149.0, 792.0], [15.0, 792.0]], 'bbox': (15, 728, 135, 65), 'method': 'polygon'}, {'text': '军工包', 'confidence': 0.9985788464546204, 'polygon': [[205.0, 768.0], [264.0, 768.0], [264.0, 790.0], [205.0, 790.0]], 'bbox': (205, 768, 60, 23), 'method': 'polygon'}, {'text': '延长天线', 'confidence': 0.9989600777626038, 'polygon': [[284.0, 769.0], [362.0, 769.0], [362.0, 790.0], [284.0, 790.0]], 'bbox': (284, 769, 79, 22), 'method': 'polygon'}, {'text': '内存卡', 'confidence': 0.9994489550590515, 'polygon': [[386.0, 769.0], [445.0, 769.0], [445.0, 790.0], [386.0, 790.0]], 'bbox': (386, 769, 60, 22), 'method': 'polygon'}, {'text': '读卡器', 'confidence': 0.9984410405158997, 'polygon': [[472.0, 767.0], [531.0, 767.0], [531.0, 792.0], [472.0, 792.0]], 'bbox': (472, 767, 60, 26), 'method': 'polygon'}, {'text': '充电线', 'confidence': 0.9991092681884766, 'polygon': [[558.0, 769.0], [617.0, 769.0], [617.0, 790.0], [558.0, 790.0]], 'bbox': (558, 769, 60, 22), 'method': 'polygon'}, {'text': '礼盒礼袋', 'confidence': 0.9988925457000732, 'polygon': [[637.0, 769.0], [714.0, 769.0], [714.0, 790.0], [637.0, 790.0]], 'bbox': (637, 769, 78, 22), 'method': 'polygon'}, {'text': '充电头', 'confidence': 0.9975659251213074, 'polygon': [[735.0, 767.0], [794.0, 767.0], [794.0, 792.0], [735.0, 792.0]], 'bbox': (735, 767, 60, 26), 'method': 'polygon'}] +[2025-10-24 08:51:27,783] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 90.3%): '暴走兔' +[2025-10-24 08:51:27,784] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): 'BAOZOUTU' +[2025-10-24 08:51:27,784] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): 'AM' +[2025-10-24 08:51:27,784] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.8%): '全波段收音机' +[2025-10-24 08:51:27,784] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): '1500' +[2025-10-24 08:51:27,784] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.8%): '蓝牙音' +[2025-10-24 08:51:27,784] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 100.0%): '响' +[2025-10-24 08:51:27,785] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): 'R11' +[2025-10-24 08:51:27,785] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): 'RADIO/BT/MUSIC PLAYER' +[2025-10-24 08:51:27,785] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.8%): '4000毫安锂电池' +[2025-10-24 08:51:27,785] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 100.0%): '手' +[2025-10-24 08:51:27,785] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.9%): '电' +[2025-10-24 08:51:27,785] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.8%): '筒' +[2025-10-24 08:51:27,785] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 96.5%): 'SOS警报/指南针' +[2025-10-24 08:51:27,785] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.0%): '手摇/太阳能发电' +[2025-10-24 08:51:27,786] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 98.6%): '可插U盘/TF卡' +[2025-10-24 08:51:27,786] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 59.0%): '嘉走兔' +[2025-10-24 08:51:27,786] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.9%): '限时' +[2025-10-24 08:51:27,786] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 100.0%): '赠送' +[2025-10-24 08:51:27,786] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.9%): '军工包' +[2025-10-24 08:51:27,786] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.9%): '延长天线' +[2025-10-24 08:51:27,786] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.9%): '内存卡' +[2025-10-24 08:51:27,786] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.8%): '读卡器' +[2025-10-24 08:51:27,786] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.9%): '充电线' +[2025-10-24 08:51:27,786] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.9%): '礼盒礼袋' +[2025-10-24 08:51:27,787] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.8%): '充电头' +[2025-10-24 08:51:27,787] [LogListener] [DEBUG] [loggerModule.py:debug:275] 필터링 결과: 21/26개 (신뢰도 + & 중국어) +[2025-10-24 08:51:27,787] [LogListener] [DEBUG] [loggerModule.py:debug:275] filter_ocr_results: [{'text': '暴走兔', 'confidence': 0.9030442237854004, 'polygon': [[136.0, 21.0], [270.0, 23.0], [269.0, 63.0], [135.0, 61.0]], 'bbox': (135, 21, 136, 43), 'method': 'polygon'}, {'text': '全波段收音机', 'confidence': 0.9978317618370056, 'polygon': [[81.0, 158.0], [288.0, 156.0], [288.0, 184.0], [81.0, 186.0]], 'bbox': (81, 156, 208, 31), 'method': 'polygon'}, {'text': '蓝牙音', 'confidence': 0.9984228610992432, 'polygon': [[79.0, 213.0], [273.0, 213.0], [273.0, 248.0], [79.0, 248.0]], 'bbox': (79, 213, 195, 36), 'method': 'polygon'}, {'text': '响', 'confidence': 0.9996689558029175, 'polygon': [[259.0, 219.0], [290.0, 219.0], [290.0, 244.0], [259.0, 244.0]], 'bbox': (259, 219, 32, 26), 'method': 'polygon'}, {'text': '4000毫安锂电池', 'confidence': 0.9978349208831787, 'polygon': [[82.0, 279.0], [289.0, 279.0], [289.0, 303.0], [82.0, 303.0]], 'bbox': (82, 279, 208, 25), 'method': 'polygon'}, {'text': '手', 'confidence': 0.9999611377716064, 'polygon': [[80.0, 335.0], [113.0, 335.0], [113.0, 365.0], [80.0, 365.0]], 'bbox': (80, 335, 34, 31), 'method': 'polygon'}, {'text': '电', 'confidence': 0.9988621473312378, 'polygon': [[168.0, 336.0], [204.0, 336.0], [204.0, 365.0], [168.0, 365.0]], 'bbox': (168, 336, 37, 30), 'method': 'polygon'}, {'text': '筒', 'confidence': 0.998289167881012, 'polygon': [[263.0, 336.0], [290.0, 336.0], [290.0, 365.0], [263.0, 365.0]], 'bbox': (263, 336, 28, 30), 'method': 'polygon'}, {'text': 'SOS警报/指南针', 'confidence': 0.9654105305671692, 'polygon': [[81.0, 398.0], [288.0, 398.0], [288.0, 425.0], [81.0, 425.0]], 'bbox': (81, 398, 208, 28), 'method': 'polygon'}, {'text': '手摇/太阳能发电', 'confidence': 0.98994380235672, 'polygon': [[83.0, 457.0], [289.0, 457.0], [289.0, 485.0], [83.0, 485.0]], 'bbox': (83, 457, 207, 29), 'method': 'polygon'}, {'text': '可插U盘/TF卡', 'confidence': 0.9858887195587158, 'polygon': [[82.0, 518.0], [290.0, 518.0], [290.0, 545.0], [82.0, 545.0]], 'bbox': (82, 518, 209, 28), 'method': 'polygon'}, {'text': '嘉走兔', 'confidence': 0.5901210904121399, 'polygon': [[538.0, 523.0], [583.0, 523.0], [583.0, 538.0], [538.0, 538.0]], 'bbox': (538, 523, 46, 16), 'method': 'polygon'}, {'text': '限时', 'confidence': 0.9985836148262024, 'polygon': [[18.0, 659.0], [150.0, 661.0], [149.0, 729.0], [17.0, 727.0]], 'bbox': (17, 659, 134, 71), 'method': 'polygon'}, {'text': '赠送', 'confidence': 0.9996605515480042, 'polygon': [[15.0, 728.0], [149.0, 728.0], [149.0, 792.0], [15.0, 792.0]], 'bbox': (15, 728, 135, 65), 'method': 'polygon'}, {'text': '军工包', 'confidence': 0.9985788464546204, 'polygon': [[205.0, 768.0], [264.0, 768.0], [264.0, 790.0], [205.0, 790.0]], 'bbox': (205, 768, 60, 23), 'method': 'polygon'}, {'text': '延长天线', 'confidence': 0.9989600777626038, 'polygon': [[284.0, 769.0], [362.0, 769.0], [362.0, 790.0], [284.0, 790.0]], 'bbox': (284, 769, 79, 22), 'method': 'polygon'}, {'text': '内存卡', 'confidence': 0.9994489550590515, 'polygon': [[386.0, 769.0], [445.0, 769.0], [445.0, 790.0], [386.0, 790.0]], 'bbox': (386, 769, 60, 22), 'method': 'polygon'}, {'text': '读卡器', 'confidence': 0.9984410405158997, 'polygon': [[472.0, 767.0], [531.0, 767.0], [531.0, 792.0], [472.0, 792.0]], 'bbox': (472, 767, 60, 26), 'method': 'polygon'}, {'text': '充电线', 'confidence': 0.9991092681884766, 'polygon': [[558.0, 769.0], [617.0, 769.0], [617.0, 790.0], [558.0, 790.0]], 'bbox': (558, 769, 60, 22), 'method': 'polygon'}, {'text': '礼盒礼袋', 'confidence': 0.9988925457000732, 'polygon': [[637.0, 769.0], [714.0, 769.0], [714.0, 790.0], [637.0, 790.0]], 'bbox': (637, 769, 78, 22), 'method': 'polygon'}, {'text': '充电头', 'confidence': 0.9975659251213074, 'polygon': [[735.0, 767.0], [794.0, 767.0], [794.0, 792.0], [735.0, 792.0]], 'bbox': (735, 767, 60, 26), 'method': 'polygon'}] +[2025-10-24 08:51:27,788] [LogListener] [DEBUG] [loggerModule.py:debug:275] 중국어 텍스트 21개 필터링 완료 +[2025-10-24 08:51:27,788] [LogListener] [DEBUG] [loggerModule.py:debug:275] 한글 텍스트 0개 필터링 완료 +[2025-10-24 08:51:30,868] [LogListener] [DEBUG] [loggerModule.py:debug:275] translated_texts: ['도망친 토끼', '풀 밴드 라디오', '블루투스 사운드', '반지', '4000mAh 리튬 배터리', '손', '전기', '실린더', 'SOS 경보 / 나침반', '핸드 크랭크 / 태양광 발전', 'U 디스크 삽입 가능 / TF 카드', '지아 주이투', '제한된 시간', '포기하다', '군사 가방', '확장 안테나', '메모리 카드', '카드 리더', '충전 케이블', '선물 상자 선물 가방', '충전 헤드'] +[2025-10-24 08:51:30,869] [LogListener] [DEBUG] [loggerModule.py:debug:275] 마스크 생성 완료 +[2025-10-24 08:51:30,869] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 1 OCR 권한 없음, 전체 번역 모드 +[2025-10-24 08:51:30,875] [LogListener] [DEBUG] [loggerModule.py:debug:275] [AUTO Inpaint] coverage=0.264, comps=5, min_center_dist=0.224 → request +[2025-10-24 08:51:30,875] [LogListener] [DEBUG] [loggerModule.py:debug:275] ocr_count: 21 +[2025-10-24 08:51:30,875] [LogListener] [DEBUG] [loggerModule.py:debug:275] is_member_valid: False +[2025-10-24 08:51:30,875] [LogListener] [DEBUG] [loggerModule.py:debug:275] [set_inpaint_method] prefix=option, target_key=optionIMGTrans_type, trans_type=GPU → inpaint_method=migan +[2025-10-24 08:51:30,875] [LogListener] [DEBUG] [loggerModule.py:debug:275] 최종 inpaint_method: migan +[2025-10-24 08:51:30,875] [LogListener] [DEBUG] [loggerModule.py:debug:275] MIGAN 인페인팅 시도 +[2025-10-24 08:51:30,877] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 입력 형태 - 이미지: (1, 3, 800, 800), 마스크: (1, 1, 800, 800) +[2025-10-24 08:51:31,306] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 출력 형태: (800, 800, 3), dtype: uint8 +[2025-10-24 08:51:31,306] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 추론 완료: 428.68 ms +[2025-10-24 08:51:31,314] [LogListener] [DEBUG] [loggerModule.py:debug:275] MIGAN 인페인팅 성공 +[2025-10-24 08:51:31,314] [LogListener] [DEBUG] [loggerModule.py:debug:275] 메모리 변화 [인페인팅]: 43865.0MB -> 44083.1MB (+218.1MB, +0.5%) - 방법: migan +[2025-10-24 08:51:31,314] [LogListener] [DEBUG] [loggerModule.py:debug:275] 인페인팅 완료 +[2025-10-24 08:51:31,394] [LogListener] [DEBUG] [loggerModule.py:debug:275] 텍스트 렌더링 완료 +[2025-10-24 08:51:31,394] [LogListener] [DEBUG] [loggerModule.py:debug:275] watermark_text: 이미지 저작권 보유 +[2025-10-24 08:51:31,395] [LogListener] [DEBUG] [loggerModule.py:debug:275] is_watermark_enabled: True +[2025-10-24 08:51:31,709] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 저장 완료 : C:\ProgramData\ImgWorker\work\translated_option_img_1.webp +[2025-10-24 08:51:31,710] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 1 번역 완료: C:\ProgramData\ImgWorker\work\translated_option_img_1.webp +[2025-10-24 08:51:31,710] [LogListener] [DEBUG] [loggerModule.py:debug:275] ⏱ 이미지 파이프라인 총 5271.9ms | download=0.0ms | ocr=883.4ms | translate=3085.3ms | mask=3085.3ms | inpaint=442.7ms(migan/DirectML) | render=80.0ms | save=279.0ms +[2025-10-24 08:51:31,711] [LogListener] [DEBUG] [loggerModule.py:debug:275] process_single_image 호출 완료 +[2025-10-24 08:51:31,711] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 결과 반환 중: uid=f740071c-e962-44b6-bc6e-d0299d8eaff0 +[2025-10-24 08:51:31,711] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 결과 반환 완료: uid=f740071c-e962-44b6-bc6e-d0299d8eaff0 +[2025-10-24 08:51:31,711] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 08:51:31,712] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 33480) +[2025-10-24 08:51:33,653] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔥 작업 수신 성공 +[2025-10-24 08:51:33,653] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🚀 작업 처리 시작: cmd=process_single_image, uid=8c8fcf75-b20d-427f-b09d-ad9334c172d9 +[2025-10-24 08:51:33,653] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 프로세서 toggle_states 업데이트 : {'TEMP_IMAGE_DIR': 'C:\\ProgramData\\ImgWorker\\work', 'output_image_format': 'webp', 'use_local_rembg': True, 'local_rembg_model_path': 'D:\\py\\img_worker\\modules\\rembg_models', 'local_model_name': 'birefnet-general-lite', 'optionIMGTrans_type': 'GPU', 'detail_IMGTrans_type': 'GPU', 'thumb_trans_type': 'GPU', 'migan_use_accel': True, 'migan_onnx_path': 'D:\\py\\img_worker\\modules\\migan_onnx\\migan_pipeline_v2.onnx', 'ocr_provider_override': 'auto', 'migan_provider_override': 'auto', 'font_type': '폰트5', 'unwanted_texts': ['가격설명', '무료배송', '보증', '사은품', '세일', '이벤트', '특가', '품절', '할인', '할인가'], 'is_member_valid': False, 'authenticated_by_admin': True} +[2025-10-24 08:51:33,654] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 실행 직전: cmd=process_single_image +[2025-10-24 08:51:33,654] [LogListener] [DEBUG] [loggerModule.py:debug:275] process_single_image 호출 직전 +[2025-10-24 08:51:33,654] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 번역시작 +[2025-10-24 08:51:33,983] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 2 처리 시작: C:\ProgramData\ImgWorker\incoming\dl_5921e6c9b70d.jpg - 전체 번역 모드 +[2025-10-24 08:51:33,988] [LogListener] [DEBUG] [loggerModule.py:debug:275] 로컬 파일 경로 감지, 다운로드 생략: C:\ProgramData\ImgWorker\incoming\dl_5921e6c9b70d.jpg +[2025-10-24 08:51:33,988] [LogListener] [DEBUG] [loggerModule.py:debug:275] 옵션 이미지는 스케일 처리 건너뛰기: option +[2025-10-24 08:51:33,988] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 2 로컬 저장위치(옵션 이미지 원본 유지): C:\ProgramData\ImgWorker\incoming\dl_5921e6c9b70d.jpg +[2025-10-24 08:51:33,990] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔍 ONNX OCR 감지 방식: polygon +[2025-10-24 08:51:34,654] [LogListener] [DEBUG] [loggerModule.py:debug:275] ⚡ ONNX OCR 추론 완료: 660.7ms +[2025-10-24 08:51:34,672] [LogListener] [DEBUG] [loggerModule.py:debug:275] 📊 세부 시간 - 감지: 103.0ms, 인식: 526.7ms, 분류: 24.0ms +[2025-10-24 08:51:34,683] [LogListener] [DEBUG] [loggerModule.py:debug:275] 메모리 변화 [OCR 처리]: 44134.3MB -> 44152.1MB (+17.9MB, +0.0%) - 이미지 2 +[2025-10-24 08:51:34,683] [LogListener] [DEBUG] [loggerModule.py:debug:275] ocr_results: [{'text': '暴走兔', 'confidence': 0.9055703282356262, 'polygon': [[136.0, 21.0], [270.0, 23.0], [269.0, 63.0], [135.0, 61.0]], 'bbox': (135, 21, 136, 43), 'method': 'polygon'}, {'text': 'BAOZOUTU', 'confidence': 0.9931483268737793, 'polygon': [[134.0, 73.0], [273.0, 73.0], [273.0, 87.0], [134.0, 87.0]], 'bbox': (134, 73, 140, 15), 'method': 'polygon'}, {'text': '全波段收音机', 'confidence': 0.9979420304298401, 'polygon': [[81.0, 158.0], [288.0, 156.0], [288.0, 184.0], [81.0, 186.0]], 'bbox': (81, 156, 208, 31), 'method': 'polygon'}, {'text': '104', 'confidence': 0.7842115759849548, 'polygon': [[449.0, 200.0], [460.0, 193.0], [467.0, 204.0], [456.0, 211.0]], 'bbox': (449, 193, 19, 19), 'method': 'polygon'}, {'text': '150', 'confidence': 0.981334924697876, 'polygon': [[469.0, 193.0], [482.0, 193.0], [482.0, 208.0], [469.0, 208.0]], 'bbox': (469, 193, 14, 16), 'method': 'polygon'}, {'text': '蓝牙音', 'confidence': 0.9982023239135742, 'polygon': [[79.0, 213.0], [273.0, 213.0], [273.0, 248.0], [79.0, 248.0]], 'bbox': (79, 213, 195, 36), 'method': 'polygon'}, {'text': '响', 'confidence': 0.9996689558029175, 'polygon': [[259.0, 219.0], [290.0, 219.0], [290.0, 244.0], [259.0, 244.0]], 'bbox': (259, 219, 32, 26), 'method': 'polygon'}, {'text': '1400', 'confidence': 0.7393448948860168, 'polygon': [[470.0, 212.0], [483.0, 212.0], [483.0, 225.0], [470.0, 225.0]], 'bbox': (470, 212, 14, 14), 'method': 'polygon'}, {'text': 'R11', 'confidence': 0.9951024055480957, 'polygon': [[459.0, 245.0], [490.0, 245.0], [490.0, 256.0], [459.0, 256.0]], 'bbox': (459, 245, 32, 12), 'method': 'polygon'}, {'text': 'RADIO/BT/MUSIC', 'confidence': 0.9551190733909607, 'polygon': [[506.0, 247.0], [593.0, 247.0], [593.0, 254.0], [506.0, 254.0]], 'bbox': (506, 247, 88, 8), 'method': 'polygon'}, {'text': 'PLAYER', 'confidence': 0.9937713742256165, 'polygon': [[592.0, 243.0], [634.0, 245.0], [634.0, 256.0], [592.0, 254.0]], 'bbox': (592, 243, 43, 14), 'method': 'polygon'}, {'text': '4000毫安锂电池', 'confidence': 0.9979369640350342, 'polygon': [[82.0, 279.0], [289.0, 279.0], [289.0, 303.0], [82.0, 303.0]], 'bbox': (82, 279, 208, 25), 'method': 'polygon'}, {'text': '手', 'confidence': 0.9999611377716064, 'polygon': [[80.0, 335.0], [113.0, 335.0], [113.0, 365.0], [80.0, 365.0]], 'bbox': (80, 335, 34, 31), 'method': 'polygon'}, {'text': '电', 'confidence': 0.9988621473312378, 'polygon': [[168.0, 336.0], [204.0, 336.0], [204.0, 365.0], [168.0, 365.0]], 'bbox': (168, 336, 37, 30), 'method': 'polygon'}, {'text': '筒', 'confidence': 0.998289167881012, 'polygon': [[263.0, 336.0], [290.0, 336.0], [290.0, 365.0], [263.0, 365.0]], 'bbox': (263, 336, 28, 30), 'method': 'polygon'}, {'text': 'SOS警报/指南针', 'confidence': 0.9694029688835144, 'polygon': [[81.0, 398.0], [288.0, 398.0], [288.0, 425.0], [81.0, 425.0]], 'bbox': (81, 398, 208, 28), 'method': 'polygon'}, {'text': '手摇/太阳能发电', 'confidence': 0.9906120300292969, 'polygon': [[83.0, 457.0], [289.0, 457.0], [289.0, 485.0], [83.0, 485.0]], 'bbox': (83, 457, 207, 29), 'method': 'polygon'}, {'text': '可插U盘/TF卡', 'confidence': 0.9860914945602417, 'polygon': [[82.0, 518.0], [290.0, 518.0], [290.0, 545.0], [82.0, 545.0]], 'bbox': (82, 518, 209, 28), 'method': 'polygon'}, {'text': '限时', 'confidence': 0.9985836148262024, 'polygon': [[18.0, 659.0], [150.0, 661.0], [149.0, 729.0], [17.0, 727.0]], 'bbox': (17, 659, 134, 71), 'method': 'polygon'}, {'text': '赠送', 'confidence': 0.9996605515480042, 'polygon': [[15.0, 728.0], [149.0, 728.0], [149.0, 792.0], [15.0, 792.0]], 'bbox': (15, 728, 135, 65), 'method': 'polygon'}, {'text': '军工包', 'confidence': 0.9986985325813293, 'polygon': [[206.0, 768.0], [265.0, 768.0], [265.0, 790.0], [206.0, 790.0]], 'bbox': (206, 768, 60, 23), 'method': 'polygon'}, {'text': '延长天线', 'confidence': 0.9988996982574463, 'polygon': [[284.0, 769.0], [362.0, 769.0], [362.0, 790.0], [284.0, 790.0]], 'bbox': (284, 769, 79, 22), 'method': 'polygon'}, {'text': '内存卡', 'confidence': 0.9992824196815491, 'polygon': [[386.0, 769.0], [445.0, 769.0], [445.0, 790.0], [386.0, 790.0]], 'bbox': (386, 769, 60, 22), 'method': 'polygon'}, {'text': '读卡器', 'confidence': 0.9984410405158997, 'polygon': [[472.0, 767.0], [531.0, 767.0], [531.0, 792.0], [472.0, 792.0]], 'bbox': (472, 767, 60, 26), 'method': 'polygon'}, {'text': '充电线', 'confidence': 0.9992697834968567, 'polygon': [[558.0, 769.0], [617.0, 769.0], [617.0, 790.0], [558.0, 790.0]], 'bbox': (558, 769, 60, 22), 'method': 'polygon'}, {'text': '礼盒礼袋', 'confidence': 0.9986759424209595, 'polygon': [[637.0, 769.0], [714.0, 769.0], [714.0, 790.0], [637.0, 790.0]], 'bbox': (637, 769, 78, 22), 'method': 'polygon'}, {'text': '充电头', 'confidence': 0.9975659251213074, 'polygon': [[735.0, 767.0], [794.0, 767.0], [794.0, 792.0], [735.0, 792.0]], 'bbox': (735, 767, 60, 26), 'method': 'polygon'}] +[2025-10-24 08:51:34,684] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 90.6%): '暴走兔' +[2025-10-24 08:51:34,684] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): 'BAOZOUTU' +[2025-10-24 08:51:34,684] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.8%): '全波段收音机' +[2025-10-24 08:51:34,684] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): '104' +[2025-10-24 08:51:34,684] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): '150' +[2025-10-24 08:51:34,684] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.8%): '蓝牙音' +[2025-10-24 08:51:34,684] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 100.0%): '响' +[2025-10-24 08:51:34,685] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): '1400' +[2025-10-24 08:51:34,685] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): 'R11' +[2025-10-24 08:51:34,685] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): 'RADIO/BT/MUSIC' +[2025-10-24 08:51:34,685] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): 'PLAYER' +[2025-10-24 08:51:34,685] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.8%): '4000毫安锂电池' +[2025-10-24 08:51:34,685] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 100.0%): '手' +[2025-10-24 08:51:34,685] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.9%): '电' +[2025-10-24 08:51:34,685] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.8%): '筒' +[2025-10-24 08:51:34,685] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 96.9%): 'SOS警报/指南针' +[2025-10-24 08:51:34,685] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.1%): '手摇/太阳能发电' +[2025-10-24 08:51:34,685] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 98.6%): '可插U盘/TF卡' +[2025-10-24 08:51:34,685] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.9%): '限时' +[2025-10-24 08:51:34,685] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 100.0%): '赠送' +[2025-10-24 08:51:34,685] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.9%): '军工包' +[2025-10-24 08:51:34,686] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.9%): '延长天线' +[2025-10-24 08:51:34,686] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.9%): '内存卡' +[2025-10-24 08:51:34,686] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.8%): '读卡器' +[2025-10-24 08:51:34,686] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.9%): '充电线' +[2025-10-24 08:51:34,686] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.9%): '礼盒礼袋' +[2025-10-24 08:51:34,686] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.8%): '充电头' +[2025-10-24 08:51:34,686] [LogListener] [DEBUG] [loggerModule.py:debug:275] 필터링 결과: 20/27개 (신뢰도 + & 중국어) +[2025-10-24 08:51:34,686] [LogListener] [DEBUG] [loggerModule.py:debug:275] filter_ocr_results: [{'text': '暴走兔', 'confidence': 0.9055703282356262, 'polygon': [[136.0, 21.0], [270.0, 23.0], [269.0, 63.0], [135.0, 61.0]], 'bbox': (135, 21, 136, 43), 'method': 'polygon'}, {'text': '全波段收音机', 'confidence': 0.9979420304298401, 'polygon': [[81.0, 158.0], [288.0, 156.0], [288.0, 184.0], [81.0, 186.0]], 'bbox': (81, 156, 208, 31), 'method': 'polygon'}, {'text': '蓝牙音', 'confidence': 0.9982023239135742, 'polygon': [[79.0, 213.0], [273.0, 213.0], [273.0, 248.0], [79.0, 248.0]], 'bbox': (79, 213, 195, 36), 'method': 'polygon'}, {'text': '响', 'confidence': 0.9996689558029175, 'polygon': [[259.0, 219.0], [290.0, 219.0], [290.0, 244.0], [259.0, 244.0]], 'bbox': (259, 219, 32, 26), 'method': 'polygon'}, {'text': '4000毫安锂电池', 'confidence': 0.9979369640350342, 'polygon': [[82.0, 279.0], [289.0, 279.0], [289.0, 303.0], [82.0, 303.0]], 'bbox': (82, 279, 208, 25), 'method': 'polygon'}, {'text': '手', 'confidence': 0.9999611377716064, 'polygon': [[80.0, 335.0], [113.0, 335.0], [113.0, 365.0], [80.0, 365.0]], 'bbox': (80, 335, 34, 31), 'method': 'polygon'}, {'text': '电', 'confidence': 0.9988621473312378, 'polygon': [[168.0, 336.0], [204.0, 336.0], [204.0, 365.0], [168.0, 365.0]], 'bbox': (168, 336, 37, 30), 'method': 'polygon'}, {'text': '筒', 'confidence': 0.998289167881012, 'polygon': [[263.0, 336.0], [290.0, 336.0], [290.0, 365.0], [263.0, 365.0]], 'bbox': (263, 336, 28, 30), 'method': 'polygon'}, {'text': 'SOS警报/指南针', 'confidence': 0.9694029688835144, 'polygon': [[81.0, 398.0], [288.0, 398.0], [288.0, 425.0], [81.0, 425.0]], 'bbox': (81, 398, 208, 28), 'method': 'polygon'}, {'text': '手摇/太阳能发电', 'confidence': 0.9906120300292969, 'polygon': [[83.0, 457.0], [289.0, 457.0], [289.0, 485.0], [83.0, 485.0]], 'bbox': (83, 457, 207, 29), 'method': 'polygon'}, {'text': '可插U盘/TF卡', 'confidence': 0.9860914945602417, 'polygon': [[82.0, 518.0], [290.0, 518.0], [290.0, 545.0], [82.0, 545.0]], 'bbox': (82, 518, 209, 28), 'method': 'polygon'}, {'text': '限时', 'confidence': 0.9985836148262024, 'polygon': [[18.0, 659.0], [150.0, 661.0], [149.0, 729.0], [17.0, 727.0]], 'bbox': (17, 659, 134, 71), 'method': 'polygon'}, {'text': '赠送', 'confidence': 0.9996605515480042, 'polygon': [[15.0, 728.0], [149.0, 728.0], [149.0, 792.0], [15.0, 792.0]], 'bbox': (15, 728, 135, 65), 'method': 'polygon'}, {'text': '军工包', 'confidence': 0.9986985325813293, 'polygon': [[206.0, 768.0], [265.0, 768.0], [265.0, 790.0], [206.0, 790.0]], 'bbox': (206, 768, 60, 23), 'method': 'polygon'}, {'text': '延长天线', 'confidence': 0.9988996982574463, 'polygon': [[284.0, 769.0], [362.0, 769.0], [362.0, 790.0], [284.0, 790.0]], 'bbox': (284, 769, 79, 22), 'method': 'polygon'}, {'text': '内存卡', 'confidence': 0.9992824196815491, 'polygon': [[386.0, 769.0], [445.0, 769.0], [445.0, 790.0], [386.0, 790.0]], 'bbox': (386, 769, 60, 22), 'method': 'polygon'}, {'text': '读卡器', 'confidence': 0.9984410405158997, 'polygon': [[472.0, 767.0], [531.0, 767.0], [531.0, 792.0], [472.0, 792.0]], 'bbox': (472, 767, 60, 26), 'method': 'polygon'}, {'text': '充电线', 'confidence': 0.9992697834968567, 'polygon': [[558.0, 769.0], [617.0, 769.0], [617.0, 790.0], [558.0, 790.0]], 'bbox': (558, 769, 60, 22), 'method': 'polygon'}, {'text': '礼盒礼袋', 'confidence': 0.9986759424209595, 'polygon': [[637.0, 769.0], [714.0, 769.0], [714.0, 790.0], [637.0, 790.0]], 'bbox': (637, 769, 78, 22), 'method': 'polygon'}, {'text': '充电头', 'confidence': 0.9975659251213074, 'polygon': [[735.0, 767.0], [794.0, 767.0], [794.0, 792.0], [735.0, 792.0]], 'bbox': (735, 767, 60, 26), 'method': 'polygon'}] +[2025-10-24 08:51:34,687] [LogListener] [DEBUG] [loggerModule.py:debug:275] 중국어 텍스트 20개 필터링 완료 +[2025-10-24 08:51:34,687] [LogListener] [DEBUG] [loggerModule.py:debug:275] 한글 텍스트 0개 필터링 완료 +[2025-10-24 08:51:35,182] [LogListener] [DEBUG] [loggerModule.py:debug:275] translated_texts: ['도망친 토끼', '풀 밴드 라디오', '블루투스 사운드', '반지', '4000mAh 리튬 배터리', '손', '전기', '실린더', 'SOS 경보 / 나침반', '핸드 크랭크 / 태양광 발전', 'U 디스크 삽입 가능 / TF 카드', '제한된 시간', '포기하다', '군사 가방', '확장 안테나', '메모리 카드', '카드 리더', '충전 케이블', '선물 상자 선물 가방', '충전 헤드'] +[2025-10-24 08:51:35,183] [LogListener] [DEBUG] [loggerModule.py:debug:275] 마스크 생성 완료 +[2025-10-24 08:51:35,183] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 2 OCR 권한 없음, 전체 번역 모드 +[2025-10-24 08:51:35,191] [LogListener] [DEBUG] [loggerModule.py:debug:275] [AUTO Inpaint] coverage=0.258, comps=4, min_center_dist=0.274 → request +[2025-10-24 08:51:35,191] [LogListener] [DEBUG] [loggerModule.py:debug:275] ocr_count: 20 +[2025-10-24 08:51:35,191] [LogListener] [DEBUG] [loggerModule.py:debug:275] is_member_valid: False +[2025-10-24 08:51:35,191] [LogListener] [DEBUG] [loggerModule.py:debug:275] [set_inpaint_method] prefix=option, target_key=optionIMGTrans_type, trans_type=GPU → inpaint_method=migan +[2025-10-24 08:51:35,191] [LogListener] [DEBUG] [loggerModule.py:debug:275] 최종 inpaint_method: migan +[2025-10-24 08:51:35,192] [LogListener] [DEBUG] [loggerModule.py:debug:275] MIGAN 인페인팅 시도 +[2025-10-24 08:51:35,193] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 입력 형태 - 이미지: (1, 3, 800, 800), 마스크: (1, 1, 800, 800) +[2025-10-24 08:51:35,407] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 출력 형태: (800, 800, 3), dtype: uint8 +[2025-10-24 08:51:35,407] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 추론 완료: 213.10 ms +[2025-10-24 08:51:35,415] [LogListener] [DEBUG] [loggerModule.py:debug:275] MIGAN 인페인팅 성공 +[2025-10-24 08:51:35,415] [LogListener] [DEBUG] [loggerModule.py:debug:275] 메모리 변화 [인페인팅]: 44161.0MB -> 44187.3MB (+26.3MB, +0.1%) - 방법: migan +[2025-10-24 08:51:35,415] [LogListener] [DEBUG] [loggerModule.py:debug:275] 인페인팅 완료 +[2025-10-24 08:51:35,488] [LogListener] [DEBUG] [loggerModule.py:debug:275] 텍스트 렌더링 완료 +[2025-10-24 08:51:35,488] [LogListener] [DEBUG] [loggerModule.py:debug:275] watermark_text: 이미지 저작권 보유 +[2025-10-24 08:51:35,488] [LogListener] [DEBUG] [loggerModule.py:debug:275] is_watermark_enabled: True +[2025-10-24 08:51:35,815] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 저장 완료 : C:\ProgramData\ImgWorker\work\translated_option_img_2.webp +[2025-10-24 08:51:35,816] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 2 번역 완료: C:\ProgramData\ImgWorker\work\translated_option_img_2.webp +[2025-10-24 08:51:35,817] [LogListener] [DEBUG] [loggerModule.py:debug:275] ⏱ 이미지 파이프라인 총 2161.4ms | download=0.0ms | ocr=670.7ms | translate=499.3ms | mask=499.3ms | inpaint=231.1ms(migan/DirectML) | render=71.8ms | save=295.6ms +[2025-10-24 08:51:35,818] [LogListener] [DEBUG] [loggerModule.py:debug:275] process_single_image 호출 완료 +[2025-10-24 08:51:35,818] [ResultListener] [WARNING] [loggerModule.py:warning:287] 워커 롤링 스케줄: reason=job-count-threshold +[2025-10-24 08:51:35,818] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 결과 반환 중: uid=8c8fcf75-b20d-427f-b09d-ad9334c172d9 +[2025-10-24 08:51:35,818] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 결과 반환 완료: uid=8c8fcf75-b20d-427f-b09d-ad9334c172d9 +[2025-10-24 08:51:35,818] [WorkerRoller] [WARNING] [loggerModule.py:warning:287] 워커 롤링 시작: job-count-threshold +[2025-10-24 08:51:35,818] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 08:51:35,818] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 33480) +[2025-10-24 08:51:35,864] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔥 작업 수신 성공 +[2025-10-24 08:51:35,865] [LogListener] [DEBUG] [loggerModule.py:debug:275] Shutdown signal 수신 → 종료 +[2025-10-24 08:51:35,865] [LogListener] [DEBUG] [loggerModule.py:debug:275] OCR 모듈 정리 완료 +[2025-10-24 08:51:35,865] [LogListener] [DEBUG] [loggerModule.py:debug:275] 마스크 모듈 정리 완료 +[2025-10-24 08:51:35,866] [LogListener] [DEBUG] [loggerModule.py:debug:275] 임시 폴더 삭제됨: C:\ProgramData\ImgWorker\work +[2025-10-24 08:51:35,866] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 프로세서 소멸 +[2025-10-24 08:51:36,271] [WorkerRoller] [INFO] [loggerModule.py:info:281] 워커 프로세스 기동: PID=23704 +[2025-10-24 08:51:36,776] [LogListener] [DEBUG] [loggerModule.py:debug:275] ImageWorker 프로세스 기동 (PID=23704, Name=ImageWorkerProcess) +[2025-10-24 08:51:36,776] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔧 ImageProcessor3 초기화 시작... +[2025-10-24 08:51:36,776] [LogListener] [DEBUG] [loggerModule.py:debug:275] tracemalloc 메모리 추적 시작 +[2025-10-24 08:51:36,783] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ DirectML 환경 준비 완료 (Windows DirectX 12 기반) +[2025-10-24 08:51:36,783] [LogListener] [DEBUG] [loggerModule.py:debug:275] === 🚀 DirectML GPU 상태 초기화 시작 🚀 === +[2025-10-24 08:51:36,783] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🎯 사용자 GPU 가속 요청: False +[2025-10-24 08:51:36,783] [LogListener] [DEBUG] [loggerModule.py:debug:275] 💻 현재 운영체제: Windows +[2025-10-24 08:51:36,784] [LogListener] [DEBUG] [loggerModule.py:debug:275] GPU 가속이 비활성화됨 (toggle_states['use_cuda'] = False) +[2025-10-24 08:51:36,784] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔒 안전한 CPU 모드로 모든 GPU 설정 강제 비활성화 +[2025-10-24 08:51:36,784] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔧 ImageProcessor3 GPU 상태 요약: +[2025-10-24 08:51:36,784] [LogListener] [DEBUG] [loggerModule.py:debug:275] - CUDA 사용 가능: False +[2025-10-24 08:51:36,784] [LogListener] [DEBUG] [loggerModule.py:debug:275] - toggle_states['use_cuda']: NOT_SET +[2025-10-24 08:51:36,784] [LogListener] [DEBUG] [loggerModule.py:debug:275] - GPU 하드웨어 정보: {} +[2025-10-24 08:51:36,784] [LogListener] [DEBUG] [loggerModule.py:debug:275] ImageProcessor3 Init toggle_states: {'TEMP_IMAGE_DIR': 'C:\\ProgramData\\ImgWorker\\work', 'output_image_format': 'webp', 'use_local_rembg': True, 'local_rembg_model_path': 'D:\\py\\img_worker\\modules\\rembg_models', 'local_model_name': 'birefnet-general-lite', 'optionIMGTrans_type': 'CPU', 'detail_IMGTrans_type': 'CPU', 'thumb_trans_type': 'CPU', 'migan_use_accel': True, 'migan_onnx_path': 'D:\\py\\img_worker\\modules\\migan_onnx\\migan_pipeline_v2.onnx', 'ocr_provider_override': 'auto', 'migan_provider_override': 'auto'} +[2025-10-24 08:51:36,785] [LogListener] [DEBUG] [loggerModule.py:debug:275] is_member_valid: False +[2025-10-24 08:51:36,785] [LogListener] [DEBUG] [loggerModule.py:debug:275] debug_images 디렉토리 이미 존재: D:\py\img_worker\modules\debug_images +[2025-10-24 08:51:36,785] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.font_path: D:\py\img_worker\modules\fonts\HakgyoansimDunggeunmisoTTFB.ttf +[2025-10-24 08:51:36,785] [LogListener] [DEBUG] [loggerModule.py:debug:275] toggle_states font_path: D:\py\img_worker\modules\fonts\HakgyoansimDunggeunmisoTTFB.ttf +[2025-10-24 08:51:36,785] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.TEMP_IMAGE_DIR: C:\ProgramData\ImgWorker\work +[2025-10-24 08:51:36,785] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.debugging_save_Dir: D:\py\img_worker\modules\debug_images +[2025-10-24 08:51:36,785] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.unwanted_texts: {} +[2025-10-24 08:51:36,785] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.inpaint_method: migan +[2025-10-24 08:51:36,785] [LogListener] [DEBUG] [loggerModule.py:debug:275] Image.MAX_IMAGE_PIXELS set to 20000000 +[2025-10-24 08:51:36,785] [LogListener] [DEBUG] [loggerModule.py:debug:275] ONNX 모델 타입 설정값: 자동 선택, GPU 정보: {} +[2025-10-24 08:51:36,785] [LogListener] [DEBUG] [loggerModule.py:debug:275] 자동 선택 모드: GPU 추천 모델 simp 사용 +[2025-10-24 08:51:36,785] [LogListener] [DEBUG] [loggerModule.py:debug:275] ONNX OCR 모델 타입 결정: simp (GPU: False) +[2025-10-24 08:51:36,786] [LogListener] [DEBUG] [loggerModule.py:debug:275] ONNX OCR 모듈 CPU 모드로 설정 +[2025-10-24 08:51:36,786] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🚀 ONNX TextSystem 초기화 시작 (CPU 모드) +[2025-10-24 08:51:36,786] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🚀 ONNX TextSystem 초기화 시작 (CPU 모드) +[2025-10-24 08:51:38,372] [LogListener] [DEBUG] [loggerModule.py:debug:275] 문자 사전 파일 UTF-8 확인 완료: D:\py\img_worker\modules\onnx_ocr_module\dict\ppocr_keys_v1.txt +[2025-10-24 08:51:38,458] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ ONNX TextSystem 초기화 완료 (CPU + SIMP (호환성) 모델) +[2025-10-24 08:51:38,459] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ ONNX TextSystem 초기화 완료 (CPU + SIMP 모델) +[2025-10-24 08:51:38,459] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ ONNX OCR 모듈 초기화 성공 (CPU 모드) +[2025-10-24 08:51:38,459] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ ONNX OCR 모듈 초기화 성공 +[2025-10-24 08:51:38,459] [LogListener] [DEBUG] [loggerModule.py:debug:275] 마스크 모듈 초기화 완료 +[2025-10-24 08:51:38,459] [LogListener] [DEBUG] [loggerModule.py:debug:275] MaskModule 초기화 성공 +[2025-10-24 08:51:38,459] [LogListener] [DEBUG] [loggerModule.py:debug:275] 텍스트 렌더링 모듈 초기화 완료 +[2025-10-24 08:51:38,459] [LogListener] [DEBUG] [loggerModule.py:debug:275] 기본 폰트: D:\py\img_worker\modules\fonts\HakgyoansimDunggeunmisoTTFB.ttf +[2025-10-24 08:51:38,460] [LogListener] [DEBUG] [loggerModule.py:debug:275] TextRenderingModule 초기화 성공 +[2025-10-24 08:51:38,460] [LogListener] [DEBUG] [loggerModule.py:debug:275] 폰트 로드 성공: D:\py\img_worker\modules\fonts\HakgyoansimDunggeunmisoTTFB.ttf +[2025-10-24 08:51:38,460] [LogListener] [DEBUG] [loggerModule.py:debug:275] PostImageManager 초기화 성공 +[2025-10-24 08:51:38,460] [LogListener] [DEBUG] [loggerModule.py:debug:275] Request_AI_Server GPU 상태: CUDA 사용 가능=False +[2025-10-24 08:51:38,460] [LogListener] [DEBUG] [loggerModule.py:debug:275] Request_AI_Server 초기화 성공 +[2025-10-24 08:51:38,460] [LogListener] [DEBUG] [loggerModule.py:debug:275] GoogleTranslate 초기화 성공 +[2025-10-24 08:51:38,460] [LogListener] [DEBUG] [loggerModule.py:debug:275] MIGAN CUDA 사용 불가 - CPU 모드로 설정 +[2025-10-24 08:51:38,460] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] GPU 관리자 전달: GPUManager, can_use_cuda: False +[2025-10-24 08:51:38,491] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] GPU 관리자 사용 가능: False +[2025-10-24 08:51:38,724] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 사용 가능한 providers: ['DmlExecutionProvider', 'CPUExecutionProvider'] +[2025-10-24 08:51:38,724] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] DirectML 가속 활성화 +[2025-10-24 08:51:38,724] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 최종 providers: [('DmlExecutionProvider', {}), ('CPUExecutionProvider', {})] +[2025-10-24 08:51:38,724] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] DirectML 가속 시도: [('DmlExecutionProvider', {}), ('CPUExecutionProvider', {})] +[2025-10-24 08:51:38,725] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] DirectML 가속 성공! 실제 providers: ['DmlExecutionProvider', 'CPUExecutionProvider'] +[2025-10-24 08:51:38,725] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 입력 0: image, 형태: ['batch_size', 3, 'height', 'width'], 타입: tensor(uint8) +[2025-10-24 08:51:38,725] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 입력 1: mask, 형태: ['batch_size', 1, 'height', 'width'], 타입: tensor(uint8) +[2025-10-24 08:51:38,725] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 출력 0: result, 형태: ['ScatterNDresult_dim_0', 3, 'ScatterNDresult_dim_2', 'ScatterNDresult_dim_3'], 타입: tensor(uint8) +[2025-10-24 08:51:38,725] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 세션 준비 완료. providers=['DmlExecutionProvider', 'CPUExecutionProvider'] +[2025-10-24 08:51:38,725] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] GPU 관리자 연결 완료: GPUManager +[2025-10-24 08:51:38,725] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 파이프라인 gpu_manager 속성: True, 값: +[2025-10-24 08:51:38,725] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 초기화 완료: gpu_manager 속성=True, 값= +[2025-10-24 08:51:38,725] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔰 OCR 모듈 Warm-up 시작... +[2025-10-24 08:51:38,725] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔍 ONNX OCR 감지 방식: polygon +[2025-10-24 08:51:38,730] [LogListener] [DEBUG] [loggerModule.py:debug:275] ⚡ ONNX OCR 추론 완료: 5.0ms +[2025-10-24 08:51:38,730] [LogListener] [DEBUG] [loggerModule.py:debug:275] 📊 세부 시간 - 감지: 3.0ms, 인식: 0.0ms, 분류: 0.0ms +[2025-10-24 08:51:38,730] [LogListener] [WARNING] [loggerModule.py:warning:287] ⚠️ ONNX OCR 결과가 비어있습니다 +[2025-10-24 08:51:38,733] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ OCR 모듈 Warm-up 성공 +[2025-10-24 08:51:38,734] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔰 ImageProcessor Warm‑up 완료 +[2025-10-24 08:51:38,734] [ResultListener] [INFO] [loggerModule.py:info:281] 워커 READY 수신 +[2025-10-24 08:51:38,734] [ResultListener] [INFO] [loggerModule.py:info:281] 워커 READY 수신 +[2025-10-24 08:51:38,734] [LogListener] [DEBUG] [loggerModule.py:debug:275] 📡 추가 READY 신호 전송 완료 +[2025-10-24 08:51:38,734] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 08:51:38,734] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 23704) +[2025-10-24 08:52:38,734] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 08:52:38,734] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 08:52:38,734] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 23704) +[2025-10-24 08:53:08,417] [MainThread] [INFO] [loggerModule.py:info:281] API 서버 종료 +[2025-10-24 08:53:08,651] [MainThread] [INFO] [loggerModule.py:info:281] 워커 프로세스 종료 +[2025-10-24 08:53:10,303] [MainThread] [INFO] [loggerModule.py:info:281] API 서버 시작 +[2025-10-24 08:53:10,320] [MainThread] [INFO] [loggerModule.py:info:281] 워커 프로세스 기동: PID=11384 +[2025-10-24 08:53:10,772] [LogListener] [DEBUG] [loggerModule.py:debug:275] ImageWorker 프로세스 기동 (PID=11384, Name=ImageWorkerProcess) +[2025-10-24 08:53:10,773] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔧 ImageProcessor3 초기화 시작... +[2025-10-24 08:53:10,773] [LogListener] [DEBUG] [loggerModule.py:debug:275] tracemalloc 메모리 추적 시작 +[2025-10-24 08:53:10,779] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ DirectML 환경 준비 완료 (Windows DirectX 12 기반) +[2025-10-24 08:53:10,779] [LogListener] [DEBUG] [loggerModule.py:debug:275] === 🚀 DirectML GPU 상태 초기화 시작 🚀 === +[2025-10-24 08:53:10,779] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🎯 사용자 GPU 가속 요청: False +[2025-10-24 08:53:10,780] [LogListener] [DEBUG] [loggerModule.py:debug:275] 💻 현재 운영체제: Windows +[2025-10-24 08:53:10,780] [LogListener] [DEBUG] [loggerModule.py:debug:275] GPU 가속이 비활성화됨 (toggle_states['use_cuda'] = False) +[2025-10-24 08:53:10,780] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔒 안전한 CPU 모드로 모든 GPU 설정 강제 비활성화 +[2025-10-24 08:53:10,780] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔧 ImageProcessor3 GPU 상태 요약: +[2025-10-24 08:53:10,780] [LogListener] [DEBUG] [loggerModule.py:debug:275] - CUDA 사용 가능: False +[2025-10-24 08:53:10,780] [LogListener] [DEBUG] [loggerModule.py:debug:275] - toggle_states['use_cuda']: NOT_SET +[2025-10-24 08:53:10,780] [LogListener] [DEBUG] [loggerModule.py:debug:275] - GPU 하드웨어 정보: {} +[2025-10-24 08:53:10,780] [LogListener] [DEBUG] [loggerModule.py:debug:275] ImageProcessor3 Init toggle_states: {'TEMP_IMAGE_DIR': 'C:\\ProgramData\\ImgWorker\\work', 'output_image_format': 'webp', 'use_local_rembg': True, 'local_rembg_model_path': 'D:\\py\\img_worker\\modules\\rembg_models', 'local_model_name': 'birefnet-general-lite', 'optionIMGTrans_type': 'CPU', 'detail_IMGTrans_type': 'CPU', 'thumb_trans_type': 'CPU', 'migan_use_accel': True, 'migan_onnx_path': 'D:\\py\\img_worker\\modules\\migan_onnx\\migan_pipeline_v2.onnx', 'ocr_provider_override': 'auto', 'migan_provider_override': 'auto'} +[2025-10-24 08:53:10,780] [LogListener] [DEBUG] [loggerModule.py:debug:275] is_member_valid: False +[2025-10-24 08:53:10,780] [LogListener] [DEBUG] [loggerModule.py:debug:275] debug_images 디렉토리 이미 존재: D:\py\img_worker\modules\debug_images +[2025-10-24 08:53:10,780] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.font_path: D:\py\img_worker\modules\fonts\HakgyoansimDunggeunmisoTTFB.ttf +[2025-10-24 08:53:10,781] [LogListener] [DEBUG] [loggerModule.py:debug:275] toggle_states font_path: D:\py\img_worker\modules\fonts\HakgyoansimDunggeunmisoTTFB.ttf +[2025-10-24 08:53:10,781] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.TEMP_IMAGE_DIR: C:\ProgramData\ImgWorker\work +[2025-10-24 08:53:10,781] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.debugging_save_Dir: D:\py\img_worker\modules\debug_images +[2025-10-24 08:53:10,781] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.unwanted_texts: {} +[2025-10-24 08:53:10,781] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.inpaint_method: migan +[2025-10-24 08:53:10,781] [LogListener] [DEBUG] [loggerModule.py:debug:275] Image.MAX_IMAGE_PIXELS set to 20000000 +[2025-10-24 08:53:10,781] [LogListener] [DEBUG] [loggerModule.py:debug:275] ONNX 모델 타입 설정값: 자동 선택, GPU 정보: {} +[2025-10-24 08:53:10,781] [LogListener] [DEBUG] [loggerModule.py:debug:275] 자동 선택 모드: GPU 추천 모델 simp 사용 +[2025-10-24 08:53:10,781] [LogListener] [DEBUG] [loggerModule.py:debug:275] ONNX OCR 모델 타입 결정: simp (GPU: False) +[2025-10-24 08:53:10,781] [LogListener] [DEBUG] [loggerModule.py:debug:275] ONNX OCR 모듈 CPU 모드로 설정 +[2025-10-24 08:53:10,782] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🚀 ONNX TextSystem 초기화 시작 (CPU 모드) +[2025-10-24 08:53:10,782] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🚀 ONNX TextSystem 초기화 시작 (CPU 모드) +[2025-10-24 08:53:12,340] [LogListener] [DEBUG] [loggerModule.py:debug:275] 문자 사전 파일 UTF-8 확인 완료: D:\py\img_worker\modules\onnx_ocr_module\dict\ppocr_keys_v1.txt +[2025-10-24 08:53:12,424] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ ONNX TextSystem 초기화 완료 (CPU + SIMP (호환성) 모델) +[2025-10-24 08:53:12,424] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ ONNX TextSystem 초기화 완료 (CPU + SIMP 모델) +[2025-10-24 08:53:12,424] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ ONNX OCR 모듈 초기화 성공 (CPU 모드) +[2025-10-24 08:53:12,425] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ ONNX OCR 모듈 초기화 성공 +[2025-10-24 08:53:12,425] [LogListener] [DEBUG] [loggerModule.py:debug:275] 마스크 모듈 초기화 완료 +[2025-10-24 08:53:12,425] [LogListener] [DEBUG] [loggerModule.py:debug:275] MaskModule 초기화 성공 +[2025-10-24 08:53:12,425] [LogListener] [DEBUG] [loggerModule.py:debug:275] 텍스트 렌더링 모듈 초기화 완료 +[2025-10-24 08:53:12,425] [LogListener] [DEBUG] [loggerModule.py:debug:275] 기본 폰트: D:\py\img_worker\modules\fonts\HakgyoansimDunggeunmisoTTFB.ttf +[2025-10-24 08:53:12,425] [LogListener] [DEBUG] [loggerModule.py:debug:275] TextRenderingModule 초기화 성공 +[2025-10-24 08:53:12,426] [LogListener] [DEBUG] [loggerModule.py:debug:275] 폰트 로드 성공: D:\py\img_worker\modules\fonts\HakgyoansimDunggeunmisoTTFB.ttf +[2025-10-24 08:53:12,426] [LogListener] [DEBUG] [loggerModule.py:debug:275] PostImageManager 초기화 성공 +[2025-10-24 08:53:12,426] [LogListener] [DEBUG] [loggerModule.py:debug:275] Request_AI_Server GPU 상태: CUDA 사용 가능=False +[2025-10-24 08:53:12,426] [LogListener] [DEBUG] [loggerModule.py:debug:275] Request_AI_Server 초기화 성공 +[2025-10-24 08:53:12,426] [LogListener] [DEBUG] [loggerModule.py:debug:275] GoogleTranslate 초기화 성공 +[2025-10-24 08:53:12,426] [LogListener] [DEBUG] [loggerModule.py:debug:275] MIGAN CUDA 사용 불가 - CPU 모드로 설정 +[2025-10-24 08:53:12,426] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] GPU 관리자 전달: GPUManager, can_use_cuda: False +[2025-10-24 08:53:12,445] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] GPU 관리자 사용 가능: False +[2025-10-24 08:53:12,656] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 사용 가능한 providers: ['DmlExecutionProvider', 'CPUExecutionProvider'] +[2025-10-24 08:53:12,656] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] DirectML 가속 활성화 +[2025-10-24 08:53:12,656] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 최종 providers: [('DmlExecutionProvider', {}), ('CPUExecutionProvider', {})] +[2025-10-24 08:53:12,656] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] DirectML 가속 시도: [('DmlExecutionProvider', {}), ('CPUExecutionProvider', {})] +[2025-10-24 08:53:12,657] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] DirectML 가속 성공! 실제 providers: ['DmlExecutionProvider', 'CPUExecutionProvider'] +[2025-10-24 08:53:12,657] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 입력 0: image, 형태: ['batch_size', 3, 'height', 'width'], 타입: tensor(uint8) +[2025-10-24 08:53:12,657] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 입력 1: mask, 형태: ['batch_size', 1, 'height', 'width'], 타입: tensor(uint8) +[2025-10-24 08:53:12,657] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 출력 0: result, 형태: ['ScatterNDresult_dim_0', 3, 'ScatterNDresult_dim_2', 'ScatterNDresult_dim_3'], 타입: tensor(uint8) +[2025-10-24 08:53:12,657] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 세션 준비 완료. providers=['DmlExecutionProvider', 'CPUExecutionProvider'] +[2025-10-24 08:53:12,657] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] GPU 관리자 연결 완료: GPUManager +[2025-10-24 08:53:12,657] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 파이프라인 gpu_manager 속성: True, 값: +[2025-10-24 08:53:12,657] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 초기화 완료: gpu_manager 속성=True, 값= +[2025-10-24 08:53:12,657] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔰 OCR 모듈 Warm-up 시작... +[2025-10-24 08:53:12,658] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔍 ONNX OCR 감지 방식: polygon +[2025-10-24 08:53:12,662] [LogListener] [DEBUG] [loggerModule.py:debug:275] ⚡ ONNX OCR 추론 완료: 5.0ms +[2025-10-24 08:53:12,662] [LogListener] [DEBUG] [loggerModule.py:debug:275] 📊 세부 시간 - 감지: 3.0ms, 인식: 0.0ms, 분류: 0.0ms +[2025-10-24 08:53:12,662] [LogListener] [WARNING] [loggerModule.py:warning:287] ⚠️ ONNX OCR 결과가 비어있습니다 +[2025-10-24 08:53:12,665] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ OCR 모듈 Warm-up 성공 +[2025-10-24 08:53:12,666] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔰 ImageProcessor Warm‑up 완료 +[2025-10-24 08:53:12,666] [ResultListener] [INFO] [loggerModule.py:info:281] 워커 READY 수신 +[2025-10-24 08:53:12,666] [ResultListener] [INFO] [loggerModule.py:info:281] 워커 READY 수신 +[2025-10-24 08:53:12,666] [LogListener] [DEBUG] [loggerModule.py:debug:275] 📡 추가 READY 신호 전송 완료 +[2025-10-24 08:53:12,667] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 08:53:12,667] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 11384) +[2025-10-24 08:53:12,667] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔥 작업 수신 성공 +[2025-10-24 08:53:12,667] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🚀 작업 처리 시작: cmd=__PING__, uid=acc04e4f-333e-4bbe-9214-8c6893255d26 +[2025-10-24 08:53:12,667] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 실행 직전: cmd=__PING__ +[2025-10-24 08:53:12,667] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 결과 반환 중: uid=acc04e4f-333e-4bbe-9214-8c6893255d26 +[2025-10-24 08:53:12,667] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 결과 반환 완료: uid=acc04e4f-333e-4bbe-9214-8c6893255d26 +[2025-10-24 08:53:12,667] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 08:53:12,668] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 11384) +[2025-10-24 08:54:12,672] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 08:54:12,672] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 08:54:12,672] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 11384) +[2025-10-24 08:55:12,687] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 08:55:12,687] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 08:55:12,687] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 11384) +[2025-10-24 08:56:12,691] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 08:56:12,691] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 08:56:12,691] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 11384) +[2025-10-24 08:57:12,693] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 08:57:12,693] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 08:57:12,693] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 11384) +[2025-10-24 08:58:12,703] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 08:58:12,703] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 08:58:12,703] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 11384) +[2025-10-24 08:59:12,710] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 08:59:12,710] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 08:59:12,710] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 11384) +[2025-10-24 09:00:12,717] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 09:00:12,717] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 09:00:12,717] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 11384) +[2025-10-24 09:01:12,723] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 09:01:12,723] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 09:01:12,723] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 11384) +[2025-10-24 09:02:12,735] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 09:02:12,735] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 09:02:12,735] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 11384) +[2025-10-24 09:03:12,742] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 09:03:12,742] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 09:03:12,742] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 11384) +[2025-10-24 09:04:12,744] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 09:04:12,744] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 09:04:12,744] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 11384) +[2025-10-24 09:05:12,751] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 09:05:12,751] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 09:05:12,751] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 11384) +[2025-10-24 09:06:12,758] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 09:06:12,758] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 09:06:12,758] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 11384) +[2025-10-24 09:07:12,769] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 09:07:12,769] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 09:07:12,769] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 11384) +[2025-10-24 09:08:12,772] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 09:08:12,772] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 09:08:12,772] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 11384) +[2025-10-24 09:09:12,786] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 09:09:12,786] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 09:09:12,786] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 11384) +[2025-10-24 09:10:12,799] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 09:10:12,799] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 09:10:12,799] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 11384) +[2025-10-24 09:11:12,808] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 09:11:12,808] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 09:11:12,808] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 11384) +[2025-10-24 09:12:12,820] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 09:12:12,820] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 09:12:12,820] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 11384) +[2025-10-24 09:13:12,827] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 09:13:12,827] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 09:13:12,827] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 11384) +[2025-10-24 09:14:12,835] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 09:14:12,835] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 09:14:12,835] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 11384) +[2025-10-24 09:15:12,838] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 09:15:12,838] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 09:15:12,838] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 11384) +[2025-10-24 09:16:12,839] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 09:16:12,839] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 09:16:12,839] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 11384) +[2025-10-24 09:17:12,844] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 09:17:12,844] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 09:17:12,844] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 11384) +[2025-10-24 09:18:12,852] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 09:18:12,852] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 09:18:12,852] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 11384) +[2025-10-24 09:19:12,861] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 09:19:12,861] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 09:19:12,861] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 11384) +[2025-10-24 09:20:12,875] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 09:20:12,875] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 09:20:12,875] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 11384) +[2025-10-24 09:21:12,885] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 09:21:12,885] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 09:21:12,885] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 11384) +[2025-10-24 09:22:12,897] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 09:22:12,897] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 09:22:12,897] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 11384) +[2025-10-24 09:23:12,899] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 09:23:12,899] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 09:23:12,899] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 11384) +[2025-10-24 09:24:12,903] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 09:24:12,903] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 09:24:12,903] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 11384) +[2025-10-24 09:25:12,907] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 09:25:12,907] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 09:25:12,907] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 11384) +[2025-10-24 09:26:12,916] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 09:26:12,916] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 09:26:12,916] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 11384) +[2025-10-24 09:27:12,924] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 09:27:12,924] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 09:27:12,924] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 11384) +[2025-10-24 09:28:12,928] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 09:28:12,928] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 09:28:12,928] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 11384) +[2025-10-24 09:29:12,939] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 09:29:12,939] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 09:29:12,939] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 11384) +[2025-10-24 09:30:12,952] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 09:30:12,952] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 09:30:12,952] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 11384) +[2025-10-24 09:31:12,966] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 09:31:12,966] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 09:31:12,966] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 11384) +[2025-10-24 09:32:12,979] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 09:32:12,979] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 09:32:12,979] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 11384) +[2025-10-24 09:33:12,983] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 09:33:12,983] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 09:33:12,983] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 11384) +[2025-10-24 09:34:12,995] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 09:34:12,995] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 09:34:12,995] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 11384) +[2025-10-24 09:35:13,000] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 09:35:13,000] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 09:35:13,000] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 11384) +[2025-10-24 09:36:13,001] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 09:36:13,001] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 09:36:13,001] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 11384) +[2025-10-24 09:37:13,009] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 09:37:13,009] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 09:37:13,009] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 11384) +[2025-10-24 09:37:46,237] [MainThread] [INFO] [loggerModule.py:info:281] API 서버 종료 +[2025-10-24 09:37:46,410] [MainThread] [INFO] [loggerModule.py:info:281] 워커 프로세스 종료 +[2025-10-24 09:37:47,753] [MainThread] [INFO] [loggerModule.py:info:281] API 서버 시작 +[2025-10-24 09:37:47,759] [MainThread] [INFO] [loggerModule.py:info:281] 워커 프로세스 기동: PID=18028 +[2025-10-24 09:37:48,192] [LogListener] [DEBUG] [loggerModule.py:debug:275] ImageWorker 프로세스 기동 (PID=18028, Name=ImageWorkerProcess) +[2025-10-24 09:37:48,192] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔧 ImageProcessor3 초기화 시작... +[2025-10-24 09:37:48,192] [LogListener] [DEBUG] [loggerModule.py:debug:275] tracemalloc 메모리 추적 시작 +[2025-10-24 09:37:48,199] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ DirectML 환경 준비 완료 (Windows DirectX 12 기반) +[2025-10-24 09:37:48,199] [LogListener] [DEBUG] [loggerModule.py:debug:275] === 🚀 DirectML GPU 상태 초기화 시작 🚀 === +[2025-10-24 09:37:48,199] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🎯 사용자 GPU 가속 요청: False +[2025-10-24 09:37:48,199] [LogListener] [DEBUG] [loggerModule.py:debug:275] 💻 현재 운영체제: Windows +[2025-10-24 09:37:48,199] [LogListener] [DEBUG] [loggerModule.py:debug:275] GPU 가속이 비활성화됨 (toggle_states['use_cuda'] = False) +[2025-10-24 09:37:48,199] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔒 안전한 CPU 모드로 모든 GPU 설정 강제 비활성화 +[2025-10-24 09:37:48,199] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔧 ImageProcessor3 GPU 상태 요약: +[2025-10-24 09:37:48,199] [LogListener] [DEBUG] [loggerModule.py:debug:275] - CUDA 사용 가능: False +[2025-10-24 09:37:48,199] [LogListener] [DEBUG] [loggerModule.py:debug:275] - toggle_states['use_cuda']: NOT_SET +[2025-10-24 09:37:48,199] [LogListener] [DEBUG] [loggerModule.py:debug:275] - GPU 하드웨어 정보: {} +[2025-10-24 09:37:48,200] [LogListener] [DEBUG] [loggerModule.py:debug:275] ImageProcessor3 Init toggle_states: {'TEMP_IMAGE_DIR': 'C:\\ProgramData\\ImgWorker\\work', 'output_image_format': 'webp', 'use_local_rembg': True, 'local_rembg_model_path': 'D:\\py\\img_worker\\modules\\rembg_models', 'local_model_name': 'birefnet-general-lite', 'optionIMGTrans_type': 'CPU', 'detail_IMGTrans_type': 'CPU', 'thumb_trans_type': 'CPU', 'migan_use_accel': True, 'migan_onnx_path': 'D:\\py\\img_worker\\modules\\migan_onnx\\migan_pipeline_v2.onnx', 'ocr_provider_override': 'auto', 'migan_provider_override': 'auto'} +[2025-10-24 09:37:48,200] [LogListener] [DEBUG] [loggerModule.py:debug:275] is_member_valid: False +[2025-10-24 09:37:48,200] [LogListener] [DEBUG] [loggerModule.py:debug:275] debug_images 디렉토리 이미 존재: D:\py\img_worker\modules\debug_images +[2025-10-24 09:37:48,200] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.font_path: D:\py\img_worker\modules\fonts\HakgyoansimDunggeunmisoTTFB.ttf +[2025-10-24 09:37:48,200] [LogListener] [DEBUG] [loggerModule.py:debug:275] toggle_states font_path: D:\py\img_worker\modules\fonts\HakgyoansimDunggeunmisoTTFB.ttf +[2025-10-24 09:37:48,200] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.TEMP_IMAGE_DIR: C:\ProgramData\ImgWorker\work +[2025-10-24 09:37:48,200] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.debugging_save_Dir: D:\py\img_worker\modules\debug_images +[2025-10-24 09:37:48,200] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.unwanted_texts: {} +[2025-10-24 09:37:48,201] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.inpaint_method: migan +[2025-10-24 09:37:48,201] [LogListener] [DEBUG] [loggerModule.py:debug:275] Image.MAX_IMAGE_PIXELS set to 20000000 +[2025-10-24 09:37:48,201] [LogListener] [DEBUG] [loggerModule.py:debug:275] ONNX 모델 타입 설정값: 자동 선택, GPU 정보: {} +[2025-10-24 09:37:48,201] [LogListener] [DEBUG] [loggerModule.py:debug:275] 자동 선택 모드: GPU 추천 모델 simp 사용 +[2025-10-24 09:37:48,201] [LogListener] [DEBUG] [loggerModule.py:debug:275] ONNX OCR 모델 타입 결정: simp (GPU: False) +[2025-10-24 09:37:48,201] [LogListener] [DEBUG] [loggerModule.py:debug:275] ONNX OCR 모듈 CPU 모드로 설정 +[2025-10-24 09:37:48,201] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🚀 ONNX TextSystem 초기화 시작 (CPU 모드) +[2025-10-24 09:37:48,201] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🚀 ONNX TextSystem 초기화 시작 (CPU 모드) +[2025-10-24 09:37:49,791] [LogListener] [DEBUG] [loggerModule.py:debug:275] 문자 사전 파일 UTF-8 확인 완료: D:\py\img_worker\modules\onnx_ocr_module\dict\ppocr_keys_v1.txt +[2025-10-24 09:37:49,890] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ ONNX TextSystem 초기화 완료 (CPU + SIMP (호환성) 모델) +[2025-10-24 09:37:49,890] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ ONNX TextSystem 초기화 완료 (CPU + SIMP 모델) +[2025-10-24 09:37:49,890] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ ONNX OCR 모듈 초기화 성공 (CPU 모드) +[2025-10-24 09:37:49,891] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ ONNX OCR 모듈 초기화 성공 +[2025-10-24 09:37:49,891] [LogListener] [DEBUG] [loggerModule.py:debug:275] 마스크 모듈 초기화 완료 +[2025-10-24 09:37:49,891] [LogListener] [DEBUG] [loggerModule.py:debug:275] MaskModule 초기화 성공 +[2025-10-24 09:37:49,891] [LogListener] [DEBUG] [loggerModule.py:debug:275] 텍스트 렌더링 모듈 초기화 완료 +[2025-10-24 09:37:49,891] [LogListener] [DEBUG] [loggerModule.py:debug:275] 기본 폰트: D:\py\img_worker\modules\fonts\HakgyoansimDunggeunmisoTTFB.ttf +[2025-10-24 09:37:49,891] [LogListener] [DEBUG] [loggerModule.py:debug:275] TextRenderingModule 초기화 성공 +[2025-10-24 09:37:49,891] [LogListener] [DEBUG] [loggerModule.py:debug:275] 폰트 로드 성공: D:\py\img_worker\modules\fonts\HakgyoansimDunggeunmisoTTFB.ttf +[2025-10-24 09:37:49,891] [LogListener] [DEBUG] [loggerModule.py:debug:275] PostImageManager 초기화 성공 +[2025-10-24 09:37:49,892] [LogListener] [DEBUG] [loggerModule.py:debug:275] Request_AI_Server GPU 상태: CUDA 사용 가능=False +[2025-10-24 09:37:49,892] [LogListener] [DEBUG] [loggerModule.py:debug:275] Request_AI_Server 초기화 성공 +[2025-10-24 09:37:49,892] [LogListener] [DEBUG] [loggerModule.py:debug:275] GoogleTranslate 초기화 성공 +[2025-10-24 09:37:49,892] [LogListener] [DEBUG] [loggerModule.py:debug:275] MIGAN CUDA 사용 불가 - CPU 모드로 설정 +[2025-10-24 09:37:49,892] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] GPU 관리자 전달: GPUManager, can_use_cuda: False +[2025-10-24 09:37:49,913] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] GPU 관리자 사용 가능: False +[2025-10-24 09:37:50,143] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 사용 가능한 providers: ['DmlExecutionProvider', 'CPUExecutionProvider'] +[2025-10-24 09:37:50,144] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] DirectML 가속 활성화 +[2025-10-24 09:37:50,144] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 최종 providers: [('DmlExecutionProvider', {}), ('CPUExecutionProvider', {})] +[2025-10-24 09:37:50,144] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] DirectML 가속 시도: [('DmlExecutionProvider', {}), ('CPUExecutionProvider', {})] +[2025-10-24 09:37:50,144] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] DirectML 가속 성공! 실제 providers: ['DmlExecutionProvider', 'CPUExecutionProvider'] +[2025-10-24 09:37:50,145] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 입력 0: image, 형태: ['batch_size', 3, 'height', 'width'], 타입: tensor(uint8) +[2025-10-24 09:37:50,145] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 입력 1: mask, 형태: ['batch_size', 1, 'height', 'width'], 타입: tensor(uint8) +[2025-10-24 09:37:50,145] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 출력 0: result, 형태: ['ScatterNDresult_dim_0', 3, 'ScatterNDresult_dim_2', 'ScatterNDresult_dim_3'], 타입: tensor(uint8) +[2025-10-24 09:37:50,145] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 세션 준비 완료. providers=['DmlExecutionProvider', 'CPUExecutionProvider'] +[2025-10-24 09:37:50,145] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] GPU 관리자 연결 완료: GPUManager +[2025-10-24 09:37:50,145] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 파이프라인 gpu_manager 속성: True, 값: +[2025-10-24 09:37:50,145] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 초기화 완료: gpu_manager 속성=True, 값= +[2025-10-24 09:37:50,145] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔰 OCR 모듈 Warm-up 시작... +[2025-10-24 09:37:50,146] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔍 ONNX OCR 감지 방식: polygon +[2025-10-24 09:37:50,150] [LogListener] [DEBUG] [loggerModule.py:debug:275] ⚡ ONNX OCR 추론 완료: 5.0ms +[2025-10-24 09:37:50,150] [LogListener] [DEBUG] [loggerModule.py:debug:275] 📊 세부 시간 - 감지: 4.0ms, 인식: 0.0ms, 분류: 0.0ms +[2025-10-24 09:37:50,151] [LogListener] [WARNING] [loggerModule.py:warning:287] ⚠️ ONNX OCR 결과가 비어있습니다 +[2025-10-24 09:37:50,155] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ OCR 모듈 Warm-up 성공 +[2025-10-24 09:37:50,155] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔰 ImageProcessor Warm‑up 완료 +[2025-10-24 09:37:50,155] [ResultListener] [INFO] [loggerModule.py:info:281] 워커 READY 수신 +[2025-10-24 09:37:50,156] [ResultListener] [INFO] [loggerModule.py:info:281] 워커 READY 수신 +[2025-10-24 09:37:50,156] [LogListener] [DEBUG] [loggerModule.py:debug:275] 📡 추가 READY 신호 전송 완료 +[2025-10-24 09:37:50,156] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 09:37:50,156] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 18028) +[2025-10-24 09:37:50,157] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔥 작업 수신 성공 +[2025-10-24 09:37:50,157] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🚀 작업 처리 시작: cmd=__PING__, uid=b18bc3a5-ac67-4468-9352-772056495338 +[2025-10-24 09:37:50,157] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 실행 직전: cmd=__PING__ +[2025-10-24 09:37:50,157] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 결과 반환 중: uid=b18bc3a5-ac67-4468-9352-772056495338 +[2025-10-24 09:37:50,157] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 결과 반환 완료: uid=b18bc3a5-ac67-4468-9352-772056495338 +[2025-10-24 09:37:50,157] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 09:37:50,157] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 18028) +[2025-10-24 09:38:50,173] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 09:38:50,173] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 09:38:50,173] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 18028) +[2025-10-24 09:39:50,173] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 09:39:50,173] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 09:39:50,173] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 18028) +[2025-10-24 09:39:53,836] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔥 작업 수신 성공 +[2025-10-24 09:39:53,836] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🚀 작업 처리 시작: cmd=process_single_image, uid=8ea800bc-173a-42f4-9e44-706e70996eaf +[2025-10-24 09:39:53,836] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 프로세서 toggle_states 업데이트 : {'TEMP_IMAGE_DIR': 'C:\\ProgramData\\ImgWorker\\work', 'output_image_format': 'webp', 'use_local_rembg': True, 'local_rembg_model_path': 'D:\\py\\img_worker\\modules\\rembg_models', 'local_model_name': 'birefnet-general-lite', 'optionIMGTrans_type': 'GPU', 'detail_IMGTrans_type': 'GPU', 'thumb_trans_type': 'GPU', 'migan_use_accel': True, 'migan_onnx_path': 'D:\\py\\img_worker\\modules\\migan_onnx\\migan_pipeline_v2.onnx', 'ocr_provider_override': 'auto', 'migan_provider_override': 'auto', 'font_type': '폰트5', 'unwanted_texts': ['가격설명', '무료배송', '보증', '사은품', '세일', '이벤트', '특가', '품절', '할인', '할인가'], 'is_member_valid': False, 'authenticated_by_admin': True} +[2025-10-24 09:39:53,836] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 실행 직전: cmd=process_single_image +[2025-10-24 09:39:53,836] [LogListener] [DEBUG] [loggerModule.py:debug:275] process_single_image 호출 직전 +[2025-10-24 09:39:53,838] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 번역시작 +[2025-10-24 09:39:54,257] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 1 처리 시작: C:\ProgramData\ImgWorker\incoming\dl_f724b55c694d.jpg - 전체 번역 모드 +[2025-10-24 09:39:54,263] [LogListener] [DEBUG] [loggerModule.py:debug:275] 로컬 파일 경로 감지, 다운로드 생략: C:\ProgramData\ImgWorker\incoming\dl_f724b55c694d.jpg +[2025-10-24 09:39:54,263] [LogListener] [DEBUG] [loggerModule.py:debug:275] 옵션 이미지는 스케일 처리 건너뛰기: option +[2025-10-24 09:39:54,263] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 1 로컬 저장위치(옵션 이미지 원본 유지): C:\ProgramData\ImgWorker\incoming\dl_f724b55c694d.jpg +[2025-10-24 09:39:54,265] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔍 ONNX OCR 감지 방식: polygon +[2025-10-24 09:39:54,897] [LogListener] [DEBUG] [loggerModule.py:debug:275] ⚡ ONNX OCR 추론 완료: 629.0ms +[2025-10-24 09:39:54,915] [LogListener] [DEBUG] [loggerModule.py:debug:275] 📊 세부 시간 - 감지: 118.0ms, 인식: 484.0ms, 분류: 21.0ms +[2025-10-24 09:39:54,949] [LogListener] [DEBUG] [loggerModule.py:debug:275] 메모리 변화 [OCR 처리]: 41327.8MB -> 41608.3MB (+280.5MB, +0.7%) - 이미지 1 +[2025-10-24 09:39:54,949] [LogListener] [DEBUG] [loggerModule.py:debug:275] ocr_results: [{'text': '暴走免', 'confidence': 0.8268830180168152, 'polygon': [[137.0, 21.0], [270.0, 23.0], [269.0, 63.0], [136.0, 61.0]], 'bbox': (136, 21, 135, 43), 'method': 'polygon'}, {'text': 'BAOZOUTU', 'confidence': 0.9928860068321228, 'polygon': [[134.0, 73.0], [273.0, 73.0], [273.0, 87.0], [134.0, 87.0]], 'bbox': (134, 73, 140, 15), 'method': 'polygon'}, {'text': '全波段收音机', 'confidence': 0.9983271956443787, 'polygon': [[82.0, 158.0], [288.0, 158.0], [288.0, 185.0], [82.0, 185.0]], 'bbox': (82, 158, 207, 28), 'method': 'polygon'}, {'text': 'FMAMBTTFUSB', 'confidence': 0.9194031357765198, 'polygon': [[520.0, 159.0], [646.0, 151.0], [647.0, 164.0], [521.0, 172.0]], 'bbox': (520, 151, 128, 22), 'method': 'polygon'}, {'text': 'SWWB', 'confidence': 0.9913822412490845, 'polygon': [[524.0, 184.0], [554.0, 184.0], [554.0, 195.0], [524.0, 195.0]], 'bbox': (524, 184, 31, 12), 'method': 'polygon'}, {'text': '蓝牙音响', 'confidence': 0.9982503652572632, 'polygon': [[78.0, 213.0], [294.0, 213.0], [294.0, 248.0], [78.0, 248.0]], 'bbox': (78, 213, 217, 36), 'method': 'polygon'}, {'text': 'TUNE', 'confidence': 0.9709067344665527, 'polygon': [[649.0, 216.0], [669.0, 213.0], [670.0, 221.0], [650.0, 224.0]], 'bbox': (649, 213, 22, 12), 'method': 'polygon'}, {'text': '47', 'confidence': 0.7534564733505249, 'polygon': [[642.0, 226.0], [658.0, 226.0], [658.0, 234.0], [642.0, 234.0]], 'bbox': (642, 226, 17, 9), 'method': 'polygon'}, {'text': '·R11·RADIO/BT/MUSICPLAYER', 'confidence': 0.9210416674613953, 'polygon': [[531.0, 260.0], [670.0, 247.0], [671.0, 261.0], [532.0, 274.0]], 'bbox': (531, 247, 141, 28), 'method': 'polygon'}, {'text': '4000毫安锂电池', 'confidence': 0.9972505569458008, 'polygon': [[82.0, 278.0], [290.0, 278.0], [290.0, 305.0], [82.0, 305.0]], 'bbox': (82, 278, 209, 28), 'method': 'polygon'}, {'text': '手', 'confidence': 0.9999576807022095, 'polygon': [[80.0, 335.0], [113.0, 335.0], [113.0, 366.0], [80.0, 366.0]], 'bbox': (80, 335, 34, 32), 'method': 'polygon'}, {'text': '电', 'confidence': 0.9995966553688049, 'polygon': [[171.0, 338.0], [204.0, 338.0], [204.0, 364.0], [171.0, 364.0]], 'bbox': (171, 338, 34, 27), 'method': 'polygon'}, {'text': '筒', 'confidence': 0.9986861348152161, 'polygon': [[263.0, 337.0], [290.0, 337.0], [290.0, 365.0], [263.0, 365.0]], 'bbox': (263, 337, 28, 29), 'method': 'polygon'}, {'text': 'SOS警报/指南针', 'confidence': 0.9744207859039307, 'polygon': [[81.0, 398.0], [288.0, 398.0], [288.0, 425.0], [81.0, 425.0]], 'bbox': (81, 398, 208, 28), 'method': 'polygon'}, {'text': '手摇/太阳能发电', 'confidence': 0.982049822807312, 'polygon': [[81.0, 456.0], [289.0, 458.0], [289.0, 486.0], [81.0, 484.0]], 'bbox': (81, 456, 209, 31), 'method': 'polygon'}, {'text': '可插U盘/TF卡', 'confidence': 0.996004581451416, 'polygon': [[83.0, 519.0], [290.0, 519.0], [290.0, 543.0], [83.0, 543.0]], 'bbox': (83, 519, 208, 25), 'method': 'polygon'}, {'text': '限时', 'confidence': 0.9984755516052246, 'polygon': [[18.0, 659.0], [150.0, 661.0], [149.0, 729.0], [17.0, 727.0]], 'bbox': (17, 659, 134, 71), 'method': 'polygon'}, {'text': '赠送', 'confidence': 0.9996578693389893, 'polygon': [[15.0, 728.0], [149.0, 728.0], [149.0, 792.0], [15.0, 792.0]], 'bbox': (15, 728, 135, 65), 'method': 'polygon'}, {'text': '延长天线', 'confidence': 0.9981918334960938, 'polygon': [[211.0, 769.0], [288.0, 769.0], [288.0, 790.0], [211.0, 790.0]], 'bbox': (211, 769, 78, 22), 'method': 'polygon'}, {'text': '内存卡', 'confidence': 0.9986286163330078, 'polygon': [[324.0, 766.0], [383.0, 766.0], [383.0, 791.0], [324.0, 791.0]], 'bbox': (324, 766, 60, 26), 'method': 'polygon'}, {'text': '读卡器', 'confidence': 0.9983436465263367, 'polygon': [[423.0, 767.0], [483.0, 767.0], [483.0, 792.0], [423.0, 792.0]], 'bbox': (423, 767, 61, 26), 'method': 'polygon'}, {'text': '充电线', 'confidence': 0.9990179538726807, 'polygon': [[523.0, 767.0], [584.0, 767.0], [584.0, 792.0], [523.0, 792.0]], 'bbox': (523, 767, 62, 26), 'method': 'polygon'}, {'text': '礼盒礼袋', 'confidence': 0.997372031211853, 'polygon': [[617.0, 767.0], [696.0, 767.0], [696.0, 792.0], [617.0, 792.0]], 'bbox': (617, 767, 80, 26), 'method': 'polygon'}, {'text': '充电头', 'confidence': 0.9958818554878235, 'polygon': [[725.0, 767.0], [785.0, 767.0], [785.0, 792.0], [725.0, 792.0]], 'bbox': (725, 767, 61, 26), 'method': 'polygon'}] +[2025-10-24 09:39:54,950] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 82.7%): '暴走免' +[2025-10-24 09:39:54,950] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): 'BAOZOUTU' +[2025-10-24 09:39:54,950] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.8%): '全波段收音机' +[2025-10-24 09:39:54,950] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): 'FMAMBTTFUSB' +[2025-10-24 09:39:54,950] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): 'SWWB' +[2025-10-24 09:39:54,951] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.8%): '蓝牙音响' +[2025-10-24 09:39:54,951] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): 'TUNE' +[2025-10-24 09:39:54,951] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): '47' +[2025-10-24 09:39:54,951] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): '·R11·RADIO/BT/MUSICPLAYER' +[2025-10-24 09:39:54,951] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.7%): '4000毫安锂电池' +[2025-10-24 09:39:54,951] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 100.0%): '手' +[2025-10-24 09:39:54,951] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 100.0%): '电' +[2025-10-24 09:39:54,951] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.9%): '筒' +[2025-10-24 09:39:54,951] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 97.4%): 'SOS警报/指南针' +[2025-10-24 09:39:54,951] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 98.2%): '手摇/太阳能发电' +[2025-10-24 09:39:54,973] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.6%): '可插U盘/TF卡' +[2025-10-24 09:39:54,988] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.8%): '限时' +[2025-10-24 09:39:54,988] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 100.0%): '赠送' +[2025-10-24 09:39:54,988] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.8%): '延长天线' +[2025-10-24 09:39:54,988] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.9%): '内存卡' +[2025-10-24 09:39:54,988] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.8%): '读卡器' +[2025-10-24 09:39:54,989] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.9%): '充电线' +[2025-10-24 09:39:54,989] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.7%): '礼盒礼袋' +[2025-10-24 09:39:54,989] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.6%): '充电头' +[2025-10-24 09:39:54,989] [LogListener] [DEBUG] [loggerModule.py:debug:275] 필터링 결과: 18/24개 (신뢰도 + & 중국어) +[2025-10-24 09:39:54,989] [LogListener] [DEBUG] [loggerModule.py:debug:275] filter_ocr_results: [{'text': '暴走免', 'confidence': 0.8268830180168152, 'polygon': [[137.0, 21.0], [270.0, 23.0], [269.0, 63.0], [136.0, 61.0]], 'bbox': (136, 21, 135, 43), 'method': 'polygon'}, {'text': '全波段收音机', 'confidence': 0.9983271956443787, 'polygon': [[82.0, 158.0], [288.0, 158.0], [288.0, 185.0], [82.0, 185.0]], 'bbox': (82, 158, 207, 28), 'method': 'polygon'}, {'text': '蓝牙音响', 'confidence': 0.9982503652572632, 'polygon': [[78.0, 213.0], [294.0, 213.0], [294.0, 248.0], [78.0, 248.0]], 'bbox': (78, 213, 217, 36), 'method': 'polygon'}, {'text': '4000毫安锂电池', 'confidence': 0.9972505569458008, 'polygon': [[82.0, 278.0], [290.0, 278.0], [290.0, 305.0], [82.0, 305.0]], 'bbox': (82, 278, 209, 28), 'method': 'polygon'}, {'text': '手', 'confidence': 0.9999576807022095, 'polygon': [[80.0, 335.0], [113.0, 335.0], [113.0, 366.0], [80.0, 366.0]], 'bbox': (80, 335, 34, 32), 'method': 'polygon'}, {'text': '电', 'confidence': 0.9995966553688049, 'polygon': [[171.0, 338.0], [204.0, 338.0], [204.0, 364.0], [171.0, 364.0]], 'bbox': (171, 338, 34, 27), 'method': 'polygon'}, {'text': '筒', 'confidence': 0.9986861348152161, 'polygon': [[263.0, 337.0], [290.0, 337.0], [290.0, 365.0], [263.0, 365.0]], 'bbox': (263, 337, 28, 29), 'method': 'polygon'}, {'text': 'SOS警报/指南针', 'confidence': 0.9744207859039307, 'polygon': [[81.0, 398.0], [288.0, 398.0], [288.0, 425.0], [81.0, 425.0]], 'bbox': (81, 398, 208, 28), 'method': 'polygon'}, {'text': '手摇/太阳能发电', 'confidence': 0.982049822807312, 'polygon': [[81.0, 456.0], [289.0, 458.0], [289.0, 486.0], [81.0, 484.0]], 'bbox': (81, 456, 209, 31), 'method': 'polygon'}, {'text': '可插U盘/TF卡', 'confidence': 0.996004581451416, 'polygon': [[83.0, 519.0], [290.0, 519.0], [290.0, 543.0], [83.0, 543.0]], 'bbox': (83, 519, 208, 25), 'method': 'polygon'}, {'text': '限时', 'confidence': 0.9984755516052246, 'polygon': [[18.0, 659.0], [150.0, 661.0], [149.0, 729.0], [17.0, 727.0]], 'bbox': (17, 659, 134, 71), 'method': 'polygon'}, {'text': '赠送', 'confidence': 0.9996578693389893, 'polygon': [[15.0, 728.0], [149.0, 728.0], [149.0, 792.0], [15.0, 792.0]], 'bbox': (15, 728, 135, 65), 'method': 'polygon'}, {'text': '延长天线', 'confidence': 0.9981918334960938, 'polygon': [[211.0, 769.0], [288.0, 769.0], [288.0, 790.0], [211.0, 790.0]], 'bbox': (211, 769, 78, 22), 'method': 'polygon'}, {'text': '内存卡', 'confidence': 0.9986286163330078, 'polygon': [[324.0, 766.0], [383.0, 766.0], [383.0, 791.0], [324.0, 791.0]], 'bbox': (324, 766, 60, 26), 'method': 'polygon'}, {'text': '读卡器', 'confidence': 0.9983436465263367, 'polygon': [[423.0, 767.0], [483.0, 767.0], [483.0, 792.0], [423.0, 792.0]], 'bbox': (423, 767, 61, 26), 'method': 'polygon'}, {'text': '充电线', 'confidence': 0.9990179538726807, 'polygon': [[523.0, 767.0], [584.0, 767.0], [584.0, 792.0], [523.0, 792.0]], 'bbox': (523, 767, 62, 26), 'method': 'polygon'}, {'text': '礼盒礼袋', 'confidence': 0.997372031211853, 'polygon': [[617.0, 767.0], [696.0, 767.0], [696.0, 792.0], [617.0, 792.0]], 'bbox': (617, 767, 80, 26), 'method': 'polygon'}, {'text': '充电头', 'confidence': 0.9958818554878235, 'polygon': [[725.0, 767.0], [785.0, 767.0], [785.0, 792.0], [725.0, 792.0]], 'bbox': (725, 767, 61, 26), 'method': 'polygon'}] +[2025-10-24 09:39:54,990] [LogListener] [DEBUG] [loggerModule.py:debug:275] 중국어 텍스트 18개 필터링 완료 +[2025-10-24 09:39:54,990] [LogListener] [DEBUG] [loggerModule.py:debug:275] 한글 텍스트 0개 필터링 완료 +[2025-10-24 09:39:58,191] [LogListener] [DEBUG] [loggerModule.py:debug:275] translated_texts: ['무료', '풀 밴드 라디오', '블루투스 스피커', '4000mAh 리튬 배터리', '손', '전기', '실린더', 'SOS 경보 / 나침반', '핸드 크랭크 / 태양광 발전', 'U 디스크 삽입 가능 / TF 카드', '제한된 시간', '포기하다', '확장 안테나', '메모리 카드', '카드 리더', '충전 케이블', '선물 상자 선물 가방', '충전 헤드'] +[2025-10-24 09:39:58,191] [LogListener] [DEBUG] [loggerModule.py:debug:275] 마스크 생성 완료 +[2025-10-24 09:39:58,191] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 1 OCR 권한 없음, 전체 번역 모드 +[2025-10-24 09:39:58,199] [LogListener] [DEBUG] [loggerModule.py:debug:275] [AUTO Inpaint] coverage=0.202, comps=17, min_center_dist=0.052 → request +[2025-10-24 09:39:58,200] [LogListener] [DEBUG] [loggerModule.py:debug:275] ocr_count: 18 +[2025-10-24 09:39:58,200] [LogListener] [DEBUG] [loggerModule.py:debug:275] is_member_valid: False +[2025-10-24 09:39:58,200] [LogListener] [DEBUG] [loggerModule.py:debug:275] MIGAN 인페인팅 시도 +[2025-10-24 09:39:58,202] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 입력 형태 - 이미지: (1, 3, 800, 800), 마스크: (1, 1, 800, 800) +[2025-10-24 09:39:58,636] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 출력 형태: (800, 800, 3), dtype: uint8 +[2025-10-24 09:39:58,636] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 추론 완료: 433.37 ms +[2025-10-24 09:39:58,644] [LogListener] [DEBUG] [loggerModule.py:debug:275] MIGAN 인페인팅 성공 +[2025-10-24 09:39:58,644] [LogListener] [DEBUG] [loggerModule.py:debug:275] 메모리 변화 [인페인팅]: 41690.4MB -> 41872.3MB (+181.9MB, +0.4%) - 방법: migan +[2025-10-24 09:39:58,644] [LogListener] [DEBUG] [loggerModule.py:debug:275] 인페인팅 완료 +[2025-10-24 09:39:58,713] [LogListener] [DEBUG] [loggerModule.py:debug:275] 텍스트 렌더링 완료 +[2025-10-24 09:39:58,714] [LogListener] [DEBUG] [loggerModule.py:debug:275] watermark_text: 이미지 저작권 보유 +[2025-10-24 09:39:58,714] [LogListener] [DEBUG] [loggerModule.py:debug:275] is_watermark_enabled: True +[2025-10-24 09:39:59,004] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 저장 완료 : C:\ProgramData\ImgWorker\work\translated_option_img_1.webp +[2025-10-24 09:39:59,005] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 1 번역 완료: C:\ProgramData\ImgWorker\work\translated_option_img_1.webp +[2025-10-24 09:39:59,005] [LogListener] [DEBUG] [loggerModule.py:debug:275] ⏱ 이미지 파이프라인 총 5167.5ms | download=0.0ms | ocr=640.0ms | translate=3241.0ms | mask=3241.0ms | inpaint=450.4ms(migan/DirectML) | render=69.0ms | save=257.0ms +[2025-10-24 09:39:59,006] [LogListener] [DEBUG] [loggerModule.py:debug:275] process_single_image 호출 완료 +[2025-10-24 09:39:59,007] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 결과 반환 중: uid=8ea800bc-173a-42f4-9e44-706e70996eaf +[2025-10-24 09:39:59,007] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 결과 반환 완료: uid=8ea800bc-173a-42f4-9e44-706e70996eaf +[2025-10-24 09:39:59,007] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 09:39:59,007] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 18028) +[2025-10-24 09:40:00,822] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔥 작업 수신 성공 +[2025-10-24 09:40:00,822] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🚀 작업 처리 시작: cmd=process_single_image, uid=15fc8408-5bd8-47ef-8fa9-5912b605ce2d +[2025-10-24 09:40:00,822] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 프로세서 toggle_states 업데이트 : {'TEMP_IMAGE_DIR': 'C:\\ProgramData\\ImgWorker\\work', 'output_image_format': 'webp', 'use_local_rembg': True, 'local_rembg_model_path': 'D:\\py\\img_worker\\modules\\rembg_models', 'local_model_name': 'birefnet-general-lite', 'optionIMGTrans_type': 'GPU', 'detail_IMGTrans_type': 'GPU', 'thumb_trans_type': 'GPU', 'migan_use_accel': True, 'migan_onnx_path': 'D:\\py\\img_worker\\modules\\migan_onnx\\migan_pipeline_v2.onnx', 'ocr_provider_override': 'auto', 'migan_provider_override': 'auto', 'font_type': '폰트5', 'unwanted_texts': ['가격설명', '무료배송', '보증', '사은품', '세일', '이벤트', '특가', '품절', '할인', '할인가'], 'is_member_valid': False, 'authenticated_by_admin': True} +[2025-10-24 09:40:00,823] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 실행 직전: cmd=process_single_image +[2025-10-24 09:40:00,823] [LogListener] [DEBUG] [loggerModule.py:debug:275] process_single_image 호출 직전 +[2025-10-24 09:40:00,823] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 번역시작 +[2025-10-24 09:40:01,318] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 2 처리 시작: C:\ProgramData\ImgWorker\incoming\dl_c967904fe153.jpg - 전체 번역 모드 +[2025-10-24 09:40:01,323] [LogListener] [DEBUG] [loggerModule.py:debug:275] 로컬 파일 경로 감지, 다운로드 생략: C:\ProgramData\ImgWorker\incoming\dl_c967904fe153.jpg +[2025-10-24 09:40:01,323] [LogListener] [DEBUG] [loggerModule.py:debug:275] 옵션 이미지는 스케일 처리 건너뛰기: option +[2025-10-24 09:40:01,323] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 2 로컬 저장위치(옵션 이미지 원본 유지): C:\ProgramData\ImgWorker\incoming\dl_c967904fe153.jpg +[2025-10-24 09:40:01,325] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔍 ONNX OCR 감지 방식: polygon +[2025-10-24 09:40:01,767] [LogListener] [DEBUG] [loggerModule.py:debug:275] ⚡ ONNX OCR 추론 완료: 439.9ms +[2025-10-24 09:40:01,791] [LogListener] [DEBUG] [loggerModule.py:debug:275] 📊 세부 시간 - 감지: 97.6ms, 인식: 321.4ms, 분류: 14.0ms +[2025-10-24 09:40:01,792] [LogListener] [DEBUG] [loggerModule.py:debug:275] 메모리 변화 [OCR 처리]: 41997.8MB -> 42024.1MB (+26.3MB, +0.1%) - 이미지 2 +[2025-10-24 09:40:01,792] [LogListener] [DEBUG] [loggerModule.py:debug:275] ocr_results: [{'text': '暴走兔', 'confidence': 0.9217319488525391, 'polygon': [[136.0, 21.0], [270.0, 23.0], [269.0, 63.0], [135.0, 61.0]], 'bbox': (135, 21, 136, 43), 'method': 'polygon'}, {'text': 'BAOZOUTU', 'confidence': 0.9930073022842407, 'polygon': [[134.0, 73.0], [273.0, 73.0], [273.0, 87.0], [134.0, 87.0]], 'bbox': (134, 73, 140, 15), 'method': 'polygon'}, {'text': '全波段收音机', 'confidence': 0.9983155131340027, 'polygon': [[82.0, 158.0], [288.0, 158.0], [288.0, 185.0], [82.0, 185.0]], 'bbox': (82, 158, 207, 28), 'method': 'polygon'}, {'text': '蓝牙音响', 'confidence': 0.9986387491226196, 'polygon': [[78.0, 213.0], [294.0, 213.0], [294.0, 248.0], [78.0, 248.0]], 'bbox': (78, 213, 217, 36), 'method': 'polygon'}, {'text': '·R11·RADIO/BT/MUSICPLAYER', 'confidence': 0.9367609620094299, 'polygon': [[531.0, 257.0], [672.0, 245.0], [673.0, 259.0], [532.0, 271.0]], 'bbox': (531, 245, 143, 27), 'method': 'polygon'}, {'text': '4000毫安锂电池', 'confidence': 0.9972565770149231, 'polygon': [[82.0, 278.0], [290.0, 278.0], [290.0, 305.0], [82.0, 305.0]], 'bbox': (82, 278, 209, 28), 'method': 'polygon'}, {'text': '手', 'confidence': 0.9999576807022095, 'polygon': [[80.0, 335.0], [113.0, 335.0], [113.0, 366.0], [80.0, 366.0]], 'bbox': (80, 335, 34, 32), 'method': 'polygon'}, {'text': '电', 'confidence': 0.9995966553688049, 'polygon': [[171.0, 338.0], [204.0, 338.0], [204.0, 364.0], [171.0, 364.0]], 'bbox': (171, 338, 34, 27), 'method': 'polygon'}, {'text': '筒', 'confidence': 0.997241735458374, 'polygon': [[261.0, 337.0], [290.0, 337.0], [290.0, 365.0], [261.0, 365.0]], 'bbox': (261, 337, 30, 29), 'method': 'polygon'}, {'text': 'SOS警报/指南针', 'confidence': 0.973926305770874, 'polygon': [[81.0, 398.0], [288.0, 398.0], [288.0, 425.0], [81.0, 425.0]], 'bbox': (81, 398, 208, 28), 'method': 'polygon'}, {'text': '手摇/太阳能发电', 'confidence': 0.9819984436035156, 'polygon': [[81.0, 456.0], [289.0, 458.0], [289.0, 486.0], [81.0, 484.0]], 'bbox': (81, 456, 209, 31), 'method': 'polygon'}, {'text': '可插U盘/TF卡', 'confidence': 0.995929479598999, 'polygon': [[83.0, 519.0], [290.0, 519.0], [290.0, 543.0], [83.0, 543.0]], 'bbox': (83, 519, 208, 25), 'method': 'polygon'}, {'text': '限时', 'confidence': 0.9984755516052246, 'polygon': [[18.0, 659.0], [150.0, 661.0], [149.0, 729.0], [17.0, 727.0]], 'bbox': (17, 659, 134, 71), 'method': 'polygon'}, {'text': '赠送', 'confidence': 0.9996578693389893, 'polygon': [[15.0, 728.0], [149.0, 728.0], [149.0, 792.0], [15.0, 792.0]], 'bbox': (15, 728, 135, 65), 'method': 'polygon'}, {'text': '延长天线', 'confidence': 0.9986213445663452, 'polygon': [[212.0, 769.0], [288.0, 769.0], [288.0, 790.0], [212.0, 790.0]], 'bbox': (212, 769, 77, 22), 'method': 'polygon'}, {'text': '内存卡', 'confidence': 0.9986286163330078, 'polygon': [[324.0, 766.0], [383.0, 766.0], [383.0, 791.0], [324.0, 791.0]], 'bbox': (324, 766, 60, 26), 'method': 'polygon'}, {'text': '读卡器', 'confidence': 0.9984023571014404, 'polygon': [[424.0, 767.0], [483.0, 767.0], [483.0, 792.0], [424.0, 792.0]], 'bbox': (424, 767, 60, 26), 'method': 'polygon'}, {'text': '充电线', 'confidence': 0.9990179538726807, 'polygon': [[523.0, 767.0], [584.0, 767.0], [584.0, 792.0], [523.0, 792.0]], 'bbox': (523, 767, 62, 26), 'method': 'polygon'}, {'text': '礼盒礼袋', 'confidence': 0.9980711340904236, 'polygon': [[618.0, 766.0], [698.0, 768.0], [697.0, 793.0], [617.0, 791.0]], 'bbox': (617, 766, 82, 28), 'method': 'polygon'}, {'text': '充电头', 'confidence': 0.9979567527770996, 'polygon': [[727.0, 767.0], [786.0, 767.0], [786.0, 792.0], [727.0, 792.0]], 'bbox': (727, 767, 60, 26), 'method': 'polygon'}] +[2025-10-24 09:40:01,793] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 92.2%): '暴走兔' +[2025-10-24 09:40:01,793] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): 'BAOZOUTU' +[2025-10-24 09:40:01,793] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.8%): '全波段收音机' +[2025-10-24 09:40:01,793] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.9%): '蓝牙音响' +[2025-10-24 09:40:01,793] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): '·R11·RADIO/BT/MUSICPLAYER' +[2025-10-24 09:40:01,793] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.7%): '4000毫安锂电池' +[2025-10-24 09:40:01,793] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 100.0%): '手' +[2025-10-24 09:40:01,793] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 100.0%): '电' +[2025-10-24 09:40:01,793] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.7%): '筒' +[2025-10-24 09:40:01,793] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 97.4%): 'SOS警报/指南针' +[2025-10-24 09:40:01,793] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 98.2%): '手摇/太阳能发电' +[2025-10-24 09:40:01,793] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.6%): '可插U盘/TF卡' +[2025-10-24 09:40:01,793] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.8%): '限时' +[2025-10-24 09:40:01,794] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 100.0%): '赠送' +[2025-10-24 09:40:01,794] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.9%): '延长天线' +[2025-10-24 09:40:01,794] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.9%): '内存卡' +[2025-10-24 09:40:01,794] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.8%): '读卡器' +[2025-10-24 09:40:01,794] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.9%): '充电线' +[2025-10-24 09:40:01,794] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.8%): '礼盒礼袋' +[2025-10-24 09:40:01,794] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.8%): '充电头' +[2025-10-24 09:40:01,795] [LogListener] [DEBUG] [loggerModule.py:debug:275] 필터링 결과: 18/20개 (신뢰도 + & 중국어) +[2025-10-24 09:40:01,795] [LogListener] [DEBUG] [loggerModule.py:debug:275] filter_ocr_results: [{'text': '暴走兔', 'confidence': 0.9217319488525391, 'polygon': [[136.0, 21.0], [270.0, 23.0], [269.0, 63.0], [135.0, 61.0]], 'bbox': (135, 21, 136, 43), 'method': 'polygon'}, {'text': '全波段收音机', 'confidence': 0.9983155131340027, 'polygon': [[82.0, 158.0], [288.0, 158.0], [288.0, 185.0], [82.0, 185.0]], 'bbox': (82, 158, 207, 28), 'method': 'polygon'}, {'text': '蓝牙音响', 'confidence': 0.9986387491226196, 'polygon': [[78.0, 213.0], [294.0, 213.0], [294.0, 248.0], [78.0, 248.0]], 'bbox': (78, 213, 217, 36), 'method': 'polygon'}, {'text': '4000毫安锂电池', 'confidence': 0.9972565770149231, 'polygon': [[82.0, 278.0], [290.0, 278.0], [290.0, 305.0], [82.0, 305.0]], 'bbox': (82, 278, 209, 28), 'method': 'polygon'}, {'text': '手', 'confidence': 0.9999576807022095, 'polygon': [[80.0, 335.0], [113.0, 335.0], [113.0, 366.0], [80.0, 366.0]], 'bbox': (80, 335, 34, 32), 'method': 'polygon'}, {'text': '电', 'confidence': 0.9995966553688049, 'polygon': [[171.0, 338.0], [204.0, 338.0], [204.0, 364.0], [171.0, 364.0]], 'bbox': (171, 338, 34, 27), 'method': 'polygon'}, {'text': '筒', 'confidence': 0.997241735458374, 'polygon': [[261.0, 337.0], [290.0, 337.0], [290.0, 365.0], [261.0, 365.0]], 'bbox': (261, 337, 30, 29), 'method': 'polygon'}, {'text': 'SOS警报/指南针', 'confidence': 0.973926305770874, 'polygon': [[81.0, 398.0], [288.0, 398.0], [288.0, 425.0], [81.0, 425.0]], 'bbox': (81, 398, 208, 28), 'method': 'polygon'}, {'text': '手摇/太阳能发电', 'confidence': 0.9819984436035156, 'polygon': [[81.0, 456.0], [289.0, 458.0], [289.0, 486.0], [81.0, 484.0]], 'bbox': (81, 456, 209, 31), 'method': 'polygon'}, {'text': '可插U盘/TF卡', 'confidence': 0.995929479598999, 'polygon': [[83.0, 519.0], [290.0, 519.0], [290.0, 543.0], [83.0, 543.0]], 'bbox': (83, 519, 208, 25), 'method': 'polygon'}, {'text': '限时', 'confidence': 0.9984755516052246, 'polygon': [[18.0, 659.0], [150.0, 661.0], [149.0, 729.0], [17.0, 727.0]], 'bbox': (17, 659, 134, 71), 'method': 'polygon'}, {'text': '赠送', 'confidence': 0.9996578693389893, 'polygon': [[15.0, 728.0], [149.0, 728.0], [149.0, 792.0], [15.0, 792.0]], 'bbox': (15, 728, 135, 65), 'method': 'polygon'}, {'text': '延长天线', 'confidence': 0.9986213445663452, 'polygon': [[212.0, 769.0], [288.0, 769.0], [288.0, 790.0], [212.0, 790.0]], 'bbox': (212, 769, 77, 22), 'method': 'polygon'}, {'text': '内存卡', 'confidence': 0.9986286163330078, 'polygon': [[324.0, 766.0], [383.0, 766.0], [383.0, 791.0], [324.0, 791.0]], 'bbox': (324, 766, 60, 26), 'method': 'polygon'}, {'text': '读卡器', 'confidence': 0.9984023571014404, 'polygon': [[424.0, 767.0], [483.0, 767.0], [483.0, 792.0], [424.0, 792.0]], 'bbox': (424, 767, 60, 26), 'method': 'polygon'}, {'text': '充电线', 'confidence': 0.9990179538726807, 'polygon': [[523.0, 767.0], [584.0, 767.0], [584.0, 792.0], [523.0, 792.0]], 'bbox': (523, 767, 62, 26), 'method': 'polygon'}, {'text': '礼盒礼袋', 'confidence': 0.9980711340904236, 'polygon': [[618.0, 766.0], [698.0, 768.0], [697.0, 793.0], [617.0, 791.0]], 'bbox': (617, 766, 82, 28), 'method': 'polygon'}, {'text': '充电头', 'confidence': 0.9979567527770996, 'polygon': [[727.0, 767.0], [786.0, 767.0], [786.0, 792.0], [727.0, 792.0]], 'bbox': (727, 767, 60, 26), 'method': 'polygon'}] +[2025-10-24 09:40:01,795] [LogListener] [DEBUG] [loggerModule.py:debug:275] 중국어 텍스트 18개 필터링 완료 +[2025-10-24 09:40:01,795] [LogListener] [DEBUG] [loggerModule.py:debug:275] 한글 텍스트 0개 필터링 완료 +[2025-10-24 09:40:02,283] [LogListener] [DEBUG] [loggerModule.py:debug:275] translated_texts: ['도망친 토끼', '풀 밴드 라디오', '블루투스 스피커', '4000mAh 리튬 배터리', '손', '전기', '실린더', 'SOS 경보 / 나침반', '핸드 크랭크 / 태양광 발전', 'U 디스크 삽입 가능 / TF 카드', '제한된 시간', '포기하다', '확장 안테나', '메모리 카드', '카드 리더', '충전 케이블', '선물 상자 선물 가방', '충전 헤드'] +[2025-10-24 09:40:02,283] [LogListener] [DEBUG] [loggerModule.py:debug:275] 마스크 생성 완료 +[2025-10-24 09:40:02,283] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 2 OCR 권한 없음, 전체 번역 모드 +[2025-10-24 09:40:02,291] [LogListener] [DEBUG] [loggerModule.py:debug:275] [AUTO Inpaint] coverage=0.202, comps=17, min_center_dist=0.052 → request +[2025-10-24 09:40:02,291] [LogListener] [DEBUG] [loggerModule.py:debug:275] ocr_count: 18 +[2025-10-24 09:40:02,291] [LogListener] [DEBUG] [loggerModule.py:debug:275] is_member_valid: False +[2025-10-24 09:40:02,291] [LogListener] [DEBUG] [loggerModule.py:debug:275] MIGAN 인페인팅 시도 +[2025-10-24 09:40:02,293] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 입력 형태 - 이미지: (1, 3, 800, 800), 마스크: (1, 1, 800, 800) +[2025-10-24 09:40:02,502] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 출력 형태: (800, 800, 3), dtype: uint8 +[2025-10-24 09:40:02,503] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 추론 완료: 209.56 ms +[2025-10-24 09:40:02,510] [LogListener] [DEBUG] [loggerModule.py:debug:275] MIGAN 인페인팅 성공 +[2025-10-24 09:40:02,510] [LogListener] [DEBUG] [loggerModule.py:debug:275] 메모리 변화 [인페인팅]: 42007.9MB -> 42013.6MB (+5.6MB, +0.0%) - 방법: migan +[2025-10-24 09:40:02,510] [LogListener] [DEBUG] [loggerModule.py:debug:275] 인페인팅 완료 +[2025-10-24 09:40:02,565] [LogListener] [DEBUG] [loggerModule.py:debug:275] 텍스트 렌더링 완료 +[2025-10-24 09:40:02,565] [LogListener] [DEBUG] [loggerModule.py:debug:275] watermark_text: 이미지 저작권 보유 +[2025-10-24 09:40:02,565] [LogListener] [DEBUG] [loggerModule.py:debug:275] is_watermark_enabled: True +[2025-10-24 09:40:02,852] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 저장 완료 : C:\ProgramData\ImgWorker\work\translated_option_img_2.webp +[2025-10-24 09:40:02,853] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 2 번역 완료: C:\ProgramData\ImgWorker\work\translated_option_img_2.webp +[2025-10-24 09:40:02,853] [LogListener] [DEBUG] [loggerModule.py:debug:275] ⏱ 이미지 파이프라인 총 2029.1ms | download=0.0ms | ocr=448.9ms | translate=490.5ms | mask=490.5ms | inpaint=225.6ms(migan/DirectML) | render=54.0ms | save=254.5ms +[2025-10-24 09:40:02,854] [LogListener] [DEBUG] [loggerModule.py:debug:275] process_single_image 호출 완료 +[2025-10-24 09:40:02,854] [ResultListener] [WARNING] [loggerModule.py:warning:287] 워커 롤링 스케줄: reason=job-count-threshold +[2025-10-24 09:40:02,854] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 결과 반환 중: uid=15fc8408-5bd8-47ef-8fa9-5912b605ce2d +[2025-10-24 09:40:02,855] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 결과 반환 완료: uid=15fc8408-5bd8-47ef-8fa9-5912b605ce2d +[2025-10-24 09:40:02,855] [WorkerRoller] [WARNING] [loggerModule.py:warning:287] 워커 롤링 시작: job-count-threshold +[2025-10-24 09:40:02,855] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 09:40:02,855] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 18028) +[2025-10-24 09:40:02,903] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔥 작업 수신 성공 +[2025-10-24 09:40:02,904] [LogListener] [DEBUG] [loggerModule.py:debug:275] Shutdown signal 수신 → 종료 +[2025-10-24 09:40:02,904] [LogListener] [DEBUG] [loggerModule.py:debug:275] OCR 모듈 정리 완료 +[2025-10-24 09:40:02,904] [LogListener] [DEBUG] [loggerModule.py:debug:275] 마스크 모듈 정리 완료 +[2025-10-24 09:40:02,904] [LogListener] [DEBUG] [loggerModule.py:debug:275] 임시 폴더 삭제됨: C:\ProgramData\ImgWorker\work +[2025-10-24 09:40:02,904] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 프로세서 소멸 +[2025-10-24 09:40:03,358] [WorkerRoller] [INFO] [loggerModule.py:info:281] 워커 프로세스 기동: PID=31492 +[2025-10-24 09:40:03,866] [LogListener] [DEBUG] [loggerModule.py:debug:275] ImageWorker 프로세스 기동 (PID=31492, Name=ImageWorkerProcess) +[2025-10-24 09:40:03,866] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔧 ImageProcessor3 초기화 시작... +[2025-10-24 09:40:03,866] [LogListener] [DEBUG] [loggerModule.py:debug:275] tracemalloc 메모리 추적 시작 +[2025-10-24 09:40:03,873] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ DirectML 환경 준비 완료 (Windows DirectX 12 기반) +[2025-10-24 09:40:03,873] [LogListener] [DEBUG] [loggerModule.py:debug:275] === 🚀 DirectML GPU 상태 초기화 시작 🚀 === +[2025-10-24 09:40:03,873] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🎯 사용자 GPU 가속 요청: False +[2025-10-24 09:40:03,873] [LogListener] [DEBUG] [loggerModule.py:debug:275] 💻 현재 운영체제: Windows +[2025-10-24 09:40:03,874] [LogListener] [DEBUG] [loggerModule.py:debug:275] GPU 가속이 비활성화됨 (toggle_states['use_cuda'] = False) +[2025-10-24 09:40:03,874] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔒 안전한 CPU 모드로 모든 GPU 설정 강제 비활성화 +[2025-10-24 09:40:03,874] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔧 ImageProcessor3 GPU 상태 요약: +[2025-10-24 09:40:03,874] [LogListener] [DEBUG] [loggerModule.py:debug:275] - CUDA 사용 가능: False +[2025-10-24 09:40:03,874] [LogListener] [DEBUG] [loggerModule.py:debug:275] - toggle_states['use_cuda']: NOT_SET +[2025-10-24 09:40:03,874] [LogListener] [DEBUG] [loggerModule.py:debug:275] - GPU 하드웨어 정보: {} +[2025-10-24 09:40:03,874] [LogListener] [DEBUG] [loggerModule.py:debug:275] ImageProcessor3 Init toggle_states: {'TEMP_IMAGE_DIR': 'C:\\ProgramData\\ImgWorker\\work', 'output_image_format': 'webp', 'use_local_rembg': True, 'local_rembg_model_path': 'D:\\py\\img_worker\\modules\\rembg_models', 'local_model_name': 'birefnet-general-lite', 'optionIMGTrans_type': 'CPU', 'detail_IMGTrans_type': 'CPU', 'thumb_trans_type': 'CPU', 'migan_use_accel': True, 'migan_onnx_path': 'D:\\py\\img_worker\\modules\\migan_onnx\\migan_pipeline_v2.onnx', 'ocr_provider_override': 'auto', 'migan_provider_override': 'auto'} +[2025-10-24 09:40:03,875] [LogListener] [DEBUG] [loggerModule.py:debug:275] is_member_valid: False +[2025-10-24 09:40:03,875] [LogListener] [DEBUG] [loggerModule.py:debug:275] debug_images 디렉토리 이미 존재: D:\py\img_worker\modules\debug_images +[2025-10-24 09:40:03,875] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.font_path: D:\py\img_worker\modules\fonts\HakgyoansimDunggeunmisoTTFB.ttf +[2025-10-24 09:40:03,875] [LogListener] [DEBUG] [loggerModule.py:debug:275] toggle_states font_path: D:\py\img_worker\modules\fonts\HakgyoansimDunggeunmisoTTFB.ttf +[2025-10-24 09:40:03,875] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.TEMP_IMAGE_DIR: C:\ProgramData\ImgWorker\work +[2025-10-24 09:40:03,875] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.debugging_save_Dir: D:\py\img_worker\modules\debug_images +[2025-10-24 09:40:03,875] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.unwanted_texts: {} +[2025-10-24 09:40:03,875] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.inpaint_method: migan +[2025-10-24 09:40:03,875] [LogListener] [DEBUG] [loggerModule.py:debug:275] Image.MAX_IMAGE_PIXELS set to 20000000 +[2025-10-24 09:40:03,875] [LogListener] [DEBUG] [loggerModule.py:debug:275] ONNX 모델 타입 설정값: 자동 선택, GPU 정보: {} +[2025-10-24 09:40:03,875] [LogListener] [DEBUG] [loggerModule.py:debug:275] 자동 선택 모드: GPU 추천 모델 simp 사용 +[2025-10-24 09:40:03,875] [LogListener] [DEBUG] [loggerModule.py:debug:275] ONNX OCR 모델 타입 결정: simp (GPU: False) +[2025-10-24 09:40:03,875] [LogListener] [DEBUG] [loggerModule.py:debug:275] ONNX OCR 모듈 CPU 모드로 설정 +[2025-10-24 09:40:03,876] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🚀 ONNX TextSystem 초기화 시작 (CPU 모드) +[2025-10-24 09:40:03,876] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🚀 ONNX TextSystem 초기화 시작 (CPU 모드) +[2025-10-24 09:40:05,432] [LogListener] [DEBUG] [loggerModule.py:debug:275] 문자 사전 파일 UTF-8 확인 완료: D:\py\img_worker\modules\onnx_ocr_module\dict\ppocr_keys_v1.txt +[2025-10-24 09:40:05,517] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ ONNX TextSystem 초기화 완료 (CPU + SIMP (호환성) 모델) +[2025-10-24 09:40:05,517] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ ONNX TextSystem 초기화 완료 (CPU + SIMP 모델) +[2025-10-24 09:40:05,517] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ ONNX OCR 모듈 초기화 성공 (CPU 모드) +[2025-10-24 09:40:05,518] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ ONNX OCR 모듈 초기화 성공 +[2025-10-24 09:40:05,518] [LogListener] [DEBUG] [loggerModule.py:debug:275] 마스크 모듈 초기화 완료 +[2025-10-24 09:40:05,518] [LogListener] [DEBUG] [loggerModule.py:debug:275] MaskModule 초기화 성공 +[2025-10-24 09:40:05,518] [LogListener] [DEBUG] [loggerModule.py:debug:275] 텍스트 렌더링 모듈 초기화 완료 +[2025-10-24 09:40:05,518] [LogListener] [DEBUG] [loggerModule.py:debug:275] 기본 폰트: D:\py\img_worker\modules\fonts\HakgyoansimDunggeunmisoTTFB.ttf +[2025-10-24 09:40:05,518] [LogListener] [DEBUG] [loggerModule.py:debug:275] TextRenderingModule 초기화 성공 +[2025-10-24 09:40:05,518] [LogListener] [DEBUG] [loggerModule.py:debug:275] 폰트 로드 성공: D:\py\img_worker\modules\fonts\HakgyoansimDunggeunmisoTTFB.ttf +[2025-10-24 09:40:05,518] [LogListener] [DEBUG] [loggerModule.py:debug:275] PostImageManager 초기화 성공 +[2025-10-24 09:40:05,518] [LogListener] [DEBUG] [loggerModule.py:debug:275] Request_AI_Server GPU 상태: CUDA 사용 가능=False +[2025-10-24 09:40:05,519] [LogListener] [DEBUG] [loggerModule.py:debug:275] Request_AI_Server 초기화 성공 +[2025-10-24 09:40:05,519] [LogListener] [DEBUG] [loggerModule.py:debug:275] GoogleTranslate 초기화 성공 +[2025-10-24 09:40:05,519] [LogListener] [DEBUG] [loggerModule.py:debug:275] MIGAN CUDA 사용 불가 - CPU 모드로 설정 +[2025-10-24 09:40:05,519] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] GPU 관리자 전달: GPUManager, can_use_cuda: False +[2025-10-24 09:40:05,538] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] GPU 관리자 사용 가능: False +[2025-10-24 09:40:05,748] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 사용 가능한 providers: ['DmlExecutionProvider', 'CPUExecutionProvider'] +[2025-10-24 09:40:05,749] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] DirectML 가속 활성화 +[2025-10-24 09:40:05,749] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 최종 providers: [('DmlExecutionProvider', {}), ('CPUExecutionProvider', {})] +[2025-10-24 09:40:05,749] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] DirectML 가속 시도: [('DmlExecutionProvider', {}), ('CPUExecutionProvider', {})] +[2025-10-24 09:40:05,749] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] DirectML 가속 성공! 실제 providers: ['DmlExecutionProvider', 'CPUExecutionProvider'] +[2025-10-24 09:40:05,749] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 입력 0: image, 형태: ['batch_size', 3, 'height', 'width'], 타입: tensor(uint8) +[2025-10-24 09:40:05,749] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 입력 1: mask, 형태: ['batch_size', 1, 'height', 'width'], 타입: tensor(uint8) +[2025-10-24 09:40:05,749] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 출력 0: result, 형태: ['ScatterNDresult_dim_0', 3, 'ScatterNDresult_dim_2', 'ScatterNDresult_dim_3'], 타입: tensor(uint8) +[2025-10-24 09:40:05,749] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 세션 준비 완료. providers=['DmlExecutionProvider', 'CPUExecutionProvider'] +[2025-10-24 09:40:05,749] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] GPU 관리자 연결 완료: GPUManager +[2025-10-24 09:40:05,749] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 파이프라인 gpu_manager 속성: True, 값: +[2025-10-24 09:40:05,749] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 초기화 완료: gpu_manager 속성=True, 값= +[2025-10-24 09:40:05,750] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔰 OCR 모듈 Warm-up 시작... +[2025-10-24 09:40:05,750] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔍 ONNX OCR 감지 방식: polygon +[2025-10-24 09:40:05,754] [LogListener] [DEBUG] [loggerModule.py:debug:275] ⚡ ONNX OCR 추론 완료: 4.0ms +[2025-10-24 09:40:05,754] [LogListener] [DEBUG] [loggerModule.py:debug:275] 📊 세부 시간 - 감지: 3.0ms, 인식: 0.0ms, 분류: 0.0ms +[2025-10-24 09:40:05,755] [LogListener] [WARNING] [loggerModule.py:warning:287] ⚠️ ONNX OCR 결과가 비어있습니다 +[2025-10-24 09:40:05,758] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ OCR 모듈 Warm-up 성공 +[2025-10-24 09:40:05,758] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔰 ImageProcessor Warm‑up 완료 +[2025-10-24 09:40:05,758] [ResultListener] [INFO] [loggerModule.py:info:281] 워커 READY 수신 +[2025-10-24 09:40:05,758] [WorkerRoller] [INFO] [loggerModule.py:info:281] 버퍼 작업 재개: 1건 +[2025-10-24 09:40:05,758] [ResultListener] [INFO] [loggerModule.py:info:281] 워커 READY 수신 +[2025-10-24 09:40:05,758] [LogListener] [DEBUG] [loggerModule.py:debug:275] 📡 추가 READY 신호 전송 완료 +[2025-10-24 09:40:05,759] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 09:40:05,759] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 31492) +[2025-10-24 09:40:05,759] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔥 작업 수신 성공 +[2025-10-24 09:40:05,759] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🚀 작업 처리 시작: cmd=process_single_image, uid=e32dce2f-165f-4da3-b388-842e84edaf4b +[2025-10-24 09:40:05,759] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 프로세서 toggle_states 업데이트 : {'TEMP_IMAGE_DIR': 'C:\\ProgramData\\ImgWorker\\work', 'output_image_format': 'webp', 'use_local_rembg': True, 'local_rembg_model_path': 'D:\\py\\img_worker\\modules\\rembg_models', 'local_model_name': 'birefnet-general-lite', 'optionIMGTrans_type': 'GPU', 'detail_IMGTrans_type': 'GPU', 'thumb_trans_type': 'GPU', 'migan_use_accel': True, 'migan_onnx_path': 'D:\\py\\img_worker\\modules\\migan_onnx\\migan_pipeline_v2.onnx', 'ocr_provider_override': 'auto', 'migan_provider_override': 'auto', 'font_type': '폰트5', 'unwanted_texts': ['가격설명', '무료배송', '보증', '사은품', '세일', '이벤트', '특가', '품절', '할인', '할인가'], 'is_member_valid': False, 'authenticated_by_admin': True} +[2025-10-24 09:40:05,759] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 실행 직전: cmd=process_single_image +[2025-10-24 09:40:05,760] [LogListener] [DEBUG] [loggerModule.py:debug:275] process_single_image 호출 직전 +[2025-10-24 09:40:05,762] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 번역시작 +[2025-10-24 09:40:06,272] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 3 처리 시작: C:\ProgramData\ImgWorker\incoming\dl_2724c99a01ea.jpg - 전체 번역 모드 +[2025-10-24 09:40:06,277] [LogListener] [DEBUG] [loggerModule.py:debug:275] 로컬 파일 경로 감지, 다운로드 생략: C:\ProgramData\ImgWorker\incoming\dl_2724c99a01ea.jpg +[2025-10-24 09:40:06,277] [LogListener] [DEBUG] [loggerModule.py:debug:275] 옵션 이미지는 스케일 처리 건너뛰기: option +[2025-10-24 09:40:06,278] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 3 로컬 저장위치(옵션 이미지 원본 유지): C:\ProgramData\ImgWorker\incoming\dl_2724c99a01ea.jpg +[2025-10-24 09:40:06,279] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔍 ONNX OCR 감지 방식: polygon +[2025-10-24 09:40:07,133] [LogListener] [DEBUG] [loggerModule.py:debug:275] ⚡ ONNX OCR 추론 완료: 851.5ms +[2025-10-24 09:40:07,133] [LogListener] [DEBUG] [loggerModule.py:debug:275] 📊 세부 시간 - 감지: 111.6ms, 인식: 711.9ms, 분류: 23.0ms +[2025-10-24 09:40:07,159] [LogListener] [DEBUG] [loggerModule.py:debug:275] 메모리 변화 [OCR 처리]: 41650.2MB -> 41959.8MB (+309.5MB, +0.7%) - 이미지 3 +[2025-10-24 09:40:07,175] [LogListener] [DEBUG] [loggerModule.py:debug:275] ocr_results: [{'text': '暴走兔', 'confidence': 0.8949251174926758, 'polygon': [[135.0, 20.0], [271.0, 22.0], [270.0, 64.0], [134.0, 62.0]], 'bbox': (134, 20, 138, 45), 'method': 'polygon'}, {'text': 'BAOZOUTU', 'confidence': 0.9929968118667603, 'polygon': [[134.0, 73.0], [273.0, 73.0], [273.0, 87.0], [134.0, 87.0]], 'bbox': (134, 73, 140, 15), 'method': 'polygon'}, {'text': 'M', 'confidence': 0.9715226292610168, 'polygon': [[453.0, 146.0], [463.0, 146.0], [463.0, 154.0], [453.0, 154.0]], 'bbox': (453, 146, 11, 9), 'method': 'polygon'}, {'text': 'AM', 'confidence': 0.9390634298324585, 'polygon': [[471.0, 146.0], [481.0, 146.0], [481.0, 154.0], [471.0, 154.0]], 'bbox': (471, 146, 11, 9), 'method': 'polygon'}, {'text': '全波段收音机', 'confidence': 0.9977607727050781, 'polygon': [[81.0, 158.0], [288.0, 156.0], [288.0, 184.0], [81.0, 186.0]], 'bbox': (81, 156, 208, 31), 'method': 'polygon'}, {'text': '1500', 'confidence': 0.8749780058860779, 'polygon': [[469.0, 192.0], [482.0, 192.0], [482.0, 209.0], [469.0, 209.0]], 'bbox': (469, 192, 14, 18), 'method': 'polygon'}, {'text': '蓝牙音', 'confidence': 0.9980747699737549, 'polygon': [[79.0, 213.0], [271.0, 213.0], [271.0, 248.0], [79.0, 248.0]], 'bbox': (79, 213, 193, 36), 'method': 'polygon'}, {'text': '响', 'confidence': 0.9996689558029175, 'polygon': [[259.0, 219.0], [290.0, 219.0], [290.0, 244.0], [259.0, 244.0]], 'bbox': (259, 219, 32, 26), 'method': 'polygon'}, {'text': '1400', 'confidence': 0.8144437074661255, 'polygon': [[470.0, 212.0], [483.0, 212.0], [483.0, 226.0], [470.0, 226.0]], 'bbox': (470, 212, 14, 15), 'method': 'polygon'}, {'text': 'R11', 'confidence': 0.9946472644805908, 'polygon': [[457.0, 245.0], [490.0, 245.0], [490.0, 256.0], [457.0, 256.0]], 'bbox': (457, 245, 34, 12), 'method': 'polygon'}, {'text': 'RADIO/BT/MUSIC PLAYER', 'confidence': 0.9756901860237122, 'polygon': [[505.0, 245.0], [634.0, 245.0], [634.0, 255.0], [505.0, 255.0]], 'bbox': (505, 245, 130, 11), 'method': 'polygon'}, {'text': '4000毫安锂电池', 'confidence': 0.9978529810905457, 'polygon': [[82.0, 279.0], [289.0, 279.0], [289.0, 303.0], [82.0, 303.0]], 'bbox': (82, 279, 208, 25), 'method': 'polygon'}, {'text': '手', 'confidence': 0.9999611377716064, 'polygon': [[80.0, 335.0], [113.0, 335.0], [113.0, 365.0], [80.0, 365.0]], 'bbox': (80, 335, 34, 31), 'method': 'polygon'}, {'text': '电', 'confidence': 0.9988621473312378, 'polygon': [[168.0, 336.0], [204.0, 336.0], [204.0, 365.0], [168.0, 365.0]], 'bbox': (168, 336, 37, 30), 'method': 'polygon'}, {'text': '筒', 'confidence': 0.998289167881012, 'polygon': [[263.0, 336.0], [290.0, 336.0], [290.0, 365.0], [263.0, 365.0]], 'bbox': (263, 336, 28, 30), 'method': 'polygon'}, {'text': 'SOS警报/指南针', 'confidence': 0.9653626680374146, 'polygon': [[81.0, 398.0], [288.0, 398.0], [288.0, 425.0], [81.0, 425.0]], 'bbox': (81, 398, 208, 28), 'method': 'polygon'}, {'text': '手摇/太阳能发电', 'confidence': 0.9902360439300537, 'polygon': [[83.0, 457.0], [289.0, 457.0], [289.0, 485.0], [83.0, 485.0]], 'bbox': (83, 457, 207, 29), 'method': 'polygon'}, {'text': '可插U盘/TF卡', 'confidence': 0.9801925420761108, 'polygon': [[82.0, 518.0], [290.0, 518.0], [290.0, 545.0], [82.0, 545.0]], 'bbox': (82, 518, 209, 28), 'method': 'polygon'}, {'text': '限时', 'confidence': 0.9982037544250488, 'polygon': [[18.0, 659.0], [151.0, 661.0], [150.0, 729.0], [17.0, 727.0]], 'bbox': (17, 659, 135, 71), 'method': 'polygon'}, {'text': '赠送', 'confidence': 0.9996599555015564, 'polygon': [[15.0, 728.0], [149.0, 728.0], [149.0, 792.0], [15.0, 792.0]], 'bbox': (15, 728, 135, 65), 'method': 'polygon'}, {'text': '延长天线', 'confidence': 0.9983137845993042, 'polygon': [[211.0, 769.0], [288.0, 769.0], [288.0, 790.0], [211.0, 790.0]], 'bbox': (211, 769, 78, 22), 'method': 'polygon'}, {'text': '内存卡', 'confidence': 0.9989700317382812, 'polygon': [[323.0, 766.0], [384.0, 766.0], [384.0, 791.0], [323.0, 791.0]], 'bbox': (323, 766, 62, 26), 'method': 'polygon'}, {'text': '读卡器', 'confidence': 0.998264729976654, 'polygon': [[423.0, 767.0], [483.0, 767.0], [483.0, 792.0], [423.0, 792.0]], 'bbox': (423, 767, 61, 26), 'method': 'polygon'}, {'text': '充电线', 'confidence': 0.997199535369873, 'polygon': [[522.0, 767.0], [583.0, 767.0], [583.0, 792.0], [522.0, 792.0]], 'bbox': (522, 767, 62, 26), 'method': 'polygon'}, {'text': '礼盒礼袋', 'confidence': 0.9987907409667969, 'polygon': [[618.0, 768.0], [696.0, 768.0], [696.0, 791.0], [618.0, 791.0]], 'bbox': (618, 768, 79, 24), 'method': 'polygon'}, {'text': '充电头', 'confidence': 0.997718870639801, 'polygon': [[725.0, 767.0], [785.0, 767.0], [785.0, 792.0], [725.0, 792.0]], 'bbox': (725, 767, 61, 26), 'method': 'polygon'}] +[2025-10-24 09:40:07,185] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 89.5%): '暴走兔' +[2025-10-24 09:40:07,185] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): 'BAOZOUTU' +[2025-10-24 09:40:07,185] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): 'M' +[2025-10-24 09:40:07,185] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): 'AM' +[2025-10-24 09:40:07,185] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.8%): '全波段收音机' +[2025-10-24 09:40:07,185] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): '1500' +[2025-10-24 09:40:07,186] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.8%): '蓝牙音' +[2025-10-24 09:40:07,186] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 100.0%): '响' +[2025-10-24 09:40:07,186] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): '1400' +[2025-10-24 09:40:07,186] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): 'R11' +[2025-10-24 09:40:07,186] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): 'RADIO/BT/MUSIC PLAYER' +[2025-10-24 09:40:07,186] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.8%): '4000毫安锂电池' +[2025-10-24 09:40:07,186] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 100.0%): '手' +[2025-10-24 09:40:07,186] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.9%): '电' +[2025-10-24 09:40:07,186] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.8%): '筒' +[2025-10-24 09:40:07,186] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 96.5%): 'SOS警报/指南针' +[2025-10-24 09:40:07,186] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.0%): '手摇/太阳能发电' +[2025-10-24 09:40:07,187] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 98.0%): '可插U盘/TF卡' +[2025-10-24 09:40:07,187] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.8%): '限时' +[2025-10-24 09:40:07,187] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 100.0%): '赠送' +[2025-10-24 09:40:07,187] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.8%): '延长天线' +[2025-10-24 09:40:07,187] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.9%): '内存卡' +[2025-10-24 09:40:07,187] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.8%): '读卡器' +[2025-10-24 09:40:07,187] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.7%): '充电线' +[2025-10-24 09:40:07,187] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.9%): '礼盒礼袋' +[2025-10-24 09:40:07,187] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.8%): '充电头' +[2025-10-24 09:40:07,187] [LogListener] [DEBUG] [loggerModule.py:debug:275] 필터링 결과: 19/26개 (신뢰도 + & 중국어) +[2025-10-24 09:40:07,187] [LogListener] [DEBUG] [loggerModule.py:debug:275] filter_ocr_results: [{'text': '暴走兔', 'confidence': 0.8949251174926758, 'polygon': [[135.0, 20.0], [271.0, 22.0], [270.0, 64.0], [134.0, 62.0]], 'bbox': (134, 20, 138, 45), 'method': 'polygon'}, {'text': '全波段收音机', 'confidence': 0.9977607727050781, 'polygon': [[81.0, 158.0], [288.0, 156.0], [288.0, 184.0], [81.0, 186.0]], 'bbox': (81, 156, 208, 31), 'method': 'polygon'}, {'text': '蓝牙音', 'confidence': 0.9980747699737549, 'polygon': [[79.0, 213.0], [271.0, 213.0], [271.0, 248.0], [79.0, 248.0]], 'bbox': (79, 213, 193, 36), 'method': 'polygon'}, {'text': '响', 'confidence': 0.9996689558029175, 'polygon': [[259.0, 219.0], [290.0, 219.0], [290.0, 244.0], [259.0, 244.0]], 'bbox': (259, 219, 32, 26), 'method': 'polygon'}, {'text': '4000毫安锂电池', 'confidence': 0.9978529810905457, 'polygon': [[82.0, 279.0], [289.0, 279.0], [289.0, 303.0], [82.0, 303.0]], 'bbox': (82, 279, 208, 25), 'method': 'polygon'}, {'text': '手', 'confidence': 0.9999611377716064, 'polygon': [[80.0, 335.0], [113.0, 335.0], [113.0, 365.0], [80.0, 365.0]], 'bbox': (80, 335, 34, 31), 'method': 'polygon'}, {'text': '电', 'confidence': 0.9988621473312378, 'polygon': [[168.0, 336.0], [204.0, 336.0], [204.0, 365.0], [168.0, 365.0]], 'bbox': (168, 336, 37, 30), 'method': 'polygon'}, {'text': '筒', 'confidence': 0.998289167881012, 'polygon': [[263.0, 336.0], [290.0, 336.0], [290.0, 365.0], [263.0, 365.0]], 'bbox': (263, 336, 28, 30), 'method': 'polygon'}, {'text': 'SOS警报/指南针', 'confidence': 0.9653626680374146, 'polygon': [[81.0, 398.0], [288.0, 398.0], [288.0, 425.0], [81.0, 425.0]], 'bbox': (81, 398, 208, 28), 'method': 'polygon'}, {'text': '手摇/太阳能发电', 'confidence': 0.9902360439300537, 'polygon': [[83.0, 457.0], [289.0, 457.0], [289.0, 485.0], [83.0, 485.0]], 'bbox': (83, 457, 207, 29), 'method': 'polygon'}, {'text': '可插U盘/TF卡', 'confidence': 0.9801925420761108, 'polygon': [[82.0, 518.0], [290.0, 518.0], [290.0, 545.0], [82.0, 545.0]], 'bbox': (82, 518, 209, 28), 'method': 'polygon'}, {'text': '限时', 'confidence': 0.9982037544250488, 'polygon': [[18.0, 659.0], [151.0, 661.0], [150.0, 729.0], [17.0, 727.0]], 'bbox': (17, 659, 135, 71), 'method': 'polygon'}, {'text': '赠送', 'confidence': 0.9996599555015564, 'polygon': [[15.0, 728.0], [149.0, 728.0], [149.0, 792.0], [15.0, 792.0]], 'bbox': (15, 728, 135, 65), 'method': 'polygon'}, {'text': '延长天线', 'confidence': 0.9983137845993042, 'polygon': [[211.0, 769.0], [288.0, 769.0], [288.0, 790.0], [211.0, 790.0]], 'bbox': (211, 769, 78, 22), 'method': 'polygon'}, {'text': '内存卡', 'confidence': 0.9989700317382812, 'polygon': [[323.0, 766.0], [384.0, 766.0], [384.0, 791.0], [323.0, 791.0]], 'bbox': (323, 766, 62, 26), 'method': 'polygon'}, {'text': '读卡器', 'confidence': 0.998264729976654, 'polygon': [[423.0, 767.0], [483.0, 767.0], [483.0, 792.0], [423.0, 792.0]], 'bbox': (423, 767, 61, 26), 'method': 'polygon'}, {'text': '充电线', 'confidence': 0.997199535369873, 'polygon': [[522.0, 767.0], [583.0, 767.0], [583.0, 792.0], [522.0, 792.0]], 'bbox': (522, 767, 62, 26), 'method': 'polygon'}, {'text': '礼盒礼袋', 'confidence': 0.9987907409667969, 'polygon': [[618.0, 768.0], [696.0, 768.0], [696.0, 791.0], [618.0, 791.0]], 'bbox': (618, 768, 79, 24), 'method': 'polygon'}, {'text': '充电头', 'confidence': 0.997718870639801, 'polygon': [[725.0, 767.0], [785.0, 767.0], [785.0, 792.0], [725.0, 792.0]], 'bbox': (725, 767, 61, 26), 'method': 'polygon'}] +[2025-10-24 09:40:07,191] [LogListener] [DEBUG] [loggerModule.py:debug:275] 중국어 텍스트 19개 필터링 완료 +[2025-10-24 09:40:07,207] [LogListener] [DEBUG] [loggerModule.py:debug:275] 한글 텍스트 0개 필터링 완료 +[2025-10-24 09:40:07,890] [LogListener] [DEBUG] [loggerModule.py:debug:275] translated_texts: ['도망친 토끼', '풀 밴드 라디오', '블루투스 사운드', '반지', '4000mAh 리튬 배터리', '손', '전기', '실린더', 'SOS 경보 / 나침반', '핸드 크랭크 / 태양광 발전', 'U 디스크 삽입 가능 / TF 카드', '제한된 시간', '포기하다', '확장 안테나', '메모리 카드', '카드 리더', '충전 케이블', '선물 상자 선물 가방', '충전 헤드'] +[2025-10-24 09:40:07,890] [LogListener] [DEBUG] [loggerModule.py:debug:275] 마스크 생성 완료 +[2025-10-24 09:40:07,890] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 3 OCR 권한 없음, 전체 번역 모드 +[2025-10-24 09:40:07,898] [LogListener] [DEBUG] [loggerModule.py:debug:275] [AUTO Inpaint] coverage=0.202, comps=17, min_center_dist=0.053 → request +[2025-10-24 09:40:07,898] [LogListener] [DEBUG] [loggerModule.py:debug:275] ocr_count: 19 +[2025-10-24 09:40:07,899] [LogListener] [DEBUG] [loggerModule.py:debug:275] is_member_valid: False +[2025-10-24 09:40:07,899] [LogListener] [DEBUG] [loggerModule.py:debug:275] MIGAN 인페인팅 시도 +[2025-10-24 09:40:07,901] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 입력 형태 - 이미지: (1, 3, 800, 800), 마스크: (1, 1, 800, 800) +[2025-10-24 09:40:08,248] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 출력 형태: (800, 800, 3), dtype: uint8 +[2025-10-24 09:40:08,248] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 추론 완료: 346.52 ms +[2025-10-24 09:40:08,255] [LogListener] [DEBUG] [loggerModule.py:debug:275] MIGAN 인페인팅 성공 +[2025-10-24 09:40:08,255] [LogListener] [DEBUG] [loggerModule.py:debug:275] 메모리 변화 [인페인팅]: 41958.2MB -> 42200.2MB (+242.0MB, +0.6%) - 방법: migan +[2025-10-24 09:40:08,255] [LogListener] [DEBUG] [loggerModule.py:debug:275] 인페인팅 완료 +[2025-10-24 09:40:08,325] [LogListener] [DEBUG] [loggerModule.py:debug:275] 텍스트 렌더링 완료 +[2025-10-24 09:40:08,326] [LogListener] [DEBUG] [loggerModule.py:debug:275] watermark_text: 이미지 저작권 보유 +[2025-10-24 09:40:08,326] [LogListener] [DEBUG] [loggerModule.py:debug:275] is_watermark_enabled: True +[2025-10-24 09:40:08,637] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 저장 완료 : C:\ProgramData\ImgWorker\work\translated_option_img_3.webp +[2025-10-24 09:40:08,638] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 3 번역 완료: C:\ProgramData\ImgWorker\work\translated_option_img_3.webp +[2025-10-24 09:40:08,638] [LogListener] [DEBUG] [loggerModule.py:debug:275] ⏱ 이미지 파이프라인 총 2876.4ms | download=0.0ms | ocr=862.5ms | translate=704.9ms | mask=704.9ms | inpaint=362.7ms(migan/DirectML) | render=69.0ms | save=277.6ms +[2025-10-24 09:40:08,639] [LogListener] [DEBUG] [loggerModule.py:debug:275] process_single_image 호출 완료 +[2025-10-24 09:40:08,639] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 결과 반환 중: uid=e32dce2f-165f-4da3-b388-842e84edaf4b +[2025-10-24 09:40:08,639] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 결과 반환 완료: uid=e32dce2f-165f-4da3-b388-842e84edaf4b +[2025-10-24 09:40:08,639] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 09:40:08,639] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 31492) +[2025-10-24 09:40:10,311] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔥 작업 수신 성공 +[2025-10-24 09:40:10,311] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🚀 작업 처리 시작: cmd=process_single_image, uid=b699469d-6874-4ce5-97a0-448b8cb2d7e0 +[2025-10-24 09:40:10,311] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 프로세서 toggle_states 업데이트 : {'TEMP_IMAGE_DIR': 'C:\\ProgramData\\ImgWorker\\work', 'output_image_format': 'webp', 'use_local_rembg': True, 'local_rembg_model_path': 'D:\\py\\img_worker\\modules\\rembg_models', 'local_model_name': 'birefnet-general-lite', 'optionIMGTrans_type': 'GPU', 'detail_IMGTrans_type': 'GPU', 'thumb_trans_type': 'GPU', 'migan_use_accel': True, 'migan_onnx_path': 'D:\\py\\img_worker\\modules\\migan_onnx\\migan_pipeline_v2.onnx', 'ocr_provider_override': 'auto', 'migan_provider_override': 'auto', 'font_type': '폰트5', 'unwanted_texts': ['가격설명', '무료배송', '보증', '사은품', '세일', '이벤트', '특가', '품절', '할인', '할인가'], 'is_member_valid': False, 'authenticated_by_admin': True} +[2025-10-24 09:40:10,311] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 실행 직전: cmd=process_single_image +[2025-10-24 09:40:10,312] [LogListener] [DEBUG] [loggerModule.py:debug:275] process_single_image 호출 직전 +[2025-10-24 09:40:10,312] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 번역시작 +[2025-10-24 09:40:10,558] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 4 처리 시작: C:\ProgramData\ImgWorker\incoming\dl_48429f53895e.jpg - 전체 번역 모드 +[2025-10-24 09:40:10,565] [LogListener] [DEBUG] [loggerModule.py:debug:275] 로컬 파일 경로 감지, 다운로드 생략: C:\ProgramData\ImgWorker\incoming\dl_48429f53895e.jpg +[2025-10-24 09:40:10,565] [LogListener] [DEBUG] [loggerModule.py:debug:275] 옵션 이미지는 스케일 처리 건너뛰기: option +[2025-10-24 09:40:10,565] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 4 로컬 저장위치(옵션 이미지 원본 유지): C:\ProgramData\ImgWorker\incoming\dl_48429f53895e.jpg +[2025-10-24 09:40:10,567] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔍 ONNX OCR 감지 방식: polygon +[2025-10-24 09:40:11,328] [LogListener] [DEBUG] [loggerModule.py:debug:275] ⚡ ONNX OCR 추론 완료: 758.3ms +[2025-10-24 09:40:11,350] [LogListener] [DEBUG] [loggerModule.py:debug:275] 📊 세부 시간 - 감지: 111.0ms, 인식: 619.4ms, 분류: 21.0ms +[2025-10-24 09:40:11,360] [LogListener] [DEBUG] [loggerModule.py:debug:275] 메모리 변화 [OCR 처리]: 42329.9MB -> 42241.3MB (-88.6MB, -0.2%) - 이미지 4 +[2025-10-24 09:40:11,361] [LogListener] [DEBUG] [loggerModule.py:debug:275] ocr_results: [{'text': '暴走兔', 'confidence': 0.8941178917884827, 'polygon': [[135.0, 20.0], [271.0, 22.0], [270.0, 64.0], [134.0, 62.0]], 'bbox': (134, 20, 138, 45), 'method': 'polygon'}, {'text': 'BAOZOUTU', 'confidence': 0.9931483268737793, 'polygon': [[134.0, 73.0], [273.0, 73.0], [273.0, 87.0], [134.0, 87.0]], 'bbox': (134, 73, 140, 15), 'method': 'polygon'}, {'text': 'AM', 'confidence': 0.9390634298324585, 'polygon': [[471.0, 146.0], [481.0, 146.0], [481.0, 154.0], [471.0, 154.0]], 'bbox': (471, 146, 11, 9), 'method': 'polygon'}, {'text': '全波段收音机', 'confidence': 0.9979420304298401, 'polygon': [[81.0, 158.0], [288.0, 156.0], [288.0, 184.0], [81.0, 186.0]], 'bbox': (81, 156, 208, 31), 'method': 'polygon'}, {'text': '104', 'confidence': 0.7842115759849548, 'polygon': [[449.0, 200.0], [460.0, 193.0], [467.0, 204.0], [456.0, 211.0]], 'bbox': (449, 193, 19, 19), 'method': 'polygon'}, {'text': '150', 'confidence': 0.981334924697876, 'polygon': [[469.0, 193.0], [482.0, 193.0], [482.0, 208.0], [469.0, 208.0]], 'bbox': (469, 193, 14, 16), 'method': 'polygon'}, {'text': '蓝牙音', 'confidence': 0.9979119300842285, 'polygon': [[79.0, 213.0], [271.0, 213.0], [271.0, 248.0], [79.0, 248.0]], 'bbox': (79, 213, 193, 36), 'method': 'polygon'}, {'text': '响', 'confidence': 0.9996689558029175, 'polygon': [[259.0, 219.0], [290.0, 219.0], [290.0, 244.0], [259.0, 244.0]], 'bbox': (259, 219, 32, 26), 'method': 'polygon'}, {'text': '1400', 'confidence': 0.7393448948860168, 'polygon': [[470.0, 212.0], [483.0, 212.0], [483.0, 225.0], [470.0, 225.0]], 'bbox': (470, 212, 14, 14), 'method': 'polygon'}, {'text': '·R11', 'confidence': 0.846956193447113, 'polygon': [[458.0, 245.0], [490.0, 245.0], [490.0, 256.0], [458.0, 256.0]], 'bbox': (458, 245, 33, 12), 'method': 'polygon'}, {'text': 'RADIO/BT/MUSIC', 'confidence': 0.9952330589294434, 'polygon': [[505.0, 246.0], [594.0, 246.0], [594.0, 256.0], [505.0, 256.0]], 'bbox': (505, 246, 90, 11), 'method': 'polygon'}, {'text': 'PLAYER', 'confidence': 0.9937713742256165, 'polygon': [[592.0, 243.0], [634.0, 245.0], [634.0, 256.0], [592.0, 254.0]], 'bbox': (592, 243, 43, 14), 'method': 'polygon'}, {'text': '4000毫安锂电池', 'confidence': 0.9979369640350342, 'polygon': [[82.0, 279.0], [289.0, 279.0], [289.0, 303.0], [82.0, 303.0]], 'bbox': (82, 279, 208, 25), 'method': 'polygon'}, {'text': '手', 'confidence': 0.9999611377716064, 'polygon': [[80.0, 335.0], [113.0, 335.0], [113.0, 365.0], [80.0, 365.0]], 'bbox': (80, 335, 34, 31), 'method': 'polygon'}, {'text': '电', 'confidence': 0.9988621473312378, 'polygon': [[168.0, 336.0], [204.0, 336.0], [204.0, 365.0], [168.0, 365.0]], 'bbox': (168, 336, 37, 30), 'method': 'polygon'}, {'text': '筒', 'confidence': 0.998289167881012, 'polygon': [[263.0, 336.0], [290.0, 336.0], [290.0, 365.0], [263.0, 365.0]], 'bbox': (263, 336, 28, 30), 'method': 'polygon'}, {'text': 'SOS警报/指南针', 'confidence': 0.9697568416595459, 'polygon': [[81.0, 398.0], [288.0, 398.0], [288.0, 425.0], [81.0, 425.0]], 'bbox': (81, 398, 208, 28), 'method': 'polygon'}, {'text': '手摇/太阳能发电', 'confidence': 0.9907045364379883, 'polygon': [[83.0, 457.0], [289.0, 457.0], [289.0, 485.0], [83.0, 485.0]], 'bbox': (83, 457, 207, 29), 'method': 'polygon'}, {'text': '可插U盘/TF卡', 'confidence': 0.9861011505126953, 'polygon': [[82.0, 518.0], [290.0, 518.0], [290.0, 545.0], [82.0, 545.0]], 'bbox': (82, 518, 209, 28), 'method': 'polygon'}, {'text': '限时', 'confidence': 0.9982037544250488, 'polygon': [[18.0, 659.0], [151.0, 661.0], [150.0, 729.0], [17.0, 727.0]], 'bbox': (17, 659, 135, 71), 'method': 'polygon'}, {'text': '赠送', 'confidence': 0.9996599555015564, 'polygon': [[15.0, 728.0], [149.0, 728.0], [149.0, 792.0], [15.0, 792.0]], 'bbox': (15, 728, 135, 65), 'method': 'polygon'}, {'text': '延长天线', 'confidence': 0.9981579780578613, 'polygon': [[211.0, 769.0], [288.0, 769.0], [288.0, 790.0], [211.0, 790.0]], 'bbox': (211, 769, 78, 22), 'method': 'polygon'}, {'text': '内存卡', 'confidence': 0.9985320568084717, 'polygon': [[323.0, 766.0], [384.0, 766.0], [384.0, 791.0], [323.0, 791.0]], 'bbox': (323, 766, 62, 26), 'method': 'polygon'}, {'text': '读卡器', 'confidence': 0.998264729976654, 'polygon': [[423.0, 767.0], [483.0, 767.0], [483.0, 792.0], [423.0, 792.0]], 'bbox': (423, 767, 61, 26), 'method': 'polygon'}, {'text': '充电线', 'confidence': 0.9980173110961914, 'polygon': [[522.0, 767.0], [583.0, 767.0], [583.0, 792.0], [522.0, 792.0]], 'bbox': (522, 767, 62, 26), 'method': 'polygon'}, {'text': '礼盒礼袋', 'confidence': 0.9984273910522461, 'polygon': [[618.0, 768.0], [696.0, 768.0], [696.0, 791.0], [618.0, 791.0]], 'bbox': (618, 768, 79, 24), 'method': 'polygon'}, {'text': '充电头', 'confidence': 0.9954769611358643, 'polygon': [[725.0, 767.0], [785.0, 767.0], [785.0, 792.0], [725.0, 792.0]], 'bbox': (725, 767, 61, 26), 'method': 'polygon'}] +[2025-10-24 09:40:11,362] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 89.4%): '暴走兔' +[2025-10-24 09:40:11,362] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): 'BAOZOUTU' +[2025-10-24 09:40:11,362] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): 'AM' +[2025-10-24 09:40:11,362] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.8%): '全波段收音机' +[2025-10-24 09:40:11,362] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): '104' +[2025-10-24 09:40:11,363] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): '150' +[2025-10-24 09:40:11,363] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.8%): '蓝牙音' +[2025-10-24 09:40:11,363] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 100.0%): '响' +[2025-10-24 09:40:11,363] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): '1400' +[2025-10-24 09:40:11,363] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): '·R11' +[2025-10-24 09:40:11,363] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): 'RADIO/BT/MUSIC' +[2025-10-24 09:40:11,364] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): 'PLAYER' +[2025-10-24 09:40:11,364] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.8%): '4000毫安锂电池' +[2025-10-24 09:40:11,364] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 100.0%): '手' +[2025-10-24 09:40:11,364] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.9%): '电' +[2025-10-24 09:40:11,364] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.8%): '筒' +[2025-10-24 09:40:11,365] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 97.0%): 'SOS警报/指南针' +[2025-10-24 09:40:11,365] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.1%): '手摇/太阳能发电' +[2025-10-24 09:40:11,365] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 98.6%): '可插U盘/TF卡' +[2025-10-24 09:40:11,366] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.8%): '限时' +[2025-10-24 09:40:11,366] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 100.0%): '赠送' +[2025-10-24 09:40:11,366] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.8%): '延长天线' +[2025-10-24 09:40:11,366] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.9%): '内存卡' +[2025-10-24 09:40:11,366] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.8%): '读卡器' +[2025-10-24 09:40:11,366] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.8%): '充电线' +[2025-10-24 09:40:11,366] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.8%): '礼盒礼袋' +[2025-10-24 09:40:11,367] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.5%): '充电头' +[2025-10-24 09:40:11,367] [LogListener] [DEBUG] [loggerModule.py:debug:275] 필터링 결과: 19/27개 (신뢰도 + & 중국어) +[2025-10-24 09:40:11,367] [LogListener] [DEBUG] [loggerModule.py:debug:275] filter_ocr_results: [{'text': '暴走兔', 'confidence': 0.8941178917884827, 'polygon': [[135.0, 20.0], [271.0, 22.0], [270.0, 64.0], [134.0, 62.0]], 'bbox': (134, 20, 138, 45), 'method': 'polygon'}, {'text': '全波段收音机', 'confidence': 0.9979420304298401, 'polygon': [[81.0, 158.0], [288.0, 156.0], [288.0, 184.0], [81.0, 186.0]], 'bbox': (81, 156, 208, 31), 'method': 'polygon'}, {'text': '蓝牙音', 'confidence': 0.9979119300842285, 'polygon': [[79.0, 213.0], [271.0, 213.0], [271.0, 248.0], [79.0, 248.0]], 'bbox': (79, 213, 193, 36), 'method': 'polygon'}, {'text': '响', 'confidence': 0.9996689558029175, 'polygon': [[259.0, 219.0], [290.0, 219.0], [290.0, 244.0], [259.0, 244.0]], 'bbox': (259, 219, 32, 26), 'method': 'polygon'}, {'text': '4000毫安锂电池', 'confidence': 0.9979369640350342, 'polygon': [[82.0, 279.0], [289.0, 279.0], [289.0, 303.0], [82.0, 303.0]], 'bbox': (82, 279, 208, 25), 'method': 'polygon'}, {'text': '手', 'confidence': 0.9999611377716064, 'polygon': [[80.0, 335.0], [113.0, 335.0], [113.0, 365.0], [80.0, 365.0]], 'bbox': (80, 335, 34, 31), 'method': 'polygon'}, {'text': '电', 'confidence': 0.9988621473312378, 'polygon': [[168.0, 336.0], [204.0, 336.0], [204.0, 365.0], [168.0, 365.0]], 'bbox': (168, 336, 37, 30), 'method': 'polygon'}, {'text': '筒', 'confidence': 0.998289167881012, 'polygon': [[263.0, 336.0], [290.0, 336.0], [290.0, 365.0], [263.0, 365.0]], 'bbox': (263, 336, 28, 30), 'method': 'polygon'}, {'text': 'SOS警报/指南针', 'confidence': 0.9697568416595459, 'polygon': [[81.0, 398.0], [288.0, 398.0], [288.0, 425.0], [81.0, 425.0]], 'bbox': (81, 398, 208, 28), 'method': 'polygon'}, {'text': '手摇/太阳能发电', 'confidence': 0.9907045364379883, 'polygon': [[83.0, 457.0], [289.0, 457.0], [289.0, 485.0], [83.0, 485.0]], 'bbox': (83, 457, 207, 29), 'method': 'polygon'}, {'text': '可插U盘/TF卡', 'confidence': 0.9861011505126953, 'polygon': [[82.0, 518.0], [290.0, 518.0], [290.0, 545.0], [82.0, 545.0]], 'bbox': (82, 518, 209, 28), 'method': 'polygon'}, {'text': '限时', 'confidence': 0.9982037544250488, 'polygon': [[18.0, 659.0], [151.0, 661.0], [150.0, 729.0], [17.0, 727.0]], 'bbox': (17, 659, 135, 71), 'method': 'polygon'}, {'text': '赠送', 'confidence': 0.9996599555015564, 'polygon': [[15.0, 728.0], [149.0, 728.0], [149.0, 792.0], [15.0, 792.0]], 'bbox': (15, 728, 135, 65), 'method': 'polygon'}, {'text': '延长天线', 'confidence': 0.9981579780578613, 'polygon': [[211.0, 769.0], [288.0, 769.0], [288.0, 790.0], [211.0, 790.0]], 'bbox': (211, 769, 78, 22), 'method': 'polygon'}, {'text': '内存卡', 'confidence': 0.9985320568084717, 'polygon': [[323.0, 766.0], [384.0, 766.0], [384.0, 791.0], [323.0, 791.0]], 'bbox': (323, 766, 62, 26), 'method': 'polygon'}, {'text': '读卡器', 'confidence': 0.998264729976654, 'polygon': [[423.0, 767.0], [483.0, 767.0], [483.0, 792.0], [423.0, 792.0]], 'bbox': (423, 767, 61, 26), 'method': 'polygon'}, {'text': '充电线', 'confidence': 0.9980173110961914, 'polygon': [[522.0, 767.0], [583.0, 767.0], [583.0, 792.0], [522.0, 792.0]], 'bbox': (522, 767, 62, 26), 'method': 'polygon'}, {'text': '礼盒礼袋', 'confidence': 0.9984273910522461, 'polygon': [[618.0, 768.0], [696.0, 768.0], [696.0, 791.0], [618.0, 791.0]], 'bbox': (618, 768, 79, 24), 'method': 'polygon'}, {'text': '充电头', 'confidence': 0.9954769611358643, 'polygon': [[725.0, 767.0], [785.0, 767.0], [785.0, 792.0], [725.0, 792.0]], 'bbox': (725, 767, 61, 26), 'method': 'polygon'}] +[2025-10-24 09:40:11,368] [LogListener] [DEBUG] [loggerModule.py:debug:275] 중국어 텍스트 19개 필터링 완료 +[2025-10-24 09:40:11,368] [LogListener] [DEBUG] [loggerModule.py:debug:275] 한글 텍스트 0개 필터링 완료 +[2025-10-24 09:40:11,372] [LogListener] [DEBUG] [loggerModule.py:debug:275] translated_texts: ['도망친 토끼', '풀 밴드 라디오', '블루투스 사운드', '반지', '4000mAh 리튬 배터리', '손', '전기', '실린더', 'SOS 경보 / 나침반', '핸드 크랭크 / 태양광 발전', 'U 디스크 삽입 가능 / TF 카드', '제한된 시간', '포기하다', '확장 안테나', '메모리 카드', '카드 리더', '충전 케이블', '선물 상자 선물 가방', '충전 헤드'] +[2025-10-24 09:40:11,372] [LogListener] [DEBUG] [loggerModule.py:debug:275] 마스크 생성 완료 +[2025-10-24 09:40:11,372] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 4 OCR 권한 없음, 전체 번역 모드 +[2025-10-24 09:40:11,382] [LogListener] [DEBUG] [loggerModule.py:debug:275] [AUTO Inpaint] coverage=0.202, comps=17, min_center_dist=0.053 → request +[2025-10-24 09:40:11,382] [LogListener] [DEBUG] [loggerModule.py:debug:275] ocr_count: 19 +[2025-10-24 09:40:11,383] [LogListener] [DEBUG] [loggerModule.py:debug:275] is_member_valid: False +[2025-10-24 09:40:11,383] [LogListener] [DEBUG] [loggerModule.py:debug:275] MIGAN 인페인팅 시도 +[2025-10-24 09:40:11,385] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 입력 형태 - 이미지: (1, 3, 800, 800), 마스크: (1, 1, 800, 800) +[2025-10-24 09:40:11,594] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 출력 형태: (800, 800, 3), dtype: uint8 +[2025-10-24 09:40:11,595] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 추론 완료: 209.73 ms +[2025-10-24 09:40:11,604] [LogListener] [DEBUG] [loggerModule.py:debug:275] MIGAN 인페인팅 성공 +[2025-10-24 09:40:11,604] [LogListener] [DEBUG] [loggerModule.py:debug:275] 메모리 변화 [인페인팅]: 42248.2MB -> 42225.1MB (-23.1MB, -0.1%) - 방법: migan +[2025-10-24 09:40:11,604] [LogListener] [DEBUG] [loggerModule.py:debug:275] 인페인팅 완료 +[2025-10-24 09:40:11,677] [LogListener] [DEBUG] [loggerModule.py:debug:275] 텍스트 렌더링 완료 +[2025-10-24 09:40:11,678] [LogListener] [DEBUG] [loggerModule.py:debug:275] watermark_text: 이미지 저작권 보유 +[2025-10-24 09:40:11,678] [LogListener] [DEBUG] [loggerModule.py:debug:275] is_watermark_enabled: True +[2025-10-24 09:40:12,009] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 저장 완료 : C:\ProgramData\ImgWorker\work\translated_option_img_4.webp +[2025-10-24 09:40:12,010] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 4 번역 완료: C:\ProgramData\ImgWorker\work\translated_option_img_4.webp +[2025-10-24 09:40:12,010] [LogListener] [DEBUG] [loggerModule.py:debug:275] ⏱ 이미지 파이프라인 총 1696.8ms | download=0.0ms | ocr=771.4ms | translate=11.0ms | mask=11.0ms | inpaint=227.7ms(migan/DirectML) | render=74.0ms | save=288.0ms +[2025-10-24 09:40:12,011] [LogListener] [DEBUG] [loggerModule.py:debug:275] process_single_image 호출 완료 +[2025-10-24 09:40:12,011] [ResultListener] [WARNING] [loggerModule.py:warning:287] 워커 롤링 스케줄: reason=job-count-threshold +[2025-10-24 09:40:12,012] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 결과 반환 중: uid=b699469d-6874-4ce5-97a0-448b8cb2d7e0 +[2025-10-24 09:40:12,012] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 결과 반환 완료: uid=b699469d-6874-4ce5-97a0-448b8cb2d7e0 +[2025-10-24 09:40:12,012] [WorkerRoller] [WARNING] [loggerModule.py:warning:287] 워커 롤링 시작: job-count-threshold +[2025-10-24 09:40:12,012] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 09:40:12,012] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 31492) +[2025-10-24 09:40:12,030] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔥 작업 수신 성공 +[2025-10-24 09:40:12,070] [LogListener] [DEBUG] [loggerModule.py:debug:275] Shutdown signal 수신 → 종료 +[2025-10-24 09:40:12,070] [LogListener] [DEBUG] [loggerModule.py:debug:275] OCR 모듈 정리 완료 +[2025-10-24 09:40:12,070] [LogListener] [DEBUG] [loggerModule.py:debug:275] 마스크 모듈 정리 완료 +[2025-10-24 09:40:12,071] [LogListener] [DEBUG] [loggerModule.py:debug:275] 임시 폴더 삭제됨: C:\ProgramData\ImgWorker\work +[2025-10-24 09:40:12,071] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 프로세서 소멸 +[2025-10-24 09:40:12,587] [WorkerRoller] [INFO] [loggerModule.py:info:281] 워커 프로세스 기동: PID=30672 +[2025-10-24 09:40:13,125] [LogListener] [DEBUG] [loggerModule.py:debug:275] ImageWorker 프로세스 기동 (PID=30672, Name=ImageWorkerProcess) +[2025-10-24 09:40:13,125] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔧 ImageProcessor3 초기화 시작... +[2025-10-24 09:40:13,126] [LogListener] [DEBUG] [loggerModule.py:debug:275] tracemalloc 메모리 추적 시작 +[2025-10-24 09:40:13,133] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ DirectML 환경 준비 완료 (Windows DirectX 12 기반) +[2025-10-24 09:40:13,133] [LogListener] [DEBUG] [loggerModule.py:debug:275] === 🚀 DirectML GPU 상태 초기화 시작 🚀 === +[2025-10-24 09:40:13,133] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🎯 사용자 GPU 가속 요청: False +[2025-10-24 09:40:13,133] [LogListener] [DEBUG] [loggerModule.py:debug:275] 💻 현재 운영체제: Windows +[2025-10-24 09:40:13,134] [LogListener] [DEBUG] [loggerModule.py:debug:275] GPU 가속이 비활성화됨 (toggle_states['use_cuda'] = False) +[2025-10-24 09:40:13,134] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔒 안전한 CPU 모드로 모든 GPU 설정 강제 비활성화 +[2025-10-24 09:40:13,134] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔧 ImageProcessor3 GPU 상태 요약: +[2025-10-24 09:40:13,134] [LogListener] [DEBUG] [loggerModule.py:debug:275] - CUDA 사용 가능: False +[2025-10-24 09:40:13,134] [LogListener] [DEBUG] [loggerModule.py:debug:275] - toggle_states['use_cuda']: NOT_SET +[2025-10-24 09:40:13,134] [LogListener] [DEBUG] [loggerModule.py:debug:275] - GPU 하드웨어 정보: {} +[2025-10-24 09:40:13,134] [LogListener] [DEBUG] [loggerModule.py:debug:275] ImageProcessor3 Init toggle_states: {'TEMP_IMAGE_DIR': 'C:\\ProgramData\\ImgWorker\\work', 'output_image_format': 'webp', 'use_local_rembg': True, 'local_rembg_model_path': 'D:\\py\\img_worker\\modules\\rembg_models', 'local_model_name': 'birefnet-general-lite', 'optionIMGTrans_type': 'CPU', 'detail_IMGTrans_type': 'CPU', 'thumb_trans_type': 'CPU', 'migan_use_accel': True, 'migan_onnx_path': 'D:\\py\\img_worker\\modules\\migan_onnx\\migan_pipeline_v2.onnx', 'ocr_provider_override': 'auto', 'migan_provider_override': 'auto'} +[2025-10-24 09:40:13,134] [LogListener] [DEBUG] [loggerModule.py:debug:275] is_member_valid: False +[2025-10-24 09:40:13,134] [LogListener] [DEBUG] [loggerModule.py:debug:275] debug_images 디렉토리 이미 존재: D:\py\img_worker\modules\debug_images +[2025-10-24 09:40:13,134] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.font_path: D:\py\img_worker\modules\fonts\HakgyoansimDunggeunmisoTTFB.ttf +[2025-10-24 09:40:13,135] [LogListener] [DEBUG] [loggerModule.py:debug:275] toggle_states font_path: D:\py\img_worker\modules\fonts\HakgyoansimDunggeunmisoTTFB.ttf +[2025-10-24 09:40:13,135] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.TEMP_IMAGE_DIR: C:\ProgramData\ImgWorker\work +[2025-10-24 09:40:13,135] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.debugging_save_Dir: D:\py\img_worker\modules\debug_images +[2025-10-24 09:40:13,135] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.unwanted_texts: {} +[2025-10-24 09:40:13,135] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.inpaint_method: migan +[2025-10-24 09:40:13,135] [LogListener] [DEBUG] [loggerModule.py:debug:275] Image.MAX_IMAGE_PIXELS set to 20000000 +[2025-10-24 09:40:13,135] [LogListener] [DEBUG] [loggerModule.py:debug:275] ONNX 모델 타입 설정값: 자동 선택, GPU 정보: {} +[2025-10-24 09:40:13,135] [LogListener] [DEBUG] [loggerModule.py:debug:275] 자동 선택 모드: GPU 추천 모델 simp 사용 +[2025-10-24 09:40:13,135] [LogListener] [DEBUG] [loggerModule.py:debug:275] ONNX OCR 모델 타입 결정: simp (GPU: False) +[2025-10-24 09:40:13,135] [LogListener] [DEBUG] [loggerModule.py:debug:275] ONNX OCR 모듈 CPU 모드로 설정 +[2025-10-24 09:40:13,135] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🚀 ONNX TextSystem 초기화 시작 (CPU 모드) +[2025-10-24 09:40:13,135] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🚀 ONNX TextSystem 초기화 시작 (CPU 모드) +[2025-10-24 09:40:14,698] [LogListener] [DEBUG] [loggerModule.py:debug:275] 문자 사전 파일 UTF-8 확인 완료: D:\py\img_worker\modules\onnx_ocr_module\dict\ppocr_keys_v1.txt +[2025-10-24 09:40:14,781] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ ONNX TextSystem 초기화 완료 (CPU + SIMP (호환성) 모델) +[2025-10-24 09:40:14,781] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ ONNX TextSystem 초기화 완료 (CPU + SIMP 모델) +[2025-10-24 09:40:14,781] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ ONNX OCR 모듈 초기화 성공 (CPU 모드) +[2025-10-24 09:40:14,782] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ ONNX OCR 모듈 초기화 성공 +[2025-10-24 09:40:14,782] [LogListener] [DEBUG] [loggerModule.py:debug:275] 마스크 모듈 초기화 완료 +[2025-10-24 09:40:14,782] [LogListener] [DEBUG] [loggerModule.py:debug:275] MaskModule 초기화 성공 +[2025-10-24 09:40:14,782] [LogListener] [DEBUG] [loggerModule.py:debug:275] 텍스트 렌더링 모듈 초기화 완료 +[2025-10-24 09:40:14,782] [LogListener] [DEBUG] [loggerModule.py:debug:275] 기본 폰트: D:\py\img_worker\modules\fonts\HakgyoansimDunggeunmisoTTFB.ttf +[2025-10-24 09:40:14,782] [LogListener] [DEBUG] [loggerModule.py:debug:275] TextRenderingModule 초기화 성공 +[2025-10-24 09:40:14,783] [LogListener] [DEBUG] [loggerModule.py:debug:275] 폰트 로드 성공: D:\py\img_worker\modules\fonts\HakgyoansimDunggeunmisoTTFB.ttf +[2025-10-24 09:40:14,783] [LogListener] [DEBUG] [loggerModule.py:debug:275] PostImageManager 초기화 성공 +[2025-10-24 09:40:14,783] [LogListener] [DEBUG] [loggerModule.py:debug:275] Request_AI_Server GPU 상태: CUDA 사용 가능=False +[2025-10-24 09:40:14,783] [LogListener] [DEBUG] [loggerModule.py:debug:275] Request_AI_Server 초기화 성공 +[2025-10-24 09:40:14,783] [LogListener] [DEBUG] [loggerModule.py:debug:275] GoogleTranslate 초기화 성공 +[2025-10-24 09:40:14,783] [LogListener] [DEBUG] [loggerModule.py:debug:275] MIGAN CUDA 사용 불가 - CPU 모드로 설정 +[2025-10-24 09:40:14,783] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] GPU 관리자 전달: GPUManager, can_use_cuda: False +[2025-10-24 09:40:15,007] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] GPU 관리자 사용 가능: False +[2025-10-24 09:40:15,008] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 사용 가능한 providers: ['DmlExecutionProvider', 'CPUExecutionProvider'] +[2025-10-24 09:40:15,008] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] DirectML 가속 활성화 +[2025-10-24 09:40:15,008] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 최종 providers: [('DmlExecutionProvider', {}), ('CPUExecutionProvider', {})] +[2025-10-24 09:40:15,008] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] DirectML 가속 시도: [('DmlExecutionProvider', {}), ('CPUExecutionProvider', {})] +[2025-10-24 09:40:15,008] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] DirectML 가속 성공! 실제 providers: ['DmlExecutionProvider', 'CPUExecutionProvider'] +[2025-10-24 09:40:15,008] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 입력 0: image, 형태: ['batch_size', 3, 'height', 'width'], 타입: tensor(uint8) +[2025-10-24 09:40:15,008] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 입력 1: mask, 형태: ['batch_size', 1, 'height', 'width'], 타입: tensor(uint8) +[2025-10-24 09:40:15,009] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 출력 0: result, 형태: ['ScatterNDresult_dim_0', 3, 'ScatterNDresult_dim_2', 'ScatterNDresult_dim_3'], 타입: tensor(uint8) +[2025-10-24 09:40:15,009] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 세션 준비 완료. providers=['DmlExecutionProvider', 'CPUExecutionProvider'] +[2025-10-24 09:40:15,009] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] GPU 관리자 연결 완료: GPUManager +[2025-10-24 09:40:15,009] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 파이프라인 gpu_manager 속성: True, 값: +[2025-10-24 09:40:15,009] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 초기화 완료: gpu_manager 속성=True, 값= +[2025-10-24 09:40:15,009] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔰 OCR 모듈 Warm-up 시작... +[2025-10-24 09:40:15,009] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔍 ONNX OCR 감지 방식: polygon +[2025-10-24 09:40:15,014] [LogListener] [DEBUG] [loggerModule.py:debug:275] ⚡ ONNX OCR 추론 완료: 5.0ms +[2025-10-24 09:40:15,014] [LogListener] [DEBUG] [loggerModule.py:debug:275] 📊 세부 시간 - 감지: 4.0ms, 인식: 0.0ms, 분류: 0.0ms +[2025-10-24 09:40:15,014] [LogListener] [WARNING] [loggerModule.py:warning:287] ⚠️ ONNX OCR 결과가 비어있습니다 +[2025-10-24 09:40:15,017] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ OCR 모듈 Warm-up 성공 +[2025-10-24 09:40:15,018] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔰 ImageProcessor Warm‑up 완료 +[2025-10-24 09:40:15,018] [ResultListener] [INFO] [loggerModule.py:info:281] 워커 READY 수신 +[2025-10-24 09:40:15,018] [WorkerRoller] [INFO] [loggerModule.py:info:281] 버퍼 작업 재개: 1건 +[2025-10-24 09:40:15,018] [ResultListener] [INFO] [loggerModule.py:info:281] 워커 READY 수신 +[2025-10-24 09:40:15,018] [LogListener] [DEBUG] [loggerModule.py:debug:275] 📡 추가 READY 신호 전송 완료 +[2025-10-24 09:40:15,018] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 09:40:15,019] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 30672) +[2025-10-24 09:40:15,019] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔥 작업 수신 성공 +[2025-10-24 09:40:15,019] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🚀 작업 처리 시작: cmd=process_single_image, uid=6f4db593-f4f0-4b46-932f-9b18ad8624ef +[2025-10-24 09:40:15,019] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 프로세서 toggle_states 업데이트 : {'TEMP_IMAGE_DIR': 'C:\\ProgramData\\ImgWorker\\work', 'output_image_format': 'webp', 'use_local_rembg': True, 'local_rembg_model_path': 'D:\\py\\img_worker\\modules\\rembg_models', 'local_model_name': 'birefnet-general-lite', 'optionIMGTrans_type': 'GPU', 'detail_IMGTrans_type': 'GPU', 'thumb_trans_type': 'GPU', 'migan_use_accel': True, 'migan_onnx_path': 'D:\\py\\img_worker\\modules\\migan_onnx\\migan_pipeline_v2.onnx', 'ocr_provider_override': 'auto', 'migan_provider_override': 'auto', 'font_type': '폰트5', 'unwanted_texts': ['가격설명', '무료배송', '보증', '사은품', '세일', '이벤트', '특가', '품절', '할인', '할인가'], 'is_member_valid': False, 'authenticated_by_admin': True} +[2025-10-24 09:40:15,019] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 실행 직전: cmd=process_single_image +[2025-10-24 09:40:15,019] [LogListener] [DEBUG] [loggerModule.py:debug:275] process_single_image 호출 직전 +[2025-10-24 09:40:15,022] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 번역시작 +[2025-10-24 09:40:15,312] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 5 처리 시작: C:\ProgramData\ImgWorker\incoming\dl_288fac4b3a3c.jpg - 전체 번역 모드 +[2025-10-24 09:40:15,318] [LogListener] [DEBUG] [loggerModule.py:debug:275] 로컬 파일 경로 감지, 다운로드 생략: C:\ProgramData\ImgWorker\incoming\dl_288fac4b3a3c.jpg +[2025-10-24 09:40:15,318] [LogListener] [DEBUG] [loggerModule.py:debug:275] 옵션 이미지는 스케일 처리 건너뛰기: option +[2025-10-24 09:40:15,318] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 5 로컬 저장위치(옵션 이미지 원본 유지): C:\ProgramData\ImgWorker\incoming\dl_288fac4b3a3c.jpg +[2025-10-24 09:40:15,319] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔍 ONNX OCR 감지 방식: polygon +[2025-10-24 09:40:15,520] [LogListener] [DEBUG] [loggerModule.py:debug:275] ⚡ ONNX OCR 추론 완료: 200.0ms +[2025-10-24 09:40:15,531] [LogListener] [DEBUG] [loggerModule.py:debug:275] 📊 세부 시간 - 감지: 168.0ms, 인식: 27.0ms, 분류: 2.0ms +[2025-10-24 09:40:15,546] [LogListener] [DEBUG] [loggerModule.py:debug:275] 메모리 변화 [OCR 처리]: 41794.1MB -> 41910.7MB (+116.5MB, +0.3%) - 이미지 5 +[2025-10-24 09:40:15,551] [LogListener] [DEBUG] [loggerModule.py:debug:275] ocr_results: [{'text': '霸榜收音机热销榜', 'confidence': 0.9966191053390503, 'polygon': [[225.0, 58.0], [570.0, 58.0], [570.0, 96.0], [225.0, 96.0]], 'bbox': (225, 58, 346, 39), 'method': 'polygon'}, {'text': 'TOP1', 'confidence': 0.9870726466178894, 'polygon': [[290.0, 137.0], [498.0, 137.0], [498.0, 212.0], [290.0, 212.0]], 'bbox': (290, 137, 209, 76), 'method': 'polygon'}] +[2025-10-24 09:40:15,552] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.7%): '霸榜收音机热销榜' +[2025-10-24 09:40:15,552] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): 'TOP1' +[2025-10-24 09:40:15,552] [LogListener] [DEBUG] [loggerModule.py:debug:275] 필터링 결과: 1/2개 (신뢰도 + & 중국어) +[2025-10-24 09:40:15,552] [LogListener] [DEBUG] [loggerModule.py:debug:275] filter_ocr_results: [{'text': '霸榜收音机热销榜', 'confidence': 0.9966191053390503, 'polygon': [[225.0, 58.0], [570.0, 58.0], [570.0, 96.0], [225.0, 96.0]], 'bbox': (225, 58, 346, 39), 'method': 'polygon'}] +[2025-10-24 09:40:15,552] [LogListener] [DEBUG] [loggerModule.py:debug:275] 중국어 텍스트 1개 필터링 완료 +[2025-10-24 09:40:15,552] [LogListener] [DEBUG] [loggerModule.py:debug:275] 한글 텍스트 0개 필터링 완료 +[2025-10-24 09:40:17,725] [LogListener] [DEBUG] [loggerModule.py:debug:275] translated_texts: ['라디오 베스트셀러 목록 장악'] +[2025-10-24 09:40:17,725] [LogListener] [DEBUG] [loggerModule.py:debug:275] 마스크 생성 완료 +[2025-10-24 09:40:17,725] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 5 OCR 권한 없음, 전체 번역 모드 +[2025-10-24 09:40:17,732] [LogListener] [DEBUG] [loggerModule.py:debug:275] [AUTO Inpaint] coverage=0.045, comps=1, min_center_dist=0.000 → request +[2025-10-24 09:40:17,732] [LogListener] [DEBUG] [loggerModule.py:debug:275] ocr_count: 1 +[2025-10-24 09:40:17,732] [LogListener] [DEBUG] [loggerModule.py:debug:275] is_member_valid: False +[2025-10-24 09:40:17,732] [LogListener] [DEBUG] [loggerModule.py:debug:275] MIGAN 인페인팅 시도 +[2025-10-24 09:40:17,734] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 입력 형태 - 이미지: (1, 3, 790, 790), 마스크: (1, 1, 790, 790) +[2025-10-24 09:40:18,216] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 출력 형태: (790, 790, 3), dtype: uint8 +[2025-10-24 09:40:18,217] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 추론 완료: 482.39 ms +[2025-10-24 09:40:18,224] [LogListener] [DEBUG] [loggerModule.py:debug:275] MIGAN 인페인팅 성공 +[2025-10-24 09:40:18,225] [LogListener] [DEBUG] [loggerModule.py:debug:275] 메모리 변화 [인페인팅]: 41946.8MB -> 42146.2MB (+199.5MB, +0.5%) - 방법: migan +[2025-10-24 09:40:18,225] [LogListener] [DEBUG] [loggerModule.py:debug:275] 인페인팅 완료 +[2025-10-24 09:40:18,239] [LogListener] [DEBUG] [loggerModule.py:debug:275] 텍스트 렌더링 완료 +[2025-10-24 09:40:18,240] [LogListener] [DEBUG] [loggerModule.py:debug:275] watermark_text: 이미지 저작권 보유 +[2025-10-24 09:40:18,240] [LogListener] [DEBUG] [loggerModule.py:debug:275] is_watermark_enabled: True +[2025-10-24 09:40:18,519] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 저장 완료 : C:\ProgramData\ImgWorker\work\translated_option_img_5.webp +[2025-10-24 09:40:18,520] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 5 번역 완료: C:\ProgramData\ImgWorker\work\translated_option_img_5.webp +[2025-10-24 09:40:18,520] [LogListener] [DEBUG] [loggerModule.py:debug:275] ⏱ 이미지 파이프라인 총 3500.7ms | download=0.0ms | ocr=208.0ms | translate=2172.7ms | mask=2172.7ms | inpaint=498.4ms(migan/DirectML) | render=15.0ms | save=246.0ms +[2025-10-24 09:40:18,521] [LogListener] [DEBUG] [loggerModule.py:debug:275] process_single_image 호출 완료 +[2025-10-24 09:40:18,521] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 결과 반환 중: uid=6f4db593-f4f0-4b46-932f-9b18ad8624ef +[2025-10-24 09:40:18,522] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 결과 반환 완료: uid=6f4db593-f4f0-4b46-932f-9b18ad8624ef +[2025-10-24 09:40:18,522] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 09:40:18,522] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 30672) +[2025-10-24 09:40:20,264] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔥 작업 수신 성공 +[2025-10-24 09:40:20,265] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🚀 작업 처리 시작: cmd=process_single_image, uid=04d0b2d2-381b-4de0-bb84-8945a8b2934a +[2025-10-24 09:40:20,265] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 프로세서 toggle_states 업데이트 : {'TEMP_IMAGE_DIR': 'C:\\ProgramData\\ImgWorker\\work', 'output_image_format': 'webp', 'use_local_rembg': True, 'local_rembg_model_path': 'D:\\py\\img_worker\\modules\\rembg_models', 'local_model_name': 'birefnet-general-lite', 'optionIMGTrans_type': 'GPU', 'detail_IMGTrans_type': 'GPU', 'thumb_trans_type': 'GPU', 'migan_use_accel': True, 'migan_onnx_path': 'D:\\py\\img_worker\\modules\\migan_onnx\\migan_pipeline_v2.onnx', 'ocr_provider_override': 'auto', 'migan_provider_override': 'auto', 'font_type': '폰트5', 'unwanted_texts': ['가격설명', '무료배송', '보증', '사은품', '세일', '이벤트', '특가', '품절', '할인', '할인가'], 'is_member_valid': False, 'authenticated_by_admin': True} +[2025-10-24 09:40:20,265] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 실행 직전: cmd=process_single_image +[2025-10-24 09:40:20,265] [LogListener] [DEBUG] [loggerModule.py:debug:275] process_single_image 호출 직전 +[2025-10-24 09:40:20,265] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 번역시작 +[2025-10-24 09:40:20,628] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 6 처리 시작: C:\ProgramData\ImgWorker\incoming\dl_1efca929333d.jpg - 전체 번역 모드 +[2025-10-24 09:40:20,636] [LogListener] [DEBUG] [loggerModule.py:debug:275] 로컬 파일 경로 감지, 다운로드 생략: C:\ProgramData\ImgWorker\incoming\dl_1efca929333d.jpg +[2025-10-24 09:40:20,636] [LogListener] [DEBUG] [loggerModule.py:debug:275] 옵션 이미지는 스케일 처리 건너뛰기: option +[2025-10-24 09:40:20,637] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 6 로컬 저장위치(옵션 이미지 원본 유지): C:\ProgramData\ImgWorker\incoming\dl_1efca929333d.jpg +[2025-10-24 09:40:20,648] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔍 ONNX OCR 감지 방식: polygon +[2025-10-24 09:40:21,553] [LogListener] [DEBUG] [loggerModule.py:debug:275] ⚡ ONNX OCR 추론 완료: 902.6ms +[2025-10-24 09:40:21,568] [LogListener] [DEBUG] [loggerModule.py:debug:275] 📊 세부 시간 - 감지: 152.0ms, 인식: 714.6ms, 분류: 29.0ms +[2025-10-24 09:40:21,572] [LogListener] [DEBUG] [loggerModule.py:debug:275] 메모리 변화 [OCR 처리]: 42216.7MB -> 42417.8MB (+201.1MB, +0.5%) - 이미지 6 +[2025-10-24 09:40:21,572] [LogListener] [DEBUG] [loggerModule.py:debug:275] ocr_results: [{'text': '豆', 'confidence': 0.5328531265258789, 'polygon': [[141.0, 37.0], [273.0, 35.0], [273.0, 64.0], [141.0, 66.0]], 'bbox': (141, 35, 133, 32), 'method': 'polygon'}, {'text': 'BAOZOUTU', 'confidence': 0.9931097030639648, 'polygon': [[134.0, 73.0], [274.0, 73.0], [274.0, 87.0], [134.0, 87.0]], 'bbox': (134, 73, 141, 15), 'method': 'polygon'}, {'text': '可门2 3丽', 'confidence': 0.5591045022010803, 'polygon': [[125.0, 155.0], [288.0, 157.0], [288.0, 195.0], [125.0, 193.0]], 'bbox': (125, 155, 164, 41), 'method': 'polygon'}, {'text': 'FM', 'confidence': 0.9867115020751953, 'polygon': [[452.0, 146.0], [464.0, 146.0], [464.0, 155.0], [452.0, 155.0]], 'bbox': (452, 146, 13, 10), 'method': 'polygon'}, {'text': 'AM', 'confidence': 0.9248346090316772, 'polygon': [[470.0, 146.0], [481.0, 146.0], [481.0, 155.0], [470.0, 155.0]], 'bbox': (470, 146, 12, 10), 'method': 'polygon'}, {'text': 'WB', 'confidence': 0.6591552495956421, 'polygon': [[466.0, 162.0], [479.0, 159.0], [484.0, 178.0], [471.0, 181.0]], 'bbox': (466, 159, 19, 23), 'method': 'polygon'}, {'text': '10', 'confidence': 0.777127742767334, 'polygon': [[458.0, 191.0], [470.0, 203.0], [460.0, 214.0], [448.0, 202.0]], 'bbox': (448, 191, 23, 24), 'method': 'polygon'}, {'text': '1500', 'confidence': 0.8680721521377563, 'polygon': [[469.0, 192.0], [482.0, 192.0], [482.0, 209.0], [469.0, 209.0]], 'bbox': (469, 192, 14, 18), 'method': 'polygon'}, {'text': '1400', 'confidence': 0.8128303289413452, 'polygon': [[470.0, 212.0], [483.0, 212.0], [483.0, 226.0], [470.0, 226.0]], 'bbox': (470, 212, 14, 15), 'method': 'polygon'}, {'text': '-R11-', 'confidence': 0.7555524110794067, 'polygon': [[457.0, 245.0], [493.0, 245.0], [493.0, 256.0], [457.0, 256.0]], 'bbox': (457, 245, 37, 12), 'method': 'polygon'}, {'text': 'RADIO/BT/MUSICPLAYER', 'confidence': 0.9919435381889343, 'polygon': [[503.0, 244.0], [634.0, 244.0], [634.0, 257.0], [503.0, 257.0]], 'bbox': (503, 244, 132, 14), 'method': 'polygon'}, {'text': '4000mAh 2l喜 Ul2l', 'confidence': 0.8267679810523987, 'polygon': [[85.0, 281.0], [281.0, 281.0], [281.0, 301.0], [85.0, 301.0]], 'bbox': (85, 281, 197, 21), 'method': 'polygon'}, {'text': 'PER', 'confidence': 0.5212163329124451, 'polygon': [[261.0, 344.0], [291.0, 344.0], [291.0, 358.0], [261.0, 358.0]], 'bbox': (261, 344, 31, 15), 'method': 'polygon'}, {'text': '丽晟N / 百昆 SOS', 'confidence': 0.7631003856658936, 'polygon': [[82.0, 398.0], [284.0, 400.0], [284.0, 424.0], [82.0, 422.0]], 'bbox': (82, 398, 203, 27), 'method': 'polygon'}, {'text': '三', 'confidence': 0.5160584449768066, 'polygon': [[85.0, 463.0], [123.0, 463.0], [123.0, 481.0], [85.0, 481.0]], 'bbox': (85, 463, 39, 19), 'method': 'polygon'}, {'text': '应易 是品 / E屁E', 'confidence': 0.8067229986190796, 'polygon': [[124.0, 461.0], [287.0, 461.0], [287.0, 482.0], [124.0, 482.0]], 'bbox': (124, 461, 164, 22), 'method': 'polygon'}, {'text': '0立10', 'confidence': 0.6267220377922058, 'polygon': [[537.0, 524.0], [583.0, 524.0], [583.0, 538.0], [537.0, 538.0]], 'bbox': (537, 524, 47, 15), 'method': 'polygon'}] +[2025-10-24 09:40:21,573] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 53.3%): '豆' +[2025-10-24 09:40:21,573] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): 'BAOZOUTU' +[2025-10-24 09:40:21,573] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 55.9%): '可门2 3丽' +[2025-10-24 09:40:21,573] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): 'FM' +[2025-10-24 09:40:21,573] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): 'AM' +[2025-10-24 09:40:21,573] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): 'WB' +[2025-10-24 09:40:21,573] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): '10' +[2025-10-24 09:40:21,573] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): '1500' +[2025-10-24 09:40:21,573] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): '1400' +[2025-10-24 09:40:21,574] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): '-R11-' +[2025-10-24 09:40:21,574] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): 'RADIO/BT/MUSICPLAYER' +[2025-10-24 09:40:21,574] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 82.7%): '4000mAh 2l喜 Ul2l' +[2025-10-24 09:40:21,574] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): 'PER' +[2025-10-24 09:40:21,574] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 76.3%): '丽晟N / 百昆 SOS' +[2025-10-24 09:40:21,574] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 51.6%): '三' +[2025-10-24 09:40:21,574] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 80.7%): '应易 是品 / E屁E' +[2025-10-24 09:40:21,574] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 62.7%): '0立10' +[2025-10-24 09:40:21,574] [LogListener] [DEBUG] [loggerModule.py:debug:275] 필터링 결과: 7/17개 (신뢰도 + & 중국어) +[2025-10-24 09:40:21,574] [LogListener] [DEBUG] [loggerModule.py:debug:275] filter_ocr_results: [{'text': '豆', 'confidence': 0.5328531265258789, 'polygon': [[141.0, 37.0], [273.0, 35.0], [273.0, 64.0], [141.0, 66.0]], 'bbox': (141, 35, 133, 32), 'method': 'polygon'}, {'text': '可门2 3丽', 'confidence': 0.5591045022010803, 'polygon': [[125.0, 155.0], [288.0, 157.0], [288.0, 195.0], [125.0, 193.0]], 'bbox': (125, 155, 164, 41), 'method': 'polygon'}, {'text': '4000mAh 2l喜 Ul2l', 'confidence': 0.8267679810523987, 'polygon': [[85.0, 281.0], [281.0, 281.0], [281.0, 301.0], [85.0, 301.0]], 'bbox': (85, 281, 197, 21), 'method': 'polygon'}, {'text': '丽晟N / 百昆 SOS', 'confidence': 0.7631003856658936, 'polygon': [[82.0, 398.0], [284.0, 400.0], [284.0, 424.0], [82.0, 422.0]], 'bbox': (82, 398, 203, 27), 'method': 'polygon'}, {'text': '三', 'confidence': 0.5160584449768066, 'polygon': [[85.0, 463.0], [123.0, 463.0], [123.0, 481.0], [85.0, 481.0]], 'bbox': (85, 463, 39, 19), 'method': 'polygon'}, {'text': '应易 是品 / E屁E', 'confidence': 0.8067229986190796, 'polygon': [[124.0, 461.0], [287.0, 461.0], [287.0, 482.0], [124.0, 482.0]], 'bbox': (124, 461, 164, 22), 'method': 'polygon'}, {'text': '0立10', 'confidence': 0.6267220377922058, 'polygon': [[537.0, 524.0], [583.0, 524.0], [583.0, 538.0], [537.0, 538.0]], 'bbox': (537, 524, 47, 15), 'method': 'polygon'}] +[2025-10-24 09:40:21,574] [LogListener] [DEBUG] [loggerModule.py:debug:275] 중국어 텍스트 7개 필터링 완료 +[2025-10-24 09:40:21,574] [LogListener] [DEBUG] [loggerModule.py:debug:275] 한글 텍스트 0개 필터링 완료 +[2025-10-24 09:40:23,420] [LogListener] [DEBUG] [loggerModule.py:debug:275] translated_texts: ['콩', '케멘 2 3 리', '4000mAh 2l 안녕 Ul2l', '리셍 N / 바이쿤 SOS', '삼', '잉이(Ying Yi) 제품입니다 / E 방귀 E', '0~10'] +[2025-10-24 09:40:23,421] [LogListener] [DEBUG] [loggerModule.py:debug:275] 마스크 생성 완료 +[2025-10-24 09:40:23,421] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 6 OCR 권한 없음, 전체 번역 모드 +[2025-10-24 09:40:23,428] [LogListener] [DEBUG] [loggerModule.py:debug:275] [AUTO Inpaint] coverage=0.096, comps=6, min_center_dist=0.054 → request +[2025-10-24 09:40:23,428] [LogListener] [DEBUG] [loggerModule.py:debug:275] ocr_count: 7 +[2025-10-24 09:40:23,428] [LogListener] [DEBUG] [loggerModule.py:debug:275] is_member_valid: False +[2025-10-24 09:40:23,428] [LogListener] [DEBUG] [loggerModule.py:debug:275] MIGAN 인페인팅 시도 +[2025-10-24 09:40:23,436] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 입력 형태 - 이미지: (1, 3, 800, 800), 마스크: (1, 1, 800, 800) +[2025-10-24 09:40:23,650] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 출력 형태: (800, 800, 3), dtype: uint8 +[2025-10-24 09:40:23,650] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 추론 완료: 213.00 ms +[2025-10-24 09:40:23,657] [LogListener] [DEBUG] [loggerModule.py:debug:275] MIGAN 인페인팅 성공 +[2025-10-24 09:40:23,658] [LogListener] [DEBUG] [loggerModule.py:debug:275] 메모리 변화 [인페인팅]: 42410.5MB -> 42422.5MB (+12.0MB, +0.0%) - 방법: migan +[2025-10-24 09:40:23,658] [LogListener] [DEBUG] [loggerModule.py:debug:275] 인페인팅 완료 +[2025-10-24 09:40:23,683] [LogListener] [DEBUG] [loggerModule.py:debug:275] 텍스트 렌더링 완료 +[2025-10-24 09:40:23,683] [LogListener] [DEBUG] [loggerModule.py:debug:275] watermark_text: 이미지 저작권 보유 +[2025-10-24 09:40:23,684] [LogListener] [DEBUG] [loggerModule.py:debug:275] is_watermark_enabled: True +[2025-10-24 09:40:24,002] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 저장 완료 : C:\ProgramData\ImgWorker\work\translated_option_img_6.webp +[2025-10-24 09:40:24,003] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 6 번역 완료: C:\ProgramData\ImgWorker\work\translated_option_img_6.webp +[2025-10-24 09:40:24,003] [LogListener] [DEBUG] [loggerModule.py:debug:275] ⏱ 이미지 파이프라인 총 3736.0ms | download=0.0ms | ocr=924.6ms | translate=1848.3ms | mask=1848.3ms | inpaint=236.0ms(migan/DirectML) | render=26.0ms | save=284.7ms +[2025-10-24 09:40:24,004] [LogListener] [DEBUG] [loggerModule.py:debug:275] process_single_image 호출 완료 +[2025-10-24 09:40:24,004] [ResultListener] [WARNING] [loggerModule.py:warning:287] 워커 롤링 스케줄: reason=job-count-threshold +[2025-10-24 09:40:24,004] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 결과 반환 중: uid=04d0b2d2-381b-4de0-bb84-8945a8b2934a +[2025-10-24 09:40:24,004] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 결과 반환 완료: uid=04d0b2d2-381b-4de0-bb84-8945a8b2934a +[2025-10-24 09:40:24,004] [WorkerRoller] [WARNING] [loggerModule.py:warning:287] 워커 롤링 시작: job-count-threshold +[2025-10-24 09:40:24,004] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 09:40:24,004] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 30672) +[2025-10-24 09:40:24,020] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔥 작업 수신 성공 +[2025-10-24 09:40:24,058] [LogListener] [DEBUG] [loggerModule.py:debug:275] Shutdown signal 수신 → 종료 +[2025-10-24 09:40:24,058] [LogListener] [DEBUG] [loggerModule.py:debug:275] OCR 모듈 정리 완료 +[2025-10-24 09:40:24,058] [LogListener] [DEBUG] [loggerModule.py:debug:275] 마스크 모듈 정리 완료 +[2025-10-24 09:40:24,058] [LogListener] [DEBUG] [loggerModule.py:debug:275] 임시 폴더 삭제됨: C:\ProgramData\ImgWorker\work +[2025-10-24 09:40:24,058] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 프로세서 소멸 +[2025-10-24 09:40:24,518] [WorkerRoller] [INFO] [loggerModule.py:info:281] 워커 프로세스 기동: PID=20016 +[2025-10-24 09:40:25,005] [LogListener] [DEBUG] [loggerModule.py:debug:275] ImageWorker 프로세스 기동 (PID=20016, Name=ImageWorkerProcess) +[2025-10-24 09:40:25,005] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔧 ImageProcessor3 초기화 시작... +[2025-10-24 09:40:25,005] [LogListener] [DEBUG] [loggerModule.py:debug:275] tracemalloc 메모리 추적 시작 +[2025-10-24 09:40:25,012] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ DirectML 환경 준비 완료 (Windows DirectX 12 기반) +[2025-10-24 09:40:25,012] [LogListener] [DEBUG] [loggerModule.py:debug:275] === 🚀 DirectML GPU 상태 초기화 시작 🚀 === +[2025-10-24 09:40:25,012] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🎯 사용자 GPU 가속 요청: False +[2025-10-24 09:40:25,012] [LogListener] [DEBUG] [loggerModule.py:debug:275] 💻 현재 운영체제: Windows +[2025-10-24 09:40:25,012] [LogListener] [DEBUG] [loggerModule.py:debug:275] GPU 가속이 비활성화됨 (toggle_states['use_cuda'] = False) +[2025-10-24 09:40:25,013] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔒 안전한 CPU 모드로 모든 GPU 설정 강제 비활성화 +[2025-10-24 09:40:25,013] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔧 ImageProcessor3 GPU 상태 요약: +[2025-10-24 09:40:25,013] [LogListener] [DEBUG] [loggerModule.py:debug:275] - CUDA 사용 가능: False +[2025-10-24 09:40:25,013] [LogListener] [DEBUG] [loggerModule.py:debug:275] - toggle_states['use_cuda']: NOT_SET +[2025-10-24 09:40:25,013] [LogListener] [DEBUG] [loggerModule.py:debug:275] - GPU 하드웨어 정보: {} +[2025-10-24 09:40:25,013] [LogListener] [DEBUG] [loggerModule.py:debug:275] ImageProcessor3 Init toggle_states: {'TEMP_IMAGE_DIR': 'C:\\ProgramData\\ImgWorker\\work', 'output_image_format': 'webp', 'use_local_rembg': True, 'local_rembg_model_path': 'D:\\py\\img_worker\\modules\\rembg_models', 'local_model_name': 'birefnet-general-lite', 'optionIMGTrans_type': 'CPU', 'detail_IMGTrans_type': 'CPU', 'thumb_trans_type': 'CPU', 'migan_use_accel': True, 'migan_onnx_path': 'D:\\py\\img_worker\\modules\\migan_onnx\\migan_pipeline_v2.onnx', 'ocr_provider_override': 'auto', 'migan_provider_override': 'auto'} +[2025-10-24 09:40:25,013] [LogListener] [DEBUG] [loggerModule.py:debug:275] is_member_valid: False +[2025-10-24 09:40:25,014] [LogListener] [DEBUG] [loggerModule.py:debug:275] debug_images 디렉토리 이미 존재: D:\py\img_worker\modules\debug_images +[2025-10-24 09:40:25,014] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.font_path: D:\py\img_worker\modules\fonts\HakgyoansimDunggeunmisoTTFB.ttf +[2025-10-24 09:40:25,014] [LogListener] [DEBUG] [loggerModule.py:debug:275] toggle_states font_path: D:\py\img_worker\modules\fonts\HakgyoansimDunggeunmisoTTFB.ttf +[2025-10-24 09:40:25,014] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.TEMP_IMAGE_DIR: C:\ProgramData\ImgWorker\work +[2025-10-24 09:40:25,014] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.debugging_save_Dir: D:\py\img_worker\modules\debug_images +[2025-10-24 09:40:25,014] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.unwanted_texts: {} +[2025-10-24 09:40:25,014] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.inpaint_method: migan +[2025-10-24 09:40:25,014] [LogListener] [DEBUG] [loggerModule.py:debug:275] Image.MAX_IMAGE_PIXELS set to 20000000 +[2025-10-24 09:40:25,014] [LogListener] [DEBUG] [loggerModule.py:debug:275] ONNX 모델 타입 설정값: 자동 선택, GPU 정보: {} +[2025-10-24 09:40:25,014] [LogListener] [DEBUG] [loggerModule.py:debug:275] 자동 선택 모드: GPU 추천 모델 simp 사용 +[2025-10-24 09:40:25,014] [LogListener] [DEBUG] [loggerModule.py:debug:275] ONNX OCR 모델 타입 결정: simp (GPU: False) +[2025-10-24 09:40:25,014] [LogListener] [DEBUG] [loggerModule.py:debug:275] ONNX OCR 모듈 CPU 모드로 설정 +[2025-10-24 09:40:25,014] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🚀 ONNX TextSystem 초기화 시작 (CPU 모드) +[2025-10-24 09:40:25,015] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🚀 ONNX TextSystem 초기화 시작 (CPU 모드) +[2025-10-24 09:40:26,594] [LogListener] [DEBUG] [loggerModule.py:debug:275] 문자 사전 파일 UTF-8 확인 완료: D:\py\img_worker\modules\onnx_ocr_module\dict\ppocr_keys_v1.txt +[2025-10-24 09:40:26,681] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ ONNX TextSystem 초기화 완료 (CPU + SIMP (호환성) 모델) +[2025-10-24 09:40:26,681] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ ONNX TextSystem 초기화 완료 (CPU + SIMP 모델) +[2025-10-24 09:40:26,681] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ ONNX OCR 모듈 초기화 성공 (CPU 모드) +[2025-10-24 09:40:26,682] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ ONNX OCR 모듈 초기화 성공 +[2025-10-24 09:40:26,682] [LogListener] [DEBUG] [loggerModule.py:debug:275] 마스크 모듈 초기화 완료 +[2025-10-24 09:40:26,682] [LogListener] [DEBUG] [loggerModule.py:debug:275] MaskModule 초기화 성공 +[2025-10-24 09:40:26,682] [LogListener] [DEBUG] [loggerModule.py:debug:275] 텍스트 렌더링 모듈 초기화 완료 +[2025-10-24 09:40:26,682] [LogListener] [DEBUG] [loggerModule.py:debug:275] 기본 폰트: D:\py\img_worker\modules\fonts\HakgyoansimDunggeunmisoTTFB.ttf +[2025-10-24 09:40:26,682] [LogListener] [DEBUG] [loggerModule.py:debug:275] TextRenderingModule 초기화 성공 +[2025-10-24 09:40:26,682] [LogListener] [DEBUG] [loggerModule.py:debug:275] 폰트 로드 성공: D:\py\img_worker\modules\fonts\HakgyoansimDunggeunmisoTTFB.ttf +[2025-10-24 09:40:26,682] [LogListener] [DEBUG] [loggerModule.py:debug:275] PostImageManager 초기화 성공 +[2025-10-24 09:40:26,683] [LogListener] [DEBUG] [loggerModule.py:debug:275] Request_AI_Server GPU 상태: CUDA 사용 가능=False +[2025-10-24 09:40:26,683] [LogListener] [DEBUG] [loggerModule.py:debug:275] Request_AI_Server 초기화 성공 +[2025-10-24 09:40:26,683] [LogListener] [DEBUG] [loggerModule.py:debug:275] GoogleTranslate 초기화 성공 +[2025-10-24 09:40:26,683] [LogListener] [DEBUG] [loggerModule.py:debug:275] MIGAN CUDA 사용 불가 - CPU 모드로 설정 +[2025-10-24 09:40:26,683] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] GPU 관리자 전달: GPUManager, can_use_cuda: False +[2025-10-24 09:40:26,705] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] GPU 관리자 사용 가능: False +[2025-10-24 09:40:26,924] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 사용 가능한 providers: ['DmlExecutionProvider', 'CPUExecutionProvider'] +[2025-10-24 09:40:26,925] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] DirectML 가속 활성화 +[2025-10-24 09:40:26,925] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 최종 providers: [('DmlExecutionProvider', {}), ('CPUExecutionProvider', {})] +[2025-10-24 09:40:26,925] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] DirectML 가속 시도: [('DmlExecutionProvider', {}), ('CPUExecutionProvider', {})] +[2025-10-24 09:40:26,925] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] DirectML 가속 성공! 실제 providers: ['DmlExecutionProvider', 'CPUExecutionProvider'] +[2025-10-24 09:40:26,925] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 입력 0: image, 형태: ['batch_size', 3, 'height', 'width'], 타입: tensor(uint8) +[2025-10-24 09:40:26,925] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 입력 1: mask, 형태: ['batch_size', 1, 'height', 'width'], 타입: tensor(uint8) +[2025-10-24 09:40:26,925] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 출력 0: result, 형태: ['ScatterNDresult_dim_0', 3, 'ScatterNDresult_dim_2', 'ScatterNDresult_dim_3'], 타입: tensor(uint8) +[2025-10-24 09:40:26,925] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 세션 준비 완료. providers=['DmlExecutionProvider', 'CPUExecutionProvider'] +[2025-10-24 09:40:26,925] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] GPU 관리자 연결 완료: GPUManager +[2025-10-24 09:40:26,926] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 파이프라인 gpu_manager 속성: True, 값: +[2025-10-24 09:40:26,926] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 초기화 완료: gpu_manager 속성=True, 값= +[2025-10-24 09:40:26,926] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔰 OCR 모듈 Warm-up 시작... +[2025-10-24 09:40:26,926] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔍 ONNX OCR 감지 방식: polygon +[2025-10-24 09:40:26,930] [LogListener] [DEBUG] [loggerModule.py:debug:275] ⚡ ONNX OCR 추론 완료: 4.0ms +[2025-10-24 09:40:26,931] [LogListener] [DEBUG] [loggerModule.py:debug:275] 📊 세부 시간 - 감지: 3.0ms, 인식: 0.0ms, 분류: 0.0ms +[2025-10-24 09:40:26,931] [LogListener] [WARNING] [loggerModule.py:warning:287] ⚠️ ONNX OCR 결과가 비어있습니다 +[2025-10-24 09:40:26,934] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ OCR 모듈 Warm-up 성공 +[2025-10-24 09:40:26,934] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔰 ImageProcessor Warm‑up 완료 +[2025-10-24 09:40:26,935] [ResultListener] [INFO] [loggerModule.py:info:281] 워커 READY 수신 +[2025-10-24 09:40:26,935] [WorkerRoller] [INFO] [loggerModule.py:info:281] 버퍼 작업 재개: 1건 +[2025-10-24 09:40:26,935] [ResultListener] [INFO] [loggerModule.py:info:281] 워커 READY 수신 +[2025-10-24 09:40:26,935] [LogListener] [DEBUG] [loggerModule.py:debug:275] 📡 추가 READY 신호 전송 완료 +[2025-10-24 09:40:26,935] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 09:40:26,935] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 20016) +[2025-10-24 09:40:26,935] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔥 작업 수신 성공 +[2025-10-24 09:40:26,936] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🚀 작업 처리 시작: cmd=process_single_image, uid=96199fb5-a3dc-491d-8a38-90de7bb82a7d +[2025-10-24 09:40:26,936] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 프로세서 toggle_states 업데이트 : {'TEMP_IMAGE_DIR': 'C:\\ProgramData\\ImgWorker\\work', 'output_image_format': 'webp', 'use_local_rembg': True, 'local_rembg_model_path': 'D:\\py\\img_worker\\modules\\rembg_models', 'local_model_name': 'birefnet-general-lite', 'optionIMGTrans_type': 'GPU', 'detail_IMGTrans_type': 'GPU', 'thumb_trans_type': 'GPU', 'migan_use_accel': True, 'migan_onnx_path': 'D:\\py\\img_worker\\modules\\migan_onnx\\migan_pipeline_v2.onnx', 'ocr_provider_override': 'auto', 'migan_provider_override': 'auto', 'font_type': '폰트5', 'unwanted_texts': ['가격설명', '무료배송', '보증', '사은품', '세일', '이벤트', '특가', '품절', '할인', '할인가'], 'is_member_valid': False, 'authenticated_by_admin': True} +[2025-10-24 09:40:26,936] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 실행 직전: cmd=process_single_image +[2025-10-24 09:40:26,936] [LogListener] [DEBUG] [loggerModule.py:debug:275] process_single_image 호출 직전 +[2025-10-24 09:40:26,938] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 번역시작 +[2025-10-24 09:40:27,174] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 7 처리 시작: C:\ProgramData\ImgWorker\incoming\dl_535bc0a09648.jpg - 전체 번역 모드 +[2025-10-24 09:40:27,180] [LogListener] [DEBUG] [loggerModule.py:debug:275] 로컬 파일 경로 감지, 다운로드 생략: C:\ProgramData\ImgWorker\incoming\dl_535bc0a09648.jpg +[2025-10-24 09:40:27,180] [LogListener] [DEBUG] [loggerModule.py:debug:275] 옵션 이미지는 스케일 처리 건너뛰기: option +[2025-10-24 09:40:27,180] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 7 로컬 저장위치(옵션 이미지 원본 유지): C:\ProgramData\ImgWorker\incoming\dl_535bc0a09648.jpg +[2025-10-24 09:40:27,187] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔍 ONNX OCR 감지 방식: polygon +[2025-10-24 09:40:27,889] [LogListener] [DEBUG] [loggerModule.py:debug:275] ⚡ ONNX OCR 추론 완료: 700.0ms +[2025-10-24 09:40:27,889] [LogListener] [DEBUG] [loggerModule.py:debug:275] 📊 세부 시간 - 감지: 147.0ms, 인식: 524.0ms, 분류: 24.0ms +[2025-10-24 09:40:27,915] [LogListener] [DEBUG] [loggerModule.py:debug:275] 메모리 변화 [OCR 처리]: 42053.6MB -> 42315.5MB (+261.9MB, +0.6%) - 이미지 7 +[2025-10-24 09:40:27,931] [LogListener] [DEBUG] [loggerModule.py:debug:275] ocr_results: [{'text': '豆', 'confidence': 0.526243269443512, 'polygon': [[141.0, 37.0], [273.0, 35.0], [273.0, 64.0], [141.0, 66.0]], 'bbox': (141, 35, 133, 32), 'method': 'polygon'}, {'text': 'BAOZOUTU', 'confidence': 0.9928187131881714, 'polygon': [[134.0, 73.0], [274.0, 73.0], [274.0, 87.0], [134.0, 87.0]], 'bbox': (134, 73, 141, 15), 'method': 'polygon'}, {'text': '可门2 3丽', 'confidence': 0.5601375102996826, 'polygon': [[125.0, 155.0], [288.0, 157.0], [288.0, 195.0], [125.0, 193.0]], 'bbox': (125, 155, 164, 41), 'method': 'polygon'}, {'text': 'FM', 'confidence': 0.9797648787498474, 'polygon': [[452.0, 146.0], [464.0, 146.0], [464.0, 154.0], [452.0, 154.0]], 'bbox': (452, 146, 13, 9), 'method': 'polygon'}, {'text': 'AM', 'confidence': 0.9244250059127808, 'polygon': [[470.0, 146.0], [481.0, 146.0], [481.0, 155.0], [470.0, 155.0]], 'bbox': (470, 146, 12, 10), 'method': 'polygon'}, {'text': '150', 'confidence': 0.9816107153892517, 'polygon': [[469.0, 193.0], [482.0, 193.0], [482.0, 209.0], [469.0, 209.0]], 'bbox': (469, 193, 14, 17), 'method': 'polygon'}, {'text': '02', 'confidence': 0.988524317741394, 'polygon': [[453.0, 216.0], [463.0, 213.0], [466.0, 223.0], [456.0, 226.0]], 'bbox': (453, 213, 14, 14), 'method': 'polygon'}, {'text': '1400', 'confidence': 0.8128303289413452, 'polygon': [[470.0, 212.0], [483.0, 212.0], [483.0, 226.0], [470.0, 226.0]], 'bbox': (470, 212, 14, 15), 'method': 'polygon'}, {'text': '·R11', 'confidence': 0.8661974668502808, 'polygon': [[457.0, 245.0], [490.0, 245.0], [490.0, 256.0], [457.0, 256.0]], 'bbox': (457, 245, 34, 12), 'method': 'polygon'}, {'text': 'RADIO/BT/MUSIC', 'confidence': 0.9948214888572693, 'polygon': [[505.0, 246.0], [596.0, 246.0], [596.0, 256.0], [505.0, 256.0]], 'bbox': (505, 246, 92, 11), 'method': 'polygon'}, {'text': 'PLAYER', 'confidence': 0.9902213215827942, 'polygon': [[591.0, 243.0], [634.0, 245.0], [634.0, 256.0], [591.0, 254.0]], 'bbox': (591, 243, 44, 14), 'method': 'polygon'}, {'text': '4000mAh 2l喜 圳己2l', 'confidence': 0.7981270551681519, 'polygon': [[85.0, 281.0], [281.0, 281.0], [281.0, 301.0], [85.0, 301.0]], 'bbox': (85, 281, 197, 21), 'method': 'polygon'}, {'text': 'PER', 'confidence': 0.5230967402458191, 'polygon': [[261.0, 344.0], [291.0, 344.0], [291.0, 358.0], [261.0, 358.0]], 'bbox': (261, 344, 31, 15), 'method': 'polygon'}, {'text': '丽晟N / 百昆 SOS', 'confidence': 0.763592004776001, 'polygon': [[82.0, 398.0], [284.0, 400.0], [284.0, 424.0], [82.0, 422.0]], 'bbox': (82, 398, 203, 27), 'method': 'polygon'}, {'text': '三', 'confidence': 0.5126577019691467, 'polygon': [[85.0, 463.0], [123.0, 463.0], [123.0, 481.0], [85.0, 481.0]], 'bbox': (85, 463, 39, 19), 'method': 'polygon'}, {'text': '应易 是品 / E屁E', 'confidence': 0.8057035803794861, 'polygon': [[124.0, 461.0], [287.0, 461.0], [287.0, 482.0], [124.0, 482.0]], 'bbox': (124, 461, 164, 22), 'method': 'polygon'}, {'text': '慕走', 'confidence': 0.6546781659126282, 'polygon': [[537.0, 521.0], [584.0, 521.0], [584.0, 539.0], [537.0, 539.0]], 'bbox': (537, 521, 48, 19), 'method': 'polygon'}] +[2025-10-24 09:40:27,931] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 52.6%): '豆' +[2025-10-24 09:40:27,932] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): 'BAOZOUTU' +[2025-10-24 09:40:27,932] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 56.0%): '可门2 3丽' +[2025-10-24 09:40:27,932] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): 'FM' +[2025-10-24 09:40:27,932] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): 'AM' +[2025-10-24 09:40:27,932] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): '150' +[2025-10-24 09:40:27,932] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): '02' +[2025-10-24 09:40:27,932] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): '1400' +[2025-10-24 09:40:27,932] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): '·R11' +[2025-10-24 09:40:27,932] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): 'RADIO/BT/MUSIC' +[2025-10-24 09:40:27,932] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): 'PLAYER' +[2025-10-24 09:40:27,933] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 79.8%): '4000mAh 2l喜 圳己2l' +[2025-10-24 09:40:27,933] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): 'PER' +[2025-10-24 09:40:27,933] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 76.4%): '丽晟N / 百昆 SOS' +[2025-10-24 09:40:27,933] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 51.3%): '三' +[2025-10-24 09:40:27,933] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 80.6%): '应易 是品 / E屁E' +[2025-10-24 09:40:27,933] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 65.5%): '慕走' +[2025-10-24 09:40:27,933] [LogListener] [DEBUG] [loggerModule.py:debug:275] 필터링 결과: 7/17개 (신뢰도 + & 중국어) +[2025-10-24 09:40:27,933] [LogListener] [DEBUG] [loggerModule.py:debug:275] filter_ocr_results: [{'text': '豆', 'confidence': 0.526243269443512, 'polygon': [[141.0, 37.0], [273.0, 35.0], [273.0, 64.0], [141.0, 66.0]], 'bbox': (141, 35, 133, 32), 'method': 'polygon'}, {'text': '可门2 3丽', 'confidence': 0.5601375102996826, 'polygon': [[125.0, 155.0], [288.0, 157.0], [288.0, 195.0], [125.0, 193.0]], 'bbox': (125, 155, 164, 41), 'method': 'polygon'}, {'text': '4000mAh 2l喜 圳己2l', 'confidence': 0.7981270551681519, 'polygon': [[85.0, 281.0], [281.0, 281.0], [281.0, 301.0], [85.0, 301.0]], 'bbox': (85, 281, 197, 21), 'method': 'polygon'}, {'text': '丽晟N / 百昆 SOS', 'confidence': 0.763592004776001, 'polygon': [[82.0, 398.0], [284.0, 400.0], [284.0, 424.0], [82.0, 422.0]], 'bbox': (82, 398, 203, 27), 'method': 'polygon'}, {'text': '三', 'confidence': 0.5126577019691467, 'polygon': [[85.0, 463.0], [123.0, 463.0], [123.0, 481.0], [85.0, 481.0]], 'bbox': (85, 463, 39, 19), 'method': 'polygon'}, {'text': '应易 是品 / E屁E', 'confidence': 0.8057035803794861, 'polygon': [[124.0, 461.0], [287.0, 461.0], [287.0, 482.0], [124.0, 482.0]], 'bbox': (124, 461, 164, 22), 'method': 'polygon'}, {'text': '慕走', 'confidence': 0.6546781659126282, 'polygon': [[537.0, 521.0], [584.0, 521.0], [584.0, 539.0], [537.0, 539.0]], 'bbox': (537, 521, 48, 19), 'method': 'polygon'}] +[2025-10-24 09:40:27,933] [LogListener] [DEBUG] [loggerModule.py:debug:275] 중국어 텍스트 7개 필터링 완료 +[2025-10-24 09:40:27,933] [LogListener] [DEBUG] [loggerModule.py:debug:275] 한글 텍스트 0개 필터링 완료 +[2025-10-24 09:40:28,960] [LogListener] [DEBUG] [loggerModule.py:debug:275] translated_texts: ['콩', '케멘 2 3 리', '4000mAh 2l HI 젠지 2l', '리셍 N / 바이쿤 SOS', '삼', '잉이(Ying Yi) 제품입니다 / E 방귀 E', '무 주오'] +[2025-10-24 09:40:28,961] [LogListener] [DEBUG] [loggerModule.py:debug:275] 마스크 생성 완료 +[2025-10-24 09:40:28,961] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 7 OCR 권한 없음, 전체 번역 모드 +[2025-10-24 09:40:28,967] [LogListener] [DEBUG] [loggerModule.py:debug:275] [AUTO Inpaint] coverage=0.097, comps=6, min_center_dist=0.054 → request +[2025-10-24 09:40:28,967] [LogListener] [DEBUG] [loggerModule.py:debug:275] ocr_count: 7 +[2025-10-24 09:40:28,967] [LogListener] [DEBUG] [loggerModule.py:debug:275] is_member_valid: False +[2025-10-24 09:40:28,967] [LogListener] [DEBUG] [loggerModule.py:debug:275] MIGAN 인페인팅 시도 +[2025-10-24 09:40:28,974] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 입력 형태 - 이미지: (1, 3, 800, 800), 마스크: (1, 1, 800, 800) +[2025-10-24 09:40:29,316] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 출력 형태: (800, 800, 3), dtype: uint8 +[2025-10-24 09:40:29,316] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 추론 완료: 341.59 ms +[2025-10-24 09:40:29,324] [LogListener] [DEBUG] [loggerModule.py:debug:275] MIGAN 인페인팅 성공 +[2025-10-24 09:40:29,324] [LogListener] [DEBUG] [loggerModule.py:debug:275] 메모리 변화 [인페인팅]: 42337.9MB -> 42511.3MB (+173.4MB, +0.4%) - 방법: migan +[2025-10-24 09:40:29,324] [LogListener] [DEBUG] [loggerModule.py:debug:275] 인페인팅 완료 +[2025-10-24 09:40:29,355] [LogListener] [DEBUG] [loggerModule.py:debug:275] 텍스트 렌더링 완료 +[2025-10-24 09:40:29,356] [LogListener] [DEBUG] [loggerModule.py:debug:275] watermark_text: 이미지 저작권 보유 +[2025-10-24 09:40:29,356] [LogListener] [DEBUG] [loggerModule.py:debug:275] is_watermark_enabled: True +[2025-10-24 09:40:29,658] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 저장 완료 : C:\ProgramData\ImgWorker\work\translated_option_img_7.webp +[2025-10-24 09:40:29,659] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 7 번역 완료: C:\ProgramData\ImgWorker\work\translated_option_img_7.webp +[2025-10-24 09:40:29,659] [LogListener] [DEBUG] [loggerModule.py:debug:275] ⏱ 이미지 파이프라인 총 2720.9ms | download=0.0ms | ocr=715.0ms | translate=1030.2ms | mask=1030.2ms | inpaint=360.6ms(migan/DirectML) | render=31.0ms | save=272.0ms +[2025-10-24 09:40:29,660] [LogListener] [DEBUG] [loggerModule.py:debug:275] process_single_image 호출 완료 +[2025-10-24 09:40:29,660] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 결과 반환 중: uid=96199fb5-a3dc-491d-8a38-90de7bb82a7d +[2025-10-24 09:40:29,660] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 결과 반환 완료: uid=96199fb5-a3dc-491d-8a38-90de7bb82a7d +[2025-10-24 09:40:29,661] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 09:40:29,661] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 20016) +[2025-10-24 09:40:31,324] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔥 작업 수신 성공 +[2025-10-24 09:40:31,324] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🚀 작업 처리 시작: cmd=process_single_image, uid=5537caac-16ee-4cdd-8f12-b4faf66293ef +[2025-10-24 09:40:31,324] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 프로세서 toggle_states 업데이트 : {'TEMP_IMAGE_DIR': 'C:\\ProgramData\\ImgWorker\\work', 'output_image_format': 'webp', 'use_local_rembg': True, 'local_rembg_model_path': 'D:\\py\\img_worker\\modules\\rembg_models', 'local_model_name': 'birefnet-general-lite', 'optionIMGTrans_type': 'GPU', 'detail_IMGTrans_type': 'GPU', 'thumb_trans_type': 'GPU', 'migan_use_accel': True, 'migan_onnx_path': 'D:\\py\\img_worker\\modules\\migan_onnx\\migan_pipeline_v2.onnx', 'ocr_provider_override': 'auto', 'migan_provider_override': 'auto', 'font_type': '폰트5', 'unwanted_texts': ['가격설명', '무료배송', '보증', '사은품', '세일', '이벤트', '특가', '품절', '할인', '할인가'], 'is_member_valid': False, 'authenticated_by_admin': True} +[2025-10-24 09:40:31,325] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 실행 직전: cmd=process_single_image +[2025-10-24 09:40:31,325] [LogListener] [DEBUG] [loggerModule.py:debug:275] process_single_image 호출 직전 +[2025-10-24 09:40:31,325] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 번역시작 +[2025-10-24 09:40:31,603] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 8 처리 시작: C:\ProgramData\ImgWorker\incoming\dl_9064bc75ebcf.jpg - 전체 번역 모드 +[2025-10-24 09:40:31,608] [LogListener] [DEBUG] [loggerModule.py:debug:275] 로컬 파일 경로 감지, 다운로드 생략: C:\ProgramData\ImgWorker\incoming\dl_9064bc75ebcf.jpg +[2025-10-24 09:40:31,608] [LogListener] [DEBUG] [loggerModule.py:debug:275] 옵션 이미지는 스케일 처리 건너뛰기: option +[2025-10-24 09:40:31,608] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 8 로컬 저장위치(옵션 이미지 원본 유지): C:\ProgramData\ImgWorker\incoming\dl_9064bc75ebcf.jpg +[2025-10-24 09:40:31,610] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔍 ONNX OCR 감지 방식: polygon +[2025-10-24 09:40:32,153] [LogListener] [DEBUG] [loggerModule.py:debug:275] ⚡ ONNX OCR 추론 완료: 541.0ms +[2025-10-24 09:40:32,171] [LogListener] [DEBUG] [loggerModule.py:debug:275] 📊 세부 시간 - 감지: 98.0ms, 인식: 419.0ms, 분류: 18.0ms +[2025-10-24 09:40:32,182] [LogListener] [DEBUG] [loggerModule.py:debug:275] 메모리 변화 [OCR 처리]: 42592.3MB -> 42626.4MB (+34.1MB, +0.1%) - 이미지 8 +[2025-10-24 09:40:32,183] [LogListener] [DEBUG] [loggerModule.py:debug:275] ocr_results: [{'text': '暴走兔', 'confidence': 0.9268935322761536, 'polygon': [[136.0, 20.0], [271.0, 22.0], [270.0, 64.0], [135.0, 62.0]], 'bbox': (135, 20, 137, 45), 'method': 'polygon'}, {'text': 'BAOZOUTU', 'confidence': 0.9931837320327759, 'polygon': [[134.0, 73.0], [273.0, 73.0], [273.0, 87.0], [134.0, 87.0]], 'bbox': (134, 73, 140, 15), 'method': 'polygon'}, {'text': '全波段收音机', 'confidence': 0.9976252913475037, 'polygon': [[81.0, 158.0], [288.0, 156.0], [288.0, 184.0], [81.0, 186.0]], 'bbox': (81, 156, 208, 31), 'method': 'polygon'}, {'text': '.R12', 'confidence': 0.862068235874176, 'polygon': [[577.0, 152.0], [614.0, 152.0], [614.0, 163.0], [577.0, 163.0]], 'bbox': (577, 152, 38, 12), 'method': 'polygon'}, {'text': '518', 'confidence': 0.9833140969276428, 'polygon': [[488.0, 174.0], [607.0, 174.0], [607.0, 240.0], [488.0, 240.0]], 'bbox': (488, 174, 120, 67), 'method': 'polygon'}, {'text': '蓝牙音响', 'confidence': 0.9984031319618225, 'polygon': [[79.0, 213.0], [294.0, 213.0], [294.0, 248.0], [79.0, 248.0]], 'bbox': (79, 213, 216, 36), 'method': 'polygon'}, {'text': 'MULTIBANDRADIO/BT/MUSICPLAYER', 'confidence': 0.9822951555252075, 'polygon': [[464.0, 244.0], [623.0, 244.0], [623.0, 257.0], [464.0, 257.0]], 'bbox': (464, 244, 160, 14), 'method': 'polygon'}, {'text': '4000毫安锂电池', 'confidence': 0.9978502988815308, 'polygon': [[82.0, 279.0], [289.0, 279.0], [289.0, 303.0], [82.0, 303.0]], 'bbox': (82, 279, 208, 25), 'method': 'polygon'}, {'text': '手', 'confidence': 0.9999352693557739, 'polygon': [[80.0, 336.0], [116.0, 336.0], [116.0, 365.0], [80.0, 365.0]], 'bbox': (80, 336, 37, 30), 'method': 'polygon'}, {'text': '电', 'confidence': 0.9989959597587585, 'polygon': [[168.0, 336.0], [203.0, 336.0], [203.0, 365.0], [168.0, 365.0]], 'bbox': (168, 336, 36, 30), 'method': 'polygon'}, {'text': '筒', 'confidence': 0.9980816841125488, 'polygon': [[263.0, 336.0], [291.0, 336.0], [291.0, 365.0], [263.0, 365.0]], 'bbox': (263, 336, 29, 30), 'method': 'polygon'}, {'text': 'SOS警报/指南针', 'confidence': 0.9644944667816162, 'polygon': [[81.0, 397.0], [288.0, 397.0], [288.0, 425.0], [81.0, 425.0]], 'bbox': (81, 397, 208, 29), 'method': 'polygon'}, {'text': '黑走兔', 'confidence': 0.5847620368003845, 'polygon': [[531.0, 408.0], [597.0, 412.0], [595.0, 443.0], [529.0, 439.0]], 'bbox': (529, 408, 69, 36), 'method': 'polygon'}, {'text': '手摇/太阳能发电', 'confidence': 0.9910702705383301, 'polygon': [[83.0, 457.0], [289.0, 457.0], [289.0, 485.0], [83.0, 485.0]], 'bbox': (83, 457, 207, 29), 'method': 'polygon'}, {'text': '可插U盘/TF卡', 'confidence': 0.9913243055343628, 'polygon': [[81.0, 516.0], [290.0, 518.0], [290.0, 546.0], [81.0, 544.0]], 'bbox': (81, 516, 210, 31), 'method': 'polygon'}, {'text': '限时', 'confidence': 0.9980169534683228, 'polygon': [[18.0, 659.0], [151.0, 661.0], [150.0, 729.0], [17.0, 727.0]], 'bbox': (17, 659, 135, 71), 'method': 'polygon'}, {'text': '赠送', 'confidence': 0.9996706247329712, 'polygon': [[15.0, 728.0], [149.0, 728.0], [149.0, 792.0], [15.0, 792.0]], 'bbox': (15, 728, 135, 65), 'method': 'polygon'}, {'text': '延长天线', 'confidence': 0.9980688095092773, 'polygon': [[211.0, 769.0], [288.0, 769.0], [288.0, 790.0], [211.0, 790.0]], 'bbox': (211, 769, 78, 22), 'method': 'polygon'}, {'text': '内存卡', 'confidence': 0.9976118206977844, 'polygon': [[323.0, 766.0], [384.0, 766.0], [384.0, 791.0], [323.0, 791.0]], 'bbox': (323, 766, 62, 26), 'method': 'polygon'}, {'text': '读卡器', 'confidence': 0.998539388179779, 'polygon': [[423.0, 767.0], [483.0, 767.0], [483.0, 792.0], [423.0, 792.0]], 'bbox': (423, 767, 61, 26), 'method': 'polygon'}, {'text': '充电线', 'confidence': 0.9986271858215332, 'polygon': [[522.0, 767.0], [583.0, 767.0], [583.0, 792.0], [522.0, 792.0]], 'bbox': (522, 767, 62, 26), 'method': 'polygon'}, {'text': '礼盒礼袋', 'confidence': 0.9983878135681152, 'polygon': [[619.0, 768.0], [697.0, 768.0], [697.0, 791.0], [619.0, 791.0]], 'bbox': (619, 768, 79, 24), 'method': 'polygon'}, {'text': '充电头', 'confidence': 0.9977946281433105, 'polygon': [[726.0, 767.0], [786.0, 767.0], [786.0, 792.0], [726.0, 792.0]], 'bbox': (726, 767, 61, 26), 'method': 'polygon'}] +[2025-10-24 09:40:32,184] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 92.7%): '暴走兔' +[2025-10-24 09:40:32,184] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): 'BAOZOUTU' +[2025-10-24 09:40:32,184] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.8%): '全波段收音机' +[2025-10-24 09:40:32,184] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): '.R12' +[2025-10-24 09:40:32,184] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): '518' +[2025-10-24 09:40:32,184] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.8%): '蓝牙音响' +[2025-10-24 09:40:32,184] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): 'MULTIBANDRADIO/BT/MUSICPLAYER' +[2025-10-24 09:40:32,184] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.8%): '4000毫安锂电池' +[2025-10-24 09:40:32,184] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 100.0%): '手' +[2025-10-24 09:40:32,185] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.9%): '电' +[2025-10-24 09:40:32,185] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.8%): '筒' +[2025-10-24 09:40:32,185] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 96.4%): 'SOS警报/指南针' +[2025-10-24 09:40:32,185] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 58.5%): '黑走兔' +[2025-10-24 09:40:32,185] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.1%): '手摇/太阳能发电' +[2025-10-24 09:40:32,185] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.1%): '可插U盘/TF卡' +[2025-10-24 09:40:32,185] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.8%): '限时' +[2025-10-24 09:40:32,185] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 100.0%): '赠送' +[2025-10-24 09:40:32,186] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.8%): '延长天线' +[2025-10-24 09:40:32,186] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.8%): '内存卡' +[2025-10-24 09:40:32,186] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.9%): '读卡器' +[2025-10-24 09:40:32,186] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.9%): '充电线' +[2025-10-24 09:40:32,186] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.8%): '礼盒礼袋' +[2025-10-24 09:40:32,186] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.8%): '充电头' +[2025-10-24 09:40:32,186] [LogListener] [DEBUG] [loggerModule.py:debug:275] 필터링 결과: 19/23개 (신뢰도 + & 중국어) +[2025-10-24 09:40:32,186] [LogListener] [DEBUG] [loggerModule.py:debug:275] filter_ocr_results: [{'text': '暴走兔', 'confidence': 0.9268935322761536, 'polygon': [[136.0, 20.0], [271.0, 22.0], [270.0, 64.0], [135.0, 62.0]], 'bbox': (135, 20, 137, 45), 'method': 'polygon'}, {'text': '全波段收音机', 'confidence': 0.9976252913475037, 'polygon': [[81.0, 158.0], [288.0, 156.0], [288.0, 184.0], [81.0, 186.0]], 'bbox': (81, 156, 208, 31), 'method': 'polygon'}, {'text': '蓝牙音响', 'confidence': 0.9984031319618225, 'polygon': [[79.0, 213.0], [294.0, 213.0], [294.0, 248.0], [79.0, 248.0]], 'bbox': (79, 213, 216, 36), 'method': 'polygon'}, {'text': '4000毫安锂电池', 'confidence': 0.9978502988815308, 'polygon': [[82.0, 279.0], [289.0, 279.0], [289.0, 303.0], [82.0, 303.0]], 'bbox': (82, 279, 208, 25), 'method': 'polygon'}, {'text': '手', 'confidence': 0.9999352693557739, 'polygon': [[80.0, 336.0], [116.0, 336.0], [116.0, 365.0], [80.0, 365.0]], 'bbox': (80, 336, 37, 30), 'method': 'polygon'}, {'text': '电', 'confidence': 0.9989959597587585, 'polygon': [[168.0, 336.0], [203.0, 336.0], [203.0, 365.0], [168.0, 365.0]], 'bbox': (168, 336, 36, 30), 'method': 'polygon'}, {'text': '筒', 'confidence': 0.9980816841125488, 'polygon': [[263.0, 336.0], [291.0, 336.0], [291.0, 365.0], [263.0, 365.0]], 'bbox': (263, 336, 29, 30), 'method': 'polygon'}, {'text': 'SOS警报/指南针', 'confidence': 0.9644944667816162, 'polygon': [[81.0, 397.0], [288.0, 397.0], [288.0, 425.0], [81.0, 425.0]], 'bbox': (81, 397, 208, 29), 'method': 'polygon'}, {'text': '黑走兔', 'confidence': 0.5847620368003845, 'polygon': [[531.0, 408.0], [597.0, 412.0], [595.0, 443.0], [529.0, 439.0]], 'bbox': (529, 408, 69, 36), 'method': 'polygon'}, {'text': '手摇/太阳能发电', 'confidence': 0.9910702705383301, 'polygon': [[83.0, 457.0], [289.0, 457.0], [289.0, 485.0], [83.0, 485.0]], 'bbox': (83, 457, 207, 29), 'method': 'polygon'}, {'text': '可插U盘/TF卡', 'confidence': 0.9913243055343628, 'polygon': [[81.0, 516.0], [290.0, 518.0], [290.0, 546.0], [81.0, 544.0]], 'bbox': (81, 516, 210, 31), 'method': 'polygon'}, {'text': '限时', 'confidence': 0.9980169534683228, 'polygon': [[18.0, 659.0], [151.0, 661.0], [150.0, 729.0], [17.0, 727.0]], 'bbox': (17, 659, 135, 71), 'method': 'polygon'}, {'text': '赠送', 'confidence': 0.9996706247329712, 'polygon': [[15.0, 728.0], [149.0, 728.0], [149.0, 792.0], [15.0, 792.0]], 'bbox': (15, 728, 135, 65), 'method': 'polygon'}, {'text': '延长天线', 'confidence': 0.9980688095092773, 'polygon': [[211.0, 769.0], [288.0, 769.0], [288.0, 790.0], [211.0, 790.0]], 'bbox': (211, 769, 78, 22), 'method': 'polygon'}, {'text': '内存卡', 'confidence': 0.9976118206977844, 'polygon': [[323.0, 766.0], [384.0, 766.0], [384.0, 791.0], [323.0, 791.0]], 'bbox': (323, 766, 62, 26), 'method': 'polygon'}, {'text': '读卡器', 'confidence': 0.998539388179779, 'polygon': [[423.0, 767.0], [483.0, 767.0], [483.0, 792.0], [423.0, 792.0]], 'bbox': (423, 767, 61, 26), 'method': 'polygon'}, {'text': '充电线', 'confidence': 0.9986271858215332, 'polygon': [[522.0, 767.0], [583.0, 767.0], [583.0, 792.0], [522.0, 792.0]], 'bbox': (522, 767, 62, 26), 'method': 'polygon'}, {'text': '礼盒礼袋', 'confidence': 0.9983878135681152, 'polygon': [[619.0, 768.0], [697.0, 768.0], [697.0, 791.0], [619.0, 791.0]], 'bbox': (619, 768, 79, 24), 'method': 'polygon'}, {'text': '充电头', 'confidence': 0.9977946281433105, 'polygon': [[726.0, 767.0], [786.0, 767.0], [786.0, 792.0], [726.0, 792.0]], 'bbox': (726, 767, 61, 26), 'method': 'polygon'}] +[2025-10-24 09:40:32,187] [LogListener] [DEBUG] [loggerModule.py:debug:275] 중국어 텍스트 19개 필터링 완료 +[2025-10-24 09:40:32,187] [LogListener] [DEBUG] [loggerModule.py:debug:275] 한글 텍스트 0개 필터링 완료 +[2025-10-24 09:40:33,492] [LogListener] [DEBUG] [loggerModule.py:debug:275] translated_texts: ['도망친 토끼', '풀 밴드 라디오', '블루투스 스피커', '4000mAh 리튬 배터리', '손', '전기', '실린더', 'SOS 경보 / 나침반', '검은 토끼', '핸드 크랭크 / 태양광 발전', 'U 디스크 삽입 가능 / TF 카드', '제한된 시간', '포기하다', '확장 안테나', '메모리 카드', '카드 리더', '충전 케이블', '선물 상자 선물 가방', '충전 헤드'] +[2025-10-24 09:40:33,493] [LogListener] [DEBUG] [loggerModule.py:debug:275] 마스크 생성 완료 +[2025-10-24 09:40:33,493] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 8 OCR 권한 없음, 전체 번역 모드 +[2025-10-24 09:40:33,501] [LogListener] [DEBUG] [loggerModule.py:debug:275] [AUTO Inpaint] coverage=0.211, comps=18, min_center_dist=0.053 → request +[2025-10-24 09:40:33,502] [LogListener] [DEBUG] [loggerModule.py:debug:275] ocr_count: 19 +[2025-10-24 09:40:33,502] [LogListener] [DEBUG] [loggerModule.py:debug:275] is_member_valid: False +[2025-10-24 09:40:33,502] [LogListener] [DEBUG] [loggerModule.py:debug:275] MIGAN 인페인팅 시도 +[2025-10-24 09:40:33,504] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 입력 형태 - 이미지: (1, 3, 800, 800), 마스크: (1, 1, 800, 800) +[2025-10-24 09:40:33,710] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 출력 형태: (800, 800, 3), dtype: uint8 +[2025-10-24 09:40:33,711] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 추론 완료: 207.00 ms +[2025-10-24 09:40:33,719] [LogListener] [DEBUG] [loggerModule.py:debug:275] MIGAN 인페인팅 성공 +[2025-10-24 09:40:33,719] [LogListener] [DEBUG] [loggerModule.py:debug:275] 메모리 변화 [인페인팅]: 42571.6MB -> 42595.0MB (+23.4MB, +0.1%) - 방법: migan +[2025-10-24 09:40:33,719] [LogListener] [DEBUG] [loggerModule.py:debug:275] 인페인팅 완료 +[2025-10-24 09:40:33,782] [LogListener] [DEBUG] [loggerModule.py:debug:275] 텍스트 렌더링 완료 +[2025-10-24 09:40:33,783] [LogListener] [DEBUG] [loggerModule.py:debug:275] watermark_text: 이미지 저작권 보유 +[2025-10-24 09:40:33,783] [LogListener] [DEBUG] [loggerModule.py:debug:275] is_watermark_enabled: True +[2025-10-24 09:40:34,090] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 저장 완료 : C:\ProgramData\ImgWorker\work\translated_option_img_8.webp +[2025-10-24 09:40:34,091] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 8 번역 완료: C:\ProgramData\ImgWorker\work\translated_option_img_8.webp +[2025-10-24 09:40:34,092] [LogListener] [DEBUG] [loggerModule.py:debug:275] ⏱ 이미지 파이프라인 총 2764.4ms | download=0.0ms | ocr=550.0ms | translate=1309.5ms | mask=1309.5ms | inpaint=223.0ms(migan/DirectML) | render=64.0ms | save=277.0ms +[2025-10-24 09:40:34,092] [LogListener] [DEBUG] [loggerModule.py:debug:275] process_single_image 호출 완료 +[2025-10-24 09:40:34,093] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 결과 반환 중: uid=5537caac-16ee-4cdd-8f12-b4faf66293ef +[2025-10-24 09:40:34,093] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 결과 반환 완료: uid=5537caac-16ee-4cdd-8f12-b4faf66293ef +[2025-10-24 09:40:34,093] [ResultListener] [WARNING] [loggerModule.py:warning:287] 워커 롤링 스케줄: reason=job-count-threshold +[2025-10-24 09:40:34,093] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 09:40:34,093] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 20016) +[2025-10-24 09:40:34,093] [WorkerRoller] [WARNING] [loggerModule.py:warning:287] 워커 롤링 시작: job-count-threshold +[2025-10-24 09:40:34,108] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔥 작업 수신 성공 +[2025-10-24 09:40:34,142] [LogListener] [DEBUG] [loggerModule.py:debug:275] Shutdown signal 수신 → 종료 +[2025-10-24 09:40:34,142] [LogListener] [DEBUG] [loggerModule.py:debug:275] OCR 모듈 정리 완료 +[2025-10-24 09:40:34,142] [LogListener] [DEBUG] [loggerModule.py:debug:275] 마스크 모듈 정리 완료 +[2025-10-24 09:40:34,143] [LogListener] [DEBUG] [loggerModule.py:debug:275] 임시 폴더 삭제됨: C:\ProgramData\ImgWorker\work +[2025-10-24 09:40:34,143] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 프로세서 소멸 +[2025-10-24 09:40:34,612] [WorkerRoller] [INFO] [loggerModule.py:info:281] 워커 프로세스 기동: PID=17188 +[2025-10-24 09:40:35,120] [LogListener] [DEBUG] [loggerModule.py:debug:275] ImageWorker 프로세스 기동 (PID=17188, Name=ImageWorkerProcess) +[2025-10-24 09:40:35,121] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔧 ImageProcessor3 초기화 시작... +[2025-10-24 09:40:35,121] [LogListener] [DEBUG] [loggerModule.py:debug:275] tracemalloc 메모리 추적 시작 +[2025-10-24 09:40:35,128] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ DirectML 환경 준비 완료 (Windows DirectX 12 기반) +[2025-10-24 09:40:35,129] [LogListener] [DEBUG] [loggerModule.py:debug:275] === 🚀 DirectML GPU 상태 초기화 시작 🚀 === +[2025-10-24 09:40:35,129] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🎯 사용자 GPU 가속 요청: False +[2025-10-24 09:40:35,129] [LogListener] [DEBUG] [loggerModule.py:debug:275] 💻 현재 운영체제: Windows +[2025-10-24 09:40:35,129] [LogListener] [DEBUG] [loggerModule.py:debug:275] GPU 가속이 비활성화됨 (toggle_states['use_cuda'] = False) +[2025-10-24 09:40:35,129] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔒 안전한 CPU 모드로 모든 GPU 설정 강제 비활성화 +[2025-10-24 09:40:35,130] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔧 ImageProcessor3 GPU 상태 요약: +[2025-10-24 09:40:35,130] [LogListener] [DEBUG] [loggerModule.py:debug:275] - CUDA 사용 가능: False +[2025-10-24 09:40:35,130] [LogListener] [DEBUG] [loggerModule.py:debug:275] - toggle_states['use_cuda']: NOT_SET +[2025-10-24 09:40:35,130] [LogListener] [DEBUG] [loggerModule.py:debug:275] - GPU 하드웨어 정보: {} +[2025-10-24 09:40:35,130] [LogListener] [DEBUG] [loggerModule.py:debug:275] ImageProcessor3 Init toggle_states: {'TEMP_IMAGE_DIR': 'C:\\ProgramData\\ImgWorker\\work', 'output_image_format': 'webp', 'use_local_rembg': True, 'local_rembg_model_path': 'D:\\py\\img_worker\\modules\\rembg_models', 'local_model_name': 'birefnet-general-lite', 'optionIMGTrans_type': 'CPU', 'detail_IMGTrans_type': 'CPU', 'thumb_trans_type': 'CPU', 'migan_use_accel': True, 'migan_onnx_path': 'D:\\py\\img_worker\\modules\\migan_onnx\\migan_pipeline_v2.onnx', 'ocr_provider_override': 'auto', 'migan_provider_override': 'auto'} +[2025-10-24 09:40:35,130] [LogListener] [DEBUG] [loggerModule.py:debug:275] is_member_valid: False +[2025-10-24 09:40:35,130] [LogListener] [DEBUG] [loggerModule.py:debug:275] debug_images 디렉토리 이미 존재: D:\py\img_worker\modules\debug_images +[2025-10-24 09:40:35,130] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.font_path: D:\py\img_worker\modules\fonts\HakgyoansimDunggeunmisoTTFB.ttf +[2025-10-24 09:40:35,130] [LogListener] [DEBUG] [loggerModule.py:debug:275] toggle_states font_path: D:\py\img_worker\modules\fonts\HakgyoansimDunggeunmisoTTFB.ttf +[2025-10-24 09:40:35,131] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.TEMP_IMAGE_DIR: C:\ProgramData\ImgWorker\work +[2025-10-24 09:40:35,131] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.debugging_save_Dir: D:\py\img_worker\modules\debug_images +[2025-10-24 09:40:35,131] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.unwanted_texts: {} +[2025-10-24 09:40:35,131] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.inpaint_method: migan +[2025-10-24 09:40:35,131] [LogListener] [DEBUG] [loggerModule.py:debug:275] Image.MAX_IMAGE_PIXELS set to 20000000 +[2025-10-24 09:40:35,131] [LogListener] [DEBUG] [loggerModule.py:debug:275] ONNX 모델 타입 설정값: 자동 선택, GPU 정보: {} +[2025-10-24 09:40:35,131] [LogListener] [DEBUG] [loggerModule.py:debug:275] 자동 선택 모드: GPU 추천 모델 simp 사용 +[2025-10-24 09:40:35,131] [LogListener] [DEBUG] [loggerModule.py:debug:275] ONNX OCR 모델 타입 결정: simp (GPU: False) +[2025-10-24 09:40:35,131] [LogListener] [DEBUG] [loggerModule.py:debug:275] ONNX OCR 모듈 CPU 모드로 설정 +[2025-10-24 09:40:35,131] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🚀 ONNX TextSystem 초기화 시작 (CPU 모드) +[2025-10-24 09:40:35,131] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🚀 ONNX TextSystem 초기화 시작 (CPU 모드) +[2025-10-24 09:40:36,685] [LogListener] [DEBUG] [loggerModule.py:debug:275] 문자 사전 파일 UTF-8 확인 완료: D:\py\img_worker\modules\onnx_ocr_module\dict\ppocr_keys_v1.txt +[2025-10-24 09:40:36,769] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ ONNX TextSystem 초기화 완료 (CPU + SIMP (호환성) 모델) +[2025-10-24 09:40:36,769] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ ONNX TextSystem 초기화 완료 (CPU + SIMP 모델) +[2025-10-24 09:40:36,769] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ ONNX OCR 모듈 초기화 성공 (CPU 모드) +[2025-10-24 09:40:36,770] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ ONNX OCR 모듈 초기화 성공 +[2025-10-24 09:40:36,770] [LogListener] [DEBUG] [loggerModule.py:debug:275] 마스크 모듈 초기화 완료 +[2025-10-24 09:40:36,770] [LogListener] [DEBUG] [loggerModule.py:debug:275] MaskModule 초기화 성공 +[2025-10-24 09:40:36,770] [LogListener] [DEBUG] [loggerModule.py:debug:275] 텍스트 렌더링 모듈 초기화 완료 +[2025-10-24 09:40:36,770] [LogListener] [DEBUG] [loggerModule.py:debug:275] 기본 폰트: D:\py\img_worker\modules\fonts\HakgyoansimDunggeunmisoTTFB.ttf +[2025-10-24 09:40:36,771] [LogListener] [DEBUG] [loggerModule.py:debug:275] TextRenderingModule 초기화 성공 +[2025-10-24 09:40:36,771] [LogListener] [DEBUG] [loggerModule.py:debug:275] 폰트 로드 성공: D:\py\img_worker\modules\fonts\HakgyoansimDunggeunmisoTTFB.ttf +[2025-10-24 09:40:36,771] [LogListener] [DEBUG] [loggerModule.py:debug:275] PostImageManager 초기화 성공 +[2025-10-24 09:40:36,771] [LogListener] [DEBUG] [loggerModule.py:debug:275] Request_AI_Server GPU 상태: CUDA 사용 가능=False +[2025-10-24 09:40:36,771] [LogListener] [DEBUG] [loggerModule.py:debug:275] Request_AI_Server 초기화 성공 +[2025-10-24 09:40:36,771] [LogListener] [DEBUG] [loggerModule.py:debug:275] GoogleTranslate 초기화 성공 +[2025-10-24 09:40:36,771] [LogListener] [DEBUG] [loggerModule.py:debug:275] MIGAN CUDA 사용 불가 - CPU 모드로 설정 +[2025-10-24 09:40:36,771] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] GPU 관리자 전달: GPUManager, can_use_cuda: False +[2025-10-24 09:40:36,995] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] GPU 관리자 사용 가능: False +[2025-10-24 09:40:36,995] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 사용 가능한 providers: ['DmlExecutionProvider', 'CPUExecutionProvider'] +[2025-10-24 09:40:36,995] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] DirectML 가속 활성화 +[2025-10-24 09:40:36,995] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 최종 providers: [('DmlExecutionProvider', {}), ('CPUExecutionProvider', {})] +[2025-10-24 09:40:36,995] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] DirectML 가속 시도: [('DmlExecutionProvider', {}), ('CPUExecutionProvider', {})] +[2025-10-24 09:40:36,996] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] DirectML 가속 성공! 실제 providers: ['DmlExecutionProvider', 'CPUExecutionProvider'] +[2025-10-24 09:40:36,996] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 입력 0: image, 형태: ['batch_size', 3, 'height', 'width'], 타입: tensor(uint8) +[2025-10-24 09:40:36,996] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 입력 1: mask, 형태: ['batch_size', 1, 'height', 'width'], 타입: tensor(uint8) +[2025-10-24 09:40:36,996] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 출력 0: result, 형태: ['ScatterNDresult_dim_0', 3, 'ScatterNDresult_dim_2', 'ScatterNDresult_dim_3'], 타입: tensor(uint8) +[2025-10-24 09:40:36,996] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 세션 준비 완료. providers=['DmlExecutionProvider', 'CPUExecutionProvider'] +[2025-10-24 09:40:36,996] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] GPU 관리자 연결 완료: GPUManager +[2025-10-24 09:40:36,996] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 파이프라인 gpu_manager 속성: True, 값: +[2025-10-24 09:40:36,996] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 초기화 완료: gpu_manager 속성=True, 값= +[2025-10-24 09:40:36,996] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔰 OCR 모듈 Warm-up 시작... +[2025-10-24 09:40:36,996] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔍 ONNX OCR 감지 방식: polygon +[2025-10-24 09:40:37,001] [LogListener] [DEBUG] [loggerModule.py:debug:275] ⚡ ONNX OCR 추론 완료: 4.0ms +[2025-10-24 09:40:37,001] [LogListener] [DEBUG] [loggerModule.py:debug:275] 📊 세부 시간 - 감지: 3.0ms, 인식: 0.0ms, 분류: 0.0ms +[2025-10-24 09:40:37,001] [LogListener] [WARNING] [loggerModule.py:warning:287] ⚠️ ONNX OCR 결과가 비어있습니다 +[2025-10-24 09:40:37,004] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ OCR 모듈 Warm-up 성공 +[2025-10-24 09:40:37,005] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔰 ImageProcessor Warm‑up 완료 +[2025-10-24 09:40:37,005] [ResultListener] [INFO] [loggerModule.py:info:281] 워커 READY 수신 +[2025-10-24 09:40:37,005] [WorkerRoller] [INFO] [loggerModule.py:info:281] 버퍼 작업 재개: 1건 +[2025-10-24 09:40:37,005] [LogListener] [DEBUG] [loggerModule.py:debug:275] 📡 추가 READY 신호 전송 완료 +[2025-10-24 09:40:37,005] [ResultListener] [INFO] [loggerModule.py:info:281] 워커 READY 수신 +[2025-10-24 09:40:37,005] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 09:40:37,005] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 17188) +[2025-10-24 09:40:37,006] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔥 작업 수신 성공 +[2025-10-24 09:40:37,006] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🚀 작업 처리 시작: cmd=process_single_image, uid=d9f9610a-0dea-402f-a54e-7e1a52d19ce7 +[2025-10-24 09:40:37,006] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 프로세서 toggle_states 업데이트 : {'TEMP_IMAGE_DIR': 'C:\\ProgramData\\ImgWorker\\work', 'output_image_format': 'webp', 'use_local_rembg': True, 'local_rembg_model_path': 'D:\\py\\img_worker\\modules\\rembg_models', 'local_model_name': 'birefnet-general-lite', 'optionIMGTrans_type': 'GPU', 'detail_IMGTrans_type': 'GPU', 'thumb_trans_type': 'GPU', 'migan_use_accel': True, 'migan_onnx_path': 'D:\\py\\img_worker\\modules\\migan_onnx\\migan_pipeline_v2.onnx', 'ocr_provider_override': 'auto', 'migan_provider_override': 'auto', 'font_type': '폰트5', 'unwanted_texts': ['가격설명', '무료배송', '보증', '사은품', '세일', '이벤트', '특가', '품절', '할인', '할인가'], 'is_member_valid': False, 'authenticated_by_admin': True} +[2025-10-24 09:40:37,006] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 실행 직전: cmd=process_single_image +[2025-10-24 09:40:37,006] [LogListener] [DEBUG] [loggerModule.py:debug:275] process_single_image 호출 직전 +[2025-10-24 09:40:37,008] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 번역시작 +[2025-10-24 09:40:37,214] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 9 처리 시작: C:\ProgramData\ImgWorker\incoming\dl_935b456a7fd9.jpg - 전체 번역 모드 +[2025-10-24 09:40:37,221] [LogListener] [DEBUG] [loggerModule.py:debug:275] 로컬 파일 경로 감지, 다운로드 생략: C:\ProgramData\ImgWorker\incoming\dl_935b456a7fd9.jpg +[2025-10-24 09:40:37,221] [LogListener] [DEBUG] [loggerModule.py:debug:275] 옵션 이미지는 스케일 처리 건너뛰기: option +[2025-10-24 09:40:37,221] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 9 로컬 저장위치(옵션 이미지 원본 유지): C:\ProgramData\ImgWorker\incoming\dl_935b456a7fd9.jpg +[2025-10-24 09:40:37,223] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔍 ONNX OCR 감지 방식: polygon +[2025-10-24 09:40:37,836] [LogListener] [DEBUG] [loggerModule.py:debug:275] ⚡ ONNX OCR 추론 완료: 610.6ms +[2025-10-24 09:40:37,836] [LogListener] [DEBUG] [loggerModule.py:debug:275] 📊 세부 시간 - 감지: 186.0ms, 인식: 399.0ms, 분류: 19.5ms +[2025-10-24 09:40:37,853] [LogListener] [DEBUG] [loggerModule.py:debug:275] 메모리 변화 [OCR 처리]: 42244.0MB -> 42444.4MB (+200.4MB, +0.5%) - 이미지 9 +[2025-10-24 09:40:37,863] [LogListener] [DEBUG] [loggerModule.py:debug:275] ocr_results: [{'text': '暴走兔', 'confidence': 0.9204323291778564, 'polygon': [[135.0, 20.0], [271.0, 22.0], [270.0, 64.0], [134.0, 62.0]], 'bbox': (134, 20, 138, 45), 'method': 'polygon'}, {'text': 'BAOZOUTU', 'confidence': 0.993258535861969, 'polygon': [[134.0, 73.0], [273.0, 73.0], [273.0, 87.0], [134.0, 87.0]], 'bbox': (134, 73, 140, 15), 'method': 'polygon'}, {'text': '全波段收音机', 'confidence': 0.9974920749664307, 'polygon': [[81.0, 158.0], [288.0, 156.0], [288.0, 184.0], [81.0, 186.0]], 'bbox': (81, 156, 208, 31), 'method': 'polygon'}, {'text': '.R12', 'confidence': 0.8123915791511536, 'polygon': [[579.0, 152.0], [614.0, 152.0], [614.0, 163.0], [579.0, 163.0]], 'bbox': (579, 152, 36, 12), 'method': 'polygon'}, {'text': '518', 'confidence': 0.9942043423652649, 'polygon': [[487.0, 175.0], [606.0, 175.0], [606.0, 240.0], [487.0, 240.0]], 'bbox': (487, 175, 120, 66), 'method': 'polygon'}, {'text': '蓝牙音响', 'confidence': 0.9985915422439575, 'polygon': [[79.0, 214.0], [294.0, 214.0], [294.0, 248.0], [79.0, 248.0]], 'bbox': (79, 214, 216, 35), 'method': 'polygon'}, {'text': 'H', 'confidence': 0.7371025681495667, 'polygon': [[607.0, 228.0], [622.0, 228.0], [622.0, 238.0], [607.0, 238.0]], 'bbox': (607, 228, 16, 11), 'method': 'polygon'}, {'text': 'MULTIBANDRADIO/BT/MUSICPLAYER', 'confidence': 0.9846541881561279, 'polygon': [[464.0, 243.0], [623.0, 244.0], [623.0, 258.0], [464.0, 257.0]], 'bbox': (464, 243, 160, 16), 'method': 'polygon'}, {'text': '4000毫安锂电池', 'confidence': 0.9973577857017517, 'polygon': [[83.0, 279.0], [289.0, 279.0], [289.0, 303.0], [83.0, 303.0]], 'bbox': (83, 279, 207, 25), 'method': 'polygon'}, {'text': '手', 'confidence': 0.9999414682388306, 'polygon': [[80.0, 335.0], [116.0, 335.0], [116.0, 365.0], [80.0, 365.0]], 'bbox': (80, 335, 37, 31), 'method': 'polygon'}, {'text': '电', 'confidence': 0.9994295239448547, 'polygon': [[169.0, 338.0], [202.0, 338.0], [202.0, 364.0], [169.0, 364.0]], 'bbox': (169, 338, 34, 27), 'method': 'polygon'}, {'text': '筒', 'confidence': 0.9992778897285461, 'polygon': [[263.0, 336.0], [291.0, 336.0], [291.0, 365.0], [263.0, 365.0]], 'bbox': (263, 336, 29, 30), 'method': 'polygon'}, {'text': 'SOS警报/指南针', 'confidence': 0.9694313406944275, 'polygon': [[81.0, 397.0], [288.0, 397.0], [288.0, 425.0], [81.0, 425.0]], 'bbox': (81, 397, 208, 29), 'method': 'polygon'}, {'text': '手摇/太阳能发电', 'confidence': 0.9908651113510132, 'polygon': [[83.0, 457.0], [289.0, 457.0], [289.0, 485.0], [83.0, 485.0]], 'bbox': (83, 457, 207, 29), 'method': 'polygon'}, {'text': '可插U盘/TF卡', 'confidence': 0.9926060438156128, 'polygon': [[81.0, 516.0], [290.0, 518.0], [290.0, 546.0], [81.0, 544.0]], 'bbox': (81, 516, 210, 31), 'method': 'polygon'}, {'text': '限时', 'confidence': 0.9982489347457886, 'polygon': [[18.0, 659.0], [150.0, 661.0], [149.0, 729.0], [17.0, 727.0]], 'bbox': (17, 659, 134, 71), 'method': 'polygon'}, {'text': '赠送', 'confidence': 0.999593198299408, 'polygon': [[16.0, 728.0], [149.0, 728.0], [149.0, 792.0], [16.0, 792.0]], 'bbox': (16, 728, 134, 65), 'method': 'polygon'}, {'text': '延长天线', 'confidence': 0.99787837266922, 'polygon': [[211.0, 769.0], [288.0, 769.0], [288.0, 790.0], [211.0, 790.0]], 'bbox': (211, 769, 78, 22), 'method': 'polygon'}, {'text': '内存卡', 'confidence': 0.9972688555717468, 'polygon': [[323.0, 766.0], [384.0, 766.0], [384.0, 791.0], [323.0, 791.0]], 'bbox': (323, 766, 62, 26), 'method': 'polygon'}, {'text': '读卡器', 'confidence': 0.9982621669769287, 'polygon': [[423.0, 767.0], [483.0, 767.0], [483.0, 792.0], [423.0, 792.0]], 'bbox': (423, 767, 61, 26), 'method': 'polygon'}, {'text': '充电线', 'confidence': 0.9985806345939636, 'polygon': [[522.0, 767.0], [583.0, 767.0], [583.0, 792.0], [522.0, 792.0]], 'bbox': (522, 767, 62, 26), 'method': 'polygon'}, {'text': '礼盒礼袋', 'confidence': 0.9981387257575989, 'polygon': [[619.0, 768.0], [697.0, 768.0], [697.0, 791.0], [619.0, 791.0]], 'bbox': (619, 768, 79, 24), 'method': 'polygon'}, {'text': '充电头', 'confidence': 0.9978719353675842, 'polygon': [[726.0, 767.0], [786.0, 767.0], [786.0, 792.0], [726.0, 792.0]], 'bbox': (726, 767, 61, 26), 'method': 'polygon'}] +[2025-10-24 09:40:37,885] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 92.0%): '暴走兔' +[2025-10-24 09:40:37,886] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): 'BAOZOUTU' +[2025-10-24 09:40:37,886] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.7%): '全波段收音机' +[2025-10-24 09:40:37,886] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): '.R12' +[2025-10-24 09:40:37,886] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): '518' +[2025-10-24 09:40:37,886] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.9%): '蓝牙音响' +[2025-10-24 09:40:37,886] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): 'H' +[2025-10-24 09:40:37,886] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): 'MULTIBANDRADIO/BT/MUSICPLAYER' +[2025-10-24 09:40:37,886] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.7%): '4000毫安锂电池' +[2025-10-24 09:40:37,886] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 100.0%): '手' +[2025-10-24 09:40:37,886] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.9%): '电' +[2025-10-24 09:40:37,886] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.9%): '筒' +[2025-10-24 09:40:37,886] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 96.9%): 'SOS警报/指南针' +[2025-10-24 09:40:37,886] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.1%): '手摇/太阳能发电' +[2025-10-24 09:40:37,887] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.3%): '可插U盘/TF卡' +[2025-10-24 09:40:37,887] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.8%): '限时' +[2025-10-24 09:40:37,887] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 100.0%): '赠送' +[2025-10-24 09:40:37,887] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.8%): '延长天线' +[2025-10-24 09:40:37,887] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.7%): '内存卡' +[2025-10-24 09:40:37,887] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.8%): '读卡器' +[2025-10-24 09:40:37,887] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.9%): '充电线' +[2025-10-24 09:40:37,887] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.8%): '礼盒礼袋' +[2025-10-24 09:40:37,887] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.8%): '充电头' +[2025-10-24 09:40:37,894] [LogListener] [DEBUG] [loggerModule.py:debug:275] 필터링 결과: 18/23개 (신뢰도 + & 중국어) +[2025-10-24 09:40:37,894] [LogListener] [DEBUG] [loggerModule.py:debug:275] filter_ocr_results: [{'text': '暴走兔', 'confidence': 0.9204323291778564, 'polygon': [[135.0, 20.0], [271.0, 22.0], [270.0, 64.0], [134.0, 62.0]], 'bbox': (134, 20, 138, 45), 'method': 'polygon'}, {'text': '全波段收音机', 'confidence': 0.9974920749664307, 'polygon': [[81.0, 158.0], [288.0, 156.0], [288.0, 184.0], [81.0, 186.0]], 'bbox': (81, 156, 208, 31), 'method': 'polygon'}, {'text': '蓝牙音响', 'confidence': 0.9985915422439575, 'polygon': [[79.0, 214.0], [294.0, 214.0], [294.0, 248.0], [79.0, 248.0]], 'bbox': (79, 214, 216, 35), 'method': 'polygon'}, {'text': '4000毫安锂电池', 'confidence': 0.9973577857017517, 'polygon': [[83.0, 279.0], [289.0, 279.0], [289.0, 303.0], [83.0, 303.0]], 'bbox': (83, 279, 207, 25), 'method': 'polygon'}, {'text': '手', 'confidence': 0.9999414682388306, 'polygon': [[80.0, 335.0], [116.0, 335.0], [116.0, 365.0], [80.0, 365.0]], 'bbox': (80, 335, 37, 31), 'method': 'polygon'}, {'text': '电', 'confidence': 0.9994295239448547, 'polygon': [[169.0, 338.0], [202.0, 338.0], [202.0, 364.0], [169.0, 364.0]], 'bbox': (169, 338, 34, 27), 'method': 'polygon'}, {'text': '筒', 'confidence': 0.9992778897285461, 'polygon': [[263.0, 336.0], [291.0, 336.0], [291.0, 365.0], [263.0, 365.0]], 'bbox': (263, 336, 29, 30), 'method': 'polygon'}, {'text': 'SOS警报/指南针', 'confidence': 0.9694313406944275, 'polygon': [[81.0, 397.0], [288.0, 397.0], [288.0, 425.0], [81.0, 425.0]], 'bbox': (81, 397, 208, 29), 'method': 'polygon'}, {'text': '手摇/太阳能发电', 'confidence': 0.9908651113510132, 'polygon': [[83.0, 457.0], [289.0, 457.0], [289.0, 485.0], [83.0, 485.0]], 'bbox': (83, 457, 207, 29), 'method': 'polygon'}, {'text': '可插U盘/TF卡', 'confidence': 0.9926060438156128, 'polygon': [[81.0, 516.0], [290.0, 518.0], [290.0, 546.0], [81.0, 544.0]], 'bbox': (81, 516, 210, 31), 'method': 'polygon'}, {'text': '限时', 'confidence': 0.9982489347457886, 'polygon': [[18.0, 659.0], [150.0, 661.0], [149.0, 729.0], [17.0, 727.0]], 'bbox': (17, 659, 134, 71), 'method': 'polygon'}, {'text': '赠送', 'confidence': 0.999593198299408, 'polygon': [[16.0, 728.0], [149.0, 728.0], [149.0, 792.0], [16.0, 792.0]], 'bbox': (16, 728, 134, 65), 'method': 'polygon'}, {'text': '延长天线', 'confidence': 0.99787837266922, 'polygon': [[211.0, 769.0], [288.0, 769.0], [288.0, 790.0], [211.0, 790.0]], 'bbox': (211, 769, 78, 22), 'method': 'polygon'}, {'text': '内存卡', 'confidence': 0.9972688555717468, 'polygon': [[323.0, 766.0], [384.0, 766.0], [384.0, 791.0], [323.0, 791.0]], 'bbox': (323, 766, 62, 26), 'method': 'polygon'}, {'text': '读卡器', 'confidence': 0.9982621669769287, 'polygon': [[423.0, 767.0], [483.0, 767.0], [483.0, 792.0], [423.0, 792.0]], 'bbox': (423, 767, 61, 26), 'method': 'polygon'}, {'text': '充电线', 'confidence': 0.9985806345939636, 'polygon': [[522.0, 767.0], [583.0, 767.0], [583.0, 792.0], [522.0, 792.0]], 'bbox': (522, 767, 62, 26), 'method': 'polygon'}, {'text': '礼盒礼袋', 'confidence': 0.9981387257575989, 'polygon': [[619.0, 768.0], [697.0, 768.0], [697.0, 791.0], [619.0, 791.0]], 'bbox': (619, 768, 79, 24), 'method': 'polygon'}, {'text': '充电头', 'confidence': 0.9978719353675842, 'polygon': [[726.0, 767.0], [786.0, 767.0], [786.0, 792.0], [726.0, 792.0]], 'bbox': (726, 767, 61, 26), 'method': 'polygon'}] +[2025-10-24 09:40:37,910] [LogListener] [DEBUG] [loggerModule.py:debug:275] 중국어 텍스트 18개 필터링 완료 +[2025-10-24 09:40:37,925] [LogListener] [DEBUG] [loggerModule.py:debug:275] 한글 텍스트 0개 필터링 완료 +[2025-10-24 09:40:38,301] [LogListener] [DEBUG] [loggerModule.py:debug:275] translated_texts: ['도망친 토끼', '풀 밴드 라디오', '블루투스 스피커', '4000mAh 리튬 배터리', '손', '전기', '실린더', 'SOS 경보 / 나침반', '핸드 크랭크 / 태양광 발전', 'U 디스크 삽입 가능 / TF 카드', '제한된 시간', '포기하다', '확장 안테나', '메모리 카드', '카드 리더', '충전 케이블', '선물 상자 선물 가방', '충전 헤드'] +[2025-10-24 09:40:38,301] [LogListener] [DEBUG] [loggerModule.py:debug:275] 마스크 생성 완료 +[2025-10-24 09:40:38,301] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 9 OCR 권한 없음, 전체 번역 모드 +[2025-10-24 09:40:38,310] [LogListener] [DEBUG] [loggerModule.py:debug:275] [AUTO Inpaint] coverage=0.203, comps=17, min_center_dist=0.053 → request +[2025-10-24 09:40:38,310] [LogListener] [DEBUG] [loggerModule.py:debug:275] ocr_count: 18 +[2025-10-24 09:40:38,310] [LogListener] [DEBUG] [loggerModule.py:debug:275] is_member_valid: False +[2025-10-24 09:40:38,310] [LogListener] [DEBUG] [loggerModule.py:debug:275] MIGAN 인페인팅 시도 +[2025-10-24 09:40:38,312] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 입력 형태 - 이미지: (1, 3, 800, 800), 마스크: (1, 1, 800, 800) +[2025-10-24 09:40:38,660] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 출력 형태: (800, 800, 3), dtype: uint8 +[2025-10-24 09:40:38,661] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 추론 완료: 348.00 ms +[2025-10-24 09:40:38,668] [LogListener] [DEBUG] [loggerModule.py:debug:275] MIGAN 인페인팅 성공 +[2025-10-24 09:40:38,669] [LogListener] [DEBUG] [loggerModule.py:debug:275] 메모리 변화 [인페인팅]: 42457.0MB -> 42634.1MB (+177.1MB, +0.4%) - 방법: migan +[2025-10-24 09:40:38,669] [LogListener] [DEBUG] [loggerModule.py:debug:275] 인페인팅 완료 +[2025-10-24 09:40:38,738] [LogListener] [DEBUG] [loggerModule.py:debug:275] 텍스트 렌더링 완료 +[2025-10-24 09:40:38,739] [LogListener] [DEBUG] [loggerModule.py:debug:275] watermark_text: 이미지 저작권 보유 +[2025-10-24 09:40:38,739] [LogListener] [DEBUG] [loggerModule.py:debug:275] is_watermark_enabled: True +[2025-10-24 09:40:39,049] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 저장 완료 : C:\ProgramData\ImgWorker\work\translated_option_img_9.webp +[2025-10-24 09:40:39,050] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 9 번역 완료: C:\ProgramData\ImgWorker\work\translated_option_img_9.webp +[2025-10-24 09:40:39,050] [LogListener] [DEBUG] [loggerModule.py:debug:275] ⏱ 이미지 파이프라인 총 2043.1ms | download=0.0ms | ocr=622.6ms | translate=414.7ms | mask=414.7ms | inpaint=364.0ms(migan/DirectML) | render=70.0ms | save=270.0ms +[2025-10-24 09:40:39,051] [LogListener] [DEBUG] [loggerModule.py:debug:275] process_single_image 호출 완료 +[2025-10-24 09:40:39,051] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 결과 반환 중: uid=d9f9610a-0dea-402f-a54e-7e1a52d19ce7 +[2025-10-24 09:40:39,052] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 결과 반환 완료: uid=d9f9610a-0dea-402f-a54e-7e1a52d19ce7 +[2025-10-24 09:40:39,052] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 09:40:39,052] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 17188) +[2025-10-24 09:40:40,931] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔥 작업 수신 성공 +[2025-10-24 09:40:40,931] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🚀 작업 처리 시작: cmd=process_single_image, uid=f55ac318-c9b4-4e99-98c4-d5bdac5be388 +[2025-10-24 09:40:40,931] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 프로세서 toggle_states 업데이트 : {'TEMP_IMAGE_DIR': 'C:\\ProgramData\\ImgWorker\\work', 'output_image_format': 'webp', 'use_local_rembg': True, 'local_rembg_model_path': 'D:\\py\\img_worker\\modules\\rembg_models', 'local_model_name': 'birefnet-general-lite', 'optionIMGTrans_type': 'GPU', 'detail_IMGTrans_type': 'GPU', 'thumb_trans_type': 'GPU', 'migan_use_accel': True, 'migan_onnx_path': 'D:\\py\\img_worker\\modules\\migan_onnx\\migan_pipeline_v2.onnx', 'ocr_provider_override': 'auto', 'migan_provider_override': 'auto', 'font_type': '폰트5', 'unwanted_texts': ['가격설명', '무료배송', '보증', '사은품', '세일', '이벤트', '특가', '품절', '할인', '할인가'], 'is_member_valid': False, 'authenticated_by_admin': True} +[2025-10-24 09:40:40,932] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 실행 직전: cmd=process_single_image +[2025-10-24 09:40:40,932] [LogListener] [DEBUG] [loggerModule.py:debug:275] process_single_image 호출 직전 +[2025-10-24 09:40:40,932] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 번역시작 +[2025-10-24 09:40:41,324] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 10 처리 시작: C:\ProgramData\ImgWorker\incoming\dl_f6b6cf4d0d08.jpg - 전체 번역 모드 +[2025-10-24 09:40:41,330] [LogListener] [DEBUG] [loggerModule.py:debug:275] 로컬 파일 경로 감지, 다운로드 생략: C:\ProgramData\ImgWorker\incoming\dl_f6b6cf4d0d08.jpg +[2025-10-24 09:40:41,330] [LogListener] [DEBUG] [loggerModule.py:debug:275] 옵션 이미지는 스케일 처리 건너뛰기: option +[2025-10-24 09:40:41,330] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 10 로컬 저장위치(옵션 이미지 원본 유지): C:\ProgramData\ImgWorker\incoming\dl_f6b6cf4d0d08.jpg +[2025-10-24 09:40:41,331] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔍 ONNX OCR 감지 방식: polygon +[2025-10-24 09:40:41,840] [LogListener] [DEBUG] [loggerModule.py:debug:275] ⚡ ONNX OCR 추론 완료: 505.7ms +[2025-10-24 09:40:41,867] [LogListener] [DEBUG] [loggerModule.py:debug:275] 📊 세부 시간 - 감지: 101.1ms, 인식: 382.6ms, 분류: 17.0ms +[2025-10-24 09:40:41,867] [LogListener] [DEBUG] [loggerModule.py:debug:275] 메모리 변화 [OCR 처리]: 42801.6MB -> 42790.8MB (-10.8MB, -0.0%) - 이미지 10 +[2025-10-24 09:40:41,867] [LogListener] [DEBUG] [loggerModule.py:debug:275] ocr_results: [{'text': '暴走兔', 'confidence': 0.9204323291778564, 'polygon': [[135.0, 20.0], [271.0, 22.0], [270.0, 64.0], [134.0, 62.0]], 'bbox': (134, 20, 138, 45), 'method': 'polygon'}, {'text': 'BAOZOUTU', 'confidence': 0.9933307766914368, 'polygon': [[134.0, 73.0], [273.0, 73.0], [273.0, 87.0], [134.0, 87.0]], 'bbox': (134, 73, 140, 15), 'method': 'polygon'}, {'text': '全波段收音机', 'confidence': 0.9974386096000671, 'polygon': [[81.0, 158.0], [288.0, 156.0], [288.0, 184.0], [81.0, 186.0]], 'bbox': (81, 156, 208, 31), 'method': 'polygon'}, {'text': '..R12', 'confidence': 0.7863626480102539, 'polygon': [[575.0, 152.0], [614.0, 152.0], [614.0, 163.0], [575.0, 163.0]], 'bbox': (575, 152, 40, 12), 'method': 'polygon'}, {'text': '518', 'confidence': 0.9843118190765381, 'polygon': [[487.0, 175.0], [607.0, 175.0], [607.0, 240.0], [487.0, 240.0]], 'bbox': (487, 175, 121, 66), 'method': 'polygon'}, {'text': '蓝牙音响', 'confidence': 0.9985583424568176, 'polygon': [[79.0, 214.0], [294.0, 214.0], [294.0, 248.0], [79.0, 248.0]], 'bbox': (79, 214, 216, 35), 'method': 'polygon'}, {'text': 'MULTIBANDRADIO/BT/MUSICPLAYER', 'confidence': 0.9749076962471008, 'polygon': [[463.0, 243.0], [623.0, 244.0], [623.0, 258.0], [463.0, 257.0]], 'bbox': (463, 243, 161, 16), 'method': 'polygon'}, {'text': '4000毫安锂电池', 'confidence': 0.997395396232605, 'polygon': [[83.0, 279.0], [289.0, 279.0], [289.0, 303.0], [83.0, 303.0]], 'bbox': (83, 279, 207, 25), 'method': 'polygon'}, {'text': '手', 'confidence': 0.9999408721923828, 'polygon': [[80.0, 335.0], [117.0, 335.0], [117.0, 365.0], [80.0, 365.0]], 'bbox': (80, 335, 38, 31), 'method': 'polygon'}, {'text': '电', 'confidence': 0.9994295239448547, 'polygon': [[169.0, 338.0], [202.0, 338.0], [202.0, 364.0], [169.0, 364.0]], 'bbox': (169, 338, 34, 27), 'method': 'polygon'}, {'text': '筒', 'confidence': 0.9992778897285461, 'polygon': [[263.0, 336.0], [291.0, 336.0], [291.0, 365.0], [263.0, 365.0]], 'bbox': (263, 336, 29, 30), 'method': 'polygon'}, {'text': 'SOS警报/指南针', 'confidence': 0.9678227305412292, 'polygon': [[81.0, 397.0], [288.0, 397.0], [288.0, 425.0], [81.0, 425.0]], 'bbox': (81, 397, 208, 29), 'method': 'polygon'}, {'text': '手摇/太阳能发电', 'confidence': 0.9910251498222351, 'polygon': [[83.0, 457.0], [289.0, 457.0], [289.0, 485.0], [83.0, 485.0]], 'bbox': (83, 457, 207, 29), 'method': 'polygon'}, {'text': '可插U盘/TF卡', 'confidence': 0.9926397204399109, 'polygon': [[81.0, 516.0], [290.0, 518.0], [290.0, 546.0], [81.0, 544.0]], 'bbox': (81, 516, 210, 31), 'method': 'polygon'}, {'text': '限时', 'confidence': 0.9982489347457886, 'polygon': [[18.0, 659.0], [150.0, 661.0], [149.0, 729.0], [17.0, 727.0]], 'bbox': (17, 659, 134, 71), 'method': 'polygon'}, {'text': '赠送', 'confidence': 0.999593198299408, 'polygon': [[16.0, 728.0], [149.0, 728.0], [149.0, 792.0], [16.0, 792.0]], 'bbox': (16, 728, 134, 65), 'method': 'polygon'}, {'text': '延长天线', 'confidence': 0.997862696647644, 'polygon': [[211.0, 769.0], [288.0, 769.0], [288.0, 790.0], [211.0, 790.0]], 'bbox': (211, 769, 78, 22), 'method': 'polygon'}, {'text': '内存卡', 'confidence': 0.9972650408744812, 'polygon': [[323.0, 766.0], [384.0, 766.0], [384.0, 791.0], [323.0, 791.0]], 'bbox': (323, 766, 62, 26), 'method': 'polygon'}, {'text': '读卡器', 'confidence': 0.9983422756195068, 'polygon': [[423.0, 767.0], [483.0, 767.0], [483.0, 792.0], [423.0, 792.0]], 'bbox': (423, 767, 61, 26), 'method': 'polygon'}, {'text': '充电线', 'confidence': 0.9985806345939636, 'polygon': [[522.0, 767.0], [583.0, 767.0], [583.0, 792.0], [522.0, 792.0]], 'bbox': (522, 767, 62, 26), 'method': 'polygon'}, {'text': '礼盒礼袋', 'confidence': 0.9976491332054138, 'polygon': [[619.0, 768.0], [697.0, 768.0], [697.0, 791.0], [619.0, 791.0]], 'bbox': (619, 768, 79, 24), 'method': 'polygon'}, {'text': '充电头', 'confidence': 0.9978719353675842, 'polygon': [[726.0, 767.0], [786.0, 767.0], [786.0, 792.0], [726.0, 792.0]], 'bbox': (726, 767, 61, 26), 'method': 'polygon'}] +[2025-10-24 09:40:41,868] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 92.0%): '暴走兔' +[2025-10-24 09:40:41,868] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): 'BAOZOUTU' +[2025-10-24 09:40:41,868] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.7%): '全波段收音机' +[2025-10-24 09:40:41,868] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): '..R12' +[2025-10-24 09:40:41,868] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): '518' +[2025-10-24 09:40:41,868] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.9%): '蓝牙音响' +[2025-10-24 09:40:41,868] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): 'MULTIBANDRADIO/BT/MUSICPLAYER' +[2025-10-24 09:40:41,868] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.7%): '4000毫安锂电池' +[2025-10-24 09:40:41,868] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 100.0%): '手' +[2025-10-24 09:40:41,869] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.9%): '电' +[2025-10-24 09:40:41,869] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.9%): '筒' +[2025-10-24 09:40:41,869] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 96.8%): 'SOS警报/指南针' +[2025-10-24 09:40:41,869] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.1%): '手摇/太阳能发电' +[2025-10-24 09:40:41,869] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.3%): '可插U盘/TF卡' +[2025-10-24 09:40:41,869] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.8%): '限时' +[2025-10-24 09:40:41,869] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 100.0%): '赠送' +[2025-10-24 09:40:41,869] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.8%): '延长天线' +[2025-10-24 09:40:41,869] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.7%): '内存卡' +[2025-10-24 09:40:41,869] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.8%): '读卡器' +[2025-10-24 09:40:41,869] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.9%): '充电线' +[2025-10-24 09:40:41,869] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.8%): '礼盒礼袋' +[2025-10-24 09:40:41,869] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.8%): '充电头' +[2025-10-24 09:40:41,869] [LogListener] [DEBUG] [loggerModule.py:debug:275] 필터링 결과: 18/22개 (신뢰도 + & 중국어) +[2025-10-24 09:40:41,869] [LogListener] [DEBUG] [loggerModule.py:debug:275] filter_ocr_results: [{'text': '暴走兔', 'confidence': 0.9204323291778564, 'polygon': [[135.0, 20.0], [271.0, 22.0], [270.0, 64.0], [134.0, 62.0]], 'bbox': (134, 20, 138, 45), 'method': 'polygon'}, {'text': '全波段收音机', 'confidence': 0.9974386096000671, 'polygon': [[81.0, 158.0], [288.0, 156.0], [288.0, 184.0], [81.0, 186.0]], 'bbox': (81, 156, 208, 31), 'method': 'polygon'}, {'text': '蓝牙音响', 'confidence': 0.9985583424568176, 'polygon': [[79.0, 214.0], [294.0, 214.0], [294.0, 248.0], [79.0, 248.0]], 'bbox': (79, 214, 216, 35), 'method': 'polygon'}, {'text': '4000毫安锂电池', 'confidence': 0.997395396232605, 'polygon': [[83.0, 279.0], [289.0, 279.0], [289.0, 303.0], [83.0, 303.0]], 'bbox': (83, 279, 207, 25), 'method': 'polygon'}, {'text': '手', 'confidence': 0.9999408721923828, 'polygon': [[80.0, 335.0], [117.0, 335.0], [117.0, 365.0], [80.0, 365.0]], 'bbox': (80, 335, 38, 31), 'method': 'polygon'}, {'text': '电', 'confidence': 0.9994295239448547, 'polygon': [[169.0, 338.0], [202.0, 338.0], [202.0, 364.0], [169.0, 364.0]], 'bbox': (169, 338, 34, 27), 'method': 'polygon'}, {'text': '筒', 'confidence': 0.9992778897285461, 'polygon': [[263.0, 336.0], [291.0, 336.0], [291.0, 365.0], [263.0, 365.0]], 'bbox': (263, 336, 29, 30), 'method': 'polygon'}, {'text': 'SOS警报/指南针', 'confidence': 0.9678227305412292, 'polygon': [[81.0, 397.0], [288.0, 397.0], [288.0, 425.0], [81.0, 425.0]], 'bbox': (81, 397, 208, 29), 'method': 'polygon'}, {'text': '手摇/太阳能发电', 'confidence': 0.9910251498222351, 'polygon': [[83.0, 457.0], [289.0, 457.0], [289.0, 485.0], [83.0, 485.0]], 'bbox': (83, 457, 207, 29), 'method': 'polygon'}, {'text': '可插U盘/TF卡', 'confidence': 0.9926397204399109, 'polygon': [[81.0, 516.0], [290.0, 518.0], [290.0, 546.0], [81.0, 544.0]], 'bbox': (81, 516, 210, 31), 'method': 'polygon'}, {'text': '限时', 'confidence': 0.9982489347457886, 'polygon': [[18.0, 659.0], [150.0, 661.0], [149.0, 729.0], [17.0, 727.0]], 'bbox': (17, 659, 134, 71), 'method': 'polygon'}, {'text': '赠送', 'confidence': 0.999593198299408, 'polygon': [[16.0, 728.0], [149.0, 728.0], [149.0, 792.0], [16.0, 792.0]], 'bbox': (16, 728, 134, 65), 'method': 'polygon'}, {'text': '延长天线', 'confidence': 0.997862696647644, 'polygon': [[211.0, 769.0], [288.0, 769.0], [288.0, 790.0], [211.0, 790.0]], 'bbox': (211, 769, 78, 22), 'method': 'polygon'}, {'text': '内存卡', 'confidence': 0.9972650408744812, 'polygon': [[323.0, 766.0], [384.0, 766.0], [384.0, 791.0], [323.0, 791.0]], 'bbox': (323, 766, 62, 26), 'method': 'polygon'}, {'text': '读卡器', 'confidence': 0.9983422756195068, 'polygon': [[423.0, 767.0], [483.0, 767.0], [483.0, 792.0], [423.0, 792.0]], 'bbox': (423, 767, 61, 26), 'method': 'polygon'}, {'text': '充电线', 'confidence': 0.9985806345939636, 'polygon': [[522.0, 767.0], [583.0, 767.0], [583.0, 792.0], [522.0, 792.0]], 'bbox': (522, 767, 62, 26), 'method': 'polygon'}, {'text': '礼盒礼袋', 'confidence': 0.9976491332054138, 'polygon': [[619.0, 768.0], [697.0, 768.0], [697.0, 791.0], [619.0, 791.0]], 'bbox': (619, 768, 79, 24), 'method': 'polygon'}, {'text': '充电头', 'confidence': 0.9978719353675842, 'polygon': [[726.0, 767.0], [786.0, 767.0], [786.0, 792.0], [726.0, 792.0]], 'bbox': (726, 767, 61, 26), 'method': 'polygon'}] +[2025-10-24 09:40:41,870] [LogListener] [DEBUG] [loggerModule.py:debug:275] 중국어 텍스트 18개 필터링 완료 +[2025-10-24 09:40:41,870] [LogListener] [DEBUG] [loggerModule.py:debug:275] 한글 텍스트 0개 필터링 완료 +[2025-10-24 09:40:41,875] [LogListener] [DEBUG] [loggerModule.py:debug:275] translated_texts: ['도망친 토끼', '풀 밴드 라디오', '블루투스 스피커', '4000mAh 리튬 배터리', '손', '전기', '실린더', 'SOS 경보 / 나침반', '핸드 크랭크 / 태양광 발전', 'U 디스크 삽입 가능 / TF 카드', '제한된 시간', '포기하다', '확장 안테나', '메모리 카드', '카드 리더', '충전 케이블', '선물 상자 선물 가방', '충전 헤드'] +[2025-10-24 09:40:41,876] [LogListener] [DEBUG] [loggerModule.py:debug:275] 마스크 생성 완료 +[2025-10-24 09:40:41,876] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 10 OCR 권한 없음, 전체 번역 모드 +[2025-10-24 09:40:41,884] [LogListener] [DEBUG] [loggerModule.py:debug:275] [AUTO Inpaint] coverage=0.203, comps=17, min_center_dist=0.053 → request +[2025-10-24 09:40:41,884] [LogListener] [DEBUG] [loggerModule.py:debug:275] ocr_count: 18 +[2025-10-24 09:40:41,884] [LogListener] [DEBUG] [loggerModule.py:debug:275] is_member_valid: False +[2025-10-24 09:40:41,884] [LogListener] [DEBUG] [loggerModule.py:debug:275] MIGAN 인페인팅 시도 +[2025-10-24 09:40:41,886] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 입력 형태 - 이미지: (1, 3, 800, 800), 마스크: (1, 1, 800, 800) +[2025-10-24 09:40:42,056] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 출력 형태: (800, 800, 3), dtype: uint8 +[2025-10-24 09:40:42,056] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 추론 완료: 169.00 ms +[2025-10-24 09:40:42,064] [LogListener] [DEBUG] [loggerModule.py:debug:275] MIGAN 인페인팅 성공 +[2025-10-24 09:40:42,064] [LogListener] [DEBUG] [loggerModule.py:debug:275] 메모리 변화 [인페인팅]: 42766.2MB -> 42762.2MB (-4.1MB, -0.0%) - 방법: migan +[2025-10-24 09:40:42,064] [LogListener] [DEBUG] [loggerModule.py:debug:275] 인페인팅 완료 +[2025-10-24 09:40:42,121] [LogListener] [DEBUG] [loggerModule.py:debug:275] 텍스트 렌더링 완료 +[2025-10-24 09:40:42,122] [LogListener] [DEBUG] [loggerModule.py:debug:275] watermark_text: 이미지 저작권 보유 +[2025-10-24 09:40:42,122] [LogListener] [DEBUG] [loggerModule.py:debug:275] is_watermark_enabled: True +[2025-10-24 09:40:42,477] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 저장 완료 : C:\ProgramData\ImgWorker\work\translated_option_img_10.webp +[2025-10-24 09:40:42,479] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 10 번역 완료: C:\ProgramData\ImgWorker\work\translated_option_img_10.webp +[2025-10-24 09:40:42,479] [LogListener] [DEBUG] [loggerModule.py:debug:275] ⏱ 이미지 파이프라인 총 1545.1ms | download=0.0ms | ocr=515.7ms | translate=8.0ms | mask=8.0ms | inpaint=186.0ms(migan/DirectML) | render=57.0ms | save=310.0ms +[2025-10-24 09:40:42,480] [LogListener] [DEBUG] [loggerModule.py:debug:275] process_single_image 호출 완료 +[2025-10-24 09:40:42,480] [ResultListener] [WARNING] [loggerModule.py:warning:287] 워커 롤링 스케줄: reason=job-count-threshold +[2025-10-24 09:40:42,480] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 결과 반환 중: uid=f55ac318-c9b4-4e99-98c4-d5bdac5be388 +[2025-10-24 09:40:42,480] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 결과 반환 완료: uid=f55ac318-c9b4-4e99-98c4-d5bdac5be388 +[2025-10-24 09:40:42,480] [WorkerRoller] [WARNING] [loggerModule.py:warning:287] 워커 롤링 시작: job-count-threshold +[2025-10-24 09:40:42,481] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 09:40:42,481] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 17188) +[2025-10-24 09:40:42,496] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔥 작업 수신 성공 +[2025-10-24 09:40:42,547] [LogListener] [DEBUG] [loggerModule.py:debug:275] Shutdown signal 수신 → 종료 +[2025-10-24 09:40:42,547] [LogListener] [DEBUG] [loggerModule.py:debug:275] OCR 모듈 정리 완료 +[2025-10-24 09:40:42,547] [LogListener] [DEBUG] [loggerModule.py:debug:275] 마스크 모듈 정리 완료 +[2025-10-24 09:40:42,548] [LogListener] [DEBUG] [loggerModule.py:debug:275] 임시 폴더 삭제됨: C:\ProgramData\ImgWorker\work +[2025-10-24 09:40:42,548] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 프로세서 소멸 +[2025-10-24 09:40:43,094] [WorkerRoller] [INFO] [loggerModule.py:info:281] 워커 프로세스 기동: PID=7640 +[2025-10-24 09:40:43,632] [LogListener] [DEBUG] [loggerModule.py:debug:275] ImageWorker 프로세스 기동 (PID=7640, Name=ImageWorkerProcess) +[2025-10-24 09:40:43,633] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔧 ImageProcessor3 초기화 시작... +[2025-10-24 09:40:43,633] [LogListener] [DEBUG] [loggerModule.py:debug:275] tracemalloc 메모리 추적 시작 +[2025-10-24 09:40:43,639] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ DirectML 환경 준비 완료 (Windows DirectX 12 기반) +[2025-10-24 09:40:43,639] [LogListener] [DEBUG] [loggerModule.py:debug:275] === 🚀 DirectML GPU 상태 초기화 시작 🚀 === +[2025-10-24 09:40:43,640] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🎯 사용자 GPU 가속 요청: False +[2025-10-24 09:40:43,640] [LogListener] [DEBUG] [loggerModule.py:debug:275] 💻 현재 운영체제: Windows +[2025-10-24 09:40:43,640] [LogListener] [DEBUG] [loggerModule.py:debug:275] GPU 가속이 비활성화됨 (toggle_states['use_cuda'] = False) +[2025-10-24 09:40:43,640] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔒 안전한 CPU 모드로 모든 GPU 설정 강제 비활성화 +[2025-10-24 09:40:43,640] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔧 ImageProcessor3 GPU 상태 요약: +[2025-10-24 09:40:43,640] [LogListener] [DEBUG] [loggerModule.py:debug:275] - CUDA 사용 가능: False +[2025-10-24 09:40:43,640] [LogListener] [DEBUG] [loggerModule.py:debug:275] - toggle_states['use_cuda']: NOT_SET +[2025-10-24 09:40:43,640] [LogListener] [DEBUG] [loggerModule.py:debug:275] - GPU 하드웨어 정보: {} +[2025-10-24 09:40:43,641] [LogListener] [DEBUG] [loggerModule.py:debug:275] ImageProcessor3 Init toggle_states: {'TEMP_IMAGE_DIR': 'C:\\ProgramData\\ImgWorker\\work', 'output_image_format': 'webp', 'use_local_rembg': True, 'local_rembg_model_path': 'D:\\py\\img_worker\\modules\\rembg_models', 'local_model_name': 'birefnet-general-lite', 'optionIMGTrans_type': 'CPU', 'detail_IMGTrans_type': 'CPU', 'thumb_trans_type': 'CPU', 'migan_use_accel': True, 'migan_onnx_path': 'D:\\py\\img_worker\\modules\\migan_onnx\\migan_pipeline_v2.onnx', 'ocr_provider_override': 'auto', 'migan_provider_override': 'auto'} +[2025-10-24 09:40:43,641] [LogListener] [DEBUG] [loggerModule.py:debug:275] is_member_valid: False +[2025-10-24 09:40:43,641] [LogListener] [DEBUG] [loggerModule.py:debug:275] debug_images 디렉토리 이미 존재: D:\py\img_worker\modules\debug_images +[2025-10-24 09:40:43,641] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.font_path: D:\py\img_worker\modules\fonts\HakgyoansimDunggeunmisoTTFB.ttf +[2025-10-24 09:40:43,641] [LogListener] [DEBUG] [loggerModule.py:debug:275] toggle_states font_path: D:\py\img_worker\modules\fonts\HakgyoansimDunggeunmisoTTFB.ttf +[2025-10-24 09:40:43,641] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.TEMP_IMAGE_DIR: C:\ProgramData\ImgWorker\work +[2025-10-24 09:40:43,641] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.debugging_save_Dir: D:\py\img_worker\modules\debug_images +[2025-10-24 09:40:43,641] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.unwanted_texts: {} +[2025-10-24 09:40:43,641] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.inpaint_method: migan +[2025-10-24 09:40:43,641] [LogListener] [DEBUG] [loggerModule.py:debug:275] Image.MAX_IMAGE_PIXELS set to 20000000 +[2025-10-24 09:40:43,641] [LogListener] [DEBUG] [loggerModule.py:debug:275] ONNX 모델 타입 설정값: 자동 선택, GPU 정보: {} +[2025-10-24 09:40:43,641] [LogListener] [DEBUG] [loggerModule.py:debug:275] 자동 선택 모드: GPU 추천 모델 simp 사용 +[2025-10-24 09:40:43,641] [LogListener] [DEBUG] [loggerModule.py:debug:275] ONNX OCR 모델 타입 결정: simp (GPU: False) +[2025-10-24 09:40:43,641] [LogListener] [DEBUG] [loggerModule.py:debug:275] ONNX OCR 모듈 CPU 모드로 설정 +[2025-10-24 09:40:43,642] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🚀 ONNX TextSystem 초기화 시작 (CPU 모드) +[2025-10-24 09:40:43,642] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🚀 ONNX TextSystem 초기화 시작 (CPU 모드) +[2025-10-24 09:40:45,224] [LogListener] [DEBUG] [loggerModule.py:debug:275] 문자 사전 파일 UTF-8 확인 완료: D:\py\img_worker\modules\onnx_ocr_module\dict\ppocr_keys_v1.txt +[2025-10-24 09:40:45,308] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ ONNX TextSystem 초기화 완료 (CPU + SIMP (호환성) 모델) +[2025-10-24 09:40:45,309] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ ONNX TextSystem 초기화 완료 (CPU + SIMP 모델) +[2025-10-24 09:40:45,309] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ ONNX OCR 모듈 초기화 성공 (CPU 모드) +[2025-10-24 09:40:45,309] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ ONNX OCR 모듈 초기화 성공 +[2025-10-24 09:40:45,310] [LogListener] [DEBUG] [loggerModule.py:debug:275] 마스크 모듈 초기화 완료 +[2025-10-24 09:40:45,310] [LogListener] [DEBUG] [loggerModule.py:debug:275] MaskModule 초기화 성공 +[2025-10-24 09:40:45,310] [LogListener] [DEBUG] [loggerModule.py:debug:275] 텍스트 렌더링 모듈 초기화 완료 +[2025-10-24 09:40:45,310] [LogListener] [DEBUG] [loggerModule.py:debug:275] 기본 폰트: D:\py\img_worker\modules\fonts\HakgyoansimDunggeunmisoTTFB.ttf +[2025-10-24 09:40:45,310] [LogListener] [DEBUG] [loggerModule.py:debug:275] TextRenderingModule 초기화 성공 +[2025-10-24 09:40:45,310] [LogListener] [DEBUG] [loggerModule.py:debug:275] 폰트 로드 성공: D:\py\img_worker\modules\fonts\HakgyoansimDunggeunmisoTTFB.ttf +[2025-10-24 09:40:45,310] [LogListener] [DEBUG] [loggerModule.py:debug:275] PostImageManager 초기화 성공 +[2025-10-24 09:40:45,311] [LogListener] [DEBUG] [loggerModule.py:debug:275] Request_AI_Server GPU 상태: CUDA 사용 가능=False +[2025-10-24 09:40:45,311] [LogListener] [DEBUG] [loggerModule.py:debug:275] Request_AI_Server 초기화 성공 +[2025-10-24 09:40:45,311] [LogListener] [DEBUG] [loggerModule.py:debug:275] GoogleTranslate 초기화 성공 +[2025-10-24 09:40:45,311] [LogListener] [DEBUG] [loggerModule.py:debug:275] MIGAN CUDA 사용 불가 - CPU 모드로 설정 +[2025-10-24 09:40:45,311] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] GPU 관리자 전달: GPUManager, can_use_cuda: False +[2025-10-24 09:40:45,329] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] GPU 관리자 사용 가능: False +[2025-10-24 09:40:45,538] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 사용 가능한 providers: ['DmlExecutionProvider', 'CPUExecutionProvider'] +[2025-10-24 09:40:45,538] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] DirectML 가속 활성화 +[2025-10-24 09:40:45,538] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 최종 providers: [('DmlExecutionProvider', {}), ('CPUExecutionProvider', {})] +[2025-10-24 09:40:45,538] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] DirectML 가속 시도: [('DmlExecutionProvider', {}), ('CPUExecutionProvider', {})] +[2025-10-24 09:40:45,538] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] DirectML 가속 성공! 실제 providers: ['DmlExecutionProvider', 'CPUExecutionProvider'] +[2025-10-24 09:40:45,539] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 입력 0: image, 형태: ['batch_size', 3, 'height', 'width'], 타입: tensor(uint8) +[2025-10-24 09:40:45,539] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 입력 1: mask, 형태: ['batch_size', 1, 'height', 'width'], 타입: tensor(uint8) +[2025-10-24 09:40:45,539] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 출력 0: result, 형태: ['ScatterNDresult_dim_0', 3, 'ScatterNDresult_dim_2', 'ScatterNDresult_dim_3'], 타입: tensor(uint8) +[2025-10-24 09:40:45,539] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 세션 준비 완료. providers=['DmlExecutionProvider', 'CPUExecutionProvider'] +[2025-10-24 09:40:45,539] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] GPU 관리자 연결 완료: GPUManager +[2025-10-24 09:40:45,539] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 파이프라인 gpu_manager 속성: True, 값: +[2025-10-24 09:40:45,539] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 초기화 완료: gpu_manager 속성=True, 값= +[2025-10-24 09:40:45,539] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔰 OCR 모듈 Warm-up 시작... +[2025-10-24 09:40:45,539] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔍 ONNX OCR 감지 방식: polygon +[2025-10-24 09:40:45,544] [LogListener] [DEBUG] [loggerModule.py:debug:275] ⚡ ONNX OCR 추론 완료: 5.0ms +[2025-10-24 09:40:45,544] [LogListener] [DEBUG] [loggerModule.py:debug:275] 📊 세부 시간 - 감지: 3.0ms, 인식: 0.0ms, 분류: 0.0ms +[2025-10-24 09:40:45,544] [LogListener] [WARNING] [loggerModule.py:warning:287] ⚠️ ONNX OCR 결과가 비어있습니다 +[2025-10-24 09:40:45,547] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ OCR 모듈 Warm-up 성공 +[2025-10-24 09:40:45,547] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔰 ImageProcessor Warm‑up 완료 +[2025-10-24 09:40:45,548] [ResultListener] [INFO] [loggerModule.py:info:281] 워커 READY 수신 +[2025-10-24 09:40:45,548] [WorkerRoller] [INFO] [loggerModule.py:info:281] 버퍼 작업 재개: 1건 +[2025-10-24 09:40:45,548] [ResultListener] [INFO] [loggerModule.py:info:281] 워커 READY 수신 +[2025-10-24 09:40:45,548] [LogListener] [DEBUG] [loggerModule.py:debug:275] 📡 추가 READY 신호 전송 완료 +[2025-10-24 09:40:45,548] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 09:40:45,548] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 7640) +[2025-10-24 09:40:45,549] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔥 작업 수신 성공 +[2025-10-24 09:40:45,549] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🚀 작업 처리 시작: cmd=process_single_image, uid=46dddb84-c588-4c04-8d1e-baf7f78bed34 +[2025-10-24 09:40:45,549] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 프로세서 toggle_states 업데이트 : {'TEMP_IMAGE_DIR': 'C:\\ProgramData\\ImgWorker\\work', 'output_image_format': 'webp', 'use_local_rembg': True, 'local_rembg_model_path': 'D:\\py\\img_worker\\modules\\rembg_models', 'local_model_name': 'birefnet-general-lite', 'optionIMGTrans_type': 'GPU', 'detail_IMGTrans_type': 'GPU', 'thumb_trans_type': 'GPU', 'migan_use_accel': True, 'migan_onnx_path': 'D:\\py\\img_worker\\modules\\migan_onnx\\migan_pipeline_v2.onnx', 'ocr_provider_override': 'auto', 'migan_provider_override': 'auto', 'font_type': '폰트5', 'unwanted_texts': ['가격설명', '무료배송', '보증', '사은품', '세일', '이벤트', '특가', '품절', '할인', '할인가'], 'is_member_valid': False, 'authenticated_by_admin': True} +[2025-10-24 09:40:45,549] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 실행 직전: cmd=process_single_image +[2025-10-24 09:40:45,549] [LogListener] [DEBUG] [loggerModule.py:debug:275] process_single_image 호출 직전 +[2025-10-24 09:40:45,551] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 번역시작 +[2025-10-24 09:40:45,738] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 11 처리 시작: C:\ProgramData\ImgWorker\incoming\dl_8bb75f3547cd.jpg - 전체 번역 모드 +[2025-10-24 09:40:45,743] [LogListener] [DEBUG] [loggerModule.py:debug:275] 로컬 파일 경로 감지, 다운로드 생략: C:\ProgramData\ImgWorker\incoming\dl_8bb75f3547cd.jpg +[2025-10-24 09:40:45,744] [LogListener] [DEBUG] [loggerModule.py:debug:275] 옵션 이미지는 스케일 처리 건너뛰기: option +[2025-10-24 09:40:45,744] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 11 로컬 저장위치(옵션 이미지 원본 유지): C:\ProgramData\ImgWorker\incoming\dl_8bb75f3547cd.jpg +[2025-10-24 09:40:45,745] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔍 ONNX OCR 감지 방식: polygon +[2025-10-24 09:40:46,421] [LogListener] [DEBUG] [loggerModule.py:debug:275] ⚡ ONNX OCR 추론 완료: 672.6ms +[2025-10-24 09:40:46,440] [LogListener] [DEBUG] [loggerModule.py:debug:275] 📊 세부 시간 - 감지: 178.0ms, 인식: 467.6ms, 분류: 20.0ms +[2025-10-24 09:40:46,448] [LogListener] [DEBUG] [loggerModule.py:debug:275] 메모리 변화 [OCR 처리]: 42428.4MB -> 42665.9MB (+237.5MB, +0.6%) - 이미지 11 +[2025-10-24 09:40:46,464] [LogListener] [DEBUG] [loggerModule.py:debug:275] ocr_results: [{'text': '暴走兔', 'confidence': 0.9141947627067566, 'polygon': [[136.0, 21.0], [270.0, 23.0], [269.0, 63.0], [135.0, 61.0]], 'bbox': (135, 21, 136, 43), 'method': 'polygon'}, {'text': 'BAOZOUTU', 'confidence': 0.9931288957595825, 'polygon': [[134.0, 73.0], [273.0, 73.0], [273.0, 87.0], [134.0, 87.0]], 'bbox': (134, 73, 140, 15), 'method': 'polygon'}, {'text': '全波段收音机', 'confidence': 0.997622013092041, 'polygon': [[81.0, 158.0], [288.0, 156.0], [288.0, 184.0], [81.0, 186.0]], 'bbox': (81, 156, 208, 31), 'method': 'polygon'}, {'text': '..R12', 'confidence': 0.7803670763969421, 'polygon': [[573.0, 152.0], [616.0, 152.0], [616.0, 163.0], [573.0, 163.0]], 'bbox': (573, 152, 44, 12), 'method': 'polygon'}, {'text': '518', 'confidence': 0.9855506420135498, 'polygon': [[488.0, 174.0], [607.0, 174.0], [607.0, 240.0], [488.0, 240.0]], 'bbox': (488, 174, 120, 67), 'method': 'polygon'}, {'text': '蓝牙音响', 'confidence': 0.998444676399231, 'polygon': [[78.0, 211.0], [294.0, 213.0], [294.0, 248.0], [78.0, 246.0]], 'bbox': (78, 211, 217, 38), 'method': 'polygon'}, {'text': 'UH', 'confidence': 0.7753715515136719, 'polygon': [[608.0, 228.0], [621.0, 228.0], [621.0, 237.0], [608.0, 237.0]], 'bbox': (608, 228, 14, 10), 'method': 'polygon'}, {'text': 'MULTIBANDRADIO/BT/MUSICPLAYER', 'confidence': 0.9853014349937439, 'polygon': [[464.0, 244.0], [623.0, 244.0], [623.0, 257.0], [464.0, 257.0]], 'bbox': (464, 244, 160, 14), 'method': 'polygon'}, {'text': '4000毫安锂电池', 'confidence': 0.9978502988815308, 'polygon': [[82.0, 279.0], [289.0, 279.0], [289.0, 303.0], [82.0, 303.0]], 'bbox': (82, 279, 208, 25), 'method': 'polygon'}, {'text': '手', 'confidence': 0.9999699592590332, 'polygon': [[80.0, 335.0], [113.0, 335.0], [113.0, 365.0], [80.0, 365.0]], 'bbox': (80, 335, 34, 31), 'method': 'polygon'}, {'text': '电', 'confidence': 0.9992403984069824, 'polygon': [[168.0, 336.0], [204.0, 336.0], [204.0, 365.0], [168.0, 365.0]], 'bbox': (168, 336, 37, 30), 'method': 'polygon'}, {'text': '筒', 'confidence': 0.9993694424629211, 'polygon': [[263.0, 336.0], [290.0, 336.0], [290.0, 365.0], [263.0, 365.0]], 'bbox': (263, 336, 28, 30), 'method': 'polygon'}, {'text': 'SOS警报/指南针', 'confidence': 0.974646806716919, 'polygon': [[81.0, 397.0], [288.0, 397.0], [288.0, 425.0], [81.0, 425.0]], 'bbox': (81, 397, 208, 29), 'method': 'polygon'}, {'text': '燕走兔', 'confidence': 0.5333335995674133, 'polygon': [[531.0, 409.0], [595.0, 411.0], [594.0, 441.0], [530.0, 439.0]], 'bbox': (530, 409, 66, 33), 'method': 'polygon'}, {'text': '手摇/太阳能发电', 'confidence': 0.9910641312599182, 'polygon': [[83.0, 457.0], [289.0, 457.0], [289.0, 485.0], [83.0, 485.0]], 'bbox': (83, 457, 207, 29), 'method': 'polygon'}, {'text': '可插U盘/TF卡', 'confidence': 0.9867974519729614, 'polygon': [[82.0, 518.0], [290.0, 518.0], [290.0, 545.0], [82.0, 545.0]], 'bbox': (82, 518, 209, 28), 'method': 'polygon'}, {'text': '限时', 'confidence': 0.9983594417572021, 'polygon': [[18.0, 659.0], [151.0, 661.0], [150.0, 728.0], [17.0, 726.0]], 'bbox': (17, 659, 135, 70), 'method': 'polygon'}, {'text': '赠送', 'confidence': 0.9996761679649353, 'polygon': [[15.0, 728.0], [149.0, 728.0], [149.0, 792.0], [15.0, 792.0]], 'bbox': (15, 728, 135, 65), 'method': 'polygon'}, {'text': '延长天线', 'confidence': 0.9979546070098877, 'polygon': [[211.0, 769.0], [288.0, 769.0], [288.0, 790.0], [211.0, 790.0]], 'bbox': (211, 769, 78, 22), 'method': 'polygon'}, {'text': '内存卡', 'confidence': 0.9972650408744812, 'polygon': [[323.0, 766.0], [384.0, 766.0], [384.0, 791.0], [323.0, 791.0]], 'bbox': (323, 766, 62, 26), 'method': 'polygon'}, {'text': '读卡器', 'confidence': 0.9981095790863037, 'polygon': [[423.0, 767.0], [483.0, 767.0], [483.0, 792.0], [423.0, 792.0]], 'bbox': (423, 767, 61, 26), 'method': 'polygon'}, {'text': '充电线', 'confidence': 0.9985806345939636, 'polygon': [[522.0, 767.0], [583.0, 767.0], [583.0, 792.0], [522.0, 792.0]], 'bbox': (522, 767, 62, 26), 'method': 'polygon'}, {'text': '礼盒礼袋', 'confidence': 0.9975590109825134, 'polygon': [[616.0, 768.0], [694.0, 768.0], [694.0, 791.0], [616.0, 791.0]], 'bbox': (616, 768, 79, 24), 'method': 'polygon'}, {'text': '充电头', 'confidence': 0.9975325465202332, 'polygon': [[726.0, 767.0], [786.0, 767.0], [786.0, 792.0], [726.0, 792.0]], 'bbox': (726, 767, 61, 26), 'method': 'polygon'}] +[2025-10-24 09:40:46,475] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 91.4%): '暴走兔' +[2025-10-24 09:40:46,475] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): 'BAOZOUTU' +[2025-10-24 09:40:46,475] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.8%): '全波段收音机' +[2025-10-24 09:40:46,475] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): '..R12' +[2025-10-24 09:40:46,475] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): '518' +[2025-10-24 09:40:46,475] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.8%): '蓝牙音响' +[2025-10-24 09:40:46,476] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): 'UH' +[2025-10-24 09:40:46,476] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): 'MULTIBANDRADIO/BT/MUSICPLAYER' +[2025-10-24 09:40:46,476] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.8%): '4000毫安锂电池' +[2025-10-24 09:40:46,476] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 100.0%): '手' +[2025-10-24 09:40:46,476] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.9%): '电' +[2025-10-24 09:40:46,476] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.9%): '筒' +[2025-10-24 09:40:46,477] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 97.5%): 'SOS警报/指南针' +[2025-10-24 09:40:46,477] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 53.3%): '燕走兔' +[2025-10-24 09:40:46,477] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.1%): '手摇/太阳能发电' +[2025-10-24 09:40:46,477] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 98.7%): '可插U盘/TF卡' +[2025-10-24 09:40:46,477] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.8%): '限时' +[2025-10-24 09:40:46,477] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 100.0%): '赠送' +[2025-10-24 09:40:46,477] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.8%): '延长天线' +[2025-10-24 09:40:46,480] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.7%): '内存卡' +[2025-10-24 09:40:46,496] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.8%): '读卡器' +[2025-10-24 09:40:46,514] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.9%): '充电线' +[2025-10-24 09:40:46,514] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.8%): '礼盒礼袋' +[2025-10-24 09:40:46,514] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.8%): '充电头' +[2025-10-24 09:40:46,515] [LogListener] [DEBUG] [loggerModule.py:debug:275] 필터링 결과: 19/24개 (신뢰도 + & 중국어) +[2025-10-24 09:40:46,515] [LogListener] [DEBUG] [loggerModule.py:debug:275] filter_ocr_results: [{'text': '暴走兔', 'confidence': 0.9141947627067566, 'polygon': [[136.0, 21.0], [270.0, 23.0], [269.0, 63.0], [135.0, 61.0]], 'bbox': (135, 21, 136, 43), 'method': 'polygon'}, {'text': '全波段收音机', 'confidence': 0.997622013092041, 'polygon': [[81.0, 158.0], [288.0, 156.0], [288.0, 184.0], [81.0, 186.0]], 'bbox': (81, 156, 208, 31), 'method': 'polygon'}, {'text': '蓝牙音响', 'confidence': 0.998444676399231, 'polygon': [[78.0, 211.0], [294.0, 213.0], [294.0, 248.0], [78.0, 246.0]], 'bbox': (78, 211, 217, 38), 'method': 'polygon'}, {'text': '4000毫安锂电池', 'confidence': 0.9978502988815308, 'polygon': [[82.0, 279.0], [289.0, 279.0], [289.0, 303.0], [82.0, 303.0]], 'bbox': (82, 279, 208, 25), 'method': 'polygon'}, {'text': '手', 'confidence': 0.9999699592590332, 'polygon': [[80.0, 335.0], [113.0, 335.0], [113.0, 365.0], [80.0, 365.0]], 'bbox': (80, 335, 34, 31), 'method': 'polygon'}, {'text': '电', 'confidence': 0.9992403984069824, 'polygon': [[168.0, 336.0], [204.0, 336.0], [204.0, 365.0], [168.0, 365.0]], 'bbox': (168, 336, 37, 30), 'method': 'polygon'}, {'text': '筒', 'confidence': 0.9993694424629211, 'polygon': [[263.0, 336.0], [290.0, 336.0], [290.0, 365.0], [263.0, 365.0]], 'bbox': (263, 336, 28, 30), 'method': 'polygon'}, {'text': 'SOS警报/指南针', 'confidence': 0.974646806716919, 'polygon': [[81.0, 397.0], [288.0, 397.0], [288.0, 425.0], [81.0, 425.0]], 'bbox': (81, 397, 208, 29), 'method': 'polygon'}, {'text': '燕走兔', 'confidence': 0.5333335995674133, 'polygon': [[531.0, 409.0], [595.0, 411.0], [594.0, 441.0], [530.0, 439.0]], 'bbox': (530, 409, 66, 33), 'method': 'polygon'}, {'text': '手摇/太阳能发电', 'confidence': 0.9910641312599182, 'polygon': [[83.0, 457.0], [289.0, 457.0], [289.0, 485.0], [83.0, 485.0]], 'bbox': (83, 457, 207, 29), 'method': 'polygon'}, {'text': '可插U盘/TF卡', 'confidence': 0.9867974519729614, 'polygon': [[82.0, 518.0], [290.0, 518.0], [290.0, 545.0], [82.0, 545.0]], 'bbox': (82, 518, 209, 28), 'method': 'polygon'}, {'text': '限时', 'confidence': 0.9983594417572021, 'polygon': [[18.0, 659.0], [151.0, 661.0], [150.0, 728.0], [17.0, 726.0]], 'bbox': (17, 659, 135, 70), 'method': 'polygon'}, {'text': '赠送', 'confidence': 0.9996761679649353, 'polygon': [[15.0, 728.0], [149.0, 728.0], [149.0, 792.0], [15.0, 792.0]], 'bbox': (15, 728, 135, 65), 'method': 'polygon'}, {'text': '延长天线', 'confidence': 0.9979546070098877, 'polygon': [[211.0, 769.0], [288.0, 769.0], [288.0, 790.0], [211.0, 790.0]], 'bbox': (211, 769, 78, 22), 'method': 'polygon'}, {'text': '内存卡', 'confidence': 0.9972650408744812, 'polygon': [[323.0, 766.0], [384.0, 766.0], [384.0, 791.0], [323.0, 791.0]], 'bbox': (323, 766, 62, 26), 'method': 'polygon'}, {'text': '读卡器', 'confidence': 0.9981095790863037, 'polygon': [[423.0, 767.0], [483.0, 767.0], [483.0, 792.0], [423.0, 792.0]], 'bbox': (423, 767, 61, 26), 'method': 'polygon'}, {'text': '充电线', 'confidence': 0.9985806345939636, 'polygon': [[522.0, 767.0], [583.0, 767.0], [583.0, 792.0], [522.0, 792.0]], 'bbox': (522, 767, 62, 26), 'method': 'polygon'}, {'text': '礼盒礼袋', 'confidence': 0.9975590109825134, 'polygon': [[616.0, 768.0], [694.0, 768.0], [694.0, 791.0], [616.0, 791.0]], 'bbox': (616, 768, 79, 24), 'method': 'polygon'}, {'text': '充电头', 'confidence': 0.9975325465202332, 'polygon': [[726.0, 767.0], [786.0, 767.0], [786.0, 792.0], [726.0, 792.0]], 'bbox': (726, 767, 61, 26), 'method': 'polygon'}] +[2025-10-24 09:40:46,515] [LogListener] [DEBUG] [loggerModule.py:debug:275] 중국어 텍스트 19개 필터링 완료 +[2025-10-24 09:40:46,515] [LogListener] [DEBUG] [loggerModule.py:debug:275] 한글 텍스트 0개 필터링 완료 +[2025-10-24 09:40:49,372] [LogListener] [DEBUG] [loggerModule.py:debug:275] translated_texts: ['도망친 토끼', '풀 밴드 라디오', '블루투스 스피커', '4000mAh 리튬 배터리', '손', '전기', '실린더', 'SOS 경보 / 나침반', '얀 주이투', '핸드 크랭크 / 태양광 발전', 'U 디스크 삽입 가능 / TF 카드', '제한된 시간', '포기하다', '확장 안테나', '메모리 카드', '카드 리더', '충전 케이블', '선물 상자 선물 가방', '충전 헤드'] +[2025-10-24 09:40:49,373] [LogListener] [DEBUG] [loggerModule.py:debug:275] 마스크 생성 완료 +[2025-10-24 09:40:49,373] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 11 OCR 권한 없음, 전체 번역 모드 +[2025-10-24 09:40:49,381] [LogListener] [DEBUG] [loggerModule.py:debug:275] [AUTO Inpaint] coverage=0.209, comps=18, min_center_dist=0.052 → request +[2025-10-24 09:40:49,381] [LogListener] [DEBUG] [loggerModule.py:debug:275] ocr_count: 19 +[2025-10-24 09:40:49,381] [LogListener] [DEBUG] [loggerModule.py:debug:275] is_member_valid: False +[2025-10-24 09:40:49,381] [LogListener] [DEBUG] [loggerModule.py:debug:275] MIGAN 인페인팅 시도 +[2025-10-24 09:40:49,383] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 입력 형태 - 이미지: (1, 3, 800, 800), 마스크: (1, 1, 800, 800) +[2025-10-24 09:40:49,878] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 출력 형태: (800, 800, 3), dtype: uint8 +[2025-10-24 09:40:49,878] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 추론 완료: 494.00 ms +[2025-10-24 09:40:49,885] [LogListener] [DEBUG] [loggerModule.py:debug:275] MIGAN 인페인팅 성공 +[2025-10-24 09:40:49,886] [LogListener] [DEBUG] [loggerModule.py:debug:275] 메모리 변화 [인페인팅]: 42744.3MB -> 42930.7MB (+186.4MB, +0.4%) - 방법: migan +[2025-10-24 09:40:49,886] [LogListener] [DEBUG] [loggerModule.py:debug:275] 인페인팅 완료 +[2025-10-24 09:40:49,963] [LogListener] [DEBUG] [loggerModule.py:debug:275] 텍스트 렌더링 완료 +[2025-10-24 09:40:49,964] [LogListener] [DEBUG] [loggerModule.py:debug:275] watermark_text: 이미지 저작권 보유 +[2025-10-24 09:40:49,964] [LogListener] [DEBUG] [loggerModule.py:debug:275] is_watermark_enabled: True +[2025-10-24 09:40:50,277] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 저장 완료 : C:\ProgramData\ImgWorker\work\translated_option_img_11.webp +[2025-10-24 09:40:50,278] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 11 번역 완료: C:\ProgramData\ImgWorker\work\translated_option_img_11.webp +[2025-10-24 09:40:50,279] [LogListener] [DEBUG] [loggerModule.py:debug:275] ⏱ 이미지 파이프라인 총 4727.8ms | download=0.0ms | ocr=682.6ms | translate=2898.3ms | mask=2898.3ms | inpaint=510.0ms(migan/DirectML) | render=78.1ms | save=272.8ms +[2025-10-24 09:40:50,280] [LogListener] [DEBUG] [loggerModule.py:debug:275] process_single_image 호출 완료 +[2025-10-24 09:40:50,280] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 결과 반환 중: uid=46dddb84-c588-4c04-8d1e-baf7f78bed34 +[2025-10-24 09:40:50,280] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 결과 반환 완료: uid=46dddb84-c588-4c04-8d1e-baf7f78bed34 +[2025-10-24 09:40:50,280] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 09:40:50,280] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 7640) +[2025-10-24 09:40:52,097] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔥 작업 수신 성공 +[2025-10-24 09:40:52,098] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🚀 작업 처리 시작: cmd=process_single_image, uid=e43f623e-accc-4abe-8cb4-57638f049869 +[2025-10-24 09:40:52,098] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 프로세서 toggle_states 업데이트 : {'TEMP_IMAGE_DIR': 'C:\\ProgramData\\ImgWorker\\work', 'output_image_format': 'webp', 'use_local_rembg': True, 'local_rembg_model_path': 'D:\\py\\img_worker\\modules\\rembg_models', 'local_model_name': 'birefnet-general-lite', 'optionIMGTrans_type': 'GPU', 'detail_IMGTrans_type': 'GPU', 'thumb_trans_type': 'GPU', 'migan_use_accel': True, 'migan_onnx_path': 'D:\\py\\img_worker\\modules\\migan_onnx\\migan_pipeline_v2.onnx', 'ocr_provider_override': 'auto', 'migan_provider_override': 'auto', 'font_type': '폰트5', 'unwanted_texts': ['가격설명', '무료배송', '보증', '사은품', '세일', '이벤트', '특가', '품절', '할인', '할인가'], 'is_member_valid': False, 'authenticated_by_admin': True} +[2025-10-24 09:40:52,098] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 실행 직전: cmd=process_single_image +[2025-10-24 09:40:52,098] [LogListener] [DEBUG] [loggerModule.py:debug:275] process_single_image 호출 직전 +[2025-10-24 09:40:52,098] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 번역시작 +[2025-10-24 09:40:52,525] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 12 처리 시작: C:\ProgramData\ImgWorker\incoming\dl_d15b50dceaeb.jpg - 전체 번역 모드 +[2025-10-24 09:40:52,532] [LogListener] [DEBUG] [loggerModule.py:debug:275] 로컬 파일 경로 감지, 다운로드 생략: C:\ProgramData\ImgWorker\incoming\dl_d15b50dceaeb.jpg +[2025-10-24 09:40:52,532] [LogListener] [DEBUG] [loggerModule.py:debug:275] 옵션 이미지는 스케일 처리 건너뛰기: option +[2025-10-24 09:40:52,532] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 12 로컬 저장위치(옵션 이미지 원본 유지): C:\ProgramData\ImgWorker\incoming\dl_d15b50dceaeb.jpg +[2025-10-24 09:40:52,534] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔍 ONNX OCR 감지 방식: polygon +[2025-10-24 09:40:53,180] [LogListener] [DEBUG] [loggerModule.py:debug:275] ⚡ ONNX OCR 추론 완료: 643.5ms +[2025-10-24 09:40:53,198] [LogListener] [DEBUG] [loggerModule.py:debug:275] 📊 세부 시간 - 감지: 112.4ms, 인식: 506.0ms, 분류: 20.0ms +[2025-10-24 09:40:53,209] [LogListener] [DEBUG] [loggerModule.py:debug:275] 메모리 변화 [OCR 처리]: 43148.8MB -> 43170.3MB (+21.5MB, +0.0%) - 이미지 12 +[2025-10-24 09:40:53,209] [LogListener] [DEBUG] [loggerModule.py:debug:275] ocr_results: [{'text': '暴走兔', 'confidence': 0.9268935322761536, 'polygon': [[136.0, 20.0], [271.0, 22.0], [270.0, 64.0], [135.0, 62.0]], 'bbox': (135, 20, 137, 45), 'method': 'polygon'}, {'text': 'BAOZOUTU', 'confidence': 0.9931881427764893, 'polygon': [[134.0, 73.0], [273.0, 73.0], [273.0, 87.0], [134.0, 87.0]], 'bbox': (134, 73, 140, 15), 'method': 'polygon'}, {'text': '全波段收音机', 'confidence': 0.9976200461387634, 'polygon': [[81.0, 158.0], [288.0, 156.0], [288.0, 184.0], [81.0, 186.0]], 'bbox': (81, 156, 208, 31), 'method': 'polygon'}, {'text': '.R12', 'confidence': 0.862068235874176, 'polygon': [[577.0, 152.0], [614.0, 152.0], [614.0, 163.0], [577.0, 163.0]], 'bbox': (577, 152, 38, 12), 'method': 'polygon'}, {'text': '518', 'confidence': 0.9833140969276428, 'polygon': [[488.0, 174.0], [607.0, 174.0], [607.0, 240.0], [488.0, 240.0]], 'bbox': (488, 174, 120, 67), 'method': 'polygon'}, {'text': '蓝牙音响', 'confidence': 0.9983958601951599, 'polygon': [[79.0, 213.0], [294.0, 213.0], [294.0, 248.0], [79.0, 248.0]], 'bbox': (79, 213, 216, 36), 'method': 'polygon'}, {'text': 'MULTIBANDRADIO/BT/MUSICPLAYER', 'confidence': 0.9756512641906738, 'polygon': [[465.0, 244.0], [623.0, 244.0], [623.0, 257.0], [465.0, 257.0]], 'bbox': (465, 244, 159, 14), 'method': 'polygon'}, {'text': '4000毫安锂电池', 'confidence': 0.9978365302085876, 'polygon': [[82.0, 279.0], [289.0, 279.0], [289.0, 303.0], [82.0, 303.0]], 'bbox': (82, 279, 208, 25), 'method': 'polygon'}, {'text': '手', 'confidence': 0.9999254941940308, 'polygon': [[80.0, 336.0], [117.0, 336.0], [117.0, 365.0], [80.0, 365.0]], 'bbox': (80, 336, 38, 30), 'method': 'polygon'}, {'text': '电', 'confidence': 0.9991130232810974, 'polygon': [[168.0, 333.0], [206.0, 338.0], [202.0, 367.0], [164.0, 362.0]], 'bbox': (164, 333, 43, 35), 'method': 'polygon'}, {'text': '筒', 'confidence': 0.9980816841125488, 'polygon': [[263.0, 336.0], [291.0, 336.0], [291.0, 365.0], [263.0, 365.0]], 'bbox': (263, 336, 29, 30), 'method': 'polygon'}, {'text': 'SOS警报/指南针', 'confidence': 0.9601571559906006, 'polygon': [[81.0, 397.0], [288.0, 397.0], [288.0, 425.0], [81.0, 425.0]], 'bbox': (81, 397, 208, 29), 'method': 'polygon'}, {'text': '黑走兔', 'confidence': 0.5847620368003845, 'polygon': [[531.0, 408.0], [597.0, 412.0], [595.0, 443.0], [529.0, 439.0]], 'bbox': (529, 408, 69, 36), 'method': 'polygon'}, {'text': '手摇/太阳能发电', 'confidence': 0.9916560649871826, 'polygon': [[83.0, 457.0], [289.0, 457.0], [289.0, 485.0], [83.0, 485.0]], 'bbox': (83, 457, 207, 29), 'method': 'polygon'}, {'text': '可插U盘/TF卡', 'confidence': 0.9924731850624084, 'polygon': [[81.0, 516.0], [290.0, 518.0], [290.0, 546.0], [81.0, 544.0]], 'bbox': (81, 516, 210, 31), 'method': 'polygon'}, {'text': '限时', 'confidence': 0.9978593587875366, 'polygon': [[18.0, 659.0], [151.0, 661.0], [150.0, 729.0], [17.0, 727.0]], 'bbox': (17, 659, 135, 71), 'method': 'polygon'}, {'text': '赠送', 'confidence': 0.9996343851089478, 'polygon': [[15.0, 728.0], [150.0, 728.0], [150.0, 792.0], [15.0, 792.0]], 'bbox': (15, 728, 136, 65), 'method': 'polygon'}, {'text': '有线耳机', 'confidence': 0.9989824295043945, 'polygon': [[188.0, 769.0], [265.0, 769.0], [265.0, 790.0], [188.0, 790.0]], 'bbox': (188, 769, 78, 22), 'method': 'polygon'}, {'text': '延长天线', 'confidence': 0.9985175132751465, 'polygon': [[286.0, 769.0], [365.0, 769.0], [365.0, 790.0], [286.0, 790.0]], 'bbox': (286, 769, 80, 22), 'method': 'polygon'}, {'text': '内存卡', 'confidence': 0.9982945919036865, 'polygon': [[386.0, 769.0], [446.0, 769.0], [446.0, 790.0], [386.0, 790.0]], 'bbox': (386, 769, 61, 22), 'method': 'polygon'}, {'text': '读卡器', 'confidence': 0.9987030625343323, 'polygon': [[472.0, 767.0], [531.0, 767.0], [531.0, 792.0], [472.0, 792.0]], 'bbox': (472, 767, 60, 26), 'method': 'polygon'}, {'text': '充电线', 'confidence': 0.9993455410003662, 'polygon': [[560.0, 769.0], [621.0, 769.0], [621.0, 790.0], [560.0, 790.0]], 'bbox': (560, 769, 62, 22), 'method': 'polygon'}, {'text': '礼盒礼袋', 'confidence': 0.9988080263137817, 'polygon': [[640.0, 769.0], [717.0, 769.0], [717.0, 790.0], [640.0, 790.0]], 'bbox': (640, 769, 78, 22), 'method': 'polygon'}, {'text': '充电头', 'confidence': 0.9983770251274109, 'polygon': [[738.0, 767.0], [797.0, 767.0], [797.0, 792.0], [738.0, 792.0]], 'bbox': (738, 767, 60, 26), 'method': 'polygon'}] +[2025-10-24 09:40:53,210] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 92.7%): '暴走兔' +[2025-10-24 09:40:53,210] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): 'BAOZOUTU' +[2025-10-24 09:40:53,210] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.8%): '全波段收音机' +[2025-10-24 09:40:53,210] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): '.R12' +[2025-10-24 09:40:53,210] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): '518' +[2025-10-24 09:40:53,210] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.8%): '蓝牙音响' +[2025-10-24 09:40:53,210] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): 'MULTIBANDRADIO/BT/MUSICPLAYER' +[2025-10-24 09:40:53,210] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.8%): '4000毫安锂电池' +[2025-10-24 09:40:53,210] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 100.0%): '手' +[2025-10-24 09:40:53,211] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.9%): '电' +[2025-10-24 09:40:53,211] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.8%): '筒' +[2025-10-24 09:40:53,211] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 96.0%): 'SOS警报/指南针' +[2025-10-24 09:40:53,211] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 58.5%): '黑走兔' +[2025-10-24 09:40:53,211] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.2%): '手摇/太阳能发电' +[2025-10-24 09:40:53,211] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.2%): '可插U盘/TF卡' +[2025-10-24 09:40:53,211] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.8%): '限时' +[2025-10-24 09:40:53,211] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 100.0%): '赠送' +[2025-10-24 09:40:53,211] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.9%): '有线耳机' +[2025-10-24 09:40:53,211] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.9%): '延长天线' +[2025-10-24 09:40:53,211] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.8%): '内存卡' +[2025-10-24 09:40:53,211] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.9%): '读卡器' +[2025-10-24 09:40:53,211] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.9%): '充电线' +[2025-10-24 09:40:53,211] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.9%): '礼盒礼袋' +[2025-10-24 09:40:53,212] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.8%): '充电头' +[2025-10-24 09:40:53,212] [LogListener] [DEBUG] [loggerModule.py:debug:275] 필터링 결과: 20/24개 (신뢰도 + & 중국어) +[2025-10-24 09:40:53,212] [LogListener] [DEBUG] [loggerModule.py:debug:275] filter_ocr_results: [{'text': '暴走兔', 'confidence': 0.9268935322761536, 'polygon': [[136.0, 20.0], [271.0, 22.0], [270.0, 64.0], [135.0, 62.0]], 'bbox': (135, 20, 137, 45), 'method': 'polygon'}, {'text': '全波段收音机', 'confidence': 0.9976200461387634, 'polygon': [[81.0, 158.0], [288.0, 156.0], [288.0, 184.0], [81.0, 186.0]], 'bbox': (81, 156, 208, 31), 'method': 'polygon'}, {'text': '蓝牙音响', 'confidence': 0.9983958601951599, 'polygon': [[79.0, 213.0], [294.0, 213.0], [294.0, 248.0], [79.0, 248.0]], 'bbox': (79, 213, 216, 36), 'method': 'polygon'}, {'text': '4000毫安锂电池', 'confidence': 0.9978365302085876, 'polygon': [[82.0, 279.0], [289.0, 279.0], [289.0, 303.0], [82.0, 303.0]], 'bbox': (82, 279, 208, 25), 'method': 'polygon'}, {'text': '手', 'confidence': 0.9999254941940308, 'polygon': [[80.0, 336.0], [117.0, 336.0], [117.0, 365.0], [80.0, 365.0]], 'bbox': (80, 336, 38, 30), 'method': 'polygon'}, {'text': '电', 'confidence': 0.9991130232810974, 'polygon': [[168.0, 333.0], [206.0, 338.0], [202.0, 367.0], [164.0, 362.0]], 'bbox': (164, 333, 43, 35), 'method': 'polygon'}, {'text': '筒', 'confidence': 0.9980816841125488, 'polygon': [[263.0, 336.0], [291.0, 336.0], [291.0, 365.0], [263.0, 365.0]], 'bbox': (263, 336, 29, 30), 'method': 'polygon'}, {'text': 'SOS警报/指南针', 'confidence': 0.9601571559906006, 'polygon': [[81.0, 397.0], [288.0, 397.0], [288.0, 425.0], [81.0, 425.0]], 'bbox': (81, 397, 208, 29), 'method': 'polygon'}, {'text': '黑走兔', 'confidence': 0.5847620368003845, 'polygon': [[531.0, 408.0], [597.0, 412.0], [595.0, 443.0], [529.0, 439.0]], 'bbox': (529, 408, 69, 36), 'method': 'polygon'}, {'text': '手摇/太阳能发电', 'confidence': 0.9916560649871826, 'polygon': [[83.0, 457.0], [289.0, 457.0], [289.0, 485.0], [83.0, 485.0]], 'bbox': (83, 457, 207, 29), 'method': 'polygon'}, {'text': '可插U盘/TF卡', 'confidence': 0.9924731850624084, 'polygon': [[81.0, 516.0], [290.0, 518.0], [290.0, 546.0], [81.0, 544.0]], 'bbox': (81, 516, 210, 31), 'method': 'polygon'}, {'text': '限时', 'confidence': 0.9978593587875366, 'polygon': [[18.0, 659.0], [151.0, 661.0], [150.0, 729.0], [17.0, 727.0]], 'bbox': (17, 659, 135, 71), 'method': 'polygon'}, {'text': '赠送', 'confidence': 0.9996343851089478, 'polygon': [[15.0, 728.0], [150.0, 728.0], [150.0, 792.0], [15.0, 792.0]], 'bbox': (15, 728, 136, 65), 'method': 'polygon'}, {'text': '有线耳机', 'confidence': 0.9989824295043945, 'polygon': [[188.0, 769.0], [265.0, 769.0], [265.0, 790.0], [188.0, 790.0]], 'bbox': (188, 769, 78, 22), 'method': 'polygon'}, {'text': '延长天线', 'confidence': 0.9985175132751465, 'polygon': [[286.0, 769.0], [365.0, 769.0], [365.0, 790.0], [286.0, 790.0]], 'bbox': (286, 769, 80, 22), 'method': 'polygon'}, {'text': '内存卡', 'confidence': 0.9982945919036865, 'polygon': [[386.0, 769.0], [446.0, 769.0], [446.0, 790.0], [386.0, 790.0]], 'bbox': (386, 769, 61, 22), 'method': 'polygon'}, {'text': '读卡器', 'confidence': 0.9987030625343323, 'polygon': [[472.0, 767.0], [531.0, 767.0], [531.0, 792.0], [472.0, 792.0]], 'bbox': (472, 767, 60, 26), 'method': 'polygon'}, {'text': '充电线', 'confidence': 0.9993455410003662, 'polygon': [[560.0, 769.0], [621.0, 769.0], [621.0, 790.0], [560.0, 790.0]], 'bbox': (560, 769, 62, 22), 'method': 'polygon'}, {'text': '礼盒礼袋', 'confidence': 0.9988080263137817, 'polygon': [[640.0, 769.0], [717.0, 769.0], [717.0, 790.0], [640.0, 790.0]], 'bbox': (640, 769, 78, 22), 'method': 'polygon'}, {'text': '充电头', 'confidence': 0.9983770251274109, 'polygon': [[738.0, 767.0], [797.0, 767.0], [797.0, 792.0], [738.0, 792.0]], 'bbox': (738, 767, 60, 26), 'method': 'polygon'}] +[2025-10-24 09:40:53,212] [LogListener] [DEBUG] [loggerModule.py:debug:275] 중국어 텍스트 20개 필터링 완료 +[2025-10-24 09:40:53,212] [LogListener] [DEBUG] [loggerModule.py:debug:275] 한글 텍스트 0개 필터링 완료 +[2025-10-24 09:40:55,370] [LogListener] [DEBUG] [loggerModule.py:debug:275] translated_texts: ['도망친 토끼', '풀 밴드 라디오', '블루투스 스피커', '4000mAh 리튬 배터리', '손', '전기', '실린더', 'SOS 경보 / 나침반', '검은 토끼', '핸드 크랭크 / 태양광 발전', 'U 디스크 삽입 가능 / TF 카드', '제한된 시간', '포기하다', '유선 헤드폰', '확장 안테나', '메모리 카드', '카드 리더', '충전 케이블', '선물 상자 선물 가방', '충전 헤드'] +[2025-10-24 09:40:55,371] [LogListener] [DEBUG] [loggerModule.py:debug:275] 마스크 생성 완료 +[2025-10-24 09:40:55,371] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 12 OCR 권한 없음, 전체 번역 모드 +[2025-10-24 09:40:55,377] [LogListener] [DEBUG] [loggerModule.py:debug:275] [AUTO Inpaint] coverage=0.271, comps=5, min_center_dist=0.273 → request +[2025-10-24 09:40:55,377] [LogListener] [DEBUG] [loggerModule.py:debug:275] ocr_count: 20 +[2025-10-24 09:40:55,377] [LogListener] [DEBUG] [loggerModule.py:debug:275] is_member_valid: False +[2025-10-24 09:40:55,378] [LogListener] [DEBUG] [loggerModule.py:debug:275] MIGAN 인페인팅 시도 +[2025-10-24 09:40:55,380] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 입력 형태 - 이미지: (1, 3, 800, 800), 마스크: (1, 1, 800, 800) +[2025-10-24 09:40:55,496] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 출력 형태: (800, 800, 3), dtype: uint8 +[2025-10-24 09:40:55,496] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 추론 완료: 117.02 ms +[2025-10-24 09:40:55,505] [LogListener] [DEBUG] [loggerModule.py:debug:275] MIGAN 인페인팅 성공 +[2025-10-24 09:40:55,506] [LogListener] [DEBUG] [loggerModule.py:debug:275] 메모리 변화 [인페인팅]: 43189.6MB -> 43207.6MB (+18.0MB, +0.0%) - 방법: migan +[2025-10-24 09:40:55,506] [LogListener] [DEBUG] [loggerModule.py:debug:275] 인페인팅 완료 +[2025-10-24 09:40:55,568] [LogListener] [DEBUG] [loggerModule.py:debug:275] 텍스트 렌더링 완료 +[2025-10-24 09:40:55,568] [LogListener] [DEBUG] [loggerModule.py:debug:275] watermark_text: 이미지 저작권 보유 +[2025-10-24 09:40:55,568] [LogListener] [DEBUG] [loggerModule.py:debug:275] is_watermark_enabled: True +[2025-10-24 09:40:55,868] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 저장 완료 : C:\ProgramData\ImgWorker\work\translated_option_img_12.webp +[2025-10-24 09:40:55,869] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 12 번역 완료: C:\ProgramData\ImgWorker\work\translated_option_img_12.webp +[2025-10-24 09:40:55,869] [LogListener] [DEBUG] [loggerModule.py:debug:275] ⏱ 이미지 파이프라인 총 3770.2ms | download=0.0ms | ocr=655.5ms | translate=2162.0ms | mask=2162.0ms | inpaint=133.0ms(migan/DirectML) | render=61.2ms | save=268.2ms +[2025-10-24 09:40:55,870] [LogListener] [DEBUG] [loggerModule.py:debug:275] process_single_image 호출 완료 +[2025-10-24 09:40:55,870] [ResultListener] [WARNING] [loggerModule.py:warning:287] 워커 롤링 스케줄: reason=job-count-threshold +[2025-10-24 09:40:55,870] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 결과 반환 중: uid=e43f623e-accc-4abe-8cb4-57638f049869 +[2025-10-24 09:40:55,870] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 결과 반환 완료: uid=e43f623e-accc-4abe-8cb4-57638f049869 +[2025-10-24 09:40:55,870] [WorkerRoller] [WARNING] [loggerModule.py:warning:287] 워커 롤링 시작: job-count-threshold +[2025-10-24 09:40:55,870] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 09:40:55,870] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 7640) +[2025-10-24 09:40:55,885] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔥 작업 수신 성공 +[2025-10-24 09:40:55,922] [LogListener] [DEBUG] [loggerModule.py:debug:275] Shutdown signal 수신 → 종료 +[2025-10-24 09:40:55,922] [LogListener] [DEBUG] [loggerModule.py:debug:275] OCR 모듈 정리 완료 +[2025-10-24 09:40:55,922] [LogListener] [DEBUG] [loggerModule.py:debug:275] 마스크 모듈 정리 완료 +[2025-10-24 09:40:55,923] [LogListener] [DEBUG] [loggerModule.py:debug:275] 임시 폴더 삭제됨: C:\ProgramData\ImgWorker\work +[2025-10-24 09:40:55,923] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 프로세서 소멸 +[2025-10-24 09:40:56,394] [WorkerRoller] [INFO] [loggerModule.py:info:281] 워커 프로세스 기동: PID=32504 +[2025-10-24 09:40:56,903] [LogListener] [DEBUG] [loggerModule.py:debug:275] ImageWorker 프로세스 기동 (PID=32504, Name=ImageWorkerProcess) +[2025-10-24 09:40:56,903] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔧 ImageProcessor3 초기화 시작... +[2025-10-24 09:40:56,903] [LogListener] [DEBUG] [loggerModule.py:debug:275] tracemalloc 메모리 추적 시작 +[2025-10-24 09:40:56,910] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ DirectML 환경 준비 완료 (Windows DirectX 12 기반) +[2025-10-24 09:40:56,910] [LogListener] [DEBUG] [loggerModule.py:debug:275] === 🚀 DirectML GPU 상태 초기화 시작 🚀 === +[2025-10-24 09:40:56,910] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🎯 사용자 GPU 가속 요청: False +[2025-10-24 09:40:56,910] [LogListener] [DEBUG] [loggerModule.py:debug:275] 💻 현재 운영체제: Windows +[2025-10-24 09:40:56,910] [LogListener] [DEBUG] [loggerModule.py:debug:275] GPU 가속이 비활성화됨 (toggle_states['use_cuda'] = False) +[2025-10-24 09:40:56,910] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔒 안전한 CPU 모드로 모든 GPU 설정 강제 비활성화 +[2025-10-24 09:40:56,911] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔧 ImageProcessor3 GPU 상태 요약: +[2025-10-24 09:40:56,911] [LogListener] [DEBUG] [loggerModule.py:debug:275] - CUDA 사용 가능: False +[2025-10-24 09:40:56,911] [LogListener] [DEBUG] [loggerModule.py:debug:275] - toggle_states['use_cuda']: NOT_SET +[2025-10-24 09:40:56,911] [LogListener] [DEBUG] [loggerModule.py:debug:275] - GPU 하드웨어 정보: {} +[2025-10-24 09:40:56,911] [LogListener] [DEBUG] [loggerModule.py:debug:275] ImageProcessor3 Init toggle_states: {'TEMP_IMAGE_DIR': 'C:\\ProgramData\\ImgWorker\\work', 'output_image_format': 'webp', 'use_local_rembg': True, 'local_rembg_model_path': 'D:\\py\\img_worker\\modules\\rembg_models', 'local_model_name': 'birefnet-general-lite', 'optionIMGTrans_type': 'CPU', 'detail_IMGTrans_type': 'CPU', 'thumb_trans_type': 'CPU', 'migan_use_accel': True, 'migan_onnx_path': 'D:\\py\\img_worker\\modules\\migan_onnx\\migan_pipeline_v2.onnx', 'ocr_provider_override': 'auto', 'migan_provider_override': 'auto'} +[2025-10-24 09:40:56,911] [LogListener] [DEBUG] [loggerModule.py:debug:275] is_member_valid: False +[2025-10-24 09:40:56,911] [LogListener] [DEBUG] [loggerModule.py:debug:275] debug_images 디렉토리 이미 존재: D:\py\img_worker\modules\debug_images +[2025-10-24 09:40:56,911] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.font_path: D:\py\img_worker\modules\fonts\HakgyoansimDunggeunmisoTTFB.ttf +[2025-10-24 09:40:56,912] [LogListener] [DEBUG] [loggerModule.py:debug:275] toggle_states font_path: D:\py\img_worker\modules\fonts\HakgyoansimDunggeunmisoTTFB.ttf +[2025-10-24 09:40:56,912] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.TEMP_IMAGE_DIR: C:\ProgramData\ImgWorker\work +[2025-10-24 09:40:56,912] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.debugging_save_Dir: D:\py\img_worker\modules\debug_images +[2025-10-24 09:40:56,912] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.unwanted_texts: {} +[2025-10-24 09:40:56,912] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.inpaint_method: migan +[2025-10-24 09:40:56,912] [LogListener] [DEBUG] [loggerModule.py:debug:275] Image.MAX_IMAGE_PIXELS set to 20000000 +[2025-10-24 09:40:56,912] [LogListener] [DEBUG] [loggerModule.py:debug:275] ONNX 모델 타입 설정값: 자동 선택, GPU 정보: {} +[2025-10-24 09:40:56,912] [LogListener] [DEBUG] [loggerModule.py:debug:275] 자동 선택 모드: GPU 추천 모델 simp 사용 +[2025-10-24 09:40:56,912] [LogListener] [DEBUG] [loggerModule.py:debug:275] ONNX OCR 모델 타입 결정: simp (GPU: False) +[2025-10-24 09:40:56,912] [LogListener] [DEBUG] [loggerModule.py:debug:275] ONNX OCR 모듈 CPU 모드로 설정 +[2025-10-24 09:40:56,912] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🚀 ONNX TextSystem 초기화 시작 (CPU 모드) +[2025-10-24 09:40:56,912] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🚀 ONNX TextSystem 초기화 시작 (CPU 모드) +[2025-10-24 09:40:58,474] [LogListener] [DEBUG] [loggerModule.py:debug:275] 문자 사전 파일 UTF-8 확인 완료: D:\py\img_worker\modules\onnx_ocr_module\dict\ppocr_keys_v1.txt +[2025-10-24 09:40:58,560] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ ONNX TextSystem 초기화 완료 (CPU + SIMP (호환성) 모델) +[2025-10-24 09:40:58,560] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ ONNX TextSystem 초기화 완료 (CPU + SIMP 모델) +[2025-10-24 09:40:58,560] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ ONNX OCR 모듈 초기화 성공 (CPU 모드) +[2025-10-24 09:40:58,561] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ ONNX OCR 모듈 초기화 성공 +[2025-10-24 09:40:58,561] [LogListener] [DEBUG] [loggerModule.py:debug:275] 마스크 모듈 초기화 완료 +[2025-10-24 09:40:58,561] [LogListener] [DEBUG] [loggerModule.py:debug:275] MaskModule 초기화 성공 +[2025-10-24 09:40:58,561] [LogListener] [DEBUG] [loggerModule.py:debug:275] 텍스트 렌더링 모듈 초기화 완료 +[2025-10-24 09:40:58,561] [LogListener] [DEBUG] [loggerModule.py:debug:275] 기본 폰트: D:\py\img_worker\modules\fonts\HakgyoansimDunggeunmisoTTFB.ttf +[2025-10-24 09:40:58,561] [LogListener] [DEBUG] [loggerModule.py:debug:275] TextRenderingModule 초기화 성공 +[2025-10-24 09:40:58,562] [LogListener] [DEBUG] [loggerModule.py:debug:275] 폰트 로드 성공: D:\py\img_worker\modules\fonts\HakgyoansimDunggeunmisoTTFB.ttf +[2025-10-24 09:40:58,562] [LogListener] [DEBUG] [loggerModule.py:debug:275] PostImageManager 초기화 성공 +[2025-10-24 09:40:58,562] [LogListener] [DEBUG] [loggerModule.py:debug:275] Request_AI_Server GPU 상태: CUDA 사용 가능=False +[2025-10-24 09:40:58,562] [LogListener] [DEBUG] [loggerModule.py:debug:275] Request_AI_Server 초기화 성공 +[2025-10-24 09:40:58,562] [LogListener] [DEBUG] [loggerModule.py:debug:275] GoogleTranslate 초기화 성공 +[2025-10-24 09:40:58,562] [LogListener] [DEBUG] [loggerModule.py:debug:275] MIGAN CUDA 사용 불가 - CPU 모드로 설정 +[2025-10-24 09:40:58,562] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] GPU 관리자 전달: GPUManager, can_use_cuda: False +[2025-10-24 09:40:58,582] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] GPU 관리자 사용 가능: False +[2025-10-24 09:40:58,789] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 사용 가능한 providers: ['DmlExecutionProvider', 'CPUExecutionProvider'] +[2025-10-24 09:40:58,790] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] DirectML 가속 활성화 +[2025-10-24 09:40:58,790] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 최종 providers: [('DmlExecutionProvider', {}), ('CPUExecutionProvider', {})] +[2025-10-24 09:40:58,790] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] DirectML 가속 시도: [('DmlExecutionProvider', {}), ('CPUExecutionProvider', {})] +[2025-10-24 09:40:58,790] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] DirectML 가속 성공! 실제 providers: ['DmlExecutionProvider', 'CPUExecutionProvider'] +[2025-10-24 09:40:58,790] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 입력 0: image, 형태: ['batch_size', 3, 'height', 'width'], 타입: tensor(uint8) +[2025-10-24 09:40:58,790] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 입력 1: mask, 형태: ['batch_size', 1, 'height', 'width'], 타입: tensor(uint8) +[2025-10-24 09:40:58,790] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 출력 0: result, 형태: ['ScatterNDresult_dim_0', 3, 'ScatterNDresult_dim_2', 'ScatterNDresult_dim_3'], 타입: tensor(uint8) +[2025-10-24 09:40:58,791] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 세션 준비 완료. providers=['DmlExecutionProvider', 'CPUExecutionProvider'] +[2025-10-24 09:40:58,791] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] GPU 관리자 연결 완료: GPUManager +[2025-10-24 09:40:58,791] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 파이프라인 gpu_manager 속성: True, 값: +[2025-10-24 09:40:58,791] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 초기화 완료: gpu_manager 속성=True, 값= +[2025-10-24 09:40:58,791] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔰 OCR 모듈 Warm-up 시작... +[2025-10-24 09:40:58,791] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔍 ONNX OCR 감지 방식: polygon +[2025-10-24 09:40:58,795] [LogListener] [DEBUG] [loggerModule.py:debug:275] ⚡ ONNX OCR 추론 완료: 4.0ms +[2025-10-24 09:40:58,796] [LogListener] [DEBUG] [loggerModule.py:debug:275] 📊 세부 시간 - 감지: 3.0ms, 인식: 0.0ms, 분류: 0.0ms +[2025-10-24 09:40:58,796] [LogListener] [WARNING] [loggerModule.py:warning:287] ⚠️ ONNX OCR 결과가 비어있습니다 +[2025-10-24 09:40:58,799] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ OCR 모듈 Warm-up 성공 +[2025-10-24 09:40:58,799] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔰 ImageProcessor Warm‑up 완료 +[2025-10-24 09:40:58,799] [ResultListener] [INFO] [loggerModule.py:info:281] 워커 READY 수신 +[2025-10-24 09:40:58,799] [WorkerRoller] [INFO] [loggerModule.py:info:281] 버퍼 작업 재개: 1건 +[2025-10-24 09:40:58,799] [ResultListener] [INFO] [loggerModule.py:info:281] 워커 READY 수신 +[2025-10-24 09:40:58,800] [LogListener] [DEBUG] [loggerModule.py:debug:275] 📡 추가 READY 신호 전송 완료 +[2025-10-24 09:40:58,800] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 09:40:58,800] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 32504) +[2025-10-24 09:40:58,800] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔥 작업 수신 성공 +[2025-10-24 09:40:58,800] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🚀 작업 처리 시작: cmd=process_single_image, uid=a1e59fd6-e785-482b-93df-bca41717d577 +[2025-10-24 09:40:58,800] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 프로세서 toggle_states 업데이트 : {'TEMP_IMAGE_DIR': 'C:\\ProgramData\\ImgWorker\\work', 'output_image_format': 'webp', 'use_local_rembg': True, 'local_rembg_model_path': 'D:\\py\\img_worker\\modules\\rembg_models', 'local_model_name': 'birefnet-general-lite', 'optionIMGTrans_type': 'GPU', 'detail_IMGTrans_type': 'GPU', 'thumb_trans_type': 'GPU', 'migan_use_accel': True, 'migan_onnx_path': 'D:\\py\\img_worker\\modules\\migan_onnx\\migan_pipeline_v2.onnx', 'ocr_provider_override': 'auto', 'migan_provider_override': 'auto', 'font_type': '폰트5', 'unwanted_texts': ['가격설명', '무료배송', '보증', '사은품', '세일', '이벤트', '특가', '품절', '할인', '할인가'], 'is_member_valid': False, 'authenticated_by_admin': True} +[2025-10-24 09:40:58,801] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 실행 직전: cmd=process_single_image +[2025-10-24 09:40:58,801] [LogListener] [DEBUG] [loggerModule.py:debug:275] process_single_image 호출 직전 +[2025-10-24 09:40:58,803] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 번역시작 +[2025-10-24 09:40:59,136] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 13 처리 시작: C:\ProgramData\ImgWorker\incoming\dl_5721e6d7815a.jpg - 전체 번역 모드 +[2025-10-24 09:40:59,141] [LogListener] [DEBUG] [loggerModule.py:debug:275] 로컬 파일 경로 감지, 다운로드 생략: C:\ProgramData\ImgWorker\incoming\dl_5721e6d7815a.jpg +[2025-10-24 09:40:59,141] [LogListener] [DEBUG] [loggerModule.py:debug:275] 옵션 이미지는 스케일 처리 건너뛰기: option +[2025-10-24 09:40:59,142] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 13 로컬 저장위치(옵션 이미지 원본 유지): C:\ProgramData\ImgWorker\incoming\dl_5721e6d7815a.jpg +[2025-10-24 09:40:59,143] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔍 ONNX OCR 감지 방식: polygon +[2025-10-24 09:40:59,714] [LogListener] [DEBUG] [loggerModule.py:debug:275] ⚡ ONNX OCR 추론 완료: 568.0ms +[2025-10-24 09:40:59,737] [LogListener] [DEBUG] [loggerModule.py:debug:275] 📊 세부 시간 - 감지: 110.0ms, 인식: 433.0ms, 분류: 19.0ms +[2025-10-24 09:40:59,753] [LogListener] [DEBUG] [loggerModule.py:debug:275] 메모리 변화 [OCR 처리]: 42891.7MB -> 43084.9MB (+193.1MB, +0.5%) - 이미지 13 +[2025-10-24 09:40:59,765] [LogListener] [DEBUG] [loggerModule.py:debug:275] ocr_results: [{'text': '暴走兔', 'confidence': 0.9315927028656006, 'polygon': [[136.0, 20.0], [271.0, 22.0], [270.0, 64.0], [135.0, 62.0]], 'bbox': (135, 20, 137, 45), 'method': 'polygon'}, {'text': 'BAOZOUTU', 'confidence': 0.993258535861969, 'polygon': [[134.0, 73.0], [273.0, 73.0], [273.0, 87.0], [134.0, 87.0]], 'bbox': (134, 73, 140, 15), 'method': 'polygon'}, {'text': '全波段收音机', 'confidence': 0.9974920749664307, 'polygon': [[81.0, 158.0], [288.0, 156.0], [288.0, 184.0], [81.0, 186.0]], 'bbox': (81, 156, 208, 31), 'method': 'polygon'}, {'text': '.R12', 'confidence': 0.8123915791511536, 'polygon': [[579.0, 152.0], [614.0, 152.0], [614.0, 163.0], [579.0, 163.0]], 'bbox': (579, 152, 36, 12), 'method': 'polygon'}, {'text': '518', 'confidence': 0.9942043423652649, 'polygon': [[487.0, 175.0], [606.0, 175.0], [606.0, 240.0], [487.0, 240.0]], 'bbox': (487, 175, 120, 66), 'method': 'polygon'}, {'text': '蓝牙音响', 'confidence': 0.9985915422439575, 'polygon': [[79.0, 214.0], [294.0, 214.0], [294.0, 248.0], [79.0, 248.0]], 'bbox': (79, 214, 216, 35), 'method': 'polygon'}, {'text': 'H:', 'confidence': 0.5186051726341248, 'polygon': [[608.0, 228.0], [621.0, 228.0], [621.0, 237.0], [608.0, 237.0]], 'bbox': (608, 228, 14, 10), 'method': 'polygon'}, {'text': 'MULTIBANDRADIO/BT/MUSICPLAYER', 'confidence': 0.9846541881561279, 'polygon': [[464.0, 243.0], [623.0, 244.0], [623.0, 258.0], [464.0, 257.0]], 'bbox': (464, 243, 160, 16), 'method': 'polygon'}, {'text': '4000毫安锂电池', 'confidence': 0.9973577857017517, 'polygon': [[83.0, 279.0], [289.0, 279.0], [289.0, 303.0], [83.0, 303.0]], 'bbox': (83, 279, 207, 25), 'method': 'polygon'}, {'text': '手', 'confidence': 0.9999408721923828, 'polygon': [[80.0, 335.0], [117.0, 335.0], [117.0, 365.0], [80.0, 365.0]], 'bbox': (80, 335, 38, 31), 'method': 'polygon'}, {'text': '电', 'confidence': 0.9993985891342163, 'polygon': [[169.0, 338.0], [203.0, 338.0], [203.0, 364.0], [169.0, 364.0]], 'bbox': (169, 338, 35, 27), 'method': 'polygon'}, {'text': '筒', 'confidence': 0.9992778897285461, 'polygon': [[263.0, 336.0], [291.0, 336.0], [291.0, 365.0], [263.0, 365.0]], 'bbox': (263, 336, 29, 30), 'method': 'polygon'}, {'text': 'SOS警报/指南针', 'confidence': 0.9673583507537842, 'polygon': [[81.0, 397.0], [288.0, 397.0], [288.0, 425.0], [81.0, 425.0]], 'bbox': (81, 397, 208, 29), 'method': 'polygon'}, {'text': '手摇/太阳能发电', 'confidence': 0.9900031685829163, 'polygon': [[83.0, 457.0], [289.0, 457.0], [289.0, 485.0], [83.0, 485.0]], 'bbox': (83, 457, 207, 29), 'method': 'polygon'}, {'text': '可插U盘/TF卡', 'confidence': 0.990487277507782, 'polygon': [[81.0, 516.0], [290.0, 518.0], [290.0, 546.0], [81.0, 544.0]], 'bbox': (81, 516, 210, 31), 'method': 'polygon'}, {'text': '限时', 'confidence': 0.9988158941268921, 'polygon': [[17.0, 659.0], [150.0, 661.0], [149.0, 729.0], [16.0, 727.0]], 'bbox': (16, 659, 135, 71), 'method': 'polygon'}, {'text': '赠送', 'confidence': 0.9996625185012817, 'polygon': [[15.0, 728.0], [149.0, 728.0], [149.0, 792.0], [15.0, 792.0]], 'bbox': (15, 728, 135, 65), 'method': 'polygon'}, {'text': '有线耳机', 'confidence': 0.9988623857498169, 'polygon': [[188.0, 769.0], [265.0, 769.0], [265.0, 790.0], [188.0, 790.0]], 'bbox': (188, 769, 78, 22), 'method': 'polygon'}, {'text': '延长天线', 'confidence': 0.9987125396728516, 'polygon': [[286.0, 769.0], [365.0, 769.0], [365.0, 790.0], [286.0, 790.0]], 'bbox': (286, 769, 80, 22), 'method': 'polygon'}, {'text': '内存卡', 'confidence': 0.9983070492744446, 'polygon': [[386.0, 769.0], [446.0, 769.0], [446.0, 790.0], [386.0, 790.0]], 'bbox': (386, 769, 61, 22), 'method': 'polygon'}, {'text': '读卡器', 'confidence': 0.9987971186637878, 'polygon': [[472.0, 767.0], [531.0, 767.0], [531.0, 792.0], [472.0, 792.0]], 'bbox': (472, 767, 60, 26), 'method': 'polygon'}, {'text': '充电线', 'confidence': 0.9993236064910889, 'polygon': [[560.0, 769.0], [621.0, 769.0], [621.0, 790.0], [560.0, 790.0]], 'bbox': (560, 769, 62, 22), 'method': 'polygon'}, {'text': '礼盒礼袋', 'confidence': 0.9986110329627991, 'polygon': [[641.0, 769.0], [718.0, 769.0], [718.0, 790.0], [641.0, 790.0]], 'bbox': (641, 769, 78, 22), 'method': 'polygon'}, {'text': '充电头', 'confidence': 0.9981960654258728, 'polygon': [[738.0, 767.0], [797.0, 767.0], [797.0, 792.0], [738.0, 792.0]], 'bbox': (738, 767, 60, 26), 'method': 'polygon'}] +[2025-10-24 09:40:59,765] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 93.2%): '暴走兔' +[2025-10-24 09:40:59,765] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): 'BAOZOUTU' +[2025-10-24 09:40:59,766] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.7%): '全波段收音机' +[2025-10-24 09:40:59,766] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): '.R12' +[2025-10-24 09:40:59,766] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): '518' +[2025-10-24 09:40:59,766] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.9%): '蓝牙音响' +[2025-10-24 09:40:59,766] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): 'H:' +[2025-10-24 09:40:59,766] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): 'MULTIBANDRADIO/BT/MUSICPLAYER' +[2025-10-24 09:40:59,766] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.7%): '4000毫安锂电池' +[2025-10-24 09:40:59,766] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 100.0%): '手' +[2025-10-24 09:40:59,766] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.9%): '电' +[2025-10-24 09:40:59,766] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.9%): '筒' +[2025-10-24 09:40:59,766] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 96.7%): 'SOS警报/指南针' +[2025-10-24 09:40:59,766] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.0%): '手摇/太阳能发电' +[2025-10-24 09:40:59,766] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.0%): '可插U盘/TF卡' +[2025-10-24 09:40:59,766] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.9%): '限时' +[2025-10-24 09:40:59,766] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 100.0%): '赠送' +[2025-10-24 09:40:59,766] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.9%): '有线耳机' +[2025-10-24 09:40:59,767] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.9%): '延长天线' +[2025-10-24 09:40:59,767] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.8%): '内存卡' +[2025-10-24 09:40:59,767] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.9%): '读卡器' +[2025-10-24 09:40:59,767] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.9%): '充电线' +[2025-10-24 09:40:59,767] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.9%): '礼盒礼袋' +[2025-10-24 09:40:59,767] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.8%): '充电头' +[2025-10-24 09:40:59,767] [LogListener] [DEBUG] [loggerModule.py:debug:275] 필터링 결과: 19/24개 (신뢰도 + & 중국어) +[2025-10-24 09:40:59,767] [LogListener] [DEBUG] [loggerModule.py:debug:275] filter_ocr_results: [{'text': '暴走兔', 'confidence': 0.9315927028656006, 'polygon': [[136.0, 20.0], [271.0, 22.0], [270.0, 64.0], [135.0, 62.0]], 'bbox': (135, 20, 137, 45), 'method': 'polygon'}, {'text': '全波段收音机', 'confidence': 0.9974920749664307, 'polygon': [[81.0, 158.0], [288.0, 156.0], [288.0, 184.0], [81.0, 186.0]], 'bbox': (81, 156, 208, 31), 'method': 'polygon'}, {'text': '蓝牙音响', 'confidence': 0.9985915422439575, 'polygon': [[79.0, 214.0], [294.0, 214.0], [294.0, 248.0], [79.0, 248.0]], 'bbox': (79, 214, 216, 35), 'method': 'polygon'}, {'text': '4000毫安锂电池', 'confidence': 0.9973577857017517, 'polygon': [[83.0, 279.0], [289.0, 279.0], [289.0, 303.0], [83.0, 303.0]], 'bbox': (83, 279, 207, 25), 'method': 'polygon'}, {'text': '手', 'confidence': 0.9999408721923828, 'polygon': [[80.0, 335.0], [117.0, 335.0], [117.0, 365.0], [80.0, 365.0]], 'bbox': (80, 335, 38, 31), 'method': 'polygon'}, {'text': '电', 'confidence': 0.9993985891342163, 'polygon': [[169.0, 338.0], [203.0, 338.0], [203.0, 364.0], [169.0, 364.0]], 'bbox': (169, 338, 35, 27), 'method': 'polygon'}, {'text': '筒', 'confidence': 0.9992778897285461, 'polygon': [[263.0, 336.0], [291.0, 336.0], [291.0, 365.0], [263.0, 365.0]], 'bbox': (263, 336, 29, 30), 'method': 'polygon'}, {'text': 'SOS警报/指南针', 'confidence': 0.9673583507537842, 'polygon': [[81.0, 397.0], [288.0, 397.0], [288.0, 425.0], [81.0, 425.0]], 'bbox': (81, 397, 208, 29), 'method': 'polygon'}, {'text': '手摇/太阳能发电', 'confidence': 0.9900031685829163, 'polygon': [[83.0, 457.0], [289.0, 457.0], [289.0, 485.0], [83.0, 485.0]], 'bbox': (83, 457, 207, 29), 'method': 'polygon'}, {'text': '可插U盘/TF卡', 'confidence': 0.990487277507782, 'polygon': [[81.0, 516.0], [290.0, 518.0], [290.0, 546.0], [81.0, 544.0]], 'bbox': (81, 516, 210, 31), 'method': 'polygon'}, {'text': '限时', 'confidence': 0.9988158941268921, 'polygon': [[17.0, 659.0], [150.0, 661.0], [149.0, 729.0], [16.0, 727.0]], 'bbox': (16, 659, 135, 71), 'method': 'polygon'}, {'text': '赠送', 'confidence': 0.9996625185012817, 'polygon': [[15.0, 728.0], [149.0, 728.0], [149.0, 792.0], [15.0, 792.0]], 'bbox': (15, 728, 135, 65), 'method': 'polygon'}, {'text': '有线耳机', 'confidence': 0.9988623857498169, 'polygon': [[188.0, 769.0], [265.0, 769.0], [265.0, 790.0], [188.0, 790.0]], 'bbox': (188, 769, 78, 22), 'method': 'polygon'}, {'text': '延长天线', 'confidence': 0.9987125396728516, 'polygon': [[286.0, 769.0], [365.0, 769.0], [365.0, 790.0], [286.0, 790.0]], 'bbox': (286, 769, 80, 22), 'method': 'polygon'}, {'text': '内存卡', 'confidence': 0.9983070492744446, 'polygon': [[386.0, 769.0], [446.0, 769.0], [446.0, 790.0], [386.0, 790.0]], 'bbox': (386, 769, 61, 22), 'method': 'polygon'}, {'text': '读卡器', 'confidence': 0.9987971186637878, 'polygon': [[472.0, 767.0], [531.0, 767.0], [531.0, 792.0], [472.0, 792.0]], 'bbox': (472, 767, 60, 26), 'method': 'polygon'}, {'text': '充电线', 'confidence': 0.9993236064910889, 'polygon': [[560.0, 769.0], [621.0, 769.0], [621.0, 790.0], [560.0, 790.0]], 'bbox': (560, 769, 62, 22), 'method': 'polygon'}, {'text': '礼盒礼袋', 'confidence': 0.9986110329627991, 'polygon': [[641.0, 769.0], [718.0, 769.0], [718.0, 790.0], [641.0, 790.0]], 'bbox': (641, 769, 78, 22), 'method': 'polygon'}, {'text': '充电头', 'confidence': 0.9981960654258728, 'polygon': [[738.0, 767.0], [797.0, 767.0], [797.0, 792.0], [738.0, 792.0]], 'bbox': (738, 767, 60, 26), 'method': 'polygon'}] +[2025-10-24 09:40:59,784] [LogListener] [DEBUG] [loggerModule.py:debug:275] 중국어 텍스트 19개 필터링 완료 +[2025-10-24 09:40:59,800] [LogListener] [DEBUG] [loggerModule.py:debug:275] 한글 텍스트 0개 필터링 완료 +[2025-10-24 09:41:00,464] [LogListener] [DEBUG] [loggerModule.py:debug:275] translated_texts: ['도망친 토끼', '풀 밴드 라디오', '블루투스 스피커', '4000mAh 리튬 배터리', '손', '전기', '실린더', 'SOS 경보 / 나침반', '핸드 크랭크 / 태양광 발전', 'U 디스크 삽입 가능 / TF 카드', '제한된 시간', '포기하다', '유선 헤드폰', '확장 안테나', '메모리 카드', '카드 리더', '충전 케이블', '선물 상자 선물 가방', '충전 헤드'] +[2025-10-24 09:41:00,465] [LogListener] [DEBUG] [loggerModule.py:debug:275] 마스크 생성 완료 +[2025-10-24 09:41:00,465] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 13 OCR 권한 없음, 전체 번역 모드 +[2025-10-24 09:41:00,472] [LogListener] [DEBUG] [loggerModule.py:debug:275] [AUTO Inpaint] coverage=0.208, comps=14, min_center_dist=0.053 → request +[2025-10-24 09:41:00,472] [LogListener] [DEBUG] [loggerModule.py:debug:275] ocr_count: 19 +[2025-10-24 09:41:00,472] [LogListener] [DEBUG] [loggerModule.py:debug:275] is_member_valid: False +[2025-10-24 09:41:00,472] [LogListener] [DEBUG] [loggerModule.py:debug:275] MIGAN 인페인팅 시도 +[2025-10-24 09:41:00,474] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 입력 형태 - 이미지: (1, 3, 800, 800), 마스크: (1, 1, 800, 800) +[2025-10-24 09:41:00,831] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 출력 형태: (800, 800, 3), dtype: uint8 +[2025-10-24 09:41:00,831] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 추론 완료: 356.75 ms +[2025-10-24 09:41:00,838] [LogListener] [DEBUG] [loggerModule.py:debug:275] MIGAN 인페인팅 성공 +[2025-10-24 09:41:00,838] [LogListener] [DEBUG] [loggerModule.py:debug:275] 메모리 변화 [인페인팅]: 43129.7MB -> 43328.9MB (+199.2MB, +0.5%) - 방법: migan +[2025-10-24 09:41:00,838] [LogListener] [DEBUG] [loggerModule.py:debug:275] 인페인팅 완료 +[2025-10-24 09:41:00,912] [LogListener] [DEBUG] [loggerModule.py:debug:275] 텍스트 렌더링 완료 +[2025-10-24 09:41:00,913] [LogListener] [DEBUG] [loggerModule.py:debug:275] watermark_text: 이미지 저작권 보유 +[2025-10-24 09:41:00,913] [LogListener] [DEBUG] [loggerModule.py:debug:275] is_watermark_enabled: True +[2025-10-24 09:41:01,227] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 저장 완료 : C:\ProgramData\ImgWorker\work\translated_option_img_13.webp +[2025-10-24 09:41:01,228] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 13 번역 완료: C:\ProgramData\ImgWorker\work\translated_option_img_13.webp +[2025-10-24 09:41:01,228] [LogListener] [DEBUG] [loggerModule.py:debug:275] ⏱ 이미지 파이프라인 총 2426.3ms | download=0.0ms | ocr=578.0ms | translate=698.3ms | mask=698.3ms | inpaint=372.7ms(migan/DirectML) | render=73.5ms | save=270.6ms +[2025-10-24 09:41:01,229] [LogListener] [DEBUG] [loggerModule.py:debug:275] process_single_image 호출 완료 +[2025-10-24 09:41:01,229] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 결과 반환 중: uid=a1e59fd6-e785-482b-93df-bca41717d577 +[2025-10-24 09:41:01,229] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 결과 반환 완료: uid=a1e59fd6-e785-482b-93df-bca41717d577 +[2025-10-24 09:41:01,230] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 09:41:01,230] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 32504) +[2025-10-24 09:41:03,050] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔥 작업 수신 성공 +[2025-10-24 09:41:03,050] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🚀 작업 처리 시작: cmd=process_single_image, uid=878dbdc9-a5ef-4427-a1df-bd59f9c14f55 +[2025-10-24 09:41:03,050] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 프로세서 toggle_states 업데이트 : {'TEMP_IMAGE_DIR': 'C:\\ProgramData\\ImgWorker\\work', 'output_image_format': 'webp', 'use_local_rembg': True, 'local_rembg_model_path': 'D:\\py\\img_worker\\modules\\rembg_models', 'local_model_name': 'birefnet-general-lite', 'optionIMGTrans_type': 'GPU', 'detail_IMGTrans_type': 'GPU', 'thumb_trans_type': 'GPU', 'migan_use_accel': True, 'migan_onnx_path': 'D:\\py\\img_worker\\modules\\migan_onnx\\migan_pipeline_v2.onnx', 'ocr_provider_override': 'auto', 'migan_provider_override': 'auto', 'font_type': '폰트5', 'unwanted_texts': ['가격설명', '무료배송', '보증', '사은품', '세일', '이벤트', '특가', '품절', '할인', '할인가'], 'is_member_valid': False, 'authenticated_by_admin': True} +[2025-10-24 09:41:03,050] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 실행 직전: cmd=process_single_image +[2025-10-24 09:41:03,050] [LogListener] [DEBUG] [loggerModule.py:debug:275] process_single_image 호출 직전 +[2025-10-24 09:41:03,050] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 번역시작 +[2025-10-24 09:41:03,331] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 14 처리 시작: C:\ProgramData\ImgWorker\incoming\dl_4678f3e7b312.jpg - 전체 번역 모드 +[2025-10-24 09:41:03,336] [LogListener] [DEBUG] [loggerModule.py:debug:275] 로컬 파일 경로 감지, 다운로드 생략: C:\ProgramData\ImgWorker\incoming\dl_4678f3e7b312.jpg +[2025-10-24 09:41:03,336] [LogListener] [DEBUG] [loggerModule.py:debug:275] 옵션 이미지는 스케일 처리 건너뛰기: option +[2025-10-24 09:41:03,337] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 14 로컬 저장위치(옵션 이미지 원본 유지): C:\ProgramData\ImgWorker\incoming\dl_4678f3e7b312.jpg +[2025-10-24 09:41:03,338] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔍 ONNX OCR 감지 방식: polygon +[2025-10-24 09:41:03,869] [LogListener] [DEBUG] [loggerModule.py:debug:275] ⚡ ONNX OCR 추론 완료: 528.5ms +[2025-10-24 09:41:03,886] [LogListener] [DEBUG] [loggerModule.py:debug:275] 📊 세부 시간 - 감지: 98.1ms, 인식: 408.0ms, 분류: 17.0ms +[2025-10-24 09:41:03,897] [LogListener] [DEBUG] [loggerModule.py:debug:275] 메모리 변화 [OCR 처리]: 43405.4MB -> 43408.1MB (+2.7MB, +0.0%) - 이미지 14 +[2025-10-24 09:41:03,897] [LogListener] [DEBUG] [loggerModule.py:debug:275] ocr_results: [{'text': '暴走兔', 'confidence': 0.9232043623924255, 'polygon': [[136.0, 21.0], [270.0, 23.0], [269.0, 63.0], [135.0, 61.0]], 'bbox': (135, 21, 136, 43), 'method': 'polygon'}, {'text': 'BAOZOUTU', 'confidence': 0.9927327036857605, 'polygon': [[134.0, 73.0], [273.0, 73.0], [273.0, 87.0], [134.0, 87.0]], 'bbox': (134, 73, 140, 15), 'method': 'polygon'}, {'text': '全波段收音机', 'confidence': 0.9977476000785828, 'polygon': [[81.0, 158.0], [288.0, 156.0], [288.0, 184.0], [81.0, 186.0]], 'bbox': (81, 156, 208, 31), 'method': 'polygon'}, {'text': '.R12', 'confidence': 0.8752309083938599, 'polygon': [[577.0, 152.0], [614.0, 152.0], [614.0, 163.0], [577.0, 163.0]], 'bbox': (577, 152, 38, 12), 'method': 'polygon'}, {'text': '518', 'confidence': 0.9874899387359619, 'polygon': [[487.0, 175.0], [607.0, 175.0], [607.0, 240.0], [487.0, 240.0]], 'bbox': (487, 175, 121, 66), 'method': 'polygon'}, {'text': '蓝牙音响', 'confidence': 0.998043417930603, 'polygon': [[78.0, 214.0], [293.0, 214.0], [293.0, 248.0], [78.0, 248.0]], 'bbox': (78, 214, 216, 35), 'method': 'polygon'}, {'text': 'MULTIBANDRADIO/BT/MUSICPLAYER', 'confidence': 0.9786054491996765, 'polygon': [[464.0, 244.0], [623.0, 244.0], [623.0, 258.0], [464.0, 258.0]], 'bbox': (464, 244, 160, 15), 'method': 'polygon'}, {'text': '4000毫安锂电池', 'confidence': 0.9973577857017517, 'polygon': [[83.0, 279.0], [289.0, 279.0], [289.0, 303.0], [83.0, 303.0]], 'bbox': (83, 279, 207, 25), 'method': 'polygon'}, {'text': '手', 'confidence': 0.9999405145645142, 'polygon': [[80.0, 335.0], [116.0, 335.0], [116.0, 365.0], [80.0, 365.0]], 'bbox': (80, 335, 37, 31), 'method': 'polygon'}, {'text': '电', 'confidence': 0.9990990161895752, 'polygon': [[168.0, 336.0], [203.0, 336.0], [203.0, 365.0], [168.0, 365.0]], 'bbox': (168, 336, 36, 30), 'method': 'polygon'}, {'text': '筒', 'confidence': 0.9983933568000793, 'polygon': [[263.0, 336.0], [291.0, 336.0], [291.0, 365.0], [263.0, 365.0]], 'bbox': (263, 336, 29, 30), 'method': 'polygon'}, {'text': 'SOS警报/指南针', 'confidence': 0.9725102186203003, 'polygon': [[81.0, 397.0], [288.0, 397.0], [288.0, 425.0], [81.0, 425.0]], 'bbox': (81, 397, 208, 29), 'method': 'polygon'}, {'text': '燕走兔', 'confidence': 0.5099726915359497, 'polygon': [[531.0, 409.0], [595.0, 411.0], [594.0, 441.0], [530.0, 439.0]], 'bbox': (530, 409, 66, 33), 'method': 'polygon'}, {'text': '手摇/太阳能发电', 'confidence': 0.9911617636680603, 'polygon': [[83.0, 457.0], [289.0, 457.0], [289.0, 485.0], [83.0, 485.0]], 'bbox': (83, 457, 207, 29), 'method': 'polygon'}, {'text': '可插U盘/TF卡', 'confidence': 0.9894267320632935, 'polygon': [[81.0, 516.0], [290.0, 518.0], [290.0, 546.0], [81.0, 544.0]], 'bbox': (81, 516, 210, 31), 'method': 'polygon'}, {'text': '限时', 'confidence': 0.998805046081543, 'polygon': [[17.0, 659.0], [150.0, 661.0], [149.0, 729.0], [16.0, 727.0]], 'bbox': (16, 659, 135, 71), 'method': 'polygon'}, {'text': '赠送', 'confidence': 0.9996638894081116, 'polygon': [[15.0, 728.0], [149.0, 728.0], [149.0, 792.0], [15.0, 792.0]], 'bbox': (15, 728, 135, 65), 'method': 'polygon'}, {'text': '有线耳机', 'confidence': 0.9986416101455688, 'polygon': [[189.0, 769.0], [265.0, 769.0], [265.0, 790.0], [189.0, 790.0]], 'bbox': (189, 769, 77, 22), 'method': 'polygon'}, {'text': '延长天线', 'confidence': 0.9986200928688049, 'polygon': [[286.0, 769.0], [365.0, 769.0], [365.0, 790.0], [286.0, 790.0]], 'bbox': (286, 769, 80, 22), 'method': 'polygon'}, {'text': '内存卡', 'confidence': 0.9982127547264099, 'polygon': [[386.0, 769.0], [446.0, 769.0], [446.0, 790.0], [386.0, 790.0]], 'bbox': (386, 769, 61, 22), 'method': 'polygon'}, {'text': '读卡器', 'confidence': 0.9986720085144043, 'polygon': [[472.0, 767.0], [531.0, 767.0], [531.0, 792.0], [472.0, 792.0]], 'bbox': (472, 767, 60, 26), 'method': 'polygon'}, {'text': '充电线', 'confidence': 0.9993775486946106, 'polygon': [[560.0, 769.0], [621.0, 769.0], [621.0, 790.0], [560.0, 790.0]], 'bbox': (560, 769, 62, 22), 'method': 'polygon'}, {'text': '礼盒礼袋', 'confidence': 0.9985522031784058, 'polygon': [[640.0, 769.0], [717.0, 769.0], [717.0, 790.0], [640.0, 790.0]], 'bbox': (640, 769, 78, 22), 'method': 'polygon'}, {'text': '充电头', 'confidence': 0.9980430603027344, 'polygon': [[738.0, 767.0], [797.0, 767.0], [797.0, 792.0], [738.0, 792.0]], 'bbox': (738, 767, 60, 26), 'method': 'polygon'}] +[2025-10-24 09:41:03,898] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 92.3%): '暴走兔' +[2025-10-24 09:41:03,898] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): 'BAOZOUTU' +[2025-10-24 09:41:03,898] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.8%): '全波段收音机' +[2025-10-24 09:41:03,898] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): '.R12' +[2025-10-24 09:41:03,898] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): '518' +[2025-10-24 09:41:03,898] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.8%): '蓝牙音响' +[2025-10-24 09:41:03,898] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): 'MULTIBANDRADIO/BT/MUSICPLAYER' +[2025-10-24 09:41:03,898] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.7%): '4000毫安锂电池' +[2025-10-24 09:41:03,898] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 100.0%): '手' +[2025-10-24 09:41:03,898] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.9%): '电' +[2025-10-24 09:41:03,898] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.8%): '筒' +[2025-10-24 09:41:03,898] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 97.3%): 'SOS警报/指南针' +[2025-10-24 09:41:03,898] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 51.0%): '燕走兔' +[2025-10-24 09:41:03,899] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.1%): '手摇/太阳能发电' +[2025-10-24 09:41:03,899] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 98.9%): '可插U盘/TF卡' +[2025-10-24 09:41:03,899] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.9%): '限时' +[2025-10-24 09:41:03,899] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 100.0%): '赠送' +[2025-10-24 09:41:03,899] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.9%): '有线耳机' +[2025-10-24 09:41:03,899] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.9%): '延长天线' +[2025-10-24 09:41:03,899] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.8%): '内存卡' +[2025-10-24 09:41:03,899] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.9%): '读卡器' +[2025-10-24 09:41:03,899] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.9%): '充电线' +[2025-10-24 09:41:03,899] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.9%): '礼盒礼袋' +[2025-10-24 09:41:03,899] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.8%): '充电头' +[2025-10-24 09:41:03,899] [LogListener] [DEBUG] [loggerModule.py:debug:275] 필터링 결과: 20/24개 (신뢰도 + & 중국어) +[2025-10-24 09:41:03,899] [LogListener] [DEBUG] [loggerModule.py:debug:275] filter_ocr_results: [{'text': '暴走兔', 'confidence': 0.9232043623924255, 'polygon': [[136.0, 21.0], [270.0, 23.0], [269.0, 63.0], [135.0, 61.0]], 'bbox': (135, 21, 136, 43), 'method': 'polygon'}, {'text': '全波段收音机', 'confidence': 0.9977476000785828, 'polygon': [[81.0, 158.0], [288.0, 156.0], [288.0, 184.0], [81.0, 186.0]], 'bbox': (81, 156, 208, 31), 'method': 'polygon'}, {'text': '蓝牙音响', 'confidence': 0.998043417930603, 'polygon': [[78.0, 214.0], [293.0, 214.0], [293.0, 248.0], [78.0, 248.0]], 'bbox': (78, 214, 216, 35), 'method': 'polygon'}, {'text': '4000毫安锂电池', 'confidence': 0.9973577857017517, 'polygon': [[83.0, 279.0], [289.0, 279.0], [289.0, 303.0], [83.0, 303.0]], 'bbox': (83, 279, 207, 25), 'method': 'polygon'}, {'text': '手', 'confidence': 0.9999405145645142, 'polygon': [[80.0, 335.0], [116.0, 335.0], [116.0, 365.0], [80.0, 365.0]], 'bbox': (80, 335, 37, 31), 'method': 'polygon'}, {'text': '电', 'confidence': 0.9990990161895752, 'polygon': [[168.0, 336.0], [203.0, 336.0], [203.0, 365.0], [168.0, 365.0]], 'bbox': (168, 336, 36, 30), 'method': 'polygon'}, {'text': '筒', 'confidence': 0.9983933568000793, 'polygon': [[263.0, 336.0], [291.0, 336.0], [291.0, 365.0], [263.0, 365.0]], 'bbox': (263, 336, 29, 30), 'method': 'polygon'}, {'text': 'SOS警报/指南针', 'confidence': 0.9725102186203003, 'polygon': [[81.0, 397.0], [288.0, 397.0], [288.0, 425.0], [81.0, 425.0]], 'bbox': (81, 397, 208, 29), 'method': 'polygon'}, {'text': '燕走兔', 'confidence': 0.5099726915359497, 'polygon': [[531.0, 409.0], [595.0, 411.0], [594.0, 441.0], [530.0, 439.0]], 'bbox': (530, 409, 66, 33), 'method': 'polygon'}, {'text': '手摇/太阳能发电', 'confidence': 0.9911617636680603, 'polygon': [[83.0, 457.0], [289.0, 457.0], [289.0, 485.0], [83.0, 485.0]], 'bbox': (83, 457, 207, 29), 'method': 'polygon'}, {'text': '可插U盘/TF卡', 'confidence': 0.9894267320632935, 'polygon': [[81.0, 516.0], [290.0, 518.0], [290.0, 546.0], [81.0, 544.0]], 'bbox': (81, 516, 210, 31), 'method': 'polygon'}, {'text': '限时', 'confidence': 0.998805046081543, 'polygon': [[17.0, 659.0], [150.0, 661.0], [149.0, 729.0], [16.0, 727.0]], 'bbox': (16, 659, 135, 71), 'method': 'polygon'}, {'text': '赠送', 'confidence': 0.9996638894081116, 'polygon': [[15.0, 728.0], [149.0, 728.0], [149.0, 792.0], [15.0, 792.0]], 'bbox': (15, 728, 135, 65), 'method': 'polygon'}, {'text': '有线耳机', 'confidence': 0.9986416101455688, 'polygon': [[189.0, 769.0], [265.0, 769.0], [265.0, 790.0], [189.0, 790.0]], 'bbox': (189, 769, 77, 22), 'method': 'polygon'}, {'text': '延长天线', 'confidence': 0.9986200928688049, 'polygon': [[286.0, 769.0], [365.0, 769.0], [365.0, 790.0], [286.0, 790.0]], 'bbox': (286, 769, 80, 22), 'method': 'polygon'}, {'text': '内存卡', 'confidence': 0.9982127547264099, 'polygon': [[386.0, 769.0], [446.0, 769.0], [446.0, 790.0], [386.0, 790.0]], 'bbox': (386, 769, 61, 22), 'method': 'polygon'}, {'text': '读卡器', 'confidence': 0.9986720085144043, 'polygon': [[472.0, 767.0], [531.0, 767.0], [531.0, 792.0], [472.0, 792.0]], 'bbox': (472, 767, 60, 26), 'method': 'polygon'}, {'text': '充电线', 'confidence': 0.9993775486946106, 'polygon': [[560.0, 769.0], [621.0, 769.0], [621.0, 790.0], [560.0, 790.0]], 'bbox': (560, 769, 62, 22), 'method': 'polygon'}, {'text': '礼盒礼袋', 'confidence': 0.9985522031784058, 'polygon': [[640.0, 769.0], [717.0, 769.0], [717.0, 790.0], [640.0, 790.0]], 'bbox': (640, 769, 78, 22), 'method': 'polygon'}, {'text': '充电头', 'confidence': 0.9980430603027344, 'polygon': [[738.0, 767.0], [797.0, 767.0], [797.0, 792.0], [738.0, 792.0]], 'bbox': (738, 767, 60, 26), 'method': 'polygon'}] +[2025-10-24 09:41:03,900] [LogListener] [DEBUG] [loggerModule.py:debug:275] 중국어 텍스트 20개 필터링 완료 +[2025-10-24 09:41:03,900] [LogListener] [DEBUG] [loggerModule.py:debug:275] 한글 텍스트 0개 필터링 완료 +[2025-10-24 09:41:04,338] [LogListener] [DEBUG] [loggerModule.py:debug:275] translated_texts: ['도망친 토끼', '풀 밴드 라디오', '블루투스 스피커', '4000mAh 리튬 배터리', '손', '전기', '실린더', 'SOS 경보 / 나침반', '얀 주이투', '핸드 크랭크 / 태양광 발전', 'U 디스크 삽입 가능 / TF 카드', '제한된 시간', '포기하다', '유선 헤드폰', '확장 안테나', '메모리 카드', '카드 리더', '충전 케이블', '선물 상자 선물 가방', '충전 헤드'] +[2025-10-24 09:41:04,339] [LogListener] [DEBUG] [loggerModule.py:debug:275] 마스크 생성 완료 +[2025-10-24 09:41:04,339] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 14 OCR 권한 없음, 전체 번역 모드 +[2025-10-24 09:41:04,347] [LogListener] [DEBUG] [loggerModule.py:debug:275] [AUTO Inpaint] coverage=0.269, comps=5, min_center_dist=0.273 → request +[2025-10-24 09:41:04,347] [LogListener] [DEBUG] [loggerModule.py:debug:275] ocr_count: 20 +[2025-10-24 09:41:04,347] [LogListener] [DEBUG] [loggerModule.py:debug:275] is_member_valid: False +[2025-10-24 09:41:04,347] [LogListener] [DEBUG] [loggerModule.py:debug:275] MIGAN 인페인팅 시도 +[2025-10-24 09:41:04,349] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 입력 형태 - 이미지: (1, 3, 800, 800), 마스크: (1, 1, 800, 800) +[2025-10-24 09:41:04,576] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 출력 형태: (800, 800, 3), dtype: uint8 +[2025-10-24 09:41:04,576] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 추론 완료: 227.00 ms +[2025-10-24 09:41:04,585] [LogListener] [DEBUG] [loggerModule.py:debug:275] MIGAN 인페인팅 성공 +[2025-10-24 09:41:04,586] [LogListener] [DEBUG] [loggerModule.py:debug:275] 메모리 변화 [인페인팅]: 43409.2MB -> 43414.4MB (+5.1MB, +0.0%) - 방법: migan +[2025-10-24 09:41:04,586] [LogListener] [DEBUG] [loggerModule.py:debug:275] 인페인팅 완료 +[2025-10-24 09:41:04,662] [LogListener] [DEBUG] [loggerModule.py:debug:275] 텍스트 렌더링 완료 +[2025-10-24 09:41:04,662] [LogListener] [DEBUG] [loggerModule.py:debug:275] watermark_text: 이미지 저작권 보유 +[2025-10-24 09:41:04,662] [LogListener] [DEBUG] [loggerModule.py:debug:275] is_watermark_enabled: True +[2025-10-24 09:41:05,019] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 저장 완료 : C:\ProgramData\ImgWorker\work\translated_option_img_14.webp +[2025-10-24 09:41:05,020] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 14 번역 완료: C:\ProgramData\ImgWorker\work\translated_option_img_14.webp +[2025-10-24 09:41:05,020] [LogListener] [DEBUG] [loggerModule.py:debug:275] ⏱ 이미지 파이프라인 총 1969.5ms | download=0.0ms | ocr=538.5ms | translate=441.0ms | mask=441.0ms | inpaint=245.0ms(migan/DirectML) | render=76.0ms | save=313.0ms +[2025-10-24 09:41:05,021] [LogListener] [DEBUG] [loggerModule.py:debug:275] process_single_image 호출 완료 +[2025-10-24 09:41:05,021] [ResultListener] [WARNING] [loggerModule.py:warning:287] 워커 롤링 스케줄: reason=job-count-threshold +[2025-10-24 09:41:05,021] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 결과 반환 중: uid=878dbdc9-a5ef-4427-a1df-bd59f9c14f55 +[2025-10-24 09:41:05,022] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 결과 반환 완료: uid=878dbdc9-a5ef-4427-a1df-bd59f9c14f55 +[2025-10-24 09:41:05,022] [WorkerRoller] [WARNING] [loggerModule.py:warning:287] 워커 롤링 시작: job-count-threshold +[2025-10-24 09:41:05,022] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 09:41:05,022] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 32504) +[2025-10-24 09:41:05,039] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔥 작업 수신 성공 +[2025-10-24 09:41:05,080] [LogListener] [DEBUG] [loggerModule.py:debug:275] Shutdown signal 수신 → 종료 +[2025-10-24 09:41:05,080] [LogListener] [DEBUG] [loggerModule.py:debug:275] OCR 모듈 정리 완료 +[2025-10-24 09:41:05,080] [LogListener] [DEBUG] [loggerModule.py:debug:275] 마스크 모듈 정리 완료 +[2025-10-24 09:41:05,081] [LogListener] [DEBUG] [loggerModule.py:debug:275] 임시 폴더 삭제됨: C:\ProgramData\ImgWorker\work +[2025-10-24 09:41:05,081] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 프로세서 소멸 +[2025-10-24 09:41:05,613] [WorkerRoller] [INFO] [loggerModule.py:info:281] 워커 프로세스 기동: PID=22368 +[2025-10-24 09:41:06,158] [LogListener] [DEBUG] [loggerModule.py:debug:275] ImageWorker 프로세스 기동 (PID=22368, Name=ImageWorkerProcess) +[2025-10-24 09:41:06,158] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔧 ImageProcessor3 초기화 시작... +[2025-10-24 09:41:06,159] [LogListener] [DEBUG] [loggerModule.py:debug:275] tracemalloc 메모리 추적 시작 +[2025-10-24 09:41:06,166] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ DirectML 환경 준비 완료 (Windows DirectX 12 기반) +[2025-10-24 09:41:06,166] [LogListener] [DEBUG] [loggerModule.py:debug:275] === 🚀 DirectML GPU 상태 초기화 시작 🚀 === +[2025-10-24 09:41:06,166] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🎯 사용자 GPU 가속 요청: False +[2025-10-24 09:41:06,166] [LogListener] [DEBUG] [loggerModule.py:debug:275] 💻 현재 운영체제: Windows +[2025-10-24 09:41:06,166] [LogListener] [DEBUG] [loggerModule.py:debug:275] GPU 가속이 비활성화됨 (toggle_states['use_cuda'] = False) +[2025-10-24 09:41:06,166] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔒 안전한 CPU 모드로 모든 GPU 설정 강제 비활성화 +[2025-10-24 09:41:06,167] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔧 ImageProcessor3 GPU 상태 요약: +[2025-10-24 09:41:06,167] [LogListener] [DEBUG] [loggerModule.py:debug:275] - CUDA 사용 가능: False +[2025-10-24 09:41:06,167] [LogListener] [DEBUG] [loggerModule.py:debug:275] - toggle_states['use_cuda']: NOT_SET +[2025-10-24 09:41:06,167] [LogListener] [DEBUG] [loggerModule.py:debug:275] - GPU 하드웨어 정보: {} +[2025-10-24 09:41:06,167] [LogListener] [DEBUG] [loggerModule.py:debug:275] ImageProcessor3 Init toggle_states: {'TEMP_IMAGE_DIR': 'C:\\ProgramData\\ImgWorker\\work', 'output_image_format': 'webp', 'use_local_rembg': True, 'local_rembg_model_path': 'D:\\py\\img_worker\\modules\\rembg_models', 'local_model_name': 'birefnet-general-lite', 'optionIMGTrans_type': 'CPU', 'detail_IMGTrans_type': 'CPU', 'thumb_trans_type': 'CPU', 'migan_use_accel': True, 'migan_onnx_path': 'D:\\py\\img_worker\\modules\\migan_onnx\\migan_pipeline_v2.onnx', 'ocr_provider_override': 'auto', 'migan_provider_override': 'auto'} +[2025-10-24 09:41:06,167] [LogListener] [DEBUG] [loggerModule.py:debug:275] is_member_valid: False +[2025-10-24 09:41:06,167] [LogListener] [DEBUG] [loggerModule.py:debug:275] debug_images 디렉토리 이미 존재: D:\py\img_worker\modules\debug_images +[2025-10-24 09:41:06,167] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.font_path: D:\py\img_worker\modules\fonts\HakgyoansimDunggeunmisoTTFB.ttf +[2025-10-24 09:41:06,167] [LogListener] [DEBUG] [loggerModule.py:debug:275] toggle_states font_path: D:\py\img_worker\modules\fonts\HakgyoansimDunggeunmisoTTFB.ttf +[2025-10-24 09:41:06,167] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.TEMP_IMAGE_DIR: C:\ProgramData\ImgWorker\work +[2025-10-24 09:41:06,167] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.debugging_save_Dir: D:\py\img_worker\modules\debug_images +[2025-10-24 09:41:06,167] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.unwanted_texts: {} +[2025-10-24 09:41:06,168] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.inpaint_method: migan +[2025-10-24 09:41:06,168] [LogListener] [DEBUG] [loggerModule.py:debug:275] Image.MAX_IMAGE_PIXELS set to 20000000 +[2025-10-24 09:41:06,168] [LogListener] [DEBUG] [loggerModule.py:debug:275] ONNX 모델 타입 설정값: 자동 선택, GPU 정보: {} +[2025-10-24 09:41:06,168] [LogListener] [DEBUG] [loggerModule.py:debug:275] 자동 선택 모드: GPU 추천 모델 simp 사용 +[2025-10-24 09:41:06,168] [LogListener] [DEBUG] [loggerModule.py:debug:275] ONNX OCR 모델 타입 결정: simp (GPU: False) +[2025-10-24 09:41:06,168] [LogListener] [DEBUG] [loggerModule.py:debug:275] ONNX OCR 모듈 CPU 모드로 설정 +[2025-10-24 09:41:06,168] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🚀 ONNX TextSystem 초기화 시작 (CPU 모드) +[2025-10-24 09:41:06,168] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🚀 ONNX TextSystem 초기화 시작 (CPU 모드) +[2025-10-24 09:41:07,767] [LogListener] [DEBUG] [loggerModule.py:debug:275] 문자 사전 파일 UTF-8 확인 완료: D:\py\img_worker\modules\onnx_ocr_module\dict\ppocr_keys_v1.txt +[2025-10-24 09:41:07,852] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ ONNX TextSystem 초기화 완료 (CPU + SIMP (호환성) 모델) +[2025-10-24 09:41:07,852] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ ONNX TextSystem 초기화 완료 (CPU + SIMP 모델) +[2025-10-24 09:41:07,852] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ ONNX OCR 모듈 초기화 성공 (CPU 모드) +[2025-10-24 09:41:07,853] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ ONNX OCR 모듈 초기화 성공 +[2025-10-24 09:41:07,853] [LogListener] [DEBUG] [loggerModule.py:debug:275] 마스크 모듈 초기화 완료 +[2025-10-24 09:41:07,853] [LogListener] [DEBUG] [loggerModule.py:debug:275] MaskModule 초기화 성공 +[2025-10-24 09:41:07,853] [LogListener] [DEBUG] [loggerModule.py:debug:275] 텍스트 렌더링 모듈 초기화 완료 +[2025-10-24 09:41:07,853] [LogListener] [DEBUG] [loggerModule.py:debug:275] 기본 폰트: D:\py\img_worker\modules\fonts\HakgyoansimDunggeunmisoTTFB.ttf +[2025-10-24 09:41:07,853] [LogListener] [DEBUG] [loggerModule.py:debug:275] TextRenderingModule 초기화 성공 +[2025-10-24 09:41:07,853] [LogListener] [DEBUG] [loggerModule.py:debug:275] 폰트 로드 성공: D:\py\img_worker\modules\fonts\HakgyoansimDunggeunmisoTTFB.ttf +[2025-10-24 09:41:07,853] [LogListener] [DEBUG] [loggerModule.py:debug:275] PostImageManager 초기화 성공 +[2025-10-24 09:41:07,853] [LogListener] [DEBUG] [loggerModule.py:debug:275] Request_AI_Server GPU 상태: CUDA 사용 가능=False +[2025-10-24 09:41:07,854] [LogListener] [DEBUG] [loggerModule.py:debug:275] Request_AI_Server 초기화 성공 +[2025-10-24 09:41:07,854] [LogListener] [DEBUG] [loggerModule.py:debug:275] GoogleTranslate 초기화 성공 +[2025-10-24 09:41:07,854] [LogListener] [DEBUG] [loggerModule.py:debug:275] MIGAN CUDA 사용 불가 - CPU 모드로 설정 +[2025-10-24 09:41:07,854] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] GPU 관리자 전달: GPUManager, can_use_cuda: False +[2025-10-24 09:41:07,872] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] GPU 관리자 사용 가능: False +[2025-10-24 09:41:08,083] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 사용 가능한 providers: ['DmlExecutionProvider', 'CPUExecutionProvider'] +[2025-10-24 09:41:08,083] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] DirectML 가속 활성화 +[2025-10-24 09:41:08,083] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 최종 providers: [('DmlExecutionProvider', {}), ('CPUExecutionProvider', {})] +[2025-10-24 09:41:08,084] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] DirectML 가속 시도: [('DmlExecutionProvider', {}), ('CPUExecutionProvider', {})] +[2025-10-24 09:41:08,084] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] DirectML 가속 성공! 실제 providers: ['DmlExecutionProvider', 'CPUExecutionProvider'] +[2025-10-24 09:41:08,084] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 입력 0: image, 형태: ['batch_size', 3, 'height', 'width'], 타입: tensor(uint8) +[2025-10-24 09:41:08,084] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 입력 1: mask, 형태: ['batch_size', 1, 'height', 'width'], 타입: tensor(uint8) +[2025-10-24 09:41:08,084] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 출력 0: result, 형태: ['ScatterNDresult_dim_0', 3, 'ScatterNDresult_dim_2', 'ScatterNDresult_dim_3'], 타입: tensor(uint8) +[2025-10-24 09:41:08,084] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 세션 준비 완료. providers=['DmlExecutionProvider', 'CPUExecutionProvider'] +[2025-10-24 09:41:08,084] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] GPU 관리자 연결 완료: GPUManager +[2025-10-24 09:41:08,084] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 파이프라인 gpu_manager 속성: True, 값: +[2025-10-24 09:41:08,084] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 초기화 완료: gpu_manager 속성=True, 값= +[2025-10-24 09:41:08,084] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔰 OCR 모듈 Warm-up 시작... +[2025-10-24 09:41:08,084] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔍 ONNX OCR 감지 방식: polygon +[2025-10-24 09:41:08,089] [LogListener] [DEBUG] [loggerModule.py:debug:275] ⚡ ONNX OCR 추론 완료: 5.0ms +[2025-10-24 09:41:08,089] [LogListener] [DEBUG] [loggerModule.py:debug:275] 📊 세부 시간 - 감지: 3.0ms, 인식: 0.0ms, 분류: 0.0ms +[2025-10-24 09:41:08,089] [LogListener] [WARNING] [loggerModule.py:warning:287] ⚠️ ONNX OCR 결과가 비어있습니다 +[2025-10-24 09:41:08,093] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ OCR 모듈 Warm-up 성공 +[2025-10-24 09:41:08,093] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔰 ImageProcessor Warm‑up 완료 +[2025-10-24 09:41:08,093] [ResultListener] [INFO] [loggerModule.py:info:281] 워커 READY 수신 +[2025-10-24 09:41:08,093] [WorkerRoller] [INFO] [loggerModule.py:info:281] 버퍼 작업 재개: 1건 +[2025-10-24 09:41:08,093] [ResultListener] [INFO] [loggerModule.py:info:281] 워커 READY 수신 +[2025-10-24 09:41:08,093] [LogListener] [DEBUG] [loggerModule.py:debug:275] 📡 추가 READY 신호 전송 완료 +[2025-10-24 09:41:08,094] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 09:41:08,094] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 22368) +[2025-10-24 09:41:08,094] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔥 작업 수신 성공 +[2025-10-24 09:41:08,094] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🚀 작업 처리 시작: cmd=process_single_image, uid=e02240bd-dc55-466c-a307-6752179e6220 +[2025-10-24 09:41:08,094] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 프로세서 toggle_states 업데이트 : {'TEMP_IMAGE_DIR': 'C:\\ProgramData\\ImgWorker\\work', 'output_image_format': 'webp', 'use_local_rembg': True, 'local_rembg_model_path': 'D:\\py\\img_worker\\modules\\rembg_models', 'local_model_name': 'birefnet-general-lite', 'optionIMGTrans_type': 'GPU', 'detail_IMGTrans_type': 'GPU', 'thumb_trans_type': 'GPU', 'migan_use_accel': True, 'migan_onnx_path': 'D:\\py\\img_worker\\modules\\migan_onnx\\migan_pipeline_v2.onnx', 'ocr_provider_override': 'auto', 'migan_provider_override': 'auto', 'font_type': '폰트5', 'unwanted_texts': ['가격설명', '무료배송', '보증', '사은품', '세일', '이벤트', '특가', '품절', '할인', '할인가'], 'is_member_valid': False, 'authenticated_by_admin': True} +[2025-10-24 09:41:08,095] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 실행 직전: cmd=process_single_image +[2025-10-24 09:41:08,095] [LogListener] [DEBUG] [loggerModule.py:debug:275] process_single_image 호출 직전 +[2025-10-24 09:41:08,097] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 번역시작 +[2025-10-24 09:41:08,331] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 15 처리 시작: C:\ProgramData\ImgWorker\incoming\dl_4d024ed7f883.jpg - 전체 번역 모드 +[2025-10-24 09:41:08,336] [LogListener] [DEBUG] [loggerModule.py:debug:275] 로컬 파일 경로 감지, 다운로드 생략: C:\ProgramData\ImgWorker\incoming\dl_4d024ed7f883.jpg +[2025-10-24 09:41:08,336] [LogListener] [DEBUG] [loggerModule.py:debug:275] 옵션 이미지는 스케일 처리 건너뛰기: option +[2025-10-24 09:41:08,337] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 15 로컬 저장위치(옵션 이미지 원본 유지): C:\ProgramData\ImgWorker\incoming\dl_4d024ed7f883.jpg +[2025-10-24 09:41:08,338] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔍 ONNX OCR 감지 방식: polygon +[2025-10-24 09:41:08,968] [LogListener] [DEBUG] [loggerModule.py:debug:275] ⚡ ONNX OCR 추론 완료: 628.0ms +[2025-10-24 09:41:08,968] [LogListener] [DEBUG] [loggerModule.py:debug:275] 📊 세부 시간 - 감지: 115.0ms, 인식: 486.0ms, 분류: 20.0ms +[2025-10-24 09:41:08,986] [LogListener] [DEBUG] [loggerModule.py:debug:275] 메모리 변화 [OCR 처리]: 43134.2MB -> 43392.2MB (+258.0MB, +0.6%) - 이미지 15 +[2025-10-24 09:41:09,000] [LogListener] [DEBUG] [loggerModule.py:debug:275] ocr_results: [{'text': '暴走兔', 'confidence': 0.9141947627067566, 'polygon': [[136.0, 21.0], [270.0, 23.0], [269.0, 63.0], [135.0, 61.0]], 'bbox': (135, 21, 136, 43), 'method': 'polygon'}, {'text': 'BAOZOUTU', 'confidence': 0.9931288957595825, 'polygon': [[134.0, 73.0], [273.0, 73.0], [273.0, 87.0], [134.0, 87.0]], 'bbox': (134, 73, 140, 15), 'method': 'polygon'}, {'text': '全波段收音机', 'confidence': 0.997622013092041, 'polygon': [[81.0, 158.0], [288.0, 156.0], [288.0, 184.0], [81.0, 186.0]], 'bbox': (81, 156, 208, 31), 'method': 'polygon'}, {'text': '..R12', 'confidence': 0.7803670763969421, 'polygon': [[573.0, 152.0], [616.0, 152.0], [616.0, 163.0], [573.0, 163.0]], 'bbox': (573, 152, 44, 12), 'method': 'polygon'}, {'text': '518', 'confidence': 0.990628719329834, 'polygon': [[488.0, 174.0], [606.0, 174.0], [606.0, 240.0], [488.0, 240.0]], 'bbox': (488, 174, 119, 67), 'method': 'polygon'}, {'text': '蓝牙音响', 'confidence': 0.998444676399231, 'polygon': [[78.0, 211.0], [294.0, 213.0], [294.0, 248.0], [78.0, 246.0]], 'bbox': (78, 211, 217, 38), 'method': 'polygon'}, {'text': 'UH', 'confidence': 0.7753715515136719, 'polygon': [[608.0, 228.0], [621.0, 228.0], [621.0, 237.0], [608.0, 237.0]], 'bbox': (608, 228, 14, 10), 'method': 'polygon'}, {'text': 'MULTIBANDRADIO/BT/MUSICPLAYER', 'confidence': 0.9853014349937439, 'polygon': [[464.0, 244.0], [623.0, 244.0], [623.0, 257.0], [464.0, 257.0]], 'bbox': (464, 244, 160, 14), 'method': 'polygon'}, {'text': '4000毫安锂电池', 'confidence': 0.9978502988815308, 'polygon': [[82.0, 279.0], [289.0, 279.0], [289.0, 303.0], [82.0, 303.0]], 'bbox': (82, 279, 208, 25), 'method': 'polygon'}, {'text': '手', 'confidence': 0.9999699592590332, 'polygon': [[80.0, 335.0], [113.0, 335.0], [113.0, 365.0], [80.0, 365.0]], 'bbox': (80, 335, 34, 31), 'method': 'polygon'}, {'text': '电', 'confidence': 0.9992403984069824, 'polygon': [[168.0, 336.0], [204.0, 336.0], [204.0, 365.0], [168.0, 365.0]], 'bbox': (168, 336, 37, 30), 'method': 'polygon'}, {'text': '筒', 'confidence': 0.9993694424629211, 'polygon': [[263.0, 336.0], [290.0, 336.0], [290.0, 365.0], [263.0, 365.0]], 'bbox': (263, 336, 28, 30), 'method': 'polygon'}, {'text': 'SOS警报/指南针', 'confidence': 0.9649543762207031, 'polygon': [[81.0, 397.0], [288.0, 397.0], [288.0, 425.0], [81.0, 425.0]], 'bbox': (81, 397, 208, 29), 'method': 'polygon'}, {'text': '燕走兔', 'confidence': 0.5333335995674133, 'polygon': [[531.0, 409.0], [595.0, 411.0], [594.0, 441.0], [530.0, 439.0]], 'bbox': (530, 409, 66, 33), 'method': 'polygon'}, {'text': '手摇/太阳能发电', 'confidence': 0.9916104078292847, 'polygon': [[83.0, 457.0], [289.0, 457.0], [289.0, 485.0], [83.0, 485.0]], 'bbox': (83, 457, 207, 29), 'method': 'polygon'}, {'text': '可插U盘/TF卡', 'confidence': 0.9921554923057556, 'polygon': [[81.0, 516.0], [290.0, 518.0], [290.0, 546.0], [81.0, 544.0]], 'bbox': (81, 516, 210, 31), 'method': 'polygon'}, {'text': '限时', 'confidence': 0.998456597328186, 'polygon': [[18.0, 659.0], [151.0, 661.0], [150.0, 728.0], [17.0, 726.0]], 'bbox': (17, 659, 135, 70), 'method': 'polygon'}, {'text': '赠送', 'confidence': 0.999679446220398, 'polygon': [[15.0, 728.0], [149.0, 728.0], [149.0, 792.0], [15.0, 792.0]], 'bbox': (15, 728, 135, 65), 'method': 'polygon'}, {'text': '有线耳机', 'confidence': 0.9987638592720032, 'polygon': [[188.0, 769.0], [265.0, 769.0], [265.0, 790.0], [188.0, 790.0]], 'bbox': (188, 769, 78, 22), 'method': 'polygon'}, {'text': '延长天线', 'confidence': 0.9987039566040039, 'polygon': [[286.0, 769.0], [365.0, 769.0], [365.0, 790.0], [286.0, 790.0]], 'bbox': (286, 769, 80, 22), 'method': 'polygon'}, {'text': '内存卡', 'confidence': 0.9983070492744446, 'polygon': [[386.0, 769.0], [446.0, 769.0], [446.0, 790.0], [386.0, 790.0]], 'bbox': (386, 769, 61, 22), 'method': 'polygon'}, {'text': '读卡器', 'confidence': 0.9987214207649231, 'polygon': [[472.0, 767.0], [531.0, 767.0], [531.0, 792.0], [472.0, 792.0]], 'bbox': (472, 767, 60, 26), 'method': 'polygon'}, {'text': '充电线', 'confidence': 0.9992160797119141, 'polygon': [[560.0, 769.0], [621.0, 769.0], [621.0, 790.0], [560.0, 790.0]], 'bbox': (560, 769, 62, 22), 'method': 'polygon'}, {'text': '礼盒礼袋', 'confidence': 0.9988760948181152, 'polygon': [[636.0, 769.0], [716.0, 769.0], [716.0, 790.0], [636.0, 790.0]], 'bbox': (636, 769, 81, 22), 'method': 'polygon'}, {'text': '充电头', 'confidence': 0.9980059266090393, 'polygon': [[738.0, 767.0], [797.0, 767.0], [797.0, 792.0], [738.0, 792.0]], 'bbox': (738, 767, 60, 26), 'method': 'polygon'}] +[2025-10-24 09:41:09,021] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 91.4%): '暴走兔' +[2025-10-24 09:41:09,022] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): 'BAOZOUTU' +[2025-10-24 09:41:09,022] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.8%): '全波段收音机' +[2025-10-24 09:41:09,022] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): '..R12' +[2025-10-24 09:41:09,022] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): '518' +[2025-10-24 09:41:09,022] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.8%): '蓝牙音响' +[2025-10-24 09:41:09,022] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): 'UH' +[2025-10-24 09:41:09,022] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): 'MULTIBANDRADIO/BT/MUSICPLAYER' +[2025-10-24 09:41:09,023] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.8%): '4000毫安锂电池' +[2025-10-24 09:41:09,023] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 100.0%): '手' +[2025-10-24 09:41:09,023] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.9%): '电' +[2025-10-24 09:41:09,023] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.9%): '筒' +[2025-10-24 09:41:09,023] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 96.5%): 'SOS警报/指南针' +[2025-10-24 09:41:09,023] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 53.3%): '燕走兔' +[2025-10-24 09:41:09,023] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.2%): '手摇/太阳能发电' +[2025-10-24 09:41:09,023] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.2%): '可插U盘/TF卡' +[2025-10-24 09:41:09,023] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.8%): '限时' +[2025-10-24 09:41:09,023] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 100.0%): '赠送' +[2025-10-24 09:41:09,023] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.9%): '有线耳机' +[2025-10-24 09:41:09,023] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.9%): '延长天线' +[2025-10-24 09:41:09,024] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.8%): '内存卡' +[2025-10-24 09:41:09,024] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.9%): '读卡器' +[2025-10-24 09:41:09,024] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.9%): '充电线' +[2025-10-24 09:41:09,024] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.9%): '礼盒礼袋' +[2025-10-24 09:41:09,024] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.8%): '充电头' +[2025-10-24 09:41:09,024] [LogListener] [DEBUG] [loggerModule.py:debug:275] 필터링 결과: 20/25개 (신뢰도 + & 중국어) +[2025-10-24 09:41:09,024] [LogListener] [DEBUG] [loggerModule.py:debug:275] filter_ocr_results: [{'text': '暴走兔', 'confidence': 0.9141947627067566, 'polygon': [[136.0, 21.0], [270.0, 23.0], [269.0, 63.0], [135.0, 61.0]], 'bbox': (135, 21, 136, 43), 'method': 'polygon'}, {'text': '全波段收音机', 'confidence': 0.997622013092041, 'polygon': [[81.0, 158.0], [288.0, 156.0], [288.0, 184.0], [81.0, 186.0]], 'bbox': (81, 156, 208, 31), 'method': 'polygon'}, {'text': '蓝牙音响', 'confidence': 0.998444676399231, 'polygon': [[78.0, 211.0], [294.0, 213.0], [294.0, 248.0], [78.0, 246.0]], 'bbox': (78, 211, 217, 38), 'method': 'polygon'}, {'text': '4000毫安锂电池', 'confidence': 0.9978502988815308, 'polygon': [[82.0, 279.0], [289.0, 279.0], [289.0, 303.0], [82.0, 303.0]], 'bbox': (82, 279, 208, 25), 'method': 'polygon'}, {'text': '手', 'confidence': 0.9999699592590332, 'polygon': [[80.0, 335.0], [113.0, 335.0], [113.0, 365.0], [80.0, 365.0]], 'bbox': (80, 335, 34, 31), 'method': 'polygon'}, {'text': '电', 'confidence': 0.9992403984069824, 'polygon': [[168.0, 336.0], [204.0, 336.0], [204.0, 365.0], [168.0, 365.0]], 'bbox': (168, 336, 37, 30), 'method': 'polygon'}, {'text': '筒', 'confidence': 0.9993694424629211, 'polygon': [[263.0, 336.0], [290.0, 336.0], [290.0, 365.0], [263.0, 365.0]], 'bbox': (263, 336, 28, 30), 'method': 'polygon'}, {'text': 'SOS警报/指南针', 'confidence': 0.9649543762207031, 'polygon': [[81.0, 397.0], [288.0, 397.0], [288.0, 425.0], [81.0, 425.0]], 'bbox': (81, 397, 208, 29), 'method': 'polygon'}, {'text': '燕走兔', 'confidence': 0.5333335995674133, 'polygon': [[531.0, 409.0], [595.0, 411.0], [594.0, 441.0], [530.0, 439.0]], 'bbox': (530, 409, 66, 33), 'method': 'polygon'}, {'text': '手摇/太阳能发电', 'confidence': 0.9916104078292847, 'polygon': [[83.0, 457.0], [289.0, 457.0], [289.0, 485.0], [83.0, 485.0]], 'bbox': (83, 457, 207, 29), 'method': 'polygon'}, {'text': '可插U盘/TF卡', 'confidence': 0.9921554923057556, 'polygon': [[81.0, 516.0], [290.0, 518.0], [290.0, 546.0], [81.0, 544.0]], 'bbox': (81, 516, 210, 31), 'method': 'polygon'}, {'text': '限时', 'confidence': 0.998456597328186, 'polygon': [[18.0, 659.0], [151.0, 661.0], [150.0, 728.0], [17.0, 726.0]], 'bbox': (17, 659, 135, 70), 'method': 'polygon'}, {'text': '赠送', 'confidence': 0.999679446220398, 'polygon': [[15.0, 728.0], [149.0, 728.0], [149.0, 792.0], [15.0, 792.0]], 'bbox': (15, 728, 135, 65), 'method': 'polygon'}, {'text': '有线耳机', 'confidence': 0.9987638592720032, 'polygon': [[188.0, 769.0], [265.0, 769.0], [265.0, 790.0], [188.0, 790.0]], 'bbox': (188, 769, 78, 22), 'method': 'polygon'}, {'text': '延长天线', 'confidence': 0.9987039566040039, 'polygon': [[286.0, 769.0], [365.0, 769.0], [365.0, 790.0], [286.0, 790.0]], 'bbox': (286, 769, 80, 22), 'method': 'polygon'}, {'text': '内存卡', 'confidence': 0.9983070492744446, 'polygon': [[386.0, 769.0], [446.0, 769.0], [446.0, 790.0], [386.0, 790.0]], 'bbox': (386, 769, 61, 22), 'method': 'polygon'}, {'text': '读卡器', 'confidence': 0.9987214207649231, 'polygon': [[472.0, 767.0], [531.0, 767.0], [531.0, 792.0], [472.0, 792.0]], 'bbox': (472, 767, 60, 26), 'method': 'polygon'}, {'text': '充电线', 'confidence': 0.9992160797119141, 'polygon': [[560.0, 769.0], [621.0, 769.0], [621.0, 790.0], [560.0, 790.0]], 'bbox': (560, 769, 62, 22), 'method': 'polygon'}, {'text': '礼盒礼袋', 'confidence': 0.9988760948181152, 'polygon': [[636.0, 769.0], [716.0, 769.0], [716.0, 790.0], [636.0, 790.0]], 'bbox': (636, 769, 81, 22), 'method': 'polygon'}, {'text': '充电头', 'confidence': 0.9980059266090393, 'polygon': [[738.0, 767.0], [797.0, 767.0], [797.0, 792.0], [738.0, 792.0]], 'bbox': (738, 767, 60, 26), 'method': 'polygon'}] +[2025-10-24 09:41:09,032] [LogListener] [DEBUG] [loggerModule.py:debug:275] 중국어 텍스트 20개 필터링 완료 +[2025-10-24 09:41:09,048] [LogListener] [DEBUG] [loggerModule.py:debug:275] 한글 텍스트 0개 필터링 완료 +[2025-10-24 09:41:09,578] [LogListener] [DEBUG] [loggerModule.py:debug:275] translated_texts: ['도망친 토끼', '풀 밴드 라디오', '블루투스 스피커', '4000mAh 리튬 배터리', '손', '전기', '실린더', 'SOS 경보 / 나침반', '얀 주이투', '핸드 크랭크 / 태양광 발전', 'U 디스크 삽입 가능 / TF 카드', '제한된 시간', '포기하다', '유선 헤드폰', '확장 안테나', '메모리 카드', '카드 리더', '충전 케이블', '선물 상자 선물 가방', '충전 헤드'] +[2025-10-24 09:41:09,578] [LogListener] [DEBUG] [loggerModule.py:debug:275] 마스크 생성 완료 +[2025-10-24 09:41:09,578] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 15 OCR 권한 없음, 전체 번역 모드 +[2025-10-24 09:41:09,584] [LogListener] [DEBUG] [loggerModule.py:debug:275] [AUTO Inpaint] coverage=0.269, comps=5, min_center_dist=0.273 → request +[2025-10-24 09:41:09,585] [LogListener] [DEBUG] [loggerModule.py:debug:275] ocr_count: 20 +[2025-10-24 09:41:09,585] [LogListener] [DEBUG] [loggerModule.py:debug:275] is_member_valid: False +[2025-10-24 09:41:09,585] [LogListener] [DEBUG] [loggerModule.py:debug:275] MIGAN 인페인팅 시도 +[2025-10-24 09:41:09,587] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 입력 형태 - 이미지: (1, 3, 800, 800), 마스크: (1, 1, 800, 800) +[2025-10-24 09:41:09,937] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 출력 형태: (800, 800, 3), dtype: uint8 +[2025-10-24 09:41:09,938] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 추론 완료: 350.35 ms +[2025-10-24 09:41:09,945] [LogListener] [DEBUG] [loggerModule.py:debug:275] MIGAN 인페인팅 성공 +[2025-10-24 09:41:09,945] [LogListener] [DEBUG] [loggerModule.py:debug:275] 메모리 변화 [인페인팅]: 43422.8MB -> 43618.9MB (+196.1MB, +0.5%) - 방법: migan +[2025-10-24 09:41:09,945] [LogListener] [DEBUG] [loggerModule.py:debug:275] 인페인팅 완료 +[2025-10-24 09:41:10,017] [LogListener] [DEBUG] [loggerModule.py:debug:275] 텍스트 렌더링 완료 +[2025-10-24 09:41:10,018] [LogListener] [DEBUG] [loggerModule.py:debug:275] watermark_text: 이미지 저작권 보유 +[2025-10-24 09:41:10,018] [LogListener] [DEBUG] [loggerModule.py:debug:275] is_watermark_enabled: True +[2025-10-24 09:41:10,322] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 저장 완료 : C:\ProgramData\ImgWorker\work\translated_option_img_15.webp +[2025-10-24 09:41:10,323] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 15 번역 완료: C:\ProgramData\ImgWorker\work\translated_option_img_15.webp +[2025-10-24 09:41:10,323] [LogListener] [DEBUG] [loggerModule.py:debug:275] ⏱ 이미지 파이프라인 총 2226.6ms | download=0.0ms | ocr=638.0ms | translate=555.8ms | mask=555.8ms | inpaint=366.1ms(migan/DirectML) | render=72.3ms | save=271.0ms +[2025-10-24 09:41:10,324] [LogListener] [DEBUG] [loggerModule.py:debug:275] process_single_image 호출 완료 +[2025-10-24 09:41:10,325] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 결과 반환 중: uid=e02240bd-dc55-466c-a307-6752179e6220 +[2025-10-24 09:41:10,325] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 결과 반환 완료: uid=e02240bd-dc55-466c-a307-6752179e6220 +[2025-10-24 09:41:10,325] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 09:41:10,325] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 22368) +[2025-10-24 09:41:12,146] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔥 작업 수신 성공 +[2025-10-24 09:41:12,146] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🚀 작업 처리 시작: cmd=process_single_image, uid=dcbda892-9f96-4f83-97e7-4f25afd93607 +[2025-10-24 09:41:12,146] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 프로세서 toggle_states 업데이트 : {'TEMP_IMAGE_DIR': 'C:\\ProgramData\\ImgWorker\\work', 'output_image_format': 'webp', 'use_local_rembg': True, 'local_rembg_model_path': 'D:\\py\\img_worker\\modules\\rembg_models', 'local_model_name': 'birefnet-general-lite', 'optionIMGTrans_type': 'GPU', 'detail_IMGTrans_type': 'GPU', 'thumb_trans_type': 'GPU', 'migan_use_accel': True, 'migan_onnx_path': 'D:\\py\\img_worker\\modules\\migan_onnx\\migan_pipeline_v2.onnx', 'ocr_provider_override': 'auto', 'migan_provider_override': 'auto', 'font_type': '폰트5', 'unwanted_texts': ['가격설명', '무료배송', '보증', '사은품', '세일', '이벤트', '특가', '품절', '할인', '할인가'], 'is_member_valid': False, 'authenticated_by_admin': True} +[2025-10-24 09:41:12,146] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 실행 직전: cmd=process_single_image +[2025-10-24 09:41:12,147] [LogListener] [DEBUG] [loggerModule.py:debug:275] process_single_image 호출 직전 +[2025-10-24 09:41:12,147] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 번역시작 +[2025-10-24 09:41:12,474] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 16 처리 시작: C:\ProgramData\ImgWorker\incoming\dl_4fe277249667.jpg - 전체 번역 모드 +[2025-10-24 09:41:12,480] [LogListener] [DEBUG] [loggerModule.py:debug:275] 로컬 파일 경로 감지, 다운로드 생략: C:\ProgramData\ImgWorker\incoming\dl_4fe277249667.jpg +[2025-10-24 09:41:12,480] [LogListener] [DEBUG] [loggerModule.py:debug:275] 옵션 이미지는 스케일 처리 건너뛰기: option +[2025-10-24 09:41:12,480] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 16 로컬 저장위치(옵션 이미지 원본 유지): C:\ProgramData\ImgWorker\incoming\dl_4fe277249667.jpg +[2025-10-24 09:41:12,481] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔍 ONNX OCR 감지 방식: polygon +[2025-10-24 09:41:13,092] [LogListener] [DEBUG] [loggerModule.py:debug:275] ⚡ ONNX OCR 추론 완료: 608.0ms +[2025-10-24 09:41:13,110] [LogListener] [DEBUG] [loggerModule.py:debug:275] 📊 세부 시간 - 감지: 101.0ms, 인식: 482.0ms, 분류: 19.0ms +[2025-10-24 09:41:13,120] [LogListener] [DEBUG] [loggerModule.py:debug:275] 메모리 변화 [OCR 처리]: 43683.0MB -> 43714.7MB (+31.8MB, +0.1%) - 이미지 16 +[2025-10-24 09:41:13,121] [LogListener] [DEBUG] [loggerModule.py:debug:275] ocr_results: [{'text': '暴走兔', 'confidence': 0.9141947627067566, 'polygon': [[136.0, 21.0], [270.0, 23.0], [269.0, 63.0], [135.0, 61.0]], 'bbox': (135, 21, 136, 43), 'method': 'polygon'}, {'text': 'BAOZOUTU', 'confidence': 0.9931288957595825, 'polygon': [[134.0, 73.0], [273.0, 73.0], [273.0, 87.0], [134.0, 87.0]], 'bbox': (134, 73, 140, 15), 'method': 'polygon'}, {'text': '全波段收音机', 'confidence': 0.997622013092041, 'polygon': [[81.0, 158.0], [288.0, 156.0], [288.0, 184.0], [81.0, 186.0]], 'bbox': (81, 156, 208, 31), 'method': 'polygon'}, {'text': '..R12', 'confidence': 0.7803670763969421, 'polygon': [[573.0, 152.0], [616.0, 152.0], [616.0, 163.0], [573.0, 163.0]], 'bbox': (573, 152, 44, 12), 'method': 'polygon'}, {'text': '518', 'confidence': 0.9855506420135498, 'polygon': [[488.0, 174.0], [607.0, 174.0], [607.0, 240.0], [488.0, 240.0]], 'bbox': (488, 174, 120, 67), 'method': 'polygon'}, {'text': '蓝牙音响', 'confidence': 0.998444676399231, 'polygon': [[78.0, 211.0], [294.0, 213.0], [294.0, 248.0], [78.0, 246.0]], 'bbox': (78, 211, 217, 38), 'method': 'polygon'}, {'text': 'UH', 'confidence': 0.7753715515136719, 'polygon': [[608.0, 228.0], [621.0, 228.0], [621.0, 237.0], [608.0, 237.0]], 'bbox': (608, 228, 14, 10), 'method': 'polygon'}, {'text': 'MULTIBANDRADIO/BT/MUSICPLAYER', 'confidence': 0.9853014349937439, 'polygon': [[464.0, 244.0], [623.0, 244.0], [623.0, 257.0], [464.0, 257.0]], 'bbox': (464, 244, 160, 14), 'method': 'polygon'}, {'text': '4000毫安锂电池', 'confidence': 0.997366189956665, 'polygon': [[83.0, 279.0], [289.0, 279.0], [289.0, 303.0], [83.0, 303.0]], 'bbox': (83, 279, 207, 25), 'method': 'polygon'}, {'text': '手', 'confidence': 0.9999699592590332, 'polygon': [[80.0, 335.0], [113.0, 335.0], [113.0, 365.0], [80.0, 365.0]], 'bbox': (80, 335, 34, 31), 'method': 'polygon'}, {'text': '电', 'confidence': 0.9992403984069824, 'polygon': [[168.0, 336.0], [204.0, 336.0], [204.0, 365.0], [168.0, 365.0]], 'bbox': (168, 336, 37, 30), 'method': 'polygon'}, {'text': '筒', 'confidence': 0.9993694424629211, 'polygon': [[263.0, 336.0], [290.0, 336.0], [290.0, 365.0], [263.0, 365.0]], 'bbox': (263, 336, 28, 30), 'method': 'polygon'}, {'text': 'SOS警报/指南针', 'confidence': 0.9649543762207031, 'polygon': [[81.0, 397.0], [288.0, 397.0], [288.0, 425.0], [81.0, 425.0]], 'bbox': (81, 397, 208, 29), 'method': 'polygon'}, {'text': '燕走兔', 'confidence': 0.5333335995674133, 'polygon': [[531.0, 409.0], [595.0, 411.0], [594.0, 441.0], [530.0, 439.0]], 'bbox': (530, 409, 66, 33), 'method': 'polygon'}, {'text': '手摇/太阳能发电', 'confidence': 0.9916104078292847, 'polygon': [[83.0, 457.0], [289.0, 457.0], [289.0, 485.0], [83.0, 485.0]], 'bbox': (83, 457, 207, 29), 'method': 'polygon'}, {'text': '可插U盘/TF卡', 'confidence': 0.9921554923057556, 'polygon': [[81.0, 516.0], [290.0, 518.0], [290.0, 546.0], [81.0, 544.0]], 'bbox': (81, 516, 210, 31), 'method': 'polygon'}, {'text': '限时', 'confidence': 0.9986151456832886, 'polygon': [[17.0, 659.0], [151.0, 661.0], [150.0, 729.0], [16.0, 727.0]], 'bbox': (16, 659, 136, 71), 'method': 'polygon'}, {'text': '赠送', 'confidence': 0.999679446220398, 'polygon': [[15.0, 728.0], [149.0, 728.0], [149.0, 792.0], [15.0, 792.0]], 'bbox': (15, 728, 135, 65), 'method': 'polygon'}, {'text': '军工包', 'confidence': 0.9988964200019836, 'polygon': [[205.0, 768.0], [264.0, 768.0], [264.0, 790.0], [205.0, 790.0]], 'bbox': (205, 768, 60, 23), 'method': 'polygon'}, {'text': '延长天线', 'confidence': 0.9986525774002075, 'polygon': [[284.0, 769.0], [363.0, 769.0], [363.0, 790.0], [284.0, 790.0]], 'bbox': (284, 769, 80, 22), 'method': 'polygon'}, {'text': '内存卡', 'confidence': 0.9983696937561035, 'polygon': [[385.0, 769.0], [445.0, 769.0], [445.0, 790.0], [385.0, 790.0]], 'bbox': (385, 769, 61, 22), 'method': 'polygon'}, {'text': '读卡器', 'confidence': 0.9987451434135437, 'polygon': [[472.0, 767.0], [531.0, 767.0], [531.0, 792.0], [472.0, 792.0]], 'bbox': (472, 767, 60, 26), 'method': 'polygon'}, {'text': '充电线', 'confidence': 0.9994004368782043, 'polygon': [[558.0, 769.0], [617.0, 769.0], [617.0, 790.0], [558.0, 790.0]], 'bbox': (558, 769, 60, 22), 'method': 'polygon'}, {'text': '礼盒礼袋', 'confidence': 0.9989432692527771, 'polygon': [[638.0, 769.0], [716.0, 769.0], [716.0, 790.0], [638.0, 790.0]], 'bbox': (638, 769, 79, 22), 'method': 'polygon'}, {'text': '充电头', 'confidence': 0.9980294108390808, 'polygon': [[735.0, 767.0], [794.0, 767.0], [794.0, 792.0], [735.0, 792.0]], 'bbox': (735, 767, 60, 26), 'method': 'polygon'}] +[2025-10-24 09:41:13,121] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 91.4%): '暴走兔' +[2025-10-24 09:41:13,121] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): 'BAOZOUTU' +[2025-10-24 09:41:13,122] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.8%): '全波段收音机' +[2025-10-24 09:41:13,122] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): '..R12' +[2025-10-24 09:41:13,122] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): '518' +[2025-10-24 09:41:13,122] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.8%): '蓝牙音响' +[2025-10-24 09:41:13,122] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): 'UH' +[2025-10-24 09:41:13,122] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): 'MULTIBANDRADIO/BT/MUSICPLAYER' +[2025-10-24 09:41:13,122] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.7%): '4000毫安锂电池' +[2025-10-24 09:41:13,122] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 100.0%): '手' +[2025-10-24 09:41:13,122] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.9%): '电' +[2025-10-24 09:41:13,122] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.9%): '筒' +[2025-10-24 09:41:13,122] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 96.5%): 'SOS警报/指南针' +[2025-10-24 09:41:13,122] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 53.3%): '燕走兔' +[2025-10-24 09:41:13,123] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.2%): '手摇/太阳能发电' +[2025-10-24 09:41:13,123] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.2%): '可插U盘/TF卡' +[2025-10-24 09:41:13,123] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.9%): '限时' +[2025-10-24 09:41:13,123] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 100.0%): '赠送' +[2025-10-24 09:41:13,123] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.9%): '军工包' +[2025-10-24 09:41:13,123] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.9%): '延长天线' +[2025-10-24 09:41:13,123] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.8%): '内存卡' +[2025-10-24 09:41:13,123] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.9%): '读卡器' +[2025-10-24 09:41:13,123] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.9%): '充电线' +[2025-10-24 09:41:13,123] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.9%): '礼盒礼袋' +[2025-10-24 09:41:13,123] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.8%): '充电头' +[2025-10-24 09:41:13,123] [LogListener] [DEBUG] [loggerModule.py:debug:275] 필터링 결과: 20/25개 (신뢰도 + & 중국어) +[2025-10-24 09:41:13,124] [LogListener] [DEBUG] [loggerModule.py:debug:275] filter_ocr_results: [{'text': '暴走兔', 'confidence': 0.9141947627067566, 'polygon': [[136.0, 21.0], [270.0, 23.0], [269.0, 63.0], [135.0, 61.0]], 'bbox': (135, 21, 136, 43), 'method': 'polygon'}, {'text': '全波段收音机', 'confidence': 0.997622013092041, 'polygon': [[81.0, 158.0], [288.0, 156.0], [288.0, 184.0], [81.0, 186.0]], 'bbox': (81, 156, 208, 31), 'method': 'polygon'}, {'text': '蓝牙音响', 'confidence': 0.998444676399231, 'polygon': [[78.0, 211.0], [294.0, 213.0], [294.0, 248.0], [78.0, 246.0]], 'bbox': (78, 211, 217, 38), 'method': 'polygon'}, {'text': '4000毫安锂电池', 'confidence': 0.997366189956665, 'polygon': [[83.0, 279.0], [289.0, 279.0], [289.0, 303.0], [83.0, 303.0]], 'bbox': (83, 279, 207, 25), 'method': 'polygon'}, {'text': '手', 'confidence': 0.9999699592590332, 'polygon': [[80.0, 335.0], [113.0, 335.0], [113.0, 365.0], [80.0, 365.0]], 'bbox': (80, 335, 34, 31), 'method': 'polygon'}, {'text': '电', 'confidence': 0.9992403984069824, 'polygon': [[168.0, 336.0], [204.0, 336.0], [204.0, 365.0], [168.0, 365.0]], 'bbox': (168, 336, 37, 30), 'method': 'polygon'}, {'text': '筒', 'confidence': 0.9993694424629211, 'polygon': [[263.0, 336.0], [290.0, 336.0], [290.0, 365.0], [263.0, 365.0]], 'bbox': (263, 336, 28, 30), 'method': 'polygon'}, {'text': 'SOS警报/指南针', 'confidence': 0.9649543762207031, 'polygon': [[81.0, 397.0], [288.0, 397.0], [288.0, 425.0], [81.0, 425.0]], 'bbox': (81, 397, 208, 29), 'method': 'polygon'}, {'text': '燕走兔', 'confidence': 0.5333335995674133, 'polygon': [[531.0, 409.0], [595.0, 411.0], [594.0, 441.0], [530.0, 439.0]], 'bbox': (530, 409, 66, 33), 'method': 'polygon'}, {'text': '手摇/太阳能发电', 'confidence': 0.9916104078292847, 'polygon': [[83.0, 457.0], [289.0, 457.0], [289.0, 485.0], [83.0, 485.0]], 'bbox': (83, 457, 207, 29), 'method': 'polygon'}, {'text': '可插U盘/TF卡', 'confidence': 0.9921554923057556, 'polygon': [[81.0, 516.0], [290.0, 518.0], [290.0, 546.0], [81.0, 544.0]], 'bbox': (81, 516, 210, 31), 'method': 'polygon'}, {'text': '限时', 'confidence': 0.9986151456832886, 'polygon': [[17.0, 659.0], [151.0, 661.0], [150.0, 729.0], [16.0, 727.0]], 'bbox': (16, 659, 136, 71), 'method': 'polygon'}, {'text': '赠送', 'confidence': 0.999679446220398, 'polygon': [[15.0, 728.0], [149.0, 728.0], [149.0, 792.0], [15.0, 792.0]], 'bbox': (15, 728, 135, 65), 'method': 'polygon'}, {'text': '军工包', 'confidence': 0.9988964200019836, 'polygon': [[205.0, 768.0], [264.0, 768.0], [264.0, 790.0], [205.0, 790.0]], 'bbox': (205, 768, 60, 23), 'method': 'polygon'}, {'text': '延长天线', 'confidence': 0.9986525774002075, 'polygon': [[284.0, 769.0], [363.0, 769.0], [363.0, 790.0], [284.0, 790.0]], 'bbox': (284, 769, 80, 22), 'method': 'polygon'}, {'text': '内存卡', 'confidence': 0.9983696937561035, 'polygon': [[385.0, 769.0], [445.0, 769.0], [445.0, 790.0], [385.0, 790.0]], 'bbox': (385, 769, 61, 22), 'method': 'polygon'}, {'text': '读卡器', 'confidence': 0.9987451434135437, 'polygon': [[472.0, 767.0], [531.0, 767.0], [531.0, 792.0], [472.0, 792.0]], 'bbox': (472, 767, 60, 26), 'method': 'polygon'}, {'text': '充电线', 'confidence': 0.9994004368782043, 'polygon': [[558.0, 769.0], [617.0, 769.0], [617.0, 790.0], [558.0, 790.0]], 'bbox': (558, 769, 60, 22), 'method': 'polygon'}, {'text': '礼盒礼袋', 'confidence': 0.9989432692527771, 'polygon': [[638.0, 769.0], [716.0, 769.0], [716.0, 790.0], [638.0, 790.0]], 'bbox': (638, 769, 79, 22), 'method': 'polygon'}, {'text': '充电头', 'confidence': 0.9980294108390808, 'polygon': [[735.0, 767.0], [794.0, 767.0], [794.0, 792.0], [735.0, 792.0]], 'bbox': (735, 767, 60, 26), 'method': 'polygon'}] +[2025-10-24 09:41:13,124] [LogListener] [DEBUG] [loggerModule.py:debug:275] 중국어 텍스트 20개 필터링 완료 +[2025-10-24 09:41:13,124] [LogListener] [DEBUG] [loggerModule.py:debug:275] 한글 텍스트 0개 필터링 완료 +[2025-10-24 09:41:13,646] [LogListener] [DEBUG] [loggerModule.py:debug:275] translated_texts: ['도망친 토끼', '풀 밴드 라디오', '블루투스 스피커', '4000mAh 리튬 배터리', '손', '전기', '실린더', 'SOS 경보 / 나침반', '얀 주이투', '핸드 크랭크 / 태양광 발전', 'U 디스크 삽입 가능 / TF 카드', '제한된 시간', '포기하다', '군사 가방', '확장 안테나', '메모리 카드', '카드 리더', '충전 케이블', '선물 상자 선물 가방', '충전 헤드'] +[2025-10-24 09:41:13,646] [LogListener] [DEBUG] [loggerModule.py:debug:275] 마스크 생성 완료 +[2025-10-24 09:41:13,646] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 16 OCR 권한 없음, 전체 번역 모드 +[2025-10-24 09:41:13,653] [LogListener] [DEBUG] [loggerModule.py:debug:275] [AUTO Inpaint] coverage=0.268, comps=5, min_center_dist=0.273 → request +[2025-10-24 09:41:13,653] [LogListener] [DEBUG] [loggerModule.py:debug:275] ocr_count: 20 +[2025-10-24 09:41:13,653] [LogListener] [DEBUG] [loggerModule.py:debug:275] is_member_valid: False +[2025-10-24 09:41:13,653] [LogListener] [DEBUG] [loggerModule.py:debug:275] MIGAN 인페인팅 시도 +[2025-10-24 09:41:13,655] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 입력 형태 - 이미지: (1, 3, 800, 800), 마스크: (1, 1, 800, 800) +[2025-10-24 09:41:13,866] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 출력 형태: (800, 800, 3), dtype: uint8 +[2025-10-24 09:41:13,866] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 추론 완료: 211.64 ms +[2025-10-24 09:41:13,874] [LogListener] [DEBUG] [loggerModule.py:debug:275] MIGAN 인페인팅 성공 +[2025-10-24 09:41:13,875] [LogListener] [DEBUG] [loggerModule.py:debug:275] 메모리 변화 [인페인팅]: 43718.9MB -> 43734.4MB (+15.5MB, +0.0%) - 방법: migan +[2025-10-24 09:41:13,875] [LogListener] [DEBUG] [loggerModule.py:debug:275] 인페인팅 완료 +[2025-10-24 09:41:13,955] [LogListener] [DEBUG] [loggerModule.py:debug:275] 텍스트 렌더링 완료 +[2025-10-24 09:41:13,955] [LogListener] [DEBUG] [loggerModule.py:debug:275] watermark_text: 이미지 저작권 보유 +[2025-10-24 09:41:13,956] [LogListener] [DEBUG] [loggerModule.py:debug:275] is_watermark_enabled: True +[2025-10-24 09:41:14,288] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 저장 완료 : C:\ProgramData\ImgWorker\work\translated_option_img_16.webp +[2025-10-24 09:41:14,289] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 16 번역 완료: C:\ProgramData\ImgWorker\work\translated_option_img_16.webp +[2025-10-24 09:41:14,289] [LogListener] [DEBUG] [loggerModule.py:debug:275] ⏱ 이미지 파이프라인 총 2141.8ms | download=0.0ms | ocr=618.0ms | translate=525.0ms | mask=525.0ms | inpaint=227.6ms(migan/DirectML) | render=80.3ms | save=288.4ms +[2025-10-24 09:41:14,290] [LogListener] [DEBUG] [loggerModule.py:debug:275] process_single_image 호출 완료 +[2025-10-24 09:41:14,290] [ResultListener] [WARNING] [loggerModule.py:warning:287] 워커 롤링 스케줄: reason=job-count-threshold +[2025-10-24 09:41:14,290] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 결과 반환 중: uid=dcbda892-9f96-4f83-97e7-4f25afd93607 +[2025-10-24 09:41:14,291] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 결과 반환 완료: uid=dcbda892-9f96-4f83-97e7-4f25afd93607 +[2025-10-24 09:41:14,291] [WorkerRoller] [WARNING] [loggerModule.py:warning:287] 워커 롤링 시작: job-count-threshold +[2025-10-24 09:41:14,291] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 09:41:14,291] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 22368) +[2025-10-24 09:41:14,307] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔥 작업 수신 성공 +[2025-10-24 09:41:14,341] [LogListener] [DEBUG] [loggerModule.py:debug:275] Shutdown signal 수신 → 종료 +[2025-10-24 09:41:14,342] [LogListener] [DEBUG] [loggerModule.py:debug:275] OCR 모듈 정리 완료 +[2025-10-24 09:41:14,342] [LogListener] [DEBUG] [loggerModule.py:debug:275] 마스크 모듈 정리 완료 +[2025-10-24 09:41:14,343] [LogListener] [DEBUG] [loggerModule.py:debug:275] 임시 폴더 삭제됨: C:\ProgramData\ImgWorker\work +[2025-10-24 09:41:14,343] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 프로세서 소멸 +[2025-10-24 09:41:14,847] [WorkerRoller] [INFO] [loggerModule.py:info:281] 워커 프로세스 기동: PID=31976 +[2025-10-24 09:41:15,376] [LogListener] [DEBUG] [loggerModule.py:debug:275] ImageWorker 프로세스 기동 (PID=31976, Name=ImageWorkerProcess) +[2025-10-24 09:41:15,377] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔧 ImageProcessor3 초기화 시작... +[2025-10-24 09:41:15,377] [LogListener] [DEBUG] [loggerModule.py:debug:275] tracemalloc 메모리 추적 시작 +[2025-10-24 09:41:15,384] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ DirectML 환경 준비 완료 (Windows DirectX 12 기반) +[2025-10-24 09:41:15,384] [LogListener] [DEBUG] [loggerModule.py:debug:275] === 🚀 DirectML GPU 상태 초기화 시작 🚀 === +[2025-10-24 09:41:15,384] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🎯 사용자 GPU 가속 요청: False +[2025-10-24 09:41:15,384] [LogListener] [DEBUG] [loggerModule.py:debug:275] 💻 현재 운영체제: Windows +[2025-10-24 09:41:15,384] [LogListener] [DEBUG] [loggerModule.py:debug:275] GPU 가속이 비활성화됨 (toggle_states['use_cuda'] = False) +[2025-10-24 09:41:15,385] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔒 안전한 CPU 모드로 모든 GPU 설정 강제 비활성화 +[2025-10-24 09:41:15,385] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔧 ImageProcessor3 GPU 상태 요약: +[2025-10-24 09:41:15,385] [LogListener] [DEBUG] [loggerModule.py:debug:275] - CUDA 사용 가능: False +[2025-10-24 09:41:15,385] [LogListener] [DEBUG] [loggerModule.py:debug:275] - toggle_states['use_cuda']: NOT_SET +[2025-10-24 09:41:15,385] [LogListener] [DEBUG] [loggerModule.py:debug:275] - GPU 하드웨어 정보: {} +[2025-10-24 09:41:15,385] [LogListener] [DEBUG] [loggerModule.py:debug:275] ImageProcessor3 Init toggle_states: {'TEMP_IMAGE_DIR': 'C:\\ProgramData\\ImgWorker\\work', 'output_image_format': 'webp', 'use_local_rembg': True, 'local_rembg_model_path': 'D:\\py\\img_worker\\modules\\rembg_models', 'local_model_name': 'birefnet-general-lite', 'optionIMGTrans_type': 'CPU', 'detail_IMGTrans_type': 'CPU', 'thumb_trans_type': 'CPU', 'migan_use_accel': True, 'migan_onnx_path': 'D:\\py\\img_worker\\modules\\migan_onnx\\migan_pipeline_v2.onnx', 'ocr_provider_override': 'auto', 'migan_provider_override': 'auto'} +[2025-10-24 09:41:15,385] [LogListener] [DEBUG] [loggerModule.py:debug:275] is_member_valid: False +[2025-10-24 09:41:15,385] [LogListener] [DEBUG] [loggerModule.py:debug:275] debug_images 디렉토리 이미 존재: D:\py\img_worker\modules\debug_images +[2025-10-24 09:41:15,386] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.font_path: D:\py\img_worker\modules\fonts\HakgyoansimDunggeunmisoTTFB.ttf +[2025-10-24 09:41:15,386] [LogListener] [DEBUG] [loggerModule.py:debug:275] toggle_states font_path: D:\py\img_worker\modules\fonts\HakgyoansimDunggeunmisoTTFB.ttf +[2025-10-24 09:41:15,386] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.TEMP_IMAGE_DIR: C:\ProgramData\ImgWorker\work +[2025-10-24 09:41:15,386] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.debugging_save_Dir: D:\py\img_worker\modules\debug_images +[2025-10-24 09:41:15,386] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.unwanted_texts: {} +[2025-10-24 09:41:15,386] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.inpaint_method: migan +[2025-10-24 09:41:15,386] [LogListener] [DEBUG] [loggerModule.py:debug:275] Image.MAX_IMAGE_PIXELS set to 20000000 +[2025-10-24 09:41:15,386] [LogListener] [DEBUG] [loggerModule.py:debug:275] ONNX 모델 타입 설정값: 자동 선택, GPU 정보: {} +[2025-10-24 09:41:15,386] [LogListener] [DEBUG] [loggerModule.py:debug:275] 자동 선택 모드: GPU 추천 모델 simp 사용 +[2025-10-24 09:41:15,386] [LogListener] [DEBUG] [loggerModule.py:debug:275] ONNX OCR 모델 타입 결정: simp (GPU: False) +[2025-10-24 09:41:15,386] [LogListener] [DEBUG] [loggerModule.py:debug:275] ONNX OCR 모듈 CPU 모드로 설정 +[2025-10-24 09:41:15,386] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🚀 ONNX TextSystem 초기화 시작 (CPU 모드) +[2025-10-24 09:41:15,386] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🚀 ONNX TextSystem 초기화 시작 (CPU 모드) +[2025-10-24 09:41:16,983] [LogListener] [DEBUG] [loggerModule.py:debug:275] 문자 사전 파일 UTF-8 확인 완료: D:\py\img_worker\modules\onnx_ocr_module\dict\ppocr_keys_v1.txt +[2025-10-24 09:41:17,080] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ ONNX TextSystem 초기화 완료 (CPU + SIMP (호환성) 모델) +[2025-10-24 09:41:17,081] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ ONNX TextSystem 초기화 완료 (CPU + SIMP 모델) +[2025-10-24 09:41:17,081] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ ONNX OCR 모듈 초기화 성공 (CPU 모드) +[2025-10-24 09:41:17,081] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ ONNX OCR 모듈 초기화 성공 +[2025-10-24 09:41:17,081] [LogListener] [DEBUG] [loggerModule.py:debug:275] 마스크 모듈 초기화 완료 +[2025-10-24 09:41:17,082] [LogListener] [DEBUG] [loggerModule.py:debug:275] MaskModule 초기화 성공 +[2025-10-24 09:41:17,082] [LogListener] [DEBUG] [loggerModule.py:debug:275] 텍스트 렌더링 모듈 초기화 완료 +[2025-10-24 09:41:17,082] [LogListener] [DEBUG] [loggerModule.py:debug:275] 기본 폰트: D:\py\img_worker\modules\fonts\HakgyoansimDunggeunmisoTTFB.ttf +[2025-10-24 09:41:17,082] [LogListener] [DEBUG] [loggerModule.py:debug:275] TextRenderingModule 초기화 성공 +[2025-10-24 09:41:17,082] [LogListener] [DEBUG] [loggerModule.py:debug:275] 폰트 로드 성공: D:\py\img_worker\modules\fonts\HakgyoansimDunggeunmisoTTFB.ttf +[2025-10-24 09:41:17,082] [LogListener] [DEBUG] [loggerModule.py:debug:275] PostImageManager 초기화 성공 +[2025-10-24 09:41:17,082] [LogListener] [DEBUG] [loggerModule.py:debug:275] Request_AI_Server GPU 상태: CUDA 사용 가능=False +[2025-10-24 09:41:17,082] [LogListener] [DEBUG] [loggerModule.py:debug:275] Request_AI_Server 초기화 성공 +[2025-10-24 09:41:17,083] [LogListener] [DEBUG] [loggerModule.py:debug:275] GoogleTranslate 초기화 성공 +[2025-10-24 09:41:17,083] [LogListener] [DEBUG] [loggerModule.py:debug:275] MIGAN CUDA 사용 불가 - CPU 모드로 설정 +[2025-10-24 09:41:17,083] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] GPU 관리자 전달: GPUManager, can_use_cuda: False +[2025-10-24 09:41:17,104] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] GPU 관리자 사용 가능: False +[2025-10-24 09:41:17,352] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 사용 가능한 providers: ['DmlExecutionProvider', 'CPUExecutionProvider'] +[2025-10-24 09:41:17,353] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] DirectML 가속 활성화 +[2025-10-24 09:41:17,353] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 최종 providers: [('DmlExecutionProvider', {}), ('CPUExecutionProvider', {})] +[2025-10-24 09:41:17,353] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] DirectML 가속 시도: [('DmlExecutionProvider', {}), ('CPUExecutionProvider', {})] +[2025-10-24 09:41:17,353] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] DirectML 가속 성공! 실제 providers: ['DmlExecutionProvider', 'CPUExecutionProvider'] +[2025-10-24 09:41:17,353] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 입력 0: image, 형태: ['batch_size', 3, 'height', 'width'], 타입: tensor(uint8) +[2025-10-24 09:41:17,353] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 입력 1: mask, 형태: ['batch_size', 1, 'height', 'width'], 타입: tensor(uint8) +[2025-10-24 09:41:17,353] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 출력 0: result, 형태: ['ScatterNDresult_dim_0', 3, 'ScatterNDresult_dim_2', 'ScatterNDresult_dim_3'], 타입: tensor(uint8) +[2025-10-24 09:41:17,353] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 세션 준비 완료. providers=['DmlExecutionProvider', 'CPUExecutionProvider'] +[2025-10-24 09:41:17,354] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] GPU 관리자 연결 완료: GPUManager +[2025-10-24 09:41:17,354] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 파이프라인 gpu_manager 속성: True, 값: +[2025-10-24 09:41:17,354] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 초기화 완료: gpu_manager 속성=True, 값= +[2025-10-24 09:41:17,354] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔰 OCR 모듈 Warm-up 시작... +[2025-10-24 09:41:17,354] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔍 ONNX OCR 감지 방식: polygon +[2025-10-24 09:41:17,358] [LogListener] [DEBUG] [loggerModule.py:debug:275] ⚡ ONNX OCR 추론 완료: 5.0ms +[2025-10-24 09:41:17,359] [LogListener] [DEBUG] [loggerModule.py:debug:275] 📊 세부 시간 - 감지: 3.0ms, 인식: 0.0ms, 분류: 0.0ms +[2025-10-24 09:41:17,359] [LogListener] [WARNING] [loggerModule.py:warning:287] ⚠️ ONNX OCR 결과가 비어있습니다 +[2025-10-24 09:41:17,363] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ OCR 모듈 Warm-up 성공 +[2025-10-24 09:41:17,363] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔰 ImageProcessor Warm‑up 완료 +[2025-10-24 09:41:17,363] [ResultListener] [INFO] [loggerModule.py:info:281] 워커 READY 수신 +[2025-10-24 09:41:17,364] [ResultListener] [INFO] [loggerModule.py:info:281] 워커 READY 수신 +[2025-10-24 09:41:17,364] [LogListener] [DEBUG] [loggerModule.py:debug:275] 📡 추가 READY 신호 전송 완료 +[2025-10-24 09:41:17,364] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 09:41:17,364] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 31976) +[2025-10-24 09:41:29,714] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔥 작업 수신 성공 +[2025-10-24 09:41:29,714] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🚀 작업 처리 시작: cmd=remove_background, uid=dd92af42-6277-40fe-ac44-9010e1a88c9a +[2025-10-24 09:41:29,715] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 프로세서 toggle_states 업데이트 : {'TEMP_IMAGE_DIR': 'C:\\ProgramData\\ImgWorker\\work', 'output_image_format': 'webp', 'use_local_rembg': True, 'local_rembg_model_path': 'D:\\py\\img_worker\\modules\\rembg_models', 'local_model_name': 'birefnet-general-lite', 'optionIMGTrans_type': 'GPU', 'detail_IMGTrans_type': 'GPU', 'thumb_trans_type': 'GPU', 'migan_use_accel': True, 'migan_onnx_path': 'D:\\py\\img_worker\\modules\\migan_onnx\\migan_pipeline_v2.onnx', 'ocr_provider_override': 'auto', 'migan_provider_override': 'auto'} +[2025-10-24 09:41:29,715] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 실행 직전: cmd=remove_background +[2025-10-24 09:41:29,715] [LogListener] [DEBUG] [loggerModule.py:debug:275] remove_background 호출 직전 +[2025-10-24 09:41:29,715] [LogListener] [DEBUG] [loggerModule.py:debug:275] request_rembg 호출: use_local_rembg=True, local_model_name=birefnet-general-lite +[2025-10-24 09:41:29,717] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔧 백업 rembg 시작: model_name=birefnet-general-lite, object_ratio=0.95, debug_save=True, force_cpu=False +[2025-10-24 09:41:29,717] [LogListener] [DEBUG] [loggerModule.py:debug:275] 📁 디버그 이미지 저장 디렉토리: C:\Users\ADMINI~1\AppData\Local\Temp\wrong_shape_rembg_debug_l49r0pkc +[2025-10-24 09:41:29,732] [LogListener] [DEBUG] [loggerModule.py:debug:275] 💾 1단계 저장 완료: 입력 이미지 (800, 800, 3) → C:\Users\ADMINI~1\AppData\Local\Temp\wrong_shape_rembg_debug_l49r0pkc\01_input_image.png +[2025-10-24 09:41:29,732] [LogListener] [DEBUG] [loggerModule.py:debug:275] BriaAI 배경제거 모듈 초기화 시작 +[2025-10-24 09:41:29,732] [LogListener] [DEBUG] [loggerModule.py:debug:275] ONNX Runtime 사용 가능한 프로바이더: ['DmlExecutionProvider', 'CPUExecutionProvider'] +[2025-10-24 09:41:29,732] [LogListener] [DEBUG] [loggerModule.py:debug:275] BriaAI CPU 모드로 설정 +[2025-10-24 09:41:29,733] [LogListener] [DEBUG] [loggerModule.py:debug:275] BriaAI 배경제거 모듈 초기화 완료 +[2025-10-24 09:41:29,733] [LogListener] [DEBUG] [loggerModule.py:debug:275] 백업용 내장 rembg 모듈 초기화됨 +[2025-10-24 09:41:29,733] [LogListener] [WARNING] [loggerModule.py:warning:287] 지원하지 않는 모델명 (birefnet-general-lite). bria-rmbg-1.4로 대체 사용 +[2025-10-24 09:41:29,759] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ 안전한 임시 파일 생성: C:\Users\ADMINI~1\AppData\Local\Temp\rembg_c14ea288.png (크기: 898113 bytes) +[2025-10-24 09:41:29,760] [LogListener] [DEBUG] [loggerModule.py:debug:275] 💾 2단계 저장 완료: 임시 파일 복사 → C:\Users\ADMINI~1\AppData\Local\Temp\wrong_shape_rembg_debug_l49r0pkc\02_temp_file_copy.png +[2025-10-24 09:41:29,760] [LogListener] [DEBUG] [loggerModule.py:debug:275] 백업 rembg 모듈로 배경 제거 시작: bria-rmbg-1.4 +[2025-10-24 09:41:29,760] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔥 DirectML GPU 모드로 배경 제거 실행 +[2025-10-24 09:41:29,760] [LogListener] [DEBUG] [loggerModule.py:debug:275] BriaAI ONNX 모델 로딩 시도 (원본 providers): D:\py\img_worker\modules\rembg_models\BriaRMBG1.4_model_fp16.onnx +[2025-10-24 09:41:29,898] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ BriaAI ONNX 모델 로딩 완료 (원본 providers) | Providers: ['CPUExecutionProvider'] | Input: input | Output: output +[2025-10-24 09:41:29,908] [LogListener] [DEBUG] [loggerModule.py:debug:275] BriaAI 배경제거 시작: bria-rmbg-1.4 (aggressiveness=0.5) +[2025-10-24 09:41:29,919] [LogListener] [DEBUG] [loggerModule.py:debug:275] 전처리 완료: (800, 800, 3) -> (1, 3, 1024, 1024), 값 범위: [-0.500, 0.500] +[2025-10-24 09:41:30,344] [LogListener] [DEBUG] [loggerModule.py:debug:275] ONNX 모델 출력 개수: 1, 첫 번째 출력 shape: (1, 1, 1024, 1024) +[2025-10-24 09:41:30,344] [LogListener] [DEBUG] [loggerModule.py:debug:275] 추론 완료: (1024, 1024), 값 범위: [0.000, 1.000] +[2025-10-24 09:41:30,358] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ BriaAI 배경제거 성공: bria-rmbg-1.4 (CPU, 0.45초) +[2025-10-24 09:41:30,360] [LogListener] [DEBUG] [loggerModule.py:debug:275] BriaAI 마스크 통계: {'min': 0, 'max': 255, 'mean': 100.32096875, 'nonzero_count': 271913} +[2025-10-24 09:41:30,361] [LogListener] [DEBUG] [loggerModule.py:debug:275] DirectML GPU 모드로 배경 제거 성공 +[2025-10-24 09:41:30,361] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ 백업 rembg 처리 성공: RGB, 크기: (800, 800) +[2025-10-24 09:41:30,398] [LogListener] [DEBUG] [loggerModule.py:debug:275] 💾 3단계 저장 완료: PIL 결과 RGB (800, 800) → C:\Users\ADMINI~1\AppData\Local\Temp\wrong_shape_rembg_debug_l49r0pkc\03_pil_result.png +[2025-10-24 09:41:30,399] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔍 PIL 결과 분석: mode=RGB, size=(800, 800), format=None +[2025-10-24 09:41:30,399] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔍 PIL 밴드: ('R', 'G', 'B') +[2025-10-24 09:41:30,399] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔄 RGB → BGR 변환: (800, 800, 3) +[2025-10-24 09:41:30,408] [LogListener] [DEBUG] [loggerModule.py:debug:275] 💾 4단계 저장 완료: numpy 변환 결과 (800, 800, 3) (흰배경 합성 완료) → C:\Users\ADMINI~1\AppData\Local\Temp\wrong_shape_rembg_debug_l49r0pkc\04_numpy_result.png +[2025-10-24 09:41:30,408] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔧 후처리 시작: object_ratio=0.95 +[2025-10-24 09:41:30,408] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🎨 후처리 시작: 입력 이미지 크기 (800, 800, 3), object_ratio=0.95 +[2025-10-24 09:41:30,409] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ BGR 이미지 처리: 그레이 < 250인 픽셀 수: 253502 +[2025-10-24 09:41:30,419] [LogListener] [DEBUG] [loggerModule.py:debug:275] 💾 5단계 저장: 초기 마스크 (253502개 픽셀) → C:\Users\ADMINI~1\AppData\Local\Temp\wrong_shape_rembg_debug_l49r0pkc\05_initial_mask.png +[2025-10-24 09:41:30,420] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔧 마스크 정제: 정제 전 253502개 → 정제 후 270195개 픽셀 +[2025-10-24 09:41:30,422] [LogListener] [DEBUG] [loggerModule.py:debug:275] 💾 6단계 저장: 정제된 마스크 (270195개 픽셀) → C:\Users\ADMINI~1\AppData\Local\Temp\wrong_shape_rembg_debug_l49r0pkc\06_refined_mask.png +[2025-10-24 09:41:30,424] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔍 연결 요소 분석: 22개 객체 발견 +[2025-10-24 09:41:30,425] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🎯 최대 연결 요소 선택: 149106개 픽셀 +[2025-10-24 09:41:30,427] [LogListener] [DEBUG] [loggerModule.py:debug:275] 💾 7단계 저장: 연결 요소 마스크 (149106개 픽셀) → C:\Users\ADMINI~1\AppData\Local\Temp\wrong_shape_rembg_debug_l49r0pkc\07_component_mask.png +[2025-10-24 09:41:30,428] [LogListener] [DEBUG] [loggerModule.py:debug:275] 📐 바운딩 박스 계산: 유효 픽셀 149106개 +[2025-10-24 09:41:30,429] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✂️ 크롭 영역: (393,86) ~ (761,632), 크기: (547, 369, 3) +[2025-10-24 09:41:30,434] [LogListener] [DEBUG] [loggerModule.py:debug:275] 💾 9단계 저장: 바운딩 박스 크롭 (547, 369, 3) → C:\Users\ADMINI~1\AppData\Local\Temp\wrong_shape_rembg_debug_l49r0pkc\09_bbox_crop.png +[2025-10-24 09:41:30,434] [LogListener] [DEBUG] [loggerModule.py:debug:275] 📏 마진 추가: 5px, 최종 크기: (557, 379, 3) +[2025-10-24 09:41:30,439] [LogListener] [DEBUG] [loggerModule.py:debug:275] 💾 10단계 저장: 마진 추가 (557, 379, 3) → C:\Users\ADMINI~1\AppData\Local\Temp\wrong_shape_rembg_debug_l49r0pkc\10_with_margin.png +[2025-10-24 09:41:30,439] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ 이미지 처리 완료 (이미 흰배경 합성됨): (557, 379, 3) +[2025-10-24 09:41:30,444] [LogListener] [DEBUG] [loggerModule.py:debug:275] 💾 11단계 저장: 흰 배경 합성 (557, 379, 3) → C:\Users\ADMINI~1\AppData\Local\Temp\wrong_shape_rembg_debug_l49r0pkc\11_white_background.png +[2025-10-24 09:41:30,445] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🎯 1000x1000 리사이즈 시작: object_ratio=0.95, final_img.shape=(557, 379, 3) +[2025-10-24 09:41:30,445] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🎯 정사각형 캔버스 생성 시작: 입력=557x379, 목표=1000x1000, 객체비율=0.95 +[2025-10-24 09:41:30,445] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ 이미지 리사이즈 완료: 557x379 → 950x646 (scale_factor=1.706) +[2025-10-24 09:41:30,446] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ 정사각형 캔버스 생성: (1000, 1000, 3) +[2025-10-24 09:41:30,446] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ 이미지 배치 완료: offset=(177,25), size=(646,950) +[2025-10-24 09:41:30,451] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ 정사각형 캔버스 완성: 95% 스케일링, 1000x1000, DPI 72 +[2025-10-24 09:41:30,451] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ 1000x1000 리사이즈 완료: (1000, 1000, 3) +[2025-10-24 09:41:30,451] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ 백업 rembg 후처리 완료: (1000, 1000, 3) +[2025-10-24 09:41:30,464] [LogListener] [DEBUG] [loggerModule.py:debug:275] 💾 최종 결과 저장: (1000, 1000, 3) → C:\Users\ADMINI~1\AppData\Local\Temp\wrong_shape_rembg_debug_l49r0pkc\99_final_result.png +[2025-10-24 09:41:30,465] [LogListener] [DEBUG] [loggerModule.py:debug:275] 임시 파일 삭제 완료: C:\Users\ADMINI~1\AppData\Local\Temp\rembg_c14ea288.png +[2025-10-24 09:41:30,479] [LogListener] [DEBUG] [loggerModule.py:debug:275] 배경제거 이미지 저장: C:\ProgramData\ImgWorker\work\nobg_thumb_rmb_dl_2bea915efa3c.png +[2025-10-24 09:41:30,487] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔍 ONNX OCR 감지 방식: polygon +[2025-10-24 09:41:30,846] [LogListener] [DEBUG] [loggerModule.py:debug:275] ⚡ ONNX OCR 추론 완료: 357.0ms +[2025-10-24 09:41:30,846] [LogListener] [DEBUG] [loggerModule.py:debug:275] 📊 세부 시간 - 감지: 134.0ms, 인식: 207.0ms, 분류: 12.0ms +[2025-10-24 09:41:30,870] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 93.5%): '已' +[2025-10-24 09:41:30,870] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): 'FM' +[2025-10-24 09:41:30,870] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): 'AM' +[2025-10-24 09:41:30,870] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): 'BTTFUSB' +[2025-10-24 09:41:30,870] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): 'SWWB' +[2025-10-24 09:41:30,871] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): '157100' +[2025-10-24 09:41:30,871] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): 'OH3' +[2025-10-24 09:41:30,871] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): 'R11..' +[2025-10-24 09:41:30,871] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): '1OTUNE' +[2025-10-24 09:41:30,871] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): 'RADIO/BT/MUSICPLAYER' +[2025-10-24 09:41:30,871] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): 'GONE' +[2025-10-24 09:41:30,871] [LogListener] [DEBUG] [loggerModule.py:debug:275] 필터링 결과: 1/11개 (신뢰도 + & 중국어) +[2025-10-24 09:41:30,871] [LogListener] [DEBUG] [loggerModule.py:debug:275] 중국어 텍스트 1개 필터링 완료 +[2025-10-24 09:41:30,880] [LogListener] [DEBUG] [loggerModule.py:debug:275] 배경제거 후 마스크 생성 완료 +[2025-10-24 09:41:30,880] [LogListener] [DEBUG] [loggerModule.py:debug:275] MIGAN 인페인팅 시도 +[2025-10-24 09:41:30,889] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 입력 형태 - 이미지: (1, 3, 1000, 1000), 마스크: (1, 1, 1000, 1000) +[2025-10-24 09:41:31,404] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 출력 형태: (1000, 1000, 3), dtype: uint8 +[2025-10-24 09:41:31,404] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 추론 완료: 513.65 ms +[2025-10-24 09:41:31,407] [LogListener] [DEBUG] [loggerModule.py:debug:275] MIGAN 인페인팅 성공 +[2025-10-24 09:41:31,420] [LogListener] [DEBUG] [loggerModule.py:debug:275] 인페인팅 이미지 저장: C:\ProgramData\ImgWorker\work\inpaint_thumb_rmb_dl_2bea915efa3c.png +[2025-10-24 09:41:31,421] [LogListener] [DEBUG] [loggerModule.py:debug:275] remove_background 호출 완료 +[2025-10-24 09:41:31,421] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 결과 반환 중: uid=dd92af42-6277-40fe-ac44-9010e1a88c9a +[2025-10-24 09:41:31,421] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 결과 반환 완료: uid=dd92af42-6277-40fe-ac44-9010e1a88c9a +[2025-10-24 09:41:31,421] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 09:41:31,421] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 31976) +[2025-10-24 09:41:34,183] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔥 작업 수신 성공 +[2025-10-24 09:41:34,183] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🚀 작업 처리 시작: cmd=process_single_image, uid=315df8fd-fd4a-43a8-8c4f-18a9bf81b884 +[2025-10-24 09:41:34,184] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 프로세서 toggle_states 업데이트 : {'TEMP_IMAGE_DIR': 'C:\\ProgramData\\ImgWorker\\work', 'output_image_format': 'webp', 'use_local_rembg': True, 'local_rembg_model_path': 'D:\\py\\img_worker\\modules\\rembg_models', 'local_model_name': 'birefnet-general-lite', 'optionIMGTrans_type': 'GPU', 'detail_IMGTrans_type': 'GPU', 'thumb_trans_type': 'GPU', 'migan_use_accel': True, 'migan_onnx_path': 'D:\\py\\img_worker\\modules\\migan_onnx\\migan_pipeline_v2.onnx', 'ocr_provider_override': 'auto', 'migan_provider_override': 'auto', 'font_type': '폰트5', 'unwanted_texts': ['가격설명', '무료배송', '보증', '사은품', '세일', '이벤트', '특가', '품절', '할인', '할인가'], 'is_member_valid': False, 'authenticated_by_admin': True} +[2025-10-24 09:41:34,184] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 실행 직전: cmd=process_single_image +[2025-10-24 09:41:34,184] [LogListener] [DEBUG] [loggerModule.py:debug:275] process_single_image 호출 직전 +[2025-10-24 09:41:34,185] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 번역시작 +[2025-10-24 09:41:34,445] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 2 처리 시작: C:\ProgramData\ImgWorker\incoming\dl_1aab69dde92a.jpg - 전체 번역 모드 +[2025-10-24 09:41:34,450] [LogListener] [DEBUG] [loggerModule.py:debug:275] 로컬 파일 경로 감지, 다운로드 생략: C:\ProgramData\ImgWorker\incoming\dl_1aab69dde92a.jpg +[2025-10-24 09:41:34,450] [LogListener] [DEBUG] [loggerModule.py:debug:275] 옵션 이미지는 스케일 처리 건너뛰기: thumb +[2025-10-24 09:41:34,450] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 2 로컬 저장위치(썸네일 스케일 처리 완료): C:\ProgramData\ImgWorker\incoming\dl_1aab69dde92a.jpg +[2025-10-24 09:41:34,452] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔍 ONNX OCR 감지 방식: polygon +[2025-10-24 09:41:34,682] [LogListener] [DEBUG] [loggerModule.py:debug:275] ⚡ ONNX OCR 추론 완료: 229.0ms +[2025-10-24 09:41:34,692] [LogListener] [DEBUG] [loggerModule.py:debug:275] 📊 세부 시간 - 감지: 85.0ms, 인식: 136.0ms, 분류: 4.0ms +[2025-10-24 09:41:34,692] [LogListener] [DEBUG] [loggerModule.py:debug:275] 메모리 변화 [OCR 처리]: 44866.7MB -> 44882.1MB (+15.4MB, +0.0%) - 이미지 2 +[2025-10-24 09:41:34,692] [LogListener] [DEBUG] [loggerModule.py:debug:275] ocr_results: [{'text': '手摇可持续充电', 'confidence': 0.9830713868141174, 'polygon': [[120.0, 57.0], [711.0, 57.0], [711.0, 122.0], [120.0, 122.0]], 'bbox': (120, 57, 592, 66), 'method': 'polygon'}, {'text': '突发紧急情况下,可以手摇进行充电', 'confidence': 0.9968866109848022, 'polygon': [[211.0, 148.0], [604.0, 149.0], [604.0, 173.0], [211.0, 172.0]], 'bbox': (211, 148, 394, 26), 'method': 'polygon'}, {'text': '只有手有劲,它就一直不会断电', 'confidence': 0.9770340919494629, 'polygon': [[238.0, 180.0], [579.0, 180.0], [579.0, 203.0], [238.0, 203.0]], 'bbox': (238, 180, 342, 24), 'method': 'polygon'}, {'text': 'AGIO', 'confidence': 0.6023005247116089, 'polygon': [[426.0, 343.0], [454.0, 345.0], [453.0, 355.0], [425.0, 353.0]], 'bbox': (425, 343, 30, 13), 'method': 'polygon'}, {'text': 'III', 'confidence': 0.6114151477813721, 'polygon': [[271.0, 508.0], [304.0, 527.0], [249.0, 622.0], [216.0, 603.0]], 'bbox': (216, 508, 89, 115), 'method': 'polygon'}] +[2025-10-24 09:41:34,693] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 98.3%): '手摇可持续充电' +[2025-10-24 09:41:34,693] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.7%): '突发紧急情况下,可以手摇进行充电' +[2025-10-24 09:41:34,693] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 97.7%): '只有手有劲,它就一直不会断电' +[2025-10-24 09:41:34,693] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): 'AGIO' +[2025-10-24 09:41:34,693] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): 'III' +[2025-10-24 09:41:34,693] [LogListener] [DEBUG] [loggerModule.py:debug:275] 필터링 결과: 3/5개 (신뢰도 + & 중국어) +[2025-10-24 09:41:34,693] [LogListener] [DEBUG] [loggerModule.py:debug:275] filter_ocr_results: [{'text': '手摇可持续充电', 'confidence': 0.9830713868141174, 'polygon': [[120.0, 57.0], [711.0, 57.0], [711.0, 122.0], [120.0, 122.0]], 'bbox': (120, 57, 592, 66), 'method': 'polygon'}, {'text': '突发紧急情况下,可以手摇进行充电', 'confidence': 0.9968866109848022, 'polygon': [[211.0, 148.0], [604.0, 149.0], [604.0, 173.0], [211.0, 172.0]], 'bbox': (211, 148, 394, 26), 'method': 'polygon'}, {'text': '只有手有劲,它就一直不会断电', 'confidence': 0.9770340919494629, 'polygon': [[238.0, 180.0], [579.0, 180.0], [579.0, 203.0], [238.0, 203.0]], 'bbox': (238, 180, 342, 24), 'method': 'polygon'}] +[2025-10-24 09:41:34,694] [LogListener] [DEBUG] [loggerModule.py:debug:275] 중국어 텍스트 3개 필터링 완료 +[2025-10-24 09:41:34,694] [LogListener] [DEBUG] [loggerModule.py:debug:275] 한글 텍스트 0개 필터링 완료 +[2025-10-24 09:41:36,740] [LogListener] [DEBUG] [loggerModule.py:debug:275] translated_texts: ['지속적인 충전을 위한 핸드 크랭크', '긴급상황 발생시 손으로 충전 가능', '손이 강하면 절대 힘을 잃지 않습니다'] +[2025-10-24 09:41:36,740] [LogListener] [DEBUG] [loggerModule.py:debug:275] 마스크 생성 완료 +[2025-10-24 09:41:36,741] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 2 OCR 권한 없음, 전체 번역 모드 +[2025-10-24 09:41:36,748] [LogListener] [DEBUG] [loggerModule.py:debug:275] [AUTO Inpaint] coverage=0.150, comps=1, min_center_dist=0.000 → request +[2025-10-24 09:41:36,748] [LogListener] [DEBUG] [loggerModule.py:debug:275] ocr_count: 3 +[2025-10-24 09:41:36,748] [LogListener] [DEBUG] [loggerModule.py:debug:275] is_member_valid: False +[2025-10-24 09:41:36,748] [LogListener] [DEBUG] [loggerModule.py:debug:275] MIGAN 인페인팅 시도 +[2025-10-24 09:41:36,750] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 입력 형태 - 이미지: (1, 3, 800, 800), 마스크: (1, 1, 800, 800) +[2025-10-24 09:41:36,999] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 출력 형태: (800, 800, 3), dtype: uint8 +[2025-10-24 09:41:37,000] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 추론 완료: 250.00 ms +[2025-10-24 09:41:37,007] [LogListener] [DEBUG] [loggerModule.py:debug:275] MIGAN 인페인팅 성공 +[2025-10-24 09:41:37,007] [LogListener] [DEBUG] [loggerModule.py:debug:275] 메모리 변화 [인페인팅]: 44995.5MB -> 45011.7MB (+16.2MB, +0.0%) - 방법: migan +[2025-10-24 09:41:37,007] [LogListener] [DEBUG] [loggerModule.py:debug:275] 인페인팅 완료 +[2025-10-24 09:41:37,037] [LogListener] [DEBUG] [loggerModule.py:debug:275] 텍스트 렌더링 완료 +[2025-10-24 09:41:37,038] [LogListener] [DEBUG] [loggerModule.py:debug:275] watermark_text: 이미지 저작권 보유 +[2025-10-24 09:41:37,038] [LogListener] [DEBUG] [loggerModule.py:debug:275] is_watermark_enabled: True +[2025-10-24 09:41:37,349] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 저장 완료 : C:\ProgramData\ImgWorker\work\translated_thumb_img_2.webp +[2025-10-24 09:41:37,350] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 2 번역 완료: C:\ProgramData\ImgWorker\work\translated_thumb_img_2.webp +[2025-10-24 09:41:37,350] [LogListener] [DEBUG] [loggerModule.py:debug:275] ⏱ 이미지 파이프라인 총 3164.3ms | download=0.0ms | ocr=237.0ms | translate=2047.0ms | mask=2047.0ms | inpaint=266.0ms(migan/DirectML) | render=30.0ms | save=272.0ms +[2025-10-24 09:41:37,351] [LogListener] [DEBUG] [loggerModule.py:debug:275] process_single_image 호출 완료 +[2025-10-24 09:41:37,351] [ResultListener] [WARNING] [loggerModule.py:warning:287] 워커 롤링 스케줄: reason=job-count-threshold +[2025-10-24 09:41:37,351] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 결과 반환 중: uid=315df8fd-fd4a-43a8-8c4f-18a9bf81b884 +[2025-10-24 09:41:37,351] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 결과 반환 완료: uid=315df8fd-fd4a-43a8-8c4f-18a9bf81b884 +[2025-10-24 09:41:37,351] [WorkerRoller] [WARNING] [loggerModule.py:warning:287] 워커 롤링 시작: job-count-threshold +[2025-10-24 09:41:37,351] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 09:41:37,351] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 31976) +[2025-10-24 09:41:37,364] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔥 작업 수신 성공 +[2025-10-24 09:41:37,404] [LogListener] [DEBUG] [loggerModule.py:debug:275] Shutdown signal 수신 → 종료 +[2025-10-24 09:41:37,405] [LogListener] [DEBUG] [loggerModule.py:debug:275] OCR 모듈 정리 완료 +[2025-10-24 09:41:37,405] [LogListener] [DEBUG] [loggerModule.py:debug:275] 마스크 모듈 정리 완료 +[2025-10-24 09:41:37,431] [LogListener] [DEBUG] [loggerModule.py:debug:275] 임시 폴더 삭제됨: C:\ProgramData\ImgWorker\work +[2025-10-24 09:41:37,431] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 프로세서 소멸 +[2025-10-24 09:41:37,876] [WorkerRoller] [INFO] [loggerModule.py:info:281] 워커 프로세스 기동: PID=13228 +[2025-10-24 09:41:38,319] [LogListener] [DEBUG] [loggerModule.py:debug:275] ImageWorker 프로세스 기동 (PID=13228, Name=ImageWorkerProcess) +[2025-10-24 09:41:38,319] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔧 ImageProcessor3 초기화 시작... +[2025-10-24 09:41:38,319] [LogListener] [DEBUG] [loggerModule.py:debug:275] tracemalloc 메모리 추적 시작 +[2025-10-24 09:41:38,325] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ DirectML 환경 준비 완료 (Windows DirectX 12 기반) +[2025-10-24 09:41:38,325] [LogListener] [DEBUG] [loggerModule.py:debug:275] === 🚀 DirectML GPU 상태 초기화 시작 🚀 === +[2025-10-24 09:41:38,326] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🎯 사용자 GPU 가속 요청: False +[2025-10-24 09:41:38,326] [LogListener] [DEBUG] [loggerModule.py:debug:275] 💻 현재 운영체제: Windows +[2025-10-24 09:41:38,326] [LogListener] [DEBUG] [loggerModule.py:debug:275] GPU 가속이 비활성화됨 (toggle_states['use_cuda'] = False) +[2025-10-24 09:41:38,326] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔒 안전한 CPU 모드로 모든 GPU 설정 강제 비활성화 +[2025-10-24 09:41:38,326] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔧 ImageProcessor3 GPU 상태 요약: +[2025-10-24 09:41:38,326] [LogListener] [DEBUG] [loggerModule.py:debug:275] - CUDA 사용 가능: False +[2025-10-24 09:41:38,326] [LogListener] [DEBUG] [loggerModule.py:debug:275] - toggle_states['use_cuda']: NOT_SET +[2025-10-24 09:41:38,326] [LogListener] [DEBUG] [loggerModule.py:debug:275] - GPU 하드웨어 정보: {} +[2025-10-24 09:41:38,326] [LogListener] [DEBUG] [loggerModule.py:debug:275] ImageProcessor3 Init toggle_states: {'TEMP_IMAGE_DIR': 'C:\\ProgramData\\ImgWorker\\work', 'output_image_format': 'webp', 'use_local_rembg': True, 'local_rembg_model_path': 'D:\\py\\img_worker\\modules\\rembg_models', 'local_model_name': 'birefnet-general-lite', 'optionIMGTrans_type': 'CPU', 'detail_IMGTrans_type': 'CPU', 'thumb_trans_type': 'CPU', 'migan_use_accel': True, 'migan_onnx_path': 'D:\\py\\img_worker\\modules\\migan_onnx\\migan_pipeline_v2.onnx', 'ocr_provider_override': 'auto', 'migan_provider_override': 'auto'} +[2025-10-24 09:41:38,326] [LogListener] [DEBUG] [loggerModule.py:debug:275] is_member_valid: False +[2025-10-24 09:41:38,327] [LogListener] [DEBUG] [loggerModule.py:debug:275] debug_images 디렉토리 이미 존재: D:\py\img_worker\modules\debug_images +[2025-10-24 09:41:38,327] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.font_path: D:\py\img_worker\modules\fonts\HakgyoansimDunggeunmisoTTFB.ttf +[2025-10-24 09:41:38,327] [LogListener] [DEBUG] [loggerModule.py:debug:275] toggle_states font_path: D:\py\img_worker\modules\fonts\HakgyoansimDunggeunmisoTTFB.ttf +[2025-10-24 09:41:38,327] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.TEMP_IMAGE_DIR: C:\ProgramData\ImgWorker\work +[2025-10-24 09:41:38,327] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.debugging_save_Dir: D:\py\img_worker\modules\debug_images +[2025-10-24 09:41:38,327] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.unwanted_texts: {} +[2025-10-24 09:41:38,327] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.inpaint_method: migan +[2025-10-24 09:41:38,327] [LogListener] [DEBUG] [loggerModule.py:debug:275] Image.MAX_IMAGE_PIXELS set to 20000000 +[2025-10-24 09:41:38,327] [LogListener] [DEBUG] [loggerModule.py:debug:275] ONNX 모델 타입 설정값: 자동 선택, GPU 정보: {} +[2025-10-24 09:41:38,327] [LogListener] [DEBUG] [loggerModule.py:debug:275] 자동 선택 모드: GPU 추천 모델 simp 사용 +[2025-10-24 09:41:38,327] [LogListener] [DEBUG] [loggerModule.py:debug:275] ONNX OCR 모델 타입 결정: simp (GPU: False) +[2025-10-24 09:41:38,327] [LogListener] [DEBUG] [loggerModule.py:debug:275] ONNX OCR 모듈 CPU 모드로 설정 +[2025-10-24 09:41:38,327] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🚀 ONNX TextSystem 초기화 시작 (CPU 모드) +[2025-10-24 09:41:38,327] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🚀 ONNX TextSystem 초기화 시작 (CPU 모드) +[2025-10-24 09:41:39,888] [LogListener] [DEBUG] [loggerModule.py:debug:275] 문자 사전 파일 UTF-8 확인 완료: D:\py\img_worker\modules\onnx_ocr_module\dict\ppocr_keys_v1.txt +[2025-10-24 09:41:39,977] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ ONNX TextSystem 초기화 완료 (CPU + SIMP (호환성) 모델) +[2025-10-24 09:41:39,977] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ ONNX TextSystem 초기화 완료 (CPU + SIMP 모델) +[2025-10-24 09:41:39,977] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ ONNX OCR 모듈 초기화 성공 (CPU 모드) +[2025-10-24 09:41:39,978] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ ONNX OCR 모듈 초기화 성공 +[2025-10-24 09:41:39,978] [LogListener] [DEBUG] [loggerModule.py:debug:275] 마스크 모듈 초기화 완료 +[2025-10-24 09:41:39,978] [LogListener] [DEBUG] [loggerModule.py:debug:275] MaskModule 초기화 성공 +[2025-10-24 09:41:39,978] [LogListener] [DEBUG] [loggerModule.py:debug:275] 텍스트 렌더링 모듈 초기화 완료 +[2025-10-24 09:41:39,978] [LogListener] [DEBUG] [loggerModule.py:debug:275] 기본 폰트: D:\py\img_worker\modules\fonts\HakgyoansimDunggeunmisoTTFB.ttf +[2025-10-24 09:41:39,978] [LogListener] [DEBUG] [loggerModule.py:debug:275] TextRenderingModule 초기화 성공 +[2025-10-24 09:41:39,978] [LogListener] [DEBUG] [loggerModule.py:debug:275] 폰트 로드 성공: D:\py\img_worker\modules\fonts\HakgyoansimDunggeunmisoTTFB.ttf +[2025-10-24 09:41:39,978] [LogListener] [DEBUG] [loggerModule.py:debug:275] PostImageManager 초기화 성공 +[2025-10-24 09:41:39,978] [LogListener] [DEBUG] [loggerModule.py:debug:275] Request_AI_Server GPU 상태: CUDA 사용 가능=False +[2025-10-24 09:41:39,979] [LogListener] [DEBUG] [loggerModule.py:debug:275] Request_AI_Server 초기화 성공 +[2025-10-24 09:41:39,979] [LogListener] [DEBUG] [loggerModule.py:debug:275] GoogleTranslate 초기화 성공 +[2025-10-24 09:41:39,979] [LogListener] [DEBUG] [loggerModule.py:debug:275] MIGAN CUDA 사용 불가 - CPU 모드로 설정 +[2025-10-24 09:41:39,979] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] GPU 관리자 전달: GPUManager, can_use_cuda: False +[2025-10-24 09:41:39,998] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] GPU 관리자 사용 가능: False +[2025-10-24 09:41:40,218] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 사용 가능한 providers: ['DmlExecutionProvider', 'CPUExecutionProvider'] +[2025-10-24 09:41:40,218] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] DirectML 가속 활성화 +[2025-10-24 09:41:40,218] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 최종 providers: [('DmlExecutionProvider', {}), ('CPUExecutionProvider', {})] +[2025-10-24 09:41:40,219] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] DirectML 가속 시도: [('DmlExecutionProvider', {}), ('CPUExecutionProvider', {})] +[2025-10-24 09:41:40,219] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] DirectML 가속 성공! 실제 providers: ['DmlExecutionProvider', 'CPUExecutionProvider'] +[2025-10-24 09:41:40,219] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 입력 0: image, 형태: ['batch_size', 3, 'height', 'width'], 타입: tensor(uint8) +[2025-10-24 09:41:40,219] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 입력 1: mask, 형태: ['batch_size', 1, 'height', 'width'], 타입: tensor(uint8) +[2025-10-24 09:41:40,219] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 출력 0: result, 형태: ['ScatterNDresult_dim_0', 3, 'ScatterNDresult_dim_2', 'ScatterNDresult_dim_3'], 타입: tensor(uint8) +[2025-10-24 09:41:40,219] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 세션 준비 완료. providers=['DmlExecutionProvider', 'CPUExecutionProvider'] +[2025-10-24 09:41:40,219] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] GPU 관리자 연결 완료: GPUManager +[2025-10-24 09:41:40,219] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 파이프라인 gpu_manager 속성: True, 값: +[2025-10-24 09:41:40,219] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 초기화 완료: gpu_manager 속성=True, 값= +[2025-10-24 09:41:40,219] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔰 OCR 모듈 Warm-up 시작... +[2025-10-24 09:41:40,220] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔍 ONNX OCR 감지 방식: polygon +[2025-10-24 09:41:40,224] [LogListener] [DEBUG] [loggerModule.py:debug:275] ⚡ ONNX OCR 추론 완료: 5.0ms +[2025-10-24 09:41:40,224] [LogListener] [DEBUG] [loggerModule.py:debug:275] 📊 세부 시간 - 감지: 4.0ms, 인식: 0.0ms, 분류: 0.0ms +[2025-10-24 09:41:40,224] [LogListener] [WARNING] [loggerModule.py:warning:287] ⚠️ ONNX OCR 결과가 비어있습니다 +[2025-10-24 09:41:40,228] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ OCR 모듈 Warm-up 성공 +[2025-10-24 09:41:40,228] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔰 ImageProcessor Warm‑up 완료 +[2025-10-24 09:41:40,228] [ResultListener] [INFO] [loggerModule.py:info:281] 워커 READY 수신 +[2025-10-24 09:41:40,228] [WorkerRoller] [INFO] [loggerModule.py:info:281] 버퍼 작업 재개: 1건 +[2025-10-24 09:41:40,228] [ResultListener] [INFO] [loggerModule.py:info:281] 워커 READY 수신 +[2025-10-24 09:41:40,229] [LogListener] [DEBUG] [loggerModule.py:debug:275] 📡 추가 READY 신호 전송 완료 +[2025-10-24 09:41:40,229] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 09:41:40,229] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 13228) +[2025-10-24 09:41:40,229] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔥 작업 수신 성공 +[2025-10-24 09:41:40,229] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🚀 작업 처리 시작: cmd=process_single_image, uid=ec184d15-b5d0-4d8d-bf8e-de5abc7711cf +[2025-10-24 09:41:40,229] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 프로세서 toggle_states 업데이트 : {'TEMP_IMAGE_DIR': 'C:\\ProgramData\\ImgWorker\\work', 'output_image_format': 'webp', 'use_local_rembg': True, 'local_rembg_model_path': 'D:\\py\\img_worker\\modules\\rembg_models', 'local_model_name': 'birefnet-general-lite', 'optionIMGTrans_type': 'GPU', 'detail_IMGTrans_type': 'GPU', 'thumb_trans_type': 'GPU', 'migan_use_accel': True, 'migan_onnx_path': 'D:\\py\\img_worker\\modules\\migan_onnx\\migan_pipeline_v2.onnx', 'ocr_provider_override': 'auto', 'migan_provider_override': 'auto', 'font_type': '폰트5', 'unwanted_texts': ['가격설명', '무료배송', '보증', '사은품', '세일', '이벤트', '특가', '품절', '할인', '할인가'], 'is_member_valid': False, 'authenticated_by_admin': True} +[2025-10-24 09:41:40,229] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 실행 직전: cmd=process_single_image +[2025-10-24 09:41:40,230] [LogListener] [DEBUG] [loggerModule.py:debug:275] process_single_image 호출 직전 +[2025-10-24 09:41:40,232] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 번역시작 +[2025-10-24 09:41:40,730] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 3 처리 시작: C:\ProgramData\ImgWorker\incoming\dl_9b93d8f32609.jpg - 전체 번역 모드 +[2025-10-24 09:41:40,734] [LogListener] [DEBUG] [loggerModule.py:debug:275] 로컬 파일 경로 감지, 다운로드 생략: C:\ProgramData\ImgWorker\incoming\dl_9b93d8f32609.jpg +[2025-10-24 09:41:40,734] [LogListener] [DEBUG] [loggerModule.py:debug:275] 옵션 이미지는 스케일 처리 건너뛰기: thumb +[2025-10-24 09:41:40,734] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 3 로컬 저장위치(썸네일 스케일 처리 완료): C:\ProgramData\ImgWorker\incoming\dl_9b93d8f32609.jpg +[2025-10-24 09:41:40,736] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔍 ONNX OCR 감지 방식: polygon +[2025-10-24 09:41:41,124] [LogListener] [DEBUG] [loggerModule.py:debug:275] ⚡ ONNX OCR 추론 완료: 387.0ms +[2025-10-24 09:41:41,124] [LogListener] [DEBUG] [loggerModule.py:debug:275] 📊 세부 시간 - 감지: 97.0ms, 인식: 281.0ms, 분류: 5.0ms +[2025-10-24 09:41:41,143] [LogListener] [DEBUG] [loggerModule.py:debug:275] 메모리 변화 [OCR 처리]: 39858.7MB -> 40078.4MB (+219.7MB, +0.6%) - 이미지 3 +[2025-10-24 09:41:41,161] [LogListener] [DEBUG] [loggerModule.py:debug:275] ocr_results: [{'text': '应急手电筒', 'confidence': 0.9925681352615356, 'polygon': [[200.0, 54.0], [617.0, 54.0], [617.0, 119.0], [200.0, 119.0]], 'bbox': (200, 54, 418, 66), 'method': 'polygon'}, {'text': 'SOS求救功能', 'confidence': 0.9445775151252747, 'polygon': [[143.0, 144.0], [677.0, 144.0], [677.0, 208.0], [143.0, 208.0]], 'bbox': (143, 144, 535, 65), 'method': 'polygon'}, {'text': '紧急情况下推至SOS位置,马上发出100分贝高音警报', 'confidence': 0.9787427186965942, 'polygon': [[133.0, 228.0], [709.0, 228.0], [709.0, 251.0], [133.0, 251.0]], 'bbox': (133, 228, 577, 24), 'method': 'polygon'}, {'text': '100', 'confidence': 0.9989057183265686, 'polygon': [[44.0, 650.0], [161.0, 650.0], [161.0, 721.0], [44.0, 721.0]], 'bbox': (44, 650, 118, 72), 'method': 'polygon'}, {'text': 'dB', 'confidence': 0.9922839403152466, 'polygon': [[161.0, 678.0], [215.0, 682.0], [212.0, 723.0], [158.0, 719.0]], 'bbox': (158, 678, 58, 46), 'method': 'polygon'}, {'text': '高音报警', 'confidence': 0.9997329115867615, 'polygon': [[52.0, 722.0], [211.0, 722.0], [211.0, 765.0], [52.0, 765.0]], 'bbox': (52, 722, 160, 44), 'method': 'polygon'}] +[2025-10-24 09:41:41,162] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.3%): '应急手电筒' +[2025-10-24 09:41:41,162] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 94.5%): 'SOS求救功能' +[2025-10-24 09:41:41,162] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 97.9%): '紧急情况下推至SOS位置,马上发出100分贝高音警报' +[2025-10-24 09:41:41,162] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): '100' +[2025-10-24 09:41:41,162] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): 'dB' +[2025-10-24 09:41:41,162] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 100.0%): '高音报警' +[2025-10-24 09:41:41,163] [LogListener] [DEBUG] [loggerModule.py:debug:275] 필터링 결과: 4/6개 (신뢰도 + & 중국어) +[2025-10-24 09:41:41,163] [LogListener] [DEBUG] [loggerModule.py:debug:275] filter_ocr_results: [{'text': '应急手电筒', 'confidence': 0.9925681352615356, 'polygon': [[200.0, 54.0], [617.0, 54.0], [617.0, 119.0], [200.0, 119.0]], 'bbox': (200, 54, 418, 66), 'method': 'polygon'}, {'text': 'SOS求救功能', 'confidence': 0.9445775151252747, 'polygon': [[143.0, 144.0], [677.0, 144.0], [677.0, 208.0], [143.0, 208.0]], 'bbox': (143, 144, 535, 65), 'method': 'polygon'}, {'text': '紧急情况下推至SOS位置,马上发出100分贝高音警报', 'confidence': 0.9787427186965942, 'polygon': [[133.0, 228.0], [709.0, 228.0], [709.0, 251.0], [133.0, 251.0]], 'bbox': (133, 228, 577, 24), 'method': 'polygon'}, {'text': '高音报警', 'confidence': 0.9997329115867615, 'polygon': [[52.0, 722.0], [211.0, 722.0], [211.0, 765.0], [52.0, 765.0]], 'bbox': (52, 722, 160, 44), 'method': 'polygon'}] +[2025-10-24 09:41:41,163] [LogListener] [DEBUG] [loggerModule.py:debug:275] 중국어 텍스트 4개 필터링 완료 +[2025-10-24 09:41:41,163] [LogListener] [DEBUG] [loggerModule.py:debug:275] 한글 텍스트 0개 필터링 완료 +[2025-10-24 09:41:43,415] [LogListener] [DEBUG] [loggerModule.py:debug:275] translated_texts: ['비상 손전등', 'SOS 도움말 기능', '긴급 상황 발생 시 SOS 위치로 밀면 즉시 100데시벨의 고음 경보가 울립니다.', '높은 음조 경보'] +[2025-10-24 09:41:43,416] [LogListener] [DEBUG] [loggerModule.py:debug:275] 마스크 생성 완료 +[2025-10-24 09:41:43,416] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 3 OCR 권한 없음, 전체 번역 모드 +[2025-10-24 09:41:43,422] [LogListener] [DEBUG] [loggerModule.py:debug:275] [AUTO Inpaint] coverage=0.218, comps=2, min_center_dist=0.572 → request +[2025-10-24 09:41:43,422] [LogListener] [DEBUG] [loggerModule.py:debug:275] ocr_count: 4 +[2025-10-24 09:41:43,422] [LogListener] [DEBUG] [loggerModule.py:debug:275] is_member_valid: False +[2025-10-24 09:41:43,422] [LogListener] [DEBUG] [loggerModule.py:debug:275] MIGAN 인페인팅 시도 +[2025-10-24 09:41:43,425] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 입력 형태 - 이미지: (1, 3, 800, 800), 마스크: (1, 1, 800, 800) +[2025-10-24 09:41:43,929] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 출력 형태: (800, 800, 3), dtype: uint8 +[2025-10-24 09:41:43,929] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 추론 완료: 505.00 ms +[2025-10-24 09:41:43,936] [LogListener] [DEBUG] [loggerModule.py:debug:275] MIGAN 인페인팅 성공 +[2025-10-24 09:41:43,937] [LogListener] [DEBUG] [loggerModule.py:debug:275] 메모리 변화 [인페인팅]: 40092.2MB -> 40273.2MB (+181.0MB, +0.5%) - 방법: migan +[2025-10-24 09:41:43,937] [LogListener] [DEBUG] [loggerModule.py:debug:275] 인페인팅 완료 +[2025-10-24 09:41:43,980] [LogListener] [DEBUG] [loggerModule.py:debug:275] 텍스트 렌더링 완료 +[2025-10-24 09:41:43,981] [LogListener] [DEBUG] [loggerModule.py:debug:275] watermark_text: 이미지 저작권 보유 +[2025-10-24 09:41:43,981] [LogListener] [DEBUG] [loggerModule.py:debug:275] is_watermark_enabled: True +[2025-10-24 09:41:44,305] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 저장 완료 : C:\ProgramData\ImgWorker\work\translated_thumb_img_3.webp +[2025-10-24 09:41:44,307] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 3 번역 완료: C:\ProgramData\ImgWorker\work\translated_thumb_img_3.webp +[2025-10-24 09:41:44,307] [LogListener] [DEBUG] [loggerModule.py:debug:275] ⏱ 이미지 파이프라인 총 4075.0ms | download=0.0ms | ocr=394.0ms | translate=2254.4ms | mask=2254.4ms | inpaint=520.0ms(migan/DirectML) | render=44.0ms | save=293.2ms +[2025-10-24 09:41:44,308] [LogListener] [DEBUG] [loggerModule.py:debug:275] process_single_image 호출 완료 +[2025-10-24 09:41:44,308] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 결과 반환 중: uid=ec184d15-b5d0-4d8d-bf8e-de5abc7711cf +[2025-10-24 09:41:44,308] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 결과 반환 완료: uid=ec184d15-b5d0-4d8d-bf8e-de5abc7711cf +[2025-10-24 09:41:44,308] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 09:41:44,308] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 13228) +[2025-10-24 09:41:46,908] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔥 작업 수신 성공 +[2025-10-24 09:41:46,908] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🚀 작업 처리 시작: cmd=process_single_image, uid=c0430c82-d56e-478a-aa32-18c7e741471c +[2025-10-24 09:41:46,908] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 프로세서 toggle_states 업데이트 : {'TEMP_IMAGE_DIR': 'C:\\ProgramData\\ImgWorker\\work', 'output_image_format': 'webp', 'use_local_rembg': True, 'local_rembg_model_path': 'D:\\py\\img_worker\\modules\\rembg_models', 'local_model_name': 'birefnet-general-lite', 'optionIMGTrans_type': 'GPU', 'detail_IMGTrans_type': 'GPU', 'thumb_trans_type': 'GPU', 'migan_use_accel': True, 'migan_onnx_path': 'D:\\py\\img_worker\\modules\\migan_onnx\\migan_pipeline_v2.onnx', 'ocr_provider_override': 'auto', 'migan_provider_override': 'auto', 'font_type': '폰트5', 'unwanted_texts': ['가격설명', '무료배송', '보증', '사은품', '세일', '이벤트', '특가', '품절', '할인', '할인가'], 'is_member_valid': False, 'authenticated_by_admin': True} +[2025-10-24 09:41:46,908] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 실행 직전: cmd=process_single_image +[2025-10-24 09:41:46,909] [LogListener] [DEBUG] [loggerModule.py:debug:275] process_single_image 호출 직전 +[2025-10-24 09:41:46,909] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 번역시작 +[2025-10-24 09:41:47,104] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 4 처리 시작: C:\ProgramData\ImgWorker\incoming\dl_7f7d49653e8c.jpg - 전체 번역 모드 +[2025-10-24 09:41:47,110] [LogListener] [DEBUG] [loggerModule.py:debug:275] 로컬 파일 경로 감지, 다운로드 생략: C:\ProgramData\ImgWorker\incoming\dl_7f7d49653e8c.jpg +[2025-10-24 09:41:47,111] [LogListener] [DEBUG] [loggerModule.py:debug:275] 옵션 이미지는 스케일 처리 건너뛰기: thumb +[2025-10-24 09:41:47,111] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 4 로컬 저장위치(썸네일 스케일 처리 완료): C:\ProgramData\ImgWorker\incoming\dl_7f7d49653e8c.jpg +[2025-10-24 09:41:47,112] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔍 ONNX OCR 감지 방식: polygon +[2025-10-24 09:41:47,586] [LogListener] [DEBUG] [loggerModule.py:debug:275] ⚡ ONNX OCR 추론 완료: 471.0ms +[2025-10-24 09:41:47,617] [LogListener] [DEBUG] [loggerModule.py:debug:275] 📊 세부 시간 - 감지: 106.0ms, 인식: 343.0ms, 분류: 17.0ms +[2025-10-24 09:41:47,618] [LogListener] [DEBUG] [loggerModule.py:debug:275] 메모리 변화 [OCR 처리]: 40455.6MB -> 40428.5MB (-27.1MB, -0.1%) - 이미지 4 +[2025-10-24 09:41:47,618] [LogListener] [DEBUG] [loggerModule.py:debug:275] ocr_results: [{'text': '全能选手', 'confidence': 0.9155265092849731, 'polygon': [[93.0, 48.0], [382.0, 48.0], [382.0, 103.0], [93.0, 103.0]], 'bbox': (93, 48, 290, 56), 'method': 'polygon'}, {'text': '周全之选', 'confidence': 0.9892017841339111, 'polygon': [[441.0, 50.0], [729.0, 48.0], [729.0, 103.0], [441.0, 105.0]], 'bbox': (441, 48, 289, 58), 'method': 'polygon'}, {'text': '15种功能合为一体', 'confidence': 0.9983953833580017, 'polygon': [[277.0, 123.0], [515.0, 123.0], [515.0, 150.0], [277.0, 150.0]], 'bbox': (277, 123, 239, 28), 'method': 'polygon'}, {'text': '收音天线', 'confidence': 0.9990787506103516, 'polygon': [[566.0, 164.0], [627.0, 164.0], [627.0, 182.0], [566.0, 182.0]], 'bbox': (566, 164, 62, 19), 'method': 'polygon'}, {'text': '指南针', 'confidence': 0.9995152354240417, 'polygon': [[567.0, 187.0], [612.0, 187.0], [612.0, 206.0], [567.0, 206.0]], 'bbox': (567, 187, 46, 20), 'method': 'polygon'}, {'text': '功能按键区', 'confidence': 0.9981991648674011, 'polygon': [[567.0, 211.0], [642.0, 211.0], [642.0, 229.0], [567.0, 229.0]], 'bbox': (567, 211, 76, 19), 'method': 'polygon'}, {'text': '指示灯显示', 'confidence': 0.9990951418876648, 'polygon': [[54.0, 221.0], [130.0, 223.0], [130.0, 241.0], [54.0, 239.0]], 'bbox': (54, 221, 77, 21), 'method': 'polygon'}, {'text': '手电筒', 'confidence': 0.999311625957489, 'polygon': [[567.0, 235.0], [613.0, 235.0], [613.0, 253.0], [567.0, 253.0]], 'bbox': (567, 235, 47, 19), 'method': 'polygon'}, {'text': '频道显示', 'confidence': 0.997969388961792, 'polygon': [[69.0, 261.0], [130.0, 261.0], [130.0, 279.0], [69.0, 279.0]], 'bbox': (69, 261, 62, 19), 'method': 'polygon'}, {'text': '开机按钮', 'confidence': 0.9980503916740417, 'polygon': [[567.0, 277.0], [628.0, 277.0], [628.0, 294.0], [567.0, 294.0]], 'bbox': (567, 277, 62, 18), 'method': 'polygon'}, {'text': '调频拨轮', 'confidence': 0.9247385263442993, 'polygon': [[568.0, 336.0], [629.0, 336.0], [629.0, 354.0], [568.0, 354.0]], 'bbox': (568, 336, 62, 19), 'method': 'polygon'}, {'text': '上一曲/暂停/下一曲', 'confidence': 0.9802972078323364, 'polygon': [[611.0, 402.0], [744.0, 401.0], [744.0, 419.0], [611.0, 420.0]], 'bbox': (611, 401, 134, 20), 'method': 'polygon'}, {'text': '大口径扬声器', 'confidence': 0.995476484298706, 'polygon': [[43.0, 427.0], [131.0, 427.0], [131.0, 444.0], [43.0, 444.0]], 'bbox': (43, 427, 89, 18), 'method': 'polygon'}, {'text': 'SOS警报/输出充电', 'confidence': 0.9886554479598999, 'polygon': [[620.0, 428.0], [745.0, 428.0], [745.0, 448.0], [620.0, 448.0]], 'bbox': (620, 428, 126, 21), 'method': 'polygon'}, {'text': '耳机口', 'confidence': 0.9996671080589294, 'polygon': [[645.0, 455.0], [690.0, 455.0], [690.0, 474.0], [645.0, 474.0]], 'bbox': (645, 455, 46, 20), 'method': 'polygon'}, {'text': '充电口', 'confidence': 0.9984938502311707, 'polygon': [[643.0, 476.0], [690.0, 476.0], [690.0, 494.0], [643.0, 494.0]], 'bbox': (643, 476, 48, 19), 'method': 'polygon'}, {'text': 'USB口/U盘插口', 'confidence': 0.9916080832481384, 'polygon': [[642.0, 518.0], [748.0, 518.0], [748.0, 535.0], [642.0, 535.0]], 'bbox': (642, 518, 107, 18), 'method': 'polygon'}, {'text': 'TF插口', 'confidence': 0.9968512654304504, 'polygon': [[641.0, 541.0], [690.0, 541.0], [690.0, 559.0], [641.0, 559.0]], 'bbox': (641, 541, 50, 19), 'method': 'polygon'}, {'text': '蓝牙/收音机/', 'confidence': 0.9914805293083191, 'polygon': [[643.0, 583.0], [730.0, 583.0], [730.0, 601.0], [643.0, 601.0]], 'bbox': (643, 583, 88, 19), 'method': 'polygon'}, {'text': 'TF卡模式选择', 'confidence': 0.9963052868843079, 'polygon': [[642.0, 602.0], [732.0, 602.0], [732.0, 620.0], [642.0, 620.0]], 'bbox': (642, 602, 91, 19), 'method': 'polygon'}, {'text': '太阳能充电面板', 'confidence': 0.9985271692276001, 'polygon': [[641.0, 639.0], [745.0, 639.0], [745.0, 656.0], [641.0, 656.0]], 'bbox': (641, 639, 105, 18), 'method': 'polygon'}, {'text': '手摇发电手柄', 'confidence': 0.9984351992607117, 'polygon': [[642.0, 679.0], [732.0, 679.0], [732.0, 696.0], [642.0, 696.0]], 'bbox': (642, 679, 91, 18), 'method': 'polygon'}] +[2025-10-24 09:41:47,618] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 91.6%): '全能选手' +[2025-10-24 09:41:47,619] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 98.9%): '周全之选' +[2025-10-24 09:41:47,619] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.8%): '15种功能合为一体' +[2025-10-24 09:41:47,619] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.9%): '收音天线' +[2025-10-24 09:41:47,619] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 100.0%): '指南针' +[2025-10-24 09:41:47,619] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.8%): '功能按键区' +[2025-10-24 09:41:47,619] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.9%): '指示灯显示' +[2025-10-24 09:41:47,619] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.9%): '手电筒' +[2025-10-24 09:41:47,619] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.8%): '频道显示' +[2025-10-24 09:41:47,619] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.8%): '开机按钮' +[2025-10-24 09:41:47,619] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 92.5%): '调频拨轮' +[2025-10-24 09:41:47,619] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 98.0%): '上一曲/暂停/下一曲' +[2025-10-24 09:41:47,620] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.5%): '大口径扬声器' +[2025-10-24 09:41:47,620] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 98.9%): 'SOS警报/输出充电' +[2025-10-24 09:41:47,620] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 100.0%): '耳机口' +[2025-10-24 09:41:47,620] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.8%): '充电口' +[2025-10-24 09:41:47,620] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.2%): 'USB口/U盘插口' +[2025-10-24 09:41:47,620] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.7%): 'TF插口' +[2025-10-24 09:41:47,620] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.1%): '蓝牙/收音机/' +[2025-10-24 09:41:47,620] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.6%): 'TF卡模式选择' +[2025-10-24 09:41:47,620] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.9%): '太阳能充电面板' +[2025-10-24 09:41:47,620] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.8%): '手摇发电手柄' +[2025-10-24 09:41:47,621] [LogListener] [DEBUG] [loggerModule.py:debug:275] 필터링 결과: 22/22개 (신뢰도 + & 중국어) +[2025-10-24 09:41:47,621] [LogListener] [DEBUG] [loggerModule.py:debug:275] filter_ocr_results: [{'text': '全能选手', 'confidence': 0.9155265092849731, 'polygon': [[93.0, 48.0], [382.0, 48.0], [382.0, 103.0], [93.0, 103.0]], 'bbox': (93, 48, 290, 56), 'method': 'polygon'}, {'text': '周全之选', 'confidence': 0.9892017841339111, 'polygon': [[441.0, 50.0], [729.0, 48.0], [729.0, 103.0], [441.0, 105.0]], 'bbox': (441, 48, 289, 58), 'method': 'polygon'}, {'text': '15种功能合为一体', 'confidence': 0.9983953833580017, 'polygon': [[277.0, 123.0], [515.0, 123.0], [515.0, 150.0], [277.0, 150.0]], 'bbox': (277, 123, 239, 28), 'method': 'polygon'}, {'text': '收音天线', 'confidence': 0.9990787506103516, 'polygon': [[566.0, 164.0], [627.0, 164.0], [627.0, 182.0], [566.0, 182.0]], 'bbox': (566, 164, 62, 19), 'method': 'polygon'}, {'text': '指南针', 'confidence': 0.9995152354240417, 'polygon': [[567.0, 187.0], [612.0, 187.0], [612.0, 206.0], [567.0, 206.0]], 'bbox': (567, 187, 46, 20), 'method': 'polygon'}, {'text': '功能按键区', 'confidence': 0.9981991648674011, 'polygon': [[567.0, 211.0], [642.0, 211.0], [642.0, 229.0], [567.0, 229.0]], 'bbox': (567, 211, 76, 19), 'method': 'polygon'}, {'text': '指示灯显示', 'confidence': 0.9990951418876648, 'polygon': [[54.0, 221.0], [130.0, 223.0], [130.0, 241.0], [54.0, 239.0]], 'bbox': (54, 221, 77, 21), 'method': 'polygon'}, {'text': '手电筒', 'confidence': 0.999311625957489, 'polygon': [[567.0, 235.0], [613.0, 235.0], [613.0, 253.0], [567.0, 253.0]], 'bbox': (567, 235, 47, 19), 'method': 'polygon'}, {'text': '频道显示', 'confidence': 0.997969388961792, 'polygon': [[69.0, 261.0], [130.0, 261.0], [130.0, 279.0], [69.0, 279.0]], 'bbox': (69, 261, 62, 19), 'method': 'polygon'}, {'text': '开机按钮', 'confidence': 0.9980503916740417, 'polygon': [[567.0, 277.0], [628.0, 277.0], [628.0, 294.0], [567.0, 294.0]], 'bbox': (567, 277, 62, 18), 'method': 'polygon'}, {'text': '调频拨轮', 'confidence': 0.9247385263442993, 'polygon': [[568.0, 336.0], [629.0, 336.0], [629.0, 354.0], [568.0, 354.0]], 'bbox': (568, 336, 62, 19), 'method': 'polygon'}, {'text': '上一曲/暂停/下一曲', 'confidence': 0.9802972078323364, 'polygon': [[611.0, 402.0], [744.0, 401.0], [744.0, 419.0], [611.0, 420.0]], 'bbox': (611, 401, 134, 20), 'method': 'polygon'}, {'text': '大口径扬声器', 'confidence': 0.995476484298706, 'polygon': [[43.0, 427.0], [131.0, 427.0], [131.0, 444.0], [43.0, 444.0]], 'bbox': (43, 427, 89, 18), 'method': 'polygon'}, {'text': 'SOS警报/输出充电', 'confidence': 0.9886554479598999, 'polygon': [[620.0, 428.0], [745.0, 428.0], [745.0, 448.0], [620.0, 448.0]], 'bbox': (620, 428, 126, 21), 'method': 'polygon'}, {'text': '耳机口', 'confidence': 0.9996671080589294, 'polygon': [[645.0, 455.0], [690.0, 455.0], [690.0, 474.0], [645.0, 474.0]], 'bbox': (645, 455, 46, 20), 'method': 'polygon'}, {'text': '充电口', 'confidence': 0.9984938502311707, 'polygon': [[643.0, 476.0], [690.0, 476.0], [690.0, 494.0], [643.0, 494.0]], 'bbox': (643, 476, 48, 19), 'method': 'polygon'}, {'text': 'USB口/U盘插口', 'confidence': 0.9916080832481384, 'polygon': [[642.0, 518.0], [748.0, 518.0], [748.0, 535.0], [642.0, 535.0]], 'bbox': (642, 518, 107, 18), 'method': 'polygon'}, {'text': 'TF插口', 'confidence': 0.9968512654304504, 'polygon': [[641.0, 541.0], [690.0, 541.0], [690.0, 559.0], [641.0, 559.0]], 'bbox': (641, 541, 50, 19), 'method': 'polygon'}, {'text': '蓝牙/收音机/', 'confidence': 0.9914805293083191, 'polygon': [[643.0, 583.0], [730.0, 583.0], [730.0, 601.0], [643.0, 601.0]], 'bbox': (643, 583, 88, 19), 'method': 'polygon'}, {'text': 'TF卡模式选择', 'confidence': 0.9963052868843079, 'polygon': [[642.0, 602.0], [732.0, 602.0], [732.0, 620.0], [642.0, 620.0]], 'bbox': (642, 602, 91, 19), 'method': 'polygon'}, {'text': '太阳能充电面板', 'confidence': 0.9985271692276001, 'polygon': [[641.0, 639.0], [745.0, 639.0], [745.0, 656.0], [641.0, 656.0]], 'bbox': (641, 639, 105, 18), 'method': 'polygon'}, {'text': '手摇发电手柄', 'confidence': 0.9984351992607117, 'polygon': [[642.0, 679.0], [732.0, 679.0], [732.0, 696.0], [642.0, 696.0]], 'bbox': (642, 679, 91, 18), 'method': 'polygon'}] +[2025-10-24 09:41:47,621] [LogListener] [DEBUG] [loggerModule.py:debug:275] 중국어 텍스트 22개 필터링 완료 +[2025-10-24 09:41:47,621] [LogListener] [DEBUG] [loggerModule.py:debug:275] 한글 텍스트 0개 필터링 완료 +[2025-10-24 09:41:49,324] [LogListener] [DEBUG] [loggerModule.py:debug:275] translated_texts: ['만능', '완벽한 선택', '15가지 기능이 하나로', '라디오 안테나', '나침반', '기능 키 영역', '표시 등 표시', '플래시', '채널 표시', '전원 버튼', 'FM 다이얼', '이전 노래 / 정지시키다 / 다음 노래', '대구경 스피커', 'SOS 경보 / 출력 충전', '헤드폰 잭', '충전 포트', 'USB 포트 / U 디스크 소켓', 'TF소켓', '블루투스 / 라디오', 'TF 카드 모드 선택', '태양광 충전 패널', '수동식 발전 핸들'] +[2025-10-24 09:41:49,324] [LogListener] [DEBUG] [loggerModule.py:debug:275] 마스크 생성 완료 +[2025-10-24 09:41:49,324] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 4 OCR 권한 없음, 전체 번역 모드 +[2025-10-24 09:41:49,330] [LogListener] [DEBUG] [loggerModule.py:debug:275] [AUTO Inpaint] coverage=0.232, comps=6, min_center_dist=0.103 → request +[2025-10-24 09:41:49,331] [LogListener] [DEBUG] [loggerModule.py:debug:275] ocr_count: 22 +[2025-10-24 09:41:49,331] [LogListener] [DEBUG] [loggerModule.py:debug:275] is_member_valid: False +[2025-10-24 09:41:49,331] [LogListener] [DEBUG] [loggerModule.py:debug:275] MIGAN 인페인팅 시도 +[2025-10-24 09:41:49,333] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 입력 형태 - 이미지: (1, 3, 800, 800), 마스크: (1, 1, 800, 800) +[2025-10-24 09:41:49,595] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 출력 형태: (800, 800, 3), dtype: uint8 +[2025-10-24 09:41:49,595] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 추론 완료: 263.01 ms +[2025-10-24 09:41:49,602] [LogListener] [DEBUG] [loggerModule.py:debug:275] MIGAN 인페인팅 성공 +[2025-10-24 09:41:49,603] [LogListener] [DEBUG] [loggerModule.py:debug:275] 메모리 변화 [인페인팅]: 40486.7MB -> 40498.2MB (+11.4MB, +0.0%) - 방법: migan +[2025-10-24 09:41:49,603] [LogListener] [DEBUG] [loggerModule.py:debug:275] 인페인팅 완료 +[2025-10-24 09:41:49,667] [LogListener] [DEBUG] [loggerModule.py:debug:275] 텍스트 렌더링 완료 +[2025-10-24 09:41:49,667] [LogListener] [DEBUG] [loggerModule.py:debug:275] watermark_text: 이미지 저작권 보유 +[2025-10-24 09:41:49,668] [LogListener] [DEBUG] [loggerModule.py:debug:275] is_watermark_enabled: True +[2025-10-24 09:41:49,923] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 저장 완료 : C:\ProgramData\ImgWorker\work\translated_thumb_img_4.webp +[2025-10-24 09:41:49,924] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 4 번역 완료: C:\ProgramData\ImgWorker\work\translated_thumb_img_4.webp +[2025-10-24 09:41:49,925] [LogListener] [DEBUG] [loggerModule.py:debug:275] ⏱ 이미지 파이프라인 총 3015.7ms | download=0.0ms | ocr=482.0ms | translate=1706.7ms | mask=1706.7ms | inpaint=277.0ms(migan/DirectML) | render=65.0ms | save=226.0ms +[2025-10-24 09:41:49,926] [LogListener] [DEBUG] [loggerModule.py:debug:275] process_single_image 호출 완료 +[2025-10-24 09:41:49,926] [ResultListener] [WARNING] [loggerModule.py:warning:287] 워커 롤링 스케줄: reason=job-count-threshold +[2025-10-24 09:41:49,926] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 결과 반환 중: uid=c0430c82-d56e-478a-aa32-18c7e741471c +[2025-10-24 09:41:49,926] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 결과 반환 완료: uid=c0430c82-d56e-478a-aa32-18c7e741471c +[2025-10-24 09:41:49,926] [WorkerRoller] [WARNING] [loggerModule.py:warning:287] 워커 롤링 시작: job-count-threshold +[2025-10-24 09:41:49,926] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 09:41:49,926] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 13228) +[2025-10-24 09:41:49,940] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔥 작업 수신 성공 +[2025-10-24 09:41:49,977] [LogListener] [DEBUG] [loggerModule.py:debug:275] Shutdown signal 수신 → 종료 +[2025-10-24 09:41:49,978] [LogListener] [DEBUG] [loggerModule.py:debug:275] OCR 모듈 정리 완료 +[2025-10-24 09:41:49,978] [LogListener] [DEBUG] [loggerModule.py:debug:275] 마스크 모듈 정리 완료 +[2025-10-24 09:41:49,979] [LogListener] [DEBUG] [loggerModule.py:debug:275] 임시 폴더 삭제됨: C:\ProgramData\ImgWorker\work +[2025-10-24 09:41:49,979] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 프로세서 소멸 +[2025-10-24 09:41:50,383] [WorkerRoller] [INFO] [loggerModule.py:info:281] 워커 프로세스 기동: PID=31548 +[2025-10-24 09:41:50,821] [LogListener] [DEBUG] [loggerModule.py:debug:275] ImageWorker 프로세스 기동 (PID=31548, Name=ImageWorkerProcess) +[2025-10-24 09:41:50,822] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔧 ImageProcessor3 초기화 시작... +[2025-10-24 09:41:50,822] [LogListener] [DEBUG] [loggerModule.py:debug:275] tracemalloc 메모리 추적 시작 +[2025-10-24 09:41:50,828] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ DirectML 환경 준비 완료 (Windows DirectX 12 기반) +[2025-10-24 09:41:50,829] [LogListener] [DEBUG] [loggerModule.py:debug:275] === 🚀 DirectML GPU 상태 초기화 시작 🚀 === +[2025-10-24 09:41:50,829] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🎯 사용자 GPU 가속 요청: False +[2025-10-24 09:41:50,829] [LogListener] [DEBUG] [loggerModule.py:debug:275] 💻 현재 운영체제: Windows +[2025-10-24 09:41:50,829] [LogListener] [DEBUG] [loggerModule.py:debug:275] GPU 가속이 비활성화됨 (toggle_states['use_cuda'] = False) +[2025-10-24 09:41:50,829] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔒 안전한 CPU 모드로 모든 GPU 설정 강제 비활성화 +[2025-10-24 09:41:50,829] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔧 ImageProcessor3 GPU 상태 요약: +[2025-10-24 09:41:50,829] [LogListener] [DEBUG] [loggerModule.py:debug:275] - CUDA 사용 가능: False +[2025-10-24 09:41:50,829] [LogListener] [DEBUG] [loggerModule.py:debug:275] - toggle_states['use_cuda']: NOT_SET +[2025-10-24 09:41:50,829] [LogListener] [DEBUG] [loggerModule.py:debug:275] - GPU 하드웨어 정보: {} +[2025-10-24 09:41:50,830] [LogListener] [DEBUG] [loggerModule.py:debug:275] ImageProcessor3 Init toggle_states: {'TEMP_IMAGE_DIR': 'C:\\ProgramData\\ImgWorker\\work', 'output_image_format': 'webp', 'use_local_rembg': True, 'local_rembg_model_path': 'D:\\py\\img_worker\\modules\\rembg_models', 'local_model_name': 'birefnet-general-lite', 'optionIMGTrans_type': 'CPU', 'detail_IMGTrans_type': 'CPU', 'thumb_trans_type': 'CPU', 'migan_use_accel': True, 'migan_onnx_path': 'D:\\py\\img_worker\\modules\\migan_onnx\\migan_pipeline_v2.onnx', 'ocr_provider_override': 'auto', 'migan_provider_override': 'auto'} +[2025-10-24 09:41:50,830] [LogListener] [DEBUG] [loggerModule.py:debug:275] is_member_valid: False +[2025-10-24 09:41:50,830] [LogListener] [DEBUG] [loggerModule.py:debug:275] debug_images 디렉토리 이미 존재: D:\py\img_worker\modules\debug_images +[2025-10-24 09:41:50,830] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.font_path: D:\py\img_worker\modules\fonts\HakgyoansimDunggeunmisoTTFB.ttf +[2025-10-24 09:41:50,830] [LogListener] [DEBUG] [loggerModule.py:debug:275] toggle_states font_path: D:\py\img_worker\modules\fonts\HakgyoansimDunggeunmisoTTFB.ttf +[2025-10-24 09:41:50,830] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.TEMP_IMAGE_DIR: C:\ProgramData\ImgWorker\work +[2025-10-24 09:41:50,830] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.debugging_save_Dir: D:\py\img_worker\modules\debug_images +[2025-10-24 09:41:50,830] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.unwanted_texts: {} +[2025-10-24 09:41:50,830] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.inpaint_method: migan +[2025-10-24 09:41:50,830] [LogListener] [DEBUG] [loggerModule.py:debug:275] Image.MAX_IMAGE_PIXELS set to 20000000 +[2025-10-24 09:41:50,830] [LogListener] [DEBUG] [loggerModule.py:debug:275] ONNX 모델 타입 설정값: 자동 선택, GPU 정보: {} +[2025-10-24 09:41:50,830] [LogListener] [DEBUG] [loggerModule.py:debug:275] 자동 선택 모드: GPU 추천 모델 simp 사용 +[2025-10-24 09:41:50,830] [LogListener] [DEBUG] [loggerModule.py:debug:275] ONNX OCR 모델 타입 결정: simp (GPU: False) +[2025-10-24 09:41:50,830] [LogListener] [DEBUG] [loggerModule.py:debug:275] ONNX OCR 모듈 CPU 모드로 설정 +[2025-10-24 09:41:50,830] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🚀 ONNX TextSystem 초기화 시작 (CPU 모드) +[2025-10-24 09:41:50,830] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🚀 ONNX TextSystem 초기화 시작 (CPU 모드) +[2025-10-24 09:41:52,408] [LogListener] [DEBUG] [loggerModule.py:debug:275] 문자 사전 파일 UTF-8 확인 완료: D:\py\img_worker\modules\onnx_ocr_module\dict\ppocr_keys_v1.txt +[2025-10-24 09:41:52,492] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ ONNX TextSystem 초기화 완료 (CPU + SIMP (호환성) 모델) +[2025-10-24 09:41:52,492] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ ONNX TextSystem 초기화 완료 (CPU + SIMP 모델) +[2025-10-24 09:41:52,492] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ ONNX OCR 모듈 초기화 성공 (CPU 모드) +[2025-10-24 09:41:52,493] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ ONNX OCR 모듈 초기화 성공 +[2025-10-24 09:41:52,493] [LogListener] [DEBUG] [loggerModule.py:debug:275] 마스크 모듈 초기화 완료 +[2025-10-24 09:41:52,493] [LogListener] [DEBUG] [loggerModule.py:debug:275] MaskModule 초기화 성공 +[2025-10-24 09:41:52,493] [LogListener] [DEBUG] [loggerModule.py:debug:275] 텍스트 렌더링 모듈 초기화 완료 +[2025-10-24 09:41:52,493] [LogListener] [DEBUG] [loggerModule.py:debug:275] 기본 폰트: D:\py\img_worker\modules\fonts\HakgyoansimDunggeunmisoTTFB.ttf +[2025-10-24 09:41:52,493] [LogListener] [DEBUG] [loggerModule.py:debug:275] TextRenderingModule 초기화 성공 +[2025-10-24 09:41:52,493] [LogListener] [DEBUG] [loggerModule.py:debug:275] 폰트 로드 성공: D:\py\img_worker\modules\fonts\HakgyoansimDunggeunmisoTTFB.ttf +[2025-10-24 09:41:52,493] [LogListener] [DEBUG] [loggerModule.py:debug:275] PostImageManager 초기화 성공 +[2025-10-24 09:41:52,493] [LogListener] [DEBUG] [loggerModule.py:debug:275] Request_AI_Server GPU 상태: CUDA 사용 가능=False +[2025-10-24 09:41:52,494] [LogListener] [DEBUG] [loggerModule.py:debug:275] Request_AI_Server 초기화 성공 +[2025-10-24 09:41:52,494] [LogListener] [DEBUG] [loggerModule.py:debug:275] GoogleTranslate 초기화 성공 +[2025-10-24 09:41:52,494] [LogListener] [DEBUG] [loggerModule.py:debug:275] MIGAN CUDA 사용 불가 - CPU 모드로 설정 +[2025-10-24 09:41:52,494] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] GPU 관리자 전달: GPUManager, can_use_cuda: False +[2025-10-24 09:41:52,512] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] GPU 관리자 사용 가능: False +[2025-10-24 09:41:52,710] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 사용 가능한 providers: ['DmlExecutionProvider', 'CPUExecutionProvider'] +[2025-10-24 09:41:52,711] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] DirectML 가속 활성화 +[2025-10-24 09:41:52,711] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 최종 providers: [('DmlExecutionProvider', {}), ('CPUExecutionProvider', {})] +[2025-10-24 09:41:52,711] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] DirectML 가속 시도: [('DmlExecutionProvider', {}), ('CPUExecutionProvider', {})] +[2025-10-24 09:41:52,711] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] DirectML 가속 성공! 실제 providers: ['DmlExecutionProvider', 'CPUExecutionProvider'] +[2025-10-24 09:41:52,711] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 입력 0: image, 형태: ['batch_size', 3, 'height', 'width'], 타입: tensor(uint8) +[2025-10-24 09:41:52,711] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 입력 1: mask, 형태: ['batch_size', 1, 'height', 'width'], 타입: tensor(uint8) +[2025-10-24 09:41:52,711] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 출력 0: result, 형태: ['ScatterNDresult_dim_0', 3, 'ScatterNDresult_dim_2', 'ScatterNDresult_dim_3'], 타입: tensor(uint8) +[2025-10-24 09:41:52,711] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 세션 준비 완료. providers=['DmlExecutionProvider', 'CPUExecutionProvider'] +[2025-10-24 09:41:52,711] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] GPU 관리자 연결 완료: GPUManager +[2025-10-24 09:41:52,711] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 파이프라인 gpu_manager 속성: True, 값: +[2025-10-24 09:41:52,711] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 초기화 완료: gpu_manager 속성=True, 값= +[2025-10-24 09:41:52,711] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔰 OCR 모듈 Warm-up 시작... +[2025-10-24 09:41:52,712] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔍 ONNX OCR 감지 방식: polygon +[2025-10-24 09:41:52,717] [LogListener] [DEBUG] [loggerModule.py:debug:275] ⚡ ONNX OCR 추론 완료: 3.6ms +[2025-10-24 09:41:52,717] [LogListener] [DEBUG] [loggerModule.py:debug:275] 📊 세부 시간 - 감지: 3.0ms, 인식: 0.0ms, 분류: 0.0ms +[2025-10-24 09:41:52,717] [LogListener] [WARNING] [loggerModule.py:warning:287] ⚠️ ONNX OCR 결과가 비어있습니다 +[2025-10-24 09:41:52,720] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ OCR 모듈 Warm-up 성공 +[2025-10-24 09:41:52,720] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔰 ImageProcessor Warm‑up 완료 +[2025-10-24 09:41:52,720] [ResultListener] [INFO] [loggerModule.py:info:281] 워커 READY 수신 +[2025-10-24 09:41:52,720] [WorkerRoller] [INFO] [loggerModule.py:info:281] 버퍼 작업 재개: 1건 +[2025-10-24 09:41:52,721] [ResultListener] [INFO] [loggerModule.py:info:281] 워커 READY 수신 +[2025-10-24 09:41:52,721] [LogListener] [DEBUG] [loggerModule.py:debug:275] 📡 추가 READY 신호 전송 완료 +[2025-10-24 09:41:52,721] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 09:41:52,721] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 31548) +[2025-10-24 09:41:52,721] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔥 작업 수신 성공 +[2025-10-24 09:41:52,721] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🚀 작업 처리 시작: cmd=process_single_image, uid=fbf664de-b169-4f6b-a997-efef4c2cf420 +[2025-10-24 09:41:52,721] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 프로세서 toggle_states 업데이트 : {'TEMP_IMAGE_DIR': 'C:\\ProgramData\\ImgWorker\\work', 'output_image_format': 'webp', 'use_local_rembg': True, 'local_rembg_model_path': 'D:\\py\\img_worker\\modules\\rembg_models', 'local_model_name': 'birefnet-general-lite', 'optionIMGTrans_type': 'GPU', 'detail_IMGTrans_type': 'GPU', 'thumb_trans_type': 'GPU', 'migan_use_accel': True, 'migan_onnx_path': 'D:\\py\\img_worker\\modules\\migan_onnx\\migan_pipeline_v2.onnx', 'ocr_provider_override': 'auto', 'migan_provider_override': 'auto', 'font_type': '폰트5', 'unwanted_texts': ['가격설명', '무료배송', '보증', '사은품', '세일', '이벤트', '특가', '품절', '할인', '할인가'], 'is_member_valid': False, 'authenticated_by_admin': True} +[2025-10-24 09:41:52,722] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 실행 직전: cmd=process_single_image +[2025-10-24 09:41:52,722] [LogListener] [DEBUG] [loggerModule.py:debug:275] process_single_image 호출 직전 +[2025-10-24 09:41:52,724] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 번역시작 +[2025-10-24 09:41:52,994] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 5 처리 시작: C:\ProgramData\ImgWorker\incoming\dl_4a8f3c434fc3.jpg - 전체 번역 모드 +[2025-10-24 09:41:52,999] [LogListener] [DEBUG] [loggerModule.py:debug:275] 로컬 파일 경로 감지, 다운로드 생략: C:\ProgramData\ImgWorker\incoming\dl_4a8f3c434fc3.jpg +[2025-10-24 09:41:52,999] [LogListener] [DEBUG] [loggerModule.py:debug:275] 옵션 이미지는 스케일 처리 건너뛰기: thumb +[2025-10-24 09:41:52,999] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 5 로컬 저장위치(썸네일 스케일 처리 완료): C:\ProgramData\ImgWorker\incoming\dl_4a8f3c434fc3.jpg +[2025-10-24 09:41:53,001] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔍 ONNX OCR 감지 방식: polygon +[2025-10-24 09:41:53,832] [LogListener] [DEBUG] [loggerModule.py:debug:275] ⚡ ONNX OCR 추론 완료: 829.0ms +[2025-10-24 09:41:53,832] [LogListener] [DEBUG] [loggerModule.py:debug:275] 📊 세부 시간 - 감지: 98.0ms, 인식: 716.0ms, 분류: 10.0ms +[2025-10-24 09:41:53,850] [LogListener] [DEBUG] [loggerModule.py:debug:275] 메모리 변화 [OCR 처리]: 40138.7MB -> 40483.5MB (+344.8MB, +0.9%) - 이미지 5 +[2025-10-24 09:41:53,866] [LogListener] [DEBUG] [loggerModule.py:debug:275] ocr_results: [{'text': '暴走兔官方说明', 'confidence': 0.9319658875465393, 'polygon': [[190.0, 55.0], [599.0, 55.0], [599.0, 109.0], [190.0, 109.0]], 'bbox': (190, 55, 410, 55), 'method': 'polygon'}, {'text': '近期,有大量商家大量抄袭本店产品', 'confidence': 0.9800819158554077, 'polygon': [[155.0, 183.0], [629.0, 184.0], [629.0, 211.0], [155.0, 210.0]], 'bbox': (155, 183, 475, 29), 'method': 'polygon'}, {'text': '假装售卖同款产品夸大宣传,实际产品为', 'confidence': 0.9688926339149475, 'polygon': [[142.0, 223.0], [667.0, 223.0], [667.0, 250.0], [142.0, 250.0]], 'bbox': (142, 223, 526, 28), 'method': 'polygon'}, {'text': '工厂多次翻新机器,产品外壳多有瑕疵质量存在隐患', 'confidence': 0.9615533351898193, 'polygon': [[125.0, 306.0], [683.0, 306.0], [683.0, 329.0], [125.0, 329.0]], 'bbox': (125, 306, 559, 24), 'method': 'polygon'}, {'text': '小厂仿造品,技术不成熟,做工粗糙,影响后续正常使用', 'confidence': 0.9918012022972107, 'polygon': [[120.0, 388.0], [727.0, 388.0], [727.0, 412.0], [120.0, 412.0]], 'bbox': (120, 388, 608, 25), 'method': 'polygon'}, {'text': '为保障本店正品', 'confidence': 0.9932817816734314, 'polygon': [[120.0, 536.0], [309.0, 536.0], [309.0, 563.0], [120.0, 563.0]], 'bbox': (120, 536, 190, 28), 'method': 'polygon'}, {'text': '955', 'confidence': 0.9524380564689636, 'polygon': [[573.0, 544.0], [604.0, 544.0], [604.0, 563.0], [573.0, 563.0]], 'bbox': (573, 544, 32, 20), 'method': 'polygon'}, {'text': '实际消费者的权益', 'confidence': 0.9928746819496155, 'polygon': [[91.0, 581.0], [318.0, 581.0], [318.0, 608.0], [91.0, 608.0]], 'bbox': (91, 581, 228, 28), 'method': 'polygon'}, {'text': '暴走免旗舰店在此郑重声明', 'confidence': 0.9279990196228027, 'polygon': [[53.0, 628.0], [377.0, 628.0], [377.0, 651.0], [53.0, 651.0]], 'bbox': (53, 628, 325, 24), 'method': 'polygon'}, {'text': '所有类型的售后仅对本店', 'confidence': 0.9958686232566833, 'polygon': [[66.0, 672.0], [362.0, 672.0], [362.0, 696.0], [66.0, 696.0]], 'bbox': (66, 672, 297, 25), 'method': 'polygon'}, {'text': '暴走兔已升级到第二代数显款!', 'confidence': 0.9508973956108093, 'polygon': [[437.0, 685.0], [748.0, 686.0], [748.0, 710.0], [437.0, 709.0]], 'bbox': (437, 685, 312, 26), 'method': 'polygon'}, {'text': '有订单客户提供!', 'confidence': 0.9849206209182739, 'polygon': [[105.0, 716.0], [310.0, 716.0], [310.0, 743.0], [105.0, 743.0]], 'bbox': (105, 716, 206, 28), 'method': 'polygon'}, {'text': '认准官方店铺享受正规服务保障!', 'confidence': 0.9901202321052551, 'polygon': [[425.0, 722.0], [760.0, 722.0], [760.0, 745.0], [425.0, 745.0]], 'bbox': (425, 722, 336, 24), 'method': 'polygon'}] +[2025-10-24 09:41:53,875] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 93.2%): '暴走兔官方说明' +[2025-10-24 09:41:53,875] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 98.0%): '近期,有大量商家大量抄袭本店产品' +[2025-10-24 09:41:53,875] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 96.9%): '假装售卖同款产品夸大宣传,实际产品为' +[2025-10-24 09:41:53,875] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 96.2%): '工厂多次翻新机器,产品外壳多有瑕疵质量存在隐患' +[2025-10-24 09:41:53,875] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.2%): '小厂仿造品,技术不成熟,做工粗糙,影响后续正常使用' +[2025-10-24 09:41:53,875] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.3%): '为保障本店正品' +[2025-10-24 09:41:53,875] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): '955' +[2025-10-24 09:41:53,876] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.3%): '实际消费者的权益' +[2025-10-24 09:41:53,876] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 92.8%): '暴走免旗舰店在此郑重声明' +[2025-10-24 09:41:53,876] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.6%): '所有类型的售后仅对本店' +[2025-10-24 09:41:53,876] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 95.1%): '暴走兔已升级到第二代数显款!' +[2025-10-24 09:41:53,876] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 98.5%): '有订单客户提供!' +[2025-10-24 09:41:53,876] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.0%): '认准官方店铺享受正规服务保障!' +[2025-10-24 09:41:53,876] [LogListener] [DEBUG] [loggerModule.py:debug:275] 필터링 결과: 12/13개 (신뢰도 + & 중국어) +[2025-10-24 09:41:53,876] [LogListener] [DEBUG] [loggerModule.py:debug:275] filter_ocr_results: [{'text': '暴走兔官方说明', 'confidence': 0.9319658875465393, 'polygon': [[190.0, 55.0], [599.0, 55.0], [599.0, 109.0], [190.0, 109.0]], 'bbox': (190, 55, 410, 55), 'method': 'polygon'}, {'text': '近期,有大量商家大量抄袭本店产品', 'confidence': 0.9800819158554077, 'polygon': [[155.0, 183.0], [629.0, 184.0], [629.0, 211.0], [155.0, 210.0]], 'bbox': (155, 183, 475, 29), 'method': 'polygon'}, {'text': '假装售卖同款产品夸大宣传,实际产品为', 'confidence': 0.9688926339149475, 'polygon': [[142.0, 223.0], [667.0, 223.0], [667.0, 250.0], [142.0, 250.0]], 'bbox': (142, 223, 526, 28), 'method': 'polygon'}, {'text': '工厂多次翻新机器,产品外壳多有瑕疵质量存在隐患', 'confidence': 0.9615533351898193, 'polygon': [[125.0, 306.0], [683.0, 306.0], [683.0, 329.0], [125.0, 329.0]], 'bbox': (125, 306, 559, 24), 'method': 'polygon'}, {'text': '小厂仿造品,技术不成熟,做工粗糙,影响后续正常使用', 'confidence': 0.9918012022972107, 'polygon': [[120.0, 388.0], [727.0, 388.0], [727.0, 412.0], [120.0, 412.0]], 'bbox': (120, 388, 608, 25), 'method': 'polygon'}, {'text': '为保障本店正品', 'confidence': 0.9932817816734314, 'polygon': [[120.0, 536.0], [309.0, 536.0], [309.0, 563.0], [120.0, 563.0]], 'bbox': (120, 536, 190, 28), 'method': 'polygon'}, {'text': '实际消费者的权益', 'confidence': 0.9928746819496155, 'polygon': [[91.0, 581.0], [318.0, 581.0], [318.0, 608.0], [91.0, 608.0]], 'bbox': (91, 581, 228, 28), 'method': 'polygon'}, {'text': '暴走免旗舰店在此郑重声明', 'confidence': 0.9279990196228027, 'polygon': [[53.0, 628.0], [377.0, 628.0], [377.0, 651.0], [53.0, 651.0]], 'bbox': (53, 628, 325, 24), 'method': 'polygon'}, {'text': '所有类型的售后仅对本店', 'confidence': 0.9958686232566833, 'polygon': [[66.0, 672.0], [362.0, 672.0], [362.0, 696.0], [66.0, 696.0]], 'bbox': (66, 672, 297, 25), 'method': 'polygon'}, {'text': '暴走兔已升级到第二代数显款!', 'confidence': 0.9508973956108093, 'polygon': [[437.0, 685.0], [748.0, 686.0], [748.0, 710.0], [437.0, 709.0]], 'bbox': (437, 685, 312, 26), 'method': 'polygon'}, {'text': '有订单客户提供!', 'confidence': 0.9849206209182739, 'polygon': [[105.0, 716.0], [310.0, 716.0], [310.0, 743.0], [105.0, 743.0]], 'bbox': (105, 716, 206, 28), 'method': 'polygon'}, {'text': '认准官方店铺享受正规服务保障!', 'confidence': 0.9901202321052551, 'polygon': [[425.0, 722.0], [760.0, 722.0], [760.0, 745.0], [425.0, 745.0]], 'bbox': (425, 722, 336, 24), 'method': 'polygon'}] +[2025-10-24 09:41:53,876] [LogListener] [DEBUG] [loggerModule.py:debug:275] 중국어 텍스트 12개 필터링 완료 +[2025-10-24 09:41:53,876] [LogListener] [DEBUG] [loggerModule.py:debug:275] 한글 텍스트 0개 필터링 완료 +[2025-10-24 09:41:56,919] [LogListener] [DEBUG] [loggerModule.py:debug:275] translated_texts: ['Runaway Rabbit에 대한 공식 설명', '최근에는 많은 판매자들이 우리 제품을 대량으로 복제하고 있습니다.', '동일한 상품을 판매하는 것처럼 가장하여 과장홍보하였으나, 실제 상품은', '공장에서는 기계를 여러 번 개조했는데, 제품 케이스에 결함이 있어 품질 위험이 있는 경우가 많습니다.', '미성숙한 기술과 거친 제작 기술로 소규모 공장에서 생산된 위조 제품은 이후의 정상적인 사용에 영향을 미칩니다.', '우리 매장의 진품 여부를 확인하기 위해', '실제 소비자 권리', 'Baozouwu 플래그십 스토어는 이로써 엄숙하게 선언합니다.', '모든 유형의 애프터 서비스는 우리 매장에만 해당됩니다.', 'Rampage Rabbit이 2세대 디지털 디스플레이 모델로 업그레이드 되었습니다!', '고객이 주문한 상품입니다!', '정규 서비스 보장을 받으시려면 공식 매장을 찾아보세요!'] +[2025-10-24 09:41:56,919] [LogListener] [DEBUG] [loggerModule.py:debug:275] 마스크 생성 완료 +[2025-10-24 09:41:56,920] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 5 OCR 권한 없음, 전체 번역 모드 +[2025-10-24 09:41:56,926] [LogListener] [DEBUG] [loggerModule.py:debug:275] [AUTO Inpaint] coverage=0.366, comps=6, min_center_dist=0.075 → request +[2025-10-24 09:41:56,926] [LogListener] [DEBUG] [loggerModule.py:debug:275] ocr_count: 12 +[2025-10-24 09:41:56,926] [LogListener] [DEBUG] [loggerModule.py:debug:275] is_member_valid: False +[2025-10-24 09:41:56,926] [LogListener] [DEBUG] [loggerModule.py:debug:275] MIGAN 인페인팅 시도 +[2025-10-24 09:41:56,928] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 입력 형태 - 이미지: (1, 3, 800, 800), 마스크: (1, 1, 800, 800) +[2025-10-24 09:41:57,402] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 출력 형태: (800, 800, 3), dtype: uint8 +[2025-10-24 09:41:57,403] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 추론 완료: 474.06 ms +[2025-10-24 09:41:57,412] [LogListener] [DEBUG] [loggerModule.py:debug:275] MIGAN 인페인팅 성공 +[2025-10-24 09:41:57,412] [LogListener] [DEBUG] [loggerModule.py:debug:275] 메모리 변화 [인페인팅]: 40527.0MB -> 40728.0MB (+201.0MB, +0.5%) - 방법: migan +[2025-10-24 09:41:57,412] [LogListener] [DEBUG] [loggerModule.py:debug:275] 인페인팅 완료 +[2025-10-24 09:41:57,504] [LogListener] [DEBUG] [loggerModule.py:debug:275] 텍스트 렌더링 완료 +[2025-10-24 09:41:57,505] [LogListener] [DEBUG] [loggerModule.py:debug:275] watermark_text: 이미지 저작권 보유 +[2025-10-24 09:41:57,505] [LogListener] [DEBUG] [loggerModule.py:debug:275] is_watermark_enabled: True +[2025-10-24 09:41:57,766] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 저장 완료 : C:\ProgramData\ImgWorker\work\translated_thumb_img_5.webp +[2025-10-24 09:41:57,767] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 5 번역 완료: C:\ProgramData\ImgWorker\work\translated_thumb_img_5.webp +[2025-10-24 09:41:57,767] [LogListener] [DEBUG] [loggerModule.py:debug:275] ⏱ 이미지 파이프라인 총 5044.0ms | download=0.0ms | ocr=838.0ms | translate=3043.5ms | mask=3043.5ms | inpaint=491.1ms(migan/DirectML) | render=93.5ms | save=209.2ms +[2025-10-24 09:41:57,769] [LogListener] [DEBUG] [loggerModule.py:debug:275] process_single_image 호출 완료 +[2025-10-24 09:41:57,769] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 결과 반환 중: uid=fbf664de-b169-4f6b-a997-efef4c2cf420 +[2025-10-24 09:41:57,769] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 결과 반환 완료: uid=fbf664de-b169-4f6b-a997-efef4c2cf420 +[2025-10-24 09:41:57,769] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 09:41:57,769] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 31548) +[2025-10-24 09:52:08,810] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 09:52:08,810] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 09:52:08,811] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 31548) +[2025-10-24 09:53:08,816] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 09:53:08,816] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 09:53:08,816] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 31548) +[2025-10-24 09:54:08,821] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 09:54:08,821] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 09:54:08,821] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 31548) +[2025-10-24 09:55:08,824] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 09:55:08,824] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 09:55:08,824] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 31548) +[2025-10-24 09:56:08,834] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 09:56:08,834] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 09:56:08,834] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 31548) +[2025-10-24 09:57:08,844] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 09:57:08,844] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 09:57:08,844] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 31548) +[2025-10-24 09:58:08,859] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 09:58:08,859] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 09:58:08,859] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 31548) +[2025-10-24 09:59:08,866] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 09:59:08,866] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 09:59:08,866] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 31548) +[2025-10-24 10:00:08,873] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 10:00:08,873] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 10:00:08,873] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 31548) +[2025-10-24 10:01:08,880] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 10:01:08,880] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 10:01:08,880] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 31548) +[2025-10-24 10:02:08,893] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 10:02:08,894] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 10:02:08,894] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 31548) +[2025-10-24 10:03:08,897] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 10:03:08,897] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 10:03:08,897] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 31548) +[2025-10-24 10:04:08,905] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 10:04:08,905] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 10:04:08,905] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 31548) +[2025-10-24 10:05:08,908] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 10:05:08,909] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 10:05:08,909] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 31548) +[2025-10-24 10:06:08,918] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 10:06:08,918] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 10:06:08,918] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 31548) +[2025-10-24 10:07:08,930] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 10:07:08,930] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 10:07:08,930] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 31548) +[2025-10-24 10:08:08,934] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 10:08:08,934] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 10:08:08,935] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 31548) +[2025-10-24 10:09:08,937] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 10:09:08,937] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 10:09:08,937] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 31548) +[2025-10-24 10:10:08,941] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 10:10:08,941] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 10:10:08,941] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 31548) +[2025-10-24 10:11:08,956] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 10:11:08,956] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 10:11:08,956] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 31548) +[2025-10-24 10:12:08,968] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 10:12:08,968] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 10:12:08,968] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 31548) +[2025-10-24 10:13:08,980] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 10:13:08,980] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 10:13:08,980] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 31548) +[2025-10-24 10:14:08,985] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 10:14:08,985] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 10:14:08,985] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 31548) +[2025-10-24 10:15:08,991] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 10:15:08,991] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 10:15:08,991] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 31548) +[2025-10-24 10:16:08,998] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 10:16:08,998] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 10:16:08,998] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 31548) +[2025-10-24 10:17:09,000] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 10:17:09,000] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 10:17:09,000] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 31548) +[2025-10-24 10:18:09,001] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 10:18:09,001] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 10:18:09,001] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 31548) +[2025-10-24 10:19:09,012] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 10:19:09,012] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 10:19:09,012] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 31548) +[2025-10-24 10:20:09,020] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 10:20:09,020] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 10:20:09,020] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 31548) +[2025-10-24 10:21:09,032] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 10:21:09,032] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 10:21:09,032] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 31548) +[2025-10-24 11:25:01,368] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 11:25:01,368] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 11:25:01,368] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 31548) +[2025-10-24 11:26:01,377] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 11:26:01,377] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 11:26:01,377] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 31548) +[2025-10-24 11:27:01,377] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 11:27:01,377] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 11:27:01,377] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 31548) +[2025-10-24 11:28:01,390] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 11:28:01,390] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 11:28:01,390] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 31548) +[2025-10-24 11:29:01,395] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 11:29:01,395] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 11:29:01,395] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 31548) +[2025-10-24 11:30:01,401] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 11:30:01,401] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 11:30:01,401] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 31548) +[2025-10-24 11:31:01,405] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 11:31:01,405] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 11:31:01,405] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 31548) +[2025-10-24 11:32:01,406] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 11:32:01,406] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 11:32:01,406] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 31548) +[2025-10-24 11:33:01,413] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 11:33:01,413] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 11:33:01,413] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 31548) +[2025-10-24 11:34:01,426] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 11:34:01,426] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 11:34:01,426] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 31548) +[2025-10-24 11:35:01,433] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 11:35:01,433] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 11:35:01,433] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 31548) +[2025-10-24 11:36:01,447] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 11:36:01,447] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 11:36:01,447] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 31548) +[2025-10-24 11:37:01,452] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 11:37:01,452] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 11:37:01,452] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 31548) +[2025-10-24 11:38:01,456] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 11:38:01,456] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 11:38:01,456] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 31548) +[2025-10-24 11:39:01,470] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 11:39:01,470] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 11:39:01,470] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 31548) +[2025-10-24 11:40:01,482] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 11:40:01,482] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 11:40:01,482] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 31548) +[2025-10-24 11:41:01,486] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 11:41:01,486] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 11:41:01,486] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 31548) +[2025-10-24 11:41:54,336] [MemMonitor] [WARNING] [loggerModule.py:warning:287] 워커 롤링 스케줄: reason=uptime-threshold +[2025-10-24 11:41:54,336] [WorkerRoller] [WARNING] [loggerModule.py:warning:287] 워커 롤링 시작: uptime-threshold +[2025-10-24 11:41:54,354] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔥 작업 수신 성공 +[2025-10-24 11:41:54,389] [LogListener] [DEBUG] [loggerModule.py:debug:275] Shutdown signal 수신 → 종료 +[2025-10-24 11:41:54,389] [LogListener] [DEBUG] [loggerModule.py:debug:275] OCR 모듈 정리 완료 +[2025-10-24 11:41:54,389] [LogListener] [DEBUG] [loggerModule.py:debug:275] 마스크 모듈 정리 완료 +[2025-10-24 11:41:54,390] [LogListener] [DEBUG] [loggerModule.py:debug:275] 임시 폴더 삭제됨: C:\ProgramData\ImgWorker\work +[2025-10-24 11:41:54,390] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 프로세서 소멸 +[2025-10-24 11:41:54,786] [WorkerRoller] [INFO] [loggerModule.py:info:281] 워커 프로세스 기동: PID=10204 +[2025-10-24 11:41:55,370] [LogListener] [DEBUG] [loggerModule.py:debug:275] ImageWorker 프로세스 기동 (PID=10204, Name=ImageWorkerProcess) +[2025-10-24 11:41:55,370] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔧 ImageProcessor3 초기화 시작... +[2025-10-24 11:41:55,370] [LogListener] [DEBUG] [loggerModule.py:debug:275] tracemalloc 메모리 추적 시작 +[2025-10-24 11:41:55,377] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ DirectML 환경 준비 완료 (Windows DirectX 12 기반) +[2025-10-24 11:41:55,378] [LogListener] [DEBUG] [loggerModule.py:debug:275] === 🚀 DirectML GPU 상태 초기화 시작 🚀 === +[2025-10-24 11:41:55,378] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🎯 사용자 GPU 가속 요청: False +[2025-10-24 11:41:55,378] [LogListener] [DEBUG] [loggerModule.py:debug:275] 💻 현재 운영체제: Windows +[2025-10-24 11:41:55,378] [LogListener] [DEBUG] [loggerModule.py:debug:275] GPU 가속이 비활성화됨 (toggle_states['use_cuda'] = False) +[2025-10-24 11:41:55,378] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔒 안전한 CPU 모드로 모든 GPU 설정 강제 비활성화 +[2025-10-24 11:41:55,378] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔧 ImageProcessor3 GPU 상태 요약: +[2025-10-24 11:41:55,378] [LogListener] [DEBUG] [loggerModule.py:debug:275] - CUDA 사용 가능: False +[2025-10-24 11:41:55,378] [LogListener] [DEBUG] [loggerModule.py:debug:275] - toggle_states['use_cuda']: NOT_SET +[2025-10-24 11:41:55,378] [LogListener] [DEBUG] [loggerModule.py:debug:275] - GPU 하드웨어 정보: {} +[2025-10-24 11:41:55,379] [LogListener] [DEBUG] [loggerModule.py:debug:275] ImageProcessor3 Init toggle_states: {'TEMP_IMAGE_DIR': 'C:\\ProgramData\\ImgWorker\\work', 'output_image_format': 'webp', 'use_local_rembg': True, 'local_rembg_model_path': 'D:\\py\\img_worker\\modules\\rembg_models', 'local_model_name': 'birefnet-general-lite', 'optionIMGTrans_type': 'CPU', 'detail_IMGTrans_type': 'CPU', 'thumb_trans_type': 'CPU', 'migan_use_accel': True, 'migan_onnx_path': 'D:\\py\\img_worker\\modules\\migan_onnx\\migan_pipeline_v2.onnx', 'ocr_provider_override': 'auto', 'migan_provider_override': 'auto'} +[2025-10-24 11:41:55,379] [LogListener] [DEBUG] [loggerModule.py:debug:275] is_member_valid: False +[2025-10-24 11:41:55,379] [LogListener] [DEBUG] [loggerModule.py:debug:275] debug_images 디렉토리 이미 존재: D:\py\img_worker\modules\debug_images +[2025-10-24 11:41:55,379] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.font_path: D:\py\img_worker\modules\fonts\HakgyoansimDunggeunmisoTTFB.ttf +[2025-10-24 11:41:55,379] [LogListener] [DEBUG] [loggerModule.py:debug:275] toggle_states font_path: D:\py\img_worker\modules\fonts\HakgyoansimDunggeunmisoTTFB.ttf +[2025-10-24 11:41:55,379] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.TEMP_IMAGE_DIR: C:\ProgramData\ImgWorker\work +[2025-10-24 11:41:55,379] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.debugging_save_Dir: D:\py\img_worker\modules\debug_images +[2025-10-24 11:41:55,379] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.unwanted_texts: {} +[2025-10-24 11:41:55,379] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.inpaint_method: migan +[2025-10-24 11:41:55,379] [LogListener] [DEBUG] [loggerModule.py:debug:275] Image.MAX_IMAGE_PIXELS set to 20000000 +[2025-10-24 11:41:55,379] [LogListener] [DEBUG] [loggerModule.py:debug:275] ONNX 모델 타입 설정값: 자동 선택, GPU 정보: {} +[2025-10-24 11:41:55,379] [LogListener] [DEBUG] [loggerModule.py:debug:275] 자동 선택 모드: GPU 추천 모델 simp 사용 +[2025-10-24 11:41:55,379] [LogListener] [DEBUG] [loggerModule.py:debug:275] ONNX OCR 모델 타입 결정: simp (GPU: False) +[2025-10-24 11:41:55,379] [LogListener] [DEBUG] [loggerModule.py:debug:275] ONNX OCR 모듈 CPU 모드로 설정 +[2025-10-24 11:41:55,380] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🚀 ONNX TextSystem 초기화 시작 (CPU 모드) +[2025-10-24 11:41:55,380] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🚀 ONNX TextSystem 초기화 시작 (CPU 모드) +[2025-10-24 11:41:57,049] [LogListener] [DEBUG] [loggerModule.py:debug:275] 문자 사전 파일 UTF-8 확인 완료: D:\py\img_worker\modules\onnx_ocr_module\dict\ppocr_keys_v1.txt +[2025-10-24 11:41:57,136] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ ONNX TextSystem 초기화 완료 (CPU + SIMP (호환성) 모델) +[2025-10-24 11:41:57,136] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ ONNX TextSystem 초기화 완료 (CPU + SIMP 모델) +[2025-10-24 11:41:57,136] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ ONNX OCR 모듈 초기화 성공 (CPU 모드) +[2025-10-24 11:41:57,137] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ ONNX OCR 모듈 초기화 성공 +[2025-10-24 11:41:57,137] [LogListener] [DEBUG] [loggerModule.py:debug:275] 마스크 모듈 초기화 완료 +[2025-10-24 11:41:57,137] [LogListener] [DEBUG] [loggerModule.py:debug:275] MaskModule 초기화 성공 +[2025-10-24 11:41:57,137] [LogListener] [DEBUG] [loggerModule.py:debug:275] 텍스트 렌더링 모듈 초기화 완료 +[2025-10-24 11:41:57,137] [LogListener] [DEBUG] [loggerModule.py:debug:275] 기본 폰트: D:\py\img_worker\modules\fonts\HakgyoansimDunggeunmisoTTFB.ttf +[2025-10-24 11:41:57,137] [LogListener] [DEBUG] [loggerModule.py:debug:275] TextRenderingModule 초기화 성공 +[2025-10-24 11:41:57,137] [LogListener] [DEBUG] [loggerModule.py:debug:275] 폰트 로드 성공: D:\py\img_worker\modules\fonts\HakgyoansimDunggeunmisoTTFB.ttf +[2025-10-24 11:41:57,137] [LogListener] [DEBUG] [loggerModule.py:debug:275] PostImageManager 초기화 성공 +[2025-10-24 11:41:57,137] [LogListener] [DEBUG] [loggerModule.py:debug:275] Request_AI_Server GPU 상태: CUDA 사용 가능=False +[2025-10-24 11:41:57,137] [LogListener] [DEBUG] [loggerModule.py:debug:275] Request_AI_Server 초기화 성공 +[2025-10-24 11:41:57,137] [LogListener] [DEBUG] [loggerModule.py:debug:275] GoogleTranslate 초기화 성공 +[2025-10-24 11:41:57,138] [LogListener] [DEBUG] [loggerModule.py:debug:275] MIGAN CUDA 사용 불가 - CPU 모드로 설정 +[2025-10-24 11:41:57,138] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] GPU 관리자 전달: GPUManager, can_use_cuda: False +[2025-10-24 11:41:57,159] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] GPU 관리자 사용 가능: False +[2025-10-24 11:41:57,367] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 사용 가능한 providers: ['DmlExecutionProvider', 'CPUExecutionProvider'] +[2025-10-24 11:41:57,367] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] DirectML 가속 활성화 +[2025-10-24 11:41:57,367] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 최종 providers: [('DmlExecutionProvider', {}), ('CPUExecutionProvider', {})] +[2025-10-24 11:41:57,367] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] DirectML 가속 시도: [('DmlExecutionProvider', {}), ('CPUExecutionProvider', {})] +[2025-10-24 11:41:57,368] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] DirectML 가속 성공! 실제 providers: ['DmlExecutionProvider', 'CPUExecutionProvider'] +[2025-10-24 11:41:57,368] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 입력 0: image, 형태: ['batch_size', 3, 'height', 'width'], 타입: tensor(uint8) +[2025-10-24 11:41:57,368] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 입력 1: mask, 형태: ['batch_size', 1, 'height', 'width'], 타입: tensor(uint8) +[2025-10-24 11:41:57,368] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 출력 0: result, 형태: ['ScatterNDresult_dim_0', 3, 'ScatterNDresult_dim_2', 'ScatterNDresult_dim_3'], 타입: tensor(uint8) +[2025-10-24 11:41:57,368] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 세션 준비 완료. providers=['DmlExecutionProvider', 'CPUExecutionProvider'] +[2025-10-24 11:41:57,368] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] GPU 관리자 연결 완료: GPUManager +[2025-10-24 11:41:57,368] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 파이프라인 gpu_manager 속성: True, 값: +[2025-10-24 11:41:57,368] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 초기화 완료: gpu_manager 속성=True, 값= +[2025-10-24 11:41:57,368] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔰 OCR 모듈 Warm-up 시작... +[2025-10-24 11:41:57,369] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔍 ONNX OCR 감지 방식: polygon +[2025-10-24 11:41:57,373] [LogListener] [DEBUG] [loggerModule.py:debug:275] ⚡ ONNX OCR 추론 완료: 5.0ms +[2025-10-24 11:41:57,373] [LogListener] [DEBUG] [loggerModule.py:debug:275] 📊 세부 시간 - 감지: 3.0ms, 인식: 0.0ms, 분류: 0.0ms +[2025-10-24 11:41:57,373] [LogListener] [WARNING] [loggerModule.py:warning:287] ⚠️ ONNX OCR 결과가 비어있습니다 +[2025-10-24 11:41:57,377] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ OCR 모듈 Warm-up 성공 +[2025-10-24 11:41:57,377] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔰 ImageProcessor Warm‑up 완료 +[2025-10-24 11:41:57,378] [ResultListener] [INFO] [loggerModule.py:info:281] 워커 READY 수신 +[2025-10-24 11:41:57,378] [ResultListener] [INFO] [loggerModule.py:info:281] 워커 READY 수신 +[2025-10-24 11:41:57,378] [LogListener] [DEBUG] [loggerModule.py:debug:275] 📡 추가 READY 신호 전송 완료 +[2025-10-24 11:41:57,378] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 11:41:57,378] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 10204) +[2025-10-24 11:42:57,383] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 11:42:57,383] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 11:42:57,383] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 10204) +[2025-10-24 11:43:57,395] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 11:43:57,395] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 11:43:57,395] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 10204) +[2025-10-24 11:44:57,409] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 11:44:57,409] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 11:44:57,409] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 10204) +[2025-10-24 11:45:57,421] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 11:45:57,421] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 11:45:57,421] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 10204) +[2025-10-24 11:46:57,435] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 11:46:57,435] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 11:46:57,435] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 10204) +[2025-10-24 11:47:57,446] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 11:47:57,446] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 11:47:57,446] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 10204) +[2025-10-24 11:48:57,459] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 11:48:57,459] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 11:48:57,459] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 10204) +[2025-10-24 11:49:57,464] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 11:49:57,464] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 11:49:57,464] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 10204) +[2025-10-24 11:50:57,472] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 11:50:57,472] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 11:50:57,472] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 10204) +[2025-10-24 11:51:57,472] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 11:51:57,472] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 11:51:57,472] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 10204) +[2025-10-24 11:52:57,486] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 11:52:57,486] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 11:52:57,486] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 10204) +[2025-10-24 11:53:57,496] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 11:53:57,496] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 11:53:57,496] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 10204) +[2025-10-24 11:54:57,497] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 11:54:57,497] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 11:54:57,497] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 10204) +[2025-10-24 11:55:57,508] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 11:55:57,508] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 11:55:57,508] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 10204) +[2025-10-24 12:04:40,683] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 12:04:40,683] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 12:04:40,683] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 10204) +[2025-10-24 12:05:40,692] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 12:05:40,692] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 12:05:40,692] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 10204) +[2025-10-24 12:06:40,700] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 12:06:40,700] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 12:06:40,700] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 10204) +[2025-10-24 12:07:40,704] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 12:07:40,704] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 12:07:40,704] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 10204) +[2025-10-24 12:08:40,708] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 12:08:40,709] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 12:08:40,709] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 10204) +[2025-10-24 12:09:40,725] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 12:09:40,725] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 12:09:40,725] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 10204) +[2025-10-24 12:10:40,726] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 12:10:40,726] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 12:10:40,726] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 10204) +[2025-10-24 12:11:40,733] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 12:11:40,733] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 12:11:40,733] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 10204) +[2025-10-24 12:12:40,733] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 12:12:40,733] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 12:12:40,733] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 10204) +[2025-10-24 12:13:40,734] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 12:13:40,734] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 12:13:40,734] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 10204) +[2025-10-24 12:14:40,739] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 12:14:40,739] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 12:14:40,739] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 10204) +[2025-10-24 12:15:40,741] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 12:15:40,741] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 12:15:40,741] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 10204) +[2025-10-24 12:16:40,751] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 12:16:40,751] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 12:16:40,751] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 10204) +[2025-10-24 12:17:40,755] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 12:17:40,755] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 12:17:40,755] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 10204) +[2025-10-24 12:18:40,759] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 12:18:40,759] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 12:18:40,759] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 10204) +[2025-10-24 12:19:40,764] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 12:19:40,764] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 12:19:40,764] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 10204) +[2025-10-24 12:20:40,776] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 12:20:40,776] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 12:20:40,776] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 10204) +[2025-10-24 12:21:40,789] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 12:21:40,789] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 12:21:40,789] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 10204) +[2025-10-24 12:22:40,802] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 12:22:40,802] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 12:22:40,802] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 10204) +[2025-10-24 12:23:40,809] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 12:23:40,809] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 12:23:40,809] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 10204) +[2025-10-24 12:24:40,821] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 12:24:40,821] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 12:24:40,821] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 10204) +[2025-10-24 12:25:40,825] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 12:25:40,825] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 12:25:40,825] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 10204) +[2025-10-24 12:26:40,825] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 12:26:40,825] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 12:26:40,825] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 10204) +[2025-10-24 12:27:40,836] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 12:27:40,836] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 12:27:40,836] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 10204) +[2025-10-24 12:28:40,842] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 12:28:40,842] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 12:28:40,842] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 10204) +[2025-10-24 12:29:40,851] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 12:29:40,851] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 12:29:40,851] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 10204) +[2025-10-24 12:30:40,854] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 12:30:40,854] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 12:30:40,854] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 10204) +[2025-10-24 12:31:40,869] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 12:31:40,869] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 12:31:40,869] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 10204) +[2025-10-24 12:32:40,874] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 12:32:40,874] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 12:32:40,874] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 10204) +[2025-10-24 12:33:40,884] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 12:33:40,884] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 12:33:40,884] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 10204) +[2025-10-24 12:34:40,893] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 12:34:40,893] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 12:34:40,893] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 10204) +[2025-10-24 12:45:48,070] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 12:45:48,070] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 12:45:48,070] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 10204) +[2025-10-24 12:46:48,075] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 12:46:48,075] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 12:46:48,075] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 10204) +[2025-10-24 12:47:48,081] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 12:47:48,081] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 12:47:48,081] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 10204) +[2025-10-24 12:48:48,087] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 12:48:48,087] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 12:48:48,087] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 10204) +[2025-10-24 12:49:48,101] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 12:49:48,101] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 12:49:48,101] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 10204) +[2025-10-24 12:50:48,111] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 12:50:48,111] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 12:50:48,111] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 10204) +[2025-10-24 12:51:48,120] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 12:51:48,120] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 12:51:48,120] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 10204) +[2025-10-24 12:52:48,128] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 12:52:48,128] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 12:52:48,128] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 10204) +[2025-10-24 12:53:48,141] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 12:53:48,141] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 12:53:48,141] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 10204) +[2025-10-24 12:54:48,145] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 12:54:48,145] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 12:54:48,145] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 10204) +[2025-10-24 12:55:48,160] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 12:55:48,160] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 12:55:48,160] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 10204) +[2025-10-24 12:56:48,175] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 12:56:48,175] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 12:56:48,175] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 10204) +[2025-10-24 12:57:48,186] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 12:57:48,186] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 12:57:48,186] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 10204) +[2025-10-24 12:58:48,200] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 12:58:48,200] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 12:58:48,200] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 10204) +[2025-10-24 12:59:48,212] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 12:59:48,212] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 12:59:48,212] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 10204) +[2025-10-24 13:00:48,214] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 13:00:48,214] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 13:00:48,214] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 10204) +[2025-10-24 13:01:48,225] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 13:01:48,225] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 13:01:48,225] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 10204) +[2025-10-24 13:02:48,225] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 13:02:48,225] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 13:02:48,225] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 10204) +[2025-10-24 13:03:48,234] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 13:03:48,234] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 13:03:48,234] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 10204) +[2025-10-24 13:04:48,238] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 13:04:48,238] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 13:04:48,238] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 10204) +[2025-10-24 13:05:48,240] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 13:05:48,240] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 13:05:48,240] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 10204) +[2025-10-24 13:06:48,249] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 13:06:48,249] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 13:06:48,249] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 10204) +[2025-10-24 13:07:48,264] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 13:07:48,264] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 13:07:48,264] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 10204) +[2025-10-24 13:08:48,268] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 13:08:48,268] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 13:08:48,268] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 10204) +[2025-10-24 13:09:48,274] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 13:09:48,274] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 13:09:48,274] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 10204) +[2025-10-24 13:10:48,280] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 13:10:48,280] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 13:10:48,280] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 10204) +[2025-10-24 13:11:48,285] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 13:11:48,286] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 13:11:48,286] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 10204) +[2025-10-24 13:12:48,293] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 13:12:48,293] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 13:12:48,293] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 10204) +[2025-10-24 13:13:48,295] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 13:13:48,295] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 13:13:48,295] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 10204) +[2025-10-24 13:14:48,307] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 13:14:48,307] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 13:14:48,307] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 10204) +[2025-10-24 13:37:32,127] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 13:37:32,127] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 13:37:32,128] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 10204) +[2025-10-24 13:38:32,133] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 13:38:32,133] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 13:38:32,133] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 10204) +[2025-10-24 13:39:32,145] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 13:39:32,145] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 13:39:32,145] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 10204) +[2025-10-24 13:40:32,151] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 13:40:32,151] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 13:40:32,151] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 10204) +[2025-10-24 13:41:32,154] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 13:41:32,154] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 13:41:32,154] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 10204) +[2025-10-24 13:41:59,371] [MemMonitor] [WARNING] [loggerModule.py:warning:287] 워커 롤링 스케줄: reason=uptime-threshold +[2025-10-24 13:41:59,371] [WorkerRoller] [WARNING] [loggerModule.py:warning:287] 워커 롤링 시작: uptime-threshold +[2025-10-24 13:41:59,415] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔥 작업 수신 성공 +[2025-10-24 13:41:59,415] [LogListener] [DEBUG] [loggerModule.py:debug:275] Shutdown signal 수신 → 종료 +[2025-10-24 13:41:59,415] [LogListener] [DEBUG] [loggerModule.py:debug:275] OCR 모듈 정리 완료 +[2025-10-24 13:41:59,416] [LogListener] [DEBUG] [loggerModule.py:debug:275] 마스크 모듈 정리 완료 +[2025-10-24 13:41:59,416] [LogListener] [DEBUG] [loggerModule.py:debug:275] 임시 폴더 삭제됨: C:\ProgramData\ImgWorker\work +[2025-10-24 13:41:59,416] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 프로세서 소멸 +[2025-10-24 13:41:59,663] [WorkerRoller] [INFO] [loggerModule.py:info:281] 워커 프로세스 기동: PID=19508 +[2025-10-24 13:42:00,118] [LogListener] [DEBUG] [loggerModule.py:debug:275] ImageWorker 프로세스 기동 (PID=19508, Name=ImageWorkerProcess) +[2025-10-24 13:42:00,118] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔧 ImageProcessor3 초기화 시작... +[2025-10-24 13:42:00,119] [LogListener] [DEBUG] [loggerModule.py:debug:275] tracemalloc 메모리 추적 시작 +[2025-10-24 13:42:00,125] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ DirectML 환경 준비 완료 (Windows DirectX 12 기반) +[2025-10-24 13:42:00,125] [LogListener] [DEBUG] [loggerModule.py:debug:275] === 🚀 DirectML GPU 상태 초기화 시작 🚀 === +[2025-10-24 13:42:00,125] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🎯 사용자 GPU 가속 요청: False +[2025-10-24 13:42:00,125] [LogListener] [DEBUG] [loggerModule.py:debug:275] 💻 현재 운영체제: Windows +[2025-10-24 13:42:00,126] [LogListener] [DEBUG] [loggerModule.py:debug:275] GPU 가속이 비활성화됨 (toggle_states['use_cuda'] = False) +[2025-10-24 13:42:00,126] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔒 안전한 CPU 모드로 모든 GPU 설정 강제 비활성화 +[2025-10-24 13:42:00,126] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔧 ImageProcessor3 GPU 상태 요약: +[2025-10-24 13:42:00,126] [LogListener] [DEBUG] [loggerModule.py:debug:275] - CUDA 사용 가능: False +[2025-10-24 13:42:00,126] [LogListener] [DEBUG] [loggerModule.py:debug:275] - toggle_states['use_cuda']: NOT_SET +[2025-10-24 13:42:00,126] [LogListener] [DEBUG] [loggerModule.py:debug:275] - GPU 하드웨어 정보: {} +[2025-10-24 13:42:00,126] [LogListener] [DEBUG] [loggerModule.py:debug:275] ImageProcessor3 Init toggle_states: {'TEMP_IMAGE_DIR': 'C:\\ProgramData\\ImgWorker\\work', 'output_image_format': 'webp', 'use_local_rembg': True, 'local_rembg_model_path': 'D:\\py\\img_worker\\modules\\rembg_models', 'local_model_name': 'birefnet-general-lite', 'optionIMGTrans_type': 'CPU', 'detail_IMGTrans_type': 'CPU', 'thumb_trans_type': 'CPU', 'migan_use_accel': True, 'migan_onnx_path': 'D:\\py\\img_worker\\modules\\migan_onnx\\migan_pipeline_v2.onnx', 'ocr_provider_override': 'auto', 'migan_provider_override': 'auto'} +[2025-10-24 13:42:00,126] [LogListener] [DEBUG] [loggerModule.py:debug:275] is_member_valid: False +[2025-10-24 13:42:00,127] [LogListener] [DEBUG] [loggerModule.py:debug:275] debug_images 디렉토리 이미 존재: D:\py\img_worker\modules\debug_images +[2025-10-24 13:42:00,127] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.font_path: D:\py\img_worker\modules\fonts\HakgyoansimDunggeunmisoTTFB.ttf +[2025-10-24 13:42:00,127] [LogListener] [DEBUG] [loggerModule.py:debug:275] toggle_states font_path: D:\py\img_worker\modules\fonts\HakgyoansimDunggeunmisoTTFB.ttf +[2025-10-24 13:42:00,127] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.TEMP_IMAGE_DIR: C:\ProgramData\ImgWorker\work +[2025-10-24 13:42:00,127] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.debugging_save_Dir: D:\py\img_worker\modules\debug_images +[2025-10-24 13:42:00,127] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.unwanted_texts: {} +[2025-10-24 13:42:00,127] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.inpaint_method: migan +[2025-10-24 13:42:00,127] [LogListener] [DEBUG] [loggerModule.py:debug:275] Image.MAX_IMAGE_PIXELS set to 20000000 +[2025-10-24 13:42:00,127] [LogListener] [DEBUG] [loggerModule.py:debug:275] ONNX 모델 타입 설정값: 자동 선택, GPU 정보: {} +[2025-10-24 13:42:00,127] [LogListener] [DEBUG] [loggerModule.py:debug:275] 자동 선택 모드: GPU 추천 모델 simp 사용 +[2025-10-24 13:42:00,127] [LogListener] [DEBUG] [loggerModule.py:debug:275] ONNX OCR 모델 타입 결정: simp (GPU: False) +[2025-10-24 13:42:00,128] [LogListener] [DEBUG] [loggerModule.py:debug:275] ONNX OCR 모듈 CPU 모드로 설정 +[2025-10-24 13:42:00,128] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🚀 ONNX TextSystem 초기화 시작 (CPU 모드) +[2025-10-24 13:42:00,128] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🚀 ONNX TextSystem 초기화 시작 (CPU 모드) +[2025-10-24 13:42:01,662] [LogListener] [DEBUG] [loggerModule.py:debug:275] 문자 사전 파일 UTF-8 확인 완료: D:\py\img_worker\modules\onnx_ocr_module\dict\ppocr_keys_v1.txt +[2025-10-24 13:42:01,748] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ ONNX TextSystem 초기화 완료 (CPU + SIMP (호환성) 모델) +[2025-10-24 13:42:01,748] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ ONNX TextSystem 초기화 완료 (CPU + SIMP 모델) +[2025-10-24 13:42:01,748] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ ONNX OCR 모듈 초기화 성공 (CPU 모드) +[2025-10-24 13:42:01,749] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ ONNX OCR 모듈 초기화 성공 +[2025-10-24 13:42:01,749] [LogListener] [DEBUG] [loggerModule.py:debug:275] 마스크 모듈 초기화 완료 +[2025-10-24 13:42:01,749] [LogListener] [DEBUG] [loggerModule.py:debug:275] MaskModule 초기화 성공 +[2025-10-24 13:42:01,749] [LogListener] [DEBUG] [loggerModule.py:debug:275] 텍스트 렌더링 모듈 초기화 완료 +[2025-10-24 13:42:01,749] [LogListener] [DEBUG] [loggerModule.py:debug:275] 기본 폰트: D:\py\img_worker\modules\fonts\HakgyoansimDunggeunmisoTTFB.ttf +[2025-10-24 13:42:01,750] [LogListener] [DEBUG] [loggerModule.py:debug:275] TextRenderingModule 초기화 성공 +[2025-10-24 13:42:01,750] [LogListener] [DEBUG] [loggerModule.py:debug:275] 폰트 로드 성공: D:\py\img_worker\modules\fonts\HakgyoansimDunggeunmisoTTFB.ttf +[2025-10-24 13:42:01,750] [LogListener] [DEBUG] [loggerModule.py:debug:275] PostImageManager 초기화 성공 +[2025-10-24 13:42:01,750] [LogListener] [DEBUG] [loggerModule.py:debug:275] Request_AI_Server GPU 상태: CUDA 사용 가능=False +[2025-10-24 13:42:01,750] [LogListener] [DEBUG] [loggerModule.py:debug:275] Request_AI_Server 초기화 성공 +[2025-10-24 13:42:01,750] [LogListener] [DEBUG] [loggerModule.py:debug:275] GoogleTranslate 초기화 성공 +[2025-10-24 13:42:01,750] [LogListener] [DEBUG] [loggerModule.py:debug:275] MIGAN CUDA 사용 불가 - CPU 모드로 설정 +[2025-10-24 13:42:01,750] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] GPU 관리자 전달: GPUManager, can_use_cuda: False +[2025-10-24 13:42:01,769] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] GPU 관리자 사용 가능: False +[2025-10-24 13:42:01,984] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 사용 가능한 providers: ['DmlExecutionProvider', 'CPUExecutionProvider'] +[2025-10-24 13:42:01,984] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] DirectML 가속 활성화 +[2025-10-24 13:42:01,985] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 최종 providers: [('DmlExecutionProvider', {}), ('CPUExecutionProvider', {})] +[2025-10-24 13:42:01,985] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] DirectML 가속 시도: [('DmlExecutionProvider', {}), ('CPUExecutionProvider', {})] +[2025-10-24 13:42:01,985] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] DirectML 가속 성공! 실제 providers: ['DmlExecutionProvider', 'CPUExecutionProvider'] +[2025-10-24 13:42:01,985] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 입력 0: image, 형태: ['batch_size', 3, 'height', 'width'], 타입: tensor(uint8) +[2025-10-24 13:42:01,985] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 입력 1: mask, 형태: ['batch_size', 1, 'height', 'width'], 타입: tensor(uint8) +[2025-10-24 13:42:01,985] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 출력 0: result, 형태: ['ScatterNDresult_dim_0', 3, 'ScatterNDresult_dim_2', 'ScatterNDresult_dim_3'], 타입: tensor(uint8) +[2025-10-24 13:42:01,985] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 세션 준비 완료. providers=['DmlExecutionProvider', 'CPUExecutionProvider'] +[2025-10-24 13:42:01,985] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] GPU 관리자 연결 완료: GPUManager +[2025-10-24 13:42:01,985] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 파이프라인 gpu_manager 속성: True, 값: +[2025-10-24 13:42:01,985] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 초기화 완료: gpu_manager 속성=True, 값= +[2025-10-24 13:42:01,985] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔰 OCR 모듈 Warm-up 시작... +[2025-10-24 13:42:01,986] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔍 ONNX OCR 감지 방식: polygon +[2025-10-24 13:42:01,990] [LogListener] [DEBUG] [loggerModule.py:debug:275] ⚡ ONNX OCR 추론 완료: 5.0ms +[2025-10-24 13:42:01,990] [LogListener] [DEBUG] [loggerModule.py:debug:275] 📊 세부 시간 - 감지: 3.0ms, 인식: 0.0ms, 분류: 0.0ms +[2025-10-24 13:42:01,990] [LogListener] [WARNING] [loggerModule.py:warning:287] ⚠️ ONNX OCR 결과가 비어있습니다 +[2025-10-24 13:42:01,994] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ OCR 모듈 Warm-up 성공 +[2025-10-24 13:42:01,994] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔰 ImageProcessor Warm‑up 완료 +[2025-10-24 13:42:01,994] [ResultListener] [INFO] [loggerModule.py:info:281] 워커 READY 수신 +[2025-10-24 13:42:01,995] [ResultListener] [INFO] [loggerModule.py:info:281] 워커 READY 수신 +[2025-10-24 13:42:01,995] [LogListener] [DEBUG] [loggerModule.py:debug:275] 📡 추가 READY 신호 전송 완료 +[2025-10-24 13:42:01,995] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 13:42:01,995] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 19508) +[2025-10-24 13:43:02,001] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 13:43:02,001] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 13:43:02,001] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 19508) +[2025-10-24 13:44:02,006] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 13:44:02,006] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 13:44:02,006] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 19508) +[2025-10-24 13:45:02,013] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 13:45:02,013] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 13:45:02,013] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 19508) +[2025-10-24 13:46:02,020] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 13:46:02,020] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 13:46:02,020] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 19508) +[2025-10-24 13:47:02,022] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 13:47:02,022] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 13:47:02,022] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 19508) +[2025-10-24 13:48:02,030] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 13:48:02,030] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 13:48:02,030] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 19508) +[2025-10-24 13:49:02,033] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 13:49:02,033] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 13:49:02,033] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 19508) +[2025-10-24 13:50:02,045] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 13:50:02,045] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 13:50:02,045] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 19508) +[2025-10-24 13:51:02,056] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 13:51:02,056] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 13:51:02,056] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 19508) +[2025-10-24 13:52:02,069] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 13:52:02,069] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 13:52:02,069] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 19508) +[2025-10-24 13:53:02,082] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 13:53:02,082] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 13:53:02,082] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 19508) +[2025-10-24 13:54:02,088] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 13:54:02,088] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 13:54:02,088] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 19508) +[2025-10-24 13:55:02,102] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 13:55:02,102] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 13:55:02,102] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 19508) +[2025-10-24 13:56:02,110] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 13:56:02,110] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 13:56:02,110] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 19508) +[2025-10-24 13:57:02,121] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 13:57:02,121] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 13:57:02,121] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 19508) +[2025-10-24 13:58:02,129] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 13:58:02,129] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 13:58:02,129] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 19508) +[2025-10-24 13:59:02,140] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 13:59:02,140] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 13:59:02,140] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 19508) +[2025-10-24 14:00:02,149] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 14:00:02,149] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 14:00:02,149] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 19508) +[2025-10-24 14:01:02,152] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 14:01:02,152] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 14:01:02,152] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 19508) +[2025-10-24 14:02:02,164] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 14:02:02,164] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 14:02:02,164] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 19508) +[2025-10-24 14:03:02,175] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 14:03:02,175] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 14:03:02,175] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 19508) +[2025-10-24 14:04:02,187] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 14:04:02,187] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 14:04:02,187] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 19508) +[2025-10-24 14:05:02,190] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 14:05:02,190] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 14:05:02,190] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 19508) +[2025-10-24 14:06:02,194] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 14:06:02,194] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 14:06:02,194] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 19508) +[2025-10-24 14:07:02,197] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 14:07:02,197] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 14:07:02,197] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 19508) +[2025-10-24 14:15:46,121] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 14:15:46,121] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 14:15:46,122] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 19508) +[2025-10-24 14:16:46,127] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 14:16:46,127] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 14:16:46,127] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 19508) +[2025-10-24 14:17:46,138] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 14:17:46,138] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 14:17:46,138] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 19508) +[2025-10-24 14:18:46,145] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 14:18:46,145] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 14:18:46,145] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 19508) +[2025-10-24 14:19:46,153] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 14:19:46,153] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 14:19:46,153] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 19508) +[2025-10-24 14:20:46,165] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 14:20:46,165] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 14:20:46,165] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 19508) +[2025-10-24 14:21:46,178] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 14:21:46,178] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 14:21:46,178] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 19508) +[2025-10-24 14:22:46,184] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 14:22:46,184] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 14:22:46,184] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 19508) +[2025-10-24 14:23:46,199] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 14:23:46,199] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 14:23:46,199] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 19508) +[2025-10-24 14:24:46,207] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 14:24:46,207] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 14:24:46,207] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 19508) +[2025-10-24 14:25:46,220] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 14:25:46,220] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 14:25:46,220] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 19508) +[2025-10-24 14:26:46,224] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 14:26:46,224] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 14:26:46,224] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 19508) +[2025-10-24 14:27:46,235] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 14:27:46,235] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 14:27:46,235] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 19508) +[2025-10-24 14:28:46,235] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 14:28:46,235] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 14:28:46,235] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 19508) +[2025-10-24 14:29:46,251] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 14:29:46,252] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 14:29:46,252] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 19508) +[2025-10-24 14:30:46,257] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 14:30:46,257] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 14:30:46,257] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 19508) +[2025-10-24 14:31:46,267] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 14:31:46,267] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 14:31:46,267] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 19508) +[2025-10-24 14:32:46,281] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 14:32:46,282] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 14:32:46,282] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 19508) +[2025-10-24 14:33:46,282] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 14:33:46,282] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 14:33:46,282] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 19508) +[2025-10-24 14:34:46,290] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 14:34:46,290] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 14:34:46,290] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 19508) +[2025-10-24 14:35:46,293] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 14:35:46,293] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 14:35:46,293] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 19508) +[2025-10-24 14:36:46,308] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 14:36:46,308] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 14:36:46,308] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 19508) +[2025-10-24 14:37:46,322] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 14:37:46,323] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 14:37:46,323] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 19508) +[2025-10-24 14:38:46,330] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 14:38:46,330] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 14:38:46,330] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 19508) +[2025-10-24 14:39:46,344] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 14:39:46,344] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 14:39:46,344] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 19508) +[2025-10-24 14:40:46,349] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 14:40:46,349] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 14:40:46,349] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 19508) +[2025-10-24 14:41:46,351] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 14:41:46,351] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 14:41:46,351] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 19508) +[2025-10-24 14:42:46,365] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 14:42:46,365] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 14:42:46,365] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 19508) +[2025-10-24 14:43:46,374] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 14:43:46,374] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 14:43:46,374] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 19508) +[2025-10-24 14:44:46,388] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 14:44:46,389] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 14:44:46,389] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 19508) +[2025-10-24 14:45:46,390] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 14:45:46,390] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 14:45:46,390] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 19508) +[2025-10-24 14:46:46,400] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 14:46:46,400] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 14:46:46,401] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 19508) +[2025-10-24 14:47:46,409] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 14:47:46,409] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 14:47:46,409] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 19508) +[2025-10-24 14:48:46,409] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 14:48:46,409] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 14:48:46,409] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 19508) +[2025-10-24 14:49:46,423] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 14:49:46,423] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 14:49:46,423] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 19508) +[2025-10-24 14:50:46,432] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 14:50:46,432] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 14:50:46,432] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 19508) +[2025-10-24 14:51:46,446] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 14:51:46,446] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 14:51:46,446] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 19508) +[2025-10-24 14:52:46,453] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 14:52:46,453] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 14:52:46,453] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 19508) +[2025-10-24 14:53:46,467] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 14:53:46,467] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 14:53:46,467] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 19508) +[2025-10-24 14:54:46,471] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 14:54:46,471] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 14:54:46,471] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 19508) +[2025-10-24 14:55:46,473] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 14:55:46,473] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 14:55:46,473] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 19508) +[2025-10-24 14:56:46,477] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 14:56:46,477] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 14:56:46,477] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 19508) +[2025-10-24 14:57:46,486] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 14:57:46,487] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 14:57:46,487] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 19508) +[2025-10-24 14:58:46,495] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 14:58:46,495] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 14:58:46,495] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 19508) +[2025-10-24 14:59:46,510] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 14:59:46,511] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 14:59:46,511] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 19508) +[2025-10-24 15:00:46,513] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 15:00:46,513] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 15:00:46,513] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 19508) +[2025-10-24 15:01:46,518] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 15:01:46,518] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 15:01:46,518] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 19508) +[2025-10-24 15:02:46,519] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 15:02:46,519] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 15:02:46,519] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 19508) +[2025-10-24 15:03:46,523] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 15:03:46,523] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 15:03:46,523] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 19508) +[2025-10-24 15:04:46,537] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 15:04:46,537] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 15:04:46,537] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 19508) +[2025-10-24 15:05:46,552] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 15:05:46,552] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 15:05:46,552] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 19508) +[2025-10-24 15:06:46,561] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 15:06:46,561] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 15:06:46,561] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 19508) +[2025-10-24 15:07:46,562] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 15:07:46,562] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 15:07:46,562] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 19508) +[2025-10-24 15:08:46,571] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 15:08:46,571] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 15:08:46,571] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 19508) +[2025-10-24 15:09:46,582] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 15:09:46,582] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 15:09:46,582] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 19508) +[2025-10-24 15:10:46,597] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 15:10:46,597] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 15:10:46,597] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 19508) +[2025-10-24 15:11:46,605] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 15:11:46,605] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 15:11:46,605] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 19508) +[2025-10-24 15:12:46,619] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 15:12:46,619] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 15:12:46,619] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 19508) +[2025-10-24 15:13:46,625] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 15:13:46,625] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 15:13:46,625] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 19508) +[2025-10-24 15:14:46,636] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 15:14:46,636] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 15:14:46,636] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 19508) +[2025-10-24 15:15:46,640] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 15:15:46,640] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 15:15:46,640] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 19508) +[2025-10-24 15:16:46,641] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 15:16:46,641] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 15:16:46,641] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 19508) +[2025-10-24 15:17:46,641] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 15:17:46,641] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 15:17:46,642] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 19508) +[2025-10-24 15:18:46,642] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 15:18:46,642] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 15:18:46,642] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 19508) +[2025-10-24 15:19:46,647] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 15:19:46,647] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 15:19:46,647] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 19508) +[2025-10-24 15:20:46,659] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 15:20:46,659] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 15:20:46,659] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 19508) +[2025-10-24 15:21:46,661] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 15:21:46,661] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 15:21:46,661] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 19508) +[2025-10-24 15:22:46,661] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 15:22:46,661] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 15:22:46,661] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 19508) +[2025-10-24 15:23:46,669] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 15:23:46,669] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 15:23:46,669] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 19508) +[2025-10-24 15:24:46,681] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 15:24:46,681] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 15:24:46,681] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 19508) +[2025-10-24 15:25:46,682] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 15:25:46,682] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 15:25:46,682] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 19508) +[2025-10-24 15:26:46,683] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 15:26:46,683] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 15:26:46,683] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 19508) +[2025-10-24 15:27:46,697] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 15:27:46,697] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 15:27:46,697] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 19508) +[2025-10-24 15:28:46,700] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 15:28:46,700] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 15:28:46,700] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 19508) +[2025-10-24 15:29:46,702] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 15:29:46,702] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 15:29:46,702] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 19508) +[2025-10-24 15:30:46,713] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 15:30:46,713] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 15:30:46,713] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 19508) +[2025-10-24 15:31:46,727] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 15:31:46,727] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 15:31:46,727] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 19508) +[2025-10-24 15:32:46,737] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 15:32:46,737] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 15:32:46,737] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 19508) +[2025-10-24 15:33:46,750] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 15:33:46,750] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 15:33:46,750] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 19508) +[2025-10-24 15:34:46,756] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 15:34:46,756] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 15:34:46,756] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 19508) +[2025-10-24 15:35:46,771] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 15:35:46,771] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 15:35:46,771] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 19508) +[2025-10-24 15:36:02,124] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔥 작업 수신 성공 +[2025-10-24 15:36:02,124] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🚀 작업 처리 시작: cmd=process_single_image, uid=6e302bfb-4e03-4cc4-b37d-089083b6af26 +[2025-10-24 15:36:02,125] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 프로세서 toggle_states 업데이트 : {'TEMP_IMAGE_DIR': 'C:\\ProgramData\\ImgWorker\\work', 'output_image_format': 'webp', 'use_local_rembg': True, 'local_rembg_model_path': 'D:\\py\\img_worker\\modules\\rembg_models', 'local_model_name': 'birefnet-general-lite', 'optionIMGTrans_type': 'GPU', 'detail_IMGTrans_type': 'GPU', 'thumb_trans_type': 'GPU', 'migan_use_accel': True, 'migan_onnx_path': 'D:\\py\\img_worker\\modules\\migan_onnx\\migan_pipeline_v2.onnx', 'ocr_provider_override': 'auto', 'migan_provider_override': 'auto', 'font_type': '폰트5', 'unwanted_texts': ['가격설명', '무료배송', '보증', '사은품', '세일', '이벤트', '특가', '품절', '할인', '할인가'], 'is_member_valid': False, 'authenticated_by_admin': True} +[2025-10-24 15:36:02,125] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 실행 직전: cmd=process_single_image +[2025-10-24 15:36:02,125] [LogListener] [DEBUG] [loggerModule.py:debug:275] process_single_image 호출 직전 +[2025-10-24 15:36:02,126] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 번역시작 +[2025-10-24 15:36:02,418] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 1 처리 시작: C:\ProgramData\ImgWorker\incoming\dl_d903a13cdd57.jpg - 전체 번역 모드 +[2025-10-24 15:36:02,424] [LogListener] [DEBUG] [loggerModule.py:debug:275] 로컬 파일 경로 감지, 다운로드 생략: C:\ProgramData\ImgWorker\incoming\dl_d903a13cdd57.jpg +[2025-10-24 15:36:02,424] [LogListener] [DEBUG] [loggerModule.py:debug:275] 옵션 이미지는 스케일 처리 건너뛰기: option +[2025-10-24 15:36:02,424] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 1 로컬 저장위치(옵션 이미지 원본 유지): C:\ProgramData\ImgWorker\incoming\dl_d903a13cdd57.jpg +[2025-10-24 15:36:02,433] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔍 ONNX OCR 감지 방식: polygon +[2025-10-24 15:36:03,121] [LogListener] [DEBUG] [loggerModule.py:debug:275] ⚡ ONNX OCR 추론 완료: 686.4ms +[2025-10-24 15:36:03,127] [LogListener] [DEBUG] [loggerModule.py:debug:275] 📊 세부 시간 - 감지: 119.5ms, 인식: 530.9ms, 분류: 28.0ms +[2025-10-24 15:36:03,142] [LogListener] [DEBUG] [loggerModule.py:debug:275] 메모리 변화 [OCR 처리]: 40357.0MB -> 40636.3MB (+279.3MB, +0.7%) - 이미지 1 +[2025-10-24 15:36:03,142] [LogListener] [DEBUG] [loggerModule.py:debug:275] ocr_results: [{'text': '40', 'confidence': 0.8052586317062378, 'polygon': [[240.0, 59.0], [276.0, 59.0], [276.0, 90.0], [240.0, 90.0]], 'bbox': (240, 59, 37, 32), 'method': 'polygon'}, {'text': 'OU', 'confidence': 0.770665168762207, 'polygon': [[202.0, 74.0], [229.0, 74.0], [229.0, 85.0], [202.0, 85.0]], 'bbox': (202, 74, 28, 12), 'method': 'polygon'}, {'text': 'FM', 'confidence': 0.9865574836730957, 'polygon': [[452.0, 146.0], [464.0, 146.0], [464.0, 155.0], [452.0, 155.0]], 'bbox': (452, 146, 13, 10), 'method': 'polygon'}, {'text': 'AM', 'confidence': 0.922609269618988, 'polygon': [[470.0, 146.0], [481.0, 146.0], [481.0, 155.0], [470.0, 155.0]], 'bbox': (470, 146, 12, 10), 'method': 'polygon'}, {'text': '割列叫 2 3', 'confidence': 0.7253797650337219, 'polygon': [[91.0, 159.0], [264.0, 161.0], [264.0, 199.0], [91.0, 197.0]], 'bbox': (91, 159, 174, 41), 'method': 'polygon'}, {'text': '2', 'confidence': 0.9941845536231995, 'polygon': [[265.0, 171.0], [286.0, 171.0], [286.0, 192.0], [265.0, 192.0]], 'bbox': (265, 171, 22, 22), 'method': 'polygon'}, {'text': '104', 'confidence': 0.5533893704414368, 'polygon': [[458.0, 191.0], [470.0, 203.0], [460.0, 214.0], [448.0, 202.0]], 'bbox': (448, 191, 23, 24), 'method': 'polygon'}, {'text': '1500', 'confidence': 0.8648067712783813, 'polygon': [[469.0, 192.0], [482.0, 192.0], [482.0, 209.0], [469.0, 209.0]], 'bbox': (469, 192, 14, 18), 'method': 'polygon'}, {'text': '1400', 'confidence': 0.8108676075935364, 'polygon': [[470.0, 212.0], [483.0, 212.0], [483.0, 226.0], [470.0, 226.0]], 'bbox': (470, 212, 14, 15), 'method': 'polygon'}, {'text': '·R11', 'confidence': 0.8616527915000916, 'polygon': [[457.0, 245.0], [492.0, 245.0], [492.0, 256.0], [457.0, 256.0]], 'bbox': (457, 245, 36, 12), 'method': 'polygon'}, {'text': 'RADIO/BT/MUSICPLAYER', 'confidence': 0.9919697642326355, 'polygon': [[503.0, 244.0], [634.0, 244.0], [634.0, 257.0], [503.0, 257.0]], 'bbox': (503, 244, 132, 14), 'method': 'polygon'}, {'text': '4000mAh2l9%12l', 'confidence': 0.7704598307609558, 'polygon': [[89.0, 283.0], [276.0, 283.0], [276.0, 303.0], [89.0, 303.0]], 'bbox': (89, 283, 188, 21), 'method': 'polygon'}, {'text': 'PER', 'confidence': 0.5172831416130066, 'polygon': [[261.0, 344.0], [291.0, 344.0], [291.0, 358.0], [261.0, 358.0]], 'bbox': (261, 344, 31, 15), 'method': 'polygon'}, {'text': '2] N / O SOS', 'confidence': 0.7707078456878662, 'polygon': [[91.0, 408.0], [286.0, 406.0], [286.0, 430.0], [91.0, 432.0]], 'bbox': (91, 406, 196, 27), 'method': 'polygon'}, {'text': '合', 'confidence': 0.5559926629066467, 'polygon': [[96.0, 464.0], [114.0, 464.0], [114.0, 482.0], [96.0, 482.0]], 'bbox': (96, 464, 19, 19), 'method': 'polygon'}, {'text': '/', 'confidence': 0.6035980582237244, 'polygon': [[122.0, 465.0], [278.0, 465.0], [278.0, 479.0], [122.0, 479.0]], 'bbox': (122, 465, 157, 15), 'method': 'polygon'}, {'text': '0~10', 'confidence': 0.9903686046600342, 'polygon': [[542.0, 526.0], [579.0, 526.0], [579.0, 540.0], [542.0, 540.0]], 'bbox': (542, 526, 38, 15), 'method': 'polygon'}] +[2025-10-24 15:36:03,162] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): '40' +[2025-10-24 15:36:03,162] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): 'OU' +[2025-10-24 15:36:03,162] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): 'FM' +[2025-10-24 15:36:03,162] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): 'AM' +[2025-10-24 15:36:03,163] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 72.5%): '割列叫 2 3' +[2025-10-24 15:36:03,163] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): '2' +[2025-10-24 15:36:03,163] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): '104' +[2025-10-24 15:36:03,163] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): '1500' +[2025-10-24 15:36:03,163] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): '1400' +[2025-10-24 15:36:03,163] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): '·R11' +[2025-10-24 15:36:03,163] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): 'RADIO/BT/MUSICPLAYER' +[2025-10-24 15:36:03,163] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): '4000mAh2l9%12l' +[2025-10-24 15:36:03,163] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): 'PER' +[2025-10-24 15:36:03,163] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): '2] N / O SOS' +[2025-10-24 15:36:03,163] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 55.6%): '合' +[2025-10-24 15:36:03,164] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): '/' +[2025-10-24 15:36:03,164] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): '0~10' +[2025-10-24 15:36:03,164] [LogListener] [DEBUG] [loggerModule.py:debug:275] 필터링 결과: 2/17개 (신뢰도 + & 중국어) +[2025-10-24 15:36:03,164] [LogListener] [DEBUG] [loggerModule.py:debug:275] filter_ocr_results: [{'text': '割列叫 2 3', 'confidence': 0.7253797650337219, 'polygon': [[91.0, 159.0], [264.0, 161.0], [264.0, 199.0], [91.0, 197.0]], 'bbox': (91, 159, 174, 41), 'method': 'polygon'}, {'text': '合', 'confidence': 0.5559926629066467, 'polygon': [[96.0, 464.0], [114.0, 464.0], [114.0, 482.0], [96.0, 482.0]], 'bbox': (96, 464, 19, 19), 'method': 'polygon'}] +[2025-10-24 15:36:03,164] [LogListener] [DEBUG] [loggerModule.py:debug:275] 중국어 텍스트 2개 필터링 완료 +[2025-10-24 15:36:03,164] [LogListener] [DEBUG] [loggerModule.py:debug:275] 한글 텍스트 0개 필터링 완료 +[2025-10-24 15:36:05,477] [LogListener] [DEBUG] [loggerModule.py:debug:275] translated_texts: ['절단 열을 2 3이라고 합니다.', '결합하다'] +[2025-10-24 15:36:05,477] [LogListener] [DEBUG] [loggerModule.py:debug:275] 마스크 생성 완료 +[2025-10-24 15:36:05,477] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 1 OCR 권한 없음, 전체 번역 모드 +[2025-10-24 15:36:05,484] [LogListener] [DEBUG] [loggerModule.py:debug:275] [AUTO Inpaint] coverage=0.028, comps=2, min_center_dist=0.268 → migan +[2025-10-24 15:36:05,484] [LogListener] [DEBUG] [loggerModule.py:debug:275] ocr_count: 2 +[2025-10-24 15:36:05,484] [LogListener] [DEBUG] [loggerModule.py:debug:275] is_member_valid: False +[2025-10-24 15:36:05,484] [LogListener] [DEBUG] [loggerModule.py:debug:275] MIGAN 인페인팅 시도 +[2025-10-24 15:36:05,492] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 입력 형태 - 이미지: (1, 3, 800, 800), 마스크: (1, 1, 800, 800) +[2025-10-24 15:36:05,980] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 출력 형태: (800, 800, 3), dtype: uint8 +[2025-10-24 15:36:05,980] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 추론 완료: 488.98 ms +[2025-10-24 15:36:05,988] [LogListener] [DEBUG] [loggerModule.py:debug:275] MIGAN 인페인팅 성공 +[2025-10-24 15:36:05,988] [LogListener] [DEBUG] [loggerModule.py:debug:275] 메모리 변화 [인페인팅]: 40725.7MB -> 40884.1MB (+158.4MB, +0.4%) - 방법: migan +[2025-10-24 15:36:05,988] [LogListener] [DEBUG] [loggerModule.py:debug:275] 인페인팅 완료 +[2025-10-24 15:36:06,001] [LogListener] [DEBUG] [loggerModule.py:debug:275] 텍스트 렌더링 완료 +[2025-10-24 15:36:06,002] [LogListener] [DEBUG] [loggerModule.py:debug:275] watermark_text: 이미지 저작권 보유 +[2025-10-24 15:36:06,002] [LogListener] [DEBUG] [loggerModule.py:debug:275] is_watermark_enabled: True +[2025-10-24 15:36:06,324] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 저장 완료 : C:\ProgramData\ImgWorker\work\translated_option_img_1.webp +[2025-10-24 15:36:06,325] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 1 번역 완료: C:\ProgramData\ImgWorker\work\translated_option_img_1.webp +[2025-10-24 15:36:06,326] [LogListener] [DEBUG] [loggerModule.py:debug:275] ⏱ 이미지 파이프라인 총 4199.4ms | download=0.0ms | ocr=704.4ms | translate=2313.8ms | mask=2313.8ms | inpaint=509.0ms(migan/DirectML) | render=13.0ms | save=286.0ms +[2025-10-24 15:36:06,327] [LogListener] [DEBUG] [loggerModule.py:debug:275] process_single_image 호출 완료 +[2025-10-24 15:36:06,327] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 결과 반환 중: uid=6e302bfb-4e03-4cc4-b37d-089083b6af26 +[2025-10-24 15:36:06,327] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 결과 반환 완료: uid=6e302bfb-4e03-4cc4-b37d-089083b6af26 +[2025-10-24 15:36:06,328] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 15:36:06,328] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 19508) +[2025-10-24 15:36:08,107] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔥 작업 수신 성공 +[2025-10-24 15:36:08,108] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🚀 작업 처리 시작: cmd=process_single_image, uid=a58e29d2-8351-4e7e-89b1-77c7165cc8f1 +[2025-10-24 15:36:08,108] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 프로세서 toggle_states 업데이트 : {'TEMP_IMAGE_DIR': 'C:\\ProgramData\\ImgWorker\\work', 'output_image_format': 'webp', 'use_local_rembg': True, 'local_rembg_model_path': 'D:\\py\\img_worker\\modules\\rembg_models', 'local_model_name': 'birefnet-general-lite', 'optionIMGTrans_type': 'GPU', 'detail_IMGTrans_type': 'GPU', 'thumb_trans_type': 'GPU', 'migan_use_accel': True, 'migan_onnx_path': 'D:\\py\\img_worker\\modules\\migan_onnx\\migan_pipeline_v2.onnx', 'ocr_provider_override': 'auto', 'migan_provider_override': 'auto', 'font_type': '폰트5', 'unwanted_texts': ['가격설명', '무료배송', '보증', '사은품', '세일', '이벤트', '특가', '품절', '할인', '할인가'], 'is_member_valid': False, 'authenticated_by_admin': True} +[2025-10-24 15:36:08,108] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 실행 직전: cmd=process_single_image +[2025-10-24 15:36:08,108] [LogListener] [DEBUG] [loggerModule.py:debug:275] process_single_image 호출 직전 +[2025-10-24 15:36:08,108] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 번역시작 +[2025-10-24 15:36:08,528] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 2 처리 시작: C:\ProgramData\ImgWorker\incoming\dl_4a492281954a.jpg - 전체 번역 모드 +[2025-10-24 15:36:08,534] [LogListener] [DEBUG] [loggerModule.py:debug:275] 로컬 파일 경로 감지, 다운로드 생략: C:\ProgramData\ImgWorker\incoming\dl_4a492281954a.jpg +[2025-10-24 15:36:08,534] [LogListener] [DEBUG] [loggerModule.py:debug:275] 옵션 이미지는 스케일 처리 건너뛰기: option +[2025-10-24 15:36:08,534] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 2 로컬 저장위치(옵션 이미지 원본 유지): C:\ProgramData\ImgWorker\incoming\dl_4a492281954a.jpg +[2025-10-24 15:36:08,541] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔍 ONNX OCR 감지 방식: polygon +[2025-10-24 15:36:09,245] [LogListener] [DEBUG] [loggerModule.py:debug:275] ⚡ ONNX OCR 추론 완료: 701.6ms +[2025-10-24 15:36:09,281] [LogListener] [DEBUG] [loggerModule.py:debug:275] 📊 세부 시간 - 감지: 116.0ms, 인식: 547.6ms, 분류: 32.0ms +[2025-10-24 15:36:09,281] [LogListener] [DEBUG] [loggerModule.py:debug:275] 메모리 변화 [OCR 처리]: 40961.1MB -> 41116.8MB (+155.7MB, +0.4%) - 이미지 2 +[2025-10-24 15:36:09,281] [LogListener] [DEBUG] [loggerModule.py:debug:275] ocr_results: [{'text': '40', 'confidence': 0.79648756980896, 'polygon': [[240.0, 60.0], [276.0, 60.0], [276.0, 89.0], [240.0, 89.0]], 'bbox': (240, 60, 37, 30), 'method': 'polygon'}, {'text': 'OU', 'confidence': 0.720514714717865, 'polygon': [[201.0, 74.0], [229.0, 74.0], [229.0, 85.0], [201.0, 85.0]], 'bbox': (201, 74, 29, 12), 'method': 'polygon'}, {'text': 'FM', 'confidence': 0.9799278974533081, 'polygon': [[452.0, 146.0], [464.0, 146.0], [464.0, 154.0], [452.0, 154.0]], 'bbox': (452, 146, 13, 9), 'method': 'polygon'}, {'text': 'AM', 'confidence': 0.924596905708313, 'polygon': [[470.0, 146.0], [481.0, 146.0], [481.0, 155.0], [470.0, 155.0]], 'bbox': (470, 146, 12, 10), 'method': 'polygon'}, {'text': '割列叫2 3', 'confidence': 0.6766061782836914, 'polygon': [[91.0, 159.0], [263.0, 161.0], [263.0, 199.0], [91.0, 197.0]], 'bbox': (91, 159, 173, 41), 'method': 'polygon'}, {'text': 'WB', 'confidence': 0.651782214641571, 'polygon': [[469.0, 161.0], [482.0, 161.0], [482.0, 179.0], [469.0, 179.0]], 'bbox': (469, 161, 14, 19), 'method': 'polygon'}, {'text': '2', 'confidence': 0.9980236291885376, 'polygon': [[266.0, 171.0], [287.0, 171.0], [287.0, 192.0], [266.0, 192.0]], 'bbox': (266, 171, 22, 22), 'method': 'polygon'}, {'text': '150', 'confidence': 0.9817664623260498, 'polygon': [[469.0, 193.0], [482.0, 193.0], [482.0, 209.0], [469.0, 209.0]], 'bbox': (469, 193, 14, 17), 'method': 'polygon'}, {'text': '02', 'confidence': 0.9884648323059082, 'polygon': [[453.0, 216.0], [463.0, 213.0], [466.0, 223.0], [456.0, 226.0]], 'bbox': (453, 213, 14, 14), 'method': 'polygon'}, {'text': '1400', 'confidence': 0.8108658194541931, 'polygon': [[470.0, 212.0], [483.0, 212.0], [483.0, 226.0], [470.0, 226.0]], 'bbox': (470, 212, 14, 15), 'method': 'polygon'}, {'text': '·R11', 'confidence': 0.8663603067398071, 'polygon': [[457.0, 245.0], [490.0, 245.0], [490.0, 256.0], [457.0, 256.0]], 'bbox': (457, 245, 34, 12), 'method': 'polygon'}, {'text': 'RADIO/BT/MUSIC', 'confidence': 0.9941977858543396, 'polygon': [[505.0, 246.0], [596.0, 246.0], [596.0, 256.0], [505.0, 256.0]], 'bbox': (505, 246, 92, 11), 'method': 'polygon'}, {'text': 'PLAYER', 'confidence': 0.9932400584220886, 'polygon': [[593.0, 245.0], [634.0, 245.0], [634.0, 256.0], [593.0, 256.0]], 'bbox': (593, 245, 42, 12), 'method': 'polygon'}, {'text': '4000mAh 2I HI 型l 2l', 'confidence': 0.8441988229751587, 'polygon': [[85.0, 283.0], [281.0, 283.0], [281.0, 303.0], [85.0, 303.0]], 'bbox': (85, 283, 197, 21), 'method': 'polygon'}, {'text': 'PER', 'confidence': 0.5266934037208557, 'polygon': [[261.0, 344.0], [291.0, 344.0], [291.0, 358.0], [261.0, 358.0]], 'bbox': (261, 344, 31, 15), 'method': 'polygon'}, {'text': '2l N / O SOS', 'confidence': 0.7515807151794434, 'polygon': [[91.0, 408.0], [286.0, 406.0], [286.0, 430.0], [91.0, 432.0]], 'bbox': (91, 406, 196, 27), 'method': 'polygon'}, {'text': '合', 'confidence': 0.5278617143630981, 'polygon': [[96.0, 464.0], [114.0, 464.0], [114.0, 482.0], [96.0, 482.0]], 'bbox': (96, 464, 19, 19), 'method': 'polygon'}, {'text': '/', 'confidence': 0.5799485445022583, 'polygon': [[122.0, 466.0], [278.0, 466.0], [278.0, 479.0], [122.0, 479.0]], 'bbox': (122, 466, 157, 14), 'method': 'polygon'}] +[2025-10-24 15:36:09,283] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): '40' +[2025-10-24 15:36:09,283] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): 'OU' +[2025-10-24 15:36:09,283] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): 'FM' +[2025-10-24 15:36:09,283] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): 'AM' +[2025-10-24 15:36:09,283] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 67.7%): '割列叫2 3' +[2025-10-24 15:36:09,283] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): 'WB' +[2025-10-24 15:36:09,283] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): '2' +[2025-10-24 15:36:09,283] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): '150' +[2025-10-24 15:36:09,283] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): '02' +[2025-10-24 15:36:09,283] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): '1400' +[2025-10-24 15:36:09,284] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): '·R11' +[2025-10-24 15:36:09,284] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): 'RADIO/BT/MUSIC' +[2025-10-24 15:36:09,284] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): 'PLAYER' +[2025-10-24 15:36:09,284] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 84.4%): '4000mAh 2I HI 型l 2l' +[2025-10-24 15:36:09,284] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): 'PER' +[2025-10-24 15:36:09,284] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): '2l N / O SOS' +[2025-10-24 15:36:09,284] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 52.8%): '合' +[2025-10-24 15:36:09,284] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): '/' +[2025-10-24 15:36:09,284] [LogListener] [DEBUG] [loggerModule.py:debug:275] 필터링 결과: 3/18개 (신뢰도 + & 중국어) +[2025-10-24 15:36:09,284] [LogListener] [DEBUG] [loggerModule.py:debug:275] filter_ocr_results: [{'text': '割列叫2 3', 'confidence': 0.6766061782836914, 'polygon': [[91.0, 159.0], [263.0, 161.0], [263.0, 199.0], [91.0, 197.0]], 'bbox': (91, 159, 173, 41), 'method': 'polygon'}, {'text': '4000mAh 2I HI 型l 2l', 'confidence': 0.8441988229751587, 'polygon': [[85.0, 283.0], [281.0, 283.0], [281.0, 303.0], [85.0, 303.0]], 'bbox': (85, 283, 197, 21), 'method': 'polygon'}, {'text': '合', 'confidence': 0.5278617143630981, 'polygon': [[96.0, 464.0], [114.0, 464.0], [114.0, 482.0], [96.0, 482.0]], 'bbox': (96, 464, 19, 19), 'method': 'polygon'}] +[2025-10-24 15:36:09,285] [LogListener] [DEBUG] [loggerModule.py:debug:275] 중국어 텍스트 3개 필터링 완료 +[2025-10-24 15:36:09,285] [LogListener] [DEBUG] [loggerModule.py:debug:275] 한글 텍스트 0개 필터링 완료 +[2025-10-24 15:36:11,469] [LogListener] [DEBUG] [loggerModule.py:debug:275] translated_texts: ['열 입찰가 삭감 2 3', '4000mAh 2I HI 유형 l 2l', '결합하다'] +[2025-10-24 15:36:11,470] [LogListener] [DEBUG] [loggerModule.py:debug:275] 마스크 생성 완료 +[2025-10-24 15:36:11,470] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 2 OCR 권한 없음, 전체 번역 모드 +[2025-10-24 15:36:11,476] [LogListener] [DEBUG] [loggerModule.py:debug:275] [AUTO Inpaint] coverage=0.048, comps=3, min_center_dist=0.101 → request +[2025-10-24 15:36:11,476] [LogListener] [DEBUG] [loggerModule.py:debug:275] ocr_count: 3 +[2025-10-24 15:36:11,476] [LogListener] [DEBUG] [loggerModule.py:debug:275] is_member_valid: False +[2025-10-24 15:36:11,476] [LogListener] [DEBUG] [loggerModule.py:debug:275] MIGAN 인페인팅 시도 +[2025-10-24 15:36:11,484] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 입력 형태 - 이미지: (1, 3, 800, 800), 마스크: (1, 1, 800, 800) +[2025-10-24 15:36:11,621] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 출력 형태: (800, 800, 3), dtype: uint8 +[2025-10-24 15:36:11,622] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 추론 완료: 138.01 ms +[2025-10-24 15:36:11,629] [LogListener] [DEBUG] [loggerModule.py:debug:275] MIGAN 인페인팅 성공 +[2025-10-24 15:36:11,629] [LogListener] [DEBUG] [loggerModule.py:debug:275] 메모리 변화 [인페인팅]: 41197.0MB -> 41183.9MB (-13.1MB, -0.0%) - 방법: migan +[2025-10-24 15:36:11,630] [LogListener] [DEBUG] [loggerModule.py:debug:275] 인페인팅 완료 +[2025-10-24 15:36:11,642] [LogListener] [DEBUG] [loggerModule.py:debug:275] 텍스트 렌더링 완료 +[2025-10-24 15:36:11,642] [LogListener] [DEBUG] [loggerModule.py:debug:275] watermark_text: 이미지 저작권 보유 +[2025-10-24 15:36:11,642] [LogListener] [DEBUG] [loggerModule.py:debug:275] is_watermark_enabled: True +[2025-10-24 15:36:11,967] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 저장 완료 : C:\ProgramData\ImgWorker\work\translated_option_img_2.webp +[2025-10-24 15:36:11,968] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 2 번역 완료: C:\ProgramData\ImgWorker\work\translated_option_img_2.webp +[2025-10-24 15:36:11,968] [LogListener] [DEBUG] [loggerModule.py:debug:275] ⏱ 이미지 파이프라인 총 3859.1ms | download=0.0ms | ocr=717.6ms | translate=2189.1ms | mask=2189.1ms | inpaint=159.0ms(migan/DirectML) | render=12.0ms | save=281.2ms +[2025-10-24 15:36:11,969] [LogListener] [DEBUG] [loggerModule.py:debug:275] process_single_image 호출 완료 +[2025-10-24 15:36:11,969] [ResultListener] [WARNING] [loggerModule.py:warning:287] 워커 롤링 스케줄: reason=job-count-threshold +[2025-10-24 15:36:11,969] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 결과 반환 중: uid=a58e29d2-8351-4e7e-89b1-77c7165cc8f1 +[2025-10-24 15:36:11,970] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 결과 반환 완료: uid=a58e29d2-8351-4e7e-89b1-77c7165cc8f1 +[2025-10-24 15:36:11,970] [WorkerRoller] [WARNING] [loggerModule.py:warning:287] 워커 롤링 시작: job-count-threshold +[2025-10-24 15:36:11,970] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 15:36:11,970] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 19508) +[2025-10-24 15:36:11,985] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔥 작업 수신 성공 +[2025-10-24 15:36:12,017] [LogListener] [DEBUG] [loggerModule.py:debug:275] Shutdown signal 수신 → 종료 +[2025-10-24 15:36:12,017] [LogListener] [DEBUG] [loggerModule.py:debug:275] OCR 모듈 정리 완료 +[2025-10-24 15:36:12,017] [LogListener] [DEBUG] [loggerModule.py:debug:275] 마스크 모듈 정리 완료 +[2025-10-24 15:36:12,017] [LogListener] [DEBUG] [loggerModule.py:debug:275] 임시 폴더 삭제됨: C:\ProgramData\ImgWorker\work +[2025-10-24 15:36:12,017] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 프로세서 소멸 +[2025-10-24 15:36:12,432] [WorkerRoller] [INFO] [loggerModule.py:info:281] 워커 프로세스 기동: PID=26136 +[2025-10-24 15:36:12,930] [LogListener] [DEBUG] [loggerModule.py:debug:275] ImageWorker 프로세스 기동 (PID=26136, Name=ImageWorkerProcess) +[2025-10-24 15:36:12,930] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔧 ImageProcessor3 초기화 시작... +[2025-10-24 15:36:12,931] [LogListener] [DEBUG] [loggerModule.py:debug:275] tracemalloc 메모리 추적 시작 +[2025-10-24 15:36:12,937] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ DirectML 환경 준비 완료 (Windows DirectX 12 기반) +[2025-10-24 15:36:12,937] [LogListener] [DEBUG] [loggerModule.py:debug:275] === 🚀 DirectML GPU 상태 초기화 시작 🚀 === +[2025-10-24 15:36:12,937] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🎯 사용자 GPU 가속 요청: False +[2025-10-24 15:36:12,937] [LogListener] [DEBUG] [loggerModule.py:debug:275] 💻 현재 운영체제: Windows +[2025-10-24 15:36:12,937] [LogListener] [DEBUG] [loggerModule.py:debug:275] GPU 가속이 비활성화됨 (toggle_states['use_cuda'] = False) +[2025-10-24 15:36:12,937] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔒 안전한 CPU 모드로 모든 GPU 설정 강제 비활성화 +[2025-10-24 15:36:12,937] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔧 ImageProcessor3 GPU 상태 요약: +[2025-10-24 15:36:12,938] [LogListener] [DEBUG] [loggerModule.py:debug:275] - CUDA 사용 가능: False +[2025-10-24 15:36:12,938] [LogListener] [DEBUG] [loggerModule.py:debug:275] - toggle_states['use_cuda']: NOT_SET +[2025-10-24 15:36:12,938] [LogListener] [DEBUG] [loggerModule.py:debug:275] - GPU 하드웨어 정보: {} +[2025-10-24 15:36:12,938] [LogListener] [DEBUG] [loggerModule.py:debug:275] ImageProcessor3 Init toggle_states: {'TEMP_IMAGE_DIR': 'C:\\ProgramData\\ImgWorker\\work', 'output_image_format': 'webp', 'use_local_rembg': True, 'local_rembg_model_path': 'D:\\py\\img_worker\\modules\\rembg_models', 'local_model_name': 'birefnet-general-lite', 'optionIMGTrans_type': 'CPU', 'detail_IMGTrans_type': 'CPU', 'thumb_trans_type': 'CPU', 'migan_use_accel': True, 'migan_onnx_path': 'D:\\py\\img_worker\\modules\\migan_onnx\\migan_pipeline_v2.onnx', 'ocr_provider_override': 'auto', 'migan_provider_override': 'auto'} +[2025-10-24 15:36:12,938] [LogListener] [DEBUG] [loggerModule.py:debug:275] is_member_valid: False +[2025-10-24 15:36:12,938] [LogListener] [DEBUG] [loggerModule.py:debug:275] debug_images 디렉토리 이미 존재: D:\py\img_worker\modules\debug_images +[2025-10-24 15:36:12,938] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.font_path: D:\py\img_worker\modules\fonts\HakgyoansimDunggeunmisoTTFB.ttf +[2025-10-24 15:36:12,938] [LogListener] [DEBUG] [loggerModule.py:debug:275] toggle_states font_path: D:\py\img_worker\modules\fonts\HakgyoansimDunggeunmisoTTFB.ttf +[2025-10-24 15:36:12,938] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.TEMP_IMAGE_DIR: C:\ProgramData\ImgWorker\work +[2025-10-24 15:36:12,938] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.debugging_save_Dir: D:\py\img_worker\modules\debug_images +[2025-10-24 15:36:12,938] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.unwanted_texts: {} +[2025-10-24 15:36:12,938] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.inpaint_method: migan +[2025-10-24 15:36:12,938] [LogListener] [DEBUG] [loggerModule.py:debug:275] Image.MAX_IMAGE_PIXELS set to 20000000 +[2025-10-24 15:36:12,938] [LogListener] [DEBUG] [loggerModule.py:debug:275] ONNX 모델 타입 설정값: 자동 선택, GPU 정보: {} +[2025-10-24 15:36:12,939] [LogListener] [DEBUG] [loggerModule.py:debug:275] 자동 선택 모드: GPU 추천 모델 simp 사용 +[2025-10-24 15:36:12,939] [LogListener] [DEBUG] [loggerModule.py:debug:275] ONNX OCR 모델 타입 결정: simp (GPU: False) +[2025-10-24 15:36:12,939] [LogListener] [DEBUG] [loggerModule.py:debug:275] ONNX OCR 모듈 CPU 모드로 설정 +[2025-10-24 15:36:12,939] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🚀 ONNX TextSystem 초기화 시작 (CPU 모드) +[2025-10-24 15:36:12,939] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🚀 ONNX TextSystem 초기화 시작 (CPU 모드) +[2025-10-24 15:36:14,501] [LogListener] [DEBUG] [loggerModule.py:debug:275] 문자 사전 파일 UTF-8 확인 완료: D:\py\img_worker\modules\onnx_ocr_module\dict\ppocr_keys_v1.txt +[2025-10-24 15:36:14,585] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ ONNX TextSystem 초기화 완료 (CPU + SIMP (호환성) 모델) +[2025-10-24 15:36:14,586] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ ONNX TextSystem 초기화 완료 (CPU + SIMP 모델) +[2025-10-24 15:36:14,586] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ ONNX OCR 모듈 초기화 성공 (CPU 모드) +[2025-10-24 15:36:14,586] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ ONNX OCR 모듈 초기화 성공 +[2025-10-24 15:36:14,587] [LogListener] [DEBUG] [loggerModule.py:debug:275] 마스크 모듈 초기화 완료 +[2025-10-24 15:36:14,587] [LogListener] [DEBUG] [loggerModule.py:debug:275] MaskModule 초기화 성공 +[2025-10-24 15:36:14,587] [LogListener] [DEBUG] [loggerModule.py:debug:275] 텍스트 렌더링 모듈 초기화 완료 +[2025-10-24 15:36:14,587] [LogListener] [DEBUG] [loggerModule.py:debug:275] 기본 폰트: D:\py\img_worker\modules\fonts\HakgyoansimDunggeunmisoTTFB.ttf +[2025-10-24 15:36:14,587] [LogListener] [DEBUG] [loggerModule.py:debug:275] TextRenderingModule 초기화 성공 +[2025-10-24 15:36:14,587] [LogListener] [DEBUG] [loggerModule.py:debug:275] 폰트 로드 성공: D:\py\img_worker\modules\fonts\HakgyoansimDunggeunmisoTTFB.ttf +[2025-10-24 15:36:14,587] [LogListener] [DEBUG] [loggerModule.py:debug:275] PostImageManager 초기화 성공 +[2025-10-24 15:36:14,587] [LogListener] [DEBUG] [loggerModule.py:debug:275] Request_AI_Server GPU 상태: CUDA 사용 가능=False +[2025-10-24 15:36:14,587] [LogListener] [DEBUG] [loggerModule.py:debug:275] Request_AI_Server 초기화 성공 +[2025-10-24 15:36:14,587] [LogListener] [DEBUG] [loggerModule.py:debug:275] GoogleTranslate 초기화 성공 +[2025-10-24 15:36:14,588] [LogListener] [DEBUG] [loggerModule.py:debug:275] MIGAN CUDA 사용 불가 - CPU 모드로 설정 +[2025-10-24 15:36:14,588] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] GPU 관리자 전달: GPUManager, can_use_cuda: False +[2025-10-24 15:36:14,606] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] GPU 관리자 사용 가능: False +[2025-10-24 15:36:14,816] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 사용 가능한 providers: ['DmlExecutionProvider', 'CPUExecutionProvider'] +[2025-10-24 15:36:14,817] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] DirectML 가속 활성화 +[2025-10-24 15:36:14,817] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 최종 providers: [('DmlExecutionProvider', {}), ('CPUExecutionProvider', {})] +[2025-10-24 15:36:14,817] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] DirectML 가속 시도: [('DmlExecutionProvider', {}), ('CPUExecutionProvider', {})] +[2025-10-24 15:36:14,817] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] DirectML 가속 성공! 실제 providers: ['DmlExecutionProvider', 'CPUExecutionProvider'] +[2025-10-24 15:36:14,817] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 입력 0: image, 형태: ['batch_size', 3, 'height', 'width'], 타입: tensor(uint8) +[2025-10-24 15:36:14,817] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 입력 1: mask, 형태: ['batch_size', 1, 'height', 'width'], 타입: tensor(uint8) +[2025-10-24 15:36:14,817] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 출력 0: result, 형태: ['ScatterNDresult_dim_0', 3, 'ScatterNDresult_dim_2', 'ScatterNDresult_dim_3'], 타입: tensor(uint8) +[2025-10-24 15:36:14,817] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 세션 준비 완료. providers=['DmlExecutionProvider', 'CPUExecutionProvider'] +[2025-10-24 15:36:14,817] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] GPU 관리자 연결 완료: GPUManager +[2025-10-24 15:36:14,817] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 파이프라인 gpu_manager 속성: True, 값: +[2025-10-24 15:36:14,817] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 초기화 완료: gpu_manager 속성=True, 값= +[2025-10-24 15:36:14,817] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔰 OCR 모듈 Warm-up 시작... +[2025-10-24 15:36:14,818] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔍 ONNX OCR 감지 방식: polygon +[2025-10-24 15:36:14,822] [LogListener] [DEBUG] [loggerModule.py:debug:275] ⚡ ONNX OCR 추론 완료: 5.0ms +[2025-10-24 15:36:14,822] [LogListener] [DEBUG] [loggerModule.py:debug:275] 📊 세부 시간 - 감지: 3.0ms, 인식: 0.0ms, 분류: 0.0ms +[2025-10-24 15:36:14,822] [LogListener] [WARNING] [loggerModule.py:warning:287] ⚠️ ONNX OCR 결과가 비어있습니다 +[2025-10-24 15:36:14,825] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ OCR 모듈 Warm-up 성공 +[2025-10-24 15:36:14,825] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔰 ImageProcessor Warm‑up 완료 +[2025-10-24 15:36:14,826] [ResultListener] [INFO] [loggerModule.py:info:281] 워커 READY 수신 +[2025-10-24 15:36:14,826] [WorkerRoller] [INFO] [loggerModule.py:info:281] 버퍼 작업 재개: 1건 +[2025-10-24 15:36:14,826] [ResultListener] [INFO] [loggerModule.py:info:281] 워커 READY 수신 +[2025-10-24 15:36:14,826] [LogListener] [DEBUG] [loggerModule.py:debug:275] 📡 추가 READY 신호 전송 완료 +[2025-10-24 15:36:14,826] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 15:36:14,827] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 26136) +[2025-10-24 15:36:14,827] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔥 작업 수신 성공 +[2025-10-24 15:36:14,827] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🚀 작업 처리 시작: cmd=process_single_image, uid=f0806348-7fa0-4fbc-a2de-e789bde7f651 +[2025-10-24 15:36:14,827] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 프로세서 toggle_states 업데이트 : {'TEMP_IMAGE_DIR': 'C:\\ProgramData\\ImgWorker\\work', 'output_image_format': 'webp', 'use_local_rembg': True, 'local_rembg_model_path': 'D:\\py\\img_worker\\modules\\rembg_models', 'local_model_name': 'birefnet-general-lite', 'optionIMGTrans_type': 'GPU', 'detail_IMGTrans_type': 'GPU', 'thumb_trans_type': 'GPU', 'migan_use_accel': True, 'migan_onnx_path': 'D:\\py\\img_worker\\modules\\migan_onnx\\migan_pipeline_v2.onnx', 'ocr_provider_override': 'auto', 'migan_provider_override': 'auto', 'font_type': '폰트5', 'unwanted_texts': ['가격설명', '무료배송', '보증', '사은품', '세일', '이벤트', '특가', '품절', '할인', '할인가'], 'is_member_valid': False, 'authenticated_by_admin': True} +[2025-10-24 15:36:14,828] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 실행 직전: cmd=process_single_image +[2025-10-24 15:36:14,828] [LogListener] [DEBUG] [loggerModule.py:debug:275] process_single_image 호출 직전 +[2025-10-24 15:36:14,830] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 번역시작 +[2025-10-24 15:36:15,079] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 3 처리 시작: C:\ProgramData\ImgWorker\incoming\dl_426af7f86f25.jpg - 전체 번역 모드 +[2025-10-24 15:36:15,084] [LogListener] [DEBUG] [loggerModule.py:debug:275] 로컬 파일 경로 감지, 다운로드 생략: C:\ProgramData\ImgWorker\incoming\dl_426af7f86f25.jpg +[2025-10-24 15:36:15,084] [LogListener] [DEBUG] [loggerModule.py:debug:275] 옵션 이미지는 스케일 처리 건너뛰기: option +[2025-10-24 15:36:15,084] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 3 로컬 저장위치(옵션 이미지 원본 유지): C:\ProgramData\ImgWorker\incoming\dl_426af7f86f25.jpg +[2025-10-24 15:36:15,091] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔍 ONNX OCR 감지 방식: polygon +[2025-10-24 15:36:15,743] [LogListener] [DEBUG] [loggerModule.py:debug:275] ⚡ ONNX OCR 추론 완료: 649.0ms +[2025-10-24 15:36:15,743] [LogListener] [DEBUG] [loggerModule.py:debug:275] 📊 세부 시간 - 감지: 188.0ms, 인식: 437.0ms, 분류: 19.0ms +[2025-10-24 15:36:15,763] [LogListener] [DEBUG] [loggerModule.py:debug:275] 메모리 변화 [OCR 처리]: 40751.1MB -> 40995.6MB (+244.6MB, +0.6%) - 이미지 3 +[2025-10-24 15:36:15,780] [LogListener] [DEBUG] [loggerModule.py:debug:275] ocr_results: [{'text': 'BAOZOUTU', 'confidence': 0.993288516998291, 'polygon': [[134.0, 73.0], [274.0, 73.0], [274.0, 87.0], [134.0, 87.0]], 'bbox': (134, 73, 141, 15), 'method': 'polygon'}, {'text': '..R12', 'confidence': 0.8256956338882446, 'polygon': [[574.0, 152.0], [616.0, 152.0], [616.0, 163.0], [574.0, 163.0]], 'bbox': (574, 152, 43, 12), 'method': 'polygon'}, {'text': '518', 'confidence': 0.9848126769065857, 'polygon': [[488.0, 174.0], [607.0, 174.0], [607.0, 240.0], [488.0, 240.0]], 'bbox': (488, 174, 120, 67), 'method': 'polygon'}, {'text': 'MH', 'confidence': 0.8806892037391663, 'polygon': [[608.0, 228.0], [621.0, 228.0], [621.0, 237.0], [608.0, 237.0]], 'bbox': (608, 228, 14, 10), 'method': 'polygon'}, {'text': 'MULTIBANDRADIO/BT/MUSICPLAYER', 'confidence': 0.9826321601867676, 'polygon': [[464.0, 244.0], [624.0, 244.0], [624.0, 257.0], [464.0, 257.0]], 'bbox': (464, 244, 161, 14), 'method': 'polygon'}, {'text': '4000mAh 2l喜 己l2l', 'confidence': 0.8047774434089661, 'polygon': [[85.0, 282.0], [283.0, 282.0], [283.0, 302.0], [85.0, 302.0]], 'bbox': (85, 282, 199, 21), 'method': 'polygon'}, {'text': 'PER', 'confidence': 0.5194413661956787, 'polygon': [[262.0, 344.0], [292.0, 344.0], [292.0, 358.0], [262.0, 358.0]], 'bbox': (262, 344, 31, 15), 'method': 'polygon'}, {'text': '丽是门 / 百昆 SOS', 'confidence': 0.7015822529792786, 'polygon': [[83.0, 400.0], [284.0, 400.0], [284.0, 424.0], [83.0, 424.0]], 'bbox': (83, 400, 202, 25), 'method': 'polygon'}, {'text': '克川', 'confidence': 0.6043555736541748, 'polygon': [[532.0, 424.0], [603.0, 417.0], [606.0, 442.0], [535.0, 449.0]], 'bbox': (532, 417, 75, 33), 'method': 'polygon'}, {'text': '应易 品品 / E屁E 可', 'confidence': 0.7070178985595703, 'polygon': [[85.0, 462.0], [287.0, 462.0], [287.0, 482.0], [85.0, 482.0]], 'bbox': (85, 462, 203, 21), 'method': 'polygon'}] +[2025-10-24 15:36:15,780] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): 'BAOZOUTU' +[2025-10-24 15:36:15,780] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): '..R12' +[2025-10-24 15:36:15,781] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): '518' +[2025-10-24 15:36:15,781] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): 'MH' +[2025-10-24 15:36:15,781] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): 'MULTIBANDRADIO/BT/MUSICPLAYER' +[2025-10-24 15:36:15,781] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 80.5%): '4000mAh 2l喜 己l2l' +[2025-10-24 15:36:15,781] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): 'PER' +[2025-10-24 15:36:15,781] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 70.2%): '丽是门 / 百昆 SOS' +[2025-10-24 15:36:15,781] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 60.4%): '克川' +[2025-10-24 15:36:15,781] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 70.7%): '应易 品品 / E屁E 可' +[2025-10-24 15:36:15,781] [LogListener] [DEBUG] [loggerModule.py:debug:275] 필터링 결과: 4/10개 (신뢰도 + & 중국어) +[2025-10-24 15:36:15,781] [LogListener] [DEBUG] [loggerModule.py:debug:275] filter_ocr_results: [{'text': '4000mAh 2l喜 己l2l', 'confidence': 0.8047774434089661, 'polygon': [[85.0, 282.0], [283.0, 282.0], [283.0, 302.0], [85.0, 302.0]], 'bbox': (85, 282, 199, 21), 'method': 'polygon'}, {'text': '丽是门 / 百昆 SOS', 'confidence': 0.7015822529792786, 'polygon': [[83.0, 400.0], [284.0, 400.0], [284.0, 424.0], [83.0, 424.0]], 'bbox': (83, 400, 202, 25), 'method': 'polygon'}, {'text': '克川', 'confidence': 0.6043555736541748, 'polygon': [[532.0, 424.0], [603.0, 417.0], [606.0, 442.0], [535.0, 449.0]], 'bbox': (532, 417, 75, 33), 'method': 'polygon'}, {'text': '应易 品品 / E屁E 可', 'confidence': 0.7070178985595703, 'polygon': [[85.0, 462.0], [287.0, 462.0], [287.0, 482.0], [85.0, 482.0]], 'bbox': (85, 462, 203, 21), 'method': 'polygon'}] +[2025-10-24 15:36:15,782] [LogListener] [DEBUG] [loggerModule.py:debug:275] 중국어 텍스트 4개 필터링 완료 +[2025-10-24 15:36:15,782] [LogListener] [DEBUG] [loggerModule.py:debug:275] 한글 텍스트 0개 필터링 완료 +[2025-10-24 15:36:18,399] [LogListener] [DEBUG] [loggerModule.py:debug:275] translated_texts: ['4000mAh 2l 하이지 l2l', '리시멘 / 바이쿤SOS', '카츠카와', '잉이 핀핀 / E 방귀 E 캔'] +[2025-10-24 15:36:18,399] [LogListener] [DEBUG] [loggerModule.py:debug:275] 마스크 생성 완료 +[2025-10-24 15:36:18,399] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 3 OCR 권한 없음, 전체 번역 모드 +[2025-10-24 15:36:18,404] [LogListener] [DEBUG] [loggerModule.py:debug:275] [AUTO Inpaint] coverage=0.073, comps=4, min_center_dist=0.053 → request +[2025-10-24 15:36:18,404] [LogListener] [DEBUG] [loggerModule.py:debug:275] ocr_count: 4 +[2025-10-24 15:36:18,404] [LogListener] [DEBUG] [loggerModule.py:debug:275] is_member_valid: False +[2025-10-24 15:36:18,404] [LogListener] [DEBUG] [loggerModule.py:debug:275] MIGAN 인페인팅 시도 +[2025-10-24 15:36:18,412] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 입력 형태 - 이미지: (1, 3, 800, 800), 마스크: (1, 1, 800, 800) +[2025-10-24 15:36:18,919] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 출력 형태: (800, 800, 3), dtype: uint8 +[2025-10-24 15:36:18,919] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 추론 완료: 506.46 ms +[2025-10-24 15:36:18,928] [LogListener] [DEBUG] [loggerModule.py:debug:275] MIGAN 인페인팅 성공 +[2025-10-24 15:36:18,928] [LogListener] [DEBUG] [loggerModule.py:debug:275] 메모리 변화 [인페인팅]: 41007.8MB -> 41197.6MB (+189.8MB, +0.5%) - 방법: migan +[2025-10-24 15:36:18,928] [LogListener] [DEBUG] [loggerModule.py:debug:275] 인페인팅 완료 +[2025-10-24 15:36:18,953] [LogListener] [DEBUG] [loggerModule.py:debug:275] 텍스트 렌더링 완료 +[2025-10-24 15:36:18,954] [LogListener] [DEBUG] [loggerModule.py:debug:275] watermark_text: 이미지 저작권 보유 +[2025-10-24 15:36:18,954] [LogListener] [DEBUG] [loggerModule.py:debug:275] is_watermark_enabled: True +[2025-10-24 15:36:19,286] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 저장 완료 : C:\ProgramData\ImgWorker\work\translated_option_img_3.webp +[2025-10-24 15:36:19,287] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 3 번역 완료: C:\ProgramData\ImgWorker\work\translated_option_img_3.webp +[2025-10-24 15:36:19,287] [LogListener] [DEBUG] [loggerModule.py:debug:275] ⏱ 이미지 파이프라인 총 4458.4ms | download=0.0ms | ocr=664.0ms | translate=2618.5ms | mask=2618.5ms | inpaint=528.5ms(migan/DirectML) | render=24.0ms | save=287.0ms +[2025-10-24 15:36:19,289] [LogListener] [DEBUG] [loggerModule.py:debug:275] process_single_image 호출 완료 +[2025-10-24 15:36:19,289] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 결과 반환 중: uid=f0806348-7fa0-4fbc-a2de-e789bde7f651 +[2025-10-24 15:36:19,289] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 결과 반환 완료: uid=f0806348-7fa0-4fbc-a2de-e789bde7f651 +[2025-10-24 15:36:19,289] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 15:36:19,289] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 26136) +[2025-10-24 15:36:21,077] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔥 작업 수신 성공 +[2025-10-24 15:36:21,077] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🚀 작업 처리 시작: cmd=process_single_image, uid=6a2f4227-8cf2-4129-b990-94fffcba76b0 +[2025-10-24 15:36:21,078] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 프로세서 toggle_states 업데이트 : {'TEMP_IMAGE_DIR': 'C:\\ProgramData\\ImgWorker\\work', 'output_image_format': 'webp', 'use_local_rembg': True, 'local_rembg_model_path': 'D:\\py\\img_worker\\modules\\rembg_models', 'local_model_name': 'birefnet-general-lite', 'optionIMGTrans_type': 'GPU', 'detail_IMGTrans_type': 'GPU', 'thumb_trans_type': 'GPU', 'migan_use_accel': True, 'migan_onnx_path': 'D:\\py\\img_worker\\modules\\migan_onnx\\migan_pipeline_v2.onnx', 'ocr_provider_override': 'auto', 'migan_provider_override': 'auto', 'font_type': '폰트5', 'unwanted_texts': ['가격설명', '무료배송', '보증', '사은품', '세일', '이벤트', '특가', '품절', '할인', '할인가'], 'is_member_valid': False, 'authenticated_by_admin': True} +[2025-10-24 15:36:21,078] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 실행 직전: cmd=process_single_image +[2025-10-24 15:36:21,078] [LogListener] [DEBUG] [loggerModule.py:debug:275] process_single_image 호출 직전 +[2025-10-24 15:36:21,079] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 번역시작 +[2025-10-24 15:36:21,370] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 4 처리 시작: C:\ProgramData\ImgWorker\incoming\dl_50ea8b4642a4.jpg - 전체 번역 모드 +[2025-10-24 15:36:21,378] [LogListener] [DEBUG] [loggerModule.py:debug:275] 로컬 파일 경로 감지, 다운로드 생략: C:\ProgramData\ImgWorker\incoming\dl_50ea8b4642a4.jpg +[2025-10-24 15:36:21,378] [LogListener] [DEBUG] [loggerModule.py:debug:275] 옵션 이미지는 스케일 처리 건너뛰기: option +[2025-10-24 15:36:21,378] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 4 로컬 저장위치(옵션 이미지 원본 유지): C:\ProgramData\ImgWorker\incoming\dl_50ea8b4642a4.jpg +[2025-10-24 15:36:21,386] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔍 ONNX OCR 감지 방식: polygon +[2025-10-24 15:36:22,023] [LogListener] [DEBUG] [loggerModule.py:debug:275] ⚡ ONNX OCR 추론 완료: 635.6ms +[2025-10-24 15:36:22,037] [LogListener] [DEBUG] [loggerModule.py:debug:275] 📊 세부 시간 - 감지: 136.0ms, 인식: 469.6ms, 분류: 23.0ms +[2025-10-24 15:36:22,038] [LogListener] [DEBUG] [loggerModule.py:debug:275] 메모리 변화 [OCR 처리]: 41418.2MB -> 41490.6MB (+72.4MB, +0.2%) - 이미지 4 +[2025-10-24 15:36:22,038] [LogListener] [DEBUG] [loggerModule.py:debug:275] ocr_results: [{'text': 'BAOZOUTU', 'confidence': 0.9920582175254822, 'polygon': [[133.0, 72.0], [273.0, 72.0], [273.0, 86.0], [133.0, 86.0]], 'bbox': (133, 72, 141, 15), 'method': 'polygon'}, {'text': '.R12', 'confidence': 0.8190782070159912, 'polygon': [[578.0, 152.0], [614.0, 152.0], [614.0, 163.0], [578.0, 163.0]], 'bbox': (578, 152, 37, 12), 'method': 'polygon'}, {'text': '百口2 三丽 昱', 'confidence': 0.6060160994529724, 'polygon': [[92.0, 162.0], [287.0, 164.0], [287.0, 195.0], [92.0, 193.0]], 'bbox': (92, 162, 196, 34), 'method': 'polygon'}, {'text': '518', 'confidence': 0.982236385345459, 'polygon': [[488.0, 175.0], [607.0, 175.0], [607.0, 240.0], [488.0, 240.0]], 'bbox': (488, 175, 120, 66), 'method': 'polygon'}, {'text': 'MH', 'confidence': 0.5119634866714478, 'polygon': [[607.0, 228.0], [622.0, 228.0], [622.0, 238.0], [607.0, 238.0]], 'bbox': (607, 228, 16, 11), 'method': 'polygon'}, {'text': 'MULTIBANDRADIO/BT/MUSICPLAYER', 'confidence': 0.9807587265968323, 'polygon': [[463.0, 243.0], [624.0, 244.0], [624.0, 258.0], [463.0, 257.0]], 'bbox': (463, 243, 162, 16), 'method': 'polygon'}, {'text': '4000mAh 2l喜 cl2l', 'confidence': 0.7750270366668701, 'polygon': [[85.0, 282.0], [284.0, 282.0], [284.0, 302.0], [85.0, 302.0]], 'bbox': (85, 282, 200, 21), 'method': 'polygon'}, {'text': 'PER', 'confidence': 0.5270567536354065, 'polygon': [[262.0, 344.0], [292.0, 344.0], [292.0, 358.0], [262.0, 358.0]], 'bbox': (262, 344, 31, 15), 'method': 'polygon'}, {'text': '丽是N / 百昆 SOS', 'confidence': 0.7120068073272705, 'polygon': [[83.0, 400.0], [284.0, 400.0], [284.0, 424.0], [83.0, 424.0]], 'bbox': (83, 400, 202, 25), 'method': 'polygon'}, {'text': '应易 品品 / E屁E 可', 'confidence': 0.6930113434791565, 'polygon': [[85.0, 462.0], [287.0, 462.0], [287.0, 482.0], [85.0, 482.0]], 'bbox': (85, 462, 203, 21), 'method': 'polygon'}] +[2025-10-24 15:36:22,038] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): 'BAOZOUTU' +[2025-10-24 15:36:22,039] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): '.R12' +[2025-10-24 15:36:22,039] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 60.6%): '百口2 三丽 昱' +[2025-10-24 15:36:22,039] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): '518' +[2025-10-24 15:36:22,039] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): 'MH' +[2025-10-24 15:36:22,039] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): 'MULTIBANDRADIO/BT/MUSICPLAYER' +[2025-10-24 15:36:22,039] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 77.5%): '4000mAh 2l喜 cl2l' +[2025-10-24 15:36:22,039] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): 'PER' +[2025-10-24 15:36:22,039] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 71.2%): '丽是N / 百昆 SOS' +[2025-10-24 15:36:22,039] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 69.3%): '应易 品品 / E屁E 可' +[2025-10-24 15:36:22,039] [LogListener] [DEBUG] [loggerModule.py:debug:275] 필터링 결과: 4/10개 (신뢰도 + & 중국어) +[2025-10-24 15:36:22,039] [LogListener] [DEBUG] [loggerModule.py:debug:275] filter_ocr_results: [{'text': '百口2 三丽 昱', 'confidence': 0.6060160994529724, 'polygon': [[92.0, 162.0], [287.0, 164.0], [287.0, 195.0], [92.0, 193.0]], 'bbox': (92, 162, 196, 34), 'method': 'polygon'}, {'text': '4000mAh 2l喜 cl2l', 'confidence': 0.7750270366668701, 'polygon': [[85.0, 282.0], [284.0, 282.0], [284.0, 302.0], [85.0, 302.0]], 'bbox': (85, 282, 200, 21), 'method': 'polygon'}, {'text': '丽是N / 百昆 SOS', 'confidence': 0.7120068073272705, 'polygon': [[83.0, 400.0], [284.0, 400.0], [284.0, 424.0], [83.0, 424.0]], 'bbox': (83, 400, 202, 25), 'method': 'polygon'}, {'text': '应易 品品 / E屁E 可', 'confidence': 0.6930113434791565, 'polygon': [[85.0, 462.0], [287.0, 462.0], [287.0, 482.0], [85.0, 482.0]], 'bbox': (85, 462, 203, 21), 'method': 'polygon'}] +[2025-10-24 15:36:22,040] [LogListener] [DEBUG] [loggerModule.py:debug:275] 중국어 텍스트 4개 필터링 완료 +[2025-10-24 15:36:22,040] [LogListener] [DEBUG] [loggerModule.py:debug:275] 한글 텍스트 0개 필터링 완료 +[2025-10-24 15:36:23,637] [LogListener] [DEBUG] [loggerModule.py:debug:275] translated_texts: ['바이커우 2 싼리 유', '4000mAh 2l 안녕하세요 cl2l', '리는 N이다 / 바이쿤 SOS', '잉이 핀핀 / E 방귀 E 캔'] +[2025-10-24 15:36:23,637] [LogListener] [DEBUG] [loggerModule.py:debug:275] 마스크 생성 완료 +[2025-10-24 15:36:23,637] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 4 OCR 권한 없음, 전체 번역 모드 +[2025-10-24 15:36:23,644] [LogListener] [DEBUG] [loggerModule.py:debug:275] [AUTO Inpaint] coverage=0.087, comps=4, min_center_dist=0.053 → request +[2025-10-24 15:36:23,644] [LogListener] [DEBUG] [loggerModule.py:debug:275] ocr_count: 4 +[2025-10-24 15:36:23,644] [LogListener] [DEBUG] [loggerModule.py:debug:275] is_member_valid: False +[2025-10-24 15:36:23,644] [LogListener] [DEBUG] [loggerModule.py:debug:275] MIGAN 인페인팅 시도 +[2025-10-24 15:36:23,651] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 입력 형태 - 이미지: (1, 3, 800, 800), 마스크: (1, 1, 800, 800) +[2025-10-24 15:36:23,865] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 출력 형태: (800, 800, 3), dtype: uint8 +[2025-10-24 15:36:23,866] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 추론 완료: 213.92 ms +[2025-10-24 15:36:23,873] [LogListener] [DEBUG] [loggerModule.py:debug:275] MIGAN 인페인팅 성공 +[2025-10-24 15:36:23,873] [LogListener] [DEBUG] [loggerModule.py:debug:275] 메모리 변화 [인페인팅]: 41682.6MB -> 41694.5MB (+11.9MB, +0.0%) - 방법: migan +[2025-10-24 15:36:23,873] [LogListener] [DEBUG] [loggerModule.py:debug:275] 인페인팅 완료 +[2025-10-24 15:36:23,889] [LogListener] [DEBUG] [loggerModule.py:debug:275] 텍스트 렌더링 완료 +[2025-10-24 15:36:23,889] [LogListener] [DEBUG] [loggerModule.py:debug:275] watermark_text: 이미지 저작권 보유 +[2025-10-24 15:36:23,889] [LogListener] [DEBUG] [loggerModule.py:debug:275] is_watermark_enabled: True +[2025-10-24 15:36:24,203] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 저장 완료 : C:\ProgramData\ImgWorker\work\translated_option_img_4.webp +[2025-10-24 15:36:24,204] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 4 번역 완료: C:\ProgramData\ImgWorker\work\translated_option_img_4.webp +[2025-10-24 15:36:24,204] [LogListener] [DEBUG] [loggerModule.py:debug:275] ⏱ 이미지 파이프라인 총 3125.1ms | download=0.0ms | ocr=653.6ms | translate=1598.7ms | mask=1598.7ms | inpaint=234.9ms(migan/DirectML) | render=16.0ms | save=281.3ms +[2025-10-24 15:36:24,205] [LogListener] [DEBUG] [loggerModule.py:debug:275] process_single_image 호출 완료 +[2025-10-24 15:36:24,205] [ResultListener] [WARNING] [loggerModule.py:warning:287] 워커 롤링 스케줄: reason=job-count-threshold +[2025-10-24 15:36:24,205] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 결과 반환 중: uid=6a2f4227-8cf2-4129-b990-94fffcba76b0 +[2025-10-24 15:36:24,206] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 결과 반환 완료: uid=6a2f4227-8cf2-4129-b990-94fffcba76b0 +[2025-10-24 15:36:24,206] [WorkerRoller] [WARNING] [loggerModule.py:warning:287] 워커 롤링 시작: job-count-threshold +[2025-10-24 15:36:24,206] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 15:36:24,206] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 26136) +[2025-10-24 15:36:24,220] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔥 작업 수신 성공 +[2025-10-24 15:36:24,257] [LogListener] [DEBUG] [loggerModule.py:debug:275] Shutdown signal 수신 → 종료 +[2025-10-24 15:36:24,258] [LogListener] [DEBUG] [loggerModule.py:debug:275] OCR 모듈 정리 완료 +[2025-10-24 15:36:24,258] [LogListener] [DEBUG] [loggerModule.py:debug:275] 마스크 모듈 정리 완료 +[2025-10-24 15:36:24,258] [LogListener] [DEBUG] [loggerModule.py:debug:275] 임시 폴더 삭제됨: C:\ProgramData\ImgWorker\work +[2025-10-24 15:36:24,258] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 프로세서 소멸 +[2025-10-24 15:36:24,680] [WorkerRoller] [INFO] [loggerModule.py:info:281] 워커 프로세스 기동: PID=6104 +[2025-10-24 15:36:25,220] [LogListener] [DEBUG] [loggerModule.py:debug:275] ImageWorker 프로세스 기동 (PID=6104, Name=ImageWorkerProcess) +[2025-10-24 15:36:25,220] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔧 ImageProcessor3 초기화 시작... +[2025-10-24 15:36:25,220] [LogListener] [DEBUG] [loggerModule.py:debug:275] tracemalloc 메모리 추적 시작 +[2025-10-24 15:36:25,227] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ DirectML 환경 준비 완료 (Windows DirectX 12 기반) +[2025-10-24 15:36:25,227] [LogListener] [DEBUG] [loggerModule.py:debug:275] === 🚀 DirectML GPU 상태 초기화 시작 🚀 === +[2025-10-24 15:36:25,227] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🎯 사용자 GPU 가속 요청: False +[2025-10-24 15:36:25,227] [LogListener] [DEBUG] [loggerModule.py:debug:275] 💻 현재 운영체제: Windows +[2025-10-24 15:36:25,228] [LogListener] [DEBUG] [loggerModule.py:debug:275] GPU 가속이 비활성화됨 (toggle_states['use_cuda'] = False) +[2025-10-24 15:36:25,228] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔒 안전한 CPU 모드로 모든 GPU 설정 강제 비활성화 +[2025-10-24 15:36:25,228] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔧 ImageProcessor3 GPU 상태 요약: +[2025-10-24 15:36:25,228] [LogListener] [DEBUG] [loggerModule.py:debug:275] - CUDA 사용 가능: False +[2025-10-24 15:36:25,228] [LogListener] [DEBUG] [loggerModule.py:debug:275] - toggle_states['use_cuda']: NOT_SET +[2025-10-24 15:36:25,228] [LogListener] [DEBUG] [loggerModule.py:debug:275] - GPU 하드웨어 정보: {} +[2025-10-24 15:36:25,228] [LogListener] [DEBUG] [loggerModule.py:debug:275] ImageProcessor3 Init toggle_states: {'TEMP_IMAGE_DIR': 'C:\\ProgramData\\ImgWorker\\work', 'output_image_format': 'webp', 'use_local_rembg': True, 'local_rembg_model_path': 'D:\\py\\img_worker\\modules\\rembg_models', 'local_model_name': 'birefnet-general-lite', 'optionIMGTrans_type': 'CPU', 'detail_IMGTrans_type': 'CPU', 'thumb_trans_type': 'CPU', 'migan_use_accel': True, 'migan_onnx_path': 'D:\\py\\img_worker\\modules\\migan_onnx\\migan_pipeline_v2.onnx', 'ocr_provider_override': 'auto', 'migan_provider_override': 'auto'} +[2025-10-24 15:36:25,228] [LogListener] [DEBUG] [loggerModule.py:debug:275] is_member_valid: False +[2025-10-24 15:36:25,229] [LogListener] [DEBUG] [loggerModule.py:debug:275] debug_images 디렉토리 이미 존재: D:\py\img_worker\modules\debug_images +[2025-10-24 15:36:25,229] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.font_path: D:\py\img_worker\modules\fonts\HakgyoansimDunggeunmisoTTFB.ttf +[2025-10-24 15:36:25,229] [LogListener] [DEBUG] [loggerModule.py:debug:275] toggle_states font_path: D:\py\img_worker\modules\fonts\HakgyoansimDunggeunmisoTTFB.ttf +[2025-10-24 15:36:25,229] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.TEMP_IMAGE_DIR: C:\ProgramData\ImgWorker\work +[2025-10-24 15:36:25,229] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.debugging_save_Dir: D:\py\img_worker\modules\debug_images +[2025-10-24 15:36:25,229] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.unwanted_texts: {} +[2025-10-24 15:36:25,229] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.inpaint_method: migan +[2025-10-24 15:36:25,229] [LogListener] [DEBUG] [loggerModule.py:debug:275] Image.MAX_IMAGE_PIXELS set to 20000000 +[2025-10-24 15:36:25,229] [LogListener] [DEBUG] [loggerModule.py:debug:275] ONNX 모델 타입 설정값: 자동 선택, GPU 정보: {} +[2025-10-24 15:36:25,229] [LogListener] [DEBUG] [loggerModule.py:debug:275] 자동 선택 모드: GPU 추천 모델 simp 사용 +[2025-10-24 15:36:25,229] [LogListener] [DEBUG] [loggerModule.py:debug:275] ONNX OCR 모델 타입 결정: simp (GPU: False) +[2025-10-24 15:36:25,230] [LogListener] [DEBUG] [loggerModule.py:debug:275] ONNX OCR 모듈 CPU 모드로 설정 +[2025-10-24 15:36:25,230] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🚀 ONNX TextSystem 초기화 시작 (CPU 모드) +[2025-10-24 15:36:25,230] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🚀 ONNX TextSystem 초기화 시작 (CPU 모드) +[2025-10-24 15:36:26,801] [LogListener] [DEBUG] [loggerModule.py:debug:275] 문자 사전 파일 UTF-8 확인 완료: D:\py\img_worker\modules\onnx_ocr_module\dict\ppocr_keys_v1.txt +[2025-10-24 15:36:26,886] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ ONNX TextSystem 초기화 완료 (CPU + SIMP (호환성) 모델) +[2025-10-24 15:36:26,886] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ ONNX TextSystem 초기화 완료 (CPU + SIMP 모델) +[2025-10-24 15:36:26,886] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ ONNX OCR 모듈 초기화 성공 (CPU 모드) +[2025-10-24 15:36:26,887] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ ONNX OCR 모듈 초기화 성공 +[2025-10-24 15:36:26,887] [LogListener] [DEBUG] [loggerModule.py:debug:275] 마스크 모듈 초기화 완료 +[2025-10-24 15:36:26,887] [LogListener] [DEBUG] [loggerModule.py:debug:275] MaskModule 초기화 성공 +[2025-10-24 15:36:26,887] [LogListener] [DEBUG] [loggerModule.py:debug:275] 텍스트 렌더링 모듈 초기화 완료 +[2025-10-24 15:36:26,887] [LogListener] [DEBUG] [loggerModule.py:debug:275] 기본 폰트: D:\py\img_worker\modules\fonts\HakgyoansimDunggeunmisoTTFB.ttf +[2025-10-24 15:36:26,887] [LogListener] [DEBUG] [loggerModule.py:debug:275] TextRenderingModule 초기화 성공 +[2025-10-24 15:36:26,887] [LogListener] [DEBUG] [loggerModule.py:debug:275] 폰트 로드 성공: D:\py\img_worker\modules\fonts\HakgyoansimDunggeunmisoTTFB.ttf +[2025-10-24 15:36:26,887] [LogListener] [DEBUG] [loggerModule.py:debug:275] PostImageManager 초기화 성공 +[2025-10-24 15:36:26,887] [LogListener] [DEBUG] [loggerModule.py:debug:275] Request_AI_Server GPU 상태: CUDA 사용 가능=False +[2025-10-24 15:36:26,887] [LogListener] [DEBUG] [loggerModule.py:debug:275] Request_AI_Server 초기화 성공 +[2025-10-24 15:36:26,887] [LogListener] [DEBUG] [loggerModule.py:debug:275] GoogleTranslate 초기화 성공 +[2025-10-24 15:36:26,888] [LogListener] [DEBUG] [loggerModule.py:debug:275] MIGAN CUDA 사용 불가 - CPU 모드로 설정 +[2025-10-24 15:36:26,888] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] GPU 관리자 전달: GPUManager, can_use_cuda: False +[2025-10-24 15:36:26,906] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] GPU 관리자 사용 가능: False +[2025-10-24 15:36:27,123] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 사용 가능한 providers: ['DmlExecutionProvider', 'CPUExecutionProvider'] +[2025-10-24 15:36:27,123] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] DirectML 가속 활성화 +[2025-10-24 15:36:27,123] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 최종 providers: [('DmlExecutionProvider', {}), ('CPUExecutionProvider', {})] +[2025-10-24 15:36:27,123] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] DirectML 가속 시도: [('DmlExecutionProvider', {}), ('CPUExecutionProvider', {})] +[2025-10-24 15:36:27,124] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] DirectML 가속 성공! 실제 providers: ['DmlExecutionProvider', 'CPUExecutionProvider'] +[2025-10-24 15:36:27,124] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 입력 0: image, 형태: ['batch_size', 3, 'height', 'width'], 타입: tensor(uint8) +[2025-10-24 15:36:27,124] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 입력 1: mask, 형태: ['batch_size', 1, 'height', 'width'], 타입: tensor(uint8) +[2025-10-24 15:36:27,124] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 출력 0: result, 형태: ['ScatterNDresult_dim_0', 3, 'ScatterNDresult_dim_2', 'ScatterNDresult_dim_3'], 타입: tensor(uint8) +[2025-10-24 15:36:27,124] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 세션 준비 완료. providers=['DmlExecutionProvider', 'CPUExecutionProvider'] +[2025-10-24 15:36:27,124] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] GPU 관리자 연결 완료: GPUManager +[2025-10-24 15:36:27,124] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 파이프라인 gpu_manager 속성: True, 값: +[2025-10-24 15:36:27,124] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 초기화 완료: gpu_manager 속성=True, 값= +[2025-10-24 15:36:27,124] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔰 OCR 모듈 Warm-up 시작... +[2025-10-24 15:36:27,125] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔍 ONNX OCR 감지 방식: polygon +[2025-10-24 15:36:27,129] [LogListener] [DEBUG] [loggerModule.py:debug:275] ⚡ ONNX OCR 추론 완료: 5.0ms +[2025-10-24 15:36:27,130] [LogListener] [DEBUG] [loggerModule.py:debug:275] 📊 세부 시간 - 감지: 4.0ms, 인식: 0.0ms, 분류: 0.0ms +[2025-10-24 15:36:27,130] [LogListener] [WARNING] [loggerModule.py:warning:287] ⚠️ ONNX OCR 결과가 비어있습니다 +[2025-10-24 15:36:27,133] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ OCR 모듈 Warm-up 성공 +[2025-10-24 15:36:27,133] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔰 ImageProcessor Warm‑up 완료 +[2025-10-24 15:36:27,134] [ResultListener] [INFO] [loggerModule.py:info:281] 워커 READY 수신 +[2025-10-24 15:36:27,134] [WorkerRoller] [INFO] [loggerModule.py:info:281] 버퍼 작업 재개: 1건 +[2025-10-24 15:36:27,134] [ResultListener] [INFO] [loggerModule.py:info:281] 워커 READY 수신 +[2025-10-24 15:36:27,134] [LogListener] [DEBUG] [loggerModule.py:debug:275] 📡 추가 READY 신호 전송 완료 +[2025-10-24 15:36:27,134] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 15:36:27,134] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 6104) +[2025-10-24 15:36:27,134] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔥 작업 수신 성공 +[2025-10-24 15:36:27,135] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🚀 작업 처리 시작: cmd=process_single_image, uid=f6f71c7d-70dd-454b-a330-d29f2b853503 +[2025-10-24 15:36:27,135] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 프로세서 toggle_states 업데이트 : {'TEMP_IMAGE_DIR': 'C:\\ProgramData\\ImgWorker\\work', 'output_image_format': 'webp', 'use_local_rembg': True, 'local_rembg_model_path': 'D:\\py\\img_worker\\modules\\rembg_models', 'local_model_name': 'birefnet-general-lite', 'optionIMGTrans_type': 'GPU', 'detail_IMGTrans_type': 'GPU', 'thumb_trans_type': 'GPU', 'migan_use_accel': True, 'migan_onnx_path': 'D:\\py\\img_worker\\modules\\migan_onnx\\migan_pipeline_v2.onnx', 'ocr_provider_override': 'auto', 'migan_provider_override': 'auto', 'font_type': '폰트5', 'unwanted_texts': ['가격설명', '무료배송', '보증', '사은품', '세일', '이벤트', '특가', '품절', '할인', '할인가'], 'is_member_valid': False, 'authenticated_by_admin': True} +[2025-10-24 15:36:27,135] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 실행 직전: cmd=process_single_image +[2025-10-24 15:36:27,135] [LogListener] [DEBUG] [loggerModule.py:debug:275] process_single_image 호출 직전 +[2025-10-24 15:36:27,137] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 번역시작 +[2025-10-24 15:36:27,497] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 5 처리 시작: C:\ProgramData\ImgWorker\incoming\dl_43b156fde6c2.jpg - 전체 번역 모드 +[2025-10-24 15:36:27,502] [LogListener] [DEBUG] [loggerModule.py:debug:275] 로컬 파일 경로 감지, 다운로드 생략: C:\ProgramData\ImgWorker\incoming\dl_43b156fde6c2.jpg +[2025-10-24 15:36:27,503] [LogListener] [DEBUG] [loggerModule.py:debug:275] 옵션 이미지는 스케일 처리 건너뛰기: option +[2025-10-24 15:36:27,503] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 5 로컬 저장위치(옵션 이미지 원본 유지): C:\ProgramData\ImgWorker\incoming\dl_43b156fde6c2.jpg +[2025-10-24 15:36:27,510] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔍 ONNX OCR 감지 방식: polygon +[2025-10-24 15:36:28,023] [LogListener] [DEBUG] [loggerModule.py:debug:275] ⚡ ONNX OCR 추론 완료: 511.7ms +[2025-10-24 15:36:28,024] [LogListener] [DEBUG] [loggerModule.py:debug:275] 📊 세부 시간 - 감지: 110.0ms, 인식: 378.7ms, 분류: 18.0ms +[2025-10-24 15:36:28,034] [LogListener] [DEBUG] [loggerModule.py:debug:275] 메모리 변화 [OCR 처리]: 41166.6MB -> 41331.7MB (+165.1MB, +0.4%) - 이미지 5 +[2025-10-24 15:36:28,046] [LogListener] [DEBUG] [loggerModule.py:debug:275] ocr_results: [{'text': 'BAOZOUTU', 'confidence': 0.9920599460601807, 'polygon': [[133.0, 72.0], [273.0, 72.0], [273.0, 86.0], [133.0, 86.0]], 'bbox': (133, 72, 141, 15), 'method': 'polygon'}, {'text': '..R12', 'confidence': 0.8152837753295898, 'polygon': [[574.0, 152.0], [615.0, 152.0], [615.0, 163.0], [574.0, 163.0]], 'bbox': (574, 152, 42, 12), 'method': 'polygon'}, {'text': '百口2 三丽 昱', 'confidence': 0.6027534008026123, 'polygon': [[92.0, 162.0], [287.0, 164.0], [287.0, 195.0], [92.0, 193.0]], 'bbox': (92, 162, 196, 34), 'method': 'polygon'}, {'text': '518', 'confidence': 0.984868586063385, 'polygon': [[487.0, 175.0], [607.0, 175.0], [607.0, 240.0], [487.0, 240.0]], 'bbox': (487, 175, 121, 66), 'method': 'polygon'}, {'text': 'MHX', 'confidence': 0.5008979439735413, 'polygon': [[607.0, 228.0], [622.0, 228.0], [622.0, 238.0], [607.0, 238.0]], 'bbox': (607, 228, 16, 11), 'method': 'polygon'}, {'text': 'MULTIBANDRADIO/BT/MUSICPLAYER', 'confidence': 0.9811127781867981, 'polygon': [[463.0, 243.0], [624.0, 244.0], [624.0, 258.0], [463.0, 257.0]], 'bbox': (463, 243, 162, 16), 'method': 'polygon'}, {'text': '4000mAh 2l喜 己l2l', 'confidence': 0.7726338505744934, 'polygon': [[85.0, 282.0], [284.0, 282.0], [284.0, 302.0], [85.0, 302.0]], 'bbox': (85, 282, 200, 21), 'method': 'polygon'}, {'text': 'PER', 'confidence': 0.5241203904151917, 'polygon': [[262.0, 344.0], [292.0, 344.0], [292.0, 358.0], [262.0, 358.0]], 'bbox': (262, 344, 31, 15), 'method': 'polygon'}, {'text': '丽是N / 百昆 SOS', 'confidence': 0.7132816910743713, 'polygon': [[83.0, 400.0], [284.0, 400.0], [284.0, 424.0], [83.0, 424.0]], 'bbox': (83, 400, 202, 25), 'method': 'polygon'}, {'text': '应易 品品 / E屁E 可', 'confidence': 0.6868884563446045, 'polygon': [[85.0, 462.0], [287.0, 462.0], [287.0, 482.0], [85.0, 482.0]], 'bbox': (85, 462, 203, 21), 'method': 'polygon'}] +[2025-10-24 15:36:28,061] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): 'BAOZOUTU' +[2025-10-24 15:36:28,061] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): '..R12' +[2025-10-24 15:36:28,061] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 60.3%): '百口2 三丽 昱' +[2025-10-24 15:36:28,061] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): '518' +[2025-10-24 15:36:28,062] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): 'MHX' +[2025-10-24 15:36:28,062] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): 'MULTIBANDRADIO/BT/MUSICPLAYER' +[2025-10-24 15:36:28,062] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 77.3%): '4000mAh 2l喜 己l2l' +[2025-10-24 15:36:28,062] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): 'PER' +[2025-10-24 15:36:28,062] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 71.3%): '丽是N / 百昆 SOS' +[2025-10-24 15:36:28,062] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 68.7%): '应易 品品 / E屁E 可' +[2025-10-24 15:36:28,062] [LogListener] [DEBUG] [loggerModule.py:debug:275] 필터링 결과: 4/10개 (신뢰도 + & 중국어) +[2025-10-24 15:36:28,062] [LogListener] [DEBUG] [loggerModule.py:debug:275] filter_ocr_results: [{'text': '百口2 三丽 昱', 'confidence': 0.6027534008026123, 'polygon': [[92.0, 162.0], [287.0, 164.0], [287.0, 195.0], [92.0, 193.0]], 'bbox': (92, 162, 196, 34), 'method': 'polygon'}, {'text': '4000mAh 2l喜 己l2l', 'confidence': 0.7726338505744934, 'polygon': [[85.0, 282.0], [284.0, 282.0], [284.0, 302.0], [85.0, 302.0]], 'bbox': (85, 282, 200, 21), 'method': 'polygon'}, {'text': '丽是N / 百昆 SOS', 'confidence': 0.7132816910743713, 'polygon': [[83.0, 400.0], [284.0, 400.0], [284.0, 424.0], [83.0, 424.0]], 'bbox': (83, 400, 202, 25), 'method': 'polygon'}, {'text': '应易 品品 / E屁E 可', 'confidence': 0.6868884563446045, 'polygon': [[85.0, 462.0], [287.0, 462.0], [287.0, 482.0], [85.0, 482.0]], 'bbox': (85, 462, 203, 21), 'method': 'polygon'}] +[2025-10-24 15:36:28,093] [LogListener] [DEBUG] [loggerModule.py:debug:275] 중국어 텍스트 4개 필터링 완료 +[2025-10-24 15:36:28,098] [LogListener] [DEBUG] [loggerModule.py:debug:275] 한글 텍스트 0개 필터링 완료 +[2025-10-24 15:36:29,993] [LogListener] [DEBUG] [loggerModule.py:debug:275] translated_texts: ['바이커우 2 싼리 유', '4000mAh 2l 하이지 l2l', '리는 N이다 / 바이쿤SOS', '잉이 핀핀 / E 방귀 E 캔'] +[2025-10-24 15:36:29,993] [LogListener] [DEBUG] [loggerModule.py:debug:275] 마스크 생성 완료 +[2025-10-24 15:36:29,993] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 5 OCR 권한 없음, 전체 번역 모드 +[2025-10-24 15:36:29,999] [LogListener] [DEBUG] [loggerModule.py:debug:275] [AUTO Inpaint] coverage=0.087, comps=4, min_center_dist=0.053 → request +[2025-10-24 15:36:29,999] [LogListener] [DEBUG] [loggerModule.py:debug:275] ocr_count: 4 +[2025-10-24 15:36:29,999] [LogListener] [DEBUG] [loggerModule.py:debug:275] is_member_valid: False +[2025-10-24 15:36:29,999] [LogListener] [DEBUG] [loggerModule.py:debug:275] MIGAN 인페인팅 시도 +[2025-10-24 15:36:30,006] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 입력 형태 - 이미지: (1, 3, 800, 800), 마스크: (1, 1, 800, 800) +[2025-10-24 15:36:30,490] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 출력 형태: (800, 800, 3), dtype: uint8 +[2025-10-24 15:36:30,490] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 추론 완료: 484.12 ms +[2025-10-24 15:36:30,498] [LogListener] [DEBUG] [loggerModule.py:debug:275] MIGAN 인페인팅 성공 +[2025-10-24 15:36:30,498] [LogListener] [DEBUG] [loggerModule.py:debug:275] 메모리 변화 [인페인팅]: 41205.8MB -> 41358.1MB (+152.3MB, +0.4%) - 방법: migan +[2025-10-24 15:36:30,498] [LogListener] [DEBUG] [loggerModule.py:debug:275] 인페인팅 완료 +[2025-10-24 15:36:30,523] [LogListener] [DEBUG] [loggerModule.py:debug:275] 텍스트 렌더링 완료 +[2025-10-24 15:36:30,524] [LogListener] [DEBUG] [loggerModule.py:debug:275] watermark_text: 이미지 저작권 보유 +[2025-10-24 15:36:30,524] [LogListener] [DEBUG] [loggerModule.py:debug:275] is_watermark_enabled: True +[2025-10-24 15:36:30,838] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 저장 완료 : C:\ProgramData\ImgWorker\work\translated_option_img_5.webp +[2025-10-24 15:36:30,839] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 5 번역 완료: C:\ProgramData\ImgWorker\work\translated_option_img_5.webp +[2025-10-24 15:36:30,839] [LogListener] [DEBUG] [loggerModule.py:debug:275] ⏱ 이미지 파이프라인 총 3703.1ms | download=0.0ms | ocr=526.7ms | translate=1930.7ms | mask=1930.7ms | inpaint=504.5ms(migan/DirectML) | render=25.2ms | save=281.8ms +[2025-10-24 15:36:30,840] [LogListener] [DEBUG] [loggerModule.py:debug:275] process_single_image 호출 완료 +[2025-10-24 15:36:30,841] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 결과 반환 중: uid=f6f71c7d-70dd-454b-a330-d29f2b853503 +[2025-10-24 15:36:30,841] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 결과 반환 완료: uid=f6f71c7d-70dd-454b-a330-d29f2b853503 +[2025-10-24 15:36:30,841] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 15:36:30,841] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 6104) +[2025-10-24 15:36:32,546] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔥 작업 수신 성공 +[2025-10-24 15:36:32,547] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🚀 작업 처리 시작: cmd=process_single_image, uid=d7f8a266-94c6-4e28-a1d0-135fd406290b +[2025-10-24 15:36:32,547] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 프로세서 toggle_states 업데이트 : {'TEMP_IMAGE_DIR': 'C:\\ProgramData\\ImgWorker\\work', 'output_image_format': 'webp', 'use_local_rembg': True, 'local_rembg_model_path': 'D:\\py\\img_worker\\modules\\rembg_models', 'local_model_name': 'birefnet-general-lite', 'optionIMGTrans_type': 'GPU', 'detail_IMGTrans_type': 'GPU', 'thumb_trans_type': 'GPU', 'migan_use_accel': True, 'migan_onnx_path': 'D:\\py\\img_worker\\modules\\migan_onnx\\migan_pipeline_v2.onnx', 'ocr_provider_override': 'auto', 'migan_provider_override': 'auto', 'font_type': '폰트5', 'unwanted_texts': ['가격설명', '무료배송', '보증', '사은품', '세일', '이벤트', '특가', '품절', '할인', '할인가'], 'is_member_valid': False, 'authenticated_by_admin': True} +[2025-10-24 15:36:32,547] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 실행 직전: cmd=process_single_image +[2025-10-24 15:36:32,547] [LogListener] [DEBUG] [loggerModule.py:debug:275] process_single_image 호출 직전 +[2025-10-24 15:36:32,548] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 번역시작 +[2025-10-24 15:36:32,730] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 6 처리 시작: C:\ProgramData\ImgWorker\incoming\dl_f89764827f6a.jpg - 전체 번역 모드 +[2025-10-24 15:36:32,737] [LogListener] [DEBUG] [loggerModule.py:debug:275] 로컬 파일 경로 감지, 다운로드 생략: C:\ProgramData\ImgWorker\incoming\dl_f89764827f6a.jpg +[2025-10-24 15:36:32,737] [LogListener] [DEBUG] [loggerModule.py:debug:275] 옵션 이미지는 스케일 처리 건너뛰기: option +[2025-10-24 15:36:32,737] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 6 로컬 저장위치(옵션 이미지 원본 유지): C:\ProgramData\ImgWorker\incoming\dl_f89764827f6a.jpg +[2025-10-24 15:36:32,746] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔍 ONNX OCR 감지 방식: polygon +[2025-10-24 15:36:33,358] [LogListener] [DEBUG] [loggerModule.py:debug:275] ⚡ ONNX OCR 추론 완료: 610.5ms +[2025-10-24 15:36:33,373] [LogListener] [DEBUG] [loggerModule.py:debug:275] 📊 세부 시간 - 감지: 118.1ms, 인식: 467.9ms, 분류: 19.5ms +[2025-10-24 15:36:33,373] [LogListener] [DEBUG] [loggerModule.py:debug:275] 메모리 변화 [OCR 처리]: 41355.4MB -> 41367.6MB (+12.2MB, +0.0%) - 이미지 6 +[2025-10-24 15:36:33,373] [LogListener] [DEBUG] [loggerModule.py:debug:275] ocr_results: [{'text': 'BAOZOUTU', 'confidence': 0.9932674765586853, 'polygon': [[134.0, 73.0], [274.0, 73.0], [274.0, 87.0], [134.0, 87.0]], 'bbox': (134, 73, 141, 15), 'method': 'polygon'}, {'text': '..R12.', 'confidence': 0.7745210528373718, 'polygon': [[532.0, 151.0], [618.0, 151.0], [618.0, 164.0], [532.0, 164.0]], 'bbox': (532, 151, 87, 14), 'method': 'polygon'}, {'text': '百口杞 三丽 昱', 'confidence': 0.6055483222007751, 'polygon': [[92.0, 161.0], [287.0, 164.0], [287.0, 195.0], [92.0, 192.0]], 'bbox': (92, 161, 196, 35), 'method': 'polygon'}, {'text': '518', 'confidence': 0.9850950241088867, 'polygon': [[488.0, 174.0], [607.0, 174.0], [607.0, 240.0], [488.0, 240.0]], 'bbox': (488, 174, 120, 67), 'method': 'polygon'}, {'text': 'UH', 'confidence': 0.7314228415489197, 'polygon': [[607.0, 228.0], [622.0, 228.0], [622.0, 238.0], [607.0, 238.0]], 'bbox': (607, 228, 16, 11), 'method': 'polygon'}, {'text': 'MULTIBANDRADIO/BT/MUSICPLAYER', 'confidence': 0.9863682985305786, 'polygon': [[464.0, 244.0], [624.0, 244.0], [624.0, 257.0], [464.0, 257.0]], 'bbox': (464, 244, 161, 14), 'method': 'polygon'}, {'text': '4000mAh 2l UHc12l', 'confidence': 0.7911429405212402, 'polygon': [[84.0, 281.0], [282.0, 281.0], [282.0, 301.0], [84.0, 301.0]], 'bbox': (84, 281, 199, 21), 'method': 'polygon'}, {'text': 'PER', 'confidence': 0.5149660110473633, 'polygon': [[261.0, 344.0], [291.0, 344.0], [291.0, 358.0], [261.0, 358.0]], 'bbox': (261, 344, 31, 15), 'method': 'polygon'}, {'text': '丽是N / 百昆 SOS', 'confidence': 0.6998953819274902, 'polygon': [[83.0, 400.0], [284.0, 400.0], [284.0, 424.0], [83.0, 424.0]], 'bbox': (83, 400, 202, 25), 'method': 'polygon'}, {'text': '应易 品品 / E屁E 可', 'confidence': 0.6925998330116272, 'polygon': [[85.0, 462.0], [287.0, 462.0], [287.0, 482.0], [85.0, 482.0]], 'bbox': (85, 462, 203, 21), 'method': 'polygon'}] +[2025-10-24 15:36:33,374] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): 'BAOZOUTU' +[2025-10-24 15:36:33,374] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): '..R12.' +[2025-10-24 15:36:33,374] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 60.6%): '百口杞 三丽 昱' +[2025-10-24 15:36:33,374] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): '518' +[2025-10-24 15:36:33,374] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): 'UH' +[2025-10-24 15:36:33,374] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): 'MULTIBANDRADIO/BT/MUSICPLAYER' +[2025-10-24 15:36:33,374] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): '4000mAh 2l UHc12l' +[2025-10-24 15:36:33,374] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): 'PER' +[2025-10-24 15:36:33,375] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 70.0%): '丽是N / 百昆 SOS' +[2025-10-24 15:36:33,375] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 69.3%): '应易 品品 / E屁E 可' +[2025-10-24 15:36:33,375] [LogListener] [DEBUG] [loggerModule.py:debug:275] 필터링 결과: 3/10개 (신뢰도 + & 중국어) +[2025-10-24 15:36:33,375] [LogListener] [DEBUG] [loggerModule.py:debug:275] filter_ocr_results: [{'text': '百口杞 三丽 昱', 'confidence': 0.6055483222007751, 'polygon': [[92.0, 161.0], [287.0, 164.0], [287.0, 195.0], [92.0, 192.0]], 'bbox': (92, 161, 196, 35), 'method': 'polygon'}, {'text': '丽是N / 百昆 SOS', 'confidence': 0.6998953819274902, 'polygon': [[83.0, 400.0], [284.0, 400.0], [284.0, 424.0], [83.0, 424.0]], 'bbox': (83, 400, 202, 25), 'method': 'polygon'}, {'text': '应易 品品 / E屁E 可', 'confidence': 0.6925998330116272, 'polygon': [[85.0, 462.0], [287.0, 462.0], [287.0, 482.0], [85.0, 482.0]], 'bbox': (85, 462, 203, 21), 'method': 'polygon'}] +[2025-10-24 15:36:33,375] [LogListener] [DEBUG] [loggerModule.py:debug:275] 중국어 텍스트 3개 필터링 완료 +[2025-10-24 15:36:33,375] [LogListener] [DEBUG] [loggerModule.py:debug:275] 한글 텍스트 0개 필터링 완료 +[2025-10-24 15:36:35,757] [LogListener] [DEBUG] [loggerModule.py:debug:275] translated_texts: ['바이커우치 싼리 유', '리는 N이다 / 바이쿤 SOS', '잉이 핀핀 / E 방귀 E 캔'] +[2025-10-24 15:36:35,760] [LogListener] [DEBUG] [loggerModule.py:debug:275] 마스크 생성 완료 +[2025-10-24 15:36:35,760] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 6 OCR 권한 없음, 전체 번역 모드 +[2025-10-24 15:36:35,767] [LogListener] [DEBUG] [loggerModule.py:debug:275] [AUTO Inpaint] coverage=0.066, comps=3, min_center_dist=0.053 → request +[2025-10-24 15:36:35,767] [LogListener] [DEBUG] [loggerModule.py:debug:275] ocr_count: 3 +[2025-10-24 15:36:35,768] [LogListener] [DEBUG] [loggerModule.py:debug:275] is_member_valid: False +[2025-10-24 15:36:35,768] [LogListener] [DEBUG] [loggerModule.py:debug:275] MIGAN 인페인팅 시도 +[2025-10-24 15:36:35,777] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 입력 형태 - 이미지: (1, 3, 800, 800), 마스크: (1, 1, 800, 800) +[2025-10-24 15:36:36,039] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 출력 형태: (800, 800, 3), dtype: uint8 +[2025-10-24 15:36:36,039] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 추론 완료: 263.18 ms +[2025-10-24 15:36:36,048] [LogListener] [DEBUG] [loggerModule.py:debug:275] MIGAN 인페인팅 성공 +[2025-10-24 15:36:36,048] [LogListener] [DEBUG] [loggerModule.py:debug:275] 메모리 변화 [인페인팅]: 41453.3MB -> 41431.4MB (-22.0MB, -0.1%) - 방법: migan +[2025-10-24 15:36:36,048] [LogListener] [DEBUG] [loggerModule.py:debug:275] 인페인팅 완료 +[2025-10-24 15:36:36,059] [LogListener] [DEBUG] [loggerModule.py:debug:275] 텍스트 렌더링 완료 +[2025-10-24 15:36:36,059] [LogListener] [DEBUG] [loggerModule.py:debug:275] watermark_text: 이미지 저작권 보유 +[2025-10-24 15:36:36,060] [LogListener] [DEBUG] [loggerModule.py:debug:275] is_watermark_enabled: True +[2025-10-24 15:36:36,372] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 저장 완료 : C:\ProgramData\ImgWorker\work\translated_option_img_6.webp +[2025-10-24 15:36:36,373] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 6 번역 완료: C:\ProgramData\ImgWorker\work\translated_option_img_6.webp +[2025-10-24 15:36:36,373] [LogListener] [DEBUG] [loggerModule.py:debug:275] ⏱ 이미지 파이프라인 총 3824.3ms | download=0.0ms | ocr=627.5ms | translate=2383.6ms | mask=2383.6ms | inpaint=289.5ms(migan/DirectML) | render=10.8ms | save=278.2ms +[2025-10-24 15:36:36,374] [LogListener] [DEBUG] [loggerModule.py:debug:275] process_single_image 호출 완료 +[2025-10-24 15:36:36,374] [ResultListener] [WARNING] [loggerModule.py:warning:287] 워커 롤링 스케줄: reason=job-count-threshold +[2025-10-24 15:36:36,374] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 결과 반환 중: uid=d7f8a266-94c6-4e28-a1d0-135fd406290b +[2025-10-24 15:36:36,374] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 결과 반환 완료: uid=d7f8a266-94c6-4e28-a1d0-135fd406290b +[2025-10-24 15:36:36,374] [WorkerRoller] [WARNING] [loggerModule.py:warning:287] 워커 롤링 시작: job-count-threshold +[2025-10-24 15:36:36,374] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 15:36:36,374] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 6104) +[2025-10-24 15:36:36,388] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔥 작업 수신 성공 +[2025-10-24 15:36:36,431] [LogListener] [DEBUG] [loggerModule.py:debug:275] Shutdown signal 수신 → 종료 +[2025-10-24 15:36:36,431] [LogListener] [DEBUG] [loggerModule.py:debug:275] OCR 모듈 정리 완료 +[2025-10-24 15:36:36,431] [LogListener] [DEBUG] [loggerModule.py:debug:275] 마스크 모듈 정리 완료 +[2025-10-24 15:36:36,431] [LogListener] [DEBUG] [loggerModule.py:debug:275] 임시 폴더 삭제됨: C:\ProgramData\ImgWorker\work +[2025-10-24 15:36:36,432] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 프로세서 소멸 +[2025-10-24 15:36:36,882] [WorkerRoller] [INFO] [loggerModule.py:info:281] 워커 프로세스 기동: PID=28344 +[2025-10-24 15:36:37,446] [LogListener] [DEBUG] [loggerModule.py:debug:275] ImageWorker 프로세스 기동 (PID=28344, Name=ImageWorkerProcess) +[2025-10-24 15:36:37,447] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔧 ImageProcessor3 초기화 시작... +[2025-10-24 15:36:37,447] [LogListener] [DEBUG] [loggerModule.py:debug:275] tracemalloc 메모리 추적 시작 +[2025-10-24 15:36:37,454] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ DirectML 환경 준비 완료 (Windows DirectX 12 기반) +[2025-10-24 15:36:37,454] [LogListener] [DEBUG] [loggerModule.py:debug:275] === 🚀 DirectML GPU 상태 초기화 시작 🚀 === +[2025-10-24 15:36:37,455] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🎯 사용자 GPU 가속 요청: False +[2025-10-24 15:36:37,455] [LogListener] [DEBUG] [loggerModule.py:debug:275] 💻 현재 운영체제: Windows +[2025-10-24 15:36:37,455] [LogListener] [DEBUG] [loggerModule.py:debug:275] GPU 가속이 비활성화됨 (toggle_states['use_cuda'] = False) +[2025-10-24 15:36:37,455] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔒 안전한 CPU 모드로 모든 GPU 설정 강제 비활성화 +[2025-10-24 15:36:37,455] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔧 ImageProcessor3 GPU 상태 요약: +[2025-10-24 15:36:37,455] [LogListener] [DEBUG] [loggerModule.py:debug:275] - CUDA 사용 가능: False +[2025-10-24 15:36:37,455] [LogListener] [DEBUG] [loggerModule.py:debug:275] - toggle_states['use_cuda']: NOT_SET +[2025-10-24 15:36:37,455] [LogListener] [DEBUG] [loggerModule.py:debug:275] - GPU 하드웨어 정보: {} +[2025-10-24 15:36:37,455] [LogListener] [DEBUG] [loggerModule.py:debug:275] ImageProcessor3 Init toggle_states: {'TEMP_IMAGE_DIR': 'C:\\ProgramData\\ImgWorker\\work', 'output_image_format': 'webp', 'use_local_rembg': True, 'local_rembg_model_path': 'D:\\py\\img_worker\\modules\\rembg_models', 'local_model_name': 'birefnet-general-lite', 'optionIMGTrans_type': 'CPU', 'detail_IMGTrans_type': 'CPU', 'thumb_trans_type': 'CPU', 'migan_use_accel': True, 'migan_onnx_path': 'D:\\py\\img_worker\\modules\\migan_onnx\\migan_pipeline_v2.onnx', 'ocr_provider_override': 'auto', 'migan_provider_override': 'auto'} +[2025-10-24 15:36:37,455] [LogListener] [DEBUG] [loggerModule.py:debug:275] is_member_valid: False +[2025-10-24 15:36:37,456] [LogListener] [DEBUG] [loggerModule.py:debug:275] debug_images 디렉토리 이미 존재: D:\py\img_worker\modules\debug_images +[2025-10-24 15:36:37,456] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.font_path: D:\py\img_worker\modules\fonts\HakgyoansimDunggeunmisoTTFB.ttf +[2025-10-24 15:36:37,456] [LogListener] [DEBUG] [loggerModule.py:debug:275] toggle_states font_path: D:\py\img_worker\modules\fonts\HakgyoansimDunggeunmisoTTFB.ttf +[2025-10-24 15:36:37,456] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.TEMP_IMAGE_DIR: C:\ProgramData\ImgWorker\work +[2025-10-24 15:36:37,456] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.debugging_save_Dir: D:\py\img_worker\modules\debug_images +[2025-10-24 15:36:37,456] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.unwanted_texts: {} +[2025-10-24 15:36:37,456] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.inpaint_method: migan +[2025-10-24 15:36:37,456] [LogListener] [DEBUG] [loggerModule.py:debug:275] Image.MAX_IMAGE_PIXELS set to 20000000 +[2025-10-24 15:36:37,456] [LogListener] [DEBUG] [loggerModule.py:debug:275] ONNX 모델 타입 설정값: 자동 선택, GPU 정보: {} +[2025-10-24 15:36:37,456] [LogListener] [DEBUG] [loggerModule.py:debug:275] 자동 선택 모드: GPU 추천 모델 simp 사용 +[2025-10-24 15:36:37,456] [LogListener] [DEBUG] [loggerModule.py:debug:275] ONNX OCR 모델 타입 결정: simp (GPU: False) +[2025-10-24 15:36:37,456] [LogListener] [DEBUG] [loggerModule.py:debug:275] ONNX OCR 모듈 CPU 모드로 설정 +[2025-10-24 15:36:37,456] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🚀 ONNX TextSystem 초기화 시작 (CPU 모드) +[2025-10-24 15:36:37,456] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🚀 ONNX TextSystem 초기화 시작 (CPU 모드) +[2025-10-24 15:36:39,015] [LogListener] [DEBUG] [loggerModule.py:debug:275] 문자 사전 파일 UTF-8 확인 완료: D:\py\img_worker\modules\onnx_ocr_module\dict\ppocr_keys_v1.txt +[2025-10-24 15:36:39,099] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ ONNX TextSystem 초기화 완료 (CPU + SIMP (호환성) 모델) +[2025-10-24 15:36:39,100] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ ONNX TextSystem 초기화 완료 (CPU + SIMP 모델) +[2025-10-24 15:36:39,100] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ ONNX OCR 모듈 초기화 성공 (CPU 모드) +[2025-10-24 15:36:39,100] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ ONNX OCR 모듈 초기화 성공 +[2025-10-24 15:36:39,101] [LogListener] [DEBUG] [loggerModule.py:debug:275] 마스크 모듈 초기화 완료 +[2025-10-24 15:36:39,101] [LogListener] [DEBUG] [loggerModule.py:debug:275] MaskModule 초기화 성공 +[2025-10-24 15:36:39,101] [LogListener] [DEBUG] [loggerModule.py:debug:275] 텍스트 렌더링 모듈 초기화 완료 +[2025-10-24 15:36:39,101] [LogListener] [DEBUG] [loggerModule.py:debug:275] 기본 폰트: D:\py\img_worker\modules\fonts\HakgyoansimDunggeunmisoTTFB.ttf +[2025-10-24 15:36:39,101] [LogListener] [DEBUG] [loggerModule.py:debug:275] TextRenderingModule 초기화 성공 +[2025-10-24 15:36:39,101] [LogListener] [DEBUG] [loggerModule.py:debug:275] 폰트 로드 성공: D:\py\img_worker\modules\fonts\HakgyoansimDunggeunmisoTTFB.ttf +[2025-10-24 15:36:39,101] [LogListener] [DEBUG] [loggerModule.py:debug:275] PostImageManager 초기화 성공 +[2025-10-24 15:36:39,101] [LogListener] [DEBUG] [loggerModule.py:debug:275] Request_AI_Server GPU 상태: CUDA 사용 가능=False +[2025-10-24 15:36:39,101] [LogListener] [DEBUG] [loggerModule.py:debug:275] Request_AI_Server 초기화 성공 +[2025-10-24 15:36:39,101] [LogListener] [DEBUG] [loggerModule.py:debug:275] GoogleTranslate 초기화 성공 +[2025-10-24 15:36:39,102] [LogListener] [DEBUG] [loggerModule.py:debug:275] MIGAN CUDA 사용 불가 - CPU 모드로 설정 +[2025-10-24 15:36:39,102] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] GPU 관리자 전달: GPUManager, can_use_cuda: False +[2025-10-24 15:36:39,122] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] GPU 관리자 사용 가능: False +[2025-10-24 15:36:39,333] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 사용 가능한 providers: ['DmlExecutionProvider', 'CPUExecutionProvider'] +[2025-10-24 15:36:39,334] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] DirectML 가속 활성화 +[2025-10-24 15:36:39,334] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 최종 providers: [('DmlExecutionProvider', {}), ('CPUExecutionProvider', {})] +[2025-10-24 15:36:39,334] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] DirectML 가속 시도: [('DmlExecutionProvider', {}), ('CPUExecutionProvider', {})] +[2025-10-24 15:36:39,334] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] DirectML 가속 성공! 실제 providers: ['DmlExecutionProvider', 'CPUExecutionProvider'] +[2025-10-24 15:36:39,334] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 입력 0: image, 형태: ['batch_size', 3, 'height', 'width'], 타입: tensor(uint8) +[2025-10-24 15:36:39,334] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 입력 1: mask, 형태: ['batch_size', 1, 'height', 'width'], 타입: tensor(uint8) +[2025-10-24 15:36:39,334] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 출력 0: result, 형태: ['ScatterNDresult_dim_0', 3, 'ScatterNDresult_dim_2', 'ScatterNDresult_dim_3'], 타입: tensor(uint8) +[2025-10-24 15:36:39,334] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 세션 준비 완료. providers=['DmlExecutionProvider', 'CPUExecutionProvider'] +[2025-10-24 15:36:39,334] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] GPU 관리자 연결 완료: GPUManager +[2025-10-24 15:36:39,334] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 파이프라인 gpu_manager 속성: True, 값: +[2025-10-24 15:36:39,335] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 초기화 완료: gpu_manager 속성=True, 값= +[2025-10-24 15:36:39,335] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔰 OCR 모듈 Warm-up 시작... +[2025-10-24 15:36:39,335] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔍 ONNX OCR 감지 방식: polygon +[2025-10-24 15:36:39,339] [LogListener] [DEBUG] [loggerModule.py:debug:275] ⚡ ONNX OCR 추론 완료: 4.0ms +[2025-10-24 15:36:39,339] [LogListener] [DEBUG] [loggerModule.py:debug:275] 📊 세부 시간 - 감지: 3.0ms, 인식: 0.0ms, 분류: 0.0ms +[2025-10-24 15:36:39,339] [LogListener] [WARNING] [loggerModule.py:warning:287] ⚠️ ONNX OCR 결과가 비어있습니다 +[2025-10-24 15:36:39,343] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ OCR 모듈 Warm-up 성공 +[2025-10-24 15:36:39,343] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔰 ImageProcessor Warm‑up 완료 +[2025-10-24 15:36:39,343] [ResultListener] [INFO] [loggerModule.py:info:281] 워커 READY 수신 +[2025-10-24 15:36:39,343] [WorkerRoller] [INFO] [loggerModule.py:info:281] 버퍼 작업 재개: 1건 +[2025-10-24 15:36:39,344] [ResultListener] [INFO] [loggerModule.py:info:281] 워커 READY 수신 +[2025-10-24 15:36:39,344] [LogListener] [DEBUG] [loggerModule.py:debug:275] 📡 추가 READY 신호 전송 완료 +[2025-10-24 15:36:39,344] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 15:36:39,344] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 28344) +[2025-10-24 15:36:39,344] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔥 작업 수신 성공 +[2025-10-24 15:36:39,345] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🚀 작업 처리 시작: cmd=process_single_image, uid=7f6aee59-83c9-48b0-bdc7-bda1ba530aa3 +[2025-10-24 15:36:39,345] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 프로세서 toggle_states 업데이트 : {'TEMP_IMAGE_DIR': 'C:\\ProgramData\\ImgWorker\\work', 'output_image_format': 'webp', 'use_local_rembg': True, 'local_rembg_model_path': 'D:\\py\\img_worker\\modules\\rembg_models', 'local_model_name': 'birefnet-general-lite', 'optionIMGTrans_type': 'GPU', 'detail_IMGTrans_type': 'GPU', 'thumb_trans_type': 'GPU', 'migan_use_accel': True, 'migan_onnx_path': 'D:\\py\\img_worker\\modules\\migan_onnx\\migan_pipeline_v2.onnx', 'ocr_provider_override': 'auto', 'migan_provider_override': 'auto', 'font_type': '폰트5', 'unwanted_texts': ['가격설명', '무료배송', '보증', '사은품', '세일', '이벤트', '특가', '품절', '할인', '할인가'], 'is_member_valid': False, 'authenticated_by_admin': True} +[2025-10-24 15:36:39,345] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 실행 직전: cmd=process_single_image +[2025-10-24 15:36:39,345] [LogListener] [DEBUG] [loggerModule.py:debug:275] process_single_image 호출 직전 +[2025-10-24 15:36:39,347] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 번역시작 +[2025-10-24 15:36:39,651] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 7 처리 시작: C:\ProgramData\ImgWorker\incoming\dl_cda206fabb3c.jpg - 전체 번역 모드 +[2025-10-24 15:36:39,656] [LogListener] [DEBUG] [loggerModule.py:debug:275] 로컬 파일 경로 감지, 다운로드 생략: C:\ProgramData\ImgWorker\incoming\dl_cda206fabb3c.jpg +[2025-10-24 15:36:39,656] [LogListener] [DEBUG] [loggerModule.py:debug:275] 옵션 이미지는 스케일 처리 건너뛰기: option +[2025-10-24 15:36:39,657] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 7 로컬 저장위치(옵션 이미지 원본 유지): C:\ProgramData\ImgWorker\incoming\dl_cda206fabb3c.jpg +[2025-10-24 15:36:39,664] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔍 ONNX OCR 감지 방식: polygon +[2025-10-24 15:36:40,328] [LogListener] [DEBUG] [loggerModule.py:debug:275] ⚡ ONNX OCR 추론 완료: 663.9ms +[2025-10-24 15:36:40,339] [LogListener] [DEBUG] [loggerModule.py:debug:275] 📊 세부 시간 - 감지: 109.0ms, 인식: 528.4ms, 분류: 19.0ms +[2025-10-24 15:36:40,366] [LogListener] [DEBUG] [loggerModule.py:debug:275] 메모리 변화 [OCR 처리]: 40976.6MB -> 41239.6MB (+262.9MB, +0.6%) - 이미지 7 +[2025-10-24 15:36:40,366] [LogListener] [DEBUG] [loggerModule.py:debug:275] ocr_results: [{'text': 'BAO ZOUTU', 'confidence': 0.8897886276245117, 'polygon': [[130.0, 70.0], [274.0, 64.0], [275.0, 87.0], [131.0, 93.0]], 'bbox': (130, 64, 146, 30), 'method': 'polygon'}, {'text': '..R12', 'confidence': 0.8127950429916382, 'polygon': [[575.0, 152.0], [615.0, 152.0], [615.0, 163.0], [575.0, 163.0]], 'bbox': (575, 152, 41, 12), 'method': 'polygon'}, {'text': '518', 'confidence': 0.9848101139068604, 'polygon': [[488.0, 174.0], [607.0, 174.0], [607.0, 240.0], [488.0, 240.0]], 'bbox': (488, 174, 120, 67), 'method': 'polygon'}, {'text': 'MULTIBANDRADIO/BT/MUSICPLAYER', 'confidence': 0.9826290607452393, 'polygon': [[464.0, 244.0], [624.0, 244.0], [624.0, 257.0], [464.0, 257.0]], 'bbox': (464, 244, 161, 14), 'method': 'polygon'}, {'text': '4000mAh 2l喜 圳c12l', 'confidence': 0.7905462384223938, 'polygon': [[86.0, 282.0], [282.0, 282.0], [282.0, 302.0], [86.0, 302.0]], 'bbox': (86, 282, 197, 21), 'method': 'polygon'}, {'text': 'PER', 'confidence': 0.5209131836891174, 'polygon': [[262.0, 344.0], [292.0, 344.0], [292.0, 358.0], [262.0, 358.0]], 'bbox': (262, 344, 31, 15), 'method': 'polygon'}, {'text': '丽昆N / 百昆 SOS', 'confidence': 0.717670202255249, 'polygon': [[82.0, 398.0], [284.0, 400.0], [284.0, 424.0], [82.0, 422.0]], 'bbox': (82, 398, 203, 27), 'method': 'polygon'}, {'text': '三', 'confidence': 0.5909590125083923, 'polygon': [[86.0, 463.0], [122.0, 463.0], [122.0, 481.0], [86.0, 481.0]], 'bbox': (86, 463, 37, 19), 'method': 'polygon'}, {'text': '应易 品品 / E品E', 'confidence': 0.708503007888794, 'polygon': [[124.0, 462.0], [286.0, 462.0], [286.0, 482.0], [124.0, 482.0]], 'bbox': (124, 462, 163, 21), 'method': 'polygon'}, {'text': '外', 'confidence': 0.5034343600273132, 'polygon': [[387.0, 774.0], [446.0, 774.0], [446.0, 788.0], [387.0, 788.0]], 'bbox': (387, 774, 60, 15), 'method': 'polygon'}] +[2025-10-24 15:36:40,367] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): 'BAO ZOUTU' +[2025-10-24 15:36:40,367] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): '..R12' +[2025-10-24 15:36:40,367] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): '518' +[2025-10-24 15:36:40,367] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): 'MULTIBANDRADIO/BT/MUSICPLAYER' +[2025-10-24 15:36:40,367] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 79.1%): '4000mAh 2l喜 圳c12l' +[2025-10-24 15:36:40,367] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): 'PER' +[2025-10-24 15:36:40,367] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 71.8%): '丽昆N / 百昆 SOS' +[2025-10-24 15:36:40,367] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 59.1%): '三' +[2025-10-24 15:36:40,367] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 70.9%): '应易 品品 / E品E' +[2025-10-24 15:36:40,368] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 50.3%): '外' +[2025-10-24 15:36:40,368] [LogListener] [DEBUG] [loggerModule.py:debug:275] 필터링 결과: 5/10개 (신뢰도 + & 중국어) +[2025-10-24 15:36:40,368] [LogListener] [DEBUG] [loggerModule.py:debug:275] filter_ocr_results: [{'text': '4000mAh 2l喜 圳c12l', 'confidence': 0.7905462384223938, 'polygon': [[86.0, 282.0], [282.0, 282.0], [282.0, 302.0], [86.0, 302.0]], 'bbox': (86, 282, 197, 21), 'method': 'polygon'}, {'text': '丽昆N / 百昆 SOS', 'confidence': 0.717670202255249, 'polygon': [[82.0, 398.0], [284.0, 400.0], [284.0, 424.0], [82.0, 422.0]], 'bbox': (82, 398, 203, 27), 'method': 'polygon'}, {'text': '三', 'confidence': 0.5909590125083923, 'polygon': [[86.0, 463.0], [122.0, 463.0], [122.0, 481.0], [86.0, 481.0]], 'bbox': (86, 463, 37, 19), 'method': 'polygon'}, {'text': '应易 品品 / E品E', 'confidence': 0.708503007888794, 'polygon': [[124.0, 462.0], [286.0, 462.0], [286.0, 482.0], [124.0, 482.0]], 'bbox': (124, 462, 163, 21), 'method': 'polygon'}, {'text': '外', 'confidence': 0.5034343600273132, 'polygon': [[387.0, 774.0], [446.0, 774.0], [446.0, 788.0], [387.0, 788.0]], 'bbox': (387, 774, 60, 15), 'method': 'polygon'}] +[2025-10-24 15:36:40,368] [LogListener] [DEBUG] [loggerModule.py:debug:275] 중국어 텍스트 5개 필터링 완료 +[2025-10-24 15:36:40,368] [LogListener] [DEBUG] [loggerModule.py:debug:275] 한글 텍스트 0개 필터링 완료 +[2025-10-24 15:36:42,440] [LogListener] [DEBUG] [loggerModule.py:debug:275] translated_texts: ['4000mAh 2l 하이쉔 c12l', '리쿤 N / 바이쿤 SOS', '삼', '잉이 핀핀 / E제품E', '밖의'] +[2025-10-24 15:36:42,441] [LogListener] [DEBUG] [loggerModule.py:debug:275] 마스크 생성 완료 +[2025-10-24 15:36:42,441] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 7 OCR 권한 없음, 전체 번역 모드 +[2025-10-24 15:36:42,447] [LogListener] [DEBUG] [loggerModule.py:debug:275] [AUTO Inpaint] coverage=0.061, comps=4, min_center_dist=0.054 → request +[2025-10-24 15:36:42,447] [LogListener] [DEBUG] [loggerModule.py:debug:275] ocr_count: 5 +[2025-10-24 15:36:42,447] [LogListener] [DEBUG] [loggerModule.py:debug:275] is_member_valid: False +[2025-10-24 15:36:42,447] [LogListener] [DEBUG] [loggerModule.py:debug:275] MIGAN 인페인팅 시도 +[2025-10-24 15:36:42,454] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 입력 형태 - 이미지: (1, 3, 800, 800), 마스크: (1, 1, 800, 800) +[2025-10-24 15:36:42,952] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 출력 형태: (800, 800, 3), dtype: uint8 +[2025-10-24 15:36:42,953] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 추론 완료: 497.89 ms +[2025-10-24 15:36:42,960] [LogListener] [DEBUG] [loggerModule.py:debug:275] MIGAN 인페인팅 성공 +[2025-10-24 15:36:42,961] [LogListener] [DEBUG] [loggerModule.py:debug:275] 메모리 변화 [인페인팅]: 41184.7MB -> 41360.2MB (+175.5MB, +0.4%) - 방법: migan +[2025-10-24 15:36:42,961] [LogListener] [DEBUG] [loggerModule.py:debug:275] 인페인팅 완료 +[2025-10-24 15:36:42,983] [LogListener] [DEBUG] [loggerModule.py:debug:275] 텍스트 렌더링 완료 +[2025-10-24 15:36:42,984] [LogListener] [DEBUG] [loggerModule.py:debug:275] watermark_text: 이미지 저작권 보유 +[2025-10-24 15:36:42,984] [LogListener] [DEBUG] [loggerModule.py:debug:275] is_watermark_enabled: True +[2025-10-24 15:36:43,287] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 저장 완료 : C:\ProgramData\ImgWorker\work\translated_option_img_7.webp +[2025-10-24 15:36:43,288] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 7 번역 완료: C:\ProgramData\ImgWorker\work\translated_option_img_7.webp +[2025-10-24 15:36:43,288] [LogListener] [DEBUG] [loggerModule.py:debug:275] ⏱ 이미지 파이프라인 총 3942.2ms | download=0.0ms | ocr=677.9ms | translate=2074.3ms | mask=2074.3ms | inpaint=518.9ms(migan/DirectML) | render=23.0ms | save=271.5ms +[2025-10-24 15:36:43,289] [LogListener] [DEBUG] [loggerModule.py:debug:275] process_single_image 호출 완료 +[2025-10-24 15:36:43,289] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 결과 반환 중: uid=7f6aee59-83c9-48b0-bdc7-bda1ba530aa3 +[2025-10-24 15:36:43,290] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 결과 반환 완료: uid=7f6aee59-83c9-48b0-bdc7-bda1ba530aa3 +[2025-10-24 15:36:43,290] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 15:36:43,290] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 28344) +[2025-10-24 15:36:45,096] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔥 작업 수신 성공 +[2025-10-24 15:36:45,097] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🚀 작업 처리 시작: cmd=process_single_image, uid=72d986a3-dbf9-4534-8961-f294cafae3a1 +[2025-10-24 15:36:45,097] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 프로세서 toggle_states 업데이트 : {'TEMP_IMAGE_DIR': 'C:\\ProgramData\\ImgWorker\\work', 'output_image_format': 'webp', 'use_local_rembg': True, 'local_rembg_model_path': 'D:\\py\\img_worker\\modules\\rembg_models', 'local_model_name': 'birefnet-general-lite', 'optionIMGTrans_type': 'GPU', 'detail_IMGTrans_type': 'GPU', 'thumb_trans_type': 'GPU', 'migan_use_accel': True, 'migan_onnx_path': 'D:\\py\\img_worker\\modules\\migan_onnx\\migan_pipeline_v2.onnx', 'ocr_provider_override': 'auto', 'migan_provider_override': 'auto', 'font_type': '폰트5', 'unwanted_texts': ['가격설명', '무료배송', '보증', '사은품', '세일', '이벤트', '특가', '품절', '할인', '할인가'], 'is_member_valid': False, 'authenticated_by_admin': True} +[2025-10-24 15:36:45,097] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 실행 직전: cmd=process_single_image +[2025-10-24 15:36:45,097] [LogListener] [DEBUG] [loggerModule.py:debug:275] process_single_image 호출 직전 +[2025-10-24 15:36:45,097] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 번역시작 +[2025-10-24 15:36:45,343] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 8 처리 시작: C:\ProgramData\ImgWorker\incoming\dl_d243974cfca0.jpg - 전체 번역 모드 +[2025-10-24 15:36:45,349] [LogListener] [DEBUG] [loggerModule.py:debug:275] 로컬 파일 경로 감지, 다운로드 생략: C:\ProgramData\ImgWorker\incoming\dl_d243974cfca0.jpg +[2025-10-24 15:36:45,350] [LogListener] [DEBUG] [loggerModule.py:debug:275] 옵션 이미지는 스케일 처리 건너뛰기: option +[2025-10-24 15:36:45,350] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 8 로컬 저장위치(옵션 이미지 원본 유지): C:\ProgramData\ImgWorker\incoming\dl_d243974cfca0.jpg +[2025-10-24 15:36:45,357] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔍 ONNX OCR 감지 방식: polygon +[2025-10-24 15:36:45,981] [LogListener] [DEBUG] [loggerModule.py:debug:275] ⚡ ONNX OCR 추론 완료: 621.5ms +[2025-10-24 15:36:45,996] [LogListener] [DEBUG] [loggerModule.py:debug:275] 📊 세부 시간 - 감지: 114.5ms, 인식: 481.0ms, 분류: 21.0ms +[2025-10-24 15:36:45,996] [LogListener] [DEBUG] [loggerModule.py:debug:275] 메모리 변화 [OCR 처리]: 41480.5MB -> 41474.8MB (-5.6MB, -0.0%) - 이미지 8 +[2025-10-24 15:36:45,996] [LogListener] [DEBUG] [loggerModule.py:debug:275] ocr_results: [{'text': 'BAOZOUTU', 'confidence': 0.9920588731765747, 'polygon': [[133.0, 72.0], [273.0, 72.0], [273.0, 86.0], [133.0, 86.0]], 'bbox': (133, 72, 141, 15), 'method': 'polygon'}, {'text': '.R12', 'confidence': 0.8190782070159912, 'polygon': [[578.0, 152.0], [614.0, 152.0], [614.0, 163.0], [578.0, 163.0]], 'bbox': (578, 152, 37, 12), 'method': 'polygon'}, {'text': '百口2 三丽 昱', 'confidence': 0.5990802049636841, 'polygon': [[92.0, 162.0], [287.0, 164.0], [287.0, 195.0], [92.0, 193.0]], 'bbox': (92, 162, 196, 34), 'method': 'polygon'}, {'text': '518', 'confidence': 0.9822597503662109, 'polygon': [[488.0, 175.0], [607.0, 175.0], [607.0, 240.0], [488.0, 240.0]], 'bbox': (488, 175, 120, 66), 'method': 'polygon'}, {'text': 'MH', 'confidence': 0.5119634866714478, 'polygon': [[607.0, 228.0], [622.0, 228.0], [622.0, 238.0], [607.0, 238.0]], 'bbox': (607, 228, 16, 11), 'method': 'polygon'}, {'text': 'MULTIBANDRADIO/BT/MUSICPLAYER', 'confidence': 0.9807587265968323, 'polygon': [[463.0, 243.0], [624.0, 244.0], [624.0, 258.0], [463.0, 257.0]], 'bbox': (463, 243, 162, 16), 'method': 'polygon'}, {'text': '4000mAh 2l喜 己l2l', 'confidence': 0.7767764329910278, 'polygon': [[85.0, 282.0], [284.0, 282.0], [284.0, 302.0], [85.0, 302.0]], 'bbox': (85, 282, 200, 21), 'method': 'polygon'}, {'text': 'PER', 'confidence': 0.5236435532569885, 'polygon': [[262.0, 344.0], [292.0, 344.0], [292.0, 358.0], [262.0, 358.0]], 'bbox': (262, 344, 31, 15), 'method': 'polygon'}, {'text': '丽是N / 百昆 SOS', 'confidence': 0.7104734778404236, 'polygon': [[83.0, 400.0], [284.0, 400.0], [284.0, 424.0], [83.0, 424.0]], 'bbox': (83, 400, 202, 25), 'method': 'polygon'}, {'text': '应易 品品 / E屁E 可', 'confidence': 0.7013165950775146, 'polygon': [[85.0, 462.0], [287.0, 462.0], [287.0, 482.0], [85.0, 482.0]], 'bbox': (85, 462, 203, 21), 'method': 'polygon'}] +[2025-10-24 15:36:45,997] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): 'BAOZOUTU' +[2025-10-24 15:36:45,997] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): '.R12' +[2025-10-24 15:36:45,997] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 59.9%): '百口2 三丽 昱' +[2025-10-24 15:36:45,997] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): '518' +[2025-10-24 15:36:45,997] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): 'MH' +[2025-10-24 15:36:45,997] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): 'MULTIBANDRADIO/BT/MUSICPLAYER' +[2025-10-24 15:36:45,997] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 77.7%): '4000mAh 2l喜 己l2l' +[2025-10-24 15:36:45,997] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): 'PER' +[2025-10-24 15:36:45,997] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 71.0%): '丽是N / 百昆 SOS' +[2025-10-24 15:36:45,998] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 70.1%): '应易 品品 / E屁E 可' +[2025-10-24 15:36:45,998] [LogListener] [DEBUG] [loggerModule.py:debug:275] 필터링 결과: 4/10개 (신뢰도 + & 중국어) +[2025-10-24 15:36:45,998] [LogListener] [DEBUG] [loggerModule.py:debug:275] filter_ocr_results: [{'text': '百口2 三丽 昱', 'confidence': 0.5990802049636841, 'polygon': [[92.0, 162.0], [287.0, 164.0], [287.0, 195.0], [92.0, 193.0]], 'bbox': (92, 162, 196, 34), 'method': 'polygon'}, {'text': '4000mAh 2l喜 己l2l', 'confidence': 0.7767764329910278, 'polygon': [[85.0, 282.0], [284.0, 282.0], [284.0, 302.0], [85.0, 302.0]], 'bbox': (85, 282, 200, 21), 'method': 'polygon'}, {'text': '丽是N / 百昆 SOS', 'confidence': 0.7104734778404236, 'polygon': [[83.0, 400.0], [284.0, 400.0], [284.0, 424.0], [83.0, 424.0]], 'bbox': (83, 400, 202, 25), 'method': 'polygon'}, {'text': '应易 品品 / E屁E 可', 'confidence': 0.7013165950775146, 'polygon': [[85.0, 462.0], [287.0, 462.0], [287.0, 482.0], [85.0, 482.0]], 'bbox': (85, 462, 203, 21), 'method': 'polygon'}] +[2025-10-24 15:36:45,998] [LogListener] [DEBUG] [loggerModule.py:debug:275] 중국어 텍스트 4개 필터링 완료 +[2025-10-24 15:36:45,998] [LogListener] [DEBUG] [loggerModule.py:debug:275] 한글 텍스트 0개 필터링 완료 +[2025-10-24 15:36:48,326] [LogListener] [DEBUG] [loggerModule.py:debug:275] translated_texts: ['바이커우 2 싼리 유', '4000mAh 2l 하이지 l2l', '리는 N이다 / 바이쿤 SOS', '잉이 핀핀 / E 방귀 E 캔'] +[2025-10-24 15:36:48,327] [LogListener] [DEBUG] [loggerModule.py:debug:275] 마스크 생성 완료 +[2025-10-24 15:36:48,327] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 8 OCR 권한 없음, 전체 번역 모드 +[2025-10-24 15:36:48,333] [LogListener] [DEBUG] [loggerModule.py:debug:275] [AUTO Inpaint] coverage=0.087, comps=4, min_center_dist=0.053 → request +[2025-10-24 15:36:48,333] [LogListener] [DEBUG] [loggerModule.py:debug:275] ocr_count: 4 +[2025-10-24 15:36:48,334] [LogListener] [DEBUG] [loggerModule.py:debug:275] is_member_valid: False +[2025-10-24 15:36:48,334] [LogListener] [DEBUG] [loggerModule.py:debug:275] MIGAN 인페인팅 시도 +[2025-10-24 15:36:48,341] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 입력 형태 - 이미지: (1, 3, 800, 800), 마스크: (1, 1, 800, 800) +[2025-10-24 15:36:48,492] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 출력 형태: (800, 800, 3), dtype: uint8 +[2025-10-24 15:36:48,492] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 추론 완료: 150.00 ms +[2025-10-24 15:36:48,500] [LogListener] [DEBUG] [loggerModule.py:debug:275] MIGAN 인페인팅 성공 +[2025-10-24 15:36:48,500] [LogListener] [DEBUG] [loggerModule.py:debug:275] 메모리 변화 [인페인팅]: 41579.1MB -> 41582.9MB (+3.8MB, +0.0%) - 방법: migan +[2025-10-24 15:36:48,500] [LogListener] [DEBUG] [loggerModule.py:debug:275] 인페인팅 완료 +[2025-10-24 15:36:48,515] [LogListener] [DEBUG] [loggerModule.py:debug:275] 텍스트 렌더링 완료 +[2025-10-24 15:36:48,516] [LogListener] [DEBUG] [loggerModule.py:debug:275] watermark_text: 이미지 저작권 보유 +[2025-10-24 15:36:48,516] [LogListener] [DEBUG] [loggerModule.py:debug:275] is_watermark_enabled: True +[2025-10-24 15:36:48,824] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 저장 완료 : C:\ProgramData\ImgWorker\work\translated_option_img_8.webp +[2025-10-24 15:36:48,825] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 8 번역 완료: C:\ProgramData\ImgWorker\work\translated_option_img_8.webp +[2025-10-24 15:36:48,825] [LogListener] [DEBUG] [loggerModule.py:debug:275] ⏱ 이미지 파이프라인 총 3726.6ms | download=0.0ms | ocr=637.5ms | translate=2331.0ms | mask=2331.0ms | inpaint=172.0ms(migan/DirectML) | render=16.0ms | save=279.0ms +[2025-10-24 15:36:48,826] [LogListener] [DEBUG] [loggerModule.py:debug:275] process_single_image 호출 완료 +[2025-10-24 15:36:48,826] [ResultListener] [WARNING] [loggerModule.py:warning:287] 워커 롤링 스케줄: reason=job-count-threshold +[2025-10-24 15:36:48,827] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 결과 반환 중: uid=72d986a3-dbf9-4534-8961-f294cafae3a1 +[2025-10-24 15:36:48,827] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 결과 반환 완료: uid=72d986a3-dbf9-4534-8961-f294cafae3a1 +[2025-10-24 15:36:48,827] [WorkerRoller] [WARNING] [loggerModule.py:warning:287] 워커 롤링 시작: job-count-threshold +[2025-10-24 15:36:48,827] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 15:36:48,827] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 28344) +[2025-10-24 15:36:48,843] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔥 작업 수신 성공 +[2025-10-24 15:36:48,873] [LogListener] [DEBUG] [loggerModule.py:debug:275] Shutdown signal 수신 → 종료 +[2025-10-24 15:36:48,873] [LogListener] [DEBUG] [loggerModule.py:debug:275] OCR 모듈 정리 완료 +[2025-10-24 15:36:48,873] [LogListener] [DEBUG] [loggerModule.py:debug:275] 마스크 모듈 정리 완료 +[2025-10-24 15:36:48,873] [LogListener] [DEBUG] [loggerModule.py:debug:275] 임시 폴더 삭제됨: C:\ProgramData\ImgWorker\work +[2025-10-24 15:36:48,874] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 프로세서 소멸 +[2025-10-24 15:36:49,295] [WorkerRoller] [INFO] [loggerModule.py:info:281] 워커 프로세스 기동: PID=21804 +[2025-10-24 15:36:49,782] [LogListener] [DEBUG] [loggerModule.py:debug:275] ImageWorker 프로세스 기동 (PID=21804, Name=ImageWorkerProcess) +[2025-10-24 15:36:49,782] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔧 ImageProcessor3 초기화 시작... +[2025-10-24 15:36:49,782] [LogListener] [DEBUG] [loggerModule.py:debug:275] tracemalloc 메모리 추적 시작 +[2025-10-24 15:36:49,788] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ DirectML 환경 준비 완료 (Windows DirectX 12 기반) +[2025-10-24 15:36:49,788] [LogListener] [DEBUG] [loggerModule.py:debug:275] === 🚀 DirectML GPU 상태 초기화 시작 🚀 === +[2025-10-24 15:36:49,788] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🎯 사용자 GPU 가속 요청: False +[2025-10-24 15:36:49,789] [LogListener] [DEBUG] [loggerModule.py:debug:275] 💻 현재 운영체제: Windows +[2025-10-24 15:36:49,789] [LogListener] [DEBUG] [loggerModule.py:debug:275] GPU 가속이 비활성화됨 (toggle_states['use_cuda'] = False) +[2025-10-24 15:36:49,789] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔒 안전한 CPU 모드로 모든 GPU 설정 강제 비활성화 +[2025-10-24 15:36:49,789] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔧 ImageProcessor3 GPU 상태 요약: +[2025-10-24 15:36:49,789] [LogListener] [DEBUG] [loggerModule.py:debug:275] - CUDA 사용 가능: False +[2025-10-24 15:36:49,789] [LogListener] [DEBUG] [loggerModule.py:debug:275] - toggle_states['use_cuda']: NOT_SET +[2025-10-24 15:36:49,789] [LogListener] [DEBUG] [loggerModule.py:debug:275] - GPU 하드웨어 정보: {} +[2025-10-24 15:36:49,789] [LogListener] [DEBUG] [loggerModule.py:debug:275] ImageProcessor3 Init toggle_states: {'TEMP_IMAGE_DIR': 'C:\\ProgramData\\ImgWorker\\work', 'output_image_format': 'webp', 'use_local_rembg': True, 'local_rembg_model_path': 'D:\\py\\img_worker\\modules\\rembg_models', 'local_model_name': 'birefnet-general-lite', 'optionIMGTrans_type': 'CPU', 'detail_IMGTrans_type': 'CPU', 'thumb_trans_type': 'CPU', 'migan_use_accel': True, 'migan_onnx_path': 'D:\\py\\img_worker\\modules\\migan_onnx\\migan_pipeline_v2.onnx', 'ocr_provider_override': 'auto', 'migan_provider_override': 'auto'} +[2025-10-24 15:36:49,789] [LogListener] [DEBUG] [loggerModule.py:debug:275] is_member_valid: False +[2025-10-24 15:36:49,789] [LogListener] [DEBUG] [loggerModule.py:debug:275] debug_images 디렉토리 이미 존재: D:\py\img_worker\modules\debug_images +[2025-10-24 15:36:49,790] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.font_path: D:\py\img_worker\modules\fonts\HakgyoansimDunggeunmisoTTFB.ttf +[2025-10-24 15:36:49,790] [LogListener] [DEBUG] [loggerModule.py:debug:275] toggle_states font_path: D:\py\img_worker\modules\fonts\HakgyoansimDunggeunmisoTTFB.ttf +[2025-10-24 15:36:49,790] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.TEMP_IMAGE_DIR: C:\ProgramData\ImgWorker\work +[2025-10-24 15:36:49,790] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.debugging_save_Dir: D:\py\img_worker\modules\debug_images +[2025-10-24 15:36:49,790] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.unwanted_texts: {} +[2025-10-24 15:36:49,790] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.inpaint_method: migan +[2025-10-24 15:36:49,790] [LogListener] [DEBUG] [loggerModule.py:debug:275] Image.MAX_IMAGE_PIXELS set to 20000000 +[2025-10-24 15:36:49,790] [LogListener] [DEBUG] [loggerModule.py:debug:275] ONNX 모델 타입 설정값: 자동 선택, GPU 정보: {} +[2025-10-24 15:36:49,790] [LogListener] [DEBUG] [loggerModule.py:debug:275] 자동 선택 모드: GPU 추천 모델 simp 사용 +[2025-10-24 15:36:49,790] [LogListener] [DEBUG] [loggerModule.py:debug:275] ONNX OCR 모델 타입 결정: simp (GPU: False) +[2025-10-24 15:36:49,790] [LogListener] [DEBUG] [loggerModule.py:debug:275] ONNX OCR 모듈 CPU 모드로 설정 +[2025-10-24 15:36:49,790] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🚀 ONNX TextSystem 초기화 시작 (CPU 모드) +[2025-10-24 15:36:49,790] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🚀 ONNX TextSystem 초기화 시작 (CPU 모드) +[2025-10-24 15:36:51,369] [LogListener] [DEBUG] [loggerModule.py:debug:275] 문자 사전 파일 UTF-8 확인 완료: D:\py\img_worker\modules\onnx_ocr_module\dict\ppocr_keys_v1.txt +[2025-10-24 15:36:51,468] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ ONNX TextSystem 초기화 완료 (CPU + SIMP (호환성) 모델) +[2025-10-24 15:36:51,468] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ ONNX TextSystem 초기화 완료 (CPU + SIMP 모델) +[2025-10-24 15:36:51,468] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ ONNX OCR 모듈 초기화 성공 (CPU 모드) +[2025-10-24 15:36:51,469] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ ONNX OCR 모듈 초기화 성공 +[2025-10-24 15:36:51,469] [LogListener] [DEBUG] [loggerModule.py:debug:275] 마스크 모듈 초기화 완료 +[2025-10-24 15:36:51,469] [LogListener] [DEBUG] [loggerModule.py:debug:275] MaskModule 초기화 성공 +[2025-10-24 15:36:51,469] [LogListener] [DEBUG] [loggerModule.py:debug:275] 텍스트 렌더링 모듈 초기화 완료 +[2025-10-24 15:36:51,470] [LogListener] [DEBUG] [loggerModule.py:debug:275] 기본 폰트: D:\py\img_worker\modules\fonts\HakgyoansimDunggeunmisoTTFB.ttf +[2025-10-24 15:36:51,470] [LogListener] [DEBUG] [loggerModule.py:debug:275] TextRenderingModule 초기화 성공 +[2025-10-24 15:36:51,470] [LogListener] [DEBUG] [loggerModule.py:debug:275] 폰트 로드 성공: D:\py\img_worker\modules\fonts\HakgyoansimDunggeunmisoTTFB.ttf +[2025-10-24 15:36:51,470] [LogListener] [DEBUG] [loggerModule.py:debug:275] PostImageManager 초기화 성공 +[2025-10-24 15:36:51,470] [LogListener] [DEBUG] [loggerModule.py:debug:275] Request_AI_Server GPU 상태: CUDA 사용 가능=False +[2025-10-24 15:36:51,470] [LogListener] [DEBUG] [loggerModule.py:debug:275] Request_AI_Server 초기화 성공 +[2025-10-24 15:36:51,470] [LogListener] [DEBUG] [loggerModule.py:debug:275] GoogleTranslate 초기화 성공 +[2025-10-24 15:36:51,470] [LogListener] [DEBUG] [loggerModule.py:debug:275] MIGAN CUDA 사용 불가 - CPU 모드로 설정 +[2025-10-24 15:36:51,470] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] GPU 관리자 전달: GPUManager, can_use_cuda: False +[2025-10-24 15:36:51,497] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] GPU 관리자 사용 가능: False +[2025-10-24 15:36:51,705] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 사용 가능한 providers: ['DmlExecutionProvider', 'CPUExecutionProvider'] +[2025-10-24 15:36:51,706] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] DirectML 가속 활성화 +[2025-10-24 15:36:51,706] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 최종 providers: [('DmlExecutionProvider', {}), ('CPUExecutionProvider', {})] +[2025-10-24 15:36:51,706] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] DirectML 가속 시도: [('DmlExecutionProvider', {}), ('CPUExecutionProvider', {})] +[2025-10-24 15:36:51,706] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] DirectML 가속 성공! 실제 providers: ['DmlExecutionProvider', 'CPUExecutionProvider'] +[2025-10-24 15:36:51,706] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 입력 0: image, 형태: ['batch_size', 3, 'height', 'width'], 타입: tensor(uint8) +[2025-10-24 15:36:51,706] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 입력 1: mask, 형태: ['batch_size', 1, 'height', 'width'], 타입: tensor(uint8) +[2025-10-24 15:36:51,706] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 출력 0: result, 형태: ['ScatterNDresult_dim_0', 3, 'ScatterNDresult_dim_2', 'ScatterNDresult_dim_3'], 타입: tensor(uint8) +[2025-10-24 15:36:51,706] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 세션 준비 완료. providers=['DmlExecutionProvider', 'CPUExecutionProvider'] +[2025-10-24 15:36:51,706] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] GPU 관리자 연결 완료: GPUManager +[2025-10-24 15:36:51,706] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 파이프라인 gpu_manager 속성: True, 값: +[2025-10-24 15:36:51,707] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 초기화 완료: gpu_manager 속성=True, 값= +[2025-10-24 15:36:51,707] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔰 OCR 모듈 Warm-up 시작... +[2025-10-24 15:36:51,707] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔍 ONNX OCR 감지 방식: polygon +[2025-10-24 15:36:51,712] [LogListener] [DEBUG] [loggerModule.py:debug:275] ⚡ ONNX OCR 추론 완료: 4.0ms +[2025-10-24 15:36:51,712] [LogListener] [DEBUG] [loggerModule.py:debug:275] 📊 세부 시간 - 감지: 3.0ms, 인식: 0.0ms, 분류: 0.0ms +[2025-10-24 15:36:51,712] [LogListener] [WARNING] [loggerModule.py:warning:287] ⚠️ ONNX OCR 결과가 비어있습니다 +[2025-10-24 15:36:51,715] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ OCR 모듈 Warm-up 성공 +[2025-10-24 15:36:51,715] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔰 ImageProcessor Warm‑up 완료 +[2025-10-24 15:36:51,716] [ResultListener] [INFO] [loggerModule.py:info:281] 워커 READY 수신 +[2025-10-24 15:36:51,716] [WorkerRoller] [INFO] [loggerModule.py:info:281] 버퍼 작업 재개: 1건 +[2025-10-24 15:36:51,716] [ResultListener] [INFO] [loggerModule.py:info:281] 워커 READY 수신 +[2025-10-24 15:36:51,716] [LogListener] [DEBUG] [loggerModule.py:debug:275] 📡 추가 READY 신호 전송 완료 +[2025-10-24 15:36:51,716] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 15:36:51,716] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 21804) +[2025-10-24 15:36:51,716] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔥 작업 수신 성공 +[2025-10-24 15:36:51,717] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🚀 작업 처리 시작: cmd=process_single_image, uid=5395bfc7-3a23-4ee8-9c07-a6092263376c +[2025-10-24 15:36:51,717] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 프로세서 toggle_states 업데이트 : {'TEMP_IMAGE_DIR': 'C:\\ProgramData\\ImgWorker\\work', 'output_image_format': 'webp', 'use_local_rembg': True, 'local_rembg_model_path': 'D:\\py\\img_worker\\modules\\rembg_models', 'local_model_name': 'birefnet-general-lite', 'optionIMGTrans_type': 'GPU', 'detail_IMGTrans_type': 'GPU', 'thumb_trans_type': 'GPU', 'migan_use_accel': True, 'migan_onnx_path': 'D:\\py\\img_worker\\modules\\migan_onnx\\migan_pipeline_v2.onnx', 'ocr_provider_override': 'auto', 'migan_provider_override': 'auto', 'font_type': '폰트5', 'unwanted_texts': ['가격설명', '무료배송', '보증', '사은품', '세일', '이벤트', '특가', '품절', '할인', '할인가'], 'is_member_valid': False, 'authenticated_by_admin': True} +[2025-10-24 15:36:51,717] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 실행 직전: cmd=process_single_image +[2025-10-24 15:36:51,717] [LogListener] [DEBUG] [loggerModule.py:debug:275] process_single_image 호출 직전 +[2025-10-24 15:36:51,719] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 번역시작 +[2025-10-24 15:36:52,044] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 9 처리 시작: C:\ProgramData\ImgWorker\incoming\dl_ab221859d801.jpg - 전체 번역 모드 +[2025-10-24 15:36:52,050] [LogListener] [DEBUG] [loggerModule.py:debug:275] 로컬 파일 경로 감지, 다운로드 생략: C:\ProgramData\ImgWorker\incoming\dl_ab221859d801.jpg +[2025-10-24 15:36:52,050] [LogListener] [DEBUG] [loggerModule.py:debug:275] 옵션 이미지는 스케일 처리 건너뛰기: option +[2025-10-24 15:36:52,050] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 9 로컬 저장위치(옵션 이미지 원본 유지): C:\ProgramData\ImgWorker\incoming\dl_ab221859d801.jpg +[2025-10-24 15:36:52,057] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔍 ONNX OCR 감지 방식: polygon +[2025-10-24 15:36:52,770] [LogListener] [DEBUG] [loggerModule.py:debug:275] ⚡ ONNX OCR 추론 완료: 712.1ms +[2025-10-24 15:36:52,770] [LogListener] [DEBUG] [loggerModule.py:debug:275] 📊 세부 시간 - 감지: 208.0ms, 인식: 478.7ms, 분류: 19.6ms +[2025-10-24 15:36:52,792] [LogListener] [DEBUG] [loggerModule.py:debug:275] 메모리 변화 [OCR 처리]: 41200.4MB -> 41409.3MB (+208.9MB, +0.5%) - 이미지 9 +[2025-10-24 15:36:52,808] [LogListener] [DEBUG] [loggerModule.py:debug:275] ocr_results: [{'text': '豆', 'confidence': 0.5649668574333191, 'polygon': [[141.0, 37.0], [273.0, 35.0], [273.0, 64.0], [141.0, 66.0]], 'bbox': (141, 35, 133, 32), 'method': 'polygon'}, {'text': 'BAOZOUTU', 'confidence': 0.9927836060523987, 'polygon': [[134.0, 73.0], [273.0, 73.0], [273.0, 87.0], [134.0, 87.0]], 'bbox': (134, 73, 140, 15), 'method': 'polygon'}, {'text': '百门2 三丽 晏', 'confidence': 0.6047340631484985, 'polygon': [[92.0, 160.0], [287.0, 162.0], [287.0, 193.0], [92.0, 191.0]], 'bbox': (92, 160, 196, 34), 'method': 'polygon'}, {'text': '..R12', 'confidence': 0.8613664507865906, 'polygon': [[574.0, 152.0], [614.0, 152.0], [614.0, 163.0], [574.0, 163.0]], 'bbox': (574, 152, 41, 12), 'method': 'polygon'}, {'text': '518', 'confidence': 0.9885902404785156, 'polygon': [[487.0, 175.0], [607.0, 175.0], [607.0, 240.0], [487.0, 240.0]], 'bbox': (487, 175, 121, 66), 'method': 'polygon'}, {'text': 'MH', 'confidence': 0.5186610221862793, 'polygon': [[608.0, 228.0], [621.0, 228.0], [621.0, 237.0], [608.0, 237.0]], 'bbox': (608, 228, 14, 10), 'method': 'polygon'}, {'text': 'MULTIBANDRADIO/BT/MUSICPLAYER', 'confidence': 0.980320394039154, 'polygon': [[464.0, 244.0], [623.0, 244.0], [623.0, 258.0], [464.0, 258.0]], 'bbox': (464, 244, 160, 15), 'method': 'polygon'}, {'text': '4000mAh 2l喜 圳己l2l', 'confidence': 0.809324324131012, 'polygon': [[86.0, 282.0], [284.0, 282.0], [284.0, 302.0], [86.0, 302.0]], 'bbox': (86, 282, 199, 21), 'method': 'polygon'}, {'text': 'PER', 'confidence': 0.5217652916908264, 'polygon': [[262.0, 344.0], [292.0, 344.0], [292.0, 358.0], [262.0, 358.0]], 'bbox': (262, 344, 31, 15), 'method': 'polygon'}, {'text': '丽晟N / 百昆 SOS', 'confidence': 0.726224422454834, 'polygon': [[82.0, 398.0], [284.0, 400.0], [284.0, 424.0], [82.0, 422.0]], 'bbox': (82, 398, 203, 27), 'method': 'polygon'}, {'text': '三', 'confidence': 0.5896188616752625, 'polygon': [[86.0, 463.0], [122.0, 463.0], [122.0, 481.0], [86.0, 481.0]], 'bbox': (86, 463, 37, 19), 'method': 'polygon'}, {'text': '应易 品品 / E屁E', 'confidence': 0.7386505007743835, 'polygon': [[124.0, 462.0], [287.0, 462.0], [287.0, 482.0], [124.0, 482.0]], 'bbox': (124, 462, 164, 21), 'method': 'polygon'}] +[2025-10-24 15:36:52,809] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 56.5%): '豆' +[2025-10-24 15:36:52,809] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): 'BAOZOUTU' +[2025-10-24 15:36:52,809] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 60.5%): '百门2 三丽 晏' +[2025-10-24 15:36:52,809] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): '..R12' +[2025-10-24 15:36:52,810] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): '518' +[2025-10-24 15:36:52,810] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): 'MH' +[2025-10-24 15:36:52,810] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): 'MULTIBANDRADIO/BT/MUSICPLAYER' +[2025-10-24 15:36:52,810] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 80.9%): '4000mAh 2l喜 圳己l2l' +[2025-10-24 15:36:52,810] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): 'PER' +[2025-10-24 15:36:52,810] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 72.6%): '丽晟N / 百昆 SOS' +[2025-10-24 15:36:52,810] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 59.0%): '三' +[2025-10-24 15:36:52,810] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 73.9%): '应易 品品 / E屁E' +[2025-10-24 15:36:52,810] [LogListener] [DEBUG] [loggerModule.py:debug:275] 필터링 결과: 6/12개 (신뢰도 + & 중국어) +[2025-10-24 15:36:52,810] [LogListener] [DEBUG] [loggerModule.py:debug:275] filter_ocr_results: [{'text': '豆', 'confidence': 0.5649668574333191, 'polygon': [[141.0, 37.0], [273.0, 35.0], [273.0, 64.0], [141.0, 66.0]], 'bbox': (141, 35, 133, 32), 'method': 'polygon'}, {'text': '百门2 三丽 晏', 'confidence': 0.6047340631484985, 'polygon': [[92.0, 160.0], [287.0, 162.0], [287.0, 193.0], [92.0, 191.0]], 'bbox': (92, 160, 196, 34), 'method': 'polygon'}, {'text': '4000mAh 2l喜 圳己l2l', 'confidence': 0.809324324131012, 'polygon': [[86.0, 282.0], [284.0, 282.0], [284.0, 302.0], [86.0, 302.0]], 'bbox': (86, 282, 199, 21), 'method': 'polygon'}, {'text': '丽晟N / 百昆 SOS', 'confidence': 0.726224422454834, 'polygon': [[82.0, 398.0], [284.0, 400.0], [284.0, 424.0], [82.0, 422.0]], 'bbox': (82, 398, 203, 27), 'method': 'polygon'}, {'text': '三', 'confidence': 0.5896188616752625, 'polygon': [[86.0, 463.0], [122.0, 463.0], [122.0, 481.0], [86.0, 481.0]], 'bbox': (86, 463, 37, 19), 'method': 'polygon'}, {'text': '应易 品品 / E屁E', 'confidence': 0.7386505007743835, 'polygon': [[124.0, 462.0], [287.0, 462.0], [287.0, 482.0], [124.0, 482.0]], 'bbox': (124, 462, 164, 21), 'method': 'polygon'}] +[2025-10-24 15:36:52,810] [LogListener] [DEBUG] [loggerModule.py:debug:275] 중국어 텍스트 6개 필터링 완료 +[2025-10-24 15:36:52,811] [LogListener] [DEBUG] [loggerModule.py:debug:275] 한글 텍스트 0개 필터링 완료 +[2025-10-24 15:36:54,754] [LogListener] [DEBUG] [loggerModule.py:debug:275] translated_texts: ['콩', '백 도어 2 싼리 얀', '4000mAh 2l 안녕 Zhenji l2l', '리셍 N / 바이쿤 SOS', '삼', '잉이 핀핀 / E 방귀 E'] +[2025-10-24 15:36:54,755] [LogListener] [DEBUG] [loggerModule.py:debug:275] 마스크 생성 완료 +[2025-10-24 15:36:54,755] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 9 OCR 권한 없음, 전체 번역 모드 +[2025-10-24 15:36:54,762] [LogListener] [DEBUG] [loggerModule.py:debug:275] [AUTO Inpaint] coverage=0.092, comps=5, min_center_dist=0.054 → request +[2025-10-24 15:36:54,762] [LogListener] [DEBUG] [loggerModule.py:debug:275] ocr_count: 6 +[2025-10-24 15:36:54,762] [LogListener] [DEBUG] [loggerModule.py:debug:275] is_member_valid: False +[2025-10-24 15:36:54,762] [LogListener] [DEBUG] [loggerModule.py:debug:275] MIGAN 인페인팅 시도 +[2025-10-24 15:36:54,770] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 입력 형태 - 이미지: (1, 3, 800, 800), 마스크: (1, 1, 800, 800) +[2025-10-24 15:36:55,242] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 출력 형태: (800, 800, 3), dtype: uint8 +[2025-10-24 15:36:55,242] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 추론 완료: 473.25 ms +[2025-10-24 15:36:55,250] [LogListener] [DEBUG] [loggerModule.py:debug:275] MIGAN 인페인팅 성공 +[2025-10-24 15:36:55,251] [LogListener] [DEBUG] [loggerModule.py:debug:275] 메모리 변화 [인페인팅]: 41472.0MB -> 41649.1MB (+177.2MB, +0.4%) - 방법: migan +[2025-10-24 15:36:55,251] [LogListener] [DEBUG] [loggerModule.py:debug:275] 인페인팅 완료 +[2025-10-24 15:36:55,281] [LogListener] [DEBUG] [loggerModule.py:debug:275] 텍스트 렌더링 완료 +[2025-10-24 15:36:55,282] [LogListener] [DEBUG] [loggerModule.py:debug:275] watermark_text: 이미지 저작권 보유 +[2025-10-24 15:36:55,282] [LogListener] [DEBUG] [loggerModule.py:debug:275] is_watermark_enabled: True +[2025-10-24 15:36:55,586] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 저장 완료 : C:\ProgramData\ImgWorker\work\translated_option_img_9.webp +[2025-10-24 15:36:55,587] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 9 번역 완료: C:\ProgramData\ImgWorker\work\translated_option_img_9.webp +[2025-10-24 15:36:55,588] [LogListener] [DEBUG] [loggerModule.py:debug:275] ⏱ 이미지 파이프라인 총 3869.0ms | download=0.0ms | ocr=726.1ms | translate=1946.0ms | mask=1946.0ms | inpaint=494.5ms(migan/DirectML) | render=31.0ms | save=270.9ms +[2025-10-24 15:36:55,589] [LogListener] [DEBUG] [loggerModule.py:debug:275] process_single_image 호출 완료 +[2025-10-24 15:36:55,589] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 결과 반환 중: uid=5395bfc7-3a23-4ee8-9c07-a6092263376c +[2025-10-24 15:36:55,589] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 결과 반환 완료: uid=5395bfc7-3a23-4ee8-9c07-a6092263376c +[2025-10-24 15:36:55,589] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 15:36:55,589] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 21804) +[2025-10-24 15:36:57,480] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔥 작업 수신 성공 +[2025-10-24 15:36:57,481] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🚀 작업 처리 시작: cmd=process_single_image, uid=3f0d08cf-58a2-49bc-88bb-b5428a6da109 +[2025-10-24 15:36:57,481] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 프로세서 toggle_states 업데이트 : {'TEMP_IMAGE_DIR': 'C:\\ProgramData\\ImgWorker\\work', 'output_image_format': 'webp', 'use_local_rembg': True, 'local_rembg_model_path': 'D:\\py\\img_worker\\modules\\rembg_models', 'local_model_name': 'birefnet-general-lite', 'optionIMGTrans_type': 'GPU', 'detail_IMGTrans_type': 'GPU', 'thumb_trans_type': 'GPU', 'migan_use_accel': True, 'migan_onnx_path': 'D:\\py\\img_worker\\modules\\migan_onnx\\migan_pipeline_v2.onnx', 'ocr_provider_override': 'auto', 'migan_provider_override': 'auto', 'font_type': '폰트5', 'unwanted_texts': ['가격설명', '무료배송', '보증', '사은품', '세일', '이벤트', '특가', '품절', '할인', '할인가'], 'is_member_valid': False, 'authenticated_by_admin': True} +[2025-10-24 15:36:57,481] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 실행 직전: cmd=process_single_image +[2025-10-24 15:36:57,481] [LogListener] [DEBUG] [loggerModule.py:debug:275] process_single_image 호출 직전 +[2025-10-24 15:36:57,481] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 번역시작 +[2025-10-24 15:36:57,723] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 10 처리 시작: C:\ProgramData\ImgWorker\incoming\dl_27311b5aef4f.jpg - 전체 번역 모드 +[2025-10-24 15:36:57,730] [LogListener] [DEBUG] [loggerModule.py:debug:275] 로컬 파일 경로 감지, 다운로드 생략: C:\ProgramData\ImgWorker\incoming\dl_27311b5aef4f.jpg +[2025-10-24 15:36:57,730] [LogListener] [DEBUG] [loggerModule.py:debug:275] 옵션 이미지는 스케일 처리 건너뛰기: option +[2025-10-24 15:36:57,730] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 10 로컬 저장위치(옵션 이미지 원본 유지): C:\ProgramData\ImgWorker\incoming\dl_27311b5aef4f.jpg +[2025-10-24 15:36:57,737] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔍 ONNX OCR 감지 방식: polygon +[2025-10-24 15:36:58,429] [LogListener] [DEBUG] [loggerModule.py:debug:275] ⚡ ONNX OCR 추론 완료: 689.1ms +[2025-10-24 15:36:58,429] [LogListener] [DEBUG] [loggerModule.py:debug:275] 📊 세부 시간 - 감지: 117.0ms, 인식: 546.1ms, 분류: 20.0ms +[2025-10-24 15:36:58,443] [LogListener] [DEBUG] [loggerModule.py:debug:275] 메모리 변화 [OCR 처리]: 41880.3MB -> 41920.4MB (+40.1MB, +0.1%) - 이미지 10 +[2025-10-24 15:36:58,444] [LogListener] [DEBUG] [loggerModule.py:debug:275] ocr_results: [{'text': '豆', 'confidence': 0.5405263304710388, 'polygon': [[141.0, 37.0], [273.0, 35.0], [273.0, 64.0], [141.0, 66.0]], 'bbox': (141, 35, 133, 32), 'method': 'polygon'}, {'text': 'BAOZOUTU', 'confidence': 0.9931001663208008, 'polygon': [[134.0, 73.0], [273.0, 73.0], [273.0, 87.0], [134.0, 87.0]], 'bbox': (134, 73, 140, 15), 'method': 'polygon'}, {'text': '..R12.', 'confidence': 0.7730600237846375, 'polygon': [[532.0, 151.0], [618.0, 151.0], [618.0, 164.0], [532.0, 164.0]], 'bbox': (532, 151, 87, 14), 'method': 'polygon'}, {'text': '百向2 三丽', 'confidence': 0.5099514722824097, 'polygon': [[130.0, 163.0], [286.0, 163.0], [286.0, 191.0], [130.0, 191.0]], 'bbox': (130, 163, 157, 29), 'method': 'polygon'}, {'text': '518', 'confidence': 0.9850978255271912, 'polygon': [[488.0, 174.0], [607.0, 174.0], [607.0, 240.0], [488.0, 240.0]], 'bbox': (488, 174, 120, 67), 'method': 'polygon'}, {'text': 'UH', 'confidence': 0.7314228415489197, 'polygon': [[607.0, 228.0], [622.0, 228.0], [622.0, 238.0], [607.0, 238.0]], 'bbox': (607, 228, 16, 11), 'method': 'polygon'}, {'text': 'MULTIBANDRADIO/BT/MUSICPLAYER', 'confidence': 0.9851949214935303, 'polygon': [[464.0, 244.0], [623.0, 244.0], [623.0, 257.0], [464.0, 257.0]], 'bbox': (464, 244, 160, 14), 'method': 'polygon'}, {'text': '4000mAh 2l喜 圳c12l', 'confidence': 0.7801928520202637, 'polygon': [[86.0, 282.0], [282.0, 282.0], [282.0, 302.0], [86.0, 302.0]], 'bbox': (86, 282, 197, 21), 'method': 'polygon'}, {'text': 'PER', 'confidence': 0.5186991691589355, 'polygon': [[261.0, 344.0], [291.0, 344.0], [291.0, 358.0], [261.0, 358.0]], 'bbox': (261, 344, 31, 15), 'method': 'polygon'}, {'text': '丽昆N / 百昆 SOS', 'confidence': 0.7239213585853577, 'polygon': [[82.0, 398.0], [284.0, 400.0], [284.0, 424.0], [82.0, 422.0]], 'bbox': (82, 398, 203, 27), 'method': 'polygon'}, {'text': '应易 品品 / E品E', 'confidence': 0.7217307686805725, 'polygon': [[124.0, 462.0], [286.0, 462.0], [286.0, 482.0], [124.0, 482.0]], 'bbox': (124, 462, 163, 21), 'method': 'polygon'}, {'text': '社', 'confidence': 0.5745720267295837, 'polygon': [[628.0, 774.0], [715.0, 774.0], [715.0, 787.0], [628.0, 787.0]], 'bbox': (628, 774, 88, 14), 'method': 'polygon'}] +[2025-10-24 15:36:58,444] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 54.1%): '豆' +[2025-10-24 15:36:58,444] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): 'BAOZOUTU' +[2025-10-24 15:36:58,444] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): '..R12.' +[2025-10-24 15:36:58,444] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 51.0%): '百向2 三丽' +[2025-10-24 15:36:58,445] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): '518' +[2025-10-24 15:36:58,445] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): 'UH' +[2025-10-24 15:36:58,445] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): 'MULTIBANDRADIO/BT/MUSICPLAYER' +[2025-10-24 15:36:58,445] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 78.0%): '4000mAh 2l喜 圳c12l' +[2025-10-24 15:36:58,445] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): 'PER' +[2025-10-24 15:36:58,445] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 72.4%): '丽昆N / 百昆 SOS' +[2025-10-24 15:36:58,445] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 72.2%): '应易 品品 / E品E' +[2025-10-24 15:36:58,445] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 57.5%): '社' +[2025-10-24 15:36:58,445] [LogListener] [DEBUG] [loggerModule.py:debug:275] 필터링 결과: 6/12개 (신뢰도 + & 중국어) +[2025-10-24 15:36:58,445] [LogListener] [DEBUG] [loggerModule.py:debug:275] filter_ocr_results: [{'text': '豆', 'confidence': 0.5405263304710388, 'polygon': [[141.0, 37.0], [273.0, 35.0], [273.0, 64.0], [141.0, 66.0]], 'bbox': (141, 35, 133, 32), 'method': 'polygon'}, {'text': '百向2 三丽', 'confidence': 0.5099514722824097, 'polygon': [[130.0, 163.0], [286.0, 163.0], [286.0, 191.0], [130.0, 191.0]], 'bbox': (130, 163, 157, 29), 'method': 'polygon'}, {'text': '4000mAh 2l喜 圳c12l', 'confidence': 0.7801928520202637, 'polygon': [[86.0, 282.0], [282.0, 282.0], [282.0, 302.0], [86.0, 302.0]], 'bbox': (86, 282, 197, 21), 'method': 'polygon'}, {'text': '丽昆N / 百昆 SOS', 'confidence': 0.7239213585853577, 'polygon': [[82.0, 398.0], [284.0, 400.0], [284.0, 424.0], [82.0, 422.0]], 'bbox': (82, 398, 203, 27), 'method': 'polygon'}, {'text': '应易 品品 / E品E', 'confidence': 0.7217307686805725, 'polygon': [[124.0, 462.0], [286.0, 462.0], [286.0, 482.0], [124.0, 482.0]], 'bbox': (124, 462, 163, 21), 'method': 'polygon'}, {'text': '社', 'confidence': 0.5745720267295837, 'polygon': [[628.0, 774.0], [715.0, 774.0], [715.0, 787.0], [628.0, 787.0]], 'bbox': (628, 774, 88, 14), 'method': 'polygon'}] +[2025-10-24 15:36:58,446] [LogListener] [DEBUG] [loggerModule.py:debug:275] 중국어 텍스트 6개 필터링 완료 +[2025-10-24 15:36:58,446] [LogListener] [DEBUG] [loggerModule.py:debug:275] 한글 텍스트 0개 필터링 완료 +[2025-10-24 15:36:59,449] [LogListener] [DEBUG] [loggerModule.py:debug:275] translated_texts: ['콩', '바이샹 2 산리', '4000mAh 2l 하이쉔 c12l', '리쿤 N / 바이쿤 SOS', '잉이 핀핀 / E제품E', '사회'] +[2025-10-24 15:36:59,450] [LogListener] [DEBUG] [loggerModule.py:debug:275] 마스크 생성 완료 +[2025-10-24 15:36:59,450] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 10 OCR 권한 없음, 전체 번역 모드 +[2025-10-24 15:36:59,457] [LogListener] [DEBUG] [loggerModule.py:debug:275] [AUTO Inpaint] coverage=0.092, comps=6, min_center_dist=0.058 → request +[2025-10-24 15:36:59,457] [LogListener] [DEBUG] [loggerModule.py:debug:275] ocr_count: 6 +[2025-10-24 15:36:59,457] [LogListener] [DEBUG] [loggerModule.py:debug:275] is_member_valid: False +[2025-10-24 15:36:59,457] [LogListener] [DEBUG] [loggerModule.py:debug:275] MIGAN 인페인팅 시도 +[2025-10-24 15:36:59,465] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 입력 형태 - 이미지: (1, 3, 800, 800), 마스크: (1, 1, 800, 800) +[2025-10-24 15:36:59,658] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 출력 형태: (800, 800, 3), dtype: uint8 +[2025-10-24 15:36:59,658] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 추론 완료: 192.25 ms +[2025-10-24 15:36:59,669] [LogListener] [DEBUG] [loggerModule.py:debug:275] MIGAN 인페인팅 성공 +[2025-10-24 15:36:59,669] [LogListener] [DEBUG] [loggerModule.py:debug:275] 메모리 변화 [인페인팅]: 41983.9MB -> 42001.4MB (+17.5MB, +0.0%) - 방법: migan +[2025-10-24 15:36:59,669] [LogListener] [DEBUG] [loggerModule.py:debug:275] 인페인팅 완료 +[2025-10-24 15:36:59,693] [LogListener] [DEBUG] [loggerModule.py:debug:275] 텍스트 렌더링 완료 +[2025-10-24 15:36:59,693] [LogListener] [DEBUG] [loggerModule.py:debug:275] watermark_text: 이미지 저작권 보유 +[2025-10-24 15:36:59,693] [LogListener] [DEBUG] [loggerModule.py:debug:275] is_watermark_enabled: True +[2025-10-24 15:37:00,049] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 저장 완료 : C:\ProgramData\ImgWorker\work\translated_option_img_10.webp +[2025-10-24 15:37:00,050] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 10 번역 완료: C:\ProgramData\ImgWorker\work\translated_option_img_10.webp +[2025-10-24 15:37:00,050] [LogListener] [DEBUG] [loggerModule.py:debug:275] ⏱ 이미지 파이프라인 총 2568.1ms | download=0.0ms | ocr=705.7ms | translate=1006.5ms | mask=1006.5ms | inpaint=217.2ms(migan/DirectML) | render=24.0ms | save=311.4ms +[2025-10-24 15:37:00,051] [LogListener] [DEBUG] [loggerModule.py:debug:275] process_single_image 호출 완료 +[2025-10-24 15:37:00,052] [ResultListener] [WARNING] [loggerModule.py:warning:287] 워커 롤링 스케줄: reason=job-count-threshold +[2025-10-24 15:37:00,052] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 결과 반환 중: uid=3f0d08cf-58a2-49bc-88bb-b5428a6da109 +[2025-10-24 15:37:00,052] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 결과 반환 완료: uid=3f0d08cf-58a2-49bc-88bb-b5428a6da109 +[2025-10-24 15:37:00,052] [WorkerRoller] [WARNING] [loggerModule.py:warning:287] 워커 롤링 시작: job-count-threshold +[2025-10-24 15:37:00,052] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 15:37:00,052] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 21804) +[2025-10-24 15:37:00,068] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔥 작업 수신 성공 +[2025-10-24 15:37:00,117] [LogListener] [DEBUG] [loggerModule.py:debug:275] Shutdown signal 수신 → 종료 +[2025-10-24 15:37:00,117] [LogListener] [DEBUG] [loggerModule.py:debug:275] OCR 모듈 정리 완료 +[2025-10-24 15:37:00,117] [LogListener] [DEBUG] [loggerModule.py:debug:275] 마스크 모듈 정리 완료 +[2025-10-24 15:37:00,118] [LogListener] [DEBUG] [loggerModule.py:debug:275] 임시 폴더 삭제됨: C:\ProgramData\ImgWorker\work +[2025-10-24 15:37:00,118] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 프로세서 소멸 +[2025-10-24 15:37:00,583] [WorkerRoller] [INFO] [loggerModule.py:info:281] 워커 프로세스 기동: PID=33740 +[2025-10-24 15:37:01,120] [LogListener] [DEBUG] [loggerModule.py:debug:275] ImageWorker 프로세스 기동 (PID=33740, Name=ImageWorkerProcess) +[2025-10-24 15:37:01,121] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔧 ImageProcessor3 초기화 시작... +[2025-10-24 15:37:01,121] [LogListener] [DEBUG] [loggerModule.py:debug:275] tracemalloc 메모리 추적 시작 +[2025-10-24 15:37:01,128] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ DirectML 환경 준비 완료 (Windows DirectX 12 기반) +[2025-10-24 15:37:01,129] [LogListener] [DEBUG] [loggerModule.py:debug:275] === 🚀 DirectML GPU 상태 초기화 시작 🚀 === +[2025-10-24 15:37:01,129] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🎯 사용자 GPU 가속 요청: False +[2025-10-24 15:37:01,129] [LogListener] [DEBUG] [loggerModule.py:debug:275] 💻 현재 운영체제: Windows +[2025-10-24 15:37:01,129] [LogListener] [DEBUG] [loggerModule.py:debug:275] GPU 가속이 비활성화됨 (toggle_states['use_cuda'] = False) +[2025-10-24 15:37:01,129] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔒 안전한 CPU 모드로 모든 GPU 설정 강제 비활성화 +[2025-10-24 15:37:01,129] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔧 ImageProcessor3 GPU 상태 요약: +[2025-10-24 15:37:01,129] [LogListener] [DEBUG] [loggerModule.py:debug:275] - CUDA 사용 가능: False +[2025-10-24 15:37:01,130] [LogListener] [DEBUG] [loggerModule.py:debug:275] - toggle_states['use_cuda']: NOT_SET +[2025-10-24 15:37:01,130] [LogListener] [DEBUG] [loggerModule.py:debug:275] - GPU 하드웨어 정보: {} +[2025-10-24 15:37:01,130] [LogListener] [DEBUG] [loggerModule.py:debug:275] ImageProcessor3 Init toggle_states: {'TEMP_IMAGE_DIR': 'C:\\ProgramData\\ImgWorker\\work', 'output_image_format': 'webp', 'use_local_rembg': True, 'local_rembg_model_path': 'D:\\py\\img_worker\\modules\\rembg_models', 'local_model_name': 'birefnet-general-lite', 'optionIMGTrans_type': 'CPU', 'detail_IMGTrans_type': 'CPU', 'thumb_trans_type': 'CPU', 'migan_use_accel': True, 'migan_onnx_path': 'D:\\py\\img_worker\\modules\\migan_onnx\\migan_pipeline_v2.onnx', 'ocr_provider_override': 'auto', 'migan_provider_override': 'auto'} +[2025-10-24 15:37:01,130] [LogListener] [DEBUG] [loggerModule.py:debug:275] is_member_valid: False +[2025-10-24 15:37:01,130] [LogListener] [DEBUG] [loggerModule.py:debug:275] debug_images 디렉토리 이미 존재: D:\py\img_worker\modules\debug_images +[2025-10-24 15:37:01,130] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.font_path: D:\py\img_worker\modules\fonts\HakgyoansimDunggeunmisoTTFB.ttf +[2025-10-24 15:37:01,130] [LogListener] [DEBUG] [loggerModule.py:debug:275] toggle_states font_path: D:\py\img_worker\modules\fonts\HakgyoansimDunggeunmisoTTFB.ttf +[2025-10-24 15:37:01,130] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.TEMP_IMAGE_DIR: C:\ProgramData\ImgWorker\work +[2025-10-24 15:37:01,130] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.debugging_save_Dir: D:\py\img_worker\modules\debug_images +[2025-10-24 15:37:01,130] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.unwanted_texts: {} +[2025-10-24 15:37:01,130] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.inpaint_method: migan +[2025-10-24 15:37:01,130] [LogListener] [DEBUG] [loggerModule.py:debug:275] Image.MAX_IMAGE_PIXELS set to 20000000 +[2025-10-24 15:37:01,130] [LogListener] [DEBUG] [loggerModule.py:debug:275] ONNX 모델 타입 설정값: 자동 선택, GPU 정보: {} +[2025-10-24 15:37:01,130] [LogListener] [DEBUG] [loggerModule.py:debug:275] 자동 선택 모드: GPU 추천 모델 simp 사용 +[2025-10-24 15:37:01,131] [LogListener] [DEBUG] [loggerModule.py:debug:275] ONNX OCR 모델 타입 결정: simp (GPU: False) +[2025-10-24 15:37:01,131] [LogListener] [DEBUG] [loggerModule.py:debug:275] ONNX OCR 모듈 CPU 모드로 설정 +[2025-10-24 15:37:01,131] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🚀 ONNX TextSystem 초기화 시작 (CPU 모드) +[2025-10-24 15:37:01,131] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🚀 ONNX TextSystem 초기화 시작 (CPU 모드) +[2025-10-24 15:37:02,680] [LogListener] [DEBUG] [loggerModule.py:debug:275] 문자 사전 파일 UTF-8 확인 완료: D:\py\img_worker\modules\onnx_ocr_module\dict\ppocr_keys_v1.txt +[2025-10-24 15:37:02,764] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ ONNX TextSystem 초기화 완료 (CPU + SIMP (호환성) 모델) +[2025-10-24 15:37:02,765] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ ONNX TextSystem 초기화 완료 (CPU + SIMP 모델) +[2025-10-24 15:37:02,765] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ ONNX OCR 모듈 초기화 성공 (CPU 모드) +[2025-10-24 15:37:02,765] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ ONNX OCR 모듈 초기화 성공 +[2025-10-24 15:37:02,765] [LogListener] [DEBUG] [loggerModule.py:debug:275] 마스크 모듈 초기화 완료 +[2025-10-24 15:37:02,765] [LogListener] [DEBUG] [loggerModule.py:debug:275] MaskModule 초기화 성공 +[2025-10-24 15:37:02,766] [LogListener] [DEBUG] [loggerModule.py:debug:275] 텍스트 렌더링 모듈 초기화 완료 +[2025-10-24 15:37:02,766] [LogListener] [DEBUG] [loggerModule.py:debug:275] 기본 폰트: D:\py\img_worker\modules\fonts\HakgyoansimDunggeunmisoTTFB.ttf +[2025-10-24 15:37:02,766] [LogListener] [DEBUG] [loggerModule.py:debug:275] TextRenderingModule 초기화 성공 +[2025-10-24 15:37:02,766] [LogListener] [DEBUG] [loggerModule.py:debug:275] 폰트 로드 성공: D:\py\img_worker\modules\fonts\HakgyoansimDunggeunmisoTTFB.ttf +[2025-10-24 15:37:02,766] [LogListener] [DEBUG] [loggerModule.py:debug:275] PostImageManager 초기화 성공 +[2025-10-24 15:37:02,766] [LogListener] [DEBUG] [loggerModule.py:debug:275] Request_AI_Server GPU 상태: CUDA 사용 가능=False +[2025-10-24 15:37:02,766] [LogListener] [DEBUG] [loggerModule.py:debug:275] Request_AI_Server 초기화 성공 +[2025-10-24 15:37:02,766] [LogListener] [DEBUG] [loggerModule.py:debug:275] GoogleTranslate 초기화 성공 +[2025-10-24 15:37:02,766] [LogListener] [DEBUG] [loggerModule.py:debug:275] MIGAN CUDA 사용 불가 - CPU 모드로 설정 +[2025-10-24 15:37:02,766] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] GPU 관리자 전달: GPUManager, can_use_cuda: False +[2025-10-24 15:37:02,784] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] GPU 관리자 사용 가능: False +[2025-10-24 15:37:02,995] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 사용 가능한 providers: ['DmlExecutionProvider', 'CPUExecutionProvider'] +[2025-10-24 15:37:02,995] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] DirectML 가속 활성화 +[2025-10-24 15:37:02,995] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 최종 providers: [('DmlExecutionProvider', {}), ('CPUExecutionProvider', {})] +[2025-10-24 15:37:02,995] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] DirectML 가속 시도: [('DmlExecutionProvider', {}), ('CPUExecutionProvider', {})] +[2025-10-24 15:37:02,995] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] DirectML 가속 성공! 실제 providers: ['DmlExecutionProvider', 'CPUExecutionProvider'] +[2025-10-24 15:37:02,995] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 입력 0: image, 형태: ['batch_size', 3, 'height', 'width'], 타입: tensor(uint8) +[2025-10-24 15:37:02,996] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 입력 1: mask, 형태: ['batch_size', 1, 'height', 'width'], 타입: tensor(uint8) +[2025-10-24 15:37:02,996] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 출력 0: result, 형태: ['ScatterNDresult_dim_0', 3, 'ScatterNDresult_dim_2', 'ScatterNDresult_dim_3'], 타입: tensor(uint8) +[2025-10-24 15:37:02,996] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 세션 준비 완료. providers=['DmlExecutionProvider', 'CPUExecutionProvider'] +[2025-10-24 15:37:02,996] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] GPU 관리자 연결 완료: GPUManager +[2025-10-24 15:37:02,996] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 파이프라인 gpu_manager 속성: True, 값: +[2025-10-24 15:37:02,996] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 초기화 완료: gpu_manager 속성=True, 값= +[2025-10-24 15:37:02,996] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔰 OCR 모듈 Warm-up 시작... +[2025-10-24 15:37:02,996] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔍 ONNX OCR 감지 방식: polygon +[2025-10-24 15:37:03,001] [LogListener] [DEBUG] [loggerModule.py:debug:275] ⚡ ONNX OCR 추론 완료: 4.0ms +[2025-10-24 15:37:03,001] [LogListener] [DEBUG] [loggerModule.py:debug:275] 📊 세부 시간 - 감지: 3.0ms, 인식: 0.0ms, 분류: 0.0ms +[2025-10-24 15:37:03,001] [LogListener] [WARNING] [loggerModule.py:warning:287] ⚠️ ONNX OCR 결과가 비어있습니다 +[2025-10-24 15:37:03,004] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ OCR 모듈 Warm-up 성공 +[2025-10-24 15:37:03,004] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔰 ImageProcessor Warm‑up 완료 +[2025-10-24 15:37:03,004] [ResultListener] [INFO] [loggerModule.py:info:281] 워커 READY 수신 +[2025-10-24 15:37:03,004] [WorkerRoller] [INFO] [loggerModule.py:info:281] 버퍼 작업 재개: 1건 +[2025-10-24 15:37:03,004] [ResultListener] [INFO] [loggerModule.py:info:281] 워커 READY 수신 +[2025-10-24 15:37:03,005] [LogListener] [DEBUG] [loggerModule.py:debug:275] 📡 추가 READY 신호 전송 완료 +[2025-10-24 15:37:03,005] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 15:37:03,005] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 33740) +[2025-10-24 15:37:03,005] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔥 작업 수신 성공 +[2025-10-24 15:37:03,005] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🚀 작업 처리 시작: cmd=process_single_image, uid=d0fc3e11-4114-4b3c-a99d-cf3e44f8d2a0 +[2025-10-24 15:37:03,005] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 프로세서 toggle_states 업데이트 : {'TEMP_IMAGE_DIR': 'C:\\ProgramData\\ImgWorker\\work', 'output_image_format': 'webp', 'use_local_rembg': True, 'local_rembg_model_path': 'D:\\py\\img_worker\\modules\\rembg_models', 'local_model_name': 'birefnet-general-lite', 'optionIMGTrans_type': 'GPU', 'detail_IMGTrans_type': 'GPU', 'thumb_trans_type': 'GPU', 'migan_use_accel': True, 'migan_onnx_path': 'D:\\py\\img_worker\\modules\\migan_onnx\\migan_pipeline_v2.onnx', 'ocr_provider_override': 'auto', 'migan_provider_override': 'auto', 'font_type': '폰트5', 'unwanted_texts': ['가격설명', '무료배송', '보증', '사은품', '세일', '이벤트', '특가', '품절', '할인', '할인가'], 'is_member_valid': False, 'authenticated_by_admin': True} +[2025-10-24 15:37:03,006] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 실행 직전: cmd=process_single_image +[2025-10-24 15:37:03,006] [LogListener] [DEBUG] [loggerModule.py:debug:275] process_single_image 호출 직전 +[2025-10-24 15:37:03,008] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 번역시작 +[2025-10-24 15:37:03,481] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 11 처리 시작: C:\ProgramData\ImgWorker\incoming\dl_6fb6f2d4cc9a.jpg - 전체 번역 모드 +[2025-10-24 15:37:03,487] [LogListener] [DEBUG] [loggerModule.py:debug:275] 로컬 파일 경로 감지, 다운로드 생략: C:\ProgramData\ImgWorker\incoming\dl_6fb6f2d4cc9a.jpg +[2025-10-24 15:37:03,487] [LogListener] [DEBUG] [loggerModule.py:debug:275] 옵션 이미지는 스케일 처리 건너뛰기: option +[2025-10-24 15:37:03,487] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 11 로컬 저장위치(옵션 이미지 원본 유지): C:\ProgramData\ImgWorker\incoming\dl_6fb6f2d4cc9a.jpg +[2025-10-24 15:37:03,489] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔍 ONNX OCR 감지 방식: polygon +[2025-10-24 15:37:04,081] [LogListener] [DEBUG] [loggerModule.py:debug:275] ⚡ ONNX OCR 추론 완료: 589.1ms +[2025-10-24 15:37:04,081] [LogListener] [DEBUG] [loggerModule.py:debug:275] 📊 세부 시간 - 감지: 110.7ms, 인식: 454.0ms, 분류: 19.0ms +[2025-10-24 15:37:04,099] [LogListener] [DEBUG] [loggerModule.py:debug:275] 메모리 변화 [OCR 처리]: 41803.2MB -> 42016.7MB (+213.5MB, +0.5%) - 이미지 11 +[2025-10-24 15:37:04,106] [LogListener] [DEBUG] [loggerModule.py:debug:275] ocr_results: [{'text': '暴走兔', 'confidence': 0.9088842272758484, 'polygon': [[135.0, 20.0], [271.0, 22.0], [270.0, 64.0], [134.0, 62.0]], 'bbox': (134, 20, 138, 45), 'method': 'polygon'}, {'text': 'BAOZOUTU', 'confidence': 0.993258535861969, 'polygon': [[134.0, 73.0], [273.0, 73.0], [273.0, 87.0], [134.0, 87.0]], 'bbox': (134, 73, 140, 15), 'method': 'polygon'}, {'text': '全波段收音机', 'confidence': 0.9974920749664307, 'polygon': [[81.0, 158.0], [288.0, 156.0], [288.0, 184.0], [81.0, 186.0]], 'bbox': (81, 156, 208, 31), 'method': 'polygon'}, {'text': '.R12', 'confidence': 0.8123915791511536, 'polygon': [[579.0, 152.0], [614.0, 152.0], [614.0, 163.0], [579.0, 163.0]], 'bbox': (579, 152, 36, 12), 'method': 'polygon'}, {'text': '518', 'confidence': 0.9942043423652649, 'polygon': [[487.0, 175.0], [606.0, 175.0], [606.0, 240.0], [487.0, 240.0]], 'bbox': (487, 175, 120, 66), 'method': 'polygon'}, {'text': '蓝牙音响', 'confidence': 0.9985915422439575, 'polygon': [[79.0, 214.0], [294.0, 214.0], [294.0, 248.0], [79.0, 248.0]], 'bbox': (79, 214, 216, 35), 'method': 'polygon'}, {'text': 'H:', 'confidence': 0.5186051726341248, 'polygon': [[608.0, 228.0], [621.0, 228.0], [621.0, 237.0], [608.0, 237.0]], 'bbox': (608, 228, 14, 10), 'method': 'polygon'}, {'text': 'MULTIBANDRADIO/BT/MUSICPLAYER', 'confidence': 0.9846541881561279, 'polygon': [[464.0, 243.0], [623.0, 244.0], [623.0, 258.0], [464.0, 257.0]], 'bbox': (464, 243, 160, 16), 'method': 'polygon'}, {'text': '4000毫安锂电池', 'confidence': 0.9973577857017517, 'polygon': [[83.0, 279.0], [289.0, 279.0], [289.0, 303.0], [83.0, 303.0]], 'bbox': (83, 279, 207, 25), 'method': 'polygon'}, {'text': '手', 'confidence': 0.9999408721923828, 'polygon': [[80.0, 335.0], [117.0, 335.0], [117.0, 365.0], [80.0, 365.0]], 'bbox': (80, 335, 38, 31), 'method': 'polygon'}, {'text': '电', 'confidence': 0.9994295239448547, 'polygon': [[169.0, 338.0], [202.0, 338.0], [202.0, 364.0], [169.0, 364.0]], 'bbox': (169, 338, 34, 27), 'method': 'polygon'}, {'text': '筒', 'confidence': 0.9992778897285461, 'polygon': [[263.0, 336.0], [291.0, 336.0], [291.0, 365.0], [263.0, 365.0]], 'bbox': (263, 336, 29, 30), 'method': 'polygon'}, {'text': 'SOS警报/指南针', 'confidence': 0.9673583507537842, 'polygon': [[81.0, 397.0], [288.0, 397.0], [288.0, 425.0], [81.0, 425.0]], 'bbox': (81, 397, 208, 29), 'method': 'polygon'}, {'text': '手摇/太阳能发电', 'confidence': 0.9900031685829163, 'polygon': [[83.0, 457.0], [289.0, 457.0], [289.0, 485.0], [83.0, 485.0]], 'bbox': (83, 457, 207, 29), 'method': 'polygon'}, {'text': '可插U盘/TF卡', 'confidence': 0.990487277507782, 'polygon': [[81.0, 516.0], [290.0, 518.0], [290.0, 546.0], [81.0, 544.0]], 'bbox': (81, 516, 210, 31), 'method': 'polygon'}, {'text': '限时', 'confidence': 0.9988158941268921, 'polygon': [[17.0, 659.0], [150.0, 661.0], [149.0, 729.0], [16.0, 727.0]], 'bbox': (16, 659, 135, 71), 'method': 'polygon'}, {'text': '赠送', 'confidence': 0.9996625185012817, 'polygon': [[15.0, 728.0], [149.0, 728.0], [149.0, 792.0], [15.0, 792.0]], 'bbox': (15, 728, 135, 65), 'method': 'polygon'}, {'text': '军工包', 'confidence': 0.9989128112792969, 'polygon': [[206.0, 768.0], [265.0, 768.0], [265.0, 790.0], [206.0, 790.0]], 'bbox': (206, 768, 60, 23), 'method': 'polygon'}, {'text': '延长天线', 'confidence': 0.9986650347709656, 'polygon': [[283.0, 769.0], [362.0, 769.0], [362.0, 790.0], [283.0, 790.0]], 'bbox': (283, 769, 80, 22), 'method': 'polygon'}, {'text': '内存卡', 'confidence': 0.9983696937561035, 'polygon': [[385.0, 769.0], [445.0, 769.0], [445.0, 790.0], [385.0, 790.0]], 'bbox': (385, 769, 61, 22), 'method': 'polygon'}, {'text': '读卡器', 'confidence': 0.9986729621887207, 'polygon': [[472.0, 767.0], [531.0, 767.0], [531.0, 792.0], [472.0, 792.0]], 'bbox': (472, 767, 60, 26), 'method': 'polygon'}, {'text': '充电线', 'confidence': 0.9994004368782043, 'polygon': [[558.0, 769.0], [617.0, 769.0], [617.0, 790.0], [558.0, 790.0]], 'bbox': (558, 769, 60, 22), 'method': 'polygon'}, {'text': '礼盒礼袋', 'confidence': 0.9987758994102478, 'polygon': [[638.0, 769.0], [716.0, 769.0], [716.0, 790.0], [638.0, 790.0]], 'bbox': (638, 769, 79, 22), 'method': 'polygon'}, {'text': '充电头', 'confidence': 0.9035988450050354, 'polygon': [[735.0, 767.0], [794.0, 767.0], [794.0, 792.0], [735.0, 792.0]], 'bbox': (735, 767, 60, 26), 'method': 'polygon'}] +[2025-10-24 15:37:04,134] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 90.9%): '暴走兔' +[2025-10-24 15:37:04,134] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): 'BAOZOUTU' +[2025-10-24 15:37:04,134] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.7%): '全波段收音机' +[2025-10-24 15:37:04,135] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): '.R12' +[2025-10-24 15:37:04,135] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): '518' +[2025-10-24 15:37:04,135] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.9%): '蓝牙音响' +[2025-10-24 15:37:04,154] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): 'H:' +[2025-10-24 15:37:04,169] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): 'MULTIBANDRADIO/BT/MUSICPLAYER' +[2025-10-24 15:37:04,175] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.7%): '4000毫安锂电池' +[2025-10-24 15:37:04,176] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 100.0%): '手' +[2025-10-24 15:37:04,176] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.9%): '电' +[2025-10-24 15:37:04,176] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.9%): '筒' +[2025-10-24 15:37:04,176] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 96.7%): 'SOS警报/指南针' +[2025-10-24 15:37:04,176] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.0%): '手摇/太阳能发电' +[2025-10-24 15:37:04,177] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.0%): '可插U盘/TF卡' +[2025-10-24 15:37:04,177] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.9%): '限时' +[2025-10-24 15:37:04,177] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 100.0%): '赠送' +[2025-10-24 15:37:04,177] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.9%): '军工包' +[2025-10-24 15:37:04,177] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.9%): '延长天线' +[2025-10-24 15:37:04,177] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.8%): '内存卡' +[2025-10-24 15:37:04,178] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.9%): '读卡器' +[2025-10-24 15:37:04,178] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.9%): '充电线' +[2025-10-24 15:37:04,178] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 99.9%): '礼盒礼袋' +[2025-10-24 15:37:04,178] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 90.4%): '充电头' +[2025-10-24 15:37:04,178] [LogListener] [DEBUG] [loggerModule.py:debug:275] 필터링 결과: 19/24개 (신뢰도 + & 중국어) +[2025-10-24 15:37:04,178] [LogListener] [DEBUG] [loggerModule.py:debug:275] filter_ocr_results: [{'text': '暴走兔', 'confidence': 0.9088842272758484, 'polygon': [[135.0, 20.0], [271.0, 22.0], [270.0, 64.0], [134.0, 62.0]], 'bbox': (134, 20, 138, 45), 'method': 'polygon'}, {'text': '全波段收音机', 'confidence': 0.9974920749664307, 'polygon': [[81.0, 158.0], [288.0, 156.0], [288.0, 184.0], [81.0, 186.0]], 'bbox': (81, 156, 208, 31), 'method': 'polygon'}, {'text': '蓝牙音响', 'confidence': 0.9985915422439575, 'polygon': [[79.0, 214.0], [294.0, 214.0], [294.0, 248.0], [79.0, 248.0]], 'bbox': (79, 214, 216, 35), 'method': 'polygon'}, {'text': '4000毫安锂电池', 'confidence': 0.9973577857017517, 'polygon': [[83.0, 279.0], [289.0, 279.0], [289.0, 303.0], [83.0, 303.0]], 'bbox': (83, 279, 207, 25), 'method': 'polygon'}, {'text': '手', 'confidence': 0.9999408721923828, 'polygon': [[80.0, 335.0], [117.0, 335.0], [117.0, 365.0], [80.0, 365.0]], 'bbox': (80, 335, 38, 31), 'method': 'polygon'}, {'text': '电', 'confidence': 0.9994295239448547, 'polygon': [[169.0, 338.0], [202.0, 338.0], [202.0, 364.0], [169.0, 364.0]], 'bbox': (169, 338, 34, 27), 'method': 'polygon'}, {'text': '筒', 'confidence': 0.9992778897285461, 'polygon': [[263.0, 336.0], [291.0, 336.0], [291.0, 365.0], [263.0, 365.0]], 'bbox': (263, 336, 29, 30), 'method': 'polygon'}, {'text': 'SOS警报/指南针', 'confidence': 0.9673583507537842, 'polygon': [[81.0, 397.0], [288.0, 397.0], [288.0, 425.0], [81.0, 425.0]], 'bbox': (81, 397, 208, 29), 'method': 'polygon'}, {'text': '手摇/太阳能发电', 'confidence': 0.9900031685829163, 'polygon': [[83.0, 457.0], [289.0, 457.0], [289.0, 485.0], [83.0, 485.0]], 'bbox': (83, 457, 207, 29), 'method': 'polygon'}, {'text': '可插U盘/TF卡', 'confidence': 0.990487277507782, 'polygon': [[81.0, 516.0], [290.0, 518.0], [290.0, 546.0], [81.0, 544.0]], 'bbox': (81, 516, 210, 31), 'method': 'polygon'}, {'text': '限时', 'confidence': 0.9988158941268921, 'polygon': [[17.0, 659.0], [150.0, 661.0], [149.0, 729.0], [16.0, 727.0]], 'bbox': (16, 659, 135, 71), 'method': 'polygon'}, {'text': '赠送', 'confidence': 0.9996625185012817, 'polygon': [[15.0, 728.0], [149.0, 728.0], [149.0, 792.0], [15.0, 792.0]], 'bbox': (15, 728, 135, 65), 'method': 'polygon'}, {'text': '军工包', 'confidence': 0.9989128112792969, 'polygon': [[206.0, 768.0], [265.0, 768.0], [265.0, 790.0], [206.0, 790.0]], 'bbox': (206, 768, 60, 23), 'method': 'polygon'}, {'text': '延长天线', 'confidence': 0.9986650347709656, 'polygon': [[283.0, 769.0], [362.0, 769.0], [362.0, 790.0], [283.0, 790.0]], 'bbox': (283, 769, 80, 22), 'method': 'polygon'}, {'text': '内存卡', 'confidence': 0.9983696937561035, 'polygon': [[385.0, 769.0], [445.0, 769.0], [445.0, 790.0], [385.0, 790.0]], 'bbox': (385, 769, 61, 22), 'method': 'polygon'}, {'text': '读卡器', 'confidence': 0.9986729621887207, 'polygon': [[472.0, 767.0], [531.0, 767.0], [531.0, 792.0], [472.0, 792.0]], 'bbox': (472, 767, 60, 26), 'method': 'polygon'}, {'text': '充电线', 'confidence': 0.9994004368782043, 'polygon': [[558.0, 769.0], [617.0, 769.0], [617.0, 790.0], [558.0, 790.0]], 'bbox': (558, 769, 60, 22), 'method': 'polygon'}, {'text': '礼盒礼袋', 'confidence': 0.9987758994102478, 'polygon': [[638.0, 769.0], [716.0, 769.0], [716.0, 790.0], [638.0, 790.0]], 'bbox': (638, 769, 79, 22), 'method': 'polygon'}, {'text': '充电头', 'confidence': 0.9035988450050354, 'polygon': [[735.0, 767.0], [794.0, 767.0], [794.0, 792.0], [735.0, 792.0]], 'bbox': (735, 767, 60, 26), 'method': 'polygon'}] +[2025-10-24 15:37:04,179] [LogListener] [DEBUG] [loggerModule.py:debug:275] 중국어 텍스트 19개 필터링 완료 +[2025-10-24 15:37:04,179] [LogListener] [DEBUG] [loggerModule.py:debug:275] 한글 텍스트 0개 필터링 완료 +[2025-10-24 15:37:05,322] [LogListener] [DEBUG] [loggerModule.py:debug:275] translated_texts: ['도망친 토끼', '풀 밴드 라디오', '블루투스 스피커', '4000mAh 리튬 배터리', '손', '전기', '실린더', 'SOS 경보 / 나침반', '핸드 크랭크 / 태양광 발전', 'U 디스크 삽입 가능 / TF 카드', '제한된 시간', '포기하다', '군사 가방', '확장 안테나', '메모리 카드', '카드 리더', '충전 케이블', '선물 상자 선물 가방', '충전 헤드'] +[2025-10-24 15:37:05,322] [LogListener] [DEBUG] [loggerModule.py:debug:275] 마스크 생성 완료 +[2025-10-24 15:37:05,322] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 11 OCR 권한 없음, 전체 번역 모드 +[2025-10-24 15:37:05,330] [LogListener] [DEBUG] [loggerModule.py:debug:275] [AUTO Inpaint] coverage=0.207, comps=15, min_center_dist=0.053 → request +[2025-10-24 15:37:05,330] [LogListener] [DEBUG] [loggerModule.py:debug:275] ocr_count: 19 +[2025-10-24 15:37:05,331] [LogListener] [DEBUG] [loggerModule.py:debug:275] is_member_valid: False +[2025-10-24 15:37:05,331] [LogListener] [DEBUG] [loggerModule.py:debug:275] MIGAN 인페인팅 시도 +[2025-10-24 15:37:05,332] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 입력 형태 - 이미지: (1, 3, 800, 800), 마스크: (1, 1, 800, 800) +[2025-10-24 15:37:05,790] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 출력 형태: (800, 800, 3), dtype: uint8 +[2025-10-24 15:37:05,790] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 추론 완료: 458.08 ms +[2025-10-24 15:37:05,798] [LogListener] [DEBUG] [loggerModule.py:debug:275] MIGAN 인페인팅 성공 +[2025-10-24 15:37:05,798] [LogListener] [DEBUG] [loggerModule.py:debug:275] 메모리 변화 [인페인팅]: 42059.3MB -> 42237.7MB (+178.4MB, +0.4%) - 방법: migan +[2025-10-24 15:37:05,799] [LogListener] [DEBUG] [loggerModule.py:debug:275] 인페인팅 완료 +[2025-10-24 15:37:05,872] [LogListener] [DEBUG] [loggerModule.py:debug:275] 텍스트 렌더링 완료 +[2025-10-24 15:37:05,873] [LogListener] [DEBUG] [loggerModule.py:debug:275] watermark_text: 이미지 저작권 보유 +[2025-10-24 15:37:05,873] [LogListener] [DEBUG] [loggerModule.py:debug:275] is_watermark_enabled: True +[2025-10-24 15:37:06,187] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 저장 완료 : C:\ProgramData\ImgWorker\work\translated_option_img_11.webp +[2025-10-24 15:37:06,188] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 11 번역 완료: C:\ProgramData\ImgWorker\work\translated_option_img_11.webp +[2025-10-24 15:37:06,188] [LogListener] [DEBUG] [loggerModule.py:debug:275] ⏱ 이미지 파이프라인 총 3180.0ms | download=0.3ms | ocr=599.8ms | translate=1186.8ms | mask=1186.8ms | inpaint=473.1ms(migan/DirectML) | render=73.0ms | save=283.0ms +[2025-10-24 15:37:06,189] [LogListener] [DEBUG] [loggerModule.py:debug:275] process_single_image 호출 완료 +[2025-10-24 15:37:06,189] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 결과 반환 중: uid=d0fc3e11-4114-4b3c-a99d-cf3e44f8d2a0 +[2025-10-24 15:37:06,189] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 결과 반환 완료: uid=d0fc3e11-4114-4b3c-a99d-cf3e44f8d2a0 +[2025-10-24 15:37:06,189] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 15:37:06,189] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 33740) +[2025-10-24 15:37:19,578] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔥 작업 수신 성공 +[2025-10-24 15:37:19,578] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🚀 작업 처리 시작: cmd=remove_background, uid=7236aed5-5af0-4bf0-96c0-adcac672d47c +[2025-10-24 15:37:19,579] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 프로세서 toggle_states 업데이트 : {'TEMP_IMAGE_DIR': 'C:\\ProgramData\\ImgWorker\\work', 'output_image_format': 'webp', 'use_local_rembg': True, 'local_rembg_model_path': 'D:\\py\\img_worker\\modules\\rembg_models', 'local_model_name': 'birefnet-general-lite', 'optionIMGTrans_type': 'GPU', 'detail_IMGTrans_type': 'GPU', 'thumb_trans_type': 'GPU', 'migan_use_accel': True, 'migan_onnx_path': 'D:\\py\\img_worker\\modules\\migan_onnx\\migan_pipeline_v2.onnx', 'ocr_provider_override': 'auto', 'migan_provider_override': 'auto'} +[2025-10-24 15:37:19,579] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 실행 직전: cmd=remove_background +[2025-10-24 15:37:19,579] [LogListener] [DEBUG] [loggerModule.py:debug:275] remove_background 호출 직전 +[2025-10-24 15:37:19,579] [LogListener] [DEBUG] [loggerModule.py:debug:275] request_rembg 호출: use_local_rembg=True, local_model_name=birefnet-general-lite +[2025-10-24 15:37:19,581] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔧 백업 rembg 시작: model_name=birefnet-general-lite, object_ratio=0.95, debug_save=True, force_cpu=False +[2025-10-24 15:37:19,582] [LogListener] [DEBUG] [loggerModule.py:debug:275] 📁 디버그 이미지 저장 디렉토리: C:\Users\ADMINI~1\AppData\Local\Temp\wrong_shape_rembg_debug_sd0s7btd +[2025-10-24 15:37:19,595] [LogListener] [DEBUG] [loggerModule.py:debug:275] 💾 1단계 저장 완료: 입력 이미지 (1000, 1000, 3) → C:\Users\ADMINI~1\AppData\Local\Temp\wrong_shape_rembg_debug_sd0s7btd\01_input_image.png +[2025-10-24 15:37:19,595] [LogListener] [DEBUG] [loggerModule.py:debug:275] BriaAI 배경제거 모듈 초기화 시작 +[2025-10-24 15:37:19,595] [LogListener] [DEBUG] [loggerModule.py:debug:275] ONNX Runtime 사용 가능한 프로바이더: ['DmlExecutionProvider', 'CPUExecutionProvider'] +[2025-10-24 15:37:19,596] [LogListener] [DEBUG] [loggerModule.py:debug:275] BriaAI CPU 모드로 설정 +[2025-10-24 15:37:19,596] [LogListener] [DEBUG] [loggerModule.py:debug:275] BriaAI 배경제거 모듈 초기화 완료 +[2025-10-24 15:37:19,596] [LogListener] [DEBUG] [loggerModule.py:debug:275] 백업용 내장 rembg 모듈 초기화됨 +[2025-10-24 15:37:19,596] [LogListener] [WARNING] [loggerModule.py:warning:287] 지원하지 않는 모델명 (birefnet-general-lite). bria-rmbg-1.4로 대체 사용 +[2025-10-24 15:37:19,624] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ 안전한 임시 파일 생성: C:\Users\ADMINI~1\AppData\Local\Temp\rembg_d6a043c0.png (크기: 728060 bytes) +[2025-10-24 15:37:19,625] [LogListener] [DEBUG] [loggerModule.py:debug:275] 💾 2단계 저장 완료: 임시 파일 복사 → C:\Users\ADMINI~1\AppData\Local\Temp\wrong_shape_rembg_debug_sd0s7btd\02_temp_file_copy.png +[2025-10-24 15:37:19,626] [LogListener] [DEBUG] [loggerModule.py:debug:275] 백업 rembg 모듈로 배경 제거 시작: bria-rmbg-1.4 +[2025-10-24 15:37:19,626] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔥 DirectML GPU 모드로 배경 제거 실행 +[2025-10-24 15:37:19,626] [LogListener] [DEBUG] [loggerModule.py:debug:275] BriaAI ONNX 모델 로딩 시도 (원본 providers): D:\py\img_worker\modules\rembg_models\BriaRMBG1.4_model_fp16.onnx +[2025-10-24 15:37:19,772] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ BriaAI ONNX 모델 로딩 완료 (원본 providers) | Providers: ['CPUExecutionProvider'] | Input: input | Output: output +[2025-10-24 15:37:19,853] [LogListener] [DEBUG] [loggerModule.py:debug:275] BriaAI 배경제거 시작: bria-rmbg-1.4 (aggressiveness=0.5) +[2025-10-24 15:37:19,862] [LogListener] [DEBUG] [loggerModule.py:debug:275] 전처리 완료: (1000, 1000, 3) -> (1, 3, 1024, 1024), 값 범위: [-0.500, 0.500] +[2025-10-24 15:37:20,266] [LogListener] [DEBUG] [loggerModule.py:debug:275] ONNX 모델 출력 개수: 1, 첫 번째 출력 shape: (1, 1, 1024, 1024) +[2025-10-24 15:37:20,267] [LogListener] [DEBUG] [loggerModule.py:debug:275] 추론 완료: (1024, 1024), 값 범위: [0.000, 1.000] +[2025-10-24 15:37:20,286] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ BriaAI 배경제거 성공: bria-rmbg-1.4 (CPU, 0.43초) +[2025-10-24 15:37:20,288] [LogListener] [DEBUG] [loggerModule.py:debug:275] BriaAI 마스크 통계: {'min': 0, 'max': 255, 'mean': 107.759077, 'nonzero_count': 437772} +[2025-10-24 15:37:20,288] [LogListener] [DEBUG] [loggerModule.py:debug:275] DirectML GPU 모드로 배경 제거 성공 +[2025-10-24 15:37:20,288] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ 백업 rembg 처리 성공: RGB, 크기: (1000, 1000) +[2025-10-24 15:37:20,333] [LogListener] [DEBUG] [loggerModule.py:debug:275] 💾 3단계 저장 완료: PIL 결과 RGB (1000, 1000) → C:\Users\ADMINI~1\AppData\Local\Temp\wrong_shape_rembg_debug_sd0s7btd\03_pil_result.png +[2025-10-24 15:37:20,333] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔍 PIL 결과 분석: mode=RGB, size=(1000, 1000), format=None +[2025-10-24 15:37:20,333] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔍 PIL 밴드: ('R', 'G', 'B') +[2025-10-24 15:37:20,334] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔄 RGB → BGR 변환: (1000, 1000, 3) +[2025-10-24 15:37:20,348] [LogListener] [DEBUG] [loggerModule.py:debug:275] 💾 4단계 저장 완료: numpy 변환 결과 (1000, 1000, 3) (흰배경 합성 완료) → C:\Users\ADMINI~1\AppData\Local\Temp\wrong_shape_rembg_debug_sd0s7btd\04_numpy_result.png +[2025-10-24 15:37:20,349] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔧 후처리 시작: object_ratio=0.95 +[2025-10-24 15:37:20,349] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🎨 후처리 시작: 입력 이미지 크기 (1000, 1000, 3), object_ratio=0.95 +[2025-10-24 15:37:20,350] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ BGR 이미지 처리: 그레이 < 250인 픽셀 수: 423463 +[2025-10-24 15:37:20,365] [LogListener] [DEBUG] [loggerModule.py:debug:275] 💾 5단계 저장: 초기 마스크 (423463개 픽셀) → C:\Users\ADMINI~1\AppData\Local\Temp\wrong_shape_rembg_debug_sd0s7btd\05_initial_mask.png +[2025-10-24 15:37:20,366] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔧 마스크 정제: 정제 전 423463개 → 정제 후 428608개 픽셀 +[2025-10-24 15:37:20,368] [LogListener] [DEBUG] [loggerModule.py:debug:275] 💾 6단계 저장: 정제된 마스크 (428608개 픽셀) → C:\Users\ADMINI~1\AppData\Local\Temp\wrong_shape_rembg_debug_sd0s7btd\06_refined_mask.png +[2025-10-24 15:37:20,370] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔍 연결 요소 분석: 3개 객체 발견 +[2025-10-24 15:37:20,371] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🎯 최대 연결 요소 선택: 428587개 픽셀 +[2025-10-24 15:37:20,373] [LogListener] [DEBUG] [loggerModule.py:debug:275] 💾 7단계 저장: 연결 요소 마스크 (428587개 픽셀) → C:\Users\ADMINI~1\AppData\Local\Temp\wrong_shape_rembg_debug_sd0s7btd\07_component_mask.png +[2025-10-24 15:37:20,376] [LogListener] [DEBUG] [loggerModule.py:debug:275] 📐 바운딩 박스 계산: 유효 픽셀 428587개 +[2025-10-24 15:37:20,377] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✂️ 크롭 영역: (187,38) ~ (812,963), 크기: (926, 626, 3) +[2025-10-24 15:37:20,388] [LogListener] [DEBUG] [loggerModule.py:debug:275] 💾 9단계 저장: 바운딩 박스 크롭 (926, 626, 3) → C:\Users\ADMINI~1\AppData\Local\Temp\wrong_shape_rembg_debug_sd0s7btd\09_bbox_crop.png +[2025-10-24 15:37:20,389] [LogListener] [DEBUG] [loggerModule.py:debug:275] 📏 마진 추가: 9px, 최종 크기: (944, 644, 3) +[2025-10-24 15:37:20,400] [LogListener] [DEBUG] [loggerModule.py:debug:275] 💾 10단계 저장: 마진 추가 (944, 644, 3) → C:\Users\ADMINI~1\AppData\Local\Temp\wrong_shape_rembg_debug_sd0s7btd\10_with_margin.png +[2025-10-24 15:37:20,400] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ 이미지 처리 완료 (이미 흰배경 합성됨): (944, 644, 3) +[2025-10-24 15:37:20,412] [LogListener] [DEBUG] [loggerModule.py:debug:275] 💾 11단계 저장: 흰 배경 합성 (944, 644, 3) → C:\Users\ADMINI~1\AppData\Local\Temp\wrong_shape_rembg_debug_sd0s7btd\11_white_background.png +[2025-10-24 15:37:20,412] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🎯 1000x1000 리사이즈 시작: object_ratio=0.95, final_img.shape=(944, 644, 3) +[2025-10-24 15:37:20,413] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🎯 정사각형 캔버스 생성 시작: 입력=944x644, 목표=1000x1000, 객체비율=0.95 +[2025-10-24 15:37:20,414] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ 이미지 리사이즈 완료: 944x644 → 950x648 (scale_factor=1.006) +[2025-10-24 15:37:20,415] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ 정사각형 캔버스 생성: (1000, 1000, 3) +[2025-10-24 15:37:20,415] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ 이미지 배치 완료: offset=(176,25), size=(648,950) +[2025-10-24 15:37:20,420] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ 정사각형 캔버스 완성: 95% 스케일링, 1000x1000, DPI 72 +[2025-10-24 15:37:20,420] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ 1000x1000 리사이즈 완료: (1000, 1000, 3) +[2025-10-24 15:37:20,420] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ 백업 rembg 후처리 완료: (1000, 1000, 3) +[2025-10-24 15:37:20,434] [LogListener] [DEBUG] [loggerModule.py:debug:275] 💾 최종 결과 저장: (1000, 1000, 3) → C:\Users\ADMINI~1\AppData\Local\Temp\wrong_shape_rembg_debug_sd0s7btd\99_final_result.png +[2025-10-24 15:37:20,435] [LogListener] [DEBUG] [loggerModule.py:debug:275] 임시 파일 삭제 완료: C:\Users\ADMINI~1\AppData\Local\Temp\rembg_d6a043c0.png +[2025-10-24 15:37:20,449] [LogListener] [DEBUG] [loggerModule.py:debug:275] 배경제거 이미지 저장: C:\ProgramData\ImgWorker\work\nobg_thumb_rmb_dl_fff8da04aa7b.png +[2025-10-24 15:37:20,456] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔍 ONNX OCR 감지 방식: polygon +[2025-10-24 15:37:20,851] [LogListener] [DEBUG] [loggerModule.py:debug:275] ⚡ ONNX OCR 추론 완료: 392.4ms +[2025-10-24 15:37:20,852] [LogListener] [DEBUG] [loggerModule.py:debug:275] 📊 세부 시간 - 감지: 129.4ms, 인식: 250.0ms, 분류: 10.0ms +[2025-10-24 15:37:20,852] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): 'FM' +[2025-10-24 15:37:20,852] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): 'AM' +[2025-10-24 15:37:20,852] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): 'BTTFUSB' +[2025-10-24 15:37:20,852] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): 'SWWB' +[2025-10-24 15:37:20,852] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): '·R11·.' +[2025-10-24 15:37:20,853] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): 'IOTUNE' +[2025-10-24 15:37:20,853] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): 'RADIO/BT/MUSICPLAYER' +[2025-10-24 15:37:20,853] [ResultListener] [WARNING] [loggerModule.py:warning:287] 워커 롤링 스케줄: reason=job-count-threshold +[2025-10-24 15:37:20,853] [LogListener] [DEBUG] [loggerModule.py:debug:275] 필터링 결과: 0/7개 (신뢰도 + & 중국어) +[2025-10-24 15:37:20,853] [LogListener] [DEBUG] [loggerModule.py:debug:275] 중국어 텍스트 0개 필터링 완료 +[2025-10-24 15:37:20,853] [WorkerRoller] [WARNING] [loggerModule.py:warning:287] 워커 롤링 시작: job-count-threshold +[2025-10-24 15:37:20,853] [LogListener] [DEBUG] [loggerModule.py:debug:275] remove_background 호출 완료 +[2025-10-24 15:37:20,853] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 결과 반환 중: uid=7236aed5-5af0-4bf0-96c0-adcac672d47c +[2025-10-24 15:37:20,854] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 결과 반환 완료: uid=7236aed5-5af0-4bf0-96c0-adcac672d47c +[2025-10-24 15:37:20,854] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 15:37:20,854] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 33740) +[2025-10-24 15:37:20,908] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔥 작업 수신 성공 +[2025-10-24 15:37:20,908] [LogListener] [DEBUG] [loggerModule.py:debug:275] Shutdown signal 수신 → 종료 +[2025-10-24 15:37:20,908] [LogListener] [DEBUG] [loggerModule.py:debug:275] OCR 모듈 정리 완료 +[2025-10-24 15:37:20,908] [LogListener] [DEBUG] [loggerModule.py:debug:275] 마스크 모듈 정리 완료 +[2025-10-24 15:37:20,931] [LogListener] [DEBUG] [loggerModule.py:debug:275] 임시 폴더 삭제됨: C:\ProgramData\ImgWorker\work +[2025-10-24 15:37:20,932] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 프로세서 소멸 +[2025-10-24 15:37:21,304] [WorkerRoller] [INFO] [loggerModule.py:info:281] 워커 프로세스 기동: PID=308 +[2025-10-24 15:37:21,852] [LogListener] [DEBUG] [loggerModule.py:debug:275] ImageWorker 프로세스 기동 (PID=308, Name=ImageWorkerProcess) +[2025-10-24 15:37:21,852] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔧 ImageProcessor3 초기화 시작... +[2025-10-24 15:37:21,852] [LogListener] [DEBUG] [loggerModule.py:debug:275] tracemalloc 메모리 추적 시작 +[2025-10-24 15:37:21,859] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ DirectML 환경 준비 완료 (Windows DirectX 12 기반) +[2025-10-24 15:37:21,859] [LogListener] [DEBUG] [loggerModule.py:debug:275] === 🚀 DirectML GPU 상태 초기화 시작 🚀 === +[2025-10-24 15:37:21,859] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🎯 사용자 GPU 가속 요청: False +[2025-10-24 15:37:21,860] [LogListener] [DEBUG] [loggerModule.py:debug:275] 💻 현재 운영체제: Windows +[2025-10-24 15:37:21,860] [LogListener] [DEBUG] [loggerModule.py:debug:275] GPU 가속이 비활성화됨 (toggle_states['use_cuda'] = False) +[2025-10-24 15:37:21,860] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔒 안전한 CPU 모드로 모든 GPU 설정 강제 비활성화 +[2025-10-24 15:37:21,860] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔧 ImageProcessor3 GPU 상태 요약: +[2025-10-24 15:37:21,860] [LogListener] [DEBUG] [loggerModule.py:debug:275] - CUDA 사용 가능: False +[2025-10-24 15:37:21,860] [LogListener] [DEBUG] [loggerModule.py:debug:275] - toggle_states['use_cuda']: NOT_SET +[2025-10-24 15:37:21,860] [LogListener] [DEBUG] [loggerModule.py:debug:275] - GPU 하드웨어 정보: {} +[2025-10-24 15:37:21,860] [LogListener] [DEBUG] [loggerModule.py:debug:275] ImageProcessor3 Init toggle_states: {'TEMP_IMAGE_DIR': 'C:\\ProgramData\\ImgWorker\\work', 'output_image_format': 'webp', 'use_local_rembg': True, 'local_rembg_model_path': 'D:\\py\\img_worker\\modules\\rembg_models', 'local_model_name': 'birefnet-general-lite', 'optionIMGTrans_type': 'CPU', 'detail_IMGTrans_type': 'CPU', 'thumb_trans_type': 'CPU', 'migan_use_accel': True, 'migan_onnx_path': 'D:\\py\\img_worker\\modules\\migan_onnx\\migan_pipeline_v2.onnx', 'ocr_provider_override': 'auto', 'migan_provider_override': 'auto'} +[2025-10-24 15:37:21,860] [LogListener] [DEBUG] [loggerModule.py:debug:275] is_member_valid: False +[2025-10-24 15:37:21,860] [LogListener] [DEBUG] [loggerModule.py:debug:275] debug_images 디렉토리 이미 존재: D:\py\img_worker\modules\debug_images +[2025-10-24 15:37:21,860] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.font_path: D:\py\img_worker\modules\fonts\HakgyoansimDunggeunmisoTTFB.ttf +[2025-10-24 15:37:21,860] [LogListener] [DEBUG] [loggerModule.py:debug:275] toggle_states font_path: D:\py\img_worker\modules\fonts\HakgyoansimDunggeunmisoTTFB.ttf +[2025-10-24 15:37:21,861] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.TEMP_IMAGE_DIR: C:\ProgramData\ImgWorker\work +[2025-10-24 15:37:21,861] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.debugging_save_Dir: D:\py\img_worker\modules\debug_images +[2025-10-24 15:37:21,861] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.unwanted_texts: {} +[2025-10-24 15:37:21,861] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.inpaint_method: migan +[2025-10-24 15:37:21,861] [LogListener] [DEBUG] [loggerModule.py:debug:275] Image.MAX_IMAGE_PIXELS set to 20000000 +[2025-10-24 15:37:21,861] [LogListener] [DEBUG] [loggerModule.py:debug:275] ONNX 모델 타입 설정값: 자동 선택, GPU 정보: {} +[2025-10-24 15:37:21,861] [LogListener] [DEBUG] [loggerModule.py:debug:275] 자동 선택 모드: GPU 추천 모델 simp 사용 +[2025-10-24 15:37:21,861] [LogListener] [DEBUG] [loggerModule.py:debug:275] ONNX OCR 모델 타입 결정: simp (GPU: False) +[2025-10-24 15:37:21,861] [LogListener] [DEBUG] [loggerModule.py:debug:275] ONNX OCR 모듈 CPU 모드로 설정 +[2025-10-24 15:37:21,861] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🚀 ONNX TextSystem 초기화 시작 (CPU 모드) +[2025-10-24 15:37:21,861] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🚀 ONNX TextSystem 초기화 시작 (CPU 모드) +[2025-10-24 15:37:23,423] [LogListener] [DEBUG] [loggerModule.py:debug:275] 문자 사전 파일 UTF-8 확인 완료: D:\py\img_worker\modules\onnx_ocr_module\dict\ppocr_keys_v1.txt +[2025-10-24 15:37:23,507] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ ONNX TextSystem 초기화 완료 (CPU + SIMP (호환성) 모델) +[2025-10-24 15:37:23,507] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ ONNX TextSystem 초기화 완료 (CPU + SIMP 모델) +[2025-10-24 15:37:23,508] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ ONNX OCR 모듈 초기화 성공 (CPU 모드) +[2025-10-24 15:37:23,508] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ ONNX OCR 모듈 초기화 성공 +[2025-10-24 15:37:23,508] [LogListener] [DEBUG] [loggerModule.py:debug:275] 마스크 모듈 초기화 완료 +[2025-10-24 15:37:23,508] [LogListener] [DEBUG] [loggerModule.py:debug:275] MaskModule 초기화 성공 +[2025-10-24 15:37:23,508] [LogListener] [DEBUG] [loggerModule.py:debug:275] 텍스트 렌더링 모듈 초기화 완료 +[2025-10-24 15:37:23,508] [LogListener] [DEBUG] [loggerModule.py:debug:275] 기본 폰트: D:\py\img_worker\modules\fonts\HakgyoansimDunggeunmisoTTFB.ttf +[2025-10-24 15:37:23,508] [LogListener] [DEBUG] [loggerModule.py:debug:275] TextRenderingModule 초기화 성공 +[2025-10-24 15:37:23,509] [LogListener] [DEBUG] [loggerModule.py:debug:275] 폰트 로드 성공: D:\py\img_worker\modules\fonts\HakgyoansimDunggeunmisoTTFB.ttf +[2025-10-24 15:37:23,509] [LogListener] [DEBUG] [loggerModule.py:debug:275] PostImageManager 초기화 성공 +[2025-10-24 15:37:23,509] [LogListener] [DEBUG] [loggerModule.py:debug:275] Request_AI_Server GPU 상태: CUDA 사용 가능=False +[2025-10-24 15:37:23,509] [LogListener] [DEBUG] [loggerModule.py:debug:275] Request_AI_Server 초기화 성공 +[2025-10-24 15:37:23,509] [LogListener] [DEBUG] [loggerModule.py:debug:275] GoogleTranslate 초기화 성공 +[2025-10-24 15:37:23,509] [LogListener] [DEBUG] [loggerModule.py:debug:275] MIGAN CUDA 사용 불가 - CPU 모드로 설정 +[2025-10-24 15:37:23,509] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] GPU 관리자 전달: GPUManager, can_use_cuda: False +[2025-10-24 15:37:23,727] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] GPU 관리자 사용 가능: False +[2025-10-24 15:37:23,727] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 사용 가능한 providers: ['DmlExecutionProvider', 'CPUExecutionProvider'] +[2025-10-24 15:37:23,727] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] DirectML 가속 활성화 +[2025-10-24 15:37:23,727] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 최종 providers: [('DmlExecutionProvider', {}), ('CPUExecutionProvider', {})] +[2025-10-24 15:37:23,727] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] DirectML 가속 시도: [('DmlExecutionProvider', {}), ('CPUExecutionProvider', {})] +[2025-10-24 15:37:23,727] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] DirectML 가속 성공! 실제 providers: ['DmlExecutionProvider', 'CPUExecutionProvider'] +[2025-10-24 15:37:23,728] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 입력 0: image, 형태: ['batch_size', 3, 'height', 'width'], 타입: tensor(uint8) +[2025-10-24 15:37:23,728] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 입력 1: mask, 형태: ['batch_size', 1, 'height', 'width'], 타입: tensor(uint8) +[2025-10-24 15:37:23,728] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 출력 0: result, 형태: ['ScatterNDresult_dim_0', 3, 'ScatterNDresult_dim_2', 'ScatterNDresult_dim_3'], 타입: tensor(uint8) +[2025-10-24 15:37:23,728] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 세션 준비 완료. providers=['DmlExecutionProvider', 'CPUExecutionProvider'] +[2025-10-24 15:37:23,728] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] GPU 관리자 연결 완료: GPUManager +[2025-10-24 15:37:23,728] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 파이프라인 gpu_manager 속성: True, 값: +[2025-10-24 15:37:23,728] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 초기화 완료: gpu_manager 속성=True, 값= +[2025-10-24 15:37:23,728] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔰 OCR 모듈 Warm-up 시작... +[2025-10-24 15:37:23,728] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔍 ONNX OCR 감지 방식: polygon +[2025-10-24 15:37:23,732] [LogListener] [DEBUG] [loggerModule.py:debug:275] ⚡ ONNX OCR 추론 완료: 4.0ms +[2025-10-24 15:37:23,733] [LogListener] [DEBUG] [loggerModule.py:debug:275] 📊 세부 시간 - 감지: 3.0ms, 인식: 0.0ms, 분류: 0.0ms +[2025-10-24 15:37:23,733] [LogListener] [WARNING] [loggerModule.py:warning:287] ⚠️ ONNX OCR 결과가 비어있습니다 +[2025-10-24 15:37:23,736] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ OCR 모듈 Warm-up 성공 +[2025-10-24 15:37:23,736] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔰 ImageProcessor Warm‑up 완료 +[2025-10-24 15:37:23,736] [ResultListener] [INFO] [loggerModule.py:info:281] 워커 READY 수신 +[2025-10-24 15:37:23,736] [WorkerRoller] [INFO] [loggerModule.py:info:281] 버퍼 작업 재개: 1건 +[2025-10-24 15:37:23,736] [ResultListener] [INFO] [loggerModule.py:info:281] 워커 READY 수신 +[2025-10-24 15:37:23,737] [LogListener] [DEBUG] [loggerModule.py:debug:275] 📡 추가 READY 신호 전송 완료 +[2025-10-24 15:37:23,737] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 15:37:23,737] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 308) +[2025-10-24 15:37:23,737] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔥 작업 수신 성공 +[2025-10-24 15:37:23,737] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🚀 작업 처리 시작: cmd=process_single_image, uid=1f2dcd4f-b8e2-4d08-8f24-6b3bc3d77e7e +[2025-10-24 15:37:23,737] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 프로세서 toggle_states 업데이트 : {'TEMP_IMAGE_DIR': 'C:\\ProgramData\\ImgWorker\\work', 'output_image_format': 'webp', 'use_local_rembg': True, 'local_rembg_model_path': 'D:\\py\\img_worker\\modules\\rembg_models', 'local_model_name': 'birefnet-general-lite', 'optionIMGTrans_type': 'GPU', 'detail_IMGTrans_type': 'GPU', 'thumb_trans_type': 'GPU', 'migan_use_accel': True, 'migan_onnx_path': 'D:\\py\\img_worker\\modules\\migan_onnx\\migan_pipeline_v2.onnx', 'ocr_provider_override': 'auto', 'migan_provider_override': 'auto', 'font_type': '폰트5', 'unwanted_texts': ['가격설명', '무료배송', '보증', '사은품', '세일', '이벤트', '특가', '품절', '할인', '할인가'], 'is_member_valid': False, 'authenticated_by_admin': True} +[2025-10-24 15:37:23,738] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 실행 직전: cmd=process_single_image +[2025-10-24 15:37:23,738] [LogListener] [DEBUG] [loggerModule.py:debug:275] process_single_image 호출 직전 +[2025-10-24 15:37:23,740] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 번역시작 +[2025-10-24 15:37:24,240] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 2 처리 시작: C:\ProgramData\ImgWorker\incoming\dl_a4d295484440.jpg - 전체 번역 모드 +[2025-10-24 15:37:24,245] [LogListener] [DEBUG] [loggerModule.py:debug:275] 로컬 파일 경로 감지, 다운로드 생략: C:\ProgramData\ImgWorker\incoming\dl_a4d295484440.jpg +[2025-10-24 15:37:24,246] [LogListener] [DEBUG] [loggerModule.py:debug:275] 옵션 이미지는 스케일 처리 건너뛰기: thumb +[2025-10-24 15:37:24,246] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 2 로컬 저장위치(썸네일 스케일 처리 완료): C:\ProgramData\ImgWorker\incoming\dl_a4d295484440.jpg +[2025-10-24 15:37:24,247] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔍 ONNX OCR 감지 방식: polygon +[2025-10-24 15:37:24,462] [LogListener] [DEBUG] [loggerModule.py:debug:275] ⚡ ONNX OCR 추론 완료: 214.0ms +[2025-10-24 15:37:24,462] [LogListener] [DEBUG] [loggerModule.py:debug:275] 📊 세부 시간 - 감지: 91.0ms, 인식: 116.0ms, 분류: 4.0ms +[2025-10-24 15:37:24,491] [LogListener] [DEBUG] [loggerModule.py:debug:275] 메모리 변화 [OCR 처리]: 42182.2MB -> 42339.7MB (+157.5MB, +0.4%) - 이미지 2 +[2025-10-24 15:37:24,534] [LogListener] [DEBUG] [loggerModule.py:debug:275] ocr_results: [{'text': '1', 'confidence': 0.5538947582244873, 'polygon': [[242.0, 181.0], [580.0, 181.0], [580.0, 204.0], [242.0, 204.0]], 'bbox': (242, 181, 339, 24), 'method': 'polygon'}, {'text': 'AGIO', 'confidence': 0.6274033784866333, 'polygon': [[426.0, 343.0], [454.0, 345.0], [453.0, 355.0], [425.0, 353.0]], 'bbox': (425, 343, 30, 13), 'method': 'polygon'}, {'text': 'II', 'confidence': 0.6672232151031494, 'polygon': [[272.0, 508.0], [304.0, 528.0], [249.0, 622.0], [217.0, 602.0]], 'bbox': (217, 508, 88, 115), 'method': 'polygon'}] +[2025-10-24 15:37:24,534] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): '1' +[2025-10-24 15:37:24,535] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): 'AGIO' +[2025-10-24 15:37:24,535] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): 'II' +[2025-10-24 15:37:24,535] [LogListener] [DEBUG] [loggerModule.py:debug:275] 필터링 결과: 0/3개 (신뢰도 + & 중국어) +[2025-10-24 15:37:24,535] [LogListener] [DEBUG] [loggerModule.py:debug:275] filter_ocr_results: [] +[2025-10-24 15:37:24,535] [LogListener] [DEBUG] [loggerModule.py:debug:275] 중국어 텍스트 0개 필터링 완료 +[2025-10-24 15:37:24,535] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 2 중국어 텍스트 없음 - 정상 케이스 (NO_TEXT) +[2025-10-24 15:37:24,535] [LogListener] [DEBUG] [loggerModule.py:debug:275] ⏱ 이미지 파이프라인 총 795.1ms | download=0.0ms | ocr=222.0ms +[2025-10-24 15:37:24,535] [LogListener] [DEBUG] [loggerModule.py:debug:275] process_single_image 호출 완료 +[2025-10-24 15:37:24,535] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 결과 반환 중: uid=1f2dcd4f-b8e2-4d08-8f24-6b3bc3d77e7e +[2025-10-24 15:37:24,535] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 결과 반환 완료: uid=1f2dcd4f-b8e2-4d08-8f24-6b3bc3d77e7e +[2025-10-24 15:37:24,535] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 15:37:24,535] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 308) +[2025-10-24 15:37:27,170] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔥 작업 수신 성공 +[2025-10-24 15:37:27,170] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🚀 작업 처리 시작: cmd=process_single_image, uid=0f621dec-1b88-4778-8814-b03e52203b3f +[2025-10-24 15:37:27,170] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 프로세서 toggle_states 업데이트 : {'TEMP_IMAGE_DIR': 'C:\\ProgramData\\ImgWorker\\work', 'output_image_format': 'webp', 'use_local_rembg': True, 'local_rembg_model_path': 'D:\\py\\img_worker\\modules\\rembg_models', 'local_model_name': 'birefnet-general-lite', 'optionIMGTrans_type': 'GPU', 'detail_IMGTrans_type': 'GPU', 'thumb_trans_type': 'GPU', 'migan_use_accel': True, 'migan_onnx_path': 'D:\\py\\img_worker\\modules\\migan_onnx\\migan_pipeline_v2.onnx', 'ocr_provider_override': 'auto', 'migan_provider_override': 'auto', 'font_type': '폰트5', 'unwanted_texts': ['가격설명', '무료배송', '보증', '사은품', '세일', '이벤트', '특가', '품절', '할인', '할인가'], 'is_member_valid': False, 'authenticated_by_admin': True} +[2025-10-24 15:37:27,171] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 실행 직전: cmd=process_single_image +[2025-10-24 15:37:27,171] [LogListener] [DEBUG] [loggerModule.py:debug:275] process_single_image 호출 직전 +[2025-10-24 15:37:27,171] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 번역시작 +[2025-10-24 15:37:27,494] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 3 처리 시작: C:\ProgramData\ImgWorker\incoming\dl_34d37d7a3842.jpg - 전체 번역 모드 +[2025-10-24 15:37:27,499] [LogListener] [DEBUG] [loggerModule.py:debug:275] 로컬 파일 경로 감지, 다운로드 생략: C:\ProgramData\ImgWorker\incoming\dl_34d37d7a3842.jpg +[2025-10-24 15:37:27,499] [LogListener] [DEBUG] [loggerModule.py:debug:275] 옵션 이미지는 스케일 처리 건너뛰기: thumb +[2025-10-24 15:37:27,499] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 3 로컬 저장위치(썸네일 스케일 처리 완료): C:\ProgramData\ImgWorker\incoming\dl_34d37d7a3842.jpg +[2025-10-24 15:37:27,501] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔍 ONNX OCR 감지 방식: polygon +[2025-10-24 15:37:28,035] [LogListener] [DEBUG] [loggerModule.py:debug:275] ⚡ ONNX OCR 추론 완료: 534.0ms +[2025-10-24 15:37:28,045] [LogListener] [DEBUG] [loggerModule.py:debug:275] 📊 세부 시간 - 감지: 87.0ms, 인식: 437.0ms, 분류: 5.0ms +[2025-10-24 15:37:28,045] [LogListener] [DEBUG] [loggerModule.py:debug:275] 메모리 변화 [OCR 처리]: 42426.1MB -> 42553.7MB (+127.6MB, +0.3%) - 이미지 3 +[2025-10-24 15:37:28,046] [LogListener] [DEBUG] [loggerModule.py:debug:275] ocr_results: [{'text': 'SOSE号7', 'confidence': 0.5055533051490784, 'polygon': [[166.0, 149.0], [653.0, 149.0], [653.0, 211.0], [166.0, 211.0]], 'bbox': (166, 149, 488, 63), 'method': 'polygon'}, {'text': '百O0Y百SOY', 'confidence': 0.5515749454498291, 'polygon': [[146.0, 231.0], [695.0, 230.0], [695.0, 250.0], [146.0, 251.0]], 'bbox': (146, 230, 550, 22), 'method': 'polygon'}, {'text': '100', 'confidence': 0.997102677822113, 'polygon': [[43.0, 648.0], [165.0, 648.0], [165.0, 728.0], [43.0, 728.0]], 'bbox': (43, 648, 123, 81), 'method': 'polygon'}, {'text': 'dB', 'confidence': 0.5481783747673035, 'polygon': [[162.0, 679.0], [216.0, 684.0], [211.0, 735.0], [157.0, 730.0]], 'bbox': (157, 679, 60, 57), 'method': 'polygon'}] +[2025-10-24 15:37:28,046] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 50.6%): 'SOSE号7' +[2025-10-24 15:37:28,046] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 55.2%): '百O0Y百SOY' +[2025-10-24 15:37:28,046] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): '100' +[2025-10-24 15:37:28,046] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): 'dB' +[2025-10-24 15:37:28,046] [LogListener] [DEBUG] [loggerModule.py:debug:275] 필터링 결과: 2/4개 (신뢰도 + & 중국어) +[2025-10-24 15:37:28,046] [LogListener] [DEBUG] [loggerModule.py:debug:275] filter_ocr_results: [{'text': 'SOSE号7', 'confidence': 0.5055533051490784, 'polygon': [[166.0, 149.0], [653.0, 149.0], [653.0, 211.0], [166.0, 211.0]], 'bbox': (166, 149, 488, 63), 'method': 'polygon'}, {'text': '百O0Y百SOY', 'confidence': 0.5515749454498291, 'polygon': [[146.0, 231.0], [695.0, 230.0], [695.0, 250.0], [146.0, 251.0]], 'bbox': (146, 230, 550, 22), 'method': 'polygon'}] +[2025-10-24 15:37:28,046] [LogListener] [DEBUG] [loggerModule.py:debug:275] 중국어 텍스트 2개 필터링 완료 +[2025-10-24 15:37:28,046] [LogListener] [DEBUG] [loggerModule.py:debug:275] 한글 텍스트 0개 필터링 완료 +[2025-10-24 15:37:30,244] [LogListener] [DEBUG] [loggerModule.py:debug:275] translated_texts: ['소세 7호', '헌드레드 SOY'] +[2025-10-24 15:37:30,244] [LogListener] [DEBUG] [loggerModule.py:debug:275] 마스크 생성 완료 +[2025-10-24 15:37:30,244] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 3 OCR 권한 없음, 전체 번역 모드 +[2025-10-24 15:37:30,251] [LogListener] [DEBUG] [loggerModule.py:debug:275] [AUTO Inpaint] coverage=0.117, comps=1, min_center_dist=0.000 → request +[2025-10-24 15:37:30,251] [LogListener] [DEBUG] [loggerModule.py:debug:275] ocr_count: 2 +[2025-10-24 15:37:30,251] [LogListener] [DEBUG] [loggerModule.py:debug:275] is_member_valid: False +[2025-10-24 15:37:30,252] [LogListener] [DEBUG] [loggerModule.py:debug:275] MIGAN 인페인팅 시도 +[2025-10-24 15:37:30,254] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 입력 형태 - 이미지: (1, 3, 800, 800), 마스크: (1, 1, 800, 800) +[2025-10-24 15:37:30,727] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 출력 형태: (800, 800, 3), dtype: uint8 +[2025-10-24 15:37:30,727] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 추론 완료: 474.01 ms +[2025-10-24 15:37:30,735] [LogListener] [DEBUG] [loggerModule.py:debug:275] MIGAN 인페인팅 성공 +[2025-10-24 15:37:30,735] [LogListener] [DEBUG] [loggerModule.py:debug:275] 메모리 변화 [인페인팅]: 42656.4MB -> 42854.8MB (+198.4MB, +0.5%) - 방법: migan +[2025-10-24 15:37:30,735] [LogListener] [DEBUG] [loggerModule.py:debug:275] 인페인팅 완료 +[2025-10-24 15:37:30,765] [LogListener] [DEBUG] [loggerModule.py:debug:275] 텍스트 렌더링 완료 +[2025-10-24 15:37:30,766] [LogListener] [DEBUG] [loggerModule.py:debug:275] watermark_text: 이미지 저작권 보유 +[2025-10-24 15:37:30,766] [LogListener] [DEBUG] [loggerModule.py:debug:275] is_watermark_enabled: True +[2025-10-24 15:37:31,080] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 저장 완료 : C:\ProgramData\ImgWorker\work\translated_thumb_img_3.webp +[2025-10-24 15:37:31,081] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 3 번역 완료: C:\ProgramData\ImgWorker\work\translated_thumb_img_3.webp +[2025-10-24 15:37:31,082] [LogListener] [DEBUG] [loggerModule.py:debug:275] ⏱ 이미지 파이프라인 총 3909.5ms | download=0.0ms | ocr=541.0ms | translate=2197.8ms | mask=2197.8ms | inpaint=490.0ms(migan/DirectML) | render=29.0ms | save=282.0ms +[2025-10-24 15:37:31,082] [LogListener] [DEBUG] [loggerModule.py:debug:275] process_single_image 호출 완료 +[2025-10-24 15:37:31,083] [ResultListener] [WARNING] [loggerModule.py:warning:287] 워커 롤링 스케줄: reason=job-count-threshold +[2025-10-24 15:37:31,083] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 결과 반환 중: uid=0f621dec-1b88-4778-8814-b03e52203b3f +[2025-10-24 15:37:31,083] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 결과 반환 완료: uid=0f621dec-1b88-4778-8814-b03e52203b3f +[2025-10-24 15:37:31,083] [WorkerRoller] [WARNING] [loggerModule.py:warning:287] 워커 롤링 시작: job-count-threshold +[2025-10-24 15:37:31,083] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 15:37:31,083] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 308) +[2025-10-24 15:37:31,099] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔥 작업 수신 성공 +[2025-10-24 15:37:31,136] [LogListener] [DEBUG] [loggerModule.py:debug:275] Shutdown signal 수신 → 종료 +[2025-10-24 15:37:31,136] [LogListener] [DEBUG] [loggerModule.py:debug:275] OCR 모듈 정리 완료 +[2025-10-24 15:37:31,136] [LogListener] [DEBUG] [loggerModule.py:debug:275] 마스크 모듈 정리 완료 +[2025-10-24 15:37:31,137] [LogListener] [DEBUG] [loggerModule.py:debug:275] 임시 폴더 삭제됨: C:\ProgramData\ImgWorker\work +[2025-10-24 15:37:31,137] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 프로세서 소멸 +[2025-10-24 15:37:31,519] [WorkerRoller] [INFO] [loggerModule.py:info:281] 워커 프로세스 기동: PID=31428 +[2025-10-24 15:37:32,023] [LogListener] [DEBUG] [loggerModule.py:debug:275] ImageWorker 프로세스 기동 (PID=31428, Name=ImageWorkerProcess) +[2025-10-24 15:37:32,023] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔧 ImageProcessor3 초기화 시작... +[2025-10-24 15:37:32,023] [LogListener] [DEBUG] [loggerModule.py:debug:275] tracemalloc 메모리 추적 시작 +[2025-10-24 15:37:32,030] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ DirectML 환경 준비 완료 (Windows DirectX 12 기반) +[2025-10-24 15:37:32,030] [LogListener] [DEBUG] [loggerModule.py:debug:275] === 🚀 DirectML GPU 상태 초기화 시작 🚀 === +[2025-10-24 15:37:32,030] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🎯 사용자 GPU 가속 요청: False +[2025-10-24 15:37:32,031] [LogListener] [DEBUG] [loggerModule.py:debug:275] 💻 현재 운영체제: Windows +[2025-10-24 15:37:32,031] [LogListener] [DEBUG] [loggerModule.py:debug:275] GPU 가속이 비활성화됨 (toggle_states['use_cuda'] = False) +[2025-10-24 15:37:32,031] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔒 안전한 CPU 모드로 모든 GPU 설정 강제 비활성화 +[2025-10-24 15:37:32,031] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔧 ImageProcessor3 GPU 상태 요약: +[2025-10-24 15:37:32,031] [LogListener] [DEBUG] [loggerModule.py:debug:275] - CUDA 사용 가능: False +[2025-10-24 15:37:32,031] [LogListener] [DEBUG] [loggerModule.py:debug:275] - toggle_states['use_cuda']: NOT_SET +[2025-10-24 15:37:32,031] [LogListener] [DEBUG] [loggerModule.py:debug:275] - GPU 하드웨어 정보: {} +[2025-10-24 15:37:32,031] [LogListener] [DEBUG] [loggerModule.py:debug:275] ImageProcessor3 Init toggle_states: {'TEMP_IMAGE_DIR': 'C:\\ProgramData\\ImgWorker\\work', 'output_image_format': 'webp', 'use_local_rembg': True, 'local_rembg_model_path': 'D:\\py\\img_worker\\modules\\rembg_models', 'local_model_name': 'birefnet-general-lite', 'optionIMGTrans_type': 'CPU', 'detail_IMGTrans_type': 'CPU', 'thumb_trans_type': 'CPU', 'migan_use_accel': True, 'migan_onnx_path': 'D:\\py\\img_worker\\modules\\migan_onnx\\migan_pipeline_v2.onnx', 'ocr_provider_override': 'auto', 'migan_provider_override': 'auto'} +[2025-10-24 15:37:32,031] [LogListener] [DEBUG] [loggerModule.py:debug:275] is_member_valid: False +[2025-10-24 15:37:32,031] [LogListener] [DEBUG] [loggerModule.py:debug:275] debug_images 디렉토리 이미 존재: D:\py\img_worker\modules\debug_images +[2025-10-24 15:37:32,031] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.font_path: D:\py\img_worker\modules\fonts\HakgyoansimDunggeunmisoTTFB.ttf +[2025-10-24 15:37:32,032] [LogListener] [DEBUG] [loggerModule.py:debug:275] toggle_states font_path: D:\py\img_worker\modules\fonts\HakgyoansimDunggeunmisoTTFB.ttf +[2025-10-24 15:37:32,032] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.TEMP_IMAGE_DIR: C:\ProgramData\ImgWorker\work +[2025-10-24 15:37:32,032] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.debugging_save_Dir: D:\py\img_worker\modules\debug_images +[2025-10-24 15:37:32,032] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.unwanted_texts: {} +[2025-10-24 15:37:32,032] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.inpaint_method: migan +[2025-10-24 15:37:32,032] [LogListener] [DEBUG] [loggerModule.py:debug:275] Image.MAX_IMAGE_PIXELS set to 20000000 +[2025-10-24 15:37:32,032] [LogListener] [DEBUG] [loggerModule.py:debug:275] ONNX 모델 타입 설정값: 자동 선택, GPU 정보: {} +[2025-10-24 15:37:32,032] [LogListener] [DEBUG] [loggerModule.py:debug:275] 자동 선택 모드: GPU 추천 모델 simp 사용 +[2025-10-24 15:37:32,032] [LogListener] [DEBUG] [loggerModule.py:debug:275] ONNX OCR 모델 타입 결정: simp (GPU: False) +[2025-10-24 15:37:32,032] [LogListener] [DEBUG] [loggerModule.py:debug:275] ONNX OCR 모듈 CPU 모드로 설정 +[2025-10-24 15:37:32,032] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🚀 ONNX TextSystem 초기화 시작 (CPU 모드) +[2025-10-24 15:37:32,032] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🚀 ONNX TextSystem 초기화 시작 (CPU 모드) +[2025-10-24 15:37:33,733] [LogListener] [DEBUG] [loggerModule.py:debug:275] 문자 사전 파일 UTF-8 확인 완료: D:\py\img_worker\modules\onnx_ocr_module\dict\ppocr_keys_v1.txt +[2025-10-24 15:37:33,820] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ ONNX TextSystem 초기화 완료 (CPU + SIMP (호환성) 모델) +[2025-10-24 15:37:33,820] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ ONNX TextSystem 초기화 완료 (CPU + SIMP 모델) +[2025-10-24 15:37:33,820] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ ONNX OCR 모듈 초기화 성공 (CPU 모드) +[2025-10-24 15:37:33,821] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ ONNX OCR 모듈 초기화 성공 +[2025-10-24 15:37:33,821] [LogListener] [DEBUG] [loggerModule.py:debug:275] 마스크 모듈 초기화 완료 +[2025-10-24 15:37:33,821] [LogListener] [DEBUG] [loggerModule.py:debug:275] MaskModule 초기화 성공 +[2025-10-24 15:37:33,821] [LogListener] [DEBUG] [loggerModule.py:debug:275] 텍스트 렌더링 모듈 초기화 완료 +[2025-10-24 15:37:33,821] [LogListener] [DEBUG] [loggerModule.py:debug:275] 기본 폰트: D:\py\img_worker\modules\fonts\HakgyoansimDunggeunmisoTTFB.ttf +[2025-10-24 15:37:33,821] [LogListener] [DEBUG] [loggerModule.py:debug:275] TextRenderingModule 초기화 성공 +[2025-10-24 15:37:33,821] [LogListener] [DEBUG] [loggerModule.py:debug:275] 폰트 로드 성공: D:\py\img_worker\modules\fonts\HakgyoansimDunggeunmisoTTFB.ttf +[2025-10-24 15:37:33,821] [LogListener] [DEBUG] [loggerModule.py:debug:275] PostImageManager 초기화 성공 +[2025-10-24 15:37:33,822] [LogListener] [DEBUG] [loggerModule.py:debug:275] Request_AI_Server GPU 상태: CUDA 사용 가능=False +[2025-10-24 15:37:33,822] [LogListener] [DEBUG] [loggerModule.py:debug:275] Request_AI_Server 초기화 성공 +[2025-10-24 15:37:33,822] [LogListener] [DEBUG] [loggerModule.py:debug:275] GoogleTranslate 초기화 성공 +[2025-10-24 15:37:33,822] [LogListener] [DEBUG] [loggerModule.py:debug:275] MIGAN CUDA 사용 불가 - CPU 모드로 설정 +[2025-10-24 15:37:33,822] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] GPU 관리자 전달: GPUManager, can_use_cuda: False +[2025-10-24 15:37:33,841] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] GPU 관리자 사용 가능: False +[2025-10-24 15:37:34,049] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 사용 가능한 providers: ['DmlExecutionProvider', 'CPUExecutionProvider'] +[2025-10-24 15:37:34,049] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] DirectML 가속 활성화 +[2025-10-24 15:37:34,049] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 최종 providers: [('DmlExecutionProvider', {}), ('CPUExecutionProvider', {})] +[2025-10-24 15:37:34,049] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] DirectML 가속 시도: [('DmlExecutionProvider', {}), ('CPUExecutionProvider', {})] +[2025-10-24 15:37:34,049] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] DirectML 가속 성공! 실제 providers: ['DmlExecutionProvider', 'CPUExecutionProvider'] +[2025-10-24 15:37:34,050] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 입력 0: image, 형태: ['batch_size', 3, 'height', 'width'], 타입: tensor(uint8) +[2025-10-24 15:37:34,050] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 입력 1: mask, 형태: ['batch_size', 1, 'height', 'width'], 타입: tensor(uint8) +[2025-10-24 15:37:34,050] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 출력 0: result, 형태: ['ScatterNDresult_dim_0', 3, 'ScatterNDresult_dim_2', 'ScatterNDresult_dim_3'], 타입: tensor(uint8) +[2025-10-24 15:37:34,050] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 세션 준비 완료. providers=['DmlExecutionProvider', 'CPUExecutionProvider'] +[2025-10-24 15:37:34,050] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] GPU 관리자 연결 완료: GPUManager +[2025-10-24 15:37:34,050] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 파이프라인 gpu_manager 속성: True, 값: +[2025-10-24 15:37:34,050] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 초기화 완료: gpu_manager 속성=True, 값= +[2025-10-24 15:37:34,050] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔰 OCR 모듈 Warm-up 시작... +[2025-10-24 15:37:34,050] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔍 ONNX OCR 감지 방식: polygon +[2025-10-24 15:37:34,055] [LogListener] [DEBUG] [loggerModule.py:debug:275] ⚡ ONNX OCR 추론 완료: 4.0ms +[2025-10-24 15:37:34,055] [LogListener] [DEBUG] [loggerModule.py:debug:275] 📊 세부 시간 - 감지: 3.0ms, 인식: 0.0ms, 분류: 0.0ms +[2025-10-24 15:37:34,055] [LogListener] [WARNING] [loggerModule.py:warning:287] ⚠️ ONNX OCR 결과가 비어있습니다 +[2025-10-24 15:37:34,059] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ OCR 모듈 Warm-up 성공 +[2025-10-24 15:37:34,059] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔰 ImageProcessor Warm‑up 완료 +[2025-10-24 15:37:34,060] [ResultListener] [INFO] [loggerModule.py:info:281] 워커 READY 수신 +[2025-10-24 15:37:34,060] [WorkerRoller] [INFO] [loggerModule.py:info:281] 버퍼 작업 재개: 1건 +[2025-10-24 15:37:34,060] [ResultListener] [INFO] [loggerModule.py:info:281] 워커 READY 수신 +[2025-10-24 15:37:34,060] [LogListener] [DEBUG] [loggerModule.py:debug:275] 📡 추가 READY 신호 전송 완료 +[2025-10-24 15:37:34,060] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 15:37:34,061] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 31428) +[2025-10-24 15:37:34,061] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔥 작업 수신 성공 +[2025-10-24 15:37:34,061] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🚀 작업 처리 시작: cmd=process_single_image, uid=b7ecf17d-730d-4020-8fa8-f6c5446a1cb3 +[2025-10-24 15:37:34,061] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 프로세서 toggle_states 업데이트 : {'TEMP_IMAGE_DIR': 'C:\\ProgramData\\ImgWorker\\work', 'output_image_format': 'webp', 'use_local_rembg': True, 'local_rembg_model_path': 'D:\\py\\img_worker\\modules\\rembg_models', 'local_model_name': 'birefnet-general-lite', 'optionIMGTrans_type': 'GPU', 'detail_IMGTrans_type': 'GPU', 'thumb_trans_type': 'GPU', 'migan_use_accel': True, 'migan_onnx_path': 'D:\\py\\img_worker\\modules\\migan_onnx\\migan_pipeline_v2.onnx', 'ocr_provider_override': 'auto', 'migan_provider_override': 'auto', 'font_type': '폰트5', 'unwanted_texts': ['가격설명', '무료배송', '보증', '사은품', '세일', '이벤트', '특가', '품절', '할인', '할인가'], 'is_member_valid': False, 'authenticated_by_admin': True} +[2025-10-24 15:37:34,061] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 실행 직전: cmd=process_single_image +[2025-10-24 15:37:34,062] [LogListener] [DEBUG] [loggerModule.py:debug:275] process_single_image 호출 직전 +[2025-10-24 15:37:34,064] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 번역시작 +[2025-10-24 15:37:34,384] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 4 처리 시작: C:\ProgramData\ImgWorker\incoming\dl_10a864922816.jpg - 전체 번역 모드 +[2025-10-24 15:37:34,393] [LogListener] [DEBUG] [loggerModule.py:debug:275] 로컬 파일 경로 감지, 다운로드 생략: C:\ProgramData\ImgWorker\incoming\dl_10a864922816.jpg +[2025-10-24 15:37:34,393] [LogListener] [DEBUG] [loggerModule.py:debug:275] 옵션 이미지는 스케일 처리 건너뛰기: thumb +[2025-10-24 15:37:34,393] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 4 로컬 저장위치(썸네일 스케일 처리 완료): C:\ProgramData\ImgWorker\incoming\dl_10a864922816.jpg +[2025-10-24 15:37:34,394] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔍 ONNX OCR 감지 방식: polygon +[2025-10-24 15:37:34,959] [LogListener] [DEBUG] [loggerModule.py:debug:275] ⚡ ONNX OCR 추론 완료: 563.1ms +[2025-10-24 15:37:34,968] [LogListener] [DEBUG] [loggerModule.py:debug:275] 📊 세부 시간 - 감지: 147.3ms, 인식: 391.8ms, 분류: 20.0ms +[2025-10-24 15:37:34,994] [LogListener] [DEBUG] [loggerModule.py:debug:275] 메모리 변화 [OCR 처리]: 42480.2MB -> 42208.1MB (-272.2MB, -0.6%) - 이미지 4 +[2025-10-24 15:37:34,995] [LogListener] [DEBUG] [loggerModule.py:debug:275] ocr_results: [{'text': '人人', 'confidence': 0.567686140537262, 'polygon': [[59.0, 231.0], [137.0, 230.0], [137.0, 248.0], [59.0, 249.0]], 'bbox': (59, 230, 79, 20), 'method': 'polygon'}, {'text': '大', 'confidence': 0.6317986249923706, 'polygon': [[567.0, 236.0], [614.0, 236.0], [614.0, 255.0], [567.0, 255.0]], 'bbox': (567, 236, 48, 20), 'method': 'polygon'}, {'text': '//', 'confidence': 0.9194601774215698, 'polygon': [[616.0, 410.0], [751.0, 410.0], [751.0, 424.0], [616.0, 424.0]], 'bbox': (616, 410, 136, 15), 'method': 'polygon'}, {'text': 'USB至三/UC', 'confidence': 0.7746509313583374, 'polygon': [[641.0, 520.0], [749.0, 520.0], [749.0, 533.0], [641.0, 533.0]], 'bbox': (641, 520, 109, 14), 'method': 'polygon'}, {'text': 'TF', 'confidence': 0.9923825263977051, 'polygon': [[640.0, 542.0], [692.0, 542.0], [692.0, 560.0], [640.0, 560.0]], 'bbox': (640, 542, 53, 19), 'method': 'polygon'}, {'text': '条是品贴', 'confidence': 0.571622908115387, 'polygon': [[641.0, 639.0], [744.0, 638.0], [744.0, 656.0], [641.0, 657.0]], 'bbox': (641, 638, 104, 20), 'method': 'polygon'}] +[2025-10-24 15:37:34,995] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 56.8%): '人人' +[2025-10-24 15:37:34,995] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 63.2%): '大' +[2025-10-24 15:37:34,995] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): '//' +[2025-10-24 15:37:34,995] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 77.5%): 'USB至三/UC' +[2025-10-24 15:37:34,996] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): 'TF' +[2025-10-24 15:37:34,996] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 57.2%): '条是品贴' +[2025-10-24 15:37:34,996] [LogListener] [DEBUG] [loggerModule.py:debug:275] 필터링 결과: 4/6개 (신뢰도 + & 중국어) +[2025-10-24 15:37:34,996] [LogListener] [DEBUG] [loggerModule.py:debug:275] filter_ocr_results: [{'text': '人人', 'confidence': 0.567686140537262, 'polygon': [[59.0, 231.0], [137.0, 230.0], [137.0, 248.0], [59.0, 249.0]], 'bbox': (59, 230, 79, 20), 'method': 'polygon'}, {'text': '大', 'confidence': 0.6317986249923706, 'polygon': [[567.0, 236.0], [614.0, 236.0], [614.0, 255.0], [567.0, 255.0]], 'bbox': (567, 236, 48, 20), 'method': 'polygon'}, {'text': 'USB至三/UC', 'confidence': 0.7746509313583374, 'polygon': [[641.0, 520.0], [749.0, 520.0], [749.0, 533.0], [641.0, 533.0]], 'bbox': (641, 520, 109, 14), 'method': 'polygon'}, {'text': '条是品贴', 'confidence': 0.571622908115387, 'polygon': [[641.0, 639.0], [744.0, 638.0], [744.0, 656.0], [641.0, 657.0]], 'bbox': (641, 638, 104, 20), 'method': 'polygon'}] +[2025-10-24 15:37:35,013] [LogListener] [DEBUG] [loggerModule.py:debug:275] 중국어 텍스트 4개 필터링 완료 +[2025-10-24 15:37:35,028] [LogListener] [DEBUG] [loggerModule.py:debug:275] 한글 텍스트 0개 필터링 완료 +[2025-10-24 15:37:36,956] [LogListener] [DEBUG] [loggerModule.py:debug:275] translated_texts: ['모든 사람', '큰', 'USB 3개까지 / UC', '해당 글은 제품 스티커입니다'] +[2025-10-24 15:37:36,956] [LogListener] [DEBUG] [loggerModule.py:debug:275] 마스크 생성 완료 +[2025-10-24 15:37:36,957] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 4 OCR 권한 없음, 전체 번역 모드 +[2025-10-24 15:37:36,963] [LogListener] [DEBUG] [loggerModule.py:debug:275] [AUTO Inpaint] coverage=0.038, comps=4, min_center_dist=0.107 → request +[2025-10-24 15:37:36,963] [LogListener] [DEBUG] [loggerModule.py:debug:275] ocr_count: 4 +[2025-10-24 15:37:36,963] [LogListener] [DEBUG] [loggerModule.py:debug:275] is_member_valid: False +[2025-10-24 15:37:36,963] [LogListener] [DEBUG] [loggerModule.py:debug:275] MIGAN 인페인팅 시도 +[2025-10-24 15:37:36,965] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 입력 형태 - 이미지: (1, 3, 800, 800), 마스크: (1, 1, 800, 800) +[2025-10-24 15:37:37,434] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 출력 형태: (800, 800, 3), dtype: uint8 +[2025-10-24 15:37:37,434] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 추론 완료: 469.02 ms +[2025-10-24 15:37:37,442] [LogListener] [DEBUG] [loggerModule.py:debug:275] MIGAN 인페인팅 성공 +[2025-10-24 15:37:37,442] [LogListener] [DEBUG] [loggerModule.py:debug:275] 메모리 변화 [인페인팅]: 42358.3MB -> 42527.1MB (+168.7MB, +0.4%) - 방법: migan +[2025-10-24 15:37:37,442] [LogListener] [DEBUG] [loggerModule.py:debug:275] 인페인팅 완료 +[2025-10-24 15:37:37,461] [LogListener] [DEBUG] [loggerModule.py:debug:275] 텍스트 렌더링 완료 +[2025-10-24 15:37:37,462] [LogListener] [DEBUG] [loggerModule.py:debug:275] watermark_text: 이미지 저작권 보유 +[2025-10-24 15:37:37,462] [LogListener] [DEBUG] [loggerModule.py:debug:275] is_watermark_enabled: True +[2025-10-24 15:37:37,739] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 저장 완료 : C:\ProgramData\ImgWorker\work\translated_thumb_img_4.webp +[2025-10-24 15:37:37,740] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 4 번역 완료: C:\ProgramData\ImgWorker\work\translated_thumb_img_4.webp +[2025-10-24 15:37:37,740] [LogListener] [DEBUG] [loggerModule.py:debug:275] ⏱ 이미지 파이프라인 총 3676.7ms | download=0.0ms | ocr=574.6ms | translate=1962.1ms | mask=1962.1ms | inpaint=484.0ms(migan/DirectML) | render=19.0ms | save=246.2ms +[2025-10-24 15:37:37,742] [LogListener] [DEBUG] [loggerModule.py:debug:275] process_single_image 호출 완료 +[2025-10-24 15:37:37,742] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 결과 반환 중: uid=b7ecf17d-730d-4020-8fa8-f6c5446a1cb3 +[2025-10-24 15:37:37,742] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 결과 반환 완료: uid=b7ecf17d-730d-4020-8fa8-f6c5446a1cb3 +[2025-10-24 15:37:37,742] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 15:37:37,742] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 31428) +[2025-10-24 15:37:40,418] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔥 작업 수신 성공 +[2025-10-24 15:37:40,418] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🚀 작업 처리 시작: cmd=process_single_image, uid=eefaf528-6ca7-4fd4-afb7-4ecc4b5c3838 +[2025-10-24 15:37:40,418] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 프로세서 toggle_states 업데이트 : {'TEMP_IMAGE_DIR': 'C:\\ProgramData\\ImgWorker\\work', 'output_image_format': 'webp', 'use_local_rembg': True, 'local_rembg_model_path': 'D:\\py\\img_worker\\modules\\rembg_models', 'local_model_name': 'birefnet-general-lite', 'optionIMGTrans_type': 'GPU', 'detail_IMGTrans_type': 'GPU', 'thumb_trans_type': 'GPU', 'migan_use_accel': True, 'migan_onnx_path': 'D:\\py\\img_worker\\modules\\migan_onnx\\migan_pipeline_v2.onnx', 'ocr_provider_override': 'auto', 'migan_provider_override': 'auto', 'font_type': '폰트5', 'unwanted_texts': ['가격설명', '무료배송', '보증', '사은품', '세일', '이벤트', '특가', '품절', '할인', '할인가'], 'is_member_valid': False, 'authenticated_by_admin': True} +[2025-10-24 15:37:40,418] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 실행 직전: cmd=process_single_image +[2025-10-24 15:37:40,418] [LogListener] [DEBUG] [loggerModule.py:debug:275] process_single_image 호출 직전 +[2025-10-24 15:37:40,419] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 번역시작 +[2025-10-24 15:37:40,647] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 5 처리 시작: C:\ProgramData\ImgWorker\incoming\dl_54f3b0ca508f.jpg - 전체 번역 모드 +[2025-10-24 15:37:40,653] [LogListener] [DEBUG] [loggerModule.py:debug:275] 로컬 파일 경로 감지, 다운로드 생략: C:\ProgramData\ImgWorker\incoming\dl_54f3b0ca508f.jpg +[2025-10-24 15:37:40,653] [LogListener] [DEBUG] [loggerModule.py:debug:275] 옵션 이미지는 스케일 처리 건너뛰기: thumb +[2025-10-24 15:37:40,653] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 5 로컬 저장위치(썸네일 스케일 처리 완료): C:\ProgramData\ImgWorker\incoming\dl_54f3b0ca508f.jpg +[2025-10-24 15:37:40,654] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔍 ONNX OCR 감지 방식: polygon +[2025-10-24 15:37:41,963] [LogListener] [DEBUG] [loggerModule.py:debug:275] ⚡ ONNX OCR 추론 완료: 1307.2ms +[2025-10-24 15:37:41,973] [LogListener] [DEBUG] [loggerModule.py:debug:275] 📊 세부 시간 - 감지: 100.0ms, 인식: 1193.2ms, 분류: 9.0ms +[2025-10-24 15:37:41,976] [LogListener] [DEBUG] [loggerModule.py:debug:275] 메모리 변화 [OCR 처리]: 42603.5MB -> 42897.6MB (+294.1MB, +0.7%) - 이미지 5 +[2025-10-24 15:37:41,976] [LogListener] [DEBUG] [loggerModule.py:debug:275] ocr_results: [{'text': 'RunawayRabbitollCHgo今含g', 'confidence': 0.8095155954360962, 'polygon': [[193.0, 71.0], [594.0, 71.0], [594.0, 95.0], [193.0, 95.0]], 'bbox': (193, 71, 402, 25), 'method': 'polygon'}, {'text': '11', 'confidence': 0.6541364789009094, 'polygon': [[175.0, 194.0], [619.0, 195.0], [619.0, 213.0], [175.0, 212.0]], 'bbox': (175, 194, 445, 20), 'method': 'polygon'}, {'text': '95', 'confidence': 0.9799211025238037, 'polygon': [[572.0, 544.0], [604.0, 544.0], [604.0, 563.0], [572.0, 563.0]], 'bbox': (572, 544, 33, 20), 'method': 'polygon'}, {'text': '品k1o号1onozoeg', 'confidence': 0.5297556519508362, 'polygon': [[58.0, 633.0], [370.0, 633.0], [370.0, 647.0], [58.0, 647.0]], 'bbox': (58, 633, 313, 15), 'method': 'polygon'}, {'text': '房12号品', 'confidence': 0.5399719476699829, 'polygon': [[73.0, 677.0], [352.0, 677.0], [352.0, 691.0], [73.0, 691.0]], 'bbox': (73, 677, 280, 15), 'method': 'polygon'}, {'text': '品品立10能', 'confidence': 0.5374453067779541, 'polygon': [[108.0, 720.0], [308.0, 720.0], [308.0, 741.0], [108.0, 741.0]], 'bbox': (108, 720, 201, 22), 'method': 'polygon'}, {'text': '百10是品Y品百品', 'confidence': 0.5379340648651123, 'polygon': [[428.0, 727.0], [758.0, 727.0], [758.0, 741.0], [428.0, 741.0]], 'bbox': (428, 727, 331, 15), 'method': 'polygon'}] +[2025-10-24 15:37:41,977] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 81.0%): 'RunawayRabbitollCHgo今含g' +[2025-10-24 15:37:41,977] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): '11' +[2025-10-24 15:37:41,977] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 제외 (중국어 없음): '95' +[2025-10-24 15:37:41,977] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 53.0%): '品k1o号1onozoeg' +[2025-10-24 15:37:41,977] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 54.0%): '房12号品' +[2025-10-24 15:37:41,977] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 53.7%): '品品立10能' +[2025-10-24 15:37:41,977] [LogListener] [DEBUG] [loggerModule.py:debug:275] [필터링] 포함 (신뢰도 53.8%): '百10是品Y品百品' +[2025-10-24 15:37:41,978] [LogListener] [DEBUG] [loggerModule.py:debug:275] 필터링 결과: 5/7개 (신뢰도 + & 중국어) +[2025-10-24 15:37:41,978] [LogListener] [DEBUG] [loggerModule.py:debug:275] filter_ocr_results: [{'text': 'RunawayRabbitollCHgo今含g', 'confidence': 0.8095155954360962, 'polygon': [[193.0, 71.0], [594.0, 71.0], [594.0, 95.0], [193.0, 95.0]], 'bbox': (193, 71, 402, 25), 'method': 'polygon'}, {'text': '品k1o号1onozoeg', 'confidence': 0.5297556519508362, 'polygon': [[58.0, 633.0], [370.0, 633.0], [370.0, 647.0], [58.0, 647.0]], 'bbox': (58, 633, 313, 15), 'method': 'polygon'}, {'text': '房12号品', 'confidence': 0.5399719476699829, 'polygon': [[73.0, 677.0], [352.0, 677.0], [352.0, 691.0], [73.0, 691.0]], 'bbox': (73, 677, 280, 15), 'method': 'polygon'}, {'text': '品品立10能', 'confidence': 0.5374453067779541, 'polygon': [[108.0, 720.0], [308.0, 720.0], [308.0, 741.0], [108.0, 741.0]], 'bbox': (108, 720, 201, 22), 'method': 'polygon'}, {'text': '百10是品Y品百品', 'confidence': 0.5379340648651123, 'polygon': [[428.0, 727.0], [758.0, 727.0], [758.0, 741.0], [428.0, 741.0]], 'bbox': (428, 727, 331, 15), 'method': 'polygon'}] +[2025-10-24 15:37:41,978] [LogListener] [DEBUG] [loggerModule.py:debug:275] 중국어 텍스트 5개 필터링 완료 +[2025-10-24 15:37:41,978] [LogListener] [DEBUG] [loggerModule.py:debug:275] 한글 텍스트 0개 필터링 완료 +[2025-10-24 15:37:44,042] [LogListener] [DEBUG] [loggerModule.py:debug:275] translated_texts: ['RunawayRabbittollCHgo에는 오늘 g가 포함되어 있습니다.', '제품 k1o 번호 1onozoeg', '12호실', '모든 제품에 대한 10가지 기술', '100은 제품 Y 제품 100 제품입니다.'] +[2025-10-24 15:37:44,043] [LogListener] [DEBUG] [loggerModule.py:debug:275] 마스크 생성 완료 +[2025-10-24 15:37:44,043] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 5 OCR 권한 없음, 전체 번역 모드 +[2025-10-24 15:37:44,049] [LogListener] [DEBUG] [loggerModule.py:debug:275] [AUTO Inpaint] coverage=0.125, comps=3, min_center_dist=0.340 → request +[2025-10-24 15:37:44,049] [LogListener] [DEBUG] [loggerModule.py:debug:275] ocr_count: 5 +[2025-10-24 15:37:44,049] [LogListener] [DEBUG] [loggerModule.py:debug:275] is_member_valid: False +[2025-10-24 15:37:44,049] [LogListener] [DEBUG] [loggerModule.py:debug:275] MIGAN 인페인팅 시도 +[2025-10-24 15:37:44,051] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 입력 형태 - 이미지: (1, 3, 800, 800), 마스크: (1, 1, 800, 800) +[2025-10-24 15:37:44,191] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 출력 형태: (800, 800, 3), dtype: uint8 +[2025-10-24 15:37:44,193] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 추론 완료: 140.00 ms +[2025-10-24 15:37:44,203] [LogListener] [DEBUG] [loggerModule.py:debug:275] MIGAN 인페인팅 성공 +[2025-10-24 15:37:44,203] [LogListener] [DEBUG] [loggerModule.py:debug:275] 메모리 변화 [인페인팅]: 43154.6MB -> 43270.9MB (+116.2MB, +0.3%) - 방법: migan +[2025-10-24 15:37:44,204] [LogListener] [DEBUG] [loggerModule.py:debug:275] 인페인팅 완료 +[2025-10-24 15:37:44,232] [LogListener] [DEBUG] [loggerModule.py:debug:275] 텍스트 렌더링 완료 +[2025-10-24 15:37:44,232] [LogListener] [DEBUG] [loggerModule.py:debug:275] watermark_text: 이미지 저작권 보유 +[2025-10-24 15:37:44,233] [LogListener] [DEBUG] [loggerModule.py:debug:275] is_watermark_enabled: True +[2025-10-24 15:37:44,449] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 저장 완료 : C:\ProgramData\ImgWorker\work\translated_thumb_img_5.webp +[2025-10-24 15:37:44,450] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 5 번역 완료: C:\ProgramData\ImgWorker\work\translated_thumb_img_5.webp +[2025-10-24 15:37:44,450] [LogListener] [DEBUG] [loggerModule.py:debug:275] ⏱ 이미지 파이프라인 총 4030.3ms | download=0.0ms | ocr=1316.2ms | translate=2065.5ms | mask=2065.5ms | inpaint=160.3ms(migan/DirectML) | render=29.6ms | save=179.4ms +[2025-10-24 15:37:44,451] [LogListener] [DEBUG] [loggerModule.py:debug:275] process_single_image 호출 완료 +[2025-10-24 15:37:44,451] [ResultListener] [WARNING] [loggerModule.py:warning:287] 워커 롤링 스케줄: reason=job-count-threshold +[2025-10-24 15:37:44,451] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 결과 반환 중: uid=eefaf528-6ca7-4fd4-afb7-4ecc4b5c3838 +[2025-10-24 15:37:44,451] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 결과 반환 완료: uid=eefaf528-6ca7-4fd4-afb7-4ecc4b5c3838 +[2025-10-24 15:37:44,451] [WorkerRoller] [WARNING] [loggerModule.py:warning:287] 워커 롤링 시작: job-count-threshold +[2025-10-24 15:37:44,451] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 15:37:44,452] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 31428) +[2025-10-24 15:37:44,476] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔥 작업 수신 성공 +[2025-10-24 15:37:44,506] [LogListener] [DEBUG] [loggerModule.py:debug:275] Shutdown signal 수신 → 종료 +[2025-10-24 15:37:44,507] [LogListener] [DEBUG] [loggerModule.py:debug:275] OCR 모듈 정리 완료 +[2025-10-24 15:37:44,507] [LogListener] [DEBUG] [loggerModule.py:debug:275] 마스크 모듈 정리 완료 +[2025-10-24 15:37:44,507] [LogListener] [DEBUG] [loggerModule.py:debug:275] 임시 폴더 삭제됨: C:\ProgramData\ImgWorker\work +[2025-10-24 15:37:44,507] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 프로세서 소멸 +[2025-10-24 15:37:44,885] [WorkerRoller] [INFO] [loggerModule.py:info:281] 워커 프로세스 기동: PID=34592 +[2025-10-24 15:37:45,404] [LogListener] [DEBUG] [loggerModule.py:debug:275] ImageWorker 프로세스 기동 (PID=34592, Name=ImageWorkerProcess) +[2025-10-24 15:37:45,404] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔧 ImageProcessor3 초기화 시작... +[2025-10-24 15:37:45,404] [LogListener] [DEBUG] [loggerModule.py:debug:275] tracemalloc 메모리 추적 시작 +[2025-10-24 15:37:45,412] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ DirectML 환경 준비 완료 (Windows DirectX 12 기반) +[2025-10-24 15:37:45,412] [LogListener] [DEBUG] [loggerModule.py:debug:275] === 🚀 DirectML GPU 상태 초기화 시작 🚀 === +[2025-10-24 15:37:45,412] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🎯 사용자 GPU 가속 요청: False +[2025-10-24 15:37:45,412] [LogListener] [DEBUG] [loggerModule.py:debug:275] 💻 현재 운영체제: Windows +[2025-10-24 15:37:45,412] [LogListener] [DEBUG] [loggerModule.py:debug:275] GPU 가속이 비활성화됨 (toggle_states['use_cuda'] = False) +[2025-10-24 15:37:45,412] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔒 안전한 CPU 모드로 모든 GPU 설정 강제 비활성화 +[2025-10-24 15:37:45,413] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔧 ImageProcessor3 GPU 상태 요약: +[2025-10-24 15:37:45,413] [LogListener] [DEBUG] [loggerModule.py:debug:275] - CUDA 사용 가능: False +[2025-10-24 15:37:45,413] [LogListener] [DEBUG] [loggerModule.py:debug:275] - toggle_states['use_cuda']: NOT_SET +[2025-10-24 15:37:45,413] [LogListener] [DEBUG] [loggerModule.py:debug:275] - GPU 하드웨어 정보: {} +[2025-10-24 15:37:45,413] [LogListener] [DEBUG] [loggerModule.py:debug:275] ImageProcessor3 Init toggle_states: {'TEMP_IMAGE_DIR': 'C:\\ProgramData\\ImgWorker\\work', 'output_image_format': 'webp', 'use_local_rembg': True, 'local_rembg_model_path': 'D:\\py\\img_worker\\modules\\rembg_models', 'local_model_name': 'birefnet-general-lite', 'optionIMGTrans_type': 'CPU', 'detail_IMGTrans_type': 'CPU', 'thumb_trans_type': 'CPU', 'migan_use_accel': True, 'migan_onnx_path': 'D:\\py\\img_worker\\modules\\migan_onnx\\migan_pipeline_v2.onnx', 'ocr_provider_override': 'auto', 'migan_provider_override': 'auto'} +[2025-10-24 15:37:45,413] [LogListener] [DEBUG] [loggerModule.py:debug:275] is_member_valid: False +[2025-10-24 15:37:45,413] [LogListener] [DEBUG] [loggerModule.py:debug:275] debug_images 디렉토리 이미 존재: D:\py\img_worker\modules\debug_images +[2025-10-24 15:37:45,413] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.font_path: D:\py\img_worker\modules\fonts\HakgyoansimDunggeunmisoTTFB.ttf +[2025-10-24 15:37:45,414] [LogListener] [DEBUG] [loggerModule.py:debug:275] toggle_states font_path: D:\py\img_worker\modules\fonts\HakgyoansimDunggeunmisoTTFB.ttf +[2025-10-24 15:37:45,414] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.TEMP_IMAGE_DIR: C:\ProgramData\ImgWorker\work +[2025-10-24 15:37:45,414] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.debugging_save_Dir: D:\py\img_worker\modules\debug_images +[2025-10-24 15:37:45,414] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.unwanted_texts: {} +[2025-10-24 15:37:45,414] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.inpaint_method: migan +[2025-10-24 15:37:45,414] [LogListener] [DEBUG] [loggerModule.py:debug:275] Image.MAX_IMAGE_PIXELS set to 20000000 +[2025-10-24 15:37:45,414] [LogListener] [DEBUG] [loggerModule.py:debug:275] ONNX 모델 타입 설정값: 자동 선택, GPU 정보: {} +[2025-10-24 15:37:45,414] [LogListener] [DEBUG] [loggerModule.py:debug:275] 자동 선택 모드: GPU 추천 모델 simp 사용 +[2025-10-24 15:37:45,414] [LogListener] [DEBUG] [loggerModule.py:debug:275] ONNX OCR 모델 타입 결정: simp (GPU: False) +[2025-10-24 15:37:45,414] [LogListener] [DEBUG] [loggerModule.py:debug:275] ONNX OCR 모듈 CPU 모드로 설정 +[2025-10-24 15:37:45,414] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🚀 ONNX TextSystem 초기화 시작 (CPU 모드) +[2025-10-24 15:37:45,415] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🚀 ONNX TextSystem 초기화 시작 (CPU 모드) +[2025-10-24 15:37:47,095] [LogListener] [DEBUG] [loggerModule.py:debug:275] 문자 사전 파일 UTF-8 확인 완료: D:\py\img_worker\modules\onnx_ocr_module\dict\ppocr_keys_v1.txt +[2025-10-24 15:37:47,215] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ ONNX TextSystem 초기화 완료 (CPU + SIMP (호환성) 모델) +[2025-10-24 15:37:47,215] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ ONNX TextSystem 초기화 완료 (CPU + SIMP 모델) +[2025-10-24 15:37:47,215] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ ONNX OCR 모듈 초기화 성공 (CPU 모드) +[2025-10-24 15:37:47,216] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ ONNX OCR 모듈 초기화 성공 +[2025-10-24 15:37:47,216] [LogListener] [DEBUG] [loggerModule.py:debug:275] 마스크 모듈 초기화 완료 +[2025-10-24 15:37:47,216] [LogListener] [DEBUG] [loggerModule.py:debug:275] MaskModule 초기화 성공 +[2025-10-24 15:37:47,216] [LogListener] [DEBUG] [loggerModule.py:debug:275] 텍스트 렌더링 모듈 초기화 완료 +[2025-10-24 15:37:47,216] [LogListener] [DEBUG] [loggerModule.py:debug:275] 기본 폰트: D:\py\img_worker\modules\fonts\HakgyoansimDunggeunmisoTTFB.ttf +[2025-10-24 15:37:47,216] [LogListener] [DEBUG] [loggerModule.py:debug:275] TextRenderingModule 초기화 성공 +[2025-10-24 15:37:47,216] [LogListener] [DEBUG] [loggerModule.py:debug:275] 폰트 로드 성공: D:\py\img_worker\modules\fonts\HakgyoansimDunggeunmisoTTFB.ttf +[2025-10-24 15:37:47,216] [LogListener] [DEBUG] [loggerModule.py:debug:275] PostImageManager 초기화 성공 +[2025-10-24 15:37:47,216] [LogListener] [DEBUG] [loggerModule.py:debug:275] Request_AI_Server GPU 상태: CUDA 사용 가능=False +[2025-10-24 15:37:47,217] [LogListener] [DEBUG] [loggerModule.py:debug:275] Request_AI_Server 초기화 성공 +[2025-10-24 15:37:47,217] [LogListener] [DEBUG] [loggerModule.py:debug:275] GoogleTranslate 초기화 성공 +[2025-10-24 15:37:47,217] [LogListener] [DEBUG] [loggerModule.py:debug:275] MIGAN CUDA 사용 불가 - CPU 모드로 설정 +[2025-10-24 15:37:47,217] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] GPU 관리자 전달: GPUManager, can_use_cuda: False +[2025-10-24 15:37:47,242] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] GPU 관리자 사용 가능: False +[2025-10-24 15:37:47,483] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 사용 가능한 providers: ['DmlExecutionProvider', 'CPUExecutionProvider'] +[2025-10-24 15:37:47,484] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] DirectML 가속 활성화 +[2025-10-24 15:37:47,484] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 최종 providers: [('DmlExecutionProvider', {}), ('CPUExecutionProvider', {})] +[2025-10-24 15:37:47,484] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] DirectML 가속 시도: [('DmlExecutionProvider', {}), ('CPUExecutionProvider', {})] +[2025-10-24 15:37:47,484] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] DirectML 가속 성공! 실제 providers: ['DmlExecutionProvider', 'CPUExecutionProvider'] +[2025-10-24 15:37:47,484] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 입력 0: image, 형태: ['batch_size', 3, 'height', 'width'], 타입: tensor(uint8) +[2025-10-24 15:37:47,484] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 입력 1: mask, 형태: ['batch_size', 1, 'height', 'width'], 타입: tensor(uint8) +[2025-10-24 15:37:47,485] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 출력 0: result, 형태: ['ScatterNDresult_dim_0', 3, 'ScatterNDresult_dim_2', 'ScatterNDresult_dim_3'], 타입: tensor(uint8) +[2025-10-24 15:37:47,485] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 세션 준비 완료. providers=['DmlExecutionProvider', 'CPUExecutionProvider'] +[2025-10-24 15:37:47,485] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] GPU 관리자 연결 완료: GPUManager +[2025-10-24 15:37:47,485] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 파이프라인 gpu_manager 속성: True, 값: +[2025-10-24 15:37:47,485] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 초기화 완료: gpu_manager 속성=True, 값= +[2025-10-24 15:37:47,485] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔰 OCR 모듈 Warm-up 시작... +[2025-10-24 15:37:47,485] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔍 ONNX OCR 감지 방식: polygon +[2025-10-24 15:37:47,490] [LogListener] [DEBUG] [loggerModule.py:debug:275] ⚡ ONNX OCR 추론 완료: 5.0ms +[2025-10-24 15:37:47,490] [LogListener] [DEBUG] [loggerModule.py:debug:275] 📊 세부 시간 - 감지: 3.0ms, 인식: 0.0ms, 분류: 0.0ms +[2025-10-24 15:37:47,491] [LogListener] [WARNING] [loggerModule.py:warning:287] ⚠️ ONNX OCR 결과가 비어있습니다 +[2025-10-24 15:37:47,495] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ OCR 모듈 Warm-up 성공 +[2025-10-24 15:37:47,495] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔰 ImageProcessor Warm‑up 완료 +[2025-10-24 15:37:47,495] [ResultListener] [INFO] [loggerModule.py:info:281] 워커 READY 수신 +[2025-10-24 15:37:47,495] [ResultListener] [INFO] [loggerModule.py:info:281] 워커 READY 수신 +[2025-10-24 15:37:47,495] [LogListener] [DEBUG] [loggerModule.py:debug:275] 📡 추가 READY 신호 전송 완료 +[2025-10-24 15:37:47,496] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 15:37:47,496] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 34592) +[2025-10-24 15:38:47,508] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 15:38:47,508] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 15:38:47,508] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 34592) +[2025-10-24 15:39:47,510] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 15:39:47,510] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 15:39:47,510] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 34592) +[2025-10-24 15:40:47,522] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 15:40:47,522] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 15:40:47,522] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 34592) +[2025-10-24 15:41:47,533] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 15:41:47,533] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 15:41:47,534] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 34592) +[2025-10-24 15:42:47,543] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 15:42:47,544] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 15:42:47,544] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 34592) +[2025-10-24 15:43:47,543] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 15:43:47,543] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 15:43:47,544] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 34592) +[2025-10-24 15:44:47,553] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 15:44:47,553] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 15:44:47,553] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 34592) +[2025-10-24 15:45:47,565] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 15:45:47,565] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 15:45:47,565] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 34592) +[2025-10-24 15:46:47,572] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 15:46:47,572] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 15:46:47,572] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 34592) +[2025-10-24 15:47:47,583] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 15:47:47,583] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 15:47:47,583] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 34592) +[2025-10-24 15:48:47,583] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 15:48:47,583] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 15:48:47,583] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 34592) +[2025-10-24 15:49:47,587] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 15:49:47,587] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 15:49:47,587] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 34592) +[2025-10-24 15:50:47,601] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 15:50:47,601] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 15:50:47,601] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 34592) +[2025-10-24 15:51:47,615] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 15:51:47,615] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 15:51:47,615] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 34592) +[2025-10-24 15:52:47,620] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 15:52:47,620] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 15:52:47,620] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 34592) +[2025-10-24 15:53:47,634] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 15:53:47,634] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 15:53:47,634] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 34592) +[2025-10-24 15:54:47,636] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 15:54:47,636] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 15:54:47,636] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 34592) +[2025-10-24 15:55:47,644] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 15:55:47,644] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 15:55:47,644] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 34592) +[2025-10-24 15:56:47,648] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 15:56:47,648] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 15:56:47,648] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 34592) +[2025-10-24 15:57:47,652] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 15:57:47,652] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 15:57:47,652] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 34592) +[2025-10-24 15:58:47,658] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 15:58:47,658] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 15:58:47,658] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 34592) +[2025-10-24 15:59:47,670] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 15:59:47,670] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 15:59:47,670] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 34592) +[2025-10-24 16:00:47,672] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 16:00:47,672] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 16:00:47,672] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 34592) +[2025-10-24 16:01:47,678] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 16:01:47,678] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 16:01:47,678] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 34592) +[2025-10-24 16:02:47,682] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 16:02:47,682] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 16:02:47,682] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 34592) +[2025-10-24 16:02:52,484] [MainThread] [INFO] [loggerModule.py:info:281] API 서버 종료 +[2025-10-24 16:02:52,771] [MainThread] [INFO] [loggerModule.py:info:281] 워커 프로세스 종료 +[2025-10-24 16:03:20,420] [MainThread] [INFO] [loggerModule.py:info:281] API 서버 시작 +[2025-10-24 16:03:20,426] [MainThread] [INFO] [loggerModule.py:info:281] 워커 프로세스 기동: PID=32100 +[2025-10-24 16:03:20,865] [LogListener] [DEBUG] [loggerModule.py:debug:275] ImageWorker 프로세스 기동 (PID=32100, Name=ImageWorkerProcess) +[2025-10-24 16:03:20,865] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔧 ImageProcessor3 초기화 시작... +[2025-10-24 16:03:20,865] [LogListener] [DEBUG] [loggerModule.py:debug:275] tracemalloc 메모리 추적 시작 +[2025-10-24 16:03:20,871] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ DirectML 환경 준비 완료 (Windows DirectX 12 기반) +[2025-10-24 16:03:20,872] [LogListener] [DEBUG] [loggerModule.py:debug:275] === 🚀 DirectML GPU 상태 초기화 시작 🚀 === +[2025-10-24 16:03:20,872] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🎯 사용자 GPU 가속 요청: False +[2025-10-24 16:03:20,872] [LogListener] [DEBUG] [loggerModule.py:debug:275] 💻 현재 운영체제: Windows +[2025-10-24 16:03:20,872] [LogListener] [DEBUG] [loggerModule.py:debug:275] GPU 가속이 비활성화됨 (toggle_states['use_cuda'] = False) +[2025-10-24 16:03:20,872] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔒 안전한 CPU 모드로 모든 GPU 설정 강제 비활성화 +[2025-10-24 16:03:20,872] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔧 ImageProcessor3 GPU 상태 요약: +[2025-10-24 16:03:20,872] [LogListener] [DEBUG] [loggerModule.py:debug:275] - CUDA 사용 가능: False +[2025-10-24 16:03:20,873] [LogListener] [DEBUG] [loggerModule.py:debug:275] - toggle_states['use_cuda']: NOT_SET +[2025-10-24 16:03:20,873] [LogListener] [DEBUG] [loggerModule.py:debug:275] - GPU 하드웨어 정보: {} +[2025-10-24 16:03:20,873] [LogListener] [DEBUG] [loggerModule.py:debug:275] ImageProcessor3 Init toggle_states: {'TEMP_IMAGE_DIR': 'C:\\ProgramData\\ImgWorker\\work', 'output_image_format': 'webp', 'use_local_rembg': True, 'local_rembg_model_path': 'D:\\py\\img_worker\\modules\\rembg_models', 'local_model_name': 'birefnet-general-lite', 'optionIMGTrans_type': 'CPU', 'detail_IMGTrans_type': 'CPU', 'thumb_trans_type': 'CPU', 'migan_use_accel': True, 'migan_onnx_path': 'D:\\py\\img_worker\\modules\\migan_onnx\\migan_pipeline_v2.onnx', 'ocr_provider_override': 'auto', 'migan_provider_override': 'auto'} +[2025-10-24 16:03:20,873] [LogListener] [DEBUG] [loggerModule.py:debug:275] is_member_valid: False +[2025-10-24 16:03:20,873] [LogListener] [DEBUG] [loggerModule.py:debug:275] debug_images 디렉토리 이미 존재: D:\py\img_worker\modules\debug_images +[2025-10-24 16:03:20,873] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.font_path: D:\py\img_worker\modules\fonts\HakgyoansimDunggeunmisoTTFB.ttf +[2025-10-24 16:03:20,873] [LogListener] [DEBUG] [loggerModule.py:debug:275] toggle_states font_path: D:\py\img_worker\modules\fonts\HakgyoansimDunggeunmisoTTFB.ttf +[2025-10-24 16:03:20,873] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.TEMP_IMAGE_DIR: C:\ProgramData\ImgWorker\work +[2025-10-24 16:03:20,873] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.debugging_save_Dir: D:\py\img_worker\modules\debug_images +[2025-10-24 16:03:20,873] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.unwanted_texts: {} +[2025-10-24 16:03:20,873] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.inpaint_method: migan +[2025-10-24 16:03:20,873] [LogListener] [DEBUG] [loggerModule.py:debug:275] Image.MAX_IMAGE_PIXELS set to 20000000 +[2025-10-24 16:03:20,873] [LogListener] [DEBUG] [loggerModule.py:debug:275] ONNX 모델 타입 설정값: 자동 선택, GPU 정보: {} +[2025-10-24 16:03:20,873] [LogListener] [DEBUG] [loggerModule.py:debug:275] 자동 선택 모드: GPU 추천 모델 simp 사용 +[2025-10-24 16:03:20,873] [LogListener] [DEBUG] [loggerModule.py:debug:275] ONNX OCR 모델 타입 결정: simp (GPU: False) +[2025-10-24 16:03:20,874] [LogListener] [DEBUG] [loggerModule.py:debug:275] ONNX OCR 모듈 CPU 모드로 설정 +[2025-10-24 16:03:20,874] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🚀 ONNX TextSystem 초기화 시작 (CPU 모드) +[2025-10-24 16:03:20,874] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🚀 ONNX TextSystem 초기화 시작 (CPU 모드) +[2025-10-24 16:03:22,409] [LogListener] [DEBUG] [loggerModule.py:debug:275] 문자 사전 파일 UTF-8 확인 완료: D:\py\img_worker\modules\onnx_ocr_module\dict\ppocr_keys_v1.txt +[2025-10-24 16:03:22,496] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ ONNX TextSystem 초기화 완료 (CPU + SIMP (호환성) 모델) +[2025-10-24 16:03:22,497] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ ONNX TextSystem 초기화 완료 (CPU + SIMP 모델) +[2025-10-24 16:03:22,497] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ ONNX OCR 모듈 초기화 성공 (CPU 모드) +[2025-10-24 16:03:22,497] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ ONNX OCR 모듈 초기화 성공 +[2025-10-24 16:03:22,497] [LogListener] [DEBUG] [loggerModule.py:debug:275] 마스크 모듈 초기화 완료 +[2025-10-24 16:03:22,497] [LogListener] [DEBUG] [loggerModule.py:debug:275] MaskModule 초기화 성공 +[2025-10-24 16:03:22,497] [LogListener] [DEBUG] [loggerModule.py:debug:275] 텍스트 렌더링 모듈 초기화 완료 +[2025-10-24 16:03:22,498] [LogListener] [DEBUG] [loggerModule.py:debug:275] 기본 폰트: D:\py\img_worker\modules\fonts\HakgyoansimDunggeunmisoTTFB.ttf +[2025-10-24 16:03:22,498] [LogListener] [DEBUG] [loggerModule.py:debug:275] TextRenderingModule 초기화 성공 +[2025-10-24 16:03:22,498] [LogListener] [DEBUG] [loggerModule.py:debug:275] 폰트 로드 성공: D:\py\img_worker\modules\fonts\HakgyoansimDunggeunmisoTTFB.ttf +[2025-10-24 16:03:22,498] [LogListener] [DEBUG] [loggerModule.py:debug:275] PostImageManager 초기화 성공 +[2025-10-24 16:03:22,498] [LogListener] [DEBUG] [loggerModule.py:debug:275] Request_AI_Server GPU 상태: CUDA 사용 가능=False +[2025-10-24 16:03:22,498] [LogListener] [DEBUG] [loggerModule.py:debug:275] Request_AI_Server 초기화 성공 +[2025-10-24 16:03:22,498] [LogListener] [DEBUG] [loggerModule.py:debug:275] GoogleTranslate 초기화 성공 +[2025-10-24 16:03:22,498] [LogListener] [DEBUG] [loggerModule.py:debug:275] MIGAN CUDA 사용 불가 - CPU 모드로 설정 +[2025-10-24 16:03:22,498] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] GPU 관리자 전달: GPUManager, can_use_cuda: False +[2025-10-24 16:03:22,518] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] GPU 관리자 사용 가능: False +[2025-10-24 16:03:22,736] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 사용 가능한 providers: ['DmlExecutionProvider', 'CPUExecutionProvider'] +[2025-10-24 16:03:22,736] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] DirectML 가속 활성화 +[2025-10-24 16:03:22,736] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 최종 providers: [('DmlExecutionProvider', {}), ('CPUExecutionProvider', {})] +[2025-10-24 16:03:22,736] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] DirectML 가속 시도: [('DmlExecutionProvider', {}), ('CPUExecutionProvider', {})] +[2025-10-24 16:03:22,736] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] DirectML 가속 성공! 실제 providers: ['DmlExecutionProvider', 'CPUExecutionProvider'] +[2025-10-24 16:03:22,737] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 입력 0: image, 형태: ['batch_size', 3, 'height', 'width'], 타입: tensor(uint8) +[2025-10-24 16:03:22,737] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 입력 1: mask, 형태: ['batch_size', 1, 'height', 'width'], 타입: tensor(uint8) +[2025-10-24 16:03:22,737] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 출력 0: result, 형태: ['ScatterNDresult_dim_0', 3, 'ScatterNDresult_dim_2', 'ScatterNDresult_dim_3'], 타입: tensor(uint8) +[2025-10-24 16:03:22,737] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 세션 준비 완료. providers=['DmlExecutionProvider', 'CPUExecutionProvider'] +[2025-10-24 16:03:22,737] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] GPU 관리자 연결 완료: GPUManager +[2025-10-24 16:03:22,737] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 파이프라인 gpu_manager 속성: True, 값: +[2025-10-24 16:03:22,737] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 초기화 완료: gpu_manager 속성=True, 값= +[2025-10-24 16:03:22,737] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔰 OCR 모듈 Warm-up 시작... +[2025-10-24 16:03:22,737] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔍 ONNX OCR 감지 방식: polygon +[2025-10-24 16:03:22,742] [LogListener] [DEBUG] [loggerModule.py:debug:275] ⚡ ONNX OCR 추론 완료: 5.0ms +[2025-10-24 16:03:22,742] [LogListener] [DEBUG] [loggerModule.py:debug:275] 📊 세부 시간 - 감지: 3.0ms, 인식: 0.0ms, 분류: 0.0ms +[2025-10-24 16:03:22,742] [LogListener] [WARNING] [loggerModule.py:warning:287] ⚠️ ONNX OCR 결과가 비어있습니다 +[2025-10-24 16:03:22,745] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ OCR 모듈 Warm-up 성공 +[2025-10-24 16:03:22,746] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔰 ImageProcessor Warm‑up 완료 +[2025-10-24 16:03:22,746] [ResultListener] [INFO] [loggerModule.py:info:281] 워커 READY 수신 +[2025-10-24 16:03:22,746] [LogListener] [DEBUG] [loggerModule.py:debug:275] 📡 추가 READY 신호 전송 완료 +[2025-10-24 16:03:22,746] [ResultListener] [INFO] [loggerModule.py:info:281] 워커 READY 수신 +[2025-10-24 16:03:22,747] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 16:03:22,747] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 32100) +[2025-10-24 16:03:22,747] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔥 작업 수신 성공 +[2025-10-24 16:03:22,747] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🚀 작업 처리 시작: cmd=__PING__, uid=f8c3a63a-148f-401b-92b2-3566cbddce9e +[2025-10-24 16:03:22,747] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 실행 직전: cmd=__PING__ +[2025-10-24 16:03:22,747] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 결과 반환 중: uid=f8c3a63a-148f-401b-92b2-3566cbddce9e +[2025-10-24 16:03:22,747] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 결과 반환 완료: uid=f8c3a63a-148f-401b-92b2-3566cbddce9e +[2025-10-24 16:03:22,747] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 16:03:22,748] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 32100) +[2025-10-24 16:03:25,329] [MainThread] [INFO] [loggerModule.py:info:281] API 서버 종료 +[2025-10-24 16:03:25,602] [MainThread] [INFO] [loggerModule.py:info:281] 워커 프로세스 종료 +[2025-10-24 17:17:06,363] [MainThread] [INFO] [loggerModule.py:info:281] API 서버 시작 +[2025-10-24 17:17:06,369] [MainThread] [INFO] [loggerModule.py:info:281] 워커 프로세스 기동: PID=17276 +[2025-10-24 17:17:06,878] [LogListener] [DEBUG] [loggerModule.py:debug:275] ImageWorker 프로세스 기동 (PID=17276, Name=ImageWorkerProcess) +[2025-10-24 17:17:06,879] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔧 ImageProcessor3 초기화 시작... +[2025-10-24 17:17:06,879] [LogListener] [DEBUG] [loggerModule.py:debug:275] tracemalloc 메모리 추적 시작 +[2025-10-24 17:17:06,885] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ DirectML 환경 준비 완료 (Windows DirectX 12 기반) +[2025-10-24 17:17:06,885] [LogListener] [DEBUG] [loggerModule.py:debug:275] === 🚀 DirectML GPU 상태 초기화 시작 🚀 === +[2025-10-24 17:17:06,886] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🎯 사용자 GPU 가속 요청: False +[2025-10-24 17:17:06,886] [LogListener] [DEBUG] [loggerModule.py:debug:275] 💻 현재 운영체제: Windows +[2025-10-24 17:17:06,886] [LogListener] [DEBUG] [loggerModule.py:debug:275] GPU 가속이 비활성화됨 (toggle_states['use_cuda'] = False) +[2025-10-24 17:17:06,886] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔒 안전한 CPU 모드로 모든 GPU 설정 강제 비활성화 +[2025-10-24 17:17:06,886] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔧 ImageProcessor3 GPU 상태 요약: +[2025-10-24 17:17:06,886] [LogListener] [DEBUG] [loggerModule.py:debug:275] - CUDA 사용 가능: False +[2025-10-24 17:17:06,886] [LogListener] [DEBUG] [loggerModule.py:debug:275] - toggle_states['use_cuda']: NOT_SET +[2025-10-24 17:17:06,886] [LogListener] [DEBUG] [loggerModule.py:debug:275] - GPU 하드웨어 정보: {} +[2025-10-24 17:17:06,886] [LogListener] [DEBUG] [loggerModule.py:debug:275] ImageProcessor3 Init toggle_states: {'TEMP_IMAGE_DIR': 'C:\\ProgramData\\ImgWorker\\work', 'output_image_format': 'webp', 'use_local_rembg': True, 'local_rembg_model_path': 'D:\\py\\img_worker\\modules\\rembg_models', 'local_model_name': 'birefnet-general-lite', 'optionIMGTrans_type': 'CPU', 'detail_IMGTrans_type': 'CPU', 'thumb_trans_type': 'CPU', 'migan_use_accel': True, 'migan_onnx_path': 'D:\\py\\img_worker\\modules\\migan_onnx\\migan_pipeline_v2.onnx', 'ocr_provider_override': 'auto', 'migan_provider_override': 'auto', 'rembg_provider_override': 'auto'} +[2025-10-24 17:17:06,886] [LogListener] [DEBUG] [loggerModule.py:debug:275] is_member_valid: False +[2025-10-24 17:17:06,886] [LogListener] [DEBUG] [loggerModule.py:debug:275] debug_images 디렉토리 이미 존재: D:\py\img_worker\modules\debug_images +[2025-10-24 17:17:06,887] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.font_path: D:\py\img_worker\modules\fonts\HakgyoansimDunggeunmisoTTFB.ttf +[2025-10-24 17:17:06,887] [LogListener] [DEBUG] [loggerModule.py:debug:275] toggle_states font_path: D:\py\img_worker\modules\fonts\HakgyoansimDunggeunmisoTTFB.ttf +[2025-10-24 17:17:06,887] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.TEMP_IMAGE_DIR: C:\ProgramData\ImgWorker\work +[2025-10-24 17:17:06,887] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.debugging_save_Dir: D:\py\img_worker\modules\debug_images +[2025-10-24 17:17:06,887] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.unwanted_texts: {} +[2025-10-24 17:17:06,887] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.inpaint_method: migan +[2025-10-24 17:17:06,887] [LogListener] [DEBUG] [loggerModule.py:debug:275] Image.MAX_IMAGE_PIXELS set to 20000000 +[2025-10-24 17:17:06,887] [LogListener] [DEBUG] [loggerModule.py:debug:275] ONNX 모델 타입 설정값: 자동 선택, GPU 정보: {} +[2025-10-24 17:17:06,887] [LogListener] [DEBUG] [loggerModule.py:debug:275] 자동 선택 모드: GPU 추천 모델 simp 사용 +[2025-10-24 17:17:06,887] [LogListener] [DEBUG] [loggerModule.py:debug:275] ONNX OCR 모델 타입 결정: simp (GPU: False) +[2025-10-24 17:17:06,887] [LogListener] [DEBUG] [loggerModule.py:debug:275] ONNX OCR 모듈 CPU 모드로 설정 +[2025-10-24 17:17:06,887] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🚀 ONNX TextSystem 초기화 시작 (CPU 모드) +[2025-10-24 17:17:06,887] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🚀 ONNX TextSystem 초기화 시작 (CPU 모드) +[2025-10-24 17:17:08,413] [LogListener] [DEBUG] [loggerModule.py:debug:275] 문자 사전 파일 UTF-8 확인 완료: D:\py\img_worker\modules\onnx_ocr_module\dict\ppocr_keys_v1.txt +[2025-10-24 17:17:08,497] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ ONNX TextSystem 초기화 완료 (CPU + SIMP (호환성) 모델) +[2025-10-24 17:17:08,497] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ ONNX TextSystem 초기화 완료 (CPU + SIMP 모델) +[2025-10-24 17:17:08,497] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ ONNX OCR 모듈 초기화 성공 (CPU 모드) +[2025-10-24 17:17:08,498] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ ONNX OCR 모듈 초기화 성공 +[2025-10-24 17:17:08,498] [LogListener] [DEBUG] [loggerModule.py:debug:275] 마스크 모듈 초기화 완료 +[2025-10-24 17:17:08,498] [LogListener] [DEBUG] [loggerModule.py:debug:275] MaskModule 초기화 성공 +[2025-10-24 17:17:08,498] [LogListener] [DEBUG] [loggerModule.py:debug:275] 텍스트 렌더링 모듈 초기화 완료 +[2025-10-24 17:17:08,498] [LogListener] [DEBUG] [loggerModule.py:debug:275] 기본 폰트: D:\py\img_worker\modules\fonts\HakgyoansimDunggeunmisoTTFB.ttf +[2025-10-24 17:17:08,499] [LogListener] [DEBUG] [loggerModule.py:debug:275] TextRenderingModule 초기화 성공 +[2025-10-24 17:17:08,499] [LogListener] [DEBUG] [loggerModule.py:debug:275] 폰트 로드 성공: D:\py\img_worker\modules\fonts\HakgyoansimDunggeunmisoTTFB.ttf +[2025-10-24 17:17:08,499] [LogListener] [DEBUG] [loggerModule.py:debug:275] PostImageManager 초기화 성공 +[2025-10-24 17:17:08,499] [LogListener] [DEBUG] [loggerModule.py:debug:275] Request_AI_Server GPU 상태: CUDA 사용 가능=False +[2025-10-24 17:17:08,499] [LogListener] [DEBUG] [loggerModule.py:debug:275] Request_AI_Server 초기화 성공 +[2025-10-24 17:17:08,499] [LogListener] [DEBUG] [loggerModule.py:debug:275] GoogleTranslate 초기화 성공 +[2025-10-24 17:17:08,499] [LogListener] [DEBUG] [loggerModule.py:debug:275] MIGAN CUDA 사용 불가 - CPU 모드로 설정 +[2025-10-24 17:17:08,499] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] GPU 관리자 전달: GPUManager, can_use_cuda: False +[2025-10-24 17:17:08,728] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] GPU 관리자 사용 가능: False +[2025-10-24 17:17:08,729] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 사용 가능한 providers: ['DmlExecutionProvider', 'CPUExecutionProvider'] +[2025-10-24 17:17:08,729] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] DirectML 가속 활성화 +[2025-10-24 17:17:08,729] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 최종 providers: [('DmlExecutionProvider', {}), ('CPUExecutionProvider', {})] +[2025-10-24 17:17:08,729] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] DirectML 가속 시도: [('DmlExecutionProvider', {}), ('CPUExecutionProvider', {})] +[2025-10-24 17:17:08,729] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] DirectML 가속 성공! 실제 providers: ['DmlExecutionProvider', 'CPUExecutionProvider'] +[2025-10-24 17:17:08,730] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 입력 0: image, 형태: ['batch_size', 3, 'height', 'width'], 타입: tensor(uint8) +[2025-10-24 17:17:08,730] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 입력 1: mask, 형태: ['batch_size', 1, 'height', 'width'], 타입: tensor(uint8) +[2025-10-24 17:17:08,730] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 출력 0: result, 형태: ['ScatterNDresult_dim_0', 3, 'ScatterNDresult_dim_2', 'ScatterNDresult_dim_3'], 타입: tensor(uint8) +[2025-10-24 17:17:08,730] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 세션 준비 완료. providers=['DmlExecutionProvider', 'CPUExecutionProvider'] +[2025-10-24 17:17:08,730] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] GPU 관리자 연결 완료: GPUManager +[2025-10-24 17:17:08,730] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 파이프라인 gpu_manager 속성: True, 값: +[2025-10-24 17:17:08,731] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 초기화 완료: gpu_manager 속성=True, 값= +[2025-10-24 17:17:08,731] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔰 OCR 모듈 Warm-up 시작... +[2025-10-24 17:17:08,731] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔍 ONNX OCR 감지 방식: polygon +[2025-10-24 17:17:08,736] [LogListener] [DEBUG] [loggerModule.py:debug:275] ⚡ ONNX OCR 추론 완료: 6.0ms +[2025-10-24 17:17:08,736] [LogListener] [DEBUG] [loggerModule.py:debug:275] 📊 세부 시간 - 감지: 4.0ms, 인식: 0.0ms, 분류: 0.0ms +[2025-10-24 17:17:08,736] [LogListener] [WARNING] [loggerModule.py:warning:287] ⚠️ ONNX OCR 결과가 비어있습니다 +[2025-10-24 17:17:08,740] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ OCR 모듈 Warm-up 성공 +[2025-10-24 17:17:08,740] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔰 ImageProcessor Warm‑up 완료 +[2025-10-24 17:17:08,740] [ResultListener] [INFO] [loggerModule.py:info:281] 워커 READY 수신 +[2025-10-24 17:17:08,741] [ResultListener] [INFO] [loggerModule.py:info:281] 워커 READY 수신 +[2025-10-24 17:17:08,741] [LogListener] [DEBUG] [loggerModule.py:debug:275] 📡 추가 READY 신호 전송 완료 +[2025-10-24 17:17:08,741] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 17:17:08,741] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 17276) +[2025-10-24 17:17:08,742] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔥 작업 수신 성공 +[2025-10-24 17:17:08,742] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🚀 작업 처리 시작: cmd=__PING__, uid=e2915b92-6809-4e3b-abe4-49179da1672c +[2025-10-24 17:17:08,742] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 실행 직전: cmd=__PING__ +[2025-10-24 17:17:08,742] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 결과 반환 중: uid=e2915b92-6809-4e3b-abe4-49179da1672c +[2025-10-24 17:17:08,742] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 결과 반환 완료: uid=e2915b92-6809-4e3b-abe4-49179da1672c +[2025-10-24 17:17:08,742] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 17:17:08,742] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 17276) +[2025-10-24 17:18:08,753] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 17:18:08,753] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 17:18:08,753] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 17276) +[2025-10-24 17:19:08,754] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 17:19:08,754] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 17:19:08,754] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 17276) +[2025-10-24 17:20:08,764] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 17:20:08,764] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 17:20:08,765] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 17276) +[2025-10-24 17:20:54,106] [MainThread] [INFO] [loggerModule.py:info:281] API 서버 종료 +[2025-10-24 17:20:54,377] [MainThread] [INFO] [loggerModule.py:info:281] 워커 프로세스 종료 +[2025-10-24 17:20:55,860] [MainThread] [INFO] [loggerModule.py:info:281] API 서버 시작 +[2025-10-24 17:20:55,865] [MainThread] [INFO] [loggerModule.py:info:281] 워커 프로세스 기동: PID=26812 +[2025-10-24 17:20:56,304] [LogListener] [DEBUG] [loggerModule.py:debug:275] ImageWorker 프로세스 기동 (PID=26812, Name=ImageWorkerProcess) +[2025-10-24 17:20:56,304] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔧 ImageProcessor3 초기화 시작... +[2025-10-24 17:20:56,304] [LogListener] [DEBUG] [loggerModule.py:debug:275] tracemalloc 메모리 추적 시작 +[2025-10-24 17:20:56,310] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ DirectML 환경 준비 완료 (Windows DirectX 12 기반) +[2025-10-24 17:20:56,311] [LogListener] [DEBUG] [loggerModule.py:debug:275] === 🚀 DirectML GPU 상태 초기화 시작 🚀 === +[2025-10-24 17:20:56,311] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🎯 사용자 GPU 가속 요청: False +[2025-10-24 17:20:56,311] [LogListener] [DEBUG] [loggerModule.py:debug:275] 💻 현재 운영체제: Windows +[2025-10-24 17:20:56,311] [LogListener] [DEBUG] [loggerModule.py:debug:275] GPU 가속이 비활성화됨 (toggle_states['use_cuda'] = False) +[2025-10-24 17:20:56,311] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔒 안전한 CPU 모드로 모든 GPU 설정 강제 비활성화 +[2025-10-24 17:20:56,311] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔧 ImageProcessor3 GPU 상태 요약: +[2025-10-24 17:20:56,311] [LogListener] [DEBUG] [loggerModule.py:debug:275] - CUDA 사용 가능: False +[2025-10-24 17:20:56,311] [LogListener] [DEBUG] [loggerModule.py:debug:275] - toggle_states['use_cuda']: NOT_SET +[2025-10-24 17:20:56,311] [LogListener] [DEBUG] [loggerModule.py:debug:275] - GPU 하드웨어 정보: {} +[2025-10-24 17:20:56,312] [LogListener] [DEBUG] [loggerModule.py:debug:275] ImageProcessor3 Init toggle_states: {'TEMP_IMAGE_DIR': 'C:\\ProgramData\\ImgWorker\\work', 'output_image_format': 'webp', 'use_local_rembg': True, 'local_rembg_model_path': 'D:\\py\\img_worker\\modules\\rembg_models', 'local_model_name': 'birefnet-general-lite', 'optionIMGTrans_type': 'CPU', 'detail_IMGTrans_type': 'CPU', 'thumb_trans_type': 'CPU', 'migan_use_accel': True, 'migan_onnx_path': 'D:\\py\\img_worker\\modules\\migan_onnx\\migan_pipeline_v2.onnx', 'ocr_provider_override': 'auto', 'migan_provider_override': 'auto', 'rembg_provider_override': 'auto'} +[2025-10-24 17:20:56,312] [LogListener] [DEBUG] [loggerModule.py:debug:275] is_member_valid: False +[2025-10-24 17:20:56,312] [LogListener] [DEBUG] [loggerModule.py:debug:275] debug_images 디렉토리 이미 존재: D:\py\img_worker\modules\debug_images +[2025-10-24 17:20:56,312] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.font_path: D:\py\img_worker\modules\fonts\HakgyoansimDunggeunmisoTTFB.ttf +[2025-10-24 17:20:56,312] [LogListener] [DEBUG] [loggerModule.py:debug:275] toggle_states font_path: D:\py\img_worker\modules\fonts\HakgyoansimDunggeunmisoTTFB.ttf +[2025-10-24 17:20:56,312] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.TEMP_IMAGE_DIR: C:\ProgramData\ImgWorker\work +[2025-10-24 17:20:56,312] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.debugging_save_Dir: D:\py\img_worker\modules\debug_images +[2025-10-24 17:20:56,312] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.unwanted_texts: {} +[2025-10-24 17:20:56,312] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.inpaint_method: migan +[2025-10-24 17:20:56,312] [LogListener] [DEBUG] [loggerModule.py:debug:275] Image.MAX_IMAGE_PIXELS set to 20000000 +[2025-10-24 17:20:56,312] [LogListener] [DEBUG] [loggerModule.py:debug:275] ONNX 모델 타입 설정값: 자동 선택, GPU 정보: {} +[2025-10-24 17:20:56,312] [LogListener] [DEBUG] [loggerModule.py:debug:275] 자동 선택 모드: GPU 추천 모델 simp 사용 +[2025-10-24 17:20:56,312] [LogListener] [DEBUG] [loggerModule.py:debug:275] ONNX OCR 모델 타입 결정: simp (GPU: False) +[2025-10-24 17:20:56,312] [LogListener] [DEBUG] [loggerModule.py:debug:275] ONNX OCR 모듈 CPU 모드로 설정 +[2025-10-24 17:20:56,313] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🚀 ONNX TextSystem 초기화 시작 (CPU 모드) +[2025-10-24 17:20:56,313] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🚀 ONNX TextSystem 초기화 시작 (CPU 모드) +[2025-10-24 17:20:57,812] [LogListener] [DEBUG] [loggerModule.py:debug:275] 문자 사전 파일 UTF-8 확인 완료: D:\py\img_worker\modules\onnx_ocr_module\dict\ppocr_keys_v1.txt +[2025-10-24 17:20:57,897] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ ONNX TextSystem 초기화 완료 (CPU + SIMP (호환성) 모델) +[2025-10-24 17:20:57,898] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ ONNX TextSystem 초기화 완료 (CPU + SIMP 모델) +[2025-10-24 17:20:57,898] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ ONNX OCR 모듈 초기화 성공 (CPU 모드) +[2025-10-24 17:20:57,899] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ ONNX OCR 모듈 초기화 성공 +[2025-10-24 17:20:57,899] [LogListener] [DEBUG] [loggerModule.py:debug:275] 마스크 모듈 초기화 완료 +[2025-10-24 17:20:57,899] [LogListener] [DEBUG] [loggerModule.py:debug:275] MaskModule 초기화 성공 +[2025-10-24 17:20:57,899] [LogListener] [DEBUG] [loggerModule.py:debug:275] 텍스트 렌더링 모듈 초기화 완료 +[2025-10-24 17:20:57,899] [LogListener] [DEBUG] [loggerModule.py:debug:275] 기본 폰트: D:\py\img_worker\modules\fonts\HakgyoansimDunggeunmisoTTFB.ttf +[2025-10-24 17:20:57,899] [LogListener] [DEBUG] [loggerModule.py:debug:275] TextRenderingModule 초기화 성공 +[2025-10-24 17:20:57,899] [LogListener] [DEBUG] [loggerModule.py:debug:275] 폰트 로드 성공: D:\py\img_worker\modules\fonts\HakgyoansimDunggeunmisoTTFB.ttf +[2025-10-24 17:20:57,899] [LogListener] [DEBUG] [loggerModule.py:debug:275] PostImageManager 초기화 성공 +[2025-10-24 17:20:57,899] [LogListener] [DEBUG] [loggerModule.py:debug:275] Request_AI_Server GPU 상태: CUDA 사용 가능=False +[2025-10-24 17:20:57,899] [LogListener] [DEBUG] [loggerModule.py:debug:275] Request_AI_Server 초기화 성공 +[2025-10-24 17:20:57,900] [LogListener] [DEBUG] [loggerModule.py:debug:275] GoogleTranslate 초기화 성공 +[2025-10-24 17:20:57,900] [LogListener] [DEBUG] [loggerModule.py:debug:275] MIGAN CUDA 사용 불가 - CPU 모드로 설정 +[2025-10-24 17:20:57,900] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] GPU 관리자 전달: GPUManager, can_use_cuda: False +[2025-10-24 17:20:57,918] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] GPU 관리자 사용 가능: False +[2025-10-24 17:20:58,130] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 사용 가능한 providers: ['DmlExecutionProvider', 'CPUExecutionProvider'] +[2025-10-24 17:20:58,130] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] DirectML 가속 활성화 +[2025-10-24 17:20:58,130] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 최종 providers: [('DmlExecutionProvider', {}), ('CPUExecutionProvider', {})] +[2025-10-24 17:20:58,130] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] DirectML 가속 시도: [('DmlExecutionProvider', {}), ('CPUExecutionProvider', {})] +[2025-10-24 17:20:58,131] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] DirectML 가속 성공! 실제 providers: ['DmlExecutionProvider', 'CPUExecutionProvider'] +[2025-10-24 17:20:58,131] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 입력 0: image, 형태: ['batch_size', 3, 'height', 'width'], 타입: tensor(uint8) +[2025-10-24 17:20:58,131] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 입력 1: mask, 형태: ['batch_size', 1, 'height', 'width'], 타입: tensor(uint8) +[2025-10-24 17:20:58,131] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 출력 0: result, 형태: ['ScatterNDresult_dim_0', 3, 'ScatterNDresult_dim_2', 'ScatterNDresult_dim_3'], 타입: tensor(uint8) +[2025-10-24 17:20:58,131] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 세션 준비 완료. providers=['DmlExecutionProvider', 'CPUExecutionProvider'] +[2025-10-24 17:20:58,131] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] GPU 관리자 연결 완료: GPUManager +[2025-10-24 17:20:58,131] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 파이프라인 gpu_manager 속성: True, 값: +[2025-10-24 17:20:58,131] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 초기화 완료: gpu_manager 속성=True, 값= +[2025-10-24 17:20:58,132] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔰 OCR 모듈 Warm-up 시작... +[2025-10-24 17:20:58,132] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔍 ONNX OCR 감지 방식: polygon +[2025-10-24 17:20:58,136] [LogListener] [DEBUG] [loggerModule.py:debug:275] ⚡ ONNX OCR 추론 완료: 4.0ms +[2025-10-24 17:20:58,136] [LogListener] [DEBUG] [loggerModule.py:debug:275] 📊 세부 시간 - 감지: 3.0ms, 인식: 0.0ms, 분류: 0.0ms +[2025-10-24 17:20:58,136] [LogListener] [WARNING] [loggerModule.py:warning:287] ⚠️ ONNX OCR 결과가 비어있습니다 +[2025-10-24 17:20:58,140] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ OCR 모듈 Warm-up 성공 +[2025-10-24 17:20:58,140] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔰 ImageProcessor Warm‑up 완료 +[2025-10-24 17:20:58,140] [ResultListener] [INFO] [loggerModule.py:info:281] 워커 READY 수신 +[2025-10-24 17:20:58,140] [ResultListener] [INFO] [loggerModule.py:info:281] 워커 READY 수신 +[2025-10-24 17:20:58,140] [LogListener] [DEBUG] [loggerModule.py:debug:275] 📡 추가 READY 신호 전송 완료 +[2025-10-24 17:20:58,141] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 17:20:58,141] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 26812) +[2025-10-24 17:20:58,141] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔥 작업 수신 성공 +[2025-10-24 17:20:58,141] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🚀 작업 처리 시작: cmd=__PING__, uid=9607aea6-8e4a-4a6c-a4b8-4c571bfd949a +[2025-10-24 17:20:58,141] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 실행 직전: cmd=__PING__ +[2025-10-24 17:20:58,141] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 결과 반환 중: uid=9607aea6-8e4a-4a6c-a4b8-4c571bfd949a +[2025-10-24 17:20:58,142] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 결과 반환 완료: uid=9607aea6-8e4a-4a6c-a4b8-4c571bfd949a +[2025-10-24 17:20:58,142] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 17:20:58,142] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 26812) +[2025-10-24 17:21:07,251] [MainThread] [INFO] [loggerModule.py:info:281] API 서버 종료 +[2025-10-24 17:21:07,452] [MainThread] [INFO] [loggerModule.py:info:281] 워커 프로세스 종료 +[2025-10-24 17:21:10,496] [MainThread] [INFO] [loggerModule.py:info:281] API 서버 시작 +[2025-10-24 17:21:10,502] [MainThread] [INFO] [loggerModule.py:info:281] 워커 프로세스 기동: PID=16136 +[2025-10-24 17:21:10,965] [LogListener] [DEBUG] [loggerModule.py:debug:275] ImageWorker 프로세스 기동 (PID=16136, Name=ImageWorkerProcess) +[2025-10-24 17:21:10,965] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔧 ImageProcessor3 초기화 시작... +[2025-10-24 17:21:10,965] [LogListener] [DEBUG] [loggerModule.py:debug:275] tracemalloc 메모리 추적 시작 +[2025-10-24 17:21:10,972] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ DirectML 환경 준비 완료 (Windows DirectX 12 기반) +[2025-10-24 17:21:10,972] [LogListener] [DEBUG] [loggerModule.py:debug:275] === 🚀 DirectML GPU 상태 초기화 시작 🚀 === +[2025-10-24 17:21:10,972] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🎯 사용자 GPU 가속 요청: False +[2025-10-24 17:21:10,972] [LogListener] [DEBUG] [loggerModule.py:debug:275] 💻 현재 운영체제: Windows +[2025-10-24 17:21:10,973] [LogListener] [DEBUG] [loggerModule.py:debug:275] GPU 가속이 비활성화됨 (toggle_states['use_cuda'] = False) +[2025-10-24 17:21:10,973] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔒 안전한 CPU 모드로 모든 GPU 설정 강제 비활성화 +[2025-10-24 17:21:10,973] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔧 ImageProcessor3 GPU 상태 요약: +[2025-10-24 17:21:10,973] [LogListener] [DEBUG] [loggerModule.py:debug:275] - CUDA 사용 가능: False +[2025-10-24 17:21:10,973] [LogListener] [DEBUG] [loggerModule.py:debug:275] - toggle_states['use_cuda']: NOT_SET +[2025-10-24 17:21:10,973] [LogListener] [DEBUG] [loggerModule.py:debug:275] - GPU 하드웨어 정보: {} +[2025-10-24 17:21:10,973] [LogListener] [DEBUG] [loggerModule.py:debug:275] ImageProcessor3 Init toggle_states: {'TEMP_IMAGE_DIR': 'C:\\ProgramData\\ImgWorker\\work', 'output_image_format': 'webp', 'use_local_rembg': True, 'local_rembg_model_path': 'D:\\py\\img_worker\\modules\\rembg_models', 'local_model_name': 'birefnet-general-lite', 'optionIMGTrans_type': 'CPU', 'detail_IMGTrans_type': 'CPU', 'thumb_trans_type': 'CPU', 'migan_use_accel': True, 'migan_onnx_path': 'D:\\py\\img_worker\\modules\\migan_onnx\\migan_pipeline_v2.onnx', 'ocr_provider_override': 'auto', 'migan_provider_override': 'auto', 'rembg_provider_override': 'auto'} +[2025-10-24 17:21:10,974] [LogListener] [DEBUG] [loggerModule.py:debug:275] is_member_valid: False +[2025-10-24 17:21:10,974] [LogListener] [DEBUG] [loggerModule.py:debug:275] debug_images 디렉토리 이미 존재: D:\py\img_worker\modules\debug_images +[2025-10-24 17:21:10,974] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.font_path: D:\py\img_worker\modules\fonts\HakgyoansimDunggeunmisoTTFB.ttf +[2025-10-24 17:21:10,974] [LogListener] [DEBUG] [loggerModule.py:debug:275] toggle_states font_path: D:\py\img_worker\modules\fonts\HakgyoansimDunggeunmisoTTFB.ttf +[2025-10-24 17:21:10,974] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.TEMP_IMAGE_DIR: C:\ProgramData\ImgWorker\work +[2025-10-24 17:21:10,974] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.debugging_save_Dir: D:\py\img_worker\modules\debug_images +[2025-10-24 17:21:10,974] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.unwanted_texts: {} +[2025-10-24 17:21:10,974] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.inpaint_method: migan +[2025-10-24 17:21:10,974] [LogListener] [DEBUG] [loggerModule.py:debug:275] Image.MAX_IMAGE_PIXELS set to 20000000 +[2025-10-24 17:21:10,974] [LogListener] [DEBUG] [loggerModule.py:debug:275] ONNX 모델 타입 설정값: 자동 선택, GPU 정보: {} +[2025-10-24 17:21:10,974] [LogListener] [DEBUG] [loggerModule.py:debug:275] 자동 선택 모드: GPU 추천 모델 simp 사용 +[2025-10-24 17:21:10,975] [LogListener] [DEBUG] [loggerModule.py:debug:275] ONNX OCR 모델 타입 결정: simp (GPU: False) +[2025-10-24 17:21:10,975] [LogListener] [DEBUG] [loggerModule.py:debug:275] ONNX OCR 모듈 CPU 모드로 설정 +[2025-10-24 17:21:10,975] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🚀 ONNX TextSystem 초기화 시작 (CPU 모드) +[2025-10-24 17:21:10,975] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🚀 ONNX TextSystem 초기화 시작 (CPU 모드) +[2025-10-24 17:21:12,501] [LogListener] [DEBUG] [loggerModule.py:debug:275] 문자 사전 파일 UTF-8 확인 완료: D:\py\img_worker\modules\onnx_ocr_module\dict\ppocr_keys_v1.txt +[2025-10-24 17:21:12,584] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ ONNX TextSystem 초기화 완료 (CPU + SIMP (호환성) 모델) +[2025-10-24 17:21:12,584] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ ONNX TextSystem 초기화 완료 (CPU + SIMP 모델) +[2025-10-24 17:21:12,584] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ ONNX OCR 모듈 초기화 성공 (CPU 모드) +[2025-10-24 17:21:12,585] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ ONNX OCR 모듈 초기화 성공 +[2025-10-24 17:21:12,585] [LogListener] [DEBUG] [loggerModule.py:debug:275] 마스크 모듈 초기화 완료 +[2025-10-24 17:21:12,585] [LogListener] [DEBUG] [loggerModule.py:debug:275] MaskModule 초기화 성공 +[2025-10-24 17:21:12,585] [LogListener] [DEBUG] [loggerModule.py:debug:275] 텍스트 렌더링 모듈 초기화 완료 +[2025-10-24 17:21:12,585] [LogListener] [DEBUG] [loggerModule.py:debug:275] 기본 폰트: D:\py\img_worker\modules\fonts\HakgyoansimDunggeunmisoTTFB.ttf +[2025-10-24 17:21:12,585] [LogListener] [DEBUG] [loggerModule.py:debug:275] TextRenderingModule 초기화 성공 +[2025-10-24 17:21:12,585] [LogListener] [DEBUG] [loggerModule.py:debug:275] 폰트 로드 성공: D:\py\img_worker\modules\fonts\HakgyoansimDunggeunmisoTTFB.ttf +[2025-10-24 17:21:12,585] [LogListener] [DEBUG] [loggerModule.py:debug:275] PostImageManager 초기화 성공 +[2025-10-24 17:21:12,586] [LogListener] [DEBUG] [loggerModule.py:debug:275] Request_AI_Server GPU 상태: CUDA 사용 가능=False +[2025-10-24 17:21:12,586] [LogListener] [DEBUG] [loggerModule.py:debug:275] Request_AI_Server 초기화 성공 +[2025-10-24 17:21:12,586] [LogListener] [DEBUG] [loggerModule.py:debug:275] GoogleTranslate 초기화 성공 +[2025-10-24 17:21:12,586] [LogListener] [DEBUG] [loggerModule.py:debug:275] MIGAN CUDA 사용 불가 - CPU 모드로 설정 +[2025-10-24 17:21:12,586] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] GPU 관리자 전달: GPUManager, can_use_cuda: False +[2025-10-24 17:21:12,812] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] GPU 관리자 사용 가능: False +[2025-10-24 17:21:12,813] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 사용 가능한 providers: ['DmlExecutionProvider', 'CPUExecutionProvider'] +[2025-10-24 17:21:12,813] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] DirectML 가속 활성화 +[2025-10-24 17:21:12,813] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 최종 providers: [('DmlExecutionProvider', {}), ('CPUExecutionProvider', {})] +[2025-10-24 17:21:12,813] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] DirectML 가속 시도: [('DmlExecutionProvider', {}), ('CPUExecutionProvider', {})] +[2025-10-24 17:21:12,813] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] DirectML 가속 성공! 실제 providers: ['DmlExecutionProvider', 'CPUExecutionProvider'] +[2025-10-24 17:21:12,813] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 입력 0: image, 형태: ['batch_size', 3, 'height', 'width'], 타입: tensor(uint8) +[2025-10-24 17:21:12,813] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 입력 1: mask, 형태: ['batch_size', 1, 'height', 'width'], 타입: tensor(uint8) +[2025-10-24 17:21:12,813] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 출력 0: result, 형태: ['ScatterNDresult_dim_0', 3, 'ScatterNDresult_dim_2', 'ScatterNDresult_dim_3'], 타입: tensor(uint8) +[2025-10-24 17:21:12,814] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 세션 준비 완료. providers=['DmlExecutionProvider', 'CPUExecutionProvider'] +[2025-10-24 17:21:12,814] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] GPU 관리자 연결 완료: GPUManager +[2025-10-24 17:21:12,814] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 파이프라인 gpu_manager 속성: True, 값: +[2025-10-24 17:21:12,814] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 초기화 완료: gpu_manager 속성=True, 값= +[2025-10-24 17:21:12,814] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔰 OCR 모듈 Warm-up 시작... +[2025-10-24 17:21:12,814] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔍 ONNX OCR 감지 방식: polygon +[2025-10-24 17:21:12,818] [LogListener] [DEBUG] [loggerModule.py:debug:275] ⚡ ONNX OCR 추론 완료: 4.0ms +[2025-10-24 17:21:12,818] [LogListener] [DEBUG] [loggerModule.py:debug:275] 📊 세부 시간 - 감지: 3.0ms, 인식: 0.0ms, 분류: 0.0ms +[2025-10-24 17:21:12,818] [LogListener] [WARNING] [loggerModule.py:warning:287] ⚠️ ONNX OCR 결과가 비어있습니다 +[2025-10-24 17:21:12,823] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ OCR 모듈 Warm-up 성공 +[2025-10-24 17:21:12,823] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔰 ImageProcessor Warm‑up 완료 +[2025-10-24 17:21:12,823] [ResultListener] [INFO] [loggerModule.py:info:281] 워커 READY 수신 +[2025-10-24 17:21:12,823] [ResultListener] [INFO] [loggerModule.py:info:281] 워커 READY 수신 +[2025-10-24 17:21:12,823] [LogListener] [DEBUG] [loggerModule.py:debug:275] 📡 추가 READY 신호 전송 완료 +[2025-10-24 17:21:12,824] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 17:21:12,824] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 16136) +[2025-10-24 17:21:12,824] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔥 작업 수신 성공 +[2025-10-24 17:21:12,824] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🚀 작업 처리 시작: cmd=__PING__, uid=3f336340-9037-4224-9af1-9579094b8b98 +[2025-10-24 17:21:12,824] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 실행 직전: cmd=__PING__ +[2025-10-24 17:21:12,824] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 결과 반환 중: uid=3f336340-9037-4224-9af1-9579094b8b98 +[2025-10-24 17:21:12,824] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 결과 반환 완료: uid=3f336340-9037-4224-9af1-9579094b8b98 +[2025-10-24 17:21:12,825] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 17:21:12,825] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 16136) +[2025-10-24 17:22:12,835] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-24 17:22:12,835] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-24 17:22:12,835] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 16136) +[2025-10-24 17:22:24,110] [MainThread] [INFO] [loggerModule.py:info:281] API 서버 종료 +[2025-10-24 17:22:24,359] [MainThread] [INFO] [loggerModule.py:info:281] 워커 프로세스 종료 diff --git a/logs/api_server.log.2025-10-23.log b/logs/api_server.log.2025-10-23.log new file mode 100644 index 0000000..493a4dc --- /dev/null +++ b/logs/api_server.log.2025-10-23.log @@ -0,0 +1,1731 @@ +[2025-10-23 22:37:34,979] [MainThread] [INFO] [loggerModule.py:info:281] API 서버 시작 +[2025-10-23 22:37:34,985] [MainThread] [INFO] [loggerModule.py:info:281] 워커 프로세스 기동: PID=19228 +[2025-10-23 22:37:35,454] [LogListener] [DEBUG] [loggerModule.py:debug:275] ImageWorker 프로세스 기동 (PID=19228, Name=ImageWorkerProcess) +[2025-10-23 22:37:35,454] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔧 ImageProcessor3 초기화 시작... +[2025-10-23 22:37:35,454] [LogListener] [DEBUG] [loggerModule.py:debug:275] tracemalloc 메모리 추적 시작 +[2025-10-23 22:37:35,460] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ DirectML 환경 준비 완료 (Windows DirectX 12 기반) +[2025-10-23 22:37:35,461] [LogListener] [DEBUG] [loggerModule.py:debug:275] === 🚀 DirectML GPU 상태 초기화 시작 🚀 === +[2025-10-23 22:37:35,461] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🎯 사용자 GPU 가속 요청: False +[2025-10-23 22:37:35,461] [LogListener] [DEBUG] [loggerModule.py:debug:275] 💻 현재 운영체제: Windows +[2025-10-23 22:37:35,461] [LogListener] [DEBUG] [loggerModule.py:debug:275] GPU 가속이 비활성화됨 (toggle_states['use_cuda'] = False) +[2025-10-23 22:37:35,461] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔒 안전한 CPU 모드로 모든 GPU 설정 강제 비활성화 +[2025-10-23 22:37:35,461] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔧 ImageProcessor3 GPU 상태 요약: +[2025-10-23 22:37:35,462] [LogListener] [DEBUG] [loggerModule.py:debug:275] - CUDA 사용 가능: False +[2025-10-23 22:37:35,462] [LogListener] [DEBUG] [loggerModule.py:debug:275] - toggle_states['use_cuda']: NOT_SET +[2025-10-23 22:37:35,462] [LogListener] [DEBUG] [loggerModule.py:debug:275] - GPU 하드웨어 정보: {} +[2025-10-23 22:37:35,462] [LogListener] [DEBUG] [loggerModule.py:debug:275] ImageProcessor3 Init toggle_states: {'TEMP_IMAGE_DIR': 'C:\\ProgramData\\ImgWorker\\work', 'output_image_format': 'webp', 'use_local_rembg': True, 'local_rembg_model_path': 'D:\\py\\img_worker\\modules\\rembg_models', 'local_model_name': 'birefnet-general-lite', 'optionIMGTrans_type': 'CPU', 'detail_IMGTrans_type': 'CPU', 'thumb_trans_type': 'CPU', 'migan_use_accel': True, 'migan_onnx_path': 'D:\\py\\img_worker\\modules\\migan_onnx\\migan_pipeline_v2.onnx', 'ocr_provider_override': 'auto'} +[2025-10-23 22:37:35,462] [LogListener] [DEBUG] [loggerModule.py:debug:275] is_member_valid: False +[2025-10-23 22:37:35,462] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔄 인페인팅 서버 선택 - 타입: main +[2025-10-23 22:37:35,462] [LogListener] [DEBUG] [loggerModule.py:debug:275] 메인서버: None +[2025-10-23 22:37:35,462] [LogListener] [DEBUG] [loggerModule.py:debug:275] 테스트서버: None +[2025-10-23 22:37:35,462] [LogListener] [ERROR] [loggerModule.py:error:293] ❌ 메인 인페인팅 서버 사용 불가: None +[2025-10-23 22:37:35,463] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔄 배경제거 서버 선택 - 타입: main +[2025-10-23 22:37:35,463] [LogListener] [DEBUG] [loggerModule.py:debug:275] 메인서버: None +[2025-10-23 22:37:35,463] [LogListener] [DEBUG] [loggerModule.py:debug:275] 테스트서버: None +[2025-10-23 22:37:35,463] [LogListener] [ERROR] [loggerModule.py:error:293] ❌ 메인 배경제거 서버 사용 불가: None +[2025-10-23 22:37:35,463] [LogListener] [ERROR] [loggerModule.py:error:293] request_inpainting_server_url 설정되지 않았습니다. +[2025-10-23 22:37:35,463] [LogListener] [DEBUG] [loggerModule.py:debug:275] debug_images 디렉토리 생성: D:\py\img_worker\modules\debug_images +[2025-10-23 22:37:35,463] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.font_path: D:\py\img_worker\modules\HakgyoansimDunggeunmisoTTFB.ttf +[2025-10-23 22:37:35,463] [LogListener] [DEBUG] [loggerModule.py:debug:275] toggle_states font_path: +[2025-10-23 22:37:35,463] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.TEMP_IMAGE_DIR: C:\ProgramData\ImgWorker\work +[2025-10-23 22:37:35,463] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.debugging_save_Dir: D:\py\img_worker\modules\debug_images +[2025-10-23 22:37:35,464] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.unwanted_texts: {} +[2025-10-23 22:37:35,464] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.inpaint_method: cv +[2025-10-23 22:37:35,464] [LogListener] [DEBUG] [loggerModule.py:debug:275] Image.MAX_IMAGE_PIXELS set to 20000000 +[2025-10-23 22:37:35,464] [LogListener] [DEBUG] [loggerModule.py:debug:275] ONNX 모델 타입 설정값: 자동 선택, GPU 정보: {} +[2025-10-23 22:37:35,464] [LogListener] [DEBUG] [loggerModule.py:debug:275] 자동 선택 모드: GPU 추천 모델 simp 사용 +[2025-10-23 22:37:35,464] [LogListener] [DEBUG] [loggerModule.py:debug:275] ONNX OCR 모델 타입 결정: simp (GPU: False) +[2025-10-23 22:37:35,464] [LogListener] [DEBUG] [loggerModule.py:debug:275] ONNX OCR 모듈 CPU 모드로 설정 +[2025-10-23 22:37:35,465] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🚀 ONNX TextSystem 초기화 시작 (CPU 모드) +[2025-10-23 22:37:35,465] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🚀 ONNX TextSystem 초기화 시작 (CPU 모드) +[2025-10-23 22:37:35,479] [LogListener] [ERROR] [loggerModule.py:error:293] ❌ ONNX TextSystem 초기화 실패: No module named 'pyclipper' +[2025-10-23 22:37:35,479] [LogListener] [WARNING] [loggerModule.py:warning:287] ONNX 초기화 1차 시도 실패: No module named 'pyclipper' +[2025-10-23 22:37:35,479] [LogListener] [ERROR] [loggerModule.py:error:293] ❌ ONNX TextSystem 모든 초기화 시도 실패: 원본 설정 예외: No module named 'pyclipper' +[2025-10-23 22:37:35,480] [LogListener] [ERROR] [loggerModule.py:error:293] ❌ ONNX OCR 모듈 초기화 실패: ONNX TextSystem 모든 초기화 시도 실패: 원본 설정 예외: No module named 'pyclipper' +[2025-10-23 22:37:35,480] [LogListener] [DEBUG] [loggerModule.py:debug:275] 마스크 모듈 초기화 완료 +[2025-10-23 22:37:35,480] [LogListener] [DEBUG] [loggerModule.py:debug:275] MaskModule 초기화 성공 +[2025-10-23 22:37:35,480] [LogListener] [DEBUG] [loggerModule.py:debug:275] 텍스트 렌더링 모듈 초기화 완료 +[2025-10-23 22:37:35,480] [LogListener] [DEBUG] [loggerModule.py:debug:275] 기본 폰트: D:\py\img_worker\modules\HakgyoansimDunggeunmisoTTFB.ttf +[2025-10-23 22:37:35,480] [LogListener] [DEBUG] [loggerModule.py:debug:275] TextRenderingModule 초기화 성공 +[2025-10-23 22:37:35,481] [LogListener] [WARNING] [loggerModule.py:warning:287] 커스텀 폰트 로드 실패 (HakgyoansimDunggeunmisoTTFB.ttf): cannot open resource +[2025-10-23 22:37:35,481] [LogListener] [DEBUG] [loggerModule.py:debug:275] 기본 폰트를 사용합니다. +[2025-10-23 22:37:35,481] [LogListener] [DEBUG] [loggerModule.py:debug:275] PostImageManager 초기화 성공 +[2025-10-23 22:37:35,481] [LogListener] [DEBUG] [loggerModule.py:debug:275] 개발환경 +[2025-10-23 22:37:35,482] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔧 개발환경이므로 rembg 로컬 서버 강제 사용: None +[2025-10-23 22:37:35,482] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔧 개발환경이므로 인페인팅 로컬 서버 강제 사용: None +[2025-10-23 22:37:35,482] [LogListener] [DEBUG] [loggerModule.py:debug:275] 📡 최종 선택된 서버 URL: +[2025-10-23 22:37:35,482] [LogListener] [DEBUG] [loggerModule.py:debug:275] 인페인팅 서버: None +[2025-10-23 22:37:35,482] [LogListener] [DEBUG] [loggerModule.py:debug:275] 배경제거 서버: None +[2025-10-23 22:37:35,482] [LogListener] [DEBUG] [loggerModule.py:debug:275] local_rembg_model_path: D:\py\img_worker\modules\rembg_models +[2025-10-23 22:37:35,482] [LogListener] [ERROR] [loggerModule.py:error:293] Request_AI_Server 초기화 실패: 'NoneType' object has no attribute 'rstrip' +[2025-10-23 22:37:35,482] [LogListener] [DEBUG] [loggerModule.py:debug:275] GoogleTranslate 초기화 성공 +[2025-10-23 22:37:35,530] [LogListener] [DEBUG] [loggerModule.py:debug:275] MIGAN CUDA 사용 불가 - CPU 모드로 설정 +[2025-10-23 22:37:35,531] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] GPU 관리자 전달: GPUManager, can_use_cuda: False +[2025-10-23 22:37:35,786] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] GPU 관리자 사용 가능: False +[2025-10-23 22:37:35,786] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 사용 가능한 providers: ['DmlExecutionProvider', 'CPUExecutionProvider'] +[2025-10-23 22:37:35,786] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] DirectML 가속 활성화 +[2025-10-23 22:37:35,786] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 최종 providers: [('DmlExecutionProvider', {}), ('CPUExecutionProvider', {})] +[2025-10-23 22:37:35,787] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] DirectML 가속 시도: [('DmlExecutionProvider', {}), ('CPUExecutionProvider', {})] +[2025-10-23 22:37:35,787] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] DirectML 가속 성공! 실제 providers: ['DmlExecutionProvider', 'CPUExecutionProvider'] +[2025-10-23 22:37:35,787] [ResultListener] [INFO] [loggerModule.py:info:281] 워커 READY 수신 +[2025-10-23 22:37:35,787] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 입력 0: image, 형태: ['batch_size', 3, 'height', 'width'], 타입: tensor(uint8) +[2025-10-23 22:37:35,787] [ResultListener] [INFO] [loggerModule.py:info:281] 워커 READY 수신 +[2025-10-23 22:37:35,787] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 입력 1: mask, 형태: ['batch_size', 1, 'height', 'width'], 타입: tensor(uint8) +[2025-10-23 22:37:35,787] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 출력 0: result, 형태: ['ScatterNDresult_dim_0', 3, 'ScatterNDresult_dim_2', 'ScatterNDresult_dim_3'], 타입: tensor(uint8) +[2025-10-23 22:37:35,788] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 세션 준비 완료. providers=['DmlExecutionProvider', 'CPUExecutionProvider'] +[2025-10-23 22:37:35,788] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] GPU 관리자 연결 완료: GPUManager +[2025-10-23 22:37:35,788] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 파이프라인 gpu_manager 속성: True, 값: +[2025-10-23 22:37:35,788] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 초기화 완료: gpu_manager 속성=True, 값= +[2025-10-23 22:37:35,788] [LogListener] [WARNING] [loggerModule.py:warning:287] OCR 모듈이 초기화되지 않아 Warm-up 건너뜀 +[2025-10-23 22:37:35,788] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔰 ImageProcessor Warm‑up 완료 +[2025-10-23 22:37:35,788] [LogListener] [DEBUG] [loggerModule.py:debug:275] 📡 추가 READY 신호 전송 완료 +[2025-10-23 22:37:35,788] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-23 22:37:35,788] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 19228) +[2025-10-23 22:37:35,788] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔥 작업 수신 성공 +[2025-10-23 22:37:35,788] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🚀 작업 처리 시작: cmd=__PING__, uid=e8ba1087-7921-4a8b-90b2-4855e6a7dc86 +[2025-10-23 22:37:35,788] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 실행 직전: cmd=__PING__ +[2025-10-23 22:37:35,788] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 결과 반환 중: uid=e8ba1087-7921-4a8b-90b2-4855e6a7dc86 +[2025-10-23 22:37:35,788] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 결과 반환 완료: uid=e8ba1087-7921-4a8b-90b2-4855e6a7dc86 +[2025-10-23 22:37:35,788] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-23 22:37:35,789] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 19228) +[2025-10-23 22:38:35,790] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-23 22:38:35,790] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-23 22:38:35,790] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 19228) +[2025-10-23 22:39:35,797] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-23 22:39:35,797] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-23 22:39:35,797] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 19228) +[2025-10-23 22:40:35,806] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-23 22:40:35,806] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-23 22:40:35,806] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 19228) +[2025-10-23 22:41:35,819] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-23 22:41:35,819] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-23 22:41:35,819] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 19228) +[2025-10-23 22:42:35,824] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-23 22:42:35,824] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-23 22:42:35,824] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 19228) +[2025-10-23 22:43:35,830] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-23 22:43:35,830] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-23 22:43:35,830] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 19228) +[2025-10-23 22:44:35,836] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-23 22:44:35,836] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-23 22:44:35,836] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 19228) +[2025-10-23 22:45:35,837] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-23 22:45:35,837] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-23 22:45:35,837] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 19228) +[2025-10-23 22:46:35,842] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-23 22:46:35,842] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-23 22:46:35,842] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 19228) +[2025-10-23 22:47:35,853] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-23 22:47:35,853] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-23 22:47:35,853] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 19228) +[2025-10-23 22:48:35,854] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-23 22:48:35,854] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-23 22:48:35,854] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 19228) +[2025-10-23 22:49:35,865] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-23 22:49:35,865] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-23 22:49:35,865] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 19228) +[2025-10-23 22:50:35,872] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-23 22:50:35,872] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-23 22:50:35,872] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 19228) +[2025-10-23 22:51:35,882] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-23 22:51:35,882] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-23 22:51:35,882] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 19228) +[2025-10-23 22:52:35,892] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-23 22:52:35,892] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-23 22:52:35,892] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 19228) +[2025-10-23 22:53:35,905] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-23 22:53:35,905] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-23 22:53:35,905] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 19228) +[2025-10-23 22:54:35,920] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-23 22:54:35,920] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-23 22:54:35,920] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 19228) +[2025-10-23 22:55:35,930] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-23 22:55:35,930] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-23 22:55:35,930] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 19228) +[2025-10-23 22:56:35,941] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-23 22:56:35,941] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-23 22:56:35,941] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 19228) +[2025-10-23 22:57:35,946] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-23 22:57:35,946] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-23 22:57:35,946] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 19228) +[2025-10-23 22:58:35,957] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-23 22:58:35,957] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-23 22:58:35,957] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 19228) +[2025-10-23 22:59:35,960] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-23 22:59:35,960] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-23 22:59:35,960] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 19228) +[2025-10-23 23:00:35,964] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-23 23:00:35,964] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-23 23:00:35,964] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 19228) +[2025-10-23 23:01:35,972] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-23 23:01:35,972] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-23 23:01:35,972] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 19228) +[2025-10-23 23:02:35,984] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-23 23:02:35,984] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-23 23:02:35,984] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 19228) +[2025-10-23 23:03:35,991] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-23 23:03:35,991] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-23 23:03:35,991] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 19228) +[2025-10-23 23:04:36,002] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-23 23:04:36,002] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-23 23:04:36,002] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 19228) +[2025-10-23 23:05:36,015] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-23 23:05:36,015] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-23 23:05:36,015] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 19228) +[2025-10-23 23:06:36,020] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-23 23:06:36,020] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-23 23:06:36,021] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 19228) +[2025-10-23 23:07:36,021] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-23 23:07:36,021] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-23 23:07:36,021] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 19228) +[2025-10-23 23:08:36,022] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-23 23:08:36,022] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-23 23:08:36,022] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 19228) +[2025-10-23 23:09:36,033] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-23 23:09:36,034] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-23 23:09:36,034] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 19228) +[2025-10-23 23:10:36,047] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-23 23:10:36,047] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-23 23:10:36,047] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 19228) +[2025-10-23 23:11:36,051] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-23 23:11:36,051] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-23 23:11:36,051] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 19228) +[2025-10-23 23:12:36,057] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-23 23:12:36,057] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-23 23:12:36,057] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 19228) +[2025-10-23 23:13:36,066] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-23 23:13:36,066] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-23 23:13:36,066] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 19228) +[2025-10-23 23:14:36,073] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-23 23:14:36,073] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-23 23:14:36,073] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 19228) +[2025-10-23 23:15:36,083] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-23 23:15:36,083] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-23 23:15:36,083] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 19228) +[2025-10-23 23:16:36,096] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-23 23:16:36,096] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-23 23:16:36,096] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 19228) +[2025-10-23 23:17:21,112] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔥 작업 수신 성공 +[2025-10-23 23:17:21,112] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🚀 작업 처리 시작: cmd=process_single_image, uid=ec7bac99-5b3f-447f-8199-442091a19ce7 +[2025-10-23 23:17:21,112] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 프로세서 toggle_states 업데이트 : {'TEMP_IMAGE_DIR': 'C:\\ProgramData\\ImgWorker\\work', 'output_image_format': 'webp', 'use_local_rembg': True, 'local_rembg_model_path': 'D:\\py\\img_worker\\modules\\rembg_models', 'local_model_name': 'birefnet-general-lite', 'optionIMGTrans_type': 'GPU', 'detail_IMGTrans_type': 'GPU', 'thumb_trans_type': 'GPU', 'migan_use_accel': True, 'migan_onnx_path': 'D:\\py\\img_worker\\modules\\migan_onnx\\migan_pipeline_v2.onnx', 'ocr_provider_override': 'auto', 'migan_provider_override': 'auto'} +[2025-10-23 23:17:21,112] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 실행 직전: cmd=process_single_image +[2025-10-23 23:17:21,113] [LogListener] [DEBUG] [loggerModule.py:debug:275] process_single_image 호출 직전 +[2025-10-23 23:17:21,114] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 번역시작 +[2025-10-23 23:17:21,559] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 1 처리 시작: D:\py\img_worker\tests\samples\1.jpg - 전체 번역 모드 +[2025-10-23 23:17:21,559] [LogListener] [DEBUG] [loggerModule.py:debug:275] 로컬 파일 경로 감지, 다운로드 생략: D:\py\img_worker\tests\samples\1.jpg +[2025-10-23 23:17:21,562] [LogListener] [DEBUG] [loggerModule.py:debug:275] 상세페이지 이미지 1 원본 크기: 816x1200 +[2025-10-23 23:17:21,567] [LogListener] [DEBUG] [loggerModule.py:debug:275] 상세페이지 이미지 1 가로 크기 조정: 816x1200 → 860x1264 +[2025-10-23 23:17:21,574] [LogListener] [DEBUG] [loggerModule.py:debug:275] 상세페이지 이미지 1 크기 조정 완료: 860x1264 +[2025-10-23 23:17:21,602] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 1 로컬 저장위치(상세페이지 전처리 완료): D:\py\img_worker\tests\samples\1_resized.jpg +[2025-10-23 23:17:21,621] [LogListener] [WARNING] [loggerModule.py:warning:287] ⚠️ ONNX OCR 모듈이 초기화되지 않았습니다. 재초기화를 시도합니다. +[2025-10-23 23:17:21,637] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔄 OCR 모듈 재초기화 시작 +[2025-10-23 23:17:21,638] [LogListener] [DEBUG] [loggerModule.py:debug:275] 기존 OCR 모듈 참조 해제 완료 +[2025-10-23 23:17:21,638] [LogListener] [DEBUG] [loggerModule.py:debug:275] ONNX 모델 타입 설정값: 자동 선택, GPU 정보: {} +[2025-10-23 23:17:21,638] [LogListener] [DEBUG] [loggerModule.py:debug:275] 자동 선택 모드: GPU 추천 모델 simp 사용 +[2025-10-23 23:17:21,638] [LogListener] [DEBUG] [loggerModule.py:debug:275] ONNX OCR 모델 타입 결정: simp (GPU: False) +[2025-10-23 23:17:21,638] [LogListener] [DEBUG] [loggerModule.py:debug:275] ONNX OCR 모듈 CPU 모드로 설정 +[2025-10-23 23:17:21,638] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🚀 ONNX TextSystem 초기화 시작 (CPU 모드) +[2025-10-23 23:17:21,638] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🚀 ONNX TextSystem 초기화 시작 (CPU 모드) +[2025-10-23 23:17:21,652] [LogListener] [ERROR] [loggerModule.py:error:293] ❌ ONNX TextSystem 초기화 실패: No module named 'pyclipper' +[2025-10-23 23:17:21,667] [LogListener] [WARNING] [loggerModule.py:warning:287] ONNX 초기화 1차 시도 실패: No module named 'pyclipper' +[2025-10-23 23:17:21,690] [LogListener] [ERROR] [loggerModule.py:error:293] ❌ ONNX TextSystem 모든 초기화 시도 실패: 원본 설정 예외: No module named 'pyclipper' +[2025-10-23 23:17:21,690] [LogListener] [ERROR] [loggerModule.py:error:293] ❌ ONNX OCR 모듈 재초기화 중 오류: ONNX TextSystem 모든 초기화 시도 실패: 원본 설정 예외: No module named 'pyclipper' +[2025-10-23 23:17:21,690] [LogListener] [ERROR] [loggerModule.py:error:293] ❌ ONNX OCR 모듈 재초기화 실패, 빈 결과 반환 +[2025-10-23 23:17:21,690] [LogListener] [DEBUG] [loggerModule.py:debug:275] 메모리 변화 [OCR 처리]: 30088.5MB -> 30126.8MB (+38.3MB, +0.1%) - 이미지 1 +[2025-10-23 23:17:21,690] [LogListener] [DEBUG] [loggerModule.py:debug:275] ocr_results: [] +[2025-10-23 23:17:21,690] [LogListener] [DEBUG] [loggerModule.py:debug:275] 필터링 결과: 0/0개 (신뢰도 + & 중국어) +[2025-10-23 23:17:21,691] [LogListener] [DEBUG] [loggerModule.py:debug:275] filter_ocr_results: [] +[2025-10-23 23:17:21,691] [LogListener] [WARNING] [loggerModule.py:warning:287] ⚠️ OCR 모듈이 초기화되지 않아 원본 이미지 반환 +[2025-10-23 23:17:21,691] [LogListener] [DEBUG] [loggerModule.py:debug:275] ⏱ 이미지 파이프라인 총 575.8ms | download=0.0ms | ocr=71.6ms +[2025-10-23 23:17:21,691] [LogListener] [DEBUG] [loggerModule.py:debug:275] process_single_image 호출 완료 +[2025-10-23 23:17:21,691] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 결과 반환 중: uid=ec7bac99-5b3f-447f-8199-442091a19ce7 +[2025-10-23 23:17:21,691] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 결과 반환 완료: uid=ec7bac99-5b3f-447f-8199-442091a19ce7 +[2025-10-23 23:17:21,691] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-23 23:17:21,691] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 19228) +[2025-10-23 23:17:22,123] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔥 작업 수신 성공 +[2025-10-23 23:17:22,123] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🚀 작업 처리 시작: cmd=process_single_image, uid=49edba02-e86a-4d36-99f4-7d52b8a7a181 +[2025-10-23 23:17:22,123] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 프로세서 toggle_states 업데이트 : {'TEMP_IMAGE_DIR': 'C:\\ProgramData\\ImgWorker\\work', 'output_image_format': 'webp', 'use_local_rembg': True, 'local_rembg_model_path': 'D:\\py\\img_worker\\modules\\rembg_models', 'local_model_name': 'birefnet-general-lite', 'optionIMGTrans_type': 'GPU', 'detail_IMGTrans_type': 'GPU', 'thumb_trans_type': 'GPU', 'migan_use_accel': True, 'migan_onnx_path': 'D:\\py\\img_worker\\modules\\migan_onnx\\migan_pipeline_v2.onnx', 'ocr_provider_override': 'auto', 'migan_provider_override': 'auto'} +[2025-10-23 23:17:22,123] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 실행 직전: cmd=process_single_image +[2025-10-23 23:17:22,124] [LogListener] [DEBUG] [loggerModule.py:debug:275] process_single_image 호출 직전 +[2025-10-23 23:17:22,124] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 번역시작 +[2025-10-23 23:17:22,462] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 2 처리 시작: D:\py\img_worker\tests\samples\2.jpg - 전체 번역 모드 +[2025-10-23 23:17:22,462] [LogListener] [DEBUG] [loggerModule.py:debug:275] 로컬 파일 경로 감지, 다운로드 생략: D:\py\img_worker\tests\samples\2.jpg +[2025-10-23 23:17:22,464] [LogListener] [DEBUG] [loggerModule.py:debug:275] 상세페이지 이미지 2 원본 크기: 640x640 +[2025-10-23 23:17:22,465] [LogListener] [DEBUG] [loggerModule.py:debug:275] 상세페이지 이미지 2 가로 크기 조정: 640x640 → 860x860 +[2025-10-23 23:17:22,491] [LogListener] [DEBUG] [loggerModule.py:debug:275] 상세페이지 이미지 2 크기 조정 완료: 860x860 +[2025-10-23 23:17:22,523] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 2 로컬 저장위치(상세페이지 전처리 완료): D:\py\img_worker\tests\samples\2_resized.jpg +[2025-10-23 23:17:22,524] [LogListener] [WARNING] [loggerModule.py:warning:287] ⚠️ ONNX OCR 모듈이 초기화되지 않았습니다. 재초기화를 시도합니다. +[2025-10-23 23:17:22,524] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔄 OCR 모듈 재초기화 시작 +[2025-10-23 23:17:22,524] [LogListener] [DEBUG] [loggerModule.py:debug:275] 기존 OCR 모듈 참조 해제 완료 +[2025-10-23 23:17:22,524] [LogListener] [DEBUG] [loggerModule.py:debug:275] ONNX 모델 타입 설정값: 자동 선택, GPU 정보: {} +[2025-10-23 23:17:22,524] [LogListener] [DEBUG] [loggerModule.py:debug:275] 자동 선택 모드: GPU 추천 모델 simp 사용 +[2025-10-23 23:17:22,524] [LogListener] [DEBUG] [loggerModule.py:debug:275] ONNX OCR 모델 타입 결정: simp (GPU: False) +[2025-10-23 23:17:22,524] [LogListener] [DEBUG] [loggerModule.py:debug:275] ONNX OCR 모듈 CPU 모드로 설정 +[2025-10-23 23:17:22,524] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🚀 ONNX TextSystem 초기화 시작 (CPU 모드) +[2025-10-23 23:17:22,524] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🚀 ONNX TextSystem 초기화 시작 (CPU 모드) +[2025-10-23 23:17:22,552] [LogListener] [ERROR] [loggerModule.py:error:293] ❌ ONNX TextSystem 초기화 실패: No module named 'pyclipper' +[2025-10-23 23:17:22,553] [LogListener] [WARNING] [loggerModule.py:warning:287] ONNX 초기화 1차 시도 실패: No module named 'pyclipper' +[2025-10-23 23:17:22,553] [LogListener] [ERROR] [loggerModule.py:error:293] ❌ ONNX TextSystem 모든 초기화 시도 실패: 원본 설정 예외: No module named 'pyclipper' +[2025-10-23 23:17:22,553] [LogListener] [ERROR] [loggerModule.py:error:293] ❌ ONNX OCR 모듈 재초기화 중 오류: ONNX TextSystem 모든 초기화 시도 실패: 원본 설정 예외: No module named 'pyclipper' +[2025-10-23 23:17:22,553] [LogListener] [ERROR] [loggerModule.py:error:293] ❌ ONNX OCR 모듈 재초기화 실패, 빈 결과 반환 +[2025-10-23 23:17:22,553] [LogListener] [DEBUG] [loggerModule.py:debug:275] 메모리 변화 [OCR 처리]: 30100.0MB -> 30098.0MB (-2.0MB, -0.0%) - 이미지 2 +[2025-10-23 23:17:22,553] [LogListener] [DEBUG] [loggerModule.py:debug:275] ocr_results: [] +[2025-10-23 23:17:22,553] [LogListener] [DEBUG] [loggerModule.py:debug:275] 필터링 결과: 0/0개 (신뢰도 + & 중국어) +[2025-10-23 23:17:22,554] [LogListener] [DEBUG] [loggerModule.py:debug:275] filter_ocr_results: [] +[2025-10-23 23:17:22,554] [ResultListener] [WARNING] [loggerModule.py:warning:287] 워커 롤링 스케줄: reason=job-count-threshold +[2025-10-23 23:17:22,554] [LogListener] [WARNING] [loggerModule.py:warning:287] ⚠️ OCR 모듈이 초기화되지 않아 원본 이미지 반환 +[2025-10-23 23:17:22,554] [LogListener] [DEBUG] [loggerModule.py:debug:275] ⏱ 이미지 파이프라인 총 428.8ms | download=0.0ms | ocr=62.0ms +[2025-10-23 23:17:22,554] [WorkerRoller] [WARNING] [loggerModule.py:warning:287] 워커 롤링 시작: job-count-threshold +[2025-10-23 23:17:22,554] [LogListener] [DEBUG] [loggerModule.py:debug:275] process_single_image 호출 완료 +[2025-10-23 23:17:22,554] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 결과 반환 중: uid=49edba02-e86a-4d36-99f4-7d52b8a7a181 +[2025-10-23 23:17:22,554] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 결과 반환 완료: uid=49edba02-e86a-4d36-99f4-7d52b8a7a181 +[2025-10-23 23:17:22,554] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-23 23:17:22,554] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 19228) +[2025-10-23 23:17:22,571] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔥 작업 수신 성공 +[2025-10-23 23:17:22,571] [LogListener] [DEBUG] [loggerModule.py:debug:275] Shutdown signal 수신 → 종료 +[2025-10-23 23:17:22,571] [LogListener] [DEBUG] [loggerModule.py:debug:275] OCR 모듈 정리 완료 +[2025-10-23 23:17:22,571] [LogListener] [DEBUG] [loggerModule.py:debug:275] 마스크 모듈 정리 완료 +[2025-10-23 23:17:22,573] [LogListener] [DEBUG] [loggerModule.py:debug:275] 임시 폴더 삭제됨: C:\ProgramData\ImgWorker\work +[2025-10-23 23:17:22,574] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 프로세서 소멸 +[2025-10-23 23:17:23,039] [WorkerRoller] [INFO] [loggerModule.py:info:281] 워커 프로세스 기동: PID=14552 +[2025-10-23 23:17:23,476] [LogListener] [DEBUG] [loggerModule.py:debug:275] ImageWorker 프로세스 기동 (PID=14552, Name=ImageWorkerProcess) +[2025-10-23 23:17:23,476] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔧 ImageProcessor3 초기화 시작... +[2025-10-23 23:17:23,476] [LogListener] [DEBUG] [loggerModule.py:debug:275] tracemalloc 메모리 추적 시작 +[2025-10-23 23:17:23,483] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ DirectML 환경 준비 완료 (Windows DirectX 12 기반) +[2025-10-23 23:17:23,483] [LogListener] [DEBUG] [loggerModule.py:debug:275] === 🚀 DirectML GPU 상태 초기화 시작 🚀 === +[2025-10-23 23:17:23,483] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🎯 사용자 GPU 가속 요청: False +[2025-10-23 23:17:23,483] [LogListener] [DEBUG] [loggerModule.py:debug:275] 💻 현재 운영체제: Windows +[2025-10-23 23:17:23,483] [LogListener] [DEBUG] [loggerModule.py:debug:275] GPU 가속이 비활성화됨 (toggle_states['use_cuda'] = False) +[2025-10-23 23:17:23,483] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔒 안전한 CPU 모드로 모든 GPU 설정 강제 비활성화 +[2025-10-23 23:17:23,483] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔧 ImageProcessor3 GPU 상태 요약: +[2025-10-23 23:17:23,483] [LogListener] [DEBUG] [loggerModule.py:debug:275] - CUDA 사용 가능: False +[2025-10-23 23:17:23,484] [LogListener] [DEBUG] [loggerModule.py:debug:275] - toggle_states['use_cuda']: NOT_SET +[2025-10-23 23:17:23,484] [LogListener] [DEBUG] [loggerModule.py:debug:275] - GPU 하드웨어 정보: {} +[2025-10-23 23:17:23,484] [LogListener] [DEBUG] [loggerModule.py:debug:275] ImageProcessor3 Init toggle_states: {'TEMP_IMAGE_DIR': 'C:\\ProgramData\\ImgWorker\\work', 'output_image_format': 'webp', 'use_local_rembg': True, 'local_rembg_model_path': 'D:\\py\\img_worker\\modules\\rembg_models', 'local_model_name': 'birefnet-general-lite', 'optionIMGTrans_type': 'CPU', 'detail_IMGTrans_type': 'CPU', 'thumb_trans_type': 'CPU', 'migan_use_accel': True, 'migan_onnx_path': 'D:\\py\\img_worker\\modules\\migan_onnx\\migan_pipeline_v2.onnx', 'ocr_provider_override': 'auto'} +[2025-10-23 23:17:23,484] [LogListener] [DEBUG] [loggerModule.py:debug:275] is_member_valid: False +[2025-10-23 23:17:23,484] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔄 인페인팅 서버 선택 - 타입: main +[2025-10-23 23:17:23,484] [LogListener] [DEBUG] [loggerModule.py:debug:275] 메인서버: None +[2025-10-23 23:17:23,484] [LogListener] [DEBUG] [loggerModule.py:debug:275] 테스트서버: None +[2025-10-23 23:17:23,484] [LogListener] [ERROR] [loggerModule.py:error:293] ❌ 메인 인페인팅 서버 사용 불가: None +[2025-10-23 23:17:23,484] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔄 배경제거 서버 선택 - 타입: main +[2025-10-23 23:17:23,484] [LogListener] [DEBUG] [loggerModule.py:debug:275] 메인서버: None +[2025-10-23 23:17:23,484] [LogListener] [DEBUG] [loggerModule.py:debug:275] 테스트서버: None +[2025-10-23 23:17:23,484] [LogListener] [ERROR] [loggerModule.py:error:293] ❌ 메인 배경제거 서버 사용 불가: None +[2025-10-23 23:17:23,484] [LogListener] [ERROR] [loggerModule.py:error:293] request_inpainting_server_url 설정되지 않았습니다. +[2025-10-23 23:17:23,485] [LogListener] [DEBUG] [loggerModule.py:debug:275] debug_images 디렉토리 이미 존재: D:\py\img_worker\modules\debug_images +[2025-10-23 23:17:23,485] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.font_path: D:\py\img_worker\modules\HakgyoansimDunggeunmisoTTFB.ttf +[2025-10-23 23:17:23,485] [LogListener] [DEBUG] [loggerModule.py:debug:275] toggle_states font_path: +[2025-10-23 23:17:23,485] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.TEMP_IMAGE_DIR: C:\ProgramData\ImgWorker\work +[2025-10-23 23:17:23,485] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.debugging_save_Dir: D:\py\img_worker\modules\debug_images +[2025-10-23 23:17:23,485] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.unwanted_texts: {} +[2025-10-23 23:17:23,485] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.inpaint_method: cv +[2025-10-23 23:17:23,485] [LogListener] [DEBUG] [loggerModule.py:debug:275] Image.MAX_IMAGE_PIXELS set to 20000000 +[2025-10-23 23:17:23,485] [LogListener] [DEBUG] [loggerModule.py:debug:275] ONNX 모델 타입 설정값: 자동 선택, GPU 정보: {} +[2025-10-23 23:17:23,485] [LogListener] [DEBUG] [loggerModule.py:debug:275] 자동 선택 모드: GPU 추천 모델 simp 사용 +[2025-10-23 23:17:23,485] [LogListener] [DEBUG] [loggerModule.py:debug:275] ONNX OCR 모델 타입 결정: simp (GPU: False) +[2025-10-23 23:17:23,485] [LogListener] [DEBUG] [loggerModule.py:debug:275] ONNX OCR 모듈 CPU 모드로 설정 +[2025-10-23 23:17:23,485] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🚀 ONNX TextSystem 초기화 시작 (CPU 모드) +[2025-10-23 23:17:23,485] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🚀 ONNX TextSystem 초기화 시작 (CPU 모드) +[2025-10-23 23:17:23,497] [LogListener] [ERROR] [loggerModule.py:error:293] ❌ ONNX TextSystem 초기화 실패: No module named 'pyclipper' +[2025-10-23 23:17:23,497] [LogListener] [WARNING] [loggerModule.py:warning:287] ONNX 초기화 1차 시도 실패: No module named 'pyclipper' +[2025-10-23 23:17:23,497] [LogListener] [ERROR] [loggerModule.py:error:293] ❌ ONNX TextSystem 모든 초기화 시도 실패: 원본 설정 예외: No module named 'pyclipper' +[2025-10-23 23:17:23,497] [LogListener] [ERROR] [loggerModule.py:error:293] ❌ ONNX OCR 모듈 초기화 실패: ONNX TextSystem 모든 초기화 시도 실패: 원본 설정 예외: No module named 'pyclipper' +[2025-10-23 23:17:23,497] [LogListener] [DEBUG] [loggerModule.py:debug:275] 마스크 모듈 초기화 완료 +[2025-10-23 23:17:23,497] [LogListener] [DEBUG] [loggerModule.py:debug:275] MaskModule 초기화 성공 +[2025-10-23 23:17:23,497] [LogListener] [DEBUG] [loggerModule.py:debug:275] 텍스트 렌더링 모듈 초기화 완료 +[2025-10-23 23:17:23,497] [LogListener] [DEBUG] [loggerModule.py:debug:275] 기본 폰트: D:\py\img_worker\modules\HakgyoansimDunggeunmisoTTFB.ttf +[2025-10-23 23:17:23,498] [LogListener] [DEBUG] [loggerModule.py:debug:275] TextRenderingModule 초기화 성공 +[2025-10-23 23:17:23,498] [LogListener] [WARNING] [loggerModule.py:warning:287] 커스텀 폰트 로드 실패 (HakgyoansimDunggeunmisoTTFB.ttf): cannot open resource +[2025-10-23 23:17:23,499] [LogListener] [DEBUG] [loggerModule.py:debug:275] 기본 폰트를 사용합니다. +[2025-10-23 23:17:23,499] [LogListener] [DEBUG] [loggerModule.py:debug:275] PostImageManager 초기화 성공 +[2025-10-23 23:17:23,499] [LogListener] [DEBUG] [loggerModule.py:debug:275] 개발환경 +[2025-10-23 23:17:23,499] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔧 개발환경이므로 rembg 로컬 서버 강제 사용: None +[2025-10-23 23:17:23,499] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔧 개발환경이므로 인페인팅 로컬 서버 강제 사용: None +[2025-10-23 23:17:23,499] [LogListener] [DEBUG] [loggerModule.py:debug:275] 📡 최종 선택된 서버 URL: +[2025-10-23 23:17:23,499] [LogListener] [DEBUG] [loggerModule.py:debug:275] 인페인팅 서버: None +[2025-10-23 23:17:23,499] [LogListener] [DEBUG] [loggerModule.py:debug:275] 배경제거 서버: None +[2025-10-23 23:17:23,499] [LogListener] [DEBUG] [loggerModule.py:debug:275] local_rembg_model_path: D:\py\img_worker\modules\rembg_models +[2025-10-23 23:17:23,500] [LogListener] [ERROR] [loggerModule.py:error:293] Request_AI_Server 초기화 실패: 'NoneType' object has no attribute 'rstrip' +[2025-10-23 23:17:23,500] [LogListener] [DEBUG] [loggerModule.py:debug:275] GoogleTranslate 초기화 성공 +[2025-10-23 23:17:23,539] [LogListener] [DEBUG] [loggerModule.py:debug:275] MIGAN CUDA 사용 불가 - CPU 모드로 설정 +[2025-10-23 23:17:23,539] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] GPU 관리자 전달: GPUManager, can_use_cuda: False +[2025-10-23 23:17:23,557] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] GPU 관리자 사용 가능: False +[2025-10-23 23:17:23,777] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 사용 가능한 providers: ['DmlExecutionProvider', 'CPUExecutionProvider'] +[2025-10-23 23:17:23,777] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] DirectML 가속 활성화 +[2025-10-23 23:17:23,777] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 최종 providers: [('DmlExecutionProvider', {}), ('CPUExecutionProvider', {})] +[2025-10-23 23:17:23,777] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] DirectML 가속 시도: [('DmlExecutionProvider', {}), ('CPUExecutionProvider', {})] +[2025-10-23 23:17:23,777] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] DirectML 가속 성공! 실제 providers: ['DmlExecutionProvider', 'CPUExecutionProvider'] +[2025-10-23 23:17:23,777] [ResultListener] [INFO] [loggerModule.py:info:281] 워커 READY 수신 +[2025-10-23 23:17:23,778] [WorkerRoller] [INFO] [loggerModule.py:info:281] 버퍼 작업 재개: 1건 +[2025-10-23 23:17:23,778] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 입력 0: image, 형태: ['batch_size', 3, 'height', 'width'], 타입: tensor(uint8) +[2025-10-23 23:17:23,778] [ResultListener] [INFO] [loggerModule.py:info:281] 워커 READY 수신 +[2025-10-23 23:17:23,778] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 입력 1: mask, 형태: ['batch_size', 1, 'height', 'width'], 타입: tensor(uint8) +[2025-10-23 23:17:23,778] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 출력 0: result, 형태: ['ScatterNDresult_dim_0', 3, 'ScatterNDresult_dim_2', 'ScatterNDresult_dim_3'], 타입: tensor(uint8) +[2025-10-23 23:17:23,778] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 세션 준비 완료. providers=['DmlExecutionProvider', 'CPUExecutionProvider'] +[2025-10-23 23:17:23,778] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] GPU 관리자 연결 완료: GPUManager +[2025-10-23 23:17:23,778] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 파이프라인 gpu_manager 속성: True, 값: +[2025-10-23 23:17:23,779] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 초기화 완료: gpu_manager 속성=True, 값= +[2025-10-23 23:17:23,779] [LogListener] [WARNING] [loggerModule.py:warning:287] OCR 모듈이 초기화되지 않아 Warm-up 건너뜀 +[2025-10-23 23:17:23,779] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔰 ImageProcessor Warm‑up 완료 +[2025-10-23 23:17:23,779] [LogListener] [DEBUG] [loggerModule.py:debug:275] 📡 추가 READY 신호 전송 완료 +[2025-10-23 23:17:23,779] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-23 23:17:23,779] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 14552) +[2025-10-23 23:17:23,779] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔥 작업 수신 성공 +[2025-10-23 23:17:23,779] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🚀 작업 처리 시작: cmd=process_single_image, uid=5c0a94b3-8f5d-48cc-a331-c51d65defc06 +[2025-10-23 23:17:23,779] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 프로세서 toggle_states 업데이트 : {'TEMP_IMAGE_DIR': 'C:\\ProgramData\\ImgWorker\\work', 'output_image_format': 'webp', 'use_local_rembg': True, 'local_rembg_model_path': 'D:\\py\\img_worker\\modules\\rembg_models', 'local_model_name': 'birefnet-general-lite', 'optionIMGTrans_type': 'GPU', 'detail_IMGTrans_type': 'GPU', 'thumb_trans_type': 'GPU', 'migan_use_accel': True, 'migan_onnx_path': 'D:\\py\\img_worker\\modules\\migan_onnx\\migan_pipeline_v2.onnx', 'ocr_provider_override': 'auto', 'migan_provider_override': 'auto'} +[2025-10-23 23:17:23,779] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 실행 직전: cmd=process_single_image +[2025-10-23 23:17:23,779] [LogListener] [DEBUG] [loggerModule.py:debug:275] process_single_image 호출 직전 +[2025-10-23 23:17:23,781] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 번역시작 +[2025-10-23 23:17:24,160] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 3 처리 시작: D:\py\img_worker\tests\samples\5.jpg - 전체 번역 모드 +[2025-10-23 23:17:24,160] [LogListener] [DEBUG] [loggerModule.py:debug:275] 로컬 파일 경로 감지, 다운로드 생략: D:\py\img_worker\tests\samples\5.jpg +[2025-10-23 23:17:24,165] [LogListener] [DEBUG] [loggerModule.py:debug:275] 상세페이지 이미지 3 원본 크기: 1200x1857 +[2025-10-23 23:17:24,173] [LogListener] [DEBUG] [loggerModule.py:debug:275] 상세페이지 이미지 3 가로 크기 조정: 1200x1857 → 860x1330 +[2025-10-23 23:17:24,193] [LogListener] [DEBUG] [loggerModule.py:debug:275] 상세페이지 이미지 3 크기 조정 완료: 860x1330 +[2025-10-23 23:17:24,218] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 3 로컬 저장위치(상세페이지 전처리 완료): D:\py\img_worker\tests\samples\5_resized.jpg +[2025-10-23 23:17:24,241] [LogListener] [WARNING] [loggerModule.py:warning:287] ⚠️ ONNX OCR 모듈이 초기화되지 않았습니다. 재초기화를 시도합니다. +[2025-10-23 23:17:24,259] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔄 OCR 모듈 재초기화 시작 +[2025-10-23 23:17:24,259] [LogListener] [DEBUG] [loggerModule.py:debug:275] 기존 OCR 모듈 참조 해제 완료 +[2025-10-23 23:17:24,259] [LogListener] [DEBUG] [loggerModule.py:debug:275] ONNX 모델 타입 설정값: 자동 선택, GPU 정보: {} +[2025-10-23 23:17:24,259] [LogListener] [DEBUG] [loggerModule.py:debug:275] 자동 선택 모드: GPU 추천 모델 simp 사용 +[2025-10-23 23:17:24,260] [LogListener] [DEBUG] [loggerModule.py:debug:275] ONNX OCR 모델 타입 결정: simp (GPU: False) +[2025-10-23 23:17:24,260] [LogListener] [DEBUG] [loggerModule.py:debug:275] ONNX OCR 모듈 CPU 모드로 설정 +[2025-10-23 23:17:24,260] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🚀 ONNX TextSystem 초기화 시작 (CPU 모드) +[2025-10-23 23:17:24,260] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🚀 ONNX TextSystem 초기화 시작 (CPU 모드) +[2025-10-23 23:17:24,284] [LogListener] [ERROR] [loggerModule.py:error:293] ❌ ONNX TextSystem 초기화 실패: No module named 'pyclipper' +[2025-10-23 23:17:24,314] [LogListener] [WARNING] [loggerModule.py:warning:287] ONNX 초기화 1차 시도 실패: No module named 'pyclipper' +[2025-10-23 23:17:24,314] [LogListener] [ERROR] [loggerModule.py:error:293] ❌ ONNX TextSystem 모든 초기화 시도 실패: 원본 설정 예외: No module named 'pyclipper' +[2025-10-23 23:17:24,314] [LogListener] [ERROR] [loggerModule.py:error:293] ❌ ONNX OCR 모듈 재초기화 중 오류: ONNX TextSystem 모든 초기화 시도 실패: 원본 설정 예외: No module named 'pyclipper' +[2025-10-23 23:17:24,315] [LogListener] [ERROR] [loggerModule.py:error:293] ❌ ONNX OCR 모듈 재초기화 실패, 빈 결과 반환 +[2025-10-23 23:17:24,315] [LogListener] [DEBUG] [loggerModule.py:debug:275] 메모리 변화 [OCR 처리]: 30202.4MB -> 30231.6MB (+29.2MB, +0.1%) - 이미지 3 +[2025-10-23 23:17:24,315] [LogListener] [DEBUG] [loggerModule.py:debug:275] ocr_results: [] +[2025-10-23 23:17:24,315] [LogListener] [DEBUG] [loggerModule.py:debug:275] 필터링 결과: 0/0개 (신뢰도 + & 중국어) +[2025-10-23 23:17:24,315] [LogListener] [DEBUG] [loggerModule.py:debug:275] filter_ocr_results: [] +[2025-10-23 23:17:24,315] [LogListener] [WARNING] [loggerModule.py:warning:287] ⚠️ OCR 모듈이 초기화되지 않아 원본 이미지 반환 +[2025-10-23 23:17:24,315] [LogListener] [DEBUG] [loggerModule.py:debug:275] ⏱ 이미지 파이프라인 총 533.0ms | download=0.0ms | ocr=87.0ms +[2025-10-23 23:17:24,315] [LogListener] [DEBUG] [loggerModule.py:debug:275] process_single_image 호출 완료 +[2025-10-23 23:17:24,316] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 결과 반환 중: uid=5c0a94b3-8f5d-48cc-a331-c51d65defc06 +[2025-10-23 23:17:24,316] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 결과 반환 완료: uid=5c0a94b3-8f5d-48cc-a331-c51d65defc06 +[2025-10-23 23:17:24,316] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-23 23:17:24,316] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 14552) +[2025-10-23 23:17:24,645] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔥 작업 수신 성공 +[2025-10-23 23:17:24,646] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🚀 작업 처리 시작: cmd=process_single_image, uid=9ca0626e-56be-425b-81e1-85611c1ab469 +[2025-10-23 23:17:24,646] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 프로세서 toggle_states 업데이트 : {'TEMP_IMAGE_DIR': 'C:\\ProgramData\\ImgWorker\\work', 'output_image_format': 'webp', 'use_local_rembg': True, 'local_rembg_model_path': 'D:\\py\\img_worker\\modules\\rembg_models', 'local_model_name': 'birefnet-general-lite', 'optionIMGTrans_type': 'GPU', 'detail_IMGTrans_type': 'GPU', 'thumb_trans_type': 'GPU', 'migan_use_accel': False, 'migan_onnx_path': 'D:\\py\\img_worker\\modules\\migan_onnx\\migan_pipeline_v2.onnx', 'ocr_provider_override': 'cpu', 'migan_provider_override': 'cpu'} +[2025-10-23 23:17:24,646] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 실행 직전: cmd=process_single_image +[2025-10-23 23:17:24,646] [LogListener] [DEBUG] [loggerModule.py:debug:275] process_single_image 호출 직전 +[2025-10-23 23:17:24,647] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 번역시작 +[2025-10-23 23:17:25,029] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 1 처리 시작: D:\py\img_worker\tests\samples\1.jpg - 전체 번역 모드 +[2025-10-23 23:17:25,029] [LogListener] [DEBUG] [loggerModule.py:debug:275] 로컬 파일 경로 감지, 다운로드 생략: D:\py\img_worker\tests\samples\1.jpg +[2025-10-23 23:17:25,031] [LogListener] [DEBUG] [loggerModule.py:debug:275] 상세페이지 이미지 1 원본 크기: 816x1200 +[2025-10-23 23:17:25,033] [LogListener] [DEBUG] [loggerModule.py:debug:275] 상세페이지 이미지 1 가로 크기 조정: 816x1200 → 860x1264 +[2025-10-23 23:17:25,060] [LogListener] [DEBUG] [loggerModule.py:debug:275] 상세페이지 이미지 1 크기 조정 완료: 860x1264 +[2025-10-23 23:17:25,089] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 1 로컬 저장위치(상세페이지 전처리 완료): D:\py\img_worker\tests\samples\1_resized.jpg +[2025-10-23 23:17:25,089] [LogListener] [WARNING] [loggerModule.py:warning:287] ⚠️ ONNX OCR 모듈이 초기화되지 않았습니다. 재초기화를 시도합니다. +[2025-10-23 23:17:25,089] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔄 OCR 모듈 재초기화 시작 +[2025-10-23 23:17:25,089] [LogListener] [DEBUG] [loggerModule.py:debug:275] 기존 OCR 모듈 참조 해제 완료 +[2025-10-23 23:17:25,089] [LogListener] [DEBUG] [loggerModule.py:debug:275] ONNX 모델 타입 설정값: 자동 선택, GPU 정보: {} +[2025-10-23 23:17:25,089] [LogListener] [DEBUG] [loggerModule.py:debug:275] 자동 선택 모드: GPU 추천 모델 simp 사용 +[2025-10-23 23:17:25,089] [LogListener] [DEBUG] [loggerModule.py:debug:275] ONNX OCR 모델 타입 결정: simp (GPU: False) +[2025-10-23 23:17:25,089] [LogListener] [DEBUG] [loggerModule.py:debug:275] ONNX OCR 모듈 CPU 모드로 설정 +[2025-10-23 23:17:25,089] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🚀 ONNX TextSystem 초기화 시작 (CPU 모드) +[2025-10-23 23:17:25,090] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🚀 ONNX TextSystem 초기화 시작 (CPU 모드) +[2025-10-23 23:17:25,119] [LogListener] [ERROR] [loggerModule.py:error:293] ❌ ONNX TextSystem 초기화 실패: No module named 'pyclipper' +[2025-10-23 23:17:25,119] [LogListener] [WARNING] [loggerModule.py:warning:287] ONNX 초기화 1차 시도 실패: No module named 'pyclipper' +[2025-10-23 23:17:25,119] [LogListener] [ERROR] [loggerModule.py:error:293] ❌ ONNX TextSystem 모든 초기화 시도 실패: 원본 설정 예외: No module named 'pyclipper' +[2025-10-23 23:17:25,119] [LogListener] [ERROR] [loggerModule.py:error:293] ❌ ONNX OCR 모듈 재초기화 중 오류: ONNX TextSystem 모든 초기화 시도 실패: 원본 설정 예외: No module named 'pyclipper' +[2025-10-23 23:17:25,120] [LogListener] [ERROR] [loggerModule.py:error:293] ❌ ONNX OCR 모듈 재초기화 실패, 빈 결과 반환 +[2025-10-23 23:17:25,120] [LogListener] [DEBUG] [loggerModule.py:debug:275] 메모리 변화 [OCR 처리]: 30228.8MB -> 30227.5MB (-1.2MB, -0.0%) - 이미지 1 +[2025-10-23 23:17:25,120] [ResultListener] [WARNING] [loggerModule.py:warning:287] 워커 롤링 스케줄: reason=job-count-threshold +[2025-10-23 23:17:25,120] [LogListener] [DEBUG] [loggerModule.py:debug:275] ocr_results: [] +[2025-10-23 23:17:25,120] [LogListener] [DEBUG] [loggerModule.py:debug:275] 필터링 결과: 0/0개 (신뢰도 + & 중국어) +[2025-10-23 23:17:25,120] [WorkerRoller] [WARNING] [loggerModule.py:warning:287] 워커 롤링 시작: job-count-threshold +[2025-10-23 23:17:25,120] [LogListener] [DEBUG] [loggerModule.py:debug:275] filter_ocr_results: [] +[2025-10-23 23:17:25,120] [LogListener] [WARNING] [loggerModule.py:warning:287] ⚠️ OCR 모듈이 초기화되지 않아 원본 이미지 반환 +[2025-10-23 23:17:25,120] [LogListener] [DEBUG] [loggerModule.py:debug:275] ⏱ 이미지 파이프라인 총 472.5ms | download=0.0ms | ocr=58.0ms +[2025-10-23 23:17:25,120] [LogListener] [DEBUG] [loggerModule.py:debug:275] process_single_image 호출 완료 +[2025-10-23 23:17:25,120] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 결과 반환 중: uid=9ca0626e-56be-425b-81e1-85611c1ab469 +[2025-10-23 23:17:25,121] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 결과 반환 완료: uid=9ca0626e-56be-425b-81e1-85611c1ab469 +[2025-10-23 23:17:25,121] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-23 23:17:25,121] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 14552) +[2025-10-23 23:17:25,134] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔥 작업 수신 성공 +[2025-10-23 23:17:25,134] [LogListener] [DEBUG] [loggerModule.py:debug:275] Shutdown signal 수신 → 종료 +[2025-10-23 23:17:25,134] [LogListener] [DEBUG] [loggerModule.py:debug:275] OCR 모듈 정리 완료 +[2025-10-23 23:17:25,134] [LogListener] [DEBUG] [loggerModule.py:debug:275] 마스크 모듈 정리 완료 +[2025-10-23 23:17:25,136] [LogListener] [DEBUG] [loggerModule.py:debug:275] 임시 폴더 삭제됨: C:\ProgramData\ImgWorker\work +[2025-10-23 23:17:25,136] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 프로세서 소멸 +[2025-10-23 23:17:25,634] [WorkerRoller] [INFO] [loggerModule.py:info:281] 워커 프로세스 기동: PID=22592 +[2025-10-23 23:17:26,108] [LogListener] [DEBUG] [loggerModule.py:debug:275] ImageWorker 프로세스 기동 (PID=22592, Name=ImageWorkerProcess) +[2025-10-23 23:17:26,108] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔧 ImageProcessor3 초기화 시작... +[2025-10-23 23:17:26,108] [LogListener] [DEBUG] [loggerModule.py:debug:275] tracemalloc 메모리 추적 시작 +[2025-10-23 23:17:26,115] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ DirectML 환경 준비 완료 (Windows DirectX 12 기반) +[2025-10-23 23:17:26,115] [LogListener] [DEBUG] [loggerModule.py:debug:275] === 🚀 DirectML GPU 상태 초기화 시작 🚀 === +[2025-10-23 23:17:26,115] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🎯 사용자 GPU 가속 요청: False +[2025-10-23 23:17:26,115] [LogListener] [DEBUG] [loggerModule.py:debug:275] 💻 현재 운영체제: Windows +[2025-10-23 23:17:26,115] [LogListener] [DEBUG] [loggerModule.py:debug:275] GPU 가속이 비활성화됨 (toggle_states['use_cuda'] = False) +[2025-10-23 23:17:26,115] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔒 안전한 CPU 모드로 모든 GPU 설정 강제 비활성화 +[2025-10-23 23:17:26,116] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔧 ImageProcessor3 GPU 상태 요약: +[2025-10-23 23:17:26,116] [LogListener] [DEBUG] [loggerModule.py:debug:275] - CUDA 사용 가능: False +[2025-10-23 23:17:26,116] [LogListener] [DEBUG] [loggerModule.py:debug:275] - toggle_states['use_cuda']: NOT_SET +[2025-10-23 23:17:26,116] [LogListener] [DEBUG] [loggerModule.py:debug:275] - GPU 하드웨어 정보: {} +[2025-10-23 23:17:26,116] [LogListener] [DEBUG] [loggerModule.py:debug:275] ImageProcessor3 Init toggle_states: {'TEMP_IMAGE_DIR': 'C:\\ProgramData\\ImgWorker\\work', 'output_image_format': 'webp', 'use_local_rembg': True, 'local_rembg_model_path': 'D:\\py\\img_worker\\modules\\rembg_models', 'local_model_name': 'birefnet-general-lite', 'optionIMGTrans_type': 'CPU', 'detail_IMGTrans_type': 'CPU', 'thumb_trans_type': 'CPU', 'migan_use_accel': True, 'migan_onnx_path': 'D:\\py\\img_worker\\modules\\migan_onnx\\migan_pipeline_v2.onnx', 'ocr_provider_override': 'auto'} +[2025-10-23 23:17:26,116] [LogListener] [DEBUG] [loggerModule.py:debug:275] is_member_valid: False +[2025-10-23 23:17:26,116] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔄 인페인팅 서버 선택 - 타입: main +[2025-10-23 23:17:26,116] [LogListener] [DEBUG] [loggerModule.py:debug:275] 메인서버: None +[2025-10-23 23:17:26,116] [LogListener] [DEBUG] [loggerModule.py:debug:275] 테스트서버: None +[2025-10-23 23:17:26,116] [LogListener] [ERROR] [loggerModule.py:error:293] ❌ 메인 인페인팅 서버 사용 불가: None +[2025-10-23 23:17:26,117] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔄 배경제거 서버 선택 - 타입: main +[2025-10-23 23:17:26,117] [LogListener] [DEBUG] [loggerModule.py:debug:275] 메인서버: None +[2025-10-23 23:17:26,117] [LogListener] [DEBUG] [loggerModule.py:debug:275] 테스트서버: None +[2025-10-23 23:17:26,117] [LogListener] [ERROR] [loggerModule.py:error:293] ❌ 메인 배경제거 서버 사용 불가: None +[2025-10-23 23:17:26,117] [LogListener] [ERROR] [loggerModule.py:error:293] request_inpainting_server_url 설정되지 않았습니다. +[2025-10-23 23:17:26,117] [LogListener] [DEBUG] [loggerModule.py:debug:275] debug_images 디렉토리 이미 존재: D:\py\img_worker\modules\debug_images +[2025-10-23 23:17:26,117] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.font_path: D:\py\img_worker\modules\HakgyoansimDunggeunmisoTTFB.ttf +[2025-10-23 23:17:26,117] [LogListener] [DEBUG] [loggerModule.py:debug:275] toggle_states font_path: +[2025-10-23 23:17:26,117] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.TEMP_IMAGE_DIR: C:\ProgramData\ImgWorker\work +[2025-10-23 23:17:26,117] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.debugging_save_Dir: D:\py\img_worker\modules\debug_images +[2025-10-23 23:17:26,117] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.unwanted_texts: {} +[2025-10-23 23:17:26,117] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.inpaint_method: cv +[2025-10-23 23:17:26,117] [LogListener] [DEBUG] [loggerModule.py:debug:275] Image.MAX_IMAGE_PIXELS set to 20000000 +[2025-10-23 23:17:26,117] [LogListener] [DEBUG] [loggerModule.py:debug:275] ONNX 모델 타입 설정값: 자동 선택, GPU 정보: {} +[2025-10-23 23:17:26,117] [LogListener] [DEBUG] [loggerModule.py:debug:275] 자동 선택 모드: GPU 추천 모델 simp 사용 +[2025-10-23 23:17:26,117] [LogListener] [DEBUG] [loggerModule.py:debug:275] ONNX OCR 모델 타입 결정: simp (GPU: False) +[2025-10-23 23:17:26,117] [LogListener] [DEBUG] [loggerModule.py:debug:275] ONNX OCR 모듈 CPU 모드로 설정 +[2025-10-23 23:17:26,118] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🚀 ONNX TextSystem 초기화 시작 (CPU 모드) +[2025-10-23 23:17:26,118] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🚀 ONNX TextSystem 초기화 시작 (CPU 모드) +[2025-10-23 23:17:26,129] [LogListener] [ERROR] [loggerModule.py:error:293] ❌ ONNX TextSystem 초기화 실패: No module named 'pyclipper' +[2025-10-23 23:17:26,129] [LogListener] [WARNING] [loggerModule.py:warning:287] ONNX 초기화 1차 시도 실패: No module named 'pyclipper' +[2025-10-23 23:17:26,129] [LogListener] [ERROR] [loggerModule.py:error:293] ❌ ONNX TextSystem 모든 초기화 시도 실패: 원본 설정 예외: No module named 'pyclipper' +[2025-10-23 23:17:26,129] [LogListener] [ERROR] [loggerModule.py:error:293] ❌ ONNX OCR 모듈 초기화 실패: ONNX TextSystem 모든 초기화 시도 실패: 원본 설정 예외: No module named 'pyclipper' +[2025-10-23 23:17:26,129] [LogListener] [DEBUG] [loggerModule.py:debug:275] 마스크 모듈 초기화 완료 +[2025-10-23 23:17:26,129] [LogListener] [DEBUG] [loggerModule.py:debug:275] MaskModule 초기화 성공 +[2025-10-23 23:17:26,129] [LogListener] [DEBUG] [loggerModule.py:debug:275] 텍스트 렌더링 모듈 초기화 완료 +[2025-10-23 23:17:26,129] [LogListener] [DEBUG] [loggerModule.py:debug:275] 기본 폰트: D:\py\img_worker\modules\HakgyoansimDunggeunmisoTTFB.ttf +[2025-10-23 23:17:26,129] [LogListener] [DEBUG] [loggerModule.py:debug:275] TextRenderingModule 초기화 성공 +[2025-10-23 23:17:26,130] [LogListener] [WARNING] [loggerModule.py:warning:287] 커스텀 폰트 로드 실패 (HakgyoansimDunggeunmisoTTFB.ttf): cannot open resource +[2025-10-23 23:17:26,130] [LogListener] [DEBUG] [loggerModule.py:debug:275] 기본 폰트를 사용합니다. +[2025-10-23 23:17:26,130] [LogListener] [DEBUG] [loggerModule.py:debug:275] PostImageManager 초기화 성공 +[2025-10-23 23:17:26,131] [LogListener] [DEBUG] [loggerModule.py:debug:275] 개발환경 +[2025-10-23 23:17:26,131] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔧 개발환경이므로 rembg 로컬 서버 강제 사용: None +[2025-10-23 23:17:26,131] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔧 개발환경이므로 인페인팅 로컬 서버 강제 사용: None +[2025-10-23 23:17:26,131] [LogListener] [DEBUG] [loggerModule.py:debug:275] 📡 최종 선택된 서버 URL: +[2025-10-23 23:17:26,131] [LogListener] [DEBUG] [loggerModule.py:debug:275] 인페인팅 서버: None +[2025-10-23 23:17:26,131] [LogListener] [DEBUG] [loggerModule.py:debug:275] 배경제거 서버: None +[2025-10-23 23:17:26,131] [LogListener] [DEBUG] [loggerModule.py:debug:275] local_rembg_model_path: D:\py\img_worker\modules\rembg_models +[2025-10-23 23:17:26,131] [LogListener] [ERROR] [loggerModule.py:error:293] Request_AI_Server 초기화 실패: 'NoneType' object has no attribute 'rstrip' +[2025-10-23 23:17:26,132] [LogListener] [DEBUG] [loggerModule.py:debug:275] GoogleTranslate 초기화 성공 +[2025-10-23 23:17:26,166] [LogListener] [DEBUG] [loggerModule.py:debug:275] MIGAN CUDA 사용 불가 - CPU 모드로 설정 +[2025-10-23 23:17:26,166] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] GPU 관리자 전달: GPUManager, can_use_cuda: False +[2025-10-23 23:17:26,414] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] GPU 관리자 사용 가능: False +[2025-10-23 23:17:26,415] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 사용 가능한 providers: ['DmlExecutionProvider', 'CPUExecutionProvider'] +[2025-10-23 23:17:26,415] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] DirectML 가속 활성화 +[2025-10-23 23:17:26,415] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 최종 providers: [('DmlExecutionProvider', {}), ('CPUExecutionProvider', {})] +[2025-10-23 23:17:26,415] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] DirectML 가속 시도: [('DmlExecutionProvider', {}), ('CPUExecutionProvider', {})] +[2025-10-23 23:17:26,415] [ResultListener] [INFO] [loggerModule.py:info:281] 워커 READY 수신 +[2025-10-23 23:17:26,415] [WorkerRoller] [INFO] [loggerModule.py:info:281] 버퍼 작업 재개: 1건 +[2025-10-23 23:17:26,415] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] DirectML 가속 성공! 실제 providers: ['DmlExecutionProvider', 'CPUExecutionProvider'] +[2025-10-23 23:17:26,415] [ResultListener] [INFO] [loggerModule.py:info:281] 워커 READY 수신 +[2025-10-23 23:17:26,416] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 입력 0: image, 형태: ['batch_size', 3, 'height', 'width'], 타입: tensor(uint8) +[2025-10-23 23:17:26,416] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 입력 1: mask, 형태: ['batch_size', 1, 'height', 'width'], 타입: tensor(uint8) +[2025-10-23 23:17:26,416] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 출력 0: result, 형태: ['ScatterNDresult_dim_0', 3, 'ScatterNDresult_dim_2', 'ScatterNDresult_dim_3'], 타입: tensor(uint8) +[2025-10-23 23:17:26,416] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 세션 준비 완료. providers=['DmlExecutionProvider', 'CPUExecutionProvider'] +[2025-10-23 23:17:26,416] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] GPU 관리자 연결 완료: GPUManager +[2025-10-23 23:17:26,416] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 파이프라인 gpu_manager 속성: True, 값: +[2025-10-23 23:17:26,416] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 초기화 완료: gpu_manager 속성=True, 값= +[2025-10-23 23:17:26,416] [LogListener] [WARNING] [loggerModule.py:warning:287] OCR 모듈이 초기화되지 않아 Warm-up 건너뜀 +[2025-10-23 23:17:26,416] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔰 ImageProcessor Warm‑up 완료 +[2025-10-23 23:17:26,417] [LogListener] [DEBUG] [loggerModule.py:debug:275] 📡 추가 READY 신호 전송 완료 +[2025-10-23 23:17:26,417] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-23 23:17:26,417] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 22592) +[2025-10-23 23:17:26,417] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔥 작업 수신 성공 +[2025-10-23 23:17:26,417] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🚀 작업 처리 시작: cmd=process_single_image, uid=4e7da3f1-c602-424b-a5f9-cae355d5a507 +[2025-10-23 23:17:26,417] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 프로세서 toggle_states 업데이트 : {'TEMP_IMAGE_DIR': 'C:\\ProgramData\\ImgWorker\\work', 'output_image_format': 'webp', 'use_local_rembg': True, 'local_rembg_model_path': 'D:\\py\\img_worker\\modules\\rembg_models', 'local_model_name': 'birefnet-general-lite', 'optionIMGTrans_type': 'GPU', 'detail_IMGTrans_type': 'GPU', 'thumb_trans_type': 'GPU', 'migan_use_accel': False, 'migan_onnx_path': 'D:\\py\\img_worker\\modules\\migan_onnx\\migan_pipeline_v2.onnx', 'ocr_provider_override': 'cpu', 'migan_provider_override': 'cpu'} +[2025-10-23 23:17:26,417] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 실행 직전: cmd=process_single_image +[2025-10-23 23:17:26,417] [LogListener] [DEBUG] [loggerModule.py:debug:275] process_single_image 호출 직전 +[2025-10-23 23:17:26,418] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 번역시작 +[2025-10-23 23:17:26,621] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 2 처리 시작: D:\py\img_worker\tests\samples\2.jpg - 전체 번역 모드 +[2025-10-23 23:17:26,621] [LogListener] [DEBUG] [loggerModule.py:debug:275] 로컬 파일 경로 감지, 다운로드 생략: D:\py\img_worker\tests\samples\2.jpg +[2025-10-23 23:17:26,623] [LogListener] [DEBUG] [loggerModule.py:debug:275] 상세페이지 이미지 2 원본 크기: 640x640 +[2025-10-23 23:17:26,627] [LogListener] [DEBUG] [loggerModule.py:debug:275] 상세페이지 이미지 2 가로 크기 조정: 640x640 → 860x860 +[2025-10-23 23:17:26,656] [LogListener] [DEBUG] [loggerModule.py:debug:275] 상세페이지 이미지 2 크기 조정 완료: 860x860 +[2025-10-23 23:17:26,689] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 2 로컬 저장위치(상세페이지 전처리 완료): D:\py\img_worker\tests\samples\2_resized.jpg +[2025-10-23 23:17:26,689] [LogListener] [WARNING] [loggerModule.py:warning:287] ⚠️ ONNX OCR 모듈이 초기화되지 않았습니다. 재초기화를 시도합니다. +[2025-10-23 23:17:26,689] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔄 OCR 모듈 재초기화 시작 +[2025-10-23 23:17:26,689] [LogListener] [DEBUG] [loggerModule.py:debug:275] 기존 OCR 모듈 참조 해제 완료 +[2025-10-23 23:17:26,689] [LogListener] [DEBUG] [loggerModule.py:debug:275] ONNX 모델 타입 설정값: 자동 선택, GPU 정보: {} +[2025-10-23 23:17:26,689] [LogListener] [DEBUG] [loggerModule.py:debug:275] 자동 선택 모드: GPU 추천 모델 simp 사용 +[2025-10-23 23:17:26,690] [LogListener] [DEBUG] [loggerModule.py:debug:275] ONNX OCR 모델 타입 결정: simp (GPU: False) +[2025-10-23 23:17:26,690] [LogListener] [DEBUG] [loggerModule.py:debug:275] ONNX OCR 모듈 CPU 모드로 설정 +[2025-10-23 23:17:26,690] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🚀 ONNX TextSystem 초기화 시작 (CPU 모드) +[2025-10-23 23:17:26,690] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🚀 ONNX TextSystem 초기화 시작 (CPU 모드) +[2025-10-23 23:17:26,698] [LogListener] [ERROR] [loggerModule.py:error:293] ❌ ONNX TextSystem 초기화 실패: No module named 'pyclipper' +[2025-10-23 23:17:26,714] [LogListener] [WARNING] [loggerModule.py:warning:287] ONNX 초기화 1차 시도 실패: No module named 'pyclipper' +[2025-10-23 23:17:26,742] [LogListener] [ERROR] [loggerModule.py:error:293] ❌ ONNX TextSystem 모든 초기화 시도 실패: 원본 설정 예외: No module named 'pyclipper' +[2025-10-23 23:17:26,742] [LogListener] [ERROR] [loggerModule.py:error:293] ❌ ONNX OCR 모듈 재초기화 중 오류: ONNX TextSystem 모든 초기화 시도 실패: 원본 설정 예외: No module named 'pyclipper' +[2025-10-23 23:17:26,742] [LogListener] [ERROR] [loggerModule.py:error:293] ❌ ONNX OCR 모듈 재초기화 실패, 빈 결과 반환 +[2025-10-23 23:17:26,742] [LogListener] [DEBUG] [loggerModule.py:debug:275] 메모리 변화 [OCR 처리]: 30140.6MB -> 30146.0MB (+5.4MB, +0.0%) - 이미지 2 +[2025-10-23 23:17:26,742] [LogListener] [DEBUG] [loggerModule.py:debug:275] ocr_results: [] +[2025-10-23 23:17:26,742] [LogListener] [DEBUG] [loggerModule.py:debug:275] 필터링 결과: 0/0개 (신뢰도 + & 중국어) +[2025-10-23 23:17:26,743] [LogListener] [DEBUG] [loggerModule.py:debug:275] filter_ocr_results: [] +[2025-10-23 23:17:26,743] [LogListener] [WARNING] [loggerModule.py:warning:287] ⚠️ OCR 모듈이 초기화되지 않아 원본 이미지 반환 +[2025-10-23 23:17:26,743] [LogListener] [DEBUG] [loggerModule.py:debug:275] ⏱ 이미지 파이프라인 총 323.6ms | download=0.0ms | ocr=65.0ms +[2025-10-23 23:17:26,743] [LogListener] [DEBUG] [loggerModule.py:debug:275] process_single_image 호출 완료 +[2025-10-23 23:17:26,743] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 결과 반환 중: uid=4e7da3f1-c602-424b-a5f9-cae355d5a507 +[2025-10-23 23:17:26,743] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 결과 반환 완료: uid=4e7da3f1-c602-424b-a5f9-cae355d5a507 +[2025-10-23 23:17:26,743] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-23 23:17:26,743] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 22592) +[2025-10-23 23:17:27,167] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔥 작업 수신 성공 +[2025-10-23 23:17:27,167] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🚀 작업 처리 시작: cmd=process_single_image, uid=8e13a5ee-c53a-4dda-b72a-12cb27f9c807 +[2025-10-23 23:17:27,167] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 프로세서 toggle_states 업데이트 : {'TEMP_IMAGE_DIR': 'C:\\ProgramData\\ImgWorker\\work', 'output_image_format': 'webp', 'use_local_rembg': True, 'local_rembg_model_path': 'D:\\py\\img_worker\\modules\\rembg_models', 'local_model_name': 'birefnet-general-lite', 'optionIMGTrans_type': 'GPU', 'detail_IMGTrans_type': 'GPU', 'thumb_trans_type': 'GPU', 'migan_use_accel': False, 'migan_onnx_path': 'D:\\py\\img_worker\\modules\\migan_onnx\\migan_pipeline_v2.onnx', 'ocr_provider_override': 'cpu', 'migan_provider_override': 'cpu'} +[2025-10-23 23:17:27,167] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 실행 직전: cmd=process_single_image +[2025-10-23 23:17:27,167] [LogListener] [DEBUG] [loggerModule.py:debug:275] process_single_image 호출 직전 +[2025-10-23 23:17:27,168] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 번역시작 +[2025-10-23 23:17:27,579] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 3 처리 시작: D:\py\img_worker\tests\samples\5.jpg - 전체 번역 모드 +[2025-10-23 23:17:27,579] [LogListener] [DEBUG] [loggerModule.py:debug:275] 로컬 파일 경로 감지, 다운로드 생략: D:\py\img_worker\tests\samples\5.jpg +[2025-10-23 23:17:27,584] [LogListener] [DEBUG] [loggerModule.py:debug:275] 상세페이지 이미지 3 원본 크기: 1200x1857 +[2025-10-23 23:17:27,586] [LogListener] [DEBUG] [loggerModule.py:debug:275] 상세페이지 이미지 3 가로 크기 조정: 1200x1857 → 860x1330 +[2025-10-23 23:17:27,612] [LogListener] [DEBUG] [loggerModule.py:debug:275] 상세페이지 이미지 3 크기 조정 완료: 860x1330 +[2025-10-23 23:17:27,626] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 3 로컬 저장위치(상세페이지 전처리 완료): D:\py\img_worker\tests\samples\5_resized.jpg +[2025-10-23 23:17:27,641] [LogListener] [WARNING] [loggerModule.py:warning:287] ⚠️ ONNX OCR 모듈이 초기화되지 않았습니다. 재초기화를 시도합니다. +[2025-10-23 23:17:27,642] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔄 OCR 모듈 재초기화 시작 +[2025-10-23 23:17:27,642] [LogListener] [DEBUG] [loggerModule.py:debug:275] 기존 OCR 모듈 참조 해제 완료 +[2025-10-23 23:17:27,642] [LogListener] [DEBUG] [loggerModule.py:debug:275] ONNX 모델 타입 설정값: 자동 선택, GPU 정보: {} +[2025-10-23 23:17:27,642] [LogListener] [DEBUG] [loggerModule.py:debug:275] 자동 선택 모드: GPU 추천 모델 simp 사용 +[2025-10-23 23:17:27,642] [LogListener] [DEBUG] [loggerModule.py:debug:275] ONNX OCR 모델 타입 결정: simp (GPU: False) +[2025-10-23 23:17:27,642] [LogListener] [DEBUG] [loggerModule.py:debug:275] ONNX OCR 모듈 CPU 모드로 설정 +[2025-10-23 23:17:27,642] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🚀 ONNX TextSystem 초기화 시작 (CPU 모드) +[2025-10-23 23:17:27,642] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🚀 ONNX TextSystem 초기화 시작 (CPU 모드) +[2025-10-23 23:17:27,669] [LogListener] [ERROR] [loggerModule.py:error:293] ❌ ONNX TextSystem 초기화 실패: No module named 'pyclipper' +[2025-10-23 23:17:27,669] [LogListener] [WARNING] [loggerModule.py:warning:287] ONNX 초기화 1차 시도 실패: No module named 'pyclipper' +[2025-10-23 23:17:27,669] [LogListener] [ERROR] [loggerModule.py:error:293] ❌ ONNX TextSystem 모든 초기화 시도 실패: 원본 설정 예외: No module named 'pyclipper' +[2025-10-23 23:17:27,669] [LogListener] [ERROR] [loggerModule.py:error:293] ❌ ONNX OCR 모듈 재초기화 중 오류: ONNX TextSystem 모든 초기화 시도 실패: 원본 설정 예외: No module named 'pyclipper' +[2025-10-23 23:17:27,670] [LogListener] [ERROR] [loggerModule.py:error:293] ❌ ONNX OCR 모듈 재초기화 실패, 빈 결과 반환 +[2025-10-23 23:17:27,670] [LogListener] [DEBUG] [loggerModule.py:debug:275] 메모리 변화 [OCR 처리]: 30224.7MB -> 30224.2MB (-0.5MB, -0.0%) - 이미지 3 +[2025-10-23 23:17:27,670] [LogListener] [DEBUG] [loggerModule.py:debug:275] ocr_results: [] +[2025-10-23 23:17:27,670] [LogListener] [DEBUG] [loggerModule.py:debug:275] 필터링 결과: 0/0개 (신뢰도 + & 중국어) +[2025-10-23 23:17:27,670] [ResultListener] [WARNING] [loggerModule.py:warning:287] 워커 롤링 스케줄: reason=job-count-threshold +[2025-10-23 23:17:27,670] [LogListener] [DEBUG] [loggerModule.py:debug:275] filter_ocr_results: [] +[2025-10-23 23:17:27,670] [LogListener] [WARNING] [loggerModule.py:warning:287] ⚠️ OCR 모듈이 초기화되지 않아 원본 이미지 반환 +[2025-10-23 23:17:27,670] [WorkerRoller] [WARNING] [loggerModule.py:warning:287] 워커 롤링 시작: job-count-threshold +[2025-10-23 23:17:27,670] [LogListener] [DEBUG] [loggerModule.py:debug:275] ⏱ 이미지 파이프라인 총 501.7ms | download=0.0ms | ocr=59.0ms +[2025-10-23 23:17:27,671] [LogListener] [DEBUG] [loggerModule.py:debug:275] process_single_image 호출 완료 +[2025-10-23 23:17:27,671] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 결과 반환 중: uid=8e13a5ee-c53a-4dda-b72a-12cb27f9c807 +[2025-10-23 23:17:27,671] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 결과 반환 완료: uid=8e13a5ee-c53a-4dda-b72a-12cb27f9c807 +[2025-10-23 23:17:27,671] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-23 23:17:27,672] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 22592) +[2025-10-23 23:17:27,689] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔥 작업 수신 성공 +[2025-10-23 23:17:27,689] [LogListener] [DEBUG] [loggerModule.py:debug:275] Shutdown signal 수신 → 종료 +[2025-10-23 23:17:27,690] [LogListener] [DEBUG] [loggerModule.py:debug:275] OCR 모듈 정리 완료 +[2025-10-23 23:17:27,690] [LogListener] [DEBUG] [loggerModule.py:debug:275] 마스크 모듈 정리 완료 +[2025-10-23 23:17:28,020] [LogListener] [DEBUG] [loggerModule.py:debug:275] 임시 폴더 삭제됨: C:\ProgramData\ImgWorker\work +[2025-10-23 23:17:28,020] [LogListener] [DEBUG] [loggerModule.py:debug:275] 이미지 프로세서 소멸 +[2025-10-23 23:17:28,855] [WorkerRoller] [INFO] [loggerModule.py:info:281] 워커 프로세스 기동: PID=24800 +[2025-10-23 23:17:29,306] [LogListener] [DEBUG] [loggerModule.py:debug:275] ImageWorker 프로세스 기동 (PID=24800, Name=ImageWorkerProcess) +[2025-10-23 23:17:29,307] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔧 ImageProcessor3 초기화 시작... +[2025-10-23 23:17:29,307] [LogListener] [DEBUG] [loggerModule.py:debug:275] tracemalloc 메모리 추적 시작 +[2025-10-23 23:17:29,313] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ DirectML 환경 준비 완료 (Windows DirectX 12 기반) +[2025-10-23 23:17:29,313] [LogListener] [DEBUG] [loggerModule.py:debug:275] === 🚀 DirectML GPU 상태 초기화 시작 🚀 === +[2025-10-23 23:17:29,313] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🎯 사용자 GPU 가속 요청: False +[2025-10-23 23:17:29,313] [LogListener] [DEBUG] [loggerModule.py:debug:275] 💻 현재 운영체제: Windows +[2025-10-23 23:17:29,313] [LogListener] [DEBUG] [loggerModule.py:debug:275] GPU 가속이 비활성화됨 (toggle_states['use_cuda'] = False) +[2025-10-23 23:17:29,314] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔒 안전한 CPU 모드로 모든 GPU 설정 강제 비활성화 +[2025-10-23 23:17:29,314] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔧 ImageProcessor3 GPU 상태 요약: +[2025-10-23 23:17:29,314] [LogListener] [DEBUG] [loggerModule.py:debug:275] - CUDA 사용 가능: False +[2025-10-23 23:17:29,314] [LogListener] [DEBUG] [loggerModule.py:debug:275] - toggle_states['use_cuda']: NOT_SET +[2025-10-23 23:17:29,314] [LogListener] [DEBUG] [loggerModule.py:debug:275] - GPU 하드웨어 정보: {} +[2025-10-23 23:17:29,314] [LogListener] [DEBUG] [loggerModule.py:debug:275] ImageProcessor3 Init toggle_states: {'TEMP_IMAGE_DIR': 'C:\\ProgramData\\ImgWorker\\work', 'output_image_format': 'webp', 'use_local_rembg': True, 'local_rembg_model_path': 'D:\\py\\img_worker\\modules\\rembg_models', 'local_model_name': 'birefnet-general-lite', 'optionIMGTrans_type': 'CPU', 'detail_IMGTrans_type': 'CPU', 'thumb_trans_type': 'CPU', 'migan_use_accel': True, 'migan_onnx_path': 'D:\\py\\img_worker\\modules\\migan_onnx\\migan_pipeline_v2.onnx', 'ocr_provider_override': 'auto'} +[2025-10-23 23:17:29,314] [LogListener] [DEBUG] [loggerModule.py:debug:275] is_member_valid: False +[2025-10-23 23:17:29,315] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔄 인페인팅 서버 선택 - 타입: main +[2025-10-23 23:17:29,315] [LogListener] [DEBUG] [loggerModule.py:debug:275] 메인서버: None +[2025-10-23 23:17:29,315] [LogListener] [DEBUG] [loggerModule.py:debug:275] 테스트서버: None +[2025-10-23 23:17:29,315] [LogListener] [ERROR] [loggerModule.py:error:293] ❌ 메인 인페인팅 서버 사용 불가: None +[2025-10-23 23:17:29,315] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔄 배경제거 서버 선택 - 타입: main +[2025-10-23 23:17:29,315] [LogListener] [DEBUG] [loggerModule.py:debug:275] 메인서버: None +[2025-10-23 23:17:29,315] [LogListener] [DEBUG] [loggerModule.py:debug:275] 테스트서버: None +[2025-10-23 23:17:29,315] [LogListener] [ERROR] [loggerModule.py:error:293] ❌ 메인 배경제거 서버 사용 불가: None +[2025-10-23 23:17:29,316] [LogListener] [ERROR] [loggerModule.py:error:293] request_inpainting_server_url 설정되지 않았습니다. +[2025-10-23 23:17:29,316] [LogListener] [DEBUG] [loggerModule.py:debug:275] debug_images 디렉토리 이미 존재: D:\py\img_worker\modules\debug_images +[2025-10-23 23:17:29,316] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.font_path: D:\py\img_worker\modules\HakgyoansimDunggeunmisoTTFB.ttf +[2025-10-23 23:17:29,316] [LogListener] [DEBUG] [loggerModule.py:debug:275] toggle_states font_path: +[2025-10-23 23:17:29,316] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.TEMP_IMAGE_DIR: C:\ProgramData\ImgWorker\work +[2025-10-23 23:17:29,316] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.debugging_save_Dir: D:\py\img_worker\modules\debug_images +[2025-10-23 23:17:29,316] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.unwanted_texts: {} +[2025-10-23 23:17:29,316] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.inpaint_method: cv +[2025-10-23 23:17:29,316] [LogListener] [DEBUG] [loggerModule.py:debug:275] Image.MAX_IMAGE_PIXELS set to 20000000 +[2025-10-23 23:17:29,317] [LogListener] [DEBUG] [loggerModule.py:debug:275] ONNX 모델 타입 설정값: 자동 선택, GPU 정보: {} +[2025-10-23 23:17:29,317] [LogListener] [DEBUG] [loggerModule.py:debug:275] 자동 선택 모드: GPU 추천 모델 simp 사용 +[2025-10-23 23:17:29,317] [LogListener] [DEBUG] [loggerModule.py:debug:275] ONNX OCR 모델 타입 결정: simp (GPU: False) +[2025-10-23 23:17:29,317] [LogListener] [DEBUG] [loggerModule.py:debug:275] ONNX OCR 모듈 CPU 모드로 설정 +[2025-10-23 23:17:29,317] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🚀 ONNX TextSystem 초기화 시작 (CPU 모드) +[2025-10-23 23:17:29,317] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🚀 ONNX TextSystem 초기화 시작 (CPU 모드) +[2025-10-23 23:17:29,327] [LogListener] [ERROR] [loggerModule.py:error:293] ❌ ONNX TextSystem 초기화 실패: No module named 'pyclipper' +[2025-10-23 23:17:29,328] [LogListener] [WARNING] [loggerModule.py:warning:287] ONNX 초기화 1차 시도 실패: No module named 'pyclipper' +[2025-10-23 23:17:29,328] [LogListener] [ERROR] [loggerModule.py:error:293] ❌ ONNX TextSystem 모든 초기화 시도 실패: 원본 설정 예외: No module named 'pyclipper' +[2025-10-23 23:17:29,328] [LogListener] [ERROR] [loggerModule.py:error:293] ❌ ONNX OCR 모듈 초기화 실패: ONNX TextSystem 모든 초기화 시도 실패: 원본 설정 예외: No module named 'pyclipper' +[2025-10-23 23:17:29,328] [LogListener] [DEBUG] [loggerModule.py:debug:275] 마스크 모듈 초기화 완료 +[2025-10-23 23:17:29,328] [LogListener] [DEBUG] [loggerModule.py:debug:275] MaskModule 초기화 성공 +[2025-10-23 23:17:29,328] [LogListener] [DEBUG] [loggerModule.py:debug:275] 텍스트 렌더링 모듈 초기화 완료 +[2025-10-23 23:17:29,328] [LogListener] [DEBUG] [loggerModule.py:debug:275] 기본 폰트: D:\py\img_worker\modules\HakgyoansimDunggeunmisoTTFB.ttf +[2025-10-23 23:17:29,328] [LogListener] [DEBUG] [loggerModule.py:debug:275] TextRenderingModule 초기화 성공 +[2025-10-23 23:17:29,329] [LogListener] [WARNING] [loggerModule.py:warning:287] 커스텀 폰트 로드 실패 (HakgyoansimDunggeunmisoTTFB.ttf): cannot open resource +[2025-10-23 23:17:29,329] [LogListener] [DEBUG] [loggerModule.py:debug:275] 기본 폰트를 사용합니다. +[2025-10-23 23:17:29,329] [LogListener] [DEBUG] [loggerModule.py:debug:275] PostImageManager 초기화 성공 +[2025-10-23 23:17:29,329] [LogListener] [DEBUG] [loggerModule.py:debug:275] 개발환경 +[2025-10-23 23:17:29,329] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔧 개발환경이므로 rembg 로컬 서버 강제 사용: None +[2025-10-23 23:17:29,329] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔧 개발환경이므로 인페인팅 로컬 서버 강제 사용: None +[2025-10-23 23:17:29,330] [LogListener] [DEBUG] [loggerModule.py:debug:275] 📡 최종 선택된 서버 URL: +[2025-10-23 23:17:29,330] [LogListener] [DEBUG] [loggerModule.py:debug:275] 인페인팅 서버: None +[2025-10-23 23:17:29,330] [LogListener] [DEBUG] [loggerModule.py:debug:275] 배경제거 서버: None +[2025-10-23 23:17:29,330] [LogListener] [DEBUG] [loggerModule.py:debug:275] local_rembg_model_path: D:\py\img_worker\modules\rembg_models +[2025-10-23 23:17:29,330] [LogListener] [ERROR] [loggerModule.py:error:293] Request_AI_Server 초기화 실패: 'NoneType' object has no attribute 'rstrip' +[2025-10-23 23:17:29,330] [LogListener] [DEBUG] [loggerModule.py:debug:275] GoogleTranslate 초기화 성공 +[2025-10-23 23:17:29,365] [LogListener] [DEBUG] [loggerModule.py:debug:275] MIGAN CUDA 사용 불가 - CPU 모드로 설정 +[2025-10-23 23:17:29,365] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] GPU 관리자 전달: GPUManager, can_use_cuda: False +[2025-10-23 23:17:29,383] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] GPU 관리자 사용 가능: False +[2025-10-23 23:17:29,603] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 사용 가능한 providers: ['DmlExecutionProvider', 'CPUExecutionProvider'] +[2025-10-23 23:17:29,603] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] DirectML 가속 활성화 +[2025-10-23 23:17:29,603] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 최종 providers: [('DmlExecutionProvider', {}), ('CPUExecutionProvider', {})] +[2025-10-23 23:17:29,604] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] DirectML 가속 시도: [('DmlExecutionProvider', {}), ('CPUExecutionProvider', {})] +[2025-10-23 23:17:29,604] [ResultListener] [INFO] [loggerModule.py:info:281] 워커 READY 수신 +[2025-10-23 23:17:29,604] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] DirectML 가속 성공! 실제 providers: ['DmlExecutionProvider', 'CPUExecutionProvider'] +[2025-10-23 23:17:29,604] [ResultListener] [INFO] [loggerModule.py:info:281] 워커 READY 수신 +[2025-10-23 23:17:29,604] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 입력 0: image, 형태: ['batch_size', 3, 'height', 'width'], 타입: tensor(uint8) +[2025-10-23 23:17:29,604] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 입력 1: mask, 형태: ['batch_size', 1, 'height', 'width'], 타입: tensor(uint8) +[2025-10-23 23:17:29,604] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 출력 0: result, 형태: ['ScatterNDresult_dim_0', 3, 'ScatterNDresult_dim_2', 'ScatterNDresult_dim_3'], 타입: tensor(uint8) +[2025-10-23 23:17:29,604] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 세션 준비 완료. providers=['DmlExecutionProvider', 'CPUExecutionProvider'] +[2025-10-23 23:17:29,605] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] GPU 관리자 연결 완료: GPUManager +[2025-10-23 23:17:29,605] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 파이프라인 gpu_manager 속성: True, 값: +[2025-10-23 23:17:29,605] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 초기화 완료: gpu_manager 속성=True, 값= +[2025-10-23 23:17:29,605] [LogListener] [WARNING] [loggerModule.py:warning:287] OCR 모듈이 초기화되지 않아 Warm-up 건너뜀 +[2025-10-23 23:17:29,605] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔰 ImageProcessor Warm‑up 완료 +[2025-10-23 23:17:29,605] [LogListener] [DEBUG] [loggerModule.py:debug:275] 📡 추가 READY 신호 전송 완료 +[2025-10-23 23:17:29,605] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-23 23:17:29,605] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 24800) +[2025-10-23 23:18:29,604] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-23 23:18:29,604] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-23 23:18:29,604] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 24800) +[2025-10-23 23:19:29,607] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-23 23:19:29,607] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-23 23:19:29,607] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 24800) +[2025-10-23 23:20:29,611] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-23 23:20:29,611] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-23 23:20:29,611] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 24800) +[2025-10-23 23:21:29,625] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-23 23:21:29,625] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-23 23:21:29,625] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 24800) +[2025-10-23 23:22:29,640] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-23 23:22:29,640] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-23 23:22:29,640] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 24800) +[2025-10-23 23:23:29,647] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-23 23:23:29,647] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-23 23:23:29,648] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 24800) +[2025-10-23 23:24:29,655] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-23 23:24:29,655] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-23 23:24:29,655] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 24800) +[2025-10-23 23:25:29,659] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-23 23:25:29,659] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-23 23:25:29,659] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 24800) +[2025-10-23 23:26:29,674] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-23 23:26:29,674] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-23 23:26:29,674] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 24800) +[2025-10-23 23:27:29,679] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-23 23:27:29,679] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-23 23:27:29,679] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 24800) +[2025-10-23 23:28:29,693] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-23 23:28:29,693] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-23 23:28:29,693] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 24800) +[2025-10-23 23:28:45,980] [MainThread] [INFO] [loggerModule.py:info:281] API 서버 종료 +[2025-10-23 23:28:46,320] [MainThread] [INFO] [loggerModule.py:info:281] 워커 프로세스 종료 +[2025-10-23 23:28:48,526] [MainThread] [INFO] [loggerModule.py:info:281] API 서버 시작 +[2025-10-23 23:28:48,532] [MainThread] [INFO] [loggerModule.py:info:281] 워커 프로세스 기동: PID=30656 +[2025-10-23 23:28:48,959] [LogListener] [DEBUG] [loggerModule.py:debug:275] ImageWorker 프로세스 기동 (PID=30656, Name=ImageWorkerProcess) +[2025-10-23 23:28:48,960] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔧 ImageProcessor3 초기화 시작... +[2025-10-23 23:28:48,960] [LogListener] [DEBUG] [loggerModule.py:debug:275] tracemalloc 메모리 추적 시작 +[2025-10-23 23:28:48,966] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ DirectML 환경 준비 완료 (Windows DirectX 12 기반) +[2025-10-23 23:28:48,966] [LogListener] [DEBUG] [loggerModule.py:debug:275] === 🚀 DirectML GPU 상태 초기화 시작 🚀 === +[2025-10-23 23:28:48,967] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🎯 사용자 GPU 가속 요청: False +[2025-10-23 23:28:48,967] [LogListener] [DEBUG] [loggerModule.py:debug:275] 💻 현재 운영체제: Windows +[2025-10-23 23:28:48,967] [LogListener] [DEBUG] [loggerModule.py:debug:275] GPU 가속이 비활성화됨 (toggle_states['use_cuda'] = False) +[2025-10-23 23:28:48,967] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔒 안전한 CPU 모드로 모든 GPU 설정 강제 비활성화 +[2025-10-23 23:28:48,967] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔧 ImageProcessor3 GPU 상태 요약: +[2025-10-23 23:28:48,967] [LogListener] [DEBUG] [loggerModule.py:debug:275] - CUDA 사용 가능: False +[2025-10-23 23:28:48,967] [LogListener] [DEBUG] [loggerModule.py:debug:275] - toggle_states['use_cuda']: NOT_SET +[2025-10-23 23:28:48,968] [LogListener] [DEBUG] [loggerModule.py:debug:275] - GPU 하드웨어 정보: {} +[2025-10-23 23:28:48,968] [LogListener] [DEBUG] [loggerModule.py:debug:275] ImageProcessor3 Init toggle_states: {'TEMP_IMAGE_DIR': 'C:\\ProgramData\\ImgWorker\\work', 'output_image_format': 'webp', 'use_local_rembg': True, 'local_rembg_model_path': 'D:\\py\\img_worker\\modules\\rembg_models', 'local_model_name': 'birefnet-general-lite', 'optionIMGTrans_type': 'CPU', 'detail_IMGTrans_type': 'CPU', 'thumb_trans_type': 'CPU', 'migan_use_accel': True, 'migan_onnx_path': 'D:\\py\\img_worker\\modules\\migan_onnx\\migan_pipeline_v2.onnx', 'ocr_provider_override': 'auto', 'migan_provider_override': 'auto'} +[2025-10-23 23:28:48,968] [LogListener] [DEBUG] [loggerModule.py:debug:275] is_member_valid: False +[2025-10-23 23:28:48,968] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔄 인페인팅 서버 선택 - 타입: main +[2025-10-23 23:28:48,968] [LogListener] [DEBUG] [loggerModule.py:debug:275] 메인서버: None +[2025-10-23 23:28:48,968] [LogListener] [DEBUG] [loggerModule.py:debug:275] 테스트서버: None +[2025-10-23 23:28:48,968] [LogListener] [ERROR] [loggerModule.py:error:293] ❌ 메인 인페인팅 서버 사용 불가: None +[2025-10-23 23:28:48,968] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔄 배경제거 서버 선택 - 타입: main +[2025-10-23 23:28:48,968] [LogListener] [DEBUG] [loggerModule.py:debug:275] 메인서버: None +[2025-10-23 23:28:48,968] [LogListener] [DEBUG] [loggerModule.py:debug:275] 테스트서버: None +[2025-10-23 23:28:48,968] [LogListener] [ERROR] [loggerModule.py:error:293] ❌ 메인 배경제거 서버 사용 불가: None +[2025-10-23 23:28:48,969] [LogListener] [ERROR] [loggerModule.py:error:293] request_inpainting_server_url 설정되지 않았습니다. +[2025-10-23 23:28:48,969] [LogListener] [DEBUG] [loggerModule.py:debug:275] debug_images 디렉토리 이미 존재: D:\py\img_worker\modules\debug_images +[2025-10-23 23:28:48,969] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.font_path: D:\py\img_worker\modules\HakgyoansimDunggeunmisoTTFB.ttf +[2025-10-23 23:28:48,969] [LogListener] [DEBUG] [loggerModule.py:debug:275] toggle_states font_path: +[2025-10-23 23:28:48,969] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.TEMP_IMAGE_DIR: C:\ProgramData\ImgWorker\work +[2025-10-23 23:28:48,969] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.debugging_save_Dir: D:\py\img_worker\modules\debug_images +[2025-10-23 23:28:48,969] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.unwanted_texts: {} +[2025-10-23 23:28:48,969] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.inpaint_method: cv +[2025-10-23 23:28:48,969] [LogListener] [DEBUG] [loggerModule.py:debug:275] Image.MAX_IMAGE_PIXELS set to 20000000 +[2025-10-23 23:28:48,969] [LogListener] [DEBUG] [loggerModule.py:debug:275] ONNX 모델 타입 설정값: 자동 선택, GPU 정보: {} +[2025-10-23 23:28:48,969] [LogListener] [DEBUG] [loggerModule.py:debug:275] 자동 선택 모드: GPU 추천 모델 simp 사용 +[2025-10-23 23:28:48,969] [LogListener] [DEBUG] [loggerModule.py:debug:275] ONNX OCR 모델 타입 결정: simp (GPU: False) +[2025-10-23 23:28:48,969] [LogListener] [DEBUG] [loggerModule.py:debug:275] ONNX OCR 모듈 CPU 모드로 설정 +[2025-10-23 23:28:48,969] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🚀 ONNX TextSystem 초기화 시작 (CPU 모드) +[2025-10-23 23:28:48,969] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🚀 ONNX TextSystem 초기화 시작 (CPU 모드) +[2025-10-23 23:28:48,980] [LogListener] [ERROR] [loggerModule.py:error:293] ❌ ONNX TextSystem 초기화 실패: No module named 'pyclipper' +[2025-10-23 23:28:48,980] [LogListener] [WARNING] [loggerModule.py:warning:287] ONNX 초기화 1차 시도 실패: No module named 'pyclipper' +[2025-10-23 23:28:48,980] [LogListener] [ERROR] [loggerModule.py:error:293] ❌ ONNX TextSystem 모든 초기화 시도 실패: 원본 설정 예외: No module named 'pyclipper' +[2025-10-23 23:28:48,981] [LogListener] [ERROR] [loggerModule.py:error:293] ❌ ONNX OCR 모듈 초기화 실패: ONNX TextSystem 모든 초기화 시도 실패: 원본 설정 예외: No module named 'pyclipper' +[2025-10-23 23:28:48,981] [LogListener] [DEBUG] [loggerModule.py:debug:275] 마스크 모듈 초기화 완료 +[2025-10-23 23:28:48,981] [LogListener] [DEBUG] [loggerModule.py:debug:275] MaskModule 초기화 성공 +[2025-10-23 23:28:48,981] [LogListener] [DEBUG] [loggerModule.py:debug:275] 텍스트 렌더링 모듈 초기화 완료 +[2025-10-23 23:28:48,981] [LogListener] [DEBUG] [loggerModule.py:debug:275] 기본 폰트: D:\py\img_worker\modules\HakgyoansimDunggeunmisoTTFB.ttf +[2025-10-23 23:28:48,981] [LogListener] [DEBUG] [loggerModule.py:debug:275] TextRenderingModule 초기화 성공 +[2025-10-23 23:28:48,982] [LogListener] [WARNING] [loggerModule.py:warning:287] 커스텀 폰트 로드 실패 (HakgyoansimDunggeunmisoTTFB.ttf): cannot open resource +[2025-10-23 23:28:48,982] [LogListener] [DEBUG] [loggerModule.py:debug:275] 기본 폰트를 사용합니다. +[2025-10-23 23:28:48,982] [LogListener] [DEBUG] [loggerModule.py:debug:275] PostImageManager 초기화 성공 +[2025-10-23 23:28:48,982] [LogListener] [DEBUG] [loggerModule.py:debug:275] 개발환경 +[2025-10-23 23:28:48,982] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔧 개발환경이므로 rembg 로컬 서버 강제 사용: None +[2025-10-23 23:28:48,982] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔧 개발환경이므로 인페인팅 로컬 서버 강제 사용: None +[2025-10-23 23:28:48,982] [LogListener] [DEBUG] [loggerModule.py:debug:275] 📡 최종 선택된 서버 URL: +[2025-10-23 23:28:48,982] [LogListener] [DEBUG] [loggerModule.py:debug:275] 인페인팅 서버: None +[2025-10-23 23:28:48,982] [LogListener] [DEBUG] [loggerModule.py:debug:275] 배경제거 서버: None +[2025-10-23 23:28:48,982] [LogListener] [DEBUG] [loggerModule.py:debug:275] local_rembg_model_path: D:\py\img_worker\modules\rembg_models +[2025-10-23 23:28:48,982] [LogListener] [ERROR] [loggerModule.py:error:293] Request_AI_Server 초기화 실패: 'NoneType' object has no attribute 'rstrip' +[2025-10-23 23:28:48,983] [LogListener] [DEBUG] [loggerModule.py:debug:275] GoogleTranslate 초기화 성공 +[2025-10-23 23:28:49,022] [LogListener] [DEBUG] [loggerModule.py:debug:275] MIGAN CUDA 사용 불가 - CPU 모드로 설정 +[2025-10-23 23:28:49,022] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] GPU 관리자 전달: GPUManager, can_use_cuda: False +[2025-10-23 23:28:49,039] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] GPU 관리자 사용 가능: False +[2025-10-23 23:28:49,245] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 사용 가능한 providers: ['DmlExecutionProvider', 'CPUExecutionProvider'] +[2025-10-23 23:28:49,246] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] DirectML 가속 활성화 +[2025-10-23 23:28:49,246] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 최종 providers: [('DmlExecutionProvider', {}), ('CPUExecutionProvider', {})] +[2025-10-23 23:28:49,246] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] DirectML 가속 시도: [('DmlExecutionProvider', {}), ('CPUExecutionProvider', {})] +[2025-10-23 23:28:49,246] [ResultListener] [INFO] [loggerModule.py:info:281] 워커 READY 수신 +[2025-10-23 23:28:49,247] [ResultListener] [INFO] [loggerModule.py:info:281] 워커 READY 수신 +[2025-10-23 23:28:49,247] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] DirectML 가속 성공! 실제 providers: ['DmlExecutionProvider', 'CPUExecutionProvider'] +[2025-10-23 23:28:49,247] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 입력 0: image, 형태: ['batch_size', 3, 'height', 'width'], 타입: tensor(uint8) +[2025-10-23 23:28:49,247] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 입력 1: mask, 형태: ['batch_size', 1, 'height', 'width'], 타입: tensor(uint8) +[2025-10-23 23:28:49,247] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 출력 0: result, 형태: ['ScatterNDresult_dim_0', 3, 'ScatterNDresult_dim_2', 'ScatterNDresult_dim_3'], 타입: tensor(uint8) +[2025-10-23 23:28:49,247] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 세션 준비 완료. providers=['DmlExecutionProvider', 'CPUExecutionProvider'] +[2025-10-23 23:28:49,247] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] GPU 관리자 연결 완료: GPUManager +[2025-10-23 23:28:49,247] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 파이프라인 gpu_manager 속성: True, 값: +[2025-10-23 23:28:49,248] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 초기화 완료: gpu_manager 속성=True, 값= +[2025-10-23 23:28:49,248] [LogListener] [WARNING] [loggerModule.py:warning:287] OCR 모듈이 초기화되지 않아 Warm-up 건너뜀 +[2025-10-23 23:28:49,248] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔰 ImageProcessor Warm‑up 완료 +[2025-10-23 23:28:49,248] [LogListener] [DEBUG] [loggerModule.py:debug:275] 📡 추가 READY 신호 전송 완료 +[2025-10-23 23:28:49,248] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-23 23:28:49,248] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 30656) +[2025-10-23 23:28:49,248] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔥 작업 수신 성공 +[2025-10-23 23:28:49,248] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🚀 작업 처리 시작: cmd=__PING__, uid=fac3939c-9800-47ad-8aaa-02f5a08b5982 +[2025-10-23 23:28:49,248] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 실행 직전: cmd=__PING__ +[2025-10-23 23:28:49,248] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 결과 반환 중: uid=fac3939c-9800-47ad-8aaa-02f5a08b5982 +[2025-10-23 23:28:49,248] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 결과 반환 완료: uid=fac3939c-9800-47ad-8aaa-02f5a08b5982 +[2025-10-23 23:28:49,248] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-23 23:28:49,248] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 30656) +[2025-10-23 23:29:49,247] [LogListener] [DEBUG] [loggerModule.py:debug:275] 30초간 작업 없음 - 계속 대기... +[2025-10-23 23:29:49,247] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-23 23:29:49,247] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 30656) +[2025-10-23 23:30:33,570] [MainThread] [INFO] [loggerModule.py:info:281] API 서버 종료 +[2025-10-23 23:30:33,966] [MainThread] [INFO] [loggerModule.py:info:281] 워커 프로세스 종료 +[2025-10-23 23:31:47,701] [MainThread] [INFO] [loggerModule.py:info:281] API 서버 시작 +[2025-10-23 23:31:47,706] [MainThread] [INFO] [loggerModule.py:info:281] 워커 프로세스 기동: PID=16328 +[2025-10-23 23:31:48,136] [LogListener] [DEBUG] [loggerModule.py:debug:275] ImageWorker 프로세스 기동 (PID=16328, Name=ImageWorkerProcess) +[2025-10-23 23:31:48,137] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔧 ImageProcessor3 초기화 시작... +[2025-10-23 23:31:48,137] [LogListener] [DEBUG] [loggerModule.py:debug:275] tracemalloc 메모리 추적 시작 +[2025-10-23 23:31:48,143] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ DirectML 환경 준비 완료 (Windows DirectX 12 기반) +[2025-10-23 23:31:48,143] [LogListener] [DEBUG] [loggerModule.py:debug:275] === 🚀 DirectML GPU 상태 초기화 시작 🚀 === +[2025-10-23 23:31:48,143] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🎯 사용자 GPU 가속 요청: False +[2025-10-23 23:31:48,143] [LogListener] [DEBUG] [loggerModule.py:debug:275] 💻 현재 운영체제: Windows +[2025-10-23 23:31:48,143] [LogListener] [DEBUG] [loggerModule.py:debug:275] GPU 가속이 비활성화됨 (toggle_states['use_cuda'] = False) +[2025-10-23 23:31:48,143] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔒 안전한 CPU 모드로 모든 GPU 설정 강제 비활성화 +[2025-10-23 23:31:48,144] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔧 ImageProcessor3 GPU 상태 요약: +[2025-10-23 23:31:48,144] [LogListener] [DEBUG] [loggerModule.py:debug:275] - CUDA 사용 가능: False +[2025-10-23 23:31:48,144] [LogListener] [DEBUG] [loggerModule.py:debug:275] - toggle_states['use_cuda']: NOT_SET +[2025-10-23 23:31:48,144] [LogListener] [DEBUG] [loggerModule.py:debug:275] - GPU 하드웨어 정보: {} +[2025-10-23 23:31:48,144] [LogListener] [DEBUG] [loggerModule.py:debug:275] ImageProcessor3 Init toggle_states: {'TEMP_IMAGE_DIR': 'C:\\ProgramData\\ImgWorker\\work', 'output_image_format': 'webp', 'use_local_rembg': True, 'local_rembg_model_path': 'D:\\py\\img_worker\\modules\\rembg_models', 'local_model_name': 'birefnet-general-lite', 'optionIMGTrans_type': 'CPU', 'detail_IMGTrans_type': 'CPU', 'thumb_trans_type': 'CPU', 'migan_use_accel': True, 'migan_onnx_path': 'D:\\py\\img_worker\\modules\\migan_onnx\\migan_pipeline_v2.onnx', 'ocr_provider_override': 'auto', 'migan_provider_override': 'auto'} +[2025-10-23 23:31:48,144] [LogListener] [DEBUG] [loggerModule.py:debug:275] is_member_valid: False +[2025-10-23 23:31:48,144] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔄 인페인팅 서버 선택 - 타입: main +[2025-10-23 23:31:48,144] [LogListener] [DEBUG] [loggerModule.py:debug:275] 메인서버: None +[2025-10-23 23:31:48,144] [LogListener] [DEBUG] [loggerModule.py:debug:275] 테스트서버: None +[2025-10-23 23:31:48,144] [LogListener] [ERROR] [loggerModule.py:error:293] ❌ 메인 인페인팅 서버 사용 불가: None +[2025-10-23 23:31:48,144] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔄 배경제거 서버 선택 - 타입: main +[2025-10-23 23:31:48,145] [LogListener] [DEBUG] [loggerModule.py:debug:275] 메인서버: None +[2025-10-23 23:31:48,145] [LogListener] [DEBUG] [loggerModule.py:debug:275] 테스트서버: None +[2025-10-23 23:31:48,145] [LogListener] [ERROR] [loggerModule.py:error:293] ❌ 메인 배경제거 서버 사용 불가: None +[2025-10-23 23:31:48,145] [LogListener] [ERROR] [loggerModule.py:error:293] request_inpainting_server_url 설정되지 않았습니다. +[2025-10-23 23:31:48,145] [LogListener] [DEBUG] [loggerModule.py:debug:275] debug_images 디렉토리 이미 존재: D:\py\img_worker\modules\debug_images +[2025-10-23 23:31:48,145] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.font_path: D:\py\img_worker\modules\HakgyoansimDunggeunmisoTTFB.ttf +[2025-10-23 23:31:48,145] [LogListener] [DEBUG] [loggerModule.py:debug:275] toggle_states font_path: +[2025-10-23 23:31:48,145] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.TEMP_IMAGE_DIR: C:\ProgramData\ImgWorker\work +[2025-10-23 23:31:48,146] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.debugging_save_Dir: D:\py\img_worker\modules\debug_images +[2025-10-23 23:31:48,146] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.unwanted_texts: {} +[2025-10-23 23:31:48,146] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.inpaint_method: cv +[2025-10-23 23:31:48,146] [LogListener] [DEBUG] [loggerModule.py:debug:275] Image.MAX_IMAGE_PIXELS set to 20000000 +[2025-10-23 23:31:48,146] [LogListener] [DEBUG] [loggerModule.py:debug:275] ONNX 모델 타입 설정값: 자동 선택, GPU 정보: {} +[2025-10-23 23:31:48,146] [LogListener] [DEBUG] [loggerModule.py:debug:275] 자동 선택 모드: GPU 추천 모델 simp 사용 +[2025-10-23 23:31:48,146] [LogListener] [DEBUG] [loggerModule.py:debug:275] ONNX OCR 모델 타입 결정: simp (GPU: False) +[2025-10-23 23:31:48,146] [LogListener] [DEBUG] [loggerModule.py:debug:275] ONNX OCR 모듈 CPU 모드로 설정 +[2025-10-23 23:31:48,146] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🚀 ONNX TextSystem 초기화 시작 (CPU 모드) +[2025-10-23 23:31:48,146] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🚀 ONNX TextSystem 초기화 시작 (CPU 모드) +[2025-10-23 23:31:48,175] [LogListener] [ERROR] [loggerModule.py:error:293] ❌ ONNX TextSystem 초기화 실패: No module named 'skimage' +[2025-10-23 23:31:48,175] [LogListener] [WARNING] [loggerModule.py:warning:287] ONNX 초기화 1차 시도 실패: No module named 'skimage' +[2025-10-23 23:31:48,175] [LogListener] [ERROR] [loggerModule.py:error:293] ❌ ONNX TextSystem 모든 초기화 시도 실패: 원본 설정 예외: No module named 'skimage' +[2025-10-23 23:31:48,175] [LogListener] [ERROR] [loggerModule.py:error:293] ❌ ONNX OCR 모듈 초기화 실패: ONNX TextSystem 모든 초기화 시도 실패: 원본 설정 예외: No module named 'skimage' +[2025-10-23 23:31:48,175] [LogListener] [DEBUG] [loggerModule.py:debug:275] 마스크 모듈 초기화 완료 +[2025-10-23 23:31:48,176] [LogListener] [DEBUG] [loggerModule.py:debug:275] MaskModule 초기화 성공 +[2025-10-23 23:31:48,176] [LogListener] [DEBUG] [loggerModule.py:debug:275] 텍스트 렌더링 모듈 초기화 완료 +[2025-10-23 23:31:48,176] [LogListener] [DEBUG] [loggerModule.py:debug:275] 기본 폰트: D:\py\img_worker\modules\HakgyoansimDunggeunmisoTTFB.ttf +[2025-10-23 23:31:48,176] [LogListener] [DEBUG] [loggerModule.py:debug:275] TextRenderingModule 초기화 성공 +[2025-10-23 23:31:48,177] [LogListener] [WARNING] [loggerModule.py:warning:287] 커스텀 폰트 로드 실패 (HakgyoansimDunggeunmisoTTFB.ttf): cannot open resource +[2025-10-23 23:31:48,177] [LogListener] [DEBUG] [loggerModule.py:debug:275] 기본 폰트를 사용합니다. +[2025-10-23 23:31:48,177] [LogListener] [DEBUG] [loggerModule.py:debug:275] PostImageManager 초기화 성공 +[2025-10-23 23:31:48,177] [LogListener] [DEBUG] [loggerModule.py:debug:275] 개발환경 +[2025-10-23 23:31:48,177] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔧 개발환경이므로 rembg 로컬 서버 강제 사용: None +[2025-10-23 23:31:48,177] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔧 개발환경이므로 인페인팅 로컬 서버 강제 사용: None +[2025-10-23 23:31:48,177] [LogListener] [DEBUG] [loggerModule.py:debug:275] 📡 최종 선택된 서버 URL: +[2025-10-23 23:31:48,177] [LogListener] [DEBUG] [loggerModule.py:debug:275] 인페인팅 서버: None +[2025-10-23 23:31:48,177] [LogListener] [DEBUG] [loggerModule.py:debug:275] 배경제거 서버: None +[2025-10-23 23:31:48,177] [LogListener] [DEBUG] [loggerModule.py:debug:275] local_rembg_model_path: D:\py\img_worker\modules\rembg_models +[2025-10-23 23:31:48,177] [LogListener] [ERROR] [loggerModule.py:error:293] Request_AI_Server 초기화 실패: 'NoneType' object has no attribute 'rstrip' +[2025-10-23 23:31:48,177] [LogListener] [DEBUG] [loggerModule.py:debug:275] GoogleTranslate 초기화 성공 +[2025-10-23 23:31:48,212] [LogListener] [DEBUG] [loggerModule.py:debug:275] MIGAN CUDA 사용 불가 - CPU 모드로 설정 +[2025-10-23 23:31:48,212] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] GPU 관리자 전달: GPUManager, can_use_cuda: False +[2025-10-23 23:31:48,440] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] GPU 관리자 사용 가능: False +[2025-10-23 23:31:48,441] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 사용 가능한 providers: ['DmlExecutionProvider', 'CPUExecutionProvider'] +[2025-10-23 23:31:48,441] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] DirectML 가속 활성화 +[2025-10-23 23:31:48,441] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 최종 providers: [('DmlExecutionProvider', {}), ('CPUExecutionProvider', {})] +[2025-10-23 23:31:48,441] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] DirectML 가속 시도: [('DmlExecutionProvider', {}), ('CPUExecutionProvider', {})] +[2025-10-23 23:31:48,441] [ResultListener] [INFO] [loggerModule.py:info:281] 워커 READY 수신 +[2025-10-23 23:31:48,441] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] DirectML 가속 성공! 실제 providers: ['DmlExecutionProvider', 'CPUExecutionProvider'] +[2025-10-23 23:31:48,441] [ResultListener] [INFO] [loggerModule.py:info:281] 워커 READY 수신 +[2025-10-23 23:31:48,442] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 입력 0: image, 형태: ['batch_size', 3, 'height', 'width'], 타입: tensor(uint8) +[2025-10-23 23:31:48,442] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 입력 1: mask, 형태: ['batch_size', 1, 'height', 'width'], 타입: tensor(uint8) +[2025-10-23 23:31:48,442] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 출력 0: result, 형태: ['ScatterNDresult_dim_0', 3, 'ScatterNDresult_dim_2', 'ScatterNDresult_dim_3'], 타입: tensor(uint8) +[2025-10-23 23:31:48,442] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 세션 준비 완료. providers=['DmlExecutionProvider', 'CPUExecutionProvider'] +[2025-10-23 23:31:48,442] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] GPU 관리자 연결 완료: GPUManager +[2025-10-23 23:31:48,442] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 파이프라인 gpu_manager 속성: True, 값: +[2025-10-23 23:31:48,442] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 초기화 완료: gpu_manager 속성=True, 값= +[2025-10-23 23:31:48,442] [LogListener] [WARNING] [loggerModule.py:warning:287] OCR 모듈이 초기화되지 않아 Warm-up 건너뜀 +[2025-10-23 23:31:48,442] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔰 ImageProcessor Warm‑up 완료 +[2025-10-23 23:31:48,442] [LogListener] [DEBUG] [loggerModule.py:debug:275] 📡 추가 READY 신호 전송 완료 +[2025-10-23 23:31:48,442] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-23 23:31:48,442] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 16328) +[2025-10-23 23:31:48,443] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔥 작업 수신 성공 +[2025-10-23 23:31:48,443] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🚀 작업 처리 시작: cmd=__PING__, uid=968eb158-af65-4bd1-92dc-90d03f18d19e +[2025-10-23 23:31:48,443] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 실행 직전: cmd=__PING__ +[2025-10-23 23:31:48,443] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 결과 반환 중: uid=968eb158-af65-4bd1-92dc-90d03f18d19e +[2025-10-23 23:31:48,443] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 결과 반환 완료: uid=968eb158-af65-4bd1-92dc-90d03f18d19e +[2025-10-23 23:31:48,443] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-23 23:31:48,443] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 16328) +[2025-10-23 23:31:54,616] [MainThread] [INFO] [loggerModule.py:info:281] API 서버 종료 +[2025-10-23 23:31:54,893] [MainThread] [INFO] [loggerModule.py:info:281] 워커 프로세스 종료 +[2025-10-23 23:35:41,101] [MainThread] [INFO] [loggerModule.py:info:281] API 서버 시작 +[2025-10-23 23:35:41,107] [MainThread] [INFO] [loggerModule.py:info:281] 워커 프로세스 기동: PID=24092 +[2025-10-23 23:35:41,625] [LogListener] [DEBUG] [loggerModule.py:debug:275] ImageWorker 프로세스 기동 (PID=24092, Name=ImageWorkerProcess) +[2025-10-23 23:35:41,626] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔧 ImageProcessor3 초기화 시작... +[2025-10-23 23:35:41,626] [LogListener] [DEBUG] [loggerModule.py:debug:275] tracemalloc 메모리 추적 시작 +[2025-10-23 23:35:41,633] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ DirectML 환경 준비 완료 (Windows DirectX 12 기반) +[2025-10-23 23:35:41,633] [LogListener] [DEBUG] [loggerModule.py:debug:275] === 🚀 DirectML GPU 상태 초기화 시작 🚀 === +[2025-10-23 23:35:41,634] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🎯 사용자 GPU 가속 요청: False +[2025-10-23 23:35:41,634] [LogListener] [DEBUG] [loggerModule.py:debug:275] 💻 현재 운영체제: Windows +[2025-10-23 23:35:41,636] [LogListener] [DEBUG] [loggerModule.py:debug:275] GPU 가속이 비활성화됨 (toggle_states['use_cuda'] = False) +[2025-10-23 23:35:41,636] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔒 안전한 CPU 모드로 모든 GPU 설정 강제 비활성화 +[2025-10-23 23:35:41,636] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔧 ImageProcessor3 GPU 상태 요약: +[2025-10-23 23:35:41,636] [LogListener] [DEBUG] [loggerModule.py:debug:275] - CUDA 사용 가능: False +[2025-10-23 23:35:41,636] [LogListener] [DEBUG] [loggerModule.py:debug:275] - toggle_states['use_cuda']: NOT_SET +[2025-10-23 23:35:41,636] [LogListener] [DEBUG] [loggerModule.py:debug:275] - GPU 하드웨어 정보: {} +[2025-10-23 23:35:41,637] [LogListener] [DEBUG] [loggerModule.py:debug:275] ImageProcessor3 Init toggle_states: {'TEMP_IMAGE_DIR': 'C:\\ProgramData\\ImgWorker\\work', 'output_image_format': 'webp', 'use_local_rembg': True, 'local_rembg_model_path': 'D:\\py\\img_worker\\modules\\rembg_models', 'local_model_name': 'birefnet-general-lite', 'optionIMGTrans_type': 'CPU', 'detail_IMGTrans_type': 'CPU', 'thumb_trans_type': 'CPU', 'migan_use_accel': True, 'migan_onnx_path': 'D:\\py\\img_worker\\modules\\migan_onnx\\migan_pipeline_v2.onnx', 'ocr_provider_override': 'auto', 'migan_provider_override': 'auto'} +[2025-10-23 23:35:41,637] [LogListener] [DEBUG] [loggerModule.py:debug:275] is_member_valid: False +[2025-10-23 23:35:41,637] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔄 인페인팅 서버 선택 - 타입: main +[2025-10-23 23:35:41,637] [LogListener] [DEBUG] [loggerModule.py:debug:275] 메인서버: None +[2025-10-23 23:35:41,637] [LogListener] [DEBUG] [loggerModule.py:debug:275] 테스트서버: None +[2025-10-23 23:35:41,637] [LogListener] [ERROR] [loggerModule.py:error:293] ❌ 메인 인페인팅 서버 사용 불가: None +[2025-10-23 23:35:41,637] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔄 배경제거 서버 선택 - 타입: main +[2025-10-23 23:35:41,637] [LogListener] [DEBUG] [loggerModule.py:debug:275] 메인서버: None +[2025-10-23 23:35:41,638] [LogListener] [DEBUG] [loggerModule.py:debug:275] 테스트서버: None +[2025-10-23 23:35:41,638] [LogListener] [ERROR] [loggerModule.py:error:293] ❌ 메인 배경제거 서버 사용 불가: None +[2025-10-23 23:35:41,638] [LogListener] [ERROR] [loggerModule.py:error:293] request_inpainting_server_url 설정되지 않았습니다. +[2025-10-23 23:35:41,638] [LogListener] [DEBUG] [loggerModule.py:debug:275] debug_images 디렉토리 이미 존재: D:\py\img_worker\modules\debug_images +[2025-10-23 23:35:41,638] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.font_path: D:\py\img_worker\modules\HakgyoansimDunggeunmisoTTFB.ttf +[2025-10-23 23:35:41,638] [LogListener] [DEBUG] [loggerModule.py:debug:275] toggle_states font_path: +[2025-10-23 23:35:41,638] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.TEMP_IMAGE_DIR: C:\ProgramData\ImgWorker\work +[2025-10-23 23:35:41,638] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.debugging_save_Dir: D:\py\img_worker\modules\debug_images +[2025-10-23 23:35:41,638] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.unwanted_texts: {} +[2025-10-23 23:35:41,638] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.inpaint_method: cv +[2025-10-23 23:35:41,638] [LogListener] [DEBUG] [loggerModule.py:debug:275] Image.MAX_IMAGE_PIXELS set to 20000000 +[2025-10-23 23:35:41,638] [LogListener] [DEBUG] [loggerModule.py:debug:275] ONNX 모델 타입 설정값: 자동 선택, GPU 정보: {} +[2025-10-23 23:35:41,638] [LogListener] [DEBUG] [loggerModule.py:debug:275] 자동 선택 모드: GPU 추천 모델 simp 사용 +[2025-10-23 23:35:41,639] [LogListener] [DEBUG] [loggerModule.py:debug:275] ONNX OCR 모델 타입 결정: simp (GPU: False) +[2025-10-23 23:35:41,639] [LogListener] [DEBUG] [loggerModule.py:debug:275] ONNX OCR 모듈 CPU 모드로 설정 +[2025-10-23 23:35:41,639] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🚀 ONNX TextSystem 초기화 시작 (CPU 모드) +[2025-10-23 23:35:41,639] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🚀 ONNX TextSystem 초기화 시작 (CPU 모드) +[2025-10-23 23:35:42,383] [LogListener] [ERROR] [loggerModule.py:error:293] ❌ ONNX TextSystem 초기화 실패: No module named 'albumentations' +[2025-10-23 23:35:42,383] [LogListener] [WARNING] [loggerModule.py:warning:287] ONNX 초기화 1차 시도 실패: No module named 'albumentations' +[2025-10-23 23:35:42,384] [LogListener] [ERROR] [loggerModule.py:error:293] ❌ ONNX TextSystem 모든 초기화 시도 실패: 원본 설정 예외: No module named 'albumentations' +[2025-10-23 23:35:42,384] [LogListener] [ERROR] [loggerModule.py:error:293] ❌ ONNX OCR 모듈 초기화 실패: ONNX TextSystem 모든 초기화 시도 실패: 원본 설정 예외: No module named 'albumentations' +[2025-10-23 23:35:42,384] [LogListener] [DEBUG] [loggerModule.py:debug:275] 마스크 모듈 초기화 완료 +[2025-10-23 23:35:42,384] [LogListener] [DEBUG] [loggerModule.py:debug:275] MaskModule 초기화 성공 +[2025-10-23 23:35:42,384] [LogListener] [DEBUG] [loggerModule.py:debug:275] 텍스트 렌더링 모듈 초기화 완료 +[2025-10-23 23:35:42,384] [LogListener] [DEBUG] [loggerModule.py:debug:275] 기본 폰트: D:\py\img_worker\modules\HakgyoansimDunggeunmisoTTFB.ttf +[2025-10-23 23:35:42,384] [LogListener] [DEBUG] [loggerModule.py:debug:275] TextRenderingModule 초기화 성공 +[2025-10-23 23:35:42,385] [LogListener] [WARNING] [loggerModule.py:warning:287] 커스텀 폰트 로드 실패 (HakgyoansimDunggeunmisoTTFB.ttf): cannot open resource +[2025-10-23 23:35:42,385] [LogListener] [DEBUG] [loggerModule.py:debug:275] 기본 폰트를 사용합니다. +[2025-10-23 23:35:42,385] [LogListener] [DEBUG] [loggerModule.py:debug:275] PostImageManager 초기화 성공 +[2025-10-23 23:35:42,385] [LogListener] [DEBUG] [loggerModule.py:debug:275] 개발환경 +[2025-10-23 23:35:42,386] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔧 개발환경이므로 rembg 로컬 서버 강제 사용: None +[2025-10-23 23:35:42,386] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔧 개발환경이므로 인페인팅 로컬 서버 강제 사용: None +[2025-10-23 23:35:42,386] [LogListener] [DEBUG] [loggerModule.py:debug:275] 📡 최종 선택된 서버 URL: +[2025-10-23 23:35:42,386] [LogListener] [DEBUG] [loggerModule.py:debug:275] 인페인팅 서버: None +[2025-10-23 23:35:42,386] [LogListener] [DEBUG] [loggerModule.py:debug:275] 배경제거 서버: None +[2025-10-23 23:35:42,386] [LogListener] [DEBUG] [loggerModule.py:debug:275] local_rembg_model_path: D:\py\img_worker\modules\rembg_models +[2025-10-23 23:35:42,386] [LogListener] [DEBUG] [loggerModule.py:debug:275] Request_AI_Server GPU 상태: CUDA 사용 가능=False +[2025-10-23 23:35:42,386] [LogListener] [DEBUG] [loggerModule.py:debug:275] Request_AI_Server 초기화 성공 +[2025-10-23 23:35:42,386] [LogListener] [DEBUG] [loggerModule.py:debug:275] GoogleTranslate 초기화 성공 +[2025-10-23 23:35:42,424] [LogListener] [DEBUG] [loggerModule.py:debug:275] MIGAN CUDA 사용 불가 - CPU 모드로 설정 +[2025-10-23 23:35:42,424] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] GPU 관리자 전달: GPUManager, can_use_cuda: False +[2025-10-23 23:35:42,443] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] GPU 관리자 사용 가능: False +[2025-10-23 23:35:42,679] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 사용 가능한 providers: ['DmlExecutionProvider', 'CPUExecutionProvider'] +[2025-10-23 23:35:42,680] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] DirectML 가속 활성화 +[2025-10-23 23:35:42,680] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 최종 providers: [('DmlExecutionProvider', {}), ('CPUExecutionProvider', {})] +[2025-10-23 23:35:42,680] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] DirectML 가속 시도: [('DmlExecutionProvider', {}), ('CPUExecutionProvider', {})] +[2025-10-23 23:35:42,680] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] DirectML 가속 성공! 실제 providers: ['DmlExecutionProvider', 'CPUExecutionProvider'] +[2025-10-23 23:35:42,680] [ResultListener] [INFO] [loggerModule.py:info:281] 워커 READY 수신 +[2025-10-23 23:35:42,680] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 입력 0: image, 형태: ['batch_size', 3, 'height', 'width'], 타입: tensor(uint8) +[2025-10-23 23:35:42,681] [ResultListener] [INFO] [loggerModule.py:info:281] 워커 READY 수신 +[2025-10-23 23:35:42,681] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 입력 1: mask, 형태: ['batch_size', 1, 'height', 'width'], 타입: tensor(uint8) +[2025-10-23 23:35:42,681] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 출력 0: result, 형태: ['ScatterNDresult_dim_0', 3, 'ScatterNDresult_dim_2', 'ScatterNDresult_dim_3'], 타입: tensor(uint8) +[2025-10-23 23:35:42,681] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 세션 준비 완료. providers=['DmlExecutionProvider', 'CPUExecutionProvider'] +[2025-10-23 23:35:42,681] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] GPU 관리자 연결 완료: GPUManager +[2025-10-23 23:35:42,681] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 파이프라인 gpu_manager 속성: True, 값: +[2025-10-23 23:35:42,681] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 초기화 완료: gpu_manager 속성=True, 값= +[2025-10-23 23:35:42,682] [LogListener] [WARNING] [loggerModule.py:warning:287] OCR 모듈이 초기화되지 않아 Warm-up 건너뜀 +[2025-10-23 23:35:42,682] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔰 ImageProcessor Warm‑up 완료 +[2025-10-23 23:35:42,682] [LogListener] [DEBUG] [loggerModule.py:debug:275] 📡 추가 READY 신호 전송 완료 +[2025-10-23 23:35:42,682] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-23 23:35:42,682] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 24092) +[2025-10-23 23:35:42,682] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔥 작업 수신 성공 +[2025-10-23 23:35:42,682] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🚀 작업 처리 시작: cmd=__PING__, uid=d8b133d8-09d3-46ac-aeb1-dd6eb74854d5 +[2025-10-23 23:35:42,682] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 실행 직전: cmd=__PING__ +[2025-10-23 23:35:42,682] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 결과 반환 중: uid=d8b133d8-09d3-46ac-aeb1-dd6eb74854d5 +[2025-10-23 23:35:42,682] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 결과 반환 완료: uid=d8b133d8-09d3-46ac-aeb1-dd6eb74854d5 +[2025-10-23 23:35:42,682] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-23 23:35:42,682] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 24092) +[2025-10-23 23:35:44,050] [MainThread] [INFO] [loggerModule.py:info:281] API 서버 종료 +[2025-10-23 23:35:44,457] [MainThread] [INFO] [loggerModule.py:info:281] 워커 프로세스 종료 +[2025-10-23 23:41:32,716] [MainThread] [INFO] [loggerModule.py:info:281] API 서버 시작 +[2025-10-23 23:41:32,721] [MainThread] [INFO] [loggerModule.py:info:281] 워커 프로세스 기동: PID=29760 +[2025-10-23 23:41:33,199] [LogListener] [DEBUG] [loggerModule.py:debug:275] ImageWorker 프로세스 기동 (PID=29760, Name=ImageWorkerProcess) +[2025-10-23 23:41:33,200] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔧 ImageProcessor3 초기화 시작... +[2025-10-23 23:41:33,200] [LogListener] [DEBUG] [loggerModule.py:debug:275] tracemalloc 메모리 추적 시작 +[2025-10-23 23:41:33,206] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ DirectML 환경 준비 완료 (Windows DirectX 12 기반) +[2025-10-23 23:41:33,206] [LogListener] [DEBUG] [loggerModule.py:debug:275] === 🚀 DirectML GPU 상태 초기화 시작 🚀 === +[2025-10-23 23:41:33,206] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🎯 사용자 GPU 가속 요청: False +[2025-10-23 23:41:33,207] [LogListener] [DEBUG] [loggerModule.py:debug:275] 💻 현재 운영체제: Windows +[2025-10-23 23:41:33,207] [LogListener] [DEBUG] [loggerModule.py:debug:275] GPU 가속이 비활성화됨 (toggle_states['use_cuda'] = False) +[2025-10-23 23:41:33,207] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔒 안전한 CPU 모드로 모든 GPU 설정 강제 비활성화 +[2025-10-23 23:41:33,207] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔧 ImageProcessor3 GPU 상태 요약: +[2025-10-23 23:41:33,207] [LogListener] [DEBUG] [loggerModule.py:debug:275] - CUDA 사용 가능: False +[2025-10-23 23:41:33,207] [LogListener] [DEBUG] [loggerModule.py:debug:275] - toggle_states['use_cuda']: NOT_SET +[2025-10-23 23:41:33,207] [LogListener] [DEBUG] [loggerModule.py:debug:275] - GPU 하드웨어 정보: {} +[2025-10-23 23:41:33,207] [LogListener] [DEBUG] [loggerModule.py:debug:275] ImageProcessor3 Init toggle_states: {'TEMP_IMAGE_DIR': 'C:\\ProgramData\\ImgWorker\\work', 'output_image_format': 'webp', 'use_local_rembg': True, 'local_rembg_model_path': 'D:\\py\\img_worker\\modules\\rembg_models', 'local_model_name': 'birefnet-general-lite', 'optionIMGTrans_type': 'CPU', 'detail_IMGTrans_type': 'CPU', 'thumb_trans_type': 'CPU', 'migan_use_accel': True, 'migan_onnx_path': 'D:\\py\\img_worker\\modules\\migan_onnx\\migan_pipeline_v2.onnx', 'ocr_provider_override': 'auto', 'migan_provider_override': 'auto'} +[2025-10-23 23:41:33,208] [LogListener] [DEBUG] [loggerModule.py:debug:275] is_member_valid: False +[2025-10-23 23:41:33,208] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔄 인페인팅 서버 선택 - 타입: main +[2025-10-23 23:41:33,208] [LogListener] [DEBUG] [loggerModule.py:debug:275] 메인서버: None +[2025-10-23 23:41:33,208] [LogListener] [DEBUG] [loggerModule.py:debug:275] 테스트서버: None +[2025-10-23 23:41:33,208] [LogListener] [ERROR] [loggerModule.py:error:293] ❌ 메인 인페인팅 서버 사용 불가: None +[2025-10-23 23:41:33,208] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔄 배경제거 서버 선택 - 타입: main +[2025-10-23 23:41:33,209] [LogListener] [DEBUG] [loggerModule.py:debug:275] 메인서버: None +[2025-10-23 23:41:33,209] [LogListener] [DEBUG] [loggerModule.py:debug:275] 테스트서버: None +[2025-10-23 23:41:33,209] [LogListener] [ERROR] [loggerModule.py:error:293] ❌ 메인 배경제거 서버 사용 불가: None +[2025-10-23 23:41:33,209] [LogListener] [ERROR] [loggerModule.py:error:293] request_inpainting_server_url 설정되지 않았습니다. +[2025-10-23 23:41:33,209] [LogListener] [DEBUG] [loggerModule.py:debug:275] debug_images 디렉토리 이미 존재: D:\py\img_worker\modules\debug_images +[2025-10-23 23:41:33,209] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.font_path: D:\py\img_worker\modules\HakgyoansimDunggeunmisoTTFB.ttf +[2025-10-23 23:41:33,209] [LogListener] [DEBUG] [loggerModule.py:debug:275] toggle_states font_path: +[2025-10-23 23:41:33,209] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.TEMP_IMAGE_DIR: C:\ProgramData\ImgWorker\work +[2025-10-23 23:41:33,209] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.debugging_save_Dir: D:\py\img_worker\modules\debug_images +[2025-10-23 23:41:33,209] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.unwanted_texts: {} +[2025-10-23 23:41:33,210] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.inpaint_method: cv +[2025-10-23 23:41:33,210] [LogListener] [DEBUG] [loggerModule.py:debug:275] Image.MAX_IMAGE_PIXELS set to 20000000 +[2025-10-23 23:41:33,210] [LogListener] [DEBUG] [loggerModule.py:debug:275] ONNX 모델 타입 설정값: 자동 선택, GPU 정보: {} +[2025-10-23 23:41:33,210] [LogListener] [DEBUG] [loggerModule.py:debug:275] 자동 선택 모드: GPU 추천 모델 simp 사용 +[2025-10-23 23:41:33,210] [LogListener] [DEBUG] [loggerModule.py:debug:275] ONNX OCR 모델 타입 결정: simp (GPU: False) +[2025-10-23 23:41:33,210] [LogListener] [DEBUG] [loggerModule.py:debug:275] ONNX OCR 모듈 CPU 모드로 설정 +[2025-10-23 23:41:33,210] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🚀 ONNX TextSystem 초기화 시작 (CPU 모드) +[2025-10-23 23:41:33,210] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🚀 ONNX TextSystem 초기화 시작 (CPU 모드) +[2025-10-23 23:41:34,754] [LogListener] [ERROR] [loggerModule.py:error:293] ❌ ONNX TextSystem 초기화 실패: No module named 'lmdb' +[2025-10-23 23:41:34,754] [LogListener] [WARNING] [loggerModule.py:warning:287] ONNX 초기화 1차 시도 실패: No module named 'lmdb' +[2025-10-23 23:41:34,754] [LogListener] [ERROR] [loggerModule.py:error:293] ❌ ONNX TextSystem 모든 초기화 시도 실패: 원본 설정 예외: No module named 'lmdb' +[2025-10-23 23:41:34,755] [LogListener] [ERROR] [loggerModule.py:error:293] ❌ ONNX OCR 모듈 초기화 실패: ONNX TextSystem 모든 초기화 시도 실패: 원본 설정 예외: No module named 'lmdb' +[2025-10-23 23:41:34,755] [LogListener] [DEBUG] [loggerModule.py:debug:275] 마스크 모듈 초기화 완료 +[2025-10-23 23:41:34,755] [LogListener] [DEBUG] [loggerModule.py:debug:275] MaskModule 초기화 성공 +[2025-10-23 23:41:34,755] [LogListener] [DEBUG] [loggerModule.py:debug:275] 텍스트 렌더링 모듈 초기화 완료 +[2025-10-23 23:41:34,755] [LogListener] [DEBUG] [loggerModule.py:debug:275] 기본 폰트: D:\py\img_worker\modules\HakgyoansimDunggeunmisoTTFB.ttf +[2025-10-23 23:41:34,755] [LogListener] [DEBUG] [loggerModule.py:debug:275] TextRenderingModule 초기화 성공 +[2025-10-23 23:41:34,756] [LogListener] [WARNING] [loggerModule.py:warning:287] 커스텀 폰트 로드 실패 (HakgyoansimDunggeunmisoTTFB.ttf): cannot open resource +[2025-10-23 23:41:34,756] [LogListener] [DEBUG] [loggerModule.py:debug:275] 기본 폰트를 사용합니다. +[2025-10-23 23:41:34,756] [LogListener] [DEBUG] [loggerModule.py:debug:275] PostImageManager 초기화 성공 +[2025-10-23 23:41:34,756] [LogListener] [DEBUG] [loggerModule.py:debug:275] 개발환경 +[2025-10-23 23:41:34,756] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔧 개발환경이므로 rembg 로컬 서버 강제 사용: None +[2025-10-23 23:41:34,757] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔧 개발환경이므로 인페인팅 로컬 서버 강제 사용: None +[2025-10-23 23:41:34,757] [LogListener] [DEBUG] [loggerModule.py:debug:275] 📡 최종 선택된 서버 URL: +[2025-10-23 23:41:34,757] [LogListener] [DEBUG] [loggerModule.py:debug:275] 인페인팅 서버: None +[2025-10-23 23:41:34,757] [LogListener] [DEBUG] [loggerModule.py:debug:275] 배경제거 서버: None +[2025-10-23 23:41:34,757] [LogListener] [DEBUG] [loggerModule.py:debug:275] local_rembg_model_path: D:\py\img_worker\modules\rembg_models +[2025-10-23 23:41:34,758] [LogListener] [DEBUG] [loggerModule.py:debug:275] Request_AI_Server GPU 상태: CUDA 사용 가능=False +[2025-10-23 23:41:34,758] [LogListener] [DEBUG] [loggerModule.py:debug:275] Request_AI_Server 초기화 성공 +[2025-10-23 23:41:34,758] [LogListener] [DEBUG] [loggerModule.py:debug:275] GoogleTranslate 초기화 성공 +[2025-10-23 23:41:34,795] [LogListener] [DEBUG] [loggerModule.py:debug:275] MIGAN CUDA 사용 불가 - CPU 모드로 설정 +[2025-10-23 23:41:34,795] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] GPU 관리자 전달: GPUManager, can_use_cuda: False +[2025-10-23 23:41:34,814] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] GPU 관리자 사용 가능: False +[2025-10-23 23:41:35,039] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 사용 가능한 providers: ['DmlExecutionProvider', 'CPUExecutionProvider'] +[2025-10-23 23:41:35,039] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] DirectML 가속 활성화 +[2025-10-23 23:41:35,039] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 최종 providers: [('DmlExecutionProvider', {}), ('CPUExecutionProvider', {})] +[2025-10-23 23:41:35,039] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] DirectML 가속 시도: [('DmlExecutionProvider', {}), ('CPUExecutionProvider', {})] +[2025-10-23 23:41:35,040] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] DirectML 가속 성공! 실제 providers: ['DmlExecutionProvider', 'CPUExecutionProvider'] +[2025-10-23 23:41:35,040] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 입력 0: image, 형태: ['batch_size', 3, 'height', 'width'], 타입: tensor(uint8) +[2025-10-23 23:41:35,040] [ResultListener] [INFO] [loggerModule.py:info:281] 워커 READY 수신 +[2025-10-23 23:41:35,040] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 입력 1: mask, 형태: ['batch_size', 1, 'height', 'width'], 타입: tensor(uint8) +[2025-10-23 23:41:35,040] [ResultListener] [INFO] [loggerModule.py:info:281] 워커 READY 수신 +[2025-10-23 23:41:35,040] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 출력 0: result, 형태: ['ScatterNDresult_dim_0', 3, 'ScatterNDresult_dim_2', 'ScatterNDresult_dim_3'], 타입: tensor(uint8) +[2025-10-23 23:41:35,040] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 세션 준비 완료. providers=['DmlExecutionProvider', 'CPUExecutionProvider'] +[2025-10-23 23:41:35,040] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] GPU 관리자 연결 완료: GPUManager +[2025-10-23 23:41:35,041] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 파이프라인 gpu_manager 속성: True, 값: +[2025-10-23 23:41:35,041] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 초기화 완료: gpu_manager 속성=True, 값= +[2025-10-23 23:41:35,041] [LogListener] [WARNING] [loggerModule.py:warning:287] OCR 모듈이 초기화되지 않아 Warm-up 건너뜀 +[2025-10-23 23:41:35,041] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔰 ImageProcessor Warm‑up 완료 +[2025-10-23 23:41:35,041] [LogListener] [DEBUG] [loggerModule.py:debug:275] 📡 추가 READY 신호 전송 완료 +[2025-10-23 23:41:35,041] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-23 23:41:35,041] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 29760) +[2025-10-23 23:41:35,041] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔥 작업 수신 성공 +[2025-10-23 23:41:35,041] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🚀 작업 처리 시작: cmd=__PING__, uid=3bf1013b-7e7a-4d19-a8c5-5773887d64eb +[2025-10-23 23:41:35,042] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 실행 직전: cmd=__PING__ +[2025-10-23 23:41:35,042] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 결과 반환 중: uid=3bf1013b-7e7a-4d19-a8c5-5773887d64eb +[2025-10-23 23:41:35,042] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 결과 반환 완료: uid=3bf1013b-7e7a-4d19-a8c5-5773887d64eb +[2025-10-23 23:41:35,042] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-23 23:41:35,042] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 29760) +[2025-10-23 23:41:35,414] [MainThread] [INFO] [loggerModule.py:info:281] API 서버 종료 +[2025-10-23 23:41:35,858] [MainThread] [INFO] [loggerModule.py:info:281] 워커 프로세스 종료 +[2025-10-23 23:42:05,246] [MainThread] [INFO] [loggerModule.py:info:281] API 서버 시작 +[2025-10-23 23:42:05,252] [MainThread] [INFO] [loggerModule.py:info:281] 워커 프로세스 기동: PID=30228 +[2025-10-23 23:42:05,698] [LogListener] [DEBUG] [loggerModule.py:debug:275] ImageWorker 프로세스 기동 (PID=30228, Name=ImageWorkerProcess) +[2025-10-23 23:42:05,698] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔧 ImageProcessor3 초기화 시작... +[2025-10-23 23:42:05,698] [LogListener] [DEBUG] [loggerModule.py:debug:275] tracemalloc 메모리 추적 시작 +[2025-10-23 23:42:05,704] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ DirectML 환경 준비 완료 (Windows DirectX 12 기반) +[2025-10-23 23:42:05,704] [LogListener] [DEBUG] [loggerModule.py:debug:275] === 🚀 DirectML GPU 상태 초기화 시작 🚀 === +[2025-10-23 23:42:05,705] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🎯 사용자 GPU 가속 요청: False +[2025-10-23 23:42:05,705] [LogListener] [DEBUG] [loggerModule.py:debug:275] 💻 현재 운영체제: Windows +[2025-10-23 23:42:05,705] [LogListener] [DEBUG] [loggerModule.py:debug:275] GPU 가속이 비활성화됨 (toggle_states['use_cuda'] = False) +[2025-10-23 23:42:05,705] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔒 안전한 CPU 모드로 모든 GPU 설정 강제 비활성화 +[2025-10-23 23:42:05,705] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔧 ImageProcessor3 GPU 상태 요약: +[2025-10-23 23:42:05,705] [LogListener] [DEBUG] [loggerModule.py:debug:275] - CUDA 사용 가능: False +[2025-10-23 23:42:05,705] [LogListener] [DEBUG] [loggerModule.py:debug:275] - toggle_states['use_cuda']: NOT_SET +[2025-10-23 23:42:05,705] [LogListener] [DEBUG] [loggerModule.py:debug:275] - GPU 하드웨어 정보: {} +[2025-10-23 23:42:05,705] [LogListener] [DEBUG] [loggerModule.py:debug:275] ImageProcessor3 Init toggle_states: {'TEMP_IMAGE_DIR': 'C:\\ProgramData\\ImgWorker\\work', 'output_image_format': 'webp', 'use_local_rembg': True, 'local_rembg_model_path': 'D:\\py\\img_worker\\modules\\rembg_models', 'local_model_name': 'birefnet-general-lite', 'optionIMGTrans_type': 'CPU', 'detail_IMGTrans_type': 'CPU', 'thumb_trans_type': 'CPU', 'migan_use_accel': True, 'migan_onnx_path': 'D:\\py\\img_worker\\modules\\migan_onnx\\migan_pipeline_v2.onnx', 'ocr_provider_override': 'auto', 'migan_provider_override': 'auto'} +[2025-10-23 23:42:05,705] [LogListener] [DEBUG] [loggerModule.py:debug:275] is_member_valid: False +[2025-10-23 23:42:05,706] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔄 인페인팅 서버 선택 - 타입: main +[2025-10-23 23:42:05,706] [LogListener] [DEBUG] [loggerModule.py:debug:275] 메인서버: None +[2025-10-23 23:42:05,706] [LogListener] [DEBUG] [loggerModule.py:debug:275] 테스트서버: None +[2025-10-23 23:42:05,706] [LogListener] [ERROR] [loggerModule.py:error:293] ❌ 메인 인페인팅 서버 사용 불가: None +[2025-10-23 23:42:05,706] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔄 배경제거 서버 선택 - 타입: main +[2025-10-23 23:42:05,706] [LogListener] [DEBUG] [loggerModule.py:debug:275] 메인서버: None +[2025-10-23 23:42:05,706] [LogListener] [DEBUG] [loggerModule.py:debug:275] 테스트서버: None +[2025-10-23 23:42:05,706] [LogListener] [ERROR] [loggerModule.py:error:293] ❌ 메인 배경제거 서버 사용 불가: None +[2025-10-23 23:42:05,706] [LogListener] [ERROR] [loggerModule.py:error:293] request_inpainting_server_url 설정되지 않았습니다. +[2025-10-23 23:42:05,706] [LogListener] [DEBUG] [loggerModule.py:debug:275] debug_images 디렉토리 이미 존재: D:\py\img_worker\modules\debug_images +[2025-10-23 23:42:05,706] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.font_path: D:\py\img_worker\modules\HakgyoansimDunggeunmisoTTFB.ttf +[2025-10-23 23:42:05,706] [LogListener] [DEBUG] [loggerModule.py:debug:275] toggle_states font_path: +[2025-10-23 23:42:05,706] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.TEMP_IMAGE_DIR: C:\ProgramData\ImgWorker\work +[2025-10-23 23:42:05,706] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.debugging_save_Dir: D:\py\img_worker\modules\debug_images +[2025-10-23 23:42:05,706] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.unwanted_texts: {} +[2025-10-23 23:42:05,706] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.inpaint_method: cv +[2025-10-23 23:42:05,707] [LogListener] [DEBUG] [loggerModule.py:debug:275] Image.MAX_IMAGE_PIXELS set to 20000000 +[2025-10-23 23:42:05,707] [LogListener] [DEBUG] [loggerModule.py:debug:275] ONNX 모델 타입 설정값: 자동 선택, GPU 정보: {} +[2025-10-23 23:42:05,707] [LogListener] [DEBUG] [loggerModule.py:debug:275] 자동 선택 모드: GPU 추천 모델 simp 사용 +[2025-10-23 23:42:05,707] [LogListener] [DEBUG] [loggerModule.py:debug:275] ONNX OCR 모델 타입 결정: simp (GPU: False) +[2025-10-23 23:42:05,707] [LogListener] [DEBUG] [loggerModule.py:debug:275] ONNX OCR 모듈 CPU 모드로 설정 +[2025-10-23 23:42:05,707] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🚀 ONNX TextSystem 초기화 시작 (CPU 모드) +[2025-10-23 23:42:05,707] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🚀 ONNX TextSystem 초기화 시작 (CPU 모드) +[2025-10-23 23:42:35,823] [MainThread] [INFO] [loggerModule.py:info:281] API 서버 시작 +[2025-10-23 23:42:35,829] [MainThread] [INFO] [loggerModule.py:info:281] 워커 프로세스 기동: PID=26888 +[2025-10-23 23:42:36,270] [LogListener] [DEBUG] [loggerModule.py:debug:275] ImageWorker 프로세스 기동 (PID=26888, Name=ImageWorkerProcess) +[2025-10-23 23:42:36,270] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔧 ImageProcessor3 초기화 시작... +[2025-10-23 23:42:36,270] [LogListener] [DEBUG] [loggerModule.py:debug:275] tracemalloc 메모리 추적 시작 +[2025-10-23 23:42:36,277] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ DirectML 환경 준비 완료 (Windows DirectX 12 기반) +[2025-10-23 23:42:36,277] [LogListener] [DEBUG] [loggerModule.py:debug:275] === 🚀 DirectML GPU 상태 초기화 시작 🚀 === +[2025-10-23 23:42:36,277] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🎯 사용자 GPU 가속 요청: False +[2025-10-23 23:42:36,277] [LogListener] [DEBUG] [loggerModule.py:debug:275] 💻 현재 운영체제: Windows +[2025-10-23 23:42:36,277] [LogListener] [DEBUG] [loggerModule.py:debug:275] GPU 가속이 비활성화됨 (toggle_states['use_cuda'] = False) +[2025-10-23 23:42:36,278] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔒 안전한 CPU 모드로 모든 GPU 설정 강제 비활성화 +[2025-10-23 23:42:36,278] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔧 ImageProcessor3 GPU 상태 요약: +[2025-10-23 23:42:36,278] [LogListener] [DEBUG] [loggerModule.py:debug:275] - CUDA 사용 가능: False +[2025-10-23 23:42:36,278] [LogListener] [DEBUG] [loggerModule.py:debug:275] - toggle_states['use_cuda']: NOT_SET +[2025-10-23 23:42:36,278] [LogListener] [DEBUG] [loggerModule.py:debug:275] - GPU 하드웨어 정보: {} +[2025-10-23 23:42:36,278] [LogListener] [DEBUG] [loggerModule.py:debug:275] ImageProcessor3 Init toggle_states: {'TEMP_IMAGE_DIR': 'C:\\ProgramData\\ImgWorker\\work', 'output_image_format': 'webp', 'use_local_rembg': True, 'local_rembg_model_path': 'D:\\py\\img_worker\\modules\\rembg_models', 'local_model_name': 'birefnet-general-lite', 'optionIMGTrans_type': 'CPU', 'detail_IMGTrans_type': 'CPU', 'thumb_trans_type': 'CPU', 'migan_use_accel': True, 'migan_onnx_path': 'D:\\py\\img_worker\\modules\\migan_onnx\\migan_pipeline_v2.onnx', 'ocr_provider_override': 'auto', 'migan_provider_override': 'auto'} +[2025-10-23 23:42:36,278] [LogListener] [DEBUG] [loggerModule.py:debug:275] is_member_valid: False +[2025-10-23 23:42:36,278] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔄 인페인팅 서버 선택 - 타입: main +[2025-10-23 23:42:36,279] [LogListener] [DEBUG] [loggerModule.py:debug:275] 메인서버: None +[2025-10-23 23:42:36,279] [LogListener] [DEBUG] [loggerModule.py:debug:275] 테스트서버: None +[2025-10-23 23:42:36,279] [LogListener] [ERROR] [loggerModule.py:error:293] ❌ 메인 인페인팅 서버 사용 불가: None +[2025-10-23 23:42:36,279] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔄 배경제거 서버 선택 - 타입: main +[2025-10-23 23:42:36,279] [LogListener] [DEBUG] [loggerModule.py:debug:275] 메인서버: None +[2025-10-23 23:42:36,279] [LogListener] [DEBUG] [loggerModule.py:debug:275] 테스트서버: None +[2025-10-23 23:42:36,279] [LogListener] [ERROR] [loggerModule.py:error:293] ❌ 메인 배경제거 서버 사용 불가: None +[2025-10-23 23:42:36,279] [LogListener] [ERROR] [loggerModule.py:error:293] request_inpainting_server_url 설정되지 않았습니다. +[2025-10-23 23:42:36,279] [LogListener] [DEBUG] [loggerModule.py:debug:275] debug_images 디렉토리 이미 존재: D:\py\img_worker\modules\debug_images +[2025-10-23 23:42:36,279] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.font_path: D:\py\img_worker\modules\HakgyoansimDunggeunmisoTTFB.ttf +[2025-10-23 23:42:36,280] [LogListener] [DEBUG] [loggerModule.py:debug:275] toggle_states font_path: +[2025-10-23 23:42:36,280] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.TEMP_IMAGE_DIR: C:\ProgramData\ImgWorker\work +[2025-10-23 23:42:36,280] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.debugging_save_Dir: D:\py\img_worker\modules\debug_images +[2025-10-23 23:42:36,280] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.unwanted_texts: {} +[2025-10-23 23:42:36,280] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.inpaint_method: cv +[2025-10-23 23:42:36,280] [LogListener] [DEBUG] [loggerModule.py:debug:275] Image.MAX_IMAGE_PIXELS set to 20000000 +[2025-10-23 23:42:36,280] [LogListener] [DEBUG] [loggerModule.py:debug:275] ONNX 모델 타입 설정값: 자동 선택, GPU 정보: {} +[2025-10-23 23:42:36,280] [LogListener] [DEBUG] [loggerModule.py:debug:275] 자동 선택 모드: GPU 추천 모델 simp 사용 +[2025-10-23 23:42:36,280] [LogListener] [DEBUG] [loggerModule.py:debug:275] ONNX OCR 모델 타입 결정: simp (GPU: False) +[2025-10-23 23:42:36,280] [LogListener] [DEBUG] [loggerModule.py:debug:275] ONNX OCR 모듈 CPU 모드로 설정 +[2025-10-23 23:42:36,281] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🚀 ONNX TextSystem 초기화 시작 (CPU 모드) +[2025-10-23 23:42:36,281] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🚀 ONNX TextSystem 초기화 시작 (CPU 모드) +[2025-10-23 23:42:37,752] [LogListener] [ERROR] [loggerModule.py:error:293] ❌ ONNX TextSystem 초기화 실패: ONNX 모델 파일을 찾을 수 없습니다. 확인 경로: D:\py\img_worker\modules\modules\onnx_ocr_module\models +[2025-10-23 23:42:37,752] [LogListener] [WARNING] [loggerModule.py:warning:287] ONNX 초기화 1차 시도 실패: ONNX 모델 파일을 찾을 수 없습니다. 확인 경로: D:\py\img_worker\modules\modules\onnx_ocr_module\models +[2025-10-23 23:42:37,752] [LogListener] [ERROR] [loggerModule.py:error:293] ❌ ONNX TextSystem 모든 초기화 시도 실패: 원본 설정 예외: ONNX 모델 파일을 찾을 수 없습니다. 확인 경로: D:\py\img_worker\modules\modules\onnx_ocr_module\models +[2025-10-23 23:42:37,752] [LogListener] [ERROR] [loggerModule.py:error:293] ❌ ONNX OCR 모듈 초기화 실패: ONNX TextSystem 모든 초기화 시도 실패: 원본 설정 예외: ONNX 모델 파일을 찾을 수 없습니다. 확인 경로: D:\py\img_worker\modules\modules\onnx_ocr_module\models +[2025-10-23 23:42:37,752] [LogListener] [DEBUG] [loggerModule.py:debug:275] 마스크 모듈 초기화 완료 +[2025-10-23 23:42:37,752] [LogListener] [DEBUG] [loggerModule.py:debug:275] MaskModule 초기화 성공 +[2025-10-23 23:42:37,752] [LogListener] [DEBUG] [loggerModule.py:debug:275] 텍스트 렌더링 모듈 초기화 완료 +[2025-10-23 23:42:37,752] [LogListener] [DEBUG] [loggerModule.py:debug:275] 기본 폰트: D:\py\img_worker\modules\HakgyoansimDunggeunmisoTTFB.ttf +[2025-10-23 23:42:37,753] [LogListener] [DEBUG] [loggerModule.py:debug:275] TextRenderingModule 초기화 성공 +[2025-10-23 23:42:37,753] [LogListener] [WARNING] [loggerModule.py:warning:287] 커스텀 폰트 로드 실패 (HakgyoansimDunggeunmisoTTFB.ttf): cannot open resource +[2025-10-23 23:42:37,753] [LogListener] [DEBUG] [loggerModule.py:debug:275] 기본 폰트를 사용합니다. +[2025-10-23 23:42:37,754] [LogListener] [DEBUG] [loggerModule.py:debug:275] PostImageManager 초기화 성공 +[2025-10-23 23:42:37,754] [LogListener] [DEBUG] [loggerModule.py:debug:275] 개발환경 +[2025-10-23 23:42:37,754] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔧 개발환경이므로 rembg 로컬 서버 강제 사용: None +[2025-10-23 23:42:37,754] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔧 개발환경이므로 인페인팅 로컬 서버 강제 사용: None +[2025-10-23 23:42:37,754] [LogListener] [DEBUG] [loggerModule.py:debug:275] 📡 최종 선택된 서버 URL: +[2025-10-23 23:42:37,754] [LogListener] [DEBUG] [loggerModule.py:debug:275] 인페인팅 서버: None +[2025-10-23 23:42:37,754] [LogListener] [DEBUG] [loggerModule.py:debug:275] 배경제거 서버: None +[2025-10-23 23:42:37,754] [LogListener] [DEBUG] [loggerModule.py:debug:275] local_rembg_model_path: D:\py\img_worker\modules\rembg_models +[2025-10-23 23:42:37,754] [LogListener] [DEBUG] [loggerModule.py:debug:275] Request_AI_Server GPU 상태: CUDA 사용 가능=False +[2025-10-23 23:42:37,754] [LogListener] [DEBUG] [loggerModule.py:debug:275] Request_AI_Server 초기화 성공 +[2025-10-23 23:42:37,754] [LogListener] [DEBUG] [loggerModule.py:debug:275] GoogleTranslate 초기화 성공 +[2025-10-23 23:42:37,789] [LogListener] [DEBUG] [loggerModule.py:debug:275] MIGAN CUDA 사용 불가 - CPU 모드로 설정 +[2025-10-23 23:42:37,789] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] GPU 관리자 전달: GPUManager, can_use_cuda: False +[2025-10-23 23:42:37,806] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] GPU 관리자 사용 가능: False +[2025-10-23 23:42:38,028] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 사용 가능한 providers: ['DmlExecutionProvider', 'CPUExecutionProvider'] +[2025-10-23 23:42:38,029] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] DirectML 가속 활성화 +[2025-10-23 23:42:38,029] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 최종 providers: [('DmlExecutionProvider', {}), ('CPUExecutionProvider', {})] +[2025-10-23 23:42:38,029] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] DirectML 가속 시도: [('DmlExecutionProvider', {}), ('CPUExecutionProvider', {})] +[2025-10-23 23:42:38,029] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] DirectML 가속 성공! 실제 providers: ['DmlExecutionProvider', 'CPUExecutionProvider'] +[2025-10-23 23:42:38,029] [ResultListener] [INFO] [loggerModule.py:info:281] 워커 READY 수신 +[2025-10-23 23:42:38,029] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 입력 0: image, 형태: ['batch_size', 3, 'height', 'width'], 타입: tensor(uint8) +[2025-10-23 23:42:38,029] [ResultListener] [INFO] [loggerModule.py:info:281] 워커 READY 수신 +[2025-10-23 23:42:38,030] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 입력 1: mask, 형태: ['batch_size', 1, 'height', 'width'], 타입: tensor(uint8) +[2025-10-23 23:42:38,030] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 출력 0: result, 형태: ['ScatterNDresult_dim_0', 3, 'ScatterNDresult_dim_2', 'ScatterNDresult_dim_3'], 타입: tensor(uint8) +[2025-10-23 23:42:38,030] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 세션 준비 완료. providers=['DmlExecutionProvider', 'CPUExecutionProvider'] +[2025-10-23 23:42:38,030] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] GPU 관리자 연결 완료: GPUManager +[2025-10-23 23:42:38,030] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 파이프라인 gpu_manager 속성: True, 값: +[2025-10-23 23:42:38,030] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 초기화 완료: gpu_manager 속성=True, 값= +[2025-10-23 23:42:38,030] [LogListener] [WARNING] [loggerModule.py:warning:287] OCR 모듈이 초기화되지 않아 Warm-up 건너뜀 +[2025-10-23 23:42:38,030] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔰 ImageProcessor Warm‑up 완료 +[2025-10-23 23:42:38,030] [LogListener] [DEBUG] [loggerModule.py:debug:275] 📡 추가 READY 신호 전송 완료 +[2025-10-23 23:42:38,030] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-23 23:42:38,030] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 26888) +[2025-10-23 23:42:38,031] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔥 작업 수신 성공 +[2025-10-23 23:42:38,031] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🚀 작업 처리 시작: cmd=__PING__, uid=ec710d03-4661-4aa5-8e5f-06321aebdc90 +[2025-10-23 23:42:38,031] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 실행 직전: cmd=__PING__ +[2025-10-23 23:42:38,031] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 결과 반환 중: uid=ec710d03-4661-4aa5-8e5f-06321aebdc90 +[2025-10-23 23:42:38,031] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 결과 반환 완료: uid=ec710d03-4661-4aa5-8e5f-06321aebdc90 +[2025-10-23 23:42:38,031] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-23 23:42:38,031] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 26888) +[2025-10-23 23:42:39,832] [MainThread] [INFO] [loggerModule.py:info:281] API 서버 종료 +[2025-10-23 23:42:40,235] [MainThread] [INFO] [loggerModule.py:info:281] 워커 프로세스 종료 +[2025-10-23 23:43:05,253] [MainThread] [WARNING] [loggerModule.py:warning:287] 워커 READY 타임아웃 +[2025-10-23 23:43:15,275] [MainThread] [WARNING] [loggerModule.py:warning:287] 워커 핑 실패 +[2025-10-23 23:43:15,276] [MainThread] [INFO] [loggerModule.py:info:281] API 서버 종료 +[2025-10-23 23:43:15,276] [MainThread] [INFO] [loggerModule.py:info:281] 워커 프로세스 종료 +[2025-10-23 23:43:16,583] [MainThread] [INFO] [loggerModule.py:info:281] API 서버 시작 +[2025-10-23 23:43:16,589] [MainThread] [INFO] [loggerModule.py:info:281] 워커 프로세스 기동: PID=22032 +[2025-10-23 23:43:17,038] [LogListener] [DEBUG] [loggerModule.py:debug:275] ImageWorker 프로세스 기동 (PID=22032, Name=ImageWorkerProcess) +[2025-10-23 23:43:17,038] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔧 ImageProcessor3 초기화 시작... +[2025-10-23 23:43:17,038] [LogListener] [DEBUG] [loggerModule.py:debug:275] tracemalloc 메모리 추적 시작 +[2025-10-23 23:43:17,045] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ DirectML 환경 준비 완료 (Windows DirectX 12 기반) +[2025-10-23 23:43:17,046] [LogListener] [DEBUG] [loggerModule.py:debug:275] === 🚀 DirectML GPU 상태 초기화 시작 🚀 === +[2025-10-23 23:43:17,046] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🎯 사용자 GPU 가속 요청: False +[2025-10-23 23:43:17,046] [LogListener] [DEBUG] [loggerModule.py:debug:275] 💻 현재 운영체제: Windows +[2025-10-23 23:43:17,046] [LogListener] [DEBUG] [loggerModule.py:debug:275] GPU 가속이 비활성화됨 (toggle_states['use_cuda'] = False) +[2025-10-23 23:43:17,046] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔒 안전한 CPU 모드로 모든 GPU 설정 강제 비활성화 +[2025-10-23 23:43:17,046] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔧 ImageProcessor3 GPU 상태 요약: +[2025-10-23 23:43:17,046] [LogListener] [DEBUG] [loggerModule.py:debug:275] - CUDA 사용 가능: False +[2025-10-23 23:43:17,046] [LogListener] [DEBUG] [loggerModule.py:debug:275] - toggle_states['use_cuda']: NOT_SET +[2025-10-23 23:43:17,046] [LogListener] [DEBUG] [loggerModule.py:debug:275] - GPU 하드웨어 정보: {} +[2025-10-23 23:43:17,046] [LogListener] [DEBUG] [loggerModule.py:debug:275] ImageProcessor3 Init toggle_states: {'TEMP_IMAGE_DIR': 'C:\\ProgramData\\ImgWorker\\work', 'output_image_format': 'webp', 'use_local_rembg': True, 'local_rembg_model_path': 'D:\\py\\img_worker\\modules\\rembg_models', 'local_model_name': 'birefnet-general-lite', 'optionIMGTrans_type': 'CPU', 'detail_IMGTrans_type': 'CPU', 'thumb_trans_type': 'CPU', 'migan_use_accel': True, 'migan_onnx_path': 'D:\\py\\img_worker\\modules\\migan_onnx\\migan_pipeline_v2.onnx', 'ocr_provider_override': 'auto', 'migan_provider_override': 'auto'} +[2025-10-23 23:43:17,047] [LogListener] [DEBUG] [loggerModule.py:debug:275] is_member_valid: False +[2025-10-23 23:43:17,047] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔄 인페인팅 서버 선택 - 타입: main +[2025-10-23 23:43:17,047] [LogListener] [DEBUG] [loggerModule.py:debug:275] 메인서버: None +[2025-10-23 23:43:17,047] [LogListener] [DEBUG] [loggerModule.py:debug:275] 테스트서버: None +[2025-10-23 23:43:17,047] [LogListener] [ERROR] [loggerModule.py:error:293] ❌ 메인 인페인팅 서버 사용 불가: None +[2025-10-23 23:43:17,047] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔄 배경제거 서버 선택 - 타입: main +[2025-10-23 23:43:17,047] [LogListener] [DEBUG] [loggerModule.py:debug:275] 메인서버: None +[2025-10-23 23:43:17,047] [LogListener] [DEBUG] [loggerModule.py:debug:275] 테스트서버: None +[2025-10-23 23:43:17,047] [LogListener] [ERROR] [loggerModule.py:error:293] ❌ 메인 배경제거 서버 사용 불가: None +[2025-10-23 23:43:17,047] [LogListener] [ERROR] [loggerModule.py:error:293] request_inpainting_server_url 설정되지 않았습니다. +[2025-10-23 23:43:17,047] [LogListener] [DEBUG] [loggerModule.py:debug:275] debug_images 디렉토리 이미 존재: D:\py\img_worker\modules\debug_images +[2025-10-23 23:43:17,047] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.font_path: D:\py\img_worker\modules\HakgyoansimDunggeunmisoTTFB.ttf +[2025-10-23 23:43:17,047] [LogListener] [DEBUG] [loggerModule.py:debug:275] toggle_states font_path: +[2025-10-23 23:43:17,047] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.TEMP_IMAGE_DIR: C:\ProgramData\ImgWorker\work +[2025-10-23 23:43:17,048] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.debugging_save_Dir: D:\py\img_worker\modules\debug_images +[2025-10-23 23:43:17,048] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.unwanted_texts: {} +[2025-10-23 23:43:17,048] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.inpaint_method: cv +[2025-10-23 23:43:17,048] [LogListener] [DEBUG] [loggerModule.py:debug:275] Image.MAX_IMAGE_PIXELS set to 20000000 +[2025-10-23 23:43:17,048] [LogListener] [DEBUG] [loggerModule.py:debug:275] ONNX 모델 타입 설정값: 자동 선택, GPU 정보: {} +[2025-10-23 23:43:17,048] [LogListener] [DEBUG] [loggerModule.py:debug:275] 자동 선택 모드: GPU 추천 모델 simp 사용 +[2025-10-23 23:43:17,048] [LogListener] [DEBUG] [loggerModule.py:debug:275] ONNX OCR 모델 타입 결정: simp (GPU: False) +[2025-10-23 23:43:17,048] [LogListener] [DEBUG] [loggerModule.py:debug:275] ONNX OCR 모듈 CPU 모드로 설정 +[2025-10-23 23:43:17,048] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🚀 ONNX TextSystem 초기화 시작 (CPU 모드) +[2025-10-23 23:43:17,048] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🚀 ONNX TextSystem 초기화 시작 (CPU 모드) +[2025-10-23 23:43:18,573] [LogListener] [DEBUG] [loggerModule.py:debug:275] 문자 사전 파일 UTF-8 확인 완료: D:\py\img_worker\modules\onnx_ocr_module\dict\ppocr_keys_v1.txt +[2025-10-23 23:43:18,657] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ ONNX TextSystem 초기화 완료 (CPU + SIMP (호환성) 모델) +[2025-10-23 23:43:18,658] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ ONNX TextSystem 초기화 완료 (CPU + SIMP 모델) +[2025-10-23 23:43:18,658] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ ONNX OCR 모듈 초기화 성공 (CPU 모드) +[2025-10-23 23:43:18,658] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ ONNX OCR 모듈 초기화 성공 +[2025-10-23 23:43:18,658] [LogListener] [DEBUG] [loggerModule.py:debug:275] 마스크 모듈 초기화 완료 +[2025-10-23 23:43:18,659] [LogListener] [DEBUG] [loggerModule.py:debug:275] MaskModule 초기화 성공 +[2025-10-23 23:43:18,659] [LogListener] [DEBUG] [loggerModule.py:debug:275] 텍스트 렌더링 모듈 초기화 완료 +[2025-10-23 23:43:18,659] [LogListener] [DEBUG] [loggerModule.py:debug:275] 기본 폰트: D:\py\img_worker\modules\HakgyoansimDunggeunmisoTTFB.ttf +[2025-10-23 23:43:18,659] [LogListener] [DEBUG] [loggerModule.py:debug:275] TextRenderingModule 초기화 성공 +[2025-10-23 23:43:18,660] [LogListener] [WARNING] [loggerModule.py:warning:287] 커스텀 폰트 로드 실패 (HakgyoansimDunggeunmisoTTFB.ttf): cannot open resource +[2025-10-23 23:43:18,660] [LogListener] [DEBUG] [loggerModule.py:debug:275] 기본 폰트를 사용합니다. +[2025-10-23 23:43:18,660] [LogListener] [DEBUG] [loggerModule.py:debug:275] PostImageManager 초기화 성공 +[2025-10-23 23:43:18,661] [LogListener] [DEBUG] [loggerModule.py:debug:275] 개발환경 +[2025-10-23 23:43:18,661] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔧 개발환경이므로 rembg 로컬 서버 강제 사용: None +[2025-10-23 23:43:18,661] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔧 개발환경이므로 인페인팅 로컬 서버 강제 사용: None +[2025-10-23 23:43:18,661] [LogListener] [DEBUG] [loggerModule.py:debug:275] 📡 최종 선택된 서버 URL: +[2025-10-23 23:43:18,661] [LogListener] [DEBUG] [loggerModule.py:debug:275] 인페인팅 서버: None +[2025-10-23 23:43:18,661] [LogListener] [DEBUG] [loggerModule.py:debug:275] 배경제거 서버: None +[2025-10-23 23:43:18,661] [LogListener] [DEBUG] [loggerModule.py:debug:275] local_rembg_model_path: D:\py\img_worker\modules\rembg_models +[2025-10-23 23:43:18,661] [LogListener] [DEBUG] [loggerModule.py:debug:275] Request_AI_Server GPU 상태: CUDA 사용 가능=False +[2025-10-23 23:43:18,661] [LogListener] [DEBUG] [loggerModule.py:debug:275] Request_AI_Server 초기화 성공 +[2025-10-23 23:43:18,661] [LogListener] [DEBUG] [loggerModule.py:debug:275] GoogleTranslate 초기화 성공 +[2025-10-23 23:43:18,661] [LogListener] [DEBUG] [loggerModule.py:debug:275] MIGAN CUDA 사용 불가 - CPU 모드로 설정 +[2025-10-23 23:43:18,661] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] GPU 관리자 전달: GPUManager, can_use_cuda: False +[2025-10-23 23:43:18,680] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] GPU 관리자 사용 가능: False +[2025-10-23 23:43:18,902] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 사용 가능한 providers: ['DmlExecutionProvider', 'CPUExecutionProvider'] +[2025-10-23 23:43:18,902] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] DirectML 가속 활성화 +[2025-10-23 23:43:18,902] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 최종 providers: [('DmlExecutionProvider', {}), ('CPUExecutionProvider', {})] +[2025-10-23 23:43:18,902] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] DirectML 가속 시도: [('DmlExecutionProvider', {}), ('CPUExecutionProvider', {})] +[2025-10-23 23:43:18,902] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] DirectML 가속 성공! 실제 providers: ['DmlExecutionProvider', 'CPUExecutionProvider'] +[2025-10-23 23:43:18,903] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 입력 0: image, 형태: ['batch_size', 3, 'height', 'width'], 타입: tensor(uint8) +[2025-10-23 23:43:18,903] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 입력 1: mask, 형태: ['batch_size', 1, 'height', 'width'], 타입: tensor(uint8) +[2025-10-23 23:43:18,903] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 출력 0: result, 형태: ['ScatterNDresult_dim_0', 3, 'ScatterNDresult_dim_2', 'ScatterNDresult_dim_3'], 타입: tensor(uint8) +[2025-10-23 23:43:18,903] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 세션 준비 완료. providers=['DmlExecutionProvider', 'CPUExecutionProvider'] +[2025-10-23 23:43:18,903] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] GPU 관리자 연결 완료: GPUManager +[2025-10-23 23:43:18,903] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 파이프라인 gpu_manager 속성: True, 값: +[2025-10-23 23:43:18,903] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 초기화 완료: gpu_manager 속성=True, 값= +[2025-10-23 23:43:18,903] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔰 OCR 모듈 Warm-up 시작... +[2025-10-23 23:43:18,903] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔍 ONNX OCR 감지 방식: polygon +[2025-10-23 23:43:18,908] [LogListener] [DEBUG] [loggerModule.py:debug:275] ⚡ ONNX OCR 추론 완료: 5.0ms +[2025-10-23 23:43:18,908] [LogListener] [DEBUG] [loggerModule.py:debug:275] 📊 세부 시간 - 감지: 4.0ms, 인식: 0.0ms, 분류: 0.0ms +[2025-10-23 23:43:18,908] [LogListener] [WARNING] [loggerModule.py:warning:287] ⚠️ ONNX OCR 결과가 비어있습니다 +[2025-10-23 23:43:18,912] [LogListener] [WARNING] [loggerModule.py:warning:287] ⚠️ OCR 모듈 Warm-up 실패: OpenCV(4.12.0) D:\a\opencv-python\opencv-python\opencv\modules\highgui\src\window.cpp:1295: error: (-2:Unspecified error) The function is not implemented. Rebuild the library with Windows, GTK+ 2.x or Cocoa support. If you are on Ubuntu or Debian, install libgtk2.0-dev and pkg-config, then re-run cmake or configure script in function 'cvDestroyAllWindows' + +[2025-10-23 23:43:18,912] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔰 ImageProcessor Warm‑up 완료 +[2025-10-23 23:43:18,912] [ResultListener] [INFO] [loggerModule.py:info:281] 워커 READY 수신 +[2025-10-23 23:43:18,912] [LogListener] [DEBUG] [loggerModule.py:debug:275] 📡 추가 READY 신호 전송 완료 +[2025-10-23 23:43:18,912] [ResultListener] [INFO] [loggerModule.py:info:281] 워커 READY 수신 +[2025-10-23 23:43:18,913] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-23 23:43:18,913] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 22032) +[2025-10-23 23:43:18,913] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔥 작업 수신 성공 +[2025-10-23 23:43:18,913] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🚀 작업 처리 시작: cmd=__PING__, uid=7bf9f294-a462-4c53-a01c-3cdd9f688652 +[2025-10-23 23:43:18,913] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 실행 직전: cmd=__PING__ +[2025-10-23 23:43:18,913] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 결과 반환 중: uid=7bf9f294-a462-4c53-a01c-3cdd9f688652 +[2025-10-23 23:43:18,913] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 결과 반환 완료: uid=7bf9f294-a462-4c53-a01c-3cdd9f688652 +[2025-10-23 23:43:18,913] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-23 23:43:18,913] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 22032) +[2025-10-23 23:43:43,892] [MainThread] [INFO] [loggerModule.py:info:281] API 서버 시작 +[2025-10-23 23:43:43,897] [MainThread] [INFO] [loggerModule.py:info:281] 워커 프로세스 기동: PID=19584 +[2025-10-23 23:43:44,323] [LogListener] [DEBUG] [loggerModule.py:debug:275] ImageWorker 프로세스 기동 (PID=19584, Name=ImageWorkerProcess) +[2025-10-23 23:43:44,324] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔧 ImageProcessor3 초기화 시작... +[2025-10-23 23:43:44,324] [LogListener] [DEBUG] [loggerModule.py:debug:275] tracemalloc 메모리 추적 시작 +[2025-10-23 23:43:44,330] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ DirectML 환경 준비 완료 (Windows DirectX 12 기반) +[2025-10-23 23:43:44,330] [LogListener] [DEBUG] [loggerModule.py:debug:275] === 🚀 DirectML GPU 상태 초기화 시작 🚀 === +[2025-10-23 23:43:44,331] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🎯 사용자 GPU 가속 요청: False +[2025-10-23 23:43:44,331] [LogListener] [DEBUG] [loggerModule.py:debug:275] 💻 현재 운영체제: Windows +[2025-10-23 23:43:44,331] [LogListener] [DEBUG] [loggerModule.py:debug:275] GPU 가속이 비활성화됨 (toggle_states['use_cuda'] = False) +[2025-10-23 23:43:44,331] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔒 안전한 CPU 모드로 모든 GPU 설정 강제 비활성화 +[2025-10-23 23:43:44,331] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔧 ImageProcessor3 GPU 상태 요약: +[2025-10-23 23:43:44,331] [LogListener] [DEBUG] [loggerModule.py:debug:275] - CUDA 사용 가능: False +[2025-10-23 23:43:44,331] [LogListener] [DEBUG] [loggerModule.py:debug:275] - toggle_states['use_cuda']: NOT_SET +[2025-10-23 23:43:44,331] [LogListener] [DEBUG] [loggerModule.py:debug:275] - GPU 하드웨어 정보: {} +[2025-10-23 23:43:44,331] [LogListener] [DEBUG] [loggerModule.py:debug:275] ImageProcessor3 Init toggle_states: {'TEMP_IMAGE_DIR': 'C:\\ProgramData\\ImgWorker\\work', 'output_image_format': 'webp', 'use_local_rembg': True, 'local_rembg_model_path': 'D:\\py\\img_worker\\modules\\rembg_models', 'local_model_name': 'birefnet-general-lite', 'optionIMGTrans_type': 'CPU', 'detail_IMGTrans_type': 'CPU', 'thumb_trans_type': 'CPU', 'migan_use_accel': True, 'migan_onnx_path': 'D:\\py\\img_worker\\modules\\migan_onnx\\migan_pipeline_v2.onnx', 'ocr_provider_override': 'auto', 'migan_provider_override': 'auto'} +[2025-10-23 23:43:44,331] [LogListener] [DEBUG] [loggerModule.py:debug:275] is_member_valid: False +[2025-10-23 23:43:44,332] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔄 인페인팅 서버 선택 - 타입: main +[2025-10-23 23:43:44,332] [LogListener] [DEBUG] [loggerModule.py:debug:275] 메인서버: None +[2025-10-23 23:43:44,332] [LogListener] [DEBUG] [loggerModule.py:debug:275] 테스트서버: None +[2025-10-23 23:43:44,332] [LogListener] [ERROR] [loggerModule.py:error:293] ❌ 메인 인페인팅 서버 사용 불가: None +[2025-10-23 23:43:44,332] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔄 배경제거 서버 선택 - 타입: main +[2025-10-23 23:43:44,332] [LogListener] [DEBUG] [loggerModule.py:debug:275] 메인서버: None +[2025-10-23 23:43:44,332] [LogListener] [DEBUG] [loggerModule.py:debug:275] 테스트서버: None +[2025-10-23 23:43:44,332] [LogListener] [ERROR] [loggerModule.py:error:293] ❌ 메인 배경제거 서버 사용 불가: None +[2025-10-23 23:43:44,332] [LogListener] [ERROR] [loggerModule.py:error:293] request_inpainting_server_url 설정되지 않았습니다. +[2025-10-23 23:43:44,332] [LogListener] [DEBUG] [loggerModule.py:debug:275] debug_images 디렉토리 이미 존재: D:\py\img_worker\modules\debug_images +[2025-10-23 23:43:44,332] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.font_path: D:\py\img_worker\modules\HakgyoansimDunggeunmisoTTFB.ttf +[2025-10-23 23:43:44,332] [LogListener] [DEBUG] [loggerModule.py:debug:275] toggle_states font_path: +[2025-10-23 23:43:44,332] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.TEMP_IMAGE_DIR: C:\ProgramData\ImgWorker\work +[2025-10-23 23:43:44,332] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.debugging_save_Dir: D:\py\img_worker\modules\debug_images +[2025-10-23 23:43:44,332] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.unwanted_texts: {} +[2025-10-23 23:43:44,332] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.inpaint_method: cv +[2025-10-23 23:43:44,332] [LogListener] [DEBUG] [loggerModule.py:debug:275] Image.MAX_IMAGE_PIXELS set to 20000000 +[2025-10-23 23:43:44,333] [LogListener] [DEBUG] [loggerModule.py:debug:275] ONNX 모델 타입 설정값: 자동 선택, GPU 정보: {} +[2025-10-23 23:43:44,333] [LogListener] [DEBUG] [loggerModule.py:debug:275] 자동 선택 모드: GPU 추천 모델 simp 사용 +[2025-10-23 23:43:44,333] [LogListener] [DEBUG] [loggerModule.py:debug:275] ONNX OCR 모델 타입 결정: simp (GPU: False) +[2025-10-23 23:43:44,333] [LogListener] [DEBUG] [loggerModule.py:debug:275] ONNX OCR 모듈 CPU 모드로 설정 +[2025-10-23 23:43:44,333] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🚀 ONNX TextSystem 초기화 시작 (CPU 모드) +[2025-10-23 23:43:44,333] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🚀 ONNX TextSystem 초기화 시작 (CPU 모드) +[2025-10-23 23:43:45,848] [LogListener] [DEBUG] [loggerModule.py:debug:275] 문자 사전 파일 UTF-8 확인 완료: D:\py\img_worker\modules\onnx_ocr_module\dict\ppocr_keys_v1.txt +[2025-10-23 23:43:45,931] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ ONNX TextSystem 초기화 완료 (CPU + SIMP (호환성) 모델) +[2025-10-23 23:43:45,931] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ ONNX TextSystem 초기화 완료 (CPU + SIMP 모델) +[2025-10-23 23:43:45,931] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ ONNX OCR 모듈 초기화 성공 (CPU 모드) +[2025-10-23 23:43:45,931] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ ONNX OCR 모듈 초기화 성공 +[2025-10-23 23:43:45,931] [LogListener] [DEBUG] [loggerModule.py:debug:275] 마스크 모듈 초기화 완료 +[2025-10-23 23:43:45,932] [LogListener] [DEBUG] [loggerModule.py:debug:275] MaskModule 초기화 성공 +[2025-10-23 23:43:45,932] [LogListener] [DEBUG] [loggerModule.py:debug:275] 텍스트 렌더링 모듈 초기화 완료 +[2025-10-23 23:43:45,932] [LogListener] [DEBUG] [loggerModule.py:debug:275] 기본 폰트: D:\py\img_worker\modules\HakgyoansimDunggeunmisoTTFB.ttf +[2025-10-23 23:43:45,932] [LogListener] [DEBUG] [loggerModule.py:debug:275] TextRenderingModule 초기화 성공 +[2025-10-23 23:43:45,933] [LogListener] [WARNING] [loggerModule.py:warning:287] 커스텀 폰트 로드 실패 (HakgyoansimDunggeunmisoTTFB.ttf): cannot open resource +[2025-10-23 23:43:45,933] [LogListener] [DEBUG] [loggerModule.py:debug:275] 기본 폰트를 사용합니다. +[2025-10-23 23:43:45,933] [LogListener] [DEBUG] [loggerModule.py:debug:275] PostImageManager 초기화 성공 +[2025-10-23 23:43:45,934] [LogListener] [DEBUG] [loggerModule.py:debug:275] 개발환경 +[2025-10-23 23:43:45,934] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔧 개발환경이므로 rembg 로컬 서버 강제 사용: None +[2025-10-23 23:43:45,934] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔧 개발환경이므로 인페인팅 로컬 서버 강제 사용: None +[2025-10-23 23:43:45,934] [LogListener] [DEBUG] [loggerModule.py:debug:275] 📡 최종 선택된 서버 URL: +[2025-10-23 23:43:45,934] [LogListener] [DEBUG] [loggerModule.py:debug:275] 인페인팅 서버: None +[2025-10-23 23:43:45,934] [LogListener] [DEBUG] [loggerModule.py:debug:275] 배경제거 서버: None +[2025-10-23 23:43:45,934] [LogListener] [DEBUG] [loggerModule.py:debug:275] local_rembg_model_path: D:\py\img_worker\modules\rembg_models +[2025-10-23 23:43:45,934] [LogListener] [DEBUG] [loggerModule.py:debug:275] Request_AI_Server GPU 상태: CUDA 사용 가능=False +[2025-10-23 23:43:45,934] [LogListener] [DEBUG] [loggerModule.py:debug:275] Request_AI_Server 초기화 성공 +[2025-10-23 23:43:45,934] [LogListener] [DEBUG] [loggerModule.py:debug:275] GoogleTranslate 초기화 성공 +[2025-10-23 23:43:45,934] [LogListener] [DEBUG] [loggerModule.py:debug:275] MIGAN CUDA 사용 불가 - CPU 모드로 설정 +[2025-10-23 23:43:45,934] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] GPU 관리자 전달: GPUManager, can_use_cuda: False +[2025-10-23 23:43:45,952] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] GPU 관리자 사용 가능: False +[2025-10-23 23:43:46,179] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 사용 가능한 providers: ['DmlExecutionProvider', 'CPUExecutionProvider'] +[2025-10-23 23:43:46,179] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] DirectML 가속 활성화 +[2025-10-23 23:43:46,179] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 최종 providers: [('DmlExecutionProvider', {}), ('CPUExecutionProvider', {})] +[2025-10-23 23:43:46,179] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] DirectML 가속 시도: [('DmlExecutionProvider', {}), ('CPUExecutionProvider', {})] +[2025-10-23 23:43:46,179] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] DirectML 가속 성공! 실제 providers: ['DmlExecutionProvider', 'CPUExecutionProvider'] +[2025-10-23 23:43:46,179] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 입력 0: image, 형태: ['batch_size', 3, 'height', 'width'], 타입: tensor(uint8) +[2025-10-23 23:43:46,180] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 입력 1: mask, 형태: ['batch_size', 1, 'height', 'width'], 타입: tensor(uint8) +[2025-10-23 23:43:46,180] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 출력 0: result, 형태: ['ScatterNDresult_dim_0', 3, 'ScatterNDresult_dim_2', 'ScatterNDresult_dim_3'], 타입: tensor(uint8) +[2025-10-23 23:43:46,180] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 세션 준비 완료. providers=['DmlExecutionProvider', 'CPUExecutionProvider'] +[2025-10-23 23:43:46,180] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] GPU 관리자 연결 완료: GPUManager +[2025-10-23 23:43:46,180] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 파이프라인 gpu_manager 속성: True, 값: +[2025-10-23 23:43:46,180] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 초기화 완료: gpu_manager 속성=True, 값= +[2025-10-23 23:43:46,180] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔰 OCR 모듈 Warm-up 시작... +[2025-10-23 23:43:46,180] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔍 ONNX OCR 감지 방식: polygon +[2025-10-23 23:43:46,185] [LogListener] [DEBUG] [loggerModule.py:debug:275] ⚡ ONNX OCR 추론 완료: 5.0ms +[2025-10-23 23:43:46,185] [LogListener] [DEBUG] [loggerModule.py:debug:275] 📊 세부 시간 - 감지: 4.0ms, 인식: 0.0ms, 분류: 0.0ms +[2025-10-23 23:43:46,185] [LogListener] [WARNING] [loggerModule.py:warning:287] ⚠️ ONNX OCR 결과가 비어있습니다 +[2025-10-23 23:43:46,188] [LogListener] [WARNING] [loggerModule.py:warning:287] ⚠️ OCR 모듈 Warm-up 실패: OpenCV(4.12.0) D:\a\opencv-python\opencv-python\opencv\modules\highgui\src\window.cpp:1295: error: (-2:Unspecified error) The function is not implemented. Rebuild the library with Windows, GTK+ 2.x or Cocoa support. If you are on Ubuntu or Debian, install libgtk2.0-dev and pkg-config, then re-run cmake or configure script in function 'cvDestroyAllWindows' + +[2025-10-23 23:43:46,188] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔰 ImageProcessor Warm‑up 완료 +[2025-10-23 23:43:46,189] [ResultListener] [INFO] [loggerModule.py:info:281] 워커 READY 수신 +[2025-10-23 23:43:46,189] [LogListener] [DEBUG] [loggerModule.py:debug:275] 📡 추가 READY 신호 전송 완료 +[2025-10-23 23:43:46,189] [ResultListener] [INFO] [loggerModule.py:info:281] 워커 READY 수신 +[2025-10-23 23:43:46,189] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-23 23:43:46,189] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 19584) +[2025-10-23 23:43:46,190] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔥 작업 수신 성공 +[2025-10-23 23:43:46,190] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🚀 작업 처리 시작: cmd=__PING__, uid=03c99bae-439d-486d-bc0d-191764c4a30c +[2025-10-23 23:43:46,190] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 실행 직전: cmd=__PING__ +[2025-10-23 23:43:46,190] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 결과 반환 중: uid=03c99bae-439d-486d-bc0d-191764c4a30c +[2025-10-23 23:43:46,190] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 결과 반환 완료: uid=03c99bae-439d-486d-bc0d-191764c4a30c +[2025-10-23 23:43:46,190] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-23 23:43:46,190] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 19584) +[2025-10-23 23:43:46,241] [MainThread] [INFO] [loggerModule.py:info:281] API 서버 종료 +[2025-10-23 23:43:46,242] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔥 작업 수신 성공 +[2025-10-23 23:43:46,823] [MainThread] [INFO] [loggerModule.py:info:281] 워커 프로세스 종료 +[2025-10-23 23:43:53,370] [MainThread] [INFO] [loggerModule.py:info:281] API 서버 종료 +[2025-10-23 23:43:53,841] [MainThread] [INFO] [loggerModule.py:info:281] 워커 프로세스 종료 +[2025-10-23 23:43:58,702] [MainThread] [INFO] [loggerModule.py:info:281] API 서버 시작 +[2025-10-23 23:43:58,708] [MainThread] [INFO] [loggerModule.py:info:281] 워커 프로세스 기동: PID=13376 +[2025-10-23 23:43:59,140] [LogListener] [DEBUG] [loggerModule.py:debug:275] ImageWorker 프로세스 기동 (PID=13376, Name=ImageWorkerProcess) +[2025-10-23 23:43:59,141] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔧 ImageProcessor3 초기화 시작... +[2025-10-23 23:43:59,141] [LogListener] [DEBUG] [loggerModule.py:debug:275] tracemalloc 메모리 추적 시작 +[2025-10-23 23:43:59,147] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ DirectML 환경 준비 완료 (Windows DirectX 12 기반) +[2025-10-23 23:43:59,147] [LogListener] [DEBUG] [loggerModule.py:debug:275] === 🚀 DirectML GPU 상태 초기화 시작 🚀 === +[2025-10-23 23:43:59,147] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🎯 사용자 GPU 가속 요청: False +[2025-10-23 23:43:59,147] [LogListener] [DEBUG] [loggerModule.py:debug:275] 💻 현재 운영체제: Windows +[2025-10-23 23:43:59,147] [LogListener] [DEBUG] [loggerModule.py:debug:275] GPU 가속이 비활성화됨 (toggle_states['use_cuda'] = False) +[2025-10-23 23:43:59,148] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔒 안전한 CPU 모드로 모든 GPU 설정 강제 비활성화 +[2025-10-23 23:43:59,148] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔧 ImageProcessor3 GPU 상태 요약: +[2025-10-23 23:43:59,148] [LogListener] [DEBUG] [loggerModule.py:debug:275] - CUDA 사용 가능: False +[2025-10-23 23:43:59,148] [LogListener] [DEBUG] [loggerModule.py:debug:275] - toggle_states['use_cuda']: NOT_SET +[2025-10-23 23:43:59,148] [LogListener] [DEBUG] [loggerModule.py:debug:275] - GPU 하드웨어 정보: {} +[2025-10-23 23:43:59,148] [LogListener] [DEBUG] [loggerModule.py:debug:275] ImageProcessor3 Init toggle_states: {'TEMP_IMAGE_DIR': 'C:\\ProgramData\\ImgWorker\\work', 'output_image_format': 'webp', 'use_local_rembg': True, 'local_rembg_model_path': 'D:\\py\\img_worker\\modules\\rembg_models', 'local_model_name': 'birefnet-general-lite', 'optionIMGTrans_type': 'CPU', 'detail_IMGTrans_type': 'CPU', 'thumb_trans_type': 'CPU', 'migan_use_accel': True, 'migan_onnx_path': 'D:\\py\\img_worker\\modules\\migan_onnx\\migan_pipeline_v2.onnx', 'ocr_provider_override': 'auto', 'migan_provider_override': 'auto'} +[2025-10-23 23:43:59,148] [LogListener] [DEBUG] [loggerModule.py:debug:275] is_member_valid: False +[2025-10-23 23:43:59,148] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔄 인페인팅 서버 선택 - 타입: main +[2025-10-23 23:43:59,148] [LogListener] [DEBUG] [loggerModule.py:debug:275] 메인서버: None +[2025-10-23 23:43:59,148] [LogListener] [DEBUG] [loggerModule.py:debug:275] 테스트서버: None +[2025-10-23 23:43:59,148] [LogListener] [ERROR] [loggerModule.py:error:293] ❌ 메인 인페인팅 서버 사용 불가: None +[2025-10-23 23:43:59,148] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔄 배경제거 서버 선택 - 타입: main +[2025-10-23 23:43:59,148] [LogListener] [DEBUG] [loggerModule.py:debug:275] 메인서버: None +[2025-10-23 23:43:59,148] [LogListener] [DEBUG] [loggerModule.py:debug:275] 테스트서버: None +[2025-10-23 23:43:59,149] [LogListener] [ERROR] [loggerModule.py:error:293] ❌ 메인 배경제거 서버 사용 불가: None +[2025-10-23 23:43:59,149] [LogListener] [ERROR] [loggerModule.py:error:293] request_inpainting_server_url 설정되지 않았습니다. +[2025-10-23 23:43:59,149] [LogListener] [DEBUG] [loggerModule.py:debug:275] debug_images 디렉토리 이미 존재: D:\py\img_worker\modules\debug_images +[2025-10-23 23:43:59,149] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.font_path: D:\py\img_worker\modules\HakgyoansimDunggeunmisoTTFB.ttf +[2025-10-23 23:43:59,149] [LogListener] [DEBUG] [loggerModule.py:debug:275] toggle_states font_path: +[2025-10-23 23:43:59,149] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.TEMP_IMAGE_DIR: C:\ProgramData\ImgWorker\work +[2025-10-23 23:43:59,149] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.debugging_save_Dir: D:\py\img_worker\modules\debug_images +[2025-10-23 23:43:59,149] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.unwanted_texts: {} +[2025-10-23 23:43:59,149] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.inpaint_method: cv +[2025-10-23 23:43:59,149] [LogListener] [DEBUG] [loggerModule.py:debug:275] Image.MAX_IMAGE_PIXELS set to 20000000 +[2025-10-23 23:43:59,149] [LogListener] [DEBUG] [loggerModule.py:debug:275] ONNX 모델 타입 설정값: 자동 선택, GPU 정보: {} +[2025-10-23 23:43:59,149] [LogListener] [DEBUG] [loggerModule.py:debug:275] 자동 선택 모드: GPU 추천 모델 simp 사용 +[2025-10-23 23:43:59,149] [LogListener] [DEBUG] [loggerModule.py:debug:275] ONNX OCR 모델 타입 결정: simp (GPU: False) +[2025-10-23 23:43:59,149] [LogListener] [DEBUG] [loggerModule.py:debug:275] ONNX OCR 모듈 CPU 모드로 설정 +[2025-10-23 23:43:59,149] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🚀 ONNX TextSystem 초기화 시작 (CPU 모드) +[2025-10-23 23:43:59,149] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🚀 ONNX TextSystem 초기화 시작 (CPU 모드) +[2025-10-23 23:44:00,771] [LogListener] [DEBUG] [loggerModule.py:debug:275] 문자 사전 파일 UTF-8 확인 완료: D:\py\img_worker\modules\onnx_ocr_module\dict\ppocr_keys_v1.txt +[2025-10-23 23:44:00,855] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ ONNX TextSystem 초기화 완료 (CPU + SIMP (호환성) 모델) +[2025-10-23 23:44:00,855] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ ONNX TextSystem 초기화 완료 (CPU + SIMP 모델) +[2025-10-23 23:44:00,855] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ ONNX OCR 모듈 초기화 성공 (CPU 모드) +[2025-10-23 23:44:00,855] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ ONNX OCR 모듈 초기화 성공 +[2025-10-23 23:44:00,855] [LogListener] [DEBUG] [loggerModule.py:debug:275] 마스크 모듈 초기화 완료 +[2025-10-23 23:44:00,856] [LogListener] [DEBUG] [loggerModule.py:debug:275] MaskModule 초기화 성공 +[2025-10-23 23:44:00,856] [LogListener] [DEBUG] [loggerModule.py:debug:275] 텍스트 렌더링 모듈 초기화 완료 +[2025-10-23 23:44:00,856] [LogListener] [DEBUG] [loggerModule.py:debug:275] 기본 폰트: D:\py\img_worker\modules\HakgyoansimDunggeunmisoTTFB.ttf +[2025-10-23 23:44:00,856] [LogListener] [DEBUG] [loggerModule.py:debug:275] TextRenderingModule 초기화 성공 +[2025-10-23 23:44:00,857] [LogListener] [WARNING] [loggerModule.py:warning:287] 커스텀 폰트 로드 실패 (HakgyoansimDunggeunmisoTTFB.ttf): cannot open resource +[2025-10-23 23:44:00,857] [LogListener] [DEBUG] [loggerModule.py:debug:275] 기본 폰트를 사용합니다. +[2025-10-23 23:44:00,857] [LogListener] [DEBUG] [loggerModule.py:debug:275] PostImageManager 초기화 성공 +[2025-10-23 23:44:00,858] [LogListener] [DEBUG] [loggerModule.py:debug:275] 개발환경 +[2025-10-23 23:44:00,858] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔧 개발환경이므로 rembg 로컬 서버 강제 사용: None +[2025-10-23 23:44:00,858] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔧 개발환경이므로 인페인팅 로컬 서버 강제 사용: None +[2025-10-23 23:44:00,858] [LogListener] [DEBUG] [loggerModule.py:debug:275] 📡 최종 선택된 서버 URL: +[2025-10-23 23:44:00,858] [LogListener] [DEBUG] [loggerModule.py:debug:275] 인페인팅 서버: None +[2025-10-23 23:44:00,858] [LogListener] [DEBUG] [loggerModule.py:debug:275] 배경제거 서버: None +[2025-10-23 23:44:00,858] [LogListener] [DEBUG] [loggerModule.py:debug:275] local_rembg_model_path: D:\py\img_worker\modules\rembg_models +[2025-10-23 23:44:00,858] [LogListener] [DEBUG] [loggerModule.py:debug:275] Request_AI_Server GPU 상태: CUDA 사용 가능=False +[2025-10-23 23:44:00,859] [LogListener] [DEBUG] [loggerModule.py:debug:275] Request_AI_Server 초기화 성공 +[2025-10-23 23:44:00,859] [LogListener] [DEBUG] [loggerModule.py:debug:275] GoogleTranslate 초기화 성공 +[2025-10-23 23:44:00,859] [LogListener] [DEBUG] [loggerModule.py:debug:275] MIGAN CUDA 사용 불가 - CPU 모드로 설정 +[2025-10-23 23:44:00,859] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] GPU 관리자 전달: GPUManager, can_use_cuda: False +[2025-10-23 23:44:00,877] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] GPU 관리자 사용 가능: False +[2025-10-23 23:44:01,098] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 사용 가능한 providers: ['DmlExecutionProvider', 'CPUExecutionProvider'] +[2025-10-23 23:44:01,098] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] DirectML 가속 활성화 +[2025-10-23 23:44:01,098] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 최종 providers: [('DmlExecutionProvider', {}), ('CPUExecutionProvider', {})] +[2025-10-23 23:44:01,099] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] DirectML 가속 시도: [('DmlExecutionProvider', {}), ('CPUExecutionProvider', {})] +[2025-10-23 23:44:01,099] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] DirectML 가속 성공! 실제 providers: ['DmlExecutionProvider', 'CPUExecutionProvider'] +[2025-10-23 23:44:01,099] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 입력 0: image, 형태: ['batch_size', 3, 'height', 'width'], 타입: tensor(uint8) +[2025-10-23 23:44:01,099] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 입력 1: mask, 형태: ['batch_size', 1, 'height', 'width'], 타입: tensor(uint8) +[2025-10-23 23:44:01,099] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 출력 0: result, 형태: ['ScatterNDresult_dim_0', 3, 'ScatterNDresult_dim_2', 'ScatterNDresult_dim_3'], 타입: tensor(uint8) +[2025-10-23 23:44:01,099] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 세션 준비 완료. providers=['DmlExecutionProvider', 'CPUExecutionProvider'] +[2025-10-23 23:44:01,099] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] GPU 관리자 연결 완료: GPUManager +[2025-10-23 23:44:01,099] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 파이프라인 gpu_manager 속성: True, 값: +[2025-10-23 23:44:01,099] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 초기화 완료: gpu_manager 속성=True, 값= +[2025-10-23 23:44:01,099] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔰 OCR 모듈 Warm-up 시작... +[2025-10-23 23:44:01,099] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔍 ONNX OCR 감지 방식: polygon +[2025-10-23 23:44:01,104] [LogListener] [DEBUG] [loggerModule.py:debug:275] ⚡ ONNX OCR 추론 완료: 5.0ms +[2025-10-23 23:44:01,105] [LogListener] [DEBUG] [loggerModule.py:debug:275] 📊 세부 시간 - 감지: 3.0ms, 인식: 0.0ms, 분류: 0.0ms +[2025-10-23 23:44:01,105] [LogListener] [WARNING] [loggerModule.py:warning:287] ⚠️ ONNX OCR 결과가 비어있습니다 +[2025-10-23 23:44:01,108] [LogListener] [WARNING] [loggerModule.py:warning:287] ⚠️ OCR 모듈 Warm-up 실패: OpenCV(4.12.0) D:\a\opencv-python\opencv-python\opencv\modules\highgui\src\window.cpp:1295: error: (-2:Unspecified error) The function is not implemented. Rebuild the library with Windows, GTK+ 2.x or Cocoa support. If you are on Ubuntu or Debian, install libgtk2.0-dev and pkg-config, then re-run cmake or configure script in function 'cvDestroyAllWindows' + +[2025-10-23 23:44:01,108] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔰 ImageProcessor Warm‑up 완료 +[2025-10-23 23:44:01,109] [ResultListener] [INFO] [loggerModule.py:info:281] 워커 READY 수신 +[2025-10-23 23:44:01,109] [ResultListener] [INFO] [loggerModule.py:info:281] 워커 READY 수신 +[2025-10-23 23:44:01,109] [LogListener] [DEBUG] [loggerModule.py:debug:275] 📡 추가 READY 신호 전송 완료 +[2025-10-23 23:44:01,109] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-23 23:44:01,109] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 13376) +[2025-10-23 23:44:01,110] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔥 작업 수신 성공 +[2025-10-23 23:44:01,110] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🚀 작업 처리 시작: cmd=__PING__, uid=887a69af-f81b-483e-9999-979a99e31876 +[2025-10-23 23:44:01,110] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 실행 직전: cmd=__PING__ +[2025-10-23 23:44:01,110] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 결과 반환 중: uid=887a69af-f81b-483e-9999-979a99e31876 +[2025-10-23 23:44:01,110] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 결과 반환 완료: uid=887a69af-f81b-483e-9999-979a99e31876 +[2025-10-23 23:44:01,110] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-23 23:44:01,110] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 13376) +[2025-10-23 23:44:04,841] [MainThread] [INFO] [loggerModule.py:info:281] API 서버 종료 +[2025-10-23 23:44:05,359] [MainThread] [INFO] [loggerModule.py:info:281] 워커 프로세스 종료 +[2025-10-23 23:49:57,987] [MainThread] [INFO] [loggerModule.py:info:281] API 서버 시작 +[2025-10-23 23:49:57,993] [MainThread] [INFO] [loggerModule.py:info:281] 워커 프로세스 기동: PID=22112 +[2025-10-23 23:49:58,446] [LogListener] [DEBUG] [loggerModule.py:debug:275] ImageWorker 프로세스 기동 (PID=22112, Name=ImageWorkerProcess) +[2025-10-23 23:49:58,447] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔧 ImageProcessor3 초기화 시작... +[2025-10-23 23:49:58,447] [LogListener] [DEBUG] [loggerModule.py:debug:275] tracemalloc 메모리 추적 시작 +[2025-10-23 23:49:58,453] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ DirectML 환경 준비 완료 (Windows DirectX 12 기반) +[2025-10-23 23:49:58,453] [LogListener] [DEBUG] [loggerModule.py:debug:275] === 🚀 DirectML GPU 상태 초기화 시작 🚀 === +[2025-10-23 23:49:58,453] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🎯 사용자 GPU 가속 요청: False +[2025-10-23 23:49:58,453] [LogListener] [DEBUG] [loggerModule.py:debug:275] 💻 현재 운영체제: Windows +[2025-10-23 23:49:58,453] [LogListener] [DEBUG] [loggerModule.py:debug:275] GPU 가속이 비활성화됨 (toggle_states['use_cuda'] = False) +[2025-10-23 23:49:58,453] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔒 안전한 CPU 모드로 모든 GPU 설정 강제 비활성화 +[2025-10-23 23:49:58,454] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔧 ImageProcessor3 GPU 상태 요약: +[2025-10-23 23:49:58,454] [LogListener] [DEBUG] [loggerModule.py:debug:275] - CUDA 사용 가능: False +[2025-10-23 23:49:58,454] [LogListener] [DEBUG] [loggerModule.py:debug:275] - toggle_states['use_cuda']: NOT_SET +[2025-10-23 23:49:58,454] [LogListener] [DEBUG] [loggerModule.py:debug:275] - GPU 하드웨어 정보: {} +[2025-10-23 23:49:58,454] [LogListener] [DEBUG] [loggerModule.py:debug:275] ImageProcessor3 Init toggle_states: {'TEMP_IMAGE_DIR': 'C:\\ProgramData\\ImgWorker\\work', 'output_image_format': 'webp', 'use_local_rembg': True, 'local_rembg_model_path': 'D:\\py\\img_worker\\modules\\rembg_models', 'local_model_name': 'birefnet-general-lite', 'optionIMGTrans_type': 'CPU', 'detail_IMGTrans_type': 'CPU', 'thumb_trans_type': 'CPU', 'migan_use_accel': True, 'migan_onnx_path': 'D:\\py\\img_worker\\modules\\migan_onnx\\migan_pipeline_v2.onnx', 'ocr_provider_override': 'auto', 'migan_provider_override': 'auto'} +[2025-10-23 23:49:58,454] [LogListener] [DEBUG] [loggerModule.py:debug:275] is_member_valid: False +[2025-10-23 23:49:58,454] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔄 인페인팅 서버 선택 - 타입: main +[2025-10-23 23:49:58,454] [LogListener] [DEBUG] [loggerModule.py:debug:275] 메인서버: None +[2025-10-23 23:49:58,455] [LogListener] [DEBUG] [loggerModule.py:debug:275] 테스트서버: None +[2025-10-23 23:49:58,455] [LogListener] [ERROR] [loggerModule.py:error:293] ❌ 메인 인페인팅 서버 사용 불가: None +[2025-10-23 23:49:58,455] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔄 배경제거 서버 선택 - 타입: main +[2025-10-23 23:49:58,455] [LogListener] [DEBUG] [loggerModule.py:debug:275] 메인서버: None +[2025-10-23 23:49:58,455] [LogListener] [DEBUG] [loggerModule.py:debug:275] 테스트서버: None +[2025-10-23 23:49:58,455] [LogListener] [ERROR] [loggerModule.py:error:293] ❌ 메인 배경제거 서버 사용 불가: None +[2025-10-23 23:49:58,455] [LogListener] [ERROR] [loggerModule.py:error:293] request_inpainting_server_url 설정되지 않았습니다. +[2025-10-23 23:49:58,455] [LogListener] [DEBUG] [loggerModule.py:debug:275] debug_images 디렉토리 이미 존재: D:\py\img_worker\modules\debug_images +[2025-10-23 23:49:58,455] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.font_path: D:\py\img_worker\modules\HakgyoansimDunggeunmisoTTFB.ttf +[2025-10-23 23:49:58,455] [LogListener] [DEBUG] [loggerModule.py:debug:275] toggle_states font_path: +[2025-10-23 23:49:58,455] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.TEMP_IMAGE_DIR: C:\ProgramData\ImgWorker\work +[2025-10-23 23:49:58,455] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.debugging_save_Dir: D:\py\img_worker\modules\debug_images +[2025-10-23 23:49:58,455] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.unwanted_texts: {} +[2025-10-23 23:49:58,455] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.inpaint_method: cv +[2025-10-23 23:49:58,456] [LogListener] [DEBUG] [loggerModule.py:debug:275] Image.MAX_IMAGE_PIXELS set to 20000000 +[2025-10-23 23:49:58,456] [LogListener] [DEBUG] [loggerModule.py:debug:275] ONNX 모델 타입 설정값: 자동 선택, GPU 정보: {} +[2025-10-23 23:49:58,456] [LogListener] [DEBUG] [loggerModule.py:debug:275] 자동 선택 모드: GPU 추천 모델 simp 사용 +[2025-10-23 23:49:58,456] [LogListener] [DEBUG] [loggerModule.py:debug:275] ONNX OCR 모델 타입 결정: simp (GPU: False) +[2025-10-23 23:49:58,456] [LogListener] [DEBUG] [loggerModule.py:debug:275] ONNX OCR 모듈 CPU 모드로 설정 +[2025-10-23 23:49:58,456] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🚀 ONNX TextSystem 초기화 시작 (CPU 모드) +[2025-10-23 23:49:58,456] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🚀 ONNX TextSystem 초기화 시작 (CPU 모드) +[2025-10-23 23:49:59,987] [LogListener] [DEBUG] [loggerModule.py:debug:275] 문자 사전 파일 UTF-8 확인 완료: D:\py\img_worker\modules\onnx_ocr_module\dict\ppocr_keys_v1.txt +[2025-10-23 23:50:00,074] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ ONNX TextSystem 초기화 완료 (CPU + SIMP (호환성) 모델) +[2025-10-23 23:50:00,075] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ ONNX TextSystem 초기화 완료 (CPU + SIMP 모델) +[2025-10-23 23:50:00,075] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ ONNX OCR 모듈 초기화 성공 (CPU 모드) +[2025-10-23 23:50:00,075] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ ONNX OCR 모듈 초기화 성공 +[2025-10-23 23:50:00,075] [LogListener] [DEBUG] [loggerModule.py:debug:275] 마스크 모듈 초기화 완료 +[2025-10-23 23:50:00,075] [LogListener] [DEBUG] [loggerModule.py:debug:275] MaskModule 초기화 성공 +[2025-10-23 23:50:00,076] [LogListener] [DEBUG] [loggerModule.py:debug:275] 텍스트 렌더링 모듈 초기화 완료 +[2025-10-23 23:50:00,076] [LogListener] [DEBUG] [loggerModule.py:debug:275] 기본 폰트: D:\py\img_worker\modules\HakgyoansimDunggeunmisoTTFB.ttf +[2025-10-23 23:50:00,076] [LogListener] [DEBUG] [loggerModule.py:debug:275] TextRenderingModule 초기화 성공 +[2025-10-23 23:50:00,077] [LogListener] [WARNING] [loggerModule.py:warning:287] 커스텀 폰트 로드 실패 (HakgyoansimDunggeunmisoTTFB.ttf): cannot open resource +[2025-10-23 23:50:00,077] [LogListener] [DEBUG] [loggerModule.py:debug:275] 기본 폰트를 사용합니다. +[2025-10-23 23:50:00,077] [LogListener] [DEBUG] [loggerModule.py:debug:275] PostImageManager 초기화 성공 +[2025-10-23 23:50:00,077] [LogListener] [DEBUG] [loggerModule.py:debug:275] 개발환경 +[2025-10-23 23:50:00,077] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔧 개발환경이므로 rembg 로컬 서버 강제 사용: None +[2025-10-23 23:50:00,078] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔧 개발환경이므로 인페인팅 로컬 서버 강제 사용: None +[2025-10-23 23:50:00,078] [LogListener] [DEBUG] [loggerModule.py:debug:275] 📡 최종 선택된 서버 URL: +[2025-10-23 23:50:00,078] [LogListener] [DEBUG] [loggerModule.py:debug:275] 인페인팅 서버: None +[2025-10-23 23:50:00,078] [LogListener] [DEBUG] [loggerModule.py:debug:275] 배경제거 서버: None +[2025-10-23 23:50:00,078] [LogListener] [DEBUG] [loggerModule.py:debug:275] local_rembg_model_path: D:\py\img_worker\modules\rembg_models +[2025-10-23 23:50:00,078] [LogListener] [DEBUG] [loggerModule.py:debug:275] Request_AI_Server GPU 상태: CUDA 사용 가능=False +[2025-10-23 23:50:00,078] [LogListener] [DEBUG] [loggerModule.py:debug:275] Request_AI_Server 초기화 성공 +[2025-10-23 23:50:00,078] [LogListener] [DEBUG] [loggerModule.py:debug:275] GoogleTranslate 초기화 성공 +[2025-10-23 23:50:00,078] [LogListener] [DEBUG] [loggerModule.py:debug:275] MIGAN CUDA 사용 불가 - CPU 모드로 설정 +[2025-10-23 23:50:00,078] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] GPU 관리자 전달: GPUManager, can_use_cuda: False +[2025-10-23 23:50:00,097] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] GPU 관리자 사용 가능: False +[2025-10-23 23:50:00,325] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 사용 가능한 providers: ['DmlExecutionProvider', 'CPUExecutionProvider'] +[2025-10-23 23:50:00,325] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] DirectML 가속 활성화 +[2025-10-23 23:50:00,325] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 최종 providers: [('DmlExecutionProvider', {}), ('CPUExecutionProvider', {})] +[2025-10-23 23:50:00,325] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] DirectML 가속 시도: [('DmlExecutionProvider', {}), ('CPUExecutionProvider', {})] +[2025-10-23 23:50:00,325] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] DirectML 가속 성공! 실제 providers: ['DmlExecutionProvider', 'CPUExecutionProvider'] +[2025-10-23 23:50:00,325] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 입력 0: image, 형태: ['batch_size', 3, 'height', 'width'], 타입: tensor(uint8) +[2025-10-23 23:50:00,325] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 입력 1: mask, 형태: ['batch_size', 1, 'height', 'width'], 타입: tensor(uint8) +[2025-10-23 23:50:00,326] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 출력 0: result, 형태: ['ScatterNDresult_dim_0', 3, 'ScatterNDresult_dim_2', 'ScatterNDresult_dim_3'], 타입: tensor(uint8) +[2025-10-23 23:50:00,326] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 세션 준비 완료. providers=['DmlExecutionProvider', 'CPUExecutionProvider'] +[2025-10-23 23:50:00,326] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] GPU 관리자 연결 완료: GPUManager +[2025-10-23 23:50:00,326] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 파이프라인 gpu_manager 속성: True, 값: +[2025-10-23 23:50:00,326] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 초기화 완료: gpu_manager 속성=True, 값= +[2025-10-23 23:50:00,326] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔰 OCR 모듈 Warm-up 시작... +[2025-10-23 23:50:00,326] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔍 ONNX OCR 감지 방식: polygon +[2025-10-23 23:50:00,331] [LogListener] [DEBUG] [loggerModule.py:debug:275] ⚡ ONNX OCR 추론 완료: 5.0ms +[2025-10-23 23:50:00,331] [LogListener] [DEBUG] [loggerModule.py:debug:275] 📊 세부 시간 - 감지: 3.0ms, 인식: 0.0ms, 분류: 0.0ms +[2025-10-23 23:50:00,331] [LogListener] [WARNING] [loggerModule.py:warning:287] ⚠️ ONNX OCR 결과가 비어있습니다 +[2025-10-23 23:50:00,334] [LogListener] [WARNING] [loggerModule.py:warning:287] ⚠️ OCR 모듈 Warm-up 실패: OpenCV(4.12.0) D:\a\opencv-python\opencv-python\opencv\modules\highgui\src\window.cpp:1295: error: (-2:Unspecified error) The function is not implemented. Rebuild the library with Windows, GTK+ 2.x or Cocoa support. If you are on Ubuntu or Debian, install libgtk2.0-dev and pkg-config, then re-run cmake or configure script in function 'cvDestroyAllWindows' + +[2025-10-23 23:50:00,334] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔰 ImageProcessor Warm‑up 완료 +[2025-10-23 23:50:00,335] [ResultListener] [INFO] [loggerModule.py:info:281] 워커 READY 수신 +[2025-10-23 23:50:00,335] [ResultListener] [INFO] [loggerModule.py:info:281] 워커 READY 수신 +[2025-10-23 23:50:00,335] [LogListener] [DEBUG] [loggerModule.py:debug:275] 📡 추가 READY 신호 전송 완료 +[2025-10-23 23:50:00,335] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-23 23:50:00,335] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 22112) +[2025-10-23 23:50:00,336] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔥 작업 수신 성공 +[2025-10-23 23:50:00,336] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🚀 작업 처리 시작: cmd=__PING__, uid=eef3261d-73f0-4914-ba19-0a5f2696ccfa +[2025-10-23 23:50:00,336] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 실행 직전: cmd=__PING__ +[2025-10-23 23:50:00,336] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 결과 반환 중: uid=eef3261d-73f0-4914-ba19-0a5f2696ccfa +[2025-10-23 23:50:00,336] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 결과 반환 완료: uid=eef3261d-73f0-4914-ba19-0a5f2696ccfa +[2025-10-23 23:50:00,336] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-23 23:50:00,336] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 22112) +[2025-10-23 23:50:04,093] [MainThread] [INFO] [loggerModule.py:info:281] API 서버 종료 +[2025-10-23 23:50:04,523] [MainThread] [INFO] [loggerModule.py:info:281] 워커 프로세스 종료 +[2025-10-23 23:57:20,657] [MainThread] [INFO] [loggerModule.py:info:281] API 서버 시작 +[2025-10-23 23:57:20,662] [MainThread] [INFO] [loggerModule.py:info:281] 워커 프로세스 기동: PID=28588 +[2025-10-23 23:57:21,112] [LogListener] [DEBUG] [loggerModule.py:debug:275] ImageWorker 프로세스 기동 (PID=28588, Name=ImageWorkerProcess) +[2025-10-23 23:57:21,112] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔧 ImageProcessor3 초기화 시작... +[2025-10-23 23:57:21,113] [LogListener] [DEBUG] [loggerModule.py:debug:275] tracemalloc 메모리 추적 시작 +[2025-10-23 23:57:21,119] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ DirectML 환경 준비 완료 (Windows DirectX 12 기반) +[2025-10-23 23:57:21,119] [LogListener] [DEBUG] [loggerModule.py:debug:275] === 🚀 DirectML GPU 상태 초기화 시작 🚀 === +[2025-10-23 23:57:21,119] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🎯 사용자 GPU 가속 요청: False +[2025-10-23 23:57:21,119] [LogListener] [DEBUG] [loggerModule.py:debug:275] 💻 현재 운영체제: Windows +[2025-10-23 23:57:21,119] [LogListener] [DEBUG] [loggerModule.py:debug:275] GPU 가속이 비활성화됨 (toggle_states['use_cuda'] = False) +[2025-10-23 23:57:21,120] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔒 안전한 CPU 모드로 모든 GPU 설정 강제 비활성화 +[2025-10-23 23:57:21,120] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔧 ImageProcessor3 GPU 상태 요약: +[2025-10-23 23:57:21,120] [LogListener] [DEBUG] [loggerModule.py:debug:275] - CUDA 사용 가능: False +[2025-10-23 23:57:21,120] [LogListener] [DEBUG] [loggerModule.py:debug:275] - toggle_states['use_cuda']: NOT_SET +[2025-10-23 23:57:21,120] [LogListener] [DEBUG] [loggerModule.py:debug:275] - GPU 하드웨어 정보: {} +[2025-10-23 23:57:21,120] [LogListener] [DEBUG] [loggerModule.py:debug:275] ImageProcessor3 Init toggle_states: {'TEMP_IMAGE_DIR': 'C:\\ProgramData\\ImgWorker\\work', 'output_image_format': 'webp', 'use_local_rembg': True, 'local_rembg_model_path': 'D:\\py\\img_worker\\modules\\rembg_models', 'local_model_name': 'birefnet-general-lite', 'optionIMGTrans_type': 'CPU', 'detail_IMGTrans_type': 'CPU', 'thumb_trans_type': 'CPU', 'migan_use_accel': True, 'migan_onnx_path': 'D:\\py\\img_worker\\modules\\migan_onnx\\migan_pipeline_v2.onnx', 'ocr_provider_override': 'auto', 'migan_provider_override': 'auto'} +[2025-10-23 23:57:21,120] [LogListener] [DEBUG] [loggerModule.py:debug:275] is_member_valid: False +[2025-10-23 23:57:21,120] [LogListener] [DEBUG] [loggerModule.py:debug:275] debug_images 디렉토리 이미 존재: D:\py\img_worker\modules\debug_images +[2025-10-23 23:57:21,121] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.font_path: D:\py\img_worker\modules\HakgyoansimDunggeunmisoTTFB.ttf +[2025-10-23 23:57:21,121] [LogListener] [DEBUG] [loggerModule.py:debug:275] toggle_states font_path: +[2025-10-23 23:57:21,121] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.TEMP_IMAGE_DIR: C:\ProgramData\ImgWorker\work +[2025-10-23 23:57:21,121] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.debugging_save_Dir: D:\py\img_worker\modules\debug_images +[2025-10-23 23:57:21,121] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.unwanted_texts: {} +[2025-10-23 23:57:21,121] [LogListener] [DEBUG] [loggerModule.py:debug:275] self.inpaint_method: migan +[2025-10-23 23:57:21,121] [LogListener] [DEBUG] [loggerModule.py:debug:275] Image.MAX_IMAGE_PIXELS set to 20000000 +[2025-10-23 23:57:21,121] [LogListener] [DEBUG] [loggerModule.py:debug:275] ONNX 모델 타입 설정값: 자동 선택, GPU 정보: {} +[2025-10-23 23:57:21,121] [LogListener] [DEBUG] [loggerModule.py:debug:275] 자동 선택 모드: GPU 추천 모델 simp 사용 +[2025-10-23 23:57:21,121] [LogListener] [DEBUG] [loggerModule.py:debug:275] ONNX OCR 모델 타입 결정: simp (GPU: False) +[2025-10-23 23:57:21,121] [LogListener] [DEBUG] [loggerModule.py:debug:275] ONNX OCR 모듈 CPU 모드로 설정 +[2025-10-23 23:57:21,121] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🚀 ONNX TextSystem 초기화 시작 (CPU 모드) +[2025-10-23 23:57:21,121] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🚀 ONNX TextSystem 초기화 시작 (CPU 모드) +[2025-10-23 23:57:22,626] [LogListener] [DEBUG] [loggerModule.py:debug:275] 문자 사전 파일 UTF-8 확인 완료: D:\py\img_worker\modules\onnx_ocr_module\dict\ppocr_keys_v1.txt +[2025-10-23 23:57:22,713] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ ONNX TextSystem 초기화 완료 (CPU + SIMP (호환성) 모델) +[2025-10-23 23:57:22,713] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ ONNX TextSystem 초기화 완료 (CPU + SIMP 모델) +[2025-10-23 23:57:22,713] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ ONNX OCR 모듈 초기화 성공 (CPU 모드) +[2025-10-23 23:57:22,713] [LogListener] [DEBUG] [loggerModule.py:debug:275] ✅ ONNX OCR 모듈 초기화 성공 +[2025-10-23 23:57:22,714] [LogListener] [DEBUG] [loggerModule.py:debug:275] 마스크 모듈 초기화 완료 +[2025-10-23 23:57:22,714] [LogListener] [DEBUG] [loggerModule.py:debug:275] MaskModule 초기화 성공 +[2025-10-23 23:57:22,714] [LogListener] [DEBUG] [loggerModule.py:debug:275] 텍스트 렌더링 모듈 초기화 완료 +[2025-10-23 23:57:22,714] [LogListener] [DEBUG] [loggerModule.py:debug:275] 기본 폰트: D:\py\img_worker\modules\HakgyoansimDunggeunmisoTTFB.ttf +[2025-10-23 23:57:22,714] [LogListener] [DEBUG] [loggerModule.py:debug:275] TextRenderingModule 초기화 성공 +[2025-10-23 23:57:22,715] [LogListener] [WARNING] [loggerModule.py:warning:287] 커스텀 폰트 로드 실패 (HakgyoansimDunggeunmisoTTFB.ttf): cannot open resource +[2025-10-23 23:57:22,715] [LogListener] [DEBUG] [loggerModule.py:debug:275] 기본 폰트를 사용합니다. +[2025-10-23 23:57:22,715] [LogListener] [DEBUG] [loggerModule.py:debug:275] PostImageManager 초기화 성공 +[2025-10-23 23:57:22,715] [LogListener] [DEBUG] [loggerModule.py:debug:275] local_rembg_model_path: D:\py\img_worker\modules\rembg_models +[2025-10-23 23:57:22,715] [LogListener] [ERROR] [loggerModule.py:error:293] Request_AI_Server 초기화 실패: name 'inpaint_server_url' is not defined +[2025-10-23 23:57:22,716] [LogListener] [DEBUG] [loggerModule.py:debug:275] GoogleTranslate 초기화 성공 +[2025-10-23 23:57:22,716] [LogListener] [DEBUG] [loggerModule.py:debug:275] MIGAN CUDA 사용 불가 - CPU 모드로 설정 +[2025-10-23 23:57:22,716] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] GPU 관리자 전달: GPUManager, can_use_cuda: False +[2025-10-23 23:57:22,735] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] GPU 관리자 사용 가능: False +[2025-10-23 23:57:22,946] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 사용 가능한 providers: ['DmlExecutionProvider', 'CPUExecutionProvider'] +[2025-10-23 23:57:22,946] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] DirectML 가속 활성화 +[2025-10-23 23:57:22,946] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 최종 providers: [('DmlExecutionProvider', {}), ('CPUExecutionProvider', {})] +[2025-10-23 23:57:22,946] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] DirectML 가속 시도: [('DmlExecutionProvider', {}), ('CPUExecutionProvider', {})] +[2025-10-23 23:57:22,946] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] DirectML 가속 성공! 실제 providers: ['DmlExecutionProvider', 'CPUExecutionProvider'] +[2025-10-23 23:57:22,946] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 입력 0: image, 형태: ['batch_size', 3, 'height', 'width'], 타입: tensor(uint8) +[2025-10-23 23:57:22,946] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 입력 1: mask, 형태: ['batch_size', 1, 'height', 'width'], 타입: tensor(uint8) +[2025-10-23 23:57:22,947] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 출력 0: result, 형태: ['ScatterNDresult_dim_0', 3, 'ScatterNDresult_dim_2', 'ScatterNDresult_dim_3'], 타입: tensor(uint8) +[2025-10-23 23:57:22,947] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 세션 준비 완료. providers=['DmlExecutionProvider', 'CPUExecutionProvider'] +[2025-10-23 23:57:22,947] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] GPU 관리자 연결 완료: GPUManager +[2025-10-23 23:57:22,947] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 파이프라인 gpu_manager 속성: True, 값: +[2025-10-23 23:57:22,947] [LogListener] [DEBUG] [loggerModule.py:debug:275] [MIGAN] 초기화 완료: gpu_manager 속성=True, 값= +[2025-10-23 23:57:22,947] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔰 OCR 모듈 Warm-up 시작... +[2025-10-23 23:57:22,947] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔍 ONNX OCR 감지 방식: polygon +[2025-10-23 23:57:22,951] [LogListener] [DEBUG] [loggerModule.py:debug:275] ⚡ ONNX OCR 추론 완료: 4.0ms +[2025-10-23 23:57:22,952] [LogListener] [DEBUG] [loggerModule.py:debug:275] 📊 세부 시간 - 감지: 3.0ms, 인식: 0.0ms, 분류: 0.0ms +[2025-10-23 23:57:22,952] [LogListener] [WARNING] [loggerModule.py:warning:287] ⚠️ ONNX OCR 결과가 비어있습니다 +[2025-10-23 23:57:22,955] [LogListener] [WARNING] [loggerModule.py:warning:287] ⚠️ OCR 모듈 Warm-up 실패: OpenCV(4.12.0) D:\a\opencv-python\opencv-python\opencv\modules\highgui\src\window.cpp:1295: error: (-2:Unspecified error) The function is not implemented. Rebuild the library with Windows, GTK+ 2.x or Cocoa support. If you are on Ubuntu or Debian, install libgtk2.0-dev and pkg-config, then re-run cmake or configure script in function 'cvDestroyAllWindows' + +[2025-10-23 23:57:22,956] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔰 ImageProcessor Warm‑up 완료 +[2025-10-23 23:57:22,956] [ResultListener] [INFO] [loggerModule.py:info:281] 워커 READY 수신 +[2025-10-23 23:57:22,956] [ResultListener] [INFO] [loggerModule.py:info:281] 워커 READY 수신 +[2025-10-23 23:57:22,956] [LogListener] [DEBUG] [loggerModule.py:debug:275] 📡 추가 READY 신호 전송 완료 +[2025-10-23 23:57:22,957] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-23 23:57:22,957] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 28588) +[2025-10-23 23:57:22,957] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🔥 작업 수신 성공 +[2025-10-23 23:57:22,957] [LogListener] [DEBUG] [loggerModule.py:debug:275] 🚀 작업 처리 시작: cmd=__PING__, uid=9efb3da2-d277-4cc2-a676-2e1d9bc46efb +[2025-10-23 23:57:22,957] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 실행 직전: cmd=__PING__ +[2025-10-23 23:57:22,957] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 결과 반환 중: uid=9efb3da2-d277-4cc2-a676-2e1d9bc46efb +[2025-10-23 23:57:22,957] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 결과 반환 완료: uid=9efb3da2-d277-4cc2-a676-2e1d9bc46efb +[2025-10-23 23:57:22,958] [LogListener] [DEBUG] [loggerModule.py:debug:275] 작업 대기 중... +[2025-10-23 23:57:22,958] [LogListener] [DEBUG] [loggerModule.py:debug:275] 큐에서 작업 대기 중... (PID: 28588) +[2025-10-23 23:57:30,367] [MainThread] [INFO] [loggerModule.py:info:281] API 서버 종료 +[2025-10-23 23:57:30,777] [MainThread] [INFO] [loggerModule.py:info:281] 워커 프로세스 종료 diff --git a/main.py b/main.py new file mode 100644 index 0000000..384b6b7 --- /dev/null +++ b/main.py @@ -0,0 +1,1429 @@ +import os +import sys +import uuid +import time +import json +import signal +import atexit +import argparse +import shutil +import subprocess +import zipfile +import tempfile +import urllib.request +import ctypes +import socket +import queue as _queue +import threading +import multiprocessing as mp +from typing import Optional, Dict, Any +from contextlib import asynccontextmanager +from dotenv import load_dotenv +import psutil + +from fastapi import FastAPI, HTTPException +from fastapi import status as http_status +from pydantic import BaseModel, Field + +from loggerModule import Logger +from modules.image_worker import worker_main + + + +def _get_base_dir(): + """ + 실행 환경별 리소스 베이스 디렉터리 자동 탐지. + 우선순위: + 1) 환경변수 IMGWK_BASE_DIR + 2) (패키징) /lib/modules → /modules → + 3) (개발) /modules → /lib/modules → + 위 후보 중 실제 리소스(onnx_ocr_module/models, migan_onnx, rembg_models, fonts)가 존재하는 첫 경로를 선택. + """ + try: + env_base = os.environ.get('IMGWK_BASE_DIR') + if env_base and os.path.isdir(env_base): + return env_base + except Exception: + pass + + is_frozen = getattr(sys, 'frozen', False) + curr_dir = os.path.abspath(os.path.dirname(__file__)) + exe_dir = os.path.dirname(sys.executable) if is_frozen else curr_dir + + candidates = [] + if is_frozen: + candidates = [ + os.path.join(exe_dir, 'lib', 'modules'), + os.path.join(exe_dir, 'modules'), + exe_dir, + ] + else: + candidates = [ + os.path.join(curr_dir, 'modules'), + os.path.join(curr_dir, 'lib', 'modules'), + curr_dir, + ] + + def has_resources(base: str) -> bool: + try: + return ( + os.path.isdir(os.path.join(base, 'onnx_ocr_module', 'models')) or + os.path.isdir(os.path.join(base, 'migan_onnx')) or + os.path.isdir(os.path.join(base, 'rembg_models')) or + os.path.isdir(os.path.join(base, 'fonts')) + ) + except Exception: + return False + + for cand in candidates: + if os.path.isdir(cand) and has_resources(cand): + return cand + + # 마지막 폴백: 첫 후보 반환 + return candidates[0] if candidates else exe_dir + + +def _should_start_tray() -> bool: + try: + # 환경변수로 강제 비활성화 가능 + flag = (os.environ.get("IMGWK_NO_TRAY", "0") or "0").strip().lower() + if flag in ("1", "true", "yes", "on"): + return False + # 서비스로 실행 중이면 트레이 금지 + if any(arg == "--service" for arg in sys.argv): + return False + # 윈도우에서만 기본 활성화 + if os.name != "nt": + return False + return True + except Exception: + return False + + +# ------------------------------------------------------------ +# 프로젝트/경로 설정 +# ------------------------------------------------------------ +if getattr(sys, "frozen", False): + # cx_Freeze 등으로 패키징된 실행 파일 위치 + ROOT_DIR = os.path.dirname(sys.executable) +else: + ROOT_DIR = os.path.abspath(os.path.dirname(__file__)) +load_dotenv(os.path.join(ROOT_DIR, ".env")) +# BASE_DIR = os.path.join(ROOT_DIR, "modules") # ImageProcessor3에서 base_dir로 활용 +BASE_DIR = _get_base_dir() + +# ProgramData 하위로 표준화된 작업/출력 경로 구성 +_PROGRAMDATA = os.environ.get("PROGRAMDATA") or os.path.join(os.environ.get("ALLUSERSPROFILE", "C:\\ProgramData")) +APP_DATA_DIR = os.path.join(_PROGRAMDATA, "ImgWorker") +TEMP_DIR = os.path.join(APP_DATA_DIR, "work") # 작업/임시 파일 +OUTPUT_DIR = os.path.join(APP_DATA_DIR, "outputs") # 최종 산출물(필요 시 사용) +os.makedirs(TEMP_DIR, exist_ok=True) +os.makedirs(OUTPUT_DIR, exist_ok=True) + +LOGS_DIR = os.path.join(APP_DATA_DIR, "logs") +os.makedirs(LOGS_DIR, exist_ok=True) + +# 서버 디스커버리/단일 인스턴스 관리 경로 +SERVER_INFO_PATH = os.path.join(APP_DATA_DIR, "server.json") +PID_FILE_PATH = os.path.join(APP_DATA_DIR, "imgworker.pid") + + +# ------------------------------------------------------------ +# 로거 초기화 +# ------------------------------------------------------------ +logger = Logger(log_file=os.path.join(LOGS_DIR, "api_server.log"), logger_name="img_worker_api") + +# ------------------------------------------------------------ +# 단일 인스턴스/포트 유틸 +# ------------------------------------------------------------ +_MUTEX_HANDLE = None +_SERVER_BIND = {"host": "127.0.0.1", "port": 8009} +_SERVICE_NAME = "ImgWorker" + +def _ensure_single_instance() -> bool: + """Windows 전역 뮤텍스로 단일 인스턴스 보장. 이미 실행 중이면 False.""" + try: + if os.name != "nt": + return True # 윈도우 외 OS는 건너뜀 + global _MUTEX_HANDLE + # Global 네임스페이스 사용(서비스/세션 간 공유) + name = "Global\\ImgWorkerSingletonMutex" + CreateMutex = ctypes.windll.kernel32.CreateMutexW + GetLastError = ctypes.windll.kernel32.GetLastError + _MUTEX_HANDLE = CreateMutex(None, False, name) + if not _MUTEX_HANDLE: + return True # 뮤텍스 생성 실패 시 강행 + ERROR_ALREADY_EXISTS = 183 + already = (GetLastError() == ERROR_ALREADY_EXISTS) + return not already + except Exception: + return True + +def _release_single_instance(): + try: + if os.name == "nt" and _MUTEX_HANDLE: + ctypes.windll.kernel32.CloseHandle(_MUTEX_HANDLE) + except Exception: + pass + +def _is_admin() -> bool: + try: + if os.name != "nt": + return True + return bool(ctypes.windll.shell32.IsUserAnAdmin()) + except Exception: + return True + +def _rerun_as_admin(extra_args: Optional[str] = None): + """관리자 권한으로 현재 스크립트를 재실행(UAC)""" + if os.name != "nt": + return + try: + params = "\"" + os.path.abspath(__file__) + "\"" + if extra_args: + params += " " + extra_args + ctypes.windll.shell32.ShellExecuteW(None, "runas", sys.executable, params, None, 1) + except Exception: + pass + +def _is_port_in_use(host: str, port: int) -> bool: + try: + with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: + s.settimeout(0.3) + return s.connect_ex((host, port)) == 0 + except Exception: + return False + +def _find_free_port(start: int, end: int, host: str = "127.0.0.1") -> int: + start = int(start) + end = int(end) + for p in range(start, max(start, end) + 1): + try: + with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: + s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) + s.bind((host, p)) + # 바인드 가능하면 사용 가능. 즉시 해제 후 그 포트 사용 + return p + except Exception: + continue + # 모두 실패 시 0(커널 할당) 사용 시도 + return 0 + +def _write_server_info(host: str, port: int): + try: + info = { + "host": host, + "port": port, + "base": f"http://{host}:{port}", + "pid": os.getpid(), + "started_at": time.time(), + } + with open(SERVER_INFO_PATH, "w", encoding="utf-8") as f: + json.dump(info, f, ensure_ascii=False, indent=2) + except Exception: + pass + +def _write_pid_file(): + try: + with open(PID_FILE_PATH, "w", encoding="utf-8") as f: + f.write(str(os.getpid())) + except Exception: + pass + +def _cleanup_runtime_files(): + try: + if os.path.isfile(SERVER_INFO_PATH): + os.remove(SERVER_INFO_PATH) + except Exception: + pass + + +def _safe_rmtree_contents(target_dir: str): + try: + if not target_dir: + return + # 안전장치: ProgramData/ImgWorker 하위만 허용 + base = os.path.abspath(APP_DATA_DIR) + td = os.path.abspath(target_dir) + try: + if os.path.commonpath([base, td]) != base: + return + except Exception: + return + os.makedirs(td, exist_ok=True) + for name in os.listdir(td): + p = os.path.join(td, name) + try: + if os.path.isdir(p): + shutil.rmtree(p, ignore_errors=True) + else: + os.remove(p) + except Exception: + pass + except Exception: + pass + + +def _purge_temp_dirs(): + """ProgramData/ImgWorker 하위 임시 폴더 정리(incoming, work, output(s)).""" + try: + incoming_dir = os.path.join(APP_DATA_DIR, "incoming") + work_dir = os.path.join(APP_DATA_DIR, "work") + output_dir = os.path.join(APP_DATA_DIR, "output") + outputs_dir = os.path.join(APP_DATA_DIR, "outputs") + for d in (incoming_dir, work_dir, output_dir, outputs_dir): + _safe_rmtree_contents(d) + except Exception: + pass + +# ---------------------------- 서비스/NSSM 유틸 ---------------------------- +def _run_cmd(cmd: list, check: bool = False) -> subprocess.CompletedProcess: + return subprocess.run(cmd, capture_output=True, text=True, check=check, shell=False) + +def _service_exists(name: str) -> bool: + try: + r = _run_cmd(["sc", "query", name]) + return r.returncode == 0 and ("STATE" in (r.stdout or "") or (r.stderr or "") == "") + except Exception: + return False + +def _build_service_bin_path(host: str, port: int) -> str: + """서비스용 binPath 문자열 구성. 패키징/스크립트 실행 모두 대응.""" + # 서비스 실행 시에도 동일한 인자를 통해 호스트/포트를 고정한다 + if getattr(sys, "frozen", False): + exe = sys.executable + return f'"{exe}" --service --host "{host}" --port {int(port)}' + else: + py = sys.executable + entry = os.path.abspath(__file__) + return f'"{py}" "{entry}" --service --host "{host}" --port {int(port)}' + +def _install_service_via_sc(name: str): + # 설치 요청 시 전달받은 환경변수(스크립트에서 설정)를 영구화하기 위해 binPath 인자로 반영 + env_host = os.environ.get("IMGWK_HOST", "127.0.0.1") + env_port = int(os.environ.get("IMGWK_PORT", "8009")) + bin_path = _build_service_bin_path(env_host, env_port) + # create service + _run_cmd(["sc", "create", name, "binPath=", bin_path, "start=", "delayed-auto"]) + # failure actions: restart after 60s + _run_cmd(["sc", "failure", name, "reset=", "0", "actions=", "restart/60000"]) + _run_cmd(["sc", "description", name, "Image Worker API"]) + _run_cmd(["sc", "start", name]) + +def _uninstall_service(name: str): + if _service_exists(name): + try: + _run_cmd(["sc", "stop", name]) + _run_cmd(["sc", "delete", name]) + except Exception: + pass + +def _install_service(): + if os.name != "nt": + print("윈도우가 아닌 환경에서는 서비스 설치를 지원하지 않습니다.") + return 1 + if not _is_admin(): + # 관리자 권한으로 재실행 + _rerun_as_admin("--install-service") + return 0 + if _service_exists(_SERVICE_NAME): + # 기존 있으면 우선 제거 후 재설치(설정 반영 목적) + _uninstall_service(_SERVICE_NAME) + try: + _install_service_via_sc(_SERVICE_NAME) + print("ImgWorker 서비스 설치/시작 완료") + return 0 + except Exception as e: + print(f"서비스 설치 실패: {e}") + return 1 + +def _remove_service(): + if os.name != "nt": + print("윈도우가 아닌 환경에서는 서비스 제거를 지원하지 않습니다.") + return 1 + if not _is_admin(): + _rerun_as_admin("--uninstall-service") + return 0 + try: + _uninstall_service(_SERVICE_NAME) + print("ImgWorker 서비스 제거 완료") + return 0 + except Exception as e: + print(f"서비스 제거 실패: {e}") + return 1 + try: + if os.path.isfile(PID_FILE_PATH): + os.remove(PID_FILE_PATH) + except Exception: + pass + + +# ------------------------------------------------------------ +# 잡/상태 모델 +# ------------------------------------------------------------ +class ProcessImageRequest(BaseModel): + file_path: str = Field(..., description="로컬 이미지 경로") + index: int = Field(0, description="이미지 인덱스(표시/로깅용)") + file_prefix: Optional[str] = Field("", description="detail|option|thumb 등 접두 구분") + toggle_overrides: Optional[Dict[str, Any]] = Field(None, description="처리 시 일시적 토글 오버라이드") + group_id: Optional[str] = Field(None, description="클라이언트 측 그룹 식별자") + seq: Optional[int] = Field(None, description="클라이언트 측 순번") + ocr: Optional[bool] = Field(None, description="OCR 전용 모드 플래그(True면 OCR+인페인팅, False면 전체 번역)") + + +class RemoveBackgroundRequest(BaseModel): + file_path: str = Field(..., description="로컬 이미지 경로") + file_prefix: Optional[str] = Field("", description="detail|option|thumb 등 접두 구분") + toggle_overrides: Optional[Dict[str, Any]] = Field(None, description="처리 시 일시적 토글 오버라이드") + + +class JobInfo(BaseModel): + job_id: str + status: str # queued|done|error + created_at: float + updated_at: float + kind: str # process_image|remove_background|ping + request: Dict[str, Any] + result: Optional[Dict[str, Any]] = None + error: Optional[str] = None + gen: Optional[int] = None # 워커 세대(롤링 구분) + timeout_at: Optional[float] = None # 잡 타임아웃 시각 + + +# ------------------------------------------------------------ +# 워커 관리자 +# ------------------------------------------------------------ +class WorkerManager: + def __init__(self, base_dir: str, temp_dir: str, logger: Logger): + self.base_dir = base_dir + self.temp_dir = temp_dir + self.logger = logger + + self._ctx = mp.get_context("spawn") + self._task_q: mp.Queue = self._ctx.Queue() + self._result_q: mp.Queue = self._ctx.Queue() + self._log_q: mp.Queue = self._ctx.Queue() + + self._proc: Optional[mp.Process] = None + self._running = threading.Event() + self._ready = threading.Event() + + # 잡 관리 + self._jobs_lock = threading.Lock() + self._jobs: Dict[str, JobInfo] = {} + + # 리스너 + self._result_thread: Optional[threading.Thread] = None + self._log_thread: Optional[threading.Thread] = None + self._job_watch_thread: Optional[threading.Thread] = None + + # 롤링/버퍼링 + self._roll_lock = threading.Lock() + self._rolling = False + self._buffered_tasks = [] # 롤링 중 신규 작업 임시 보관 + + # 백프레셔(대기열 제한) + self._max_pending_jobs = int(os.environ.get("IMGWK_MAX_PENDING", "200")) + + # 롤링 임계치 + self.ROLL_MAX_RSS_MB = int(os.environ.get("IMGWK_ROLL_MAX_RSS_MB", "1800")) + self.ROLL_MAX_JOBS = int(os.environ.get("IMGWK_ROLL_MAX_JOBS", "500")) + self.ROLL_MAX_UPTIME_SEC = int(os.environ.get("IMGWK_ROLL_MAX_UPTIME_SEC", str(2*60*60))) + if os.environ.get("IMGWK_TEST_ROLL_EVERY_2", "0") == "1": + self.ROLL_MAX_JOBS = 2 + self.JOB_TIMEOUT_SEC = int(os.environ.get("IMGWK_JOB_TIMEOUT_SEC", "600")) + + # 워커 세대(롤링 구분) + self._generation = 0 + + self._jobs_processed_in_current_worker = 0 + self._worker_started_at = 0.0 + self._mem_thread: Optional[threading.Thread] = None + # 실행 상태/성능 지표 + self._running_jobs = 0 + self._recent_total_ms = [] # 최근 작업 total_ms 집계 + + # 기본 토글/설정(필요 시 요청에서 오버라이드) + self._toggle_states: Dict[str, Any] = { + "TEMP_IMAGE_DIR": self.temp_dir, + "output_image_format": "webp", + # 배경제거: 자체(rembg) 기본값 사용 + "use_local_rembg": True, + "local_rembg_model_path": os.path.join(self.base_dir, "rembg_models"), + "local_model_name": "birefnet-general-lite", + # 인페인트: MIGAN 기본값(GPU → DirectML/CPU 폴백) + "optionIMGTrans_type": "GPU", + "detail_IMGTrans_type": "GPU", + "thumb_trans_type": "GPU", + "migan_use_accel": True, + "migan_onnx_path": os.path.join(self.base_dir, "migan_onnx", "migan_pipeline_v2.onnx"), + # 프로바이더 강제 설정( auto|dml|cpu ) + "ocr_provider_override": os.environ.get("IMGWK_OCR_PROVIDER", "auto"), + "migan_provider_override": os.environ.get("IMGWK_MIGAN_PROVIDER", "auto"), + "rembg_provider_override": os.environ.get("IMGWK_REMBG_PROVIDER", "auto"), + } + + # 전역 CPU 강제 플래그(1|true|yes|on) → 모든 가속/프로바이더를 CPU로 고정 + force_cpu_flag = os.environ.get("IMGWK_FORCE_CPU", "0").strip().lower() + if force_cpu_flag in ("1", "true", "yes", "on"): + self._toggle_states["optionIMGTrans_type"] = "CPU" + self._toggle_states["detail_IMGTrans_type"] = "CPU" + self._toggle_states["thumb_trans_type"] = "CPU" + self._toggle_states["migan_use_accel"] = False + self._toggle_states["ocr_provider_override"] = "cpu" + self._toggle_states["migan_provider_override"] = "cpu" + self._toggle_states["rembg_provider_override"] = "cpu" + # 호환용(use_cuda) 키도 함께 내려 CPU 강제 사실을 명시 + self._toggle_states["use_cuda"] = False + + # 텍스트 치환 규칙(없으면 빈 dict) + self._unwanted_words: Dict[str, str] = {} + + @property + def is_ready(self) -> bool: + return self._ready.is_set() and self._proc is not None and self._proc.is_alive() + + @property + def pid(self) -> Optional[int]: + return self._proc.pid if self._proc is not None else None + + def start(self): + if self._proc is not None and self._proc.is_alive(): + return + + self._running.set() + log_path = os.path.join(LOGS_DIR, "worker.log") + + self._generation += 1 + self._proc = self._ctx.Process( + target=worker_main, + args=( + self._task_q, + self._result_q, + self._log_q, + log_path, + self.base_dir, + self._toggle_states, + self._unwanted_words, + False, # authenticated_by_admin + ), + name="ImageWorkerProcess", + daemon=True, + ) + self._proc.start() + self.logger.info(f"워커 프로세스 기동: PID={self._proc.pid}") + + # 리스너 스레드 시작 + if self._result_thread is None or not self._result_thread.is_alive(): + self._result_thread = threading.Thread(target=self._result_listener, name="ResultListener", daemon=True) + self._result_thread.start() + + if self._log_thread is None or not self._log_thread.is_alive(): + self._log_thread = threading.Thread(target=self._log_listener, name="LogListener", daemon=True) + self._log_thread.start() + + # 워커 READY 대기(기본 180초, 환경변수로 조정) + try: + wait_sec = int(os.environ.get("IMGWK_WORKER_READY_TIMEOUT_SEC", "120")) + except Exception: + wait_sec = 180 + if not self._ready.wait(timeout=max(1, wait_sec)): + self.logger.warning("워커 READY 타임아웃") + + # 메모리/업타임 모니터 시작 + self._worker_started_at = time.time() + if self._mem_thread is None or not self._mem_thread.is_alive(): + self._mem_thread = threading.Thread(target=self._memory_monitor, name="MemMonitor", daemon=True) + self._mem_thread.start() + + if self._job_watch_thread is None or not self._job_watch_thread.is_alive(): + self._job_watch_thread = threading.Thread(target=self._job_watchdog, name="JobWatchdog", daemon=True) + self._job_watch_thread_start_at = time.time() + self._job_watch_thread.start() + + def stop(self): + try: + if self._proc is None: + return + self._running.clear() + # 워커에게 종료 신호(None) + try: + self._task_q.put(None, timeout=1) + except Exception: + pass + # 프로세스 종료 대기 + self._proc.join(timeout=10) + if self._proc.is_alive(): + try: + os.kill(self._proc.pid, signal.SIGTERM) + except Exception: + pass + self.logger.info("워커 프로세스 종료") + finally: + self._proc = None + self._ready.clear() + + def _result_listener(self): + while self._running.is_set(): + try: + msg = self._result_q.get(timeout=1) + except _queue.Empty: + continue + except Exception: + continue + + if not isinstance(msg, dict): + continue + + msg_id = msg.get("id") + + # READY 시그널 + if msg_id == "__READY__": + self._ready.set() + self.logger.info("워커 READY 수신") + continue + + # 일반 잡 결과 + with self._jobs_lock: + job = self._jobs.get(msg_id) + if job is None: + continue + # 이미 최종 상태면(에러/취소/완료) 늦은 결과는 무시 + if job.status in ("error", "done", "cancelled"): + continue + job.updated_at = time.time() + + if "error" in msg and msg["error"]: + job.status = "error" + job.error = msg.get("error") + else: + job.status = "done" + job.result = msg.get("data") if isinstance(msg.get("data"), dict) else {"data": msg.get("data")} + self._jobs[msg_id] = job + + # 잡 처리 카운트 및 롤링 조건 검사(ping 제외) + try: + if job and job.kind not in ("ping",): + self._jobs_processed_in_current_worker += 1 + if self._jobs_processed_in_current_worker >= self.ROLL_MAX_JOBS: + self._schedule_roll("job-count-threshold") + # running count 감소 및 성능 집계 + if job and job.kind not in ("ping",): + try: + if self._running_jobs > 0: + self._running_jobs -= 1 + except Exception: + self._running_jobs = 0 + try: + timings = (job.result or {}).get("timings") if isinstance(job.result, dict) else None + total_ms = float(timings.get("total_ms", 0.0)) if isinstance(timings, dict) else 0.0 + if total_ms > 0: + self._recent_total_ms.append(total_ms) + if len(self._recent_total_ms) > 100: + self._recent_total_ms = self._recent_total_ms[-100:] + except Exception: + pass + except Exception: + pass + + def _log_listener(self): + # 워커에서 넘어온 로그를 api 로거로 전달 + level_map = { + 10: "DEBUG", + 20: "INFO", + 30: "WARNING", + 40: "ERROR", + 50: "CRITICAL", + } + while self._running.is_set(): + try: + rec = self._log_q.get(timeout=1) + except _queue.Empty: + continue + except Exception: + continue + + if not isinstance(rec, dict): + continue + level = rec.get("level", 20) + message = rec.get("message", "") + # loggerModule의 표준 인터페이스 사용 + if level >= 50: + self.logger.critical(message) + elif level >= 40: + self.logger.error(message) + elif level >= 30: + self.logger.warning(message) + elif level >= 10: + self.logger.debug(message) + else: + self.logger.info(message) + + # -------------------------- 잡 제출 API -------------------------- + def _register_job(self, kind: str, req: Dict[str, Any]) -> str: + job_id = str(uuid.uuid4()) + now = time.time() + now = time.time() + info = JobInfo( + job_id=job_id, + status="queued", + created_at=now, + updated_at=now, + kind=kind, + request=req, + gen=self._generation, + timeout_at=(now + self.JOB_TIMEOUT_SEC) if self.JOB_TIMEOUT_SEC > 0 else None, + ) + with self._jobs_lock: + self._jobs[job_id] = info + return job_id + + def get_job(self, job_id: str) -> Optional[JobInfo]: + with self._jobs_lock: + return self._jobs.get(job_id) + + def cancel_job(self, job_id: str) -> bool: + # 단순 구현: 큐에서 제거는 구현 난이도가 있으므로, 아직 시작 안한 잡만 취소 플래그 처리 + with self._jobs_lock: + job = self._jobs.get(job_id) + if not job: + return False + if job.status != "queued": + return False + job.status = "cancelled" + job.error = "cancelled" + job.updated_at = time.time() + self._jobs[job_id] = job + return True + + def pending_jobs_count(self) -> int: + with self._jobs_lock: + return sum(1 for j in self._jobs.values() if j.status == "queued") + + def _enqueue_or_buffer(self, task: Dict[str, Any]): + # 롤링 중이면 버퍼, 아니면 즉시 큐에 투입 + job_id = task.get("id") + if self._rolling: + self._buffered_tasks.append(task) + else: + try: + self._task_q.put(task) + # 상태를 running 으로 변경 + if job_id: + with self._jobs_lock: + job = self._jobs.get(job_id) + if job and job.status == "queued": + job.status = "running" + job.updated_at = time.time() + self._jobs[job_id] = job + try: + self._running_jobs += 1 + except Exception: + self._running_jobs = 1 + except Exception: + if job_id: + with self._jobs_lock: + job = self._jobs.get(job_id) + if job: + job.status = "error" + job.error = "enqueue-failed" + job.updated_at = time.time() + self._jobs[job_id] = job + + def _is_http(self, path: str) -> bool: + try: + return isinstance(path, str) and (path.startswith("http://") or path.startswith("https://")) + except Exception: + return False + + def _validate_file_readable(self, path: str): + if self._is_http(path): + return # 원격 URL은 로컬 파일 검증 생략 + if not os.path.isfile(path): + raise FileNotFoundError(f"파일을 찾을 수 없습니다: {path}") + if not os.access(path, os.R_OK): + raise PermissionError(f"파일 읽기 권한이 없습니다: {path}") + + def submit_process_image(self, *, file_path: str, index: int, file_prefix: str = "", toggle_overrides: Optional[Dict[str, Any]] = None, group_id: Optional[str] = None, seq: Optional[int] = None) -> str: + self._validate_file_readable(file_path) + if self.pending_jobs_count() >= self._max_pending_jobs: + raise RuntimeError("queue-full") + + req = { + "file_path": file_path, + "index": index, + "file_prefix": file_prefix, + "group_id": group_id, + "seq": seq, + "toggle_overrides": toggle_overrides or {}, + } + job_id = self._register_job("process_image", req) + + kwargs = { + "original_image_url": file_path, + "index": index, + "delay": 0.0, + "file_prefix": file_prefix or "", + "_toggle_states": self._merge_toggle_overrides(toggle_overrides), + } + task = {"id": job_id, "cmd": "process_single_image", "kwargs": kwargs} + self._enqueue_or_buffer(task) + return job_id + + def submit_remove_background(self, *, file_path: str, file_prefix: str = "", toggle_overrides: Optional[Dict[str, Any]] = None) -> str: + self._validate_file_readable(file_path) + if self.pending_jobs_count() >= self._max_pending_jobs: + raise RuntimeError("queue-full") + + req = { + "file_path": file_path, + "file_prefix": file_prefix, + "toggle_overrides": toggle_overrides or {}, + } + job_id = self._register_job("remove_background", req) + + kwargs = { + "original_image_url": file_path, + "file_prefix": file_prefix or "", + "_toggle_states": self._merge_toggle_overrides(toggle_overrides), + } + task = {"id": job_id, "cmd": "remove_background", "kwargs": kwargs} + self._enqueue_or_buffer(task) + return job_id + + def ping(self, timeout: float = 5.0) -> bool: + job_id = str(uuid.uuid4()) + with self._jobs_lock: + now = time.time() + self._jobs[job_id] = JobInfo( + job_id=job_id, + status="queued", + created_at=now, + updated_at=now, + kind="ping", + request={}, + ) + self._task_q.put({"id": job_id, "cmd": "__PING__", "kwargs": {}}) + + # 간단한 동기 확인(폴링) + end = time.time() + timeout + while time.time() < end: + info = self.get_job(job_id) + if info and info.status in ("done", "error"): + return info.status == "done" and ((info.result or {}).get("data") == "__PONG__" or (info.result or {}).get("data") == "__PONG__") + time.sleep(0.05) + return False + + def _merge_toggle_overrides(self, overrides: Optional[Dict[str, Any]]) -> Dict[str, Any]: + merged = dict(self._toggle_states) + if overrides: + merged.update(overrides) + # 경로 보정 + merged["TEMP_IMAGE_DIR"] = self.temp_dir + os.makedirs(merged["TEMP_IMAGE_DIR"], exist_ok=True) + return merged + + # -------------------------- 제어 작업(동기) -------------------------- + def reinit_ocr(self, provider_override: Optional[str] = None, timeout: float = 10.0) -> bool: + if provider_override: + self._toggle_states["ocr_provider_override"] = provider_override + job_id = str(uuid.uuid4()) + with self._jobs_lock: + now = time.time() + self._jobs[job_id] = JobInfo( + job_id=job_id, + status="queued", + created_at=now, + updated_at=now, + kind="reinit_ocr", + request={"provider_override": provider_override}, + gen=self._generation, + timeout_at=now + 15, + ) + task = {"id": job_id, "cmd": "reinit_ocr", "kwargs": {"_toggle_states": self._toggle_states}} + self._enqueue_or_buffer(task) + end = time.time() + timeout + while time.time() < end: + info = self.get_job(job_id) + if info and info.status in ("done", "error"): + return bool((info.result or {}).get("ok", False)) and info.status == "done" + time.sleep(0.05) + return False + + # REMBG 재초기화/재설정 (호환 목적: 동일 시맨틱 유지) + def reinit_rembg(self, provider: Optional[str] = None, timeout: float = 10.0) -> bool: + if provider: + self._toggle_states["rembg_provider_override"] = provider + job_id = str(uuid.uuid4()) + with self._jobs_lock: + now = time.time() + self._jobs[job_id] = JobInfo( + job_id=job_id, + status="queued", + created_at=now, + updated_at=now, + kind="reinit_rembg", + request={"provider": provider}, + gen=self._generation, + timeout_at=now + 15, + ) + task = {"id": job_id, "cmd": "reinit_rembg", "kwargs": {"_toggle_states": self._toggle_states, "provider": provider}} + self._enqueue_or_buffer(task) + end = time.time() + timeout + while time.time() < end: + info = self.get_job(job_id) + if info and info.status in ("done", "error"): + return bool((info.result or {}).get("ok", False)) and info.status == "done" + time.sleep(0.05) + return False + + def reset_rembg(self, provider: Optional[str] = None, timeout: float = 10.0) -> bool: + # 현재 구현은 reinit과 동일 동작(세션 재생성). 향후 차별화 가능 + return self.reinit_rembg(provider=provider, timeout=timeout) + + def reset_migan(self, use_cuda: Optional[bool] = None, provider: Optional[str] = None, timeout: float = 10.0) -> bool: + if use_cuda is not None: + self._toggle_states["migan_use_accel"] = bool(use_cuda) + if provider: + self._toggle_states["migan_provider_override"] = provider + job_id = str(uuid.uuid4()) + with self._jobs_lock: + now = time.time() + self._jobs[job_id] = JobInfo( + job_id=job_id, + status="queued", + created_at=now, + updated_at=now, + kind="reset_migan", + request={"use_cuda": use_cuda, "provider": provider}, + gen=self._generation, + timeout_at=now + 15, + ) + task = {"id": job_id, "cmd": "reset_migan", "kwargs": {"_toggle_states": self._toggle_states, "use_cuda": use_cuda, "provider": provider}} + self._enqueue_or_buffer(task) + end = time.time() + timeout + while time.time() < end: + info = self.get_job(job_id) + if info and info.status in ("done", "error"): + return bool((info.result or {}).get("ok", False)) and info.status == "done" + time.sleep(0.05) + return False + + # -------------------------- 롤링 로직 -------------------------- + def _memory_monitor(self): + # 주기적으로 RSS/업타임 확인 + while self._running.is_set(): + try: + if self._proc is None: + time.sleep(1.0) + continue + pid = self._proc.pid + if not pid: + time.sleep(1.0) + continue + rss_mb = 0 + try: + p = psutil.Process(pid) + rss_mb = int(p.memory_info().rss / (1024 * 1024)) + except Exception: + pass + + if rss_mb >= self.ROLL_MAX_RSS_MB: + self._schedule_roll("memory-threshold") + + uptime = time.time() - self._worker_started_at + if uptime >= self.ROLL_MAX_UPTIME_SEC: + self._schedule_roll("uptime-threshold") + finally: + time.sleep(3.0) + + def _schedule_roll(self, reason: str): + with self._roll_lock: + if self._rolling: + return + self._rolling = True + self.logger.warning(f"워커 롤링 스케줄: reason={reason}") + t = threading.Thread(target=self._do_roll, args=(reason,), name="WorkerRoller", daemon=True) + t.start() + + def _do_roll(self, reason: str): + # 1) 기존 워커 종료 요청(None 전송) + try: + self.logger.warning(f"워커 롤링 시작: {reason}") + try: + self._task_q.put(None, timeout=1) + except Exception: + pass + # 2) 종료 대기 + if self._proc is not None: + self._proc.join(timeout=30) + if self._proc.is_alive(): + try: + os.kill(self._proc.pid, signal.SIGTERM) + except Exception: + pass + except Exception as e: + self.logger.error(f"롤링 중 종료 단계 오류: {e}") + + # 3) 상태 초기화 + self._ready.clear() + self._jobs_processed_in_current_worker = 0 + + # 4) 새 워커 기동 + try: + self.start() + except Exception as e: + self.logger.error(f"롤링 중 새 워커 기동 실패: {e}") + # 실패 시 롤링 종료 플래그는 내려서 요청이 막히지 않게 한다 + self._rolling = False + return + + # 5) 버퍼링된 작업 플러시 + try: + if self._buffered_tasks: + self.logger.info(f"버퍼 작업 재개: {len(self._buffered_tasks)}건") + for task in self._buffered_tasks: + self._task_q.put(task) + finally: + self._buffered_tasks.clear() + self._rolling = False + + # -------------------------- 잡 타임아웃 -------------------------- + def _job_watchdog(self): + while self._running.is_set(): + now = time.time() + to_mark = [] + with self._jobs_lock: + for job in self._jobs.values(): + if job.status in ("done", "error", "cancelled"): + continue + if job.timeout_at and now >= job.timeout_at: + to_mark.append(job.job_id) + for job_id in to_mark: + with self._jobs_lock: + job = self._jobs.get(job_id) + if not job or job.status in ("done", "error", "cancelled"): + continue + job.status = "error" + job.error = "timeout" + job.updated_at = time.time() + self._jobs[job_id] = job + time.sleep(0.5) + + +# ------------------------------------------------------------ +# FastAPI 앱 +# ------------------------------------------------------------ +_worker: Optional[WorkerManager] = None + + +@asynccontextmanager +async def lifespan(app: FastAPI): + # startup + global _worker + logger.info("API 서버 시작") + # 임시 폴더 정리 + _purge_temp_dirs() + _worker = WorkerManager(base_dir=BASE_DIR, temp_dir=TEMP_DIR, logger=logger) + _worker.start() + # 헬스 핑 + ok = _worker.ping(timeout=10.0) + if not ok: + logger.warning("워커 핑 실패") + # 서버 디스커버리 파일 기록(uvicorn run에서 설정된 바인드 정보 사용) + try: + _write_server_info(_SERVER_BIND.get("host", "127.0.0.1"), int(_SERVER_BIND.get("port", 8009))) + except Exception: + pass + try: + yield + finally: + # shutdown + logger.info("API 서버 종료") + # 임시 폴더 정리 + _purge_temp_dirs() + try: + if _worker: + _worker.stop() + except Exception as e: + logger.error(f"워커 종료 중 오류: {e}") + # 런타임 파일 정리 + _cleanup_runtime_files() + + +app = FastAPI(title="Image Worker API", version="1.0.0", lifespan=lifespan) + + +@app.get("/health") +def health(): + ready = _worker.is_ready if _worker else False + return {"status": "ok" if ready else "starting", "ready": ready, "pid": _worker.pid if _worker else None} + + +@app.get("/info") +def info(): + return { + "app": "Image Worker API", + "version": app.version, + "cwd": os.getcwd(), + "base_dir": BASE_DIR, + "temp_dir": TEMP_DIR, + "outputs_dir": OUTPUT_DIR, + "worker_pid": _worker.pid if _worker else None, + "ready": _worker.is_ready if _worker else False, + } + + +@app.get("/v1/worker/status") +def worker_status(): + ready = _worker.is_ready if _worker else False + avg_sec = None + active = False + try: + if _worker is not None: + active = bool(getattr(_worker, "_running_jobs", 0) > 0) + arr = getattr(_worker, "_recent_total_ms", []) + if arr: + avg_sec = (sum(arr) / len(arr)) / 1000.0 + except Exception: + pass + return { + "ready": ready, + "pid": _worker.pid if _worker else None, + "active": active, + "avg_sec_per_image": avg_sec, + } + + +@app.post("/v1/worker/start") +def worker_start(): + global _worker + if _worker is None: + _worker = WorkerManager(base_dir=BASE_DIR, temp_dir=TEMP_DIR, logger=logger) + if not _worker.is_ready: + _worker.start() + ok = _worker.ping(timeout=10.0) + if not ok: + logger.warning("워커 핑 실패") + return {"ok": True, "ready": _worker.is_ready, "pid": _worker.pid} + + +@app.post("/v1/worker/stop") +def worker_stop(): + global _worker + if _worker is None: + return {"ok": True, "ready": False, "pid": None} + _worker.stop() + return {"ok": True, "ready": False, "pid": None} + + +@app.post("/v1/server/shutdown") +def server_shutdown(): + import threading + def _exit_later(): + try: + time.sleep(0.3) + os._exit(0) + except Exception: + os._exit(0) + threading.Thread(target=_exit_later, daemon=True).start() + return {"ok": True} + + +@app.post("/v1/process-image", status_code=http_status.HTTP_202_ACCEPTED) +def process_image(req: ProcessImageRequest): + # 워커가 초기화만 되어 있다면 READY 전이라도 잡을 큐/버퍼에 적재하여 202 반환 + if _worker is None: + raise HTTPException(status_code=503, detail="Worker not initialized") + + # 원격 URL 허용: 로컬 파일이 아니면 존재/권한 검사는 생략하고 워커에서 처리 + if not (req.file_path.startswith("http://") or req.file_path.startswith("https://")): + if not os.path.isfile(req.file_path): + raise HTTPException(status_code=400, detail="file_path not found") + + try: + # ocr 플래그가 별도로 전달되면 토글에 병합하여 전달 + _toggles = dict(req.toggle_overrides or {}) + if req.ocr is not None: + _toggles["ocr"] = bool(req.ocr) + + job_id = _worker.submit_process_image( + file_path=req.file_path, + index=req.index or (req.seq or 0), + file_prefix=req.file_prefix or "", + toggle_overrides=_toggles, + group_id=req.group_id, + seq=req.seq, + ) + return {"accepted": True, "job_id": job_id} + except RuntimeError as e: + if str(e) == "queue-full": + # 429 Too Many Requests 와 유사 동작 + raise HTTPException(status_code=429, detail="queue full, retry later") + raise + except FileNotFoundError as e: + raise HTTPException(status_code=400, detail=str(e)) + except Exception as e: + logger.exception("process-image 요청 처리 중 오류") + raise HTTPException(status_code=500, detail="internal error") + + +@app.post("/v1/remove-background", status_code=http_status.HTTP_202_ACCEPTED) +def remove_background(req: RemoveBackgroundRequest): + # 워커가 초기화만 되어 있다면 READY 전이라도 큐에 적재 + if _worker is None: + raise HTTPException(status_code=503, detail="Worker not initialized") + + if not (req.file_path.startswith("http://") or req.file_path.startswith("https://")): + if not os.path.isfile(req.file_path): + raise HTTPException(status_code=400, detail="file_path not found") + + try: + job_id = _worker.submit_remove_background( + file_path=req.file_path, + file_prefix=req.file_prefix or "", + toggle_overrides=req.toggle_overrides, + ) + return {"accepted": True, "job_id": job_id} + except RuntimeError as e: + if str(e) == "queue-full": + raise HTTPException(status_code=429, detail="queue full, retry later") + raise + except FileNotFoundError as e: + raise HTTPException(status_code=400, detail=str(e)) + except Exception as e: + logger.exception("remove-background 요청 처리 중 오류") + raise HTTPException(status_code=500, detail="internal error") + + +@app.get("/v1/jobs/{job_id}") +def get_job(job_id: str): + if _worker is None: + raise HTTPException(status_code=503, detail="Worker not ready") + info = _worker.get_job(job_id) + if info is None: + raise HTTPException(status_code=404, detail="job not found") + return json.loads(info.json()) + + +class ReinitOCRRequest(BaseModel): + provider: Optional[str] = Field(None, description="ocr provider override: auto|dml|cpu") + + +@app.post("/v1/ocr/reinit") +def reinit_ocr(req: ReinitOCRRequest): + if _worker is None or not _worker.is_ready: + raise HTTPException(status_code=503, detail="Worker not ready") + ok = _worker.reinit_ocr(provider_override=req.provider) + if not ok: + raise HTTPException(status_code=500, detail="reinit ocr failed") + return {"ok": True} + + +class ResetOCRRequest(BaseModel): + provider: Optional[str] = Field(None, description="ocr provider override: auto|dml|cpu") + + +@app.post("/v1/ocr/reset") +def reset_ocr(req: ResetOCRRequest): + if _worker is None or not _worker.is_ready: + raise HTTPException(status_code=503, detail="Worker not ready") + ok = _worker.reinit_ocr(provider_override=req.provider) + if not ok: + raise HTTPException(status_code=500, detail="reset ocr failed") + return {"ok": True} + + +class ResetMIGANRequest(BaseModel): + use_cuda: Optional[bool] = Field(None, description="DirectML 사용 여부(True=시도)") + provider: Optional[str] = Field(None, description="migan provider override: auto|dml|cpu") + + +@app.post("/v1/migan/reset") +def reset_migan(req: ResetMIGANRequest): + if _worker is None or not _worker.is_ready: + raise HTTPException(status_code=503, detail="Worker not ready") + ok = _worker.reset_migan(use_cuda=req.use_cuda, provider=req.provider) + if not ok: + raise HTTPException(status_code=500, detail="reset migan failed") + return {"ok": True} + + +@app.post("/v1/migan/reinit") +def reinit_migan(req: ResetMIGANRequest): + if _worker is None or not _worker.is_ready: + raise HTTPException(status_code=503, detail="Worker not ready") + ok = _worker.reset_migan(use_cuda=req.use_cuda, provider=req.provider) + if not ok: + raise HTTPException(status_code=500, detail="reinit migan failed") + return {"ok": True} + + +class ReinitREMBGRequest(BaseModel): + provider: Optional[str] = Field(None, description="rembg provider override: auto|dml|cpu") + + +@app.post("/v1/rembg/reinit") +def reinit_rembg(req: ReinitREMBGRequest): + if _worker is None or not _worker.is_ready: + raise HTTPException(status_code=503, detail="Worker not ready") + ok = _worker.reinit_rembg(provider=req.provider) + if not ok: + raise HTTPException(status_code=500, detail="reinit rembg failed") + return {"ok": True} + + +class ResetREMBGRequest(BaseModel): + provider: Optional[str] = Field(None, description="rembg provider override: auto|dml|cpu") + + +@app.post("/v1/rembg/reset") +def reset_rembg(req: ResetREMBGRequest): + if _worker is None or not _worker.is_ready: + raise HTTPException(status_code=503, detail="Worker not ready") + ok = _worker.reset_rembg(provider=req.provider) + if not ok: + raise HTTPException(status_code=500, detail="reset rembg failed") + return {"ok": True} + + +@app.delete("/v1/jobs/{job_id}", status_code=http_status.HTTP_202_ACCEPTED) +def cancel_job(job_id: str): + if _worker is None: + raise HTTPException(status_code=503, detail="Worker not ready") + ok = _worker.cancel_job(job_id) + if not ok: + raise HTTPException(status_code=409, detail="cannot cancel (already running or done)") + return {"cancelled": True, "job_id": job_id} + + +if __name__ == "__main__": + import uvicorn + # multiprocessing - frozen executable 지원 + try: + mp.freeze_support() + except Exception: + pass + # 명령행 인자 처리 + parser = argparse.ArgumentParser(description="Image Worker API") + parser.add_argument("--install-service", action="store_true", help="윈도우 서비스 설치") + parser.add_argument("--uninstall-service", action="store_true", help="윈도우 서비스 제거") + parser.add_argument("--ensure-service", action="store_true", help="서비스가 없으면 설치") + parser.add_argument("--console", action="store_true", help="디버그 콘솔 활성화(Win32GUI 빌드에서 유효)") + args, _ = parser.parse_known_args() + + if args.install_service: + sys.exit(_install_service()) + if args.uninstall_service: + sys.exit(_remove_service()) + if args.ensure_service: + # 있으면 그대로 성공, 없으면 설치 시도 + if _service_exists(_SERVICE_NAME): + print("ImgWorker 서비스가 이미 설치되어 있습니다.") + sys.exit(0) + sys.exit(_install_service()) + # 단일 인스턴스 보장(이미 실행 중이면 종료) + if not _ensure_single_instance(): + print("ImgWorker 이미 실행 중입니다. 기존 인스턴스를 사용하세요.") + sys.exit(0) + + # 종료 시 리소스 해제 + atexit.register(_release_single_instance) + atexit.register(_cleanup_runtime_files) + + # 바인드 호스트/포트 결정(포트 충돌 자동 회피) + bind_host = os.environ.get("IMGWK_HOST", "127.0.0.1") + desired_port = int(os.environ.get("IMGWK_PORT", "8009")) + start_port = int(os.environ.get("IMGWK_PORT_START", str(desired_port))) + end_port = int(os.environ.get("IMGWK_PORT_END", str(start_port + 20))) + + if _is_port_in_use(bind_host, desired_port): + selected_port = _find_free_port(start_port, end_port, bind_host) + else: + selected_port = desired_port + + # 바인드 정보 설정 및 PID/디스커버리 파일 기록 + _SERVER_BIND = {"host": bind_host, "port": selected_port} + _write_pid_file() + _write_server_info(bind_host, selected_port) + + # 콘솔 플래그 처리 + use_console = bool(getattr(args, "console", False)) + if use_console and os.name == "nt": + try: + # 부모 콘솔에 부착 시도, 실패하면 새 콘솔 할당 + try: + ctypes.windll.kernel32.AttachConsole(-1) + except Exception: + pass + ctypes.windll.kernel32.AllocConsole() + try: + sys.stdout = open("CONOUT$", "w", buffering=1, encoding="utf-8", errors="replace") + sys.stderr = open("CONOUT$", "w", buffering=1, encoding="utf-8", errors="replace") + sys.stdin = open("CONIN$", "r", encoding="utf-8", errors="replace") + except Exception: + pass + print("[ImgWorker] 디버그 콘솔 활성화") + except Exception: + pass + + # GUI(Win32GUI) 기반 실행에서 콘솔이 없으면 안전한 더미 스트림으로 보호 + if not use_console: + class _SilentStream: + def write(self, *args, **kwargs): + pass + def flush(self): + pass + def isatty(self): + return False + try: + if sys.stdout is None: + sys.stdout = _SilentStream() + if sys.stderr is None: + sys.stderr = _SilentStream() + except Exception: + pass + + # 콘솔 스트림 상태에 맞춰 로거를 재초기화하여 StreamHandler가 올바른 스트림을 사용하도록 함 + try: + logger = Logger(log_file=os.path.join(LOGS_DIR, "api_server.log"), logger_name="img_worker_api") + except Exception: + pass + + # uvicorn 실행: 콘솔이 있으면 기본 로깅 활성, 없으면 비활성화 + uvicorn_kwargs = dict(host=bind_host, port=selected_port, reload=False, log_level="info") + if not use_console: + uvicorn_kwargs["log_config"] = None + + # 기본으로 트레이를 백그라운드에서 함께 실행(서비스나 IMGWK_NO_TRAY=1이면 비활성) + if _should_start_tray(): + try: + from modules.tray_app import TrayController + def _run_tray(): + try: + TrayController().run() + except Exception as e: + logger.error(f"트레이 스레드 오류: {e}") + t = threading.Thread(target=_run_tray, name="TrayThread", daemon=True) + t.start() + except Exception as e: + # 내장 트레이 import 실패 시, 외부 트레이 실행 파일로 폴백 + logger.warning(f"트레이 시작 실패(모듈 누락?): {e}") + try: + if getattr(sys, "frozen", False): + exe_dir = os.path.dirname(sys.executable) + tray_path = os.path.join(exe_dir, f"{_SERVICE_NAME}Tray.exe") + if os.path.isfile(tray_path): + env = os.environ.copy() + if "IMGWK_API_BASE" not in env: + # server.json을 트레이가 읽어가므로 기본값 유지 + pass + subprocess.Popen([tray_path], close_fds=True) + else: + logger.warning("외부 트레이 실행 파일을 찾을 수 없습니다") + except Exception as e2: + logger.error(f"외부 트레이 실행 실패: {e2}") + + uvicorn.run(app, **uvicorn_kwargs) + + diff --git a/modules/PP_Models/cls/inference.pdiparams b/modules/PP_Models/cls/inference.pdiparams new file mode 100644 index 0000000..3449efb Binary files /dev/null and b/modules/PP_Models/cls/inference.pdiparams differ diff --git a/modules/PP_Models/cls/inference.pdiparams.info b/modules/PP_Models/cls/inference.pdiparams.info new file mode 100644 index 0000000..f31a157 Binary files /dev/null and b/modules/PP_Models/cls/inference.pdiparams.info differ diff --git a/modules/PP_Models/cls/inference.pdmodel b/modules/PP_Models/cls/inference.pdmodel new file mode 100644 index 0000000..b90c155 Binary files /dev/null and b/modules/PP_Models/cls/inference.pdmodel differ diff --git a/modules/PP_Models/det/inference.pdiparams b/modules/PP_Models/det/inference.pdiparams new file mode 100644 index 0000000..089594a Binary files /dev/null and b/modules/PP_Models/det/inference.pdiparams differ diff --git a/modules/PP_Models/det/inference.pdiparams.info b/modules/PP_Models/det/inference.pdiparams.info new file mode 100644 index 0000000..082c148 Binary files /dev/null and b/modules/PP_Models/det/inference.pdiparams.info differ diff --git a/modules/PP_Models/det/inference.pdmodel b/modules/PP_Models/det/inference.pdmodel new file mode 100644 index 0000000..223b861 Binary files /dev/null and b/modules/PP_Models/det/inference.pdmodel differ diff --git a/modules/PP_Models/rec/inference.pdiparams b/modules/PP_Models/rec/inference.pdiparams new file mode 100644 index 0000000..4c3d9e9 Binary files /dev/null and b/modules/PP_Models/rec/inference.pdiparams differ diff --git a/modules/PP_Models/rec/inference.pdiparams.info b/modules/PP_Models/rec/inference.pdiparams.info new file mode 100644 index 0000000..923329f Binary files /dev/null and b/modules/PP_Models/rec/inference.pdiparams.info differ diff --git a/modules/PP_Models/rec/inference.pdmodel b/modules/PP_Models/rec/inference.pdmodel new file mode 100644 index 0000000..dccddcc Binary files /dev/null and b/modules/PP_Models/rec/inference.pdmodel differ diff --git a/modules/__init__.py b/modules/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/modules/__pycache__/__init__.cpython-311.pyc b/modules/__pycache__/__init__.cpython-311.pyc new file mode 100644 index 0000000..ad0d97c Binary files /dev/null and b/modules/__pycache__/__init__.cpython-311.pyc differ diff --git a/modules/__pycache__/background_removal_module.cpython-311.pyc b/modules/__pycache__/background_removal_module.cpython-311.pyc new file mode 100644 index 0000000..5837672 Binary files /dev/null and b/modules/__pycache__/background_removal_module.cpython-311.pyc differ diff --git a/modules/__pycache__/bria_background_removal_module.cpython-311.pyc b/modules/__pycache__/bria_background_removal_module.cpython-311.pyc new file mode 100644 index 0000000..82b7bd1 Binary files /dev/null and b/modules/__pycache__/bria_background_removal_module.cpython-311.pyc differ diff --git a/modules/__pycache__/gemma_client.cpython-311.pyc b/modules/__pycache__/gemma_client.cpython-311.pyc new file mode 100644 index 0000000..3f9e8ca Binary files /dev/null and b/modules/__pycache__/gemma_client.cpython-311.pyc differ diff --git a/modules/__pycache__/gpu_status_checker.cpython-311.pyc b/modules/__pycache__/gpu_status_checker.cpython-311.pyc new file mode 100644 index 0000000..ad7b5f1 Binary files /dev/null and b/modules/__pycache__/gpu_status_checker.cpython-311.pyc differ diff --git a/modules/__pycache__/gpu_utils.cpython-311.pyc b/modules/__pycache__/gpu_utils.cpython-311.pyc new file mode 100644 index 0000000..b8e3112 Binary files /dev/null and b/modules/__pycache__/gpu_utils.cpython-311.pyc differ diff --git a/modules/__pycache__/image_processor3.cpython-311.pyc b/modules/__pycache__/image_processor3.cpython-311.pyc new file mode 100644 index 0000000..664bfda Binary files /dev/null and b/modules/__pycache__/image_processor3.cpython-311.pyc differ diff --git a/modules/__pycache__/image_worker.cpython-311.pyc b/modules/__pycache__/image_worker.cpython-311.pyc new file mode 100644 index 0000000..179517c Binary files /dev/null and b/modules/__pycache__/image_worker.cpython-311.pyc differ diff --git a/modules/__pycache__/image_worker_manager.cpython-311.pyc b/modules/__pycache__/image_worker_manager.cpython-311.pyc new file mode 100644 index 0000000..72e5e4b Binary files /dev/null and b/modules/__pycache__/image_worker_manager.cpython-311.pyc differ diff --git a/modules/__pycache__/log_bridge.cpython-311.pyc b/modules/__pycache__/log_bridge.cpython-311.pyc new file mode 100644 index 0000000..cddfb9d Binary files /dev/null and b/modules/__pycache__/log_bridge.cpython-311.pyc differ diff --git a/modules/__pycache__/mask_module_for_paddle.cpython-311.pyc b/modules/__pycache__/mask_module_for_paddle.cpython-311.pyc new file mode 100644 index 0000000..9d69aca Binary files /dev/null and b/modules/__pycache__/mask_module_for_paddle.cpython-311.pyc differ diff --git a/modules/__pycache__/migan_module.cpython-311.pyc b/modules/__pycache__/migan_module.cpython-311.pyc new file mode 100644 index 0000000..b2f4ad8 Binary files /dev/null and b/modules/__pycache__/migan_module.cpython-311.pyc differ diff --git a/modules/__pycache__/ocr_module.cpython-311.pyc b/modules/__pycache__/ocr_module.cpython-311.pyc new file mode 100644 index 0000000..395c06f Binary files /dev/null and b/modules/__pycache__/ocr_module.cpython-311.pyc differ diff --git a/modules/__pycache__/postImageManager.cpython-311.pyc b/modules/__pycache__/postImageManager.cpython-311.pyc new file mode 100644 index 0000000..418a318 Binary files /dev/null and b/modules/__pycache__/postImageManager.cpython-311.pyc differ diff --git a/modules/__pycache__/request_inpaint.cpython-311.pyc b/modules/__pycache__/request_inpaint.cpython-311.pyc new file mode 100644 index 0000000..fbda949 Binary files /dev/null and b/modules/__pycache__/request_inpaint.cpython-311.pyc differ diff --git a/modules/__pycache__/settings_manager.cpython-311.pyc b/modules/__pycache__/settings_manager.cpython-311.pyc new file mode 100644 index 0000000..15d05ae Binary files /dev/null and b/modules/__pycache__/settings_manager.cpython-311.pyc differ diff --git a/modules/__pycache__/text_rendering_module.cpython-311.pyc b/modules/__pycache__/text_rendering_module.cpython-311.pyc new file mode 100644 index 0000000..00ec4d2 Binary files /dev/null and b/modules/__pycache__/text_rendering_module.cpython-311.pyc differ diff --git a/modules/__pycache__/tray_app.cpython-311.pyc b/modules/__pycache__/tray_app.cpython-311.pyc new file mode 100644 index 0000000..6414474 Binary files /dev/null and b/modules/__pycache__/tray_app.cpython-311.pyc differ diff --git a/modules/bria_background_removal_module.py b/modules/bria_background_removal_module.py new file mode 100644 index 0000000..35a4870 --- /dev/null +++ b/modules/bria_background_removal_module.py @@ -0,0 +1,776 @@ +""" +BriaAI RMBG 1.4 ONNXRuntime 기반 배경제거 모듈 +기존 background_removal_module.py를 완전 대체할 수 있는 호환 인터페이스 제공 +""" +import os +import cv2 +from PIL import Image +import logging +import numpy as np +import time +from typing import Optional, Tuple + + +class BriaBackgroundRemovalModule: + """ + BriaAI RMBG 1.4 ONNX 모델 기반 배경제거 모듈 + 기존 BackgroundRemovalModule과 완전 호환되는 인터페이스 제공 + """ + + # 지원하는 모델 목록 (BriaAI 기반) + SUPPORTED_MODELS = { + "bria-rmbg-1.4": "BriaAI RMBG 1.4 | 고품질 | 빠름 | 범용 (기본값)", + "bria-rmbg-aggressive": "BriaAI RMBG 1.4 | 강력한 배경제거 | 빠름 | 깔끔한 결과", + "bria-rmbg-gentle": "BriaAI RMBG 1.4 | 부드러운 배경제거 | 빠름 | 세밀한 경계" + } + + # 모델별 aggressiveness 매핑 + MODEL_AGGRESSIVENESS = { + "bria-rmbg-1.4": 0.5, # 기본값 + "bria-rmbg-aggressive": 0.8, # 강력한 배경제거 + "bria-rmbg-gentle": 0.2 # 부드러운 배경제거 + } + + def __init__(self, logger=None, default_model="bria-rmbg-1.4", gpu_manager=None, local_rembg_model_path: str | None = None): + self.logger = logger + self.default_model = default_model + self.gpu_manager = gpu_manager + self.local_model_path = local_rembg_model_path # BriaAI ONNX 모델 경로 + + # ONNX 세션 관련 + self._session: Optional = None + self._input_name: Optional[str] = None + self._output_name: Optional[str] = None + self._model_input_size: Tuple[int, int] = (1024, 1024) # (W, H) + self._model_loaded = False + self._init_error = None + + if self.logger: + self.logger.log("BriaAI 배경제거 모듈 초기화 시작", level=logging.INFO) + + # ONNX Runtime 사용 가능성 확인 + self._check_onnxruntime_availability() + + if self.logger: + self.logger.log("BriaAI 배경제거 모듈 초기화 완료", level=logging.INFO) + + def _check_onnxruntime_availability(self): + """ONNX Runtime 사용 가능 여부 확인""" + try: + import onnxruntime as ort + + # 사용 가능한 프로바이더 확인 + available_providers = ort.get_available_providers() + if self.logger: + self.logger.log(f"ONNX Runtime 사용 가능한 프로바이더: {available_providers}", level=logging.INFO) + + # Override/캐시 우선 결정 + provider_override = os.environ.get('IMGWK_REMBG_PROVIDER', 'auto').lower() + cached_provider = self._read_provider_cache() + self.providers = ['CPUExecutionProvider'] + + # GPU 가속 설정 (BriaAI는 가벼워서 1GB도 충분) + if self.gpu_manager and self.gpu_manager.can_use_cuda: + gpu_memory_mb = int(getattr(self.gpu_manager, 'gpu_memory_total', 0) or 0) + + def can_use_dml(): + return ('DmlExecutionProvider' in available_providers) and (gpu_memory_mb >= 1024) + + def can_use_cuda(): + return ('CUDAExecutionProvider' in available_providers) and (gpu_memory_mb >= 1024) + + if provider_override == 'cpu': + self.providers = ['CPUExecutionProvider'] + if self.logger: + self.logger.log("BriaAI provider override=cpu", level=logging.INFO) + elif provider_override == 'dml': + if can_use_dml(): + self.providers = ['DmlExecutionProvider', 'CPUExecutionProvider'] + else: + self.providers = ['CPUExecutionProvider'] + if self.logger: + self.logger.log(f"BriaAI provider override=dml → {self.providers}", level=logging.INFO) + elif cached_provider == 'dml': + if can_use_dml(): + self.providers = ['DmlExecutionProvider', 'CPUExecutionProvider'] + if self.logger: + self.logger.log("BriaAI provider cache=dml 적용", level=logging.INFO) + else: + self.providers = ['CPUExecutionProvider'] + else: + # auto: DML → CUDA → CPU 순으로 선택 + if can_use_dml(): + self.providers = ['DmlExecutionProvider', 'CPUExecutionProvider'] + if self.logger: + self.logger.log(f"BriaAI DirectML 가속 사용 가능 (VRAM: {gpu_memory_mb}MB)", level=logging.INFO) + elif can_use_cuda(): + self.providers = ['CUDAExecutionProvider', 'CPUExecutionProvider'] + if self.logger: + self.logger.log(f"BriaAI CUDA 가속 사용 가능 (VRAM: {gpu_memory_mb}MB)", level=logging.INFO) + else: + self.providers = ['CPUExecutionProvider'] + if self.logger: + self.logger.log(f"VRAM/Provider 조건 불충족으로 CPU 모드 사용 (VRAM: {gpu_memory_mb}MB)", level=logging.WARNING) + else: + self.providers = ['CPUExecutionProvider'] + if self.logger: + self.logger.log("BriaAI CPU 모드로 설정", level=logging.INFO) + + self._onnxruntime_available = True + return True + + except ImportError as e: + self._init_error = f"ONNX Runtime이 설치되지 않음: {e}" + self._onnxruntime_available = False + if self.logger: + self.logger.log(self._init_error, level=logging.ERROR) + return False + except Exception as e: + self._init_error = f"ONNX Runtime 초기화 실패: {e}" + self._onnxruntime_available = False + if self.logger: + self.logger.log(self._init_error, level=logging.ERROR) + return False + + def _load_model(self) -> bool: + """BriaAI ONNX 모델을 로드합니다.""" + if self._model_loaded: + return True + + if not self._onnxruntime_available: + if self.logger: + self.logger.log("ONNX Runtime을 사용할 수 없어 모델 로드 실패", level=logging.ERROR) + return False + + if not self.local_model_path or not os.path.exists(self.local_model_path): + self._init_error = f"BriaAI 모델 파일을 찾을 수 없음: {self.local_model_path}" + if self.logger: + self.logger.log(self._init_error, level=logging.ERROR) + return False + + # 단계별 폴백 시도 + fallback_providers = [ + (self.providers, "원본 providers"), + ([("CPUExecutionProvider", {})], "CPU 폴백") + ] + + for attempt_providers, attempt_name in fallback_providers: + try: + import onnxruntime as ort + + if self.logger: + self.logger.log(f"BriaAI ONNX 모델 로딩 시도 ({attempt_name}): {self.local_model_path}", level=logging.INFO) + + # 세션 옵션 설정 (안전성 우선) + sess_options = ort.SessionOptions() + + # CPU 모드에서는 더 보수적인 설정 + if attempt_providers == [("CPUExecutionProvider", {})]: + sess_options.graph_optimization_level = ort.GraphOptimizationLevel.ORT_DISABLE_ALL + sess_options.enable_mem_pattern = False + sess_options.enable_cpu_mem_arena = False + sess_options.execution_mode = ort.ExecutionMode.ORT_SEQUENTIAL + if self.logger: + self.logger.log("🔒 CPU 안전 모드: 모든 최적화 비활성화", level=logging.INFO) + else: + sess_options.graph_optimization_level = ort.GraphOptimizationLevel.ORT_ENABLE_BASIC + sess_options.enable_mem_pattern = True + sess_options.enable_cpu_mem_arena = True + + # DirectML 사용시 추가 설정 + if 'DmlExecutionProvider' in str(attempt_providers): + sess_options.execution_mode = ort.ExecutionMode.ORT_SEQUENTIAL + if self.logger: + self.logger.log("DirectML 최적화 설정 적용", level=logging.DEBUG) + + # ONNX 세션 생성 (타임아웃 설정) + import signal + + def timeout_handler(signum, frame): + raise TimeoutError("모델 로딩 타임아웃") + + # Windows에서는 signal.alarm이 작동하지 않으므로 threading 사용 + import threading + import time + + session_created = [False] + session_result = [None] + session_error = [None] + + def create_session(): + try: + session_result[0] = ort.InferenceSession( + self.local_model_path, + sess_options=sess_options, + providers=attempt_providers + ) + session_created[0] = True + except Exception as e: + session_error[0] = e + + # 세션 생성을 별도 스레드에서 실행 (30초 타임아웃) + session_thread = threading.Thread(target=create_session) + session_thread.daemon = True + session_thread.start() + session_thread.join(timeout=30) + + if not session_created[0]: + if session_error[0]: + raise session_error[0] + else: + raise TimeoutError("모델 로딩이 30초 내에 완료되지 않음") + + self._session = session_result[0] + + # 입출력 정보 가져오기 + inputs = self._session.get_inputs() + outputs = self._session.get_outputs() + + if not inputs or not outputs: + raise RuntimeError("ONNX 모델의 입출력 정의를 찾을 수 없습니다") + + # 입력/출력 이름 설정 + self._input_name = inputs[0].name + self._output_name = outputs[0].name + + # 실제 사용된 프로바이더 확인 + actual_providers = self._session.get_providers() + + if self.logger: + self.logger.log( + f"✅ BriaAI ONNX 모델 로딩 완료 ({attempt_name}) | " + f"Providers: {actual_providers} | " + f"Input: {self._input_name} | Output: {self._output_name}", + level=logging.INFO + ) + + self._model_loaded = True + self._providers_used = actual_providers + # 성공 프로바이더 캐시 기록 + try: + prov = 'dml' if any('Dml' in p for p in actual_providers) else 'cpu' + self._write_provider_cache(prov) + except Exception: + pass + return True + + except Exception as e: + error_msg = f"BriaAI ONNX 모델 로딩 실패 ({attempt_name}): {e}" + if self.logger: + self.logger.log(error_msg, level=logging.WARNING if attempt_name != "CPU 폴백" else logging.ERROR, exc_info=True) + + # 마지막 시도가 아니면 계속 진행 + if attempt_name != "CPU 폴백": + continue + + # 모든 시도 실패 + self._init_error = "모든 provider에서 BriaAI ONNX 모델 로딩 실패" + if self.logger: + self.logger.log(self._init_error, level=logging.ERROR) + return False + + # ───────────────────────────────────────────────────────────── + # 프로바이더 캐시 유틸 + # ───────────────────────────────────────────────────────────── + def _cache_path(self): + try: + root_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) + user_data = os.path.join(root_dir, 'user_data') + os.makedirs(user_data, exist_ok=True) + return os.path.join(user_data, 'rembg_provider.json') + except Exception: + return None + + def _read_provider_cache(self): + try: + path = self._cache_path() + if not path or not os.path.exists(path): + return None + import json + with open(path, 'r', encoding='utf-8') as f: + data = json.load(f) + prov = (data or {}).get('last_success_provider', '').lower() + return prov if prov in ('dml', 'cpu') else None + except Exception: + return None + + def _write_provider_cache(self, provider: str): + try: + path = self._cache_path() + if not path: + return + import json + with open(path, 'w', encoding='utf-8') as f: + json.dump({"last_success_provider": provider}, f, ensure_ascii=False) + except Exception: + pass + + def _preprocess(self, image_bgr: np.ndarray) -> Tuple[np.ndarray, Tuple[int, int]]: + """BGR uint8 이미지를 모델 입력(NCHW float32, 정규화)로 변환 (허깅페이스 호환)""" + orig_h, orig_w = image_bgr.shape[:2] + + # 입력 검증 + if len(image_bgr.shape) != 3 or image_bgr.shape[2] != 3: + raise ValueError(f"입력 이미지는 3채널 BGR이어야 합니다. 현재: {image_bgr.shape}") + + # BGR -> RGB (허깅페이스는 RGB 입력 가정) + image_rgb = image_bgr[:, :, ::-1].copy() + + # 리사이즈 (W,H) - 허깅페이스와 동일한 bilinear 보간 + target_w, target_h = self._model_input_size + resized = cv2.resize(image_rgb, (target_w, target_h), interpolation=cv2.INTER_LINEAR) + + # 허깅페이스 방식: float32 변환 후 정규화 + # 1. [0,255] -> [0,1] + tensor = resized.astype(np.float32) / 255.0 + + # 2. normalize(mean=[0.5,0.5,0.5], std=[1.0,1.0,1.0]) -> (x - 0.5) / 1.0 + tensor = tensor - 0.5 + + # 3. HWC -> CHW, 배치 축 추가 (NCHW) + nchw = np.transpose(tensor, (2, 0, 1))[np.newaxis, ...] + + # 디버그 정보 + if self.logger: + self.logger.log(f"전처리 완료: {image_bgr.shape} -> {nchw.shape}, 값 범위: [{nchw.min():.3f}, {nchw.max():.3f}]", level=logging.DEBUG) + + return nchw, (orig_h, orig_w) + + def _infer(self, input_tensor: np.ndarray) -> np.ndarray: + """ONNX 추론 수행 후 [H,W] 마스크 확률맵 반환 (BriaAI 모델 특화)""" + outputs = self._session.run(None, {self._input_name: input_tensor}) + + # BriaAI 모델은 여러 출력을 가질 수 있음 (6개 side outputs) + # 첫 번째 출력(d1)이 가장 정확한 결과 + if isinstance(outputs, (list, tuple)) and len(outputs) > 0: + pred = outputs[0] # 첫 번째 출력 사용 (d1) + if self.logger: + self.logger.log(f"ONNX 모델 출력 개수: {len(outputs)}, 첫 번째 출력 shape: {pred.shape}", level=logging.DEBUG) + else: + pred = outputs + + # numpy array로 변환 + pred = np.array(pred) + + # 차원 정리: [B, C, H, W] -> [H, W] + if pred.ndim == 4: + # [1, 1, H, W] -> [H, W] + pred = pred[0, 0] + elif pred.ndim == 3: + if pred.shape[0] == 1: + # [1, H, W] -> [H, W] + pred = pred[0] + else: + # [C, H, W] -> [H, W] (첫 번째 채널 사용) + pred = pred[0] + elif pred.ndim == 2: + # 이미 [H, W] 형태 + pass + else: + raise ValueError(f"예상하지 못한 출력 차원: {pred.shape}") + + # 확률값 범위 확인 및 정규화 (0~1 사이가 아닐 경우) + if pred.max() > 1.0 or pred.min() < 0.0: + if self.logger: + self.logger.log(f"출력 값 범위 이상: [{pred.min():.3f}, {pred.max():.3f}] -> sigmoid 적용", level=logging.WARNING) + # sigmoid 함수 적용 (모델에서 sigmoid가 적용되지 않은 경우) + pred = 1.0 / (1.0 + np.exp(-pred)) + + if self.logger: + self.logger.log(f"추론 완료: {pred.shape}, 값 범위: [{pred.min():.3f}, {pred.max():.3f}]", level=logging.DEBUG) + + return pred + + def _postprocess(self, mask_pred: np.ndarray, orig_size: Tuple[int, int], aggressiveness: float = 0.5) -> np.ndarray: + """모델 출력 마스크를 원본 해상도로 보간하고 0..255 uint8로 변환 (허깅페이스 방식)""" + orig_h, orig_w = orig_size + + # 모델 출력(H,W)을 원본 크기로 리사이즈 (W,H) - 허깅페이스와 동일하게 bilinear 보간 + mask_resized = cv2.resize(mask_pred, (orig_w, orig_h), interpolation=cv2.INTER_LINEAR) + + # 허깅페이스 방식의 min-max 정규화 (임계값 없이) + ma = float(mask_resized.max()) + mi = float(mask_resized.min()) + denom = (ma - mi) if (ma - mi) != 0 else 1.0 + mask_norm = (mask_resized - mi) / denom + + # 허깅페이스 방식: 직접 0-255로 스케일링 (임계값 적용 안함) + mask_255 = (mask_norm * 255).astype(np.uint8) + + # aggressiveness 파라미터 적용 (필요시에만) + if aggressiveness != 0.5: + # aggressiveness가 0.5가 아닐 때만 약간의 조정 적용 + if aggressiveness > 0.5: + # 더 공격적: 밝기 증가 + factor = 1.0 + (aggressiveness - 0.5) * 0.4 + mask_255 = np.clip(mask_255 * factor, 0, 255).astype(np.uint8) + else: + # 더 부드럽게: 밝기 감소 + factor = 0.6 + aggressiveness * 0.8 + mask_255 = np.clip(mask_255 * factor, 0, 255).astype(np.uint8) + + return mask_255 + + # =================================================================================== + # 기존 BackgroundRemovalModule 호환 인터페이스 + # =================================================================================== + + def _refine_mask( + self, + alpha_mask: np.ndarray, + *, + alpha_threshold: int | None = None, + keep_top_k_components: int | None = None, + min_component_area: int | None = None, + morph_kernel: int | None = 3, + morph_open_iters: int = 0, + morph_close_iters: int = 0, + dilate_iters: int = 0, + erode_iters: int = 0, + fill_holes: bool = False, + edge_feather: int = 0, + ) -> np.ndarray: + """알파 마스크를 후처리하여 과도 제거/잔여 잡음을 완화합니다. + + 매개변수 설명: + - alpha_threshold: 이진화 임계값(0~255). 설정 시 확실한 전경만 남깁니다. + - keep_top_k_components: 가장 큰 연결요소 K개만 유지(사람+상품=2 권장). + - min_component_area: 이 면적 미만의 작은 요소 제거. + - morph_*: 모폴로지 연산으로 노이즈 제거/홀 메움. + - dilate/erode: 경계 확장/축소 미세 조정. + - fill_holes: 내부 구멍 채우기(큰 구멍 포함). + - edge_feather: 가장자리 페더링(>0이면 가우시안 블러, 값은 커널 반경). + """ + + try: + mask_uint8 = alpha_mask.astype(np.uint8) + + # 1) 이진화 준비 + if alpha_threshold is not None: + _, bin_mask = cv2.threshold(mask_uint8, int(alpha_threshold), 255, cv2.THRESH_BINARY) + else: + # 소프트 마스크라도 후단 처리를 위해 이진 마스크 생성 + bin_mask = (mask_uint8 > 0).astype(np.uint8) * 255 + + # 2) 연결요소 기반 정리 + if keep_top_k_components is not None or (min_component_area is not None and min_component_area > 1): + num_labels, labels, stats, _ = cv2.connectedComponentsWithStats(bin_mask, connectivity=8) + # 0은 배경 + component_indices = list(range(1, num_labels)) + if min_component_area is not None: + component_indices = [i for i in component_indices if int(stats[i, cv2.CC_STAT_AREA]) >= int(min_component_area)] + + # 가장 큰 K개만 남기기 + if keep_top_k_components is not None and keep_top_k_components > 0: + component_indices = sorted( + component_indices, + key=lambda i: int(stats[i, cv2.CC_STAT_AREA]), + reverse=True, + )[: int(keep_top_k_components)] + + filtered = np.zeros_like(bin_mask) + for i in component_indices: + filtered[labels == i] = 255 + bin_mask = filtered + + # 3) 모폴로지 연산으로 노이즈 제거/홀 메움 + k = 3 if morph_kernel is None or morph_kernel <= 0 else int(morph_kernel) + kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (k, k)) + + if morph_open_iters > 0: + bin_mask = cv2.morphologyEx(bin_mask, cv2.MORPH_OPEN, kernel, iterations=int(morph_open_iters)) + + if morph_close_iters > 0: + bin_mask = cv2.morphologyEx(bin_mask, cv2.MORPH_CLOSE, kernel, iterations=int(morph_close_iters)) + + if dilate_iters > 0: + bin_mask = cv2.dilate(bin_mask, kernel, iterations=int(dilate_iters)) + if erode_iters > 0: + bin_mask = cv2.erode(bin_mask, kernel, iterations=int(erode_iters)) + + # 4) 내부 큰 구멍 채우기 + if fill_holes: + flood = bin_mask.copy() + h, w = flood.shape[:2] + flood_mask = np.zeros((h + 2, w + 2), np.uint8) + cv2.floodFill(flood, flood_mask, (0, 0), 255) + flood_inv = cv2.bitwise_not(flood) + bin_mask = cv2.bitwise_or(bin_mask, flood_inv) + + # 5) 가장자리 페더링(부드럽게) + if edge_feather and edge_feather > 0: + r = int(edge_feather) + r = max(1, min(31, r * 2 + 1)) # 홀수 커널 보장, 과도한 값 방지 + soft = cv2.GaussianBlur(bin_mask, (r, r), 0) + return soft.astype(np.uint8) + + return bin_mask.astype(np.uint8) + + except Exception as e: + if self.logger: + self.logger.log(f"마스크 후처리 오류: {e}", level=logging.ERROR, exc_info=True) + return alpha_mask + + def is_available(self): + """배경제거 모듈 사용 가능 여부 반환""" + return self._onnxruntime_available and (self.local_model_path and os.path.exists(self.local_model_path)) + + def get_init_error(self): + """초기화 에러 메시지 반환""" + return self._init_error + + def get_supported_models(self): + """지원하는 모델 목록 반환""" + return self.SUPPORTED_MODELS.copy() + + def get_default_model(self): + """기본 모델명 반환""" + return self.default_model + + def set_default_model(self, model_name): + """기본 모델 설정""" + if model_name not in self.SUPPORTED_MODELS: + raise ValueError(f"지원하지 않는 모델명: {model_name}") + self.default_model = model_name + if self.logger: + self.logger.log(f"BriaAI 기본 모델이 '{model_name}'으로 변경됨", level=logging.INFO) + + def get_model_description(self, model_name): + """모델 설명 반환""" + return self.SUPPORTED_MODELS.get(model_name, "모델 설명 없음") + + def to_white_background(self, img: Image.Image) -> Image.Image: + """RGBA 이미지를 흰 배경과 합성 (이미 RGB라면 그대로 반환)""" + if img.mode in ("RGBA", "BGRA"): + bg = Image.new("RGB", img.size, (255, 255, 255)) + bg.paste(img, mask=img.split()[-1]) + return bg + else: + # 이미 RGB이거나 다른 모드라면 RGB로 변환 + return img.convert("RGB") + + def remove_background(self, image_path, model_name=None, force_cpu=None, **kwargs): + """ + 이미지에서 배경을 제거하여 PIL Image 반환 + 기존 BackgroundRemovalModule.remove_background와 동일한 인터페이스 + """ + if not self.is_available(): + if self.logger: + self.logger.log(f"BriaAI 모듈 사용 불가: {self._init_error}", level=logging.ERROR) + return None + + if not os.path.exists(image_path): + if self.logger: + self.logger.log(f"입력 이미지가 존재하지 않습니다: {image_path}", level=logging.ERROR) + return None + + # force_cpu 매개변수 처리 + original_providers = self.providers + if force_cpu is True: + self.providers = ['CPUExecutionProvider'] + if self.logger: + self.logger.log("⚠️ CPU 모드 강제 실행 (BriaAI)", level=logging.WARNING) + elif force_cpu is False: + # GPU 가속 강제 사용 (DirectML 테스트용) + if self.gpu_manager and self.gpu_manager.can_use_cuda: + if 'DmlExecutionProvider' in original_providers: + self.providers = ['DmlExecutionProvider', 'CPUExecutionProvider'] + elif 'CUDAExecutionProvider' in original_providers: + self.providers = ['CUDAExecutionProvider', 'CPUExecutionProvider'] + else: + self.providers = ['CPUExecutionProvider'] + if self.logger: + self.logger.log(f"🔥 GPU 모드 강제 실행 (BriaAI): {self.providers}", level=logging.WARNING) + + # 모델 로드 (지연 로딩) + model_loaded = self._load_model() + + # 원래 providers 복원 + if force_cpu is not None: + self.providers = original_providers + + if not model_loaded: + return None + + try: + # 이미지 로드 + img = cv2.imread(image_path) + if img is None: + if self.logger: + self.logger.log(f"이미지 로드 실패: {image_path}", level=logging.ERROR) + return None + + # 모델명 결정 및 aggressiveness 설정 + effective_model_name = model_name or self.default_model + if effective_model_name not in self.SUPPORTED_MODELS: + if self.logger: + self.logger.log(f"지원하지 않는 모델명: {effective_model_name}. bria-rmbg-1.4로 대체 사용", level=logging.WARNING) + effective_model_name = "bria-rmbg-1.4" + + aggressiveness = self.MODEL_AGGRESSIVENESS.get(effective_model_name, 0.5) + custom_aggressiveness = kwargs.get("aggressiveness", aggressiveness) + + if self.logger: + self.logger.log(f"BriaAI 배경제거 시작: {effective_model_name} (aggressiveness={custom_aggressiveness})", level=logging.DEBUG) + + start_time = time.time() + + # 전처리 + input_tensor, (orig_h, orig_w) = self._preprocess(img) + + # 추론 + mask_pred = self._infer(input_tensor) + + # 후처리 + alpha_mask = self._postprocess(mask_pred, (orig_h, orig_w), aggressiveness=custom_aggressiveness) + + # 선택적 마스크 보정 + if any(k in kwargs for k in ( + 'alpha_threshold', 'keep_top_k_components', 'min_component_area', + 'morph_kernel', 'morph_open_iters', 'morph_close_iters', + 'dilate_iters', 'erode_iters', 'fill_holes', 'edge_feather' + )): + alpha_mask = self._refine_mask( + alpha_mask, + alpha_threshold=kwargs.get('alpha_threshold'), + keep_top_k_components=kwargs.get('keep_top_k_components'), + min_component_area=kwargs.get('min_component_area'), + morph_kernel=kwargs.get('morph_kernel', 3), + morph_open_iters=kwargs.get('morph_open_iters', 0), + morph_close_iters=kwargs.get('morph_close_iters', 0), + dilate_iters=kwargs.get('dilate_iters', 0), + erode_iters=kwargs.get('erode_iters', 0), + fill_holes=kwargs.get('fill_holes', False), + edge_feather=kwargs.get('edge_feather', 0), + ) + + # DirectML 알파 채널 이슈 회피: 바로 흰 배경 합성된 BGR로 처리 + img_rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) + + # 알파 마스크를 이용해 흰 배경 합성 (DirectML 알파 채널 이슈 회피) + alpha_normalized = alpha_mask.astype(np.float32) / 255.0 + alpha_3d = np.stack([alpha_normalized] * 3, axis=-1) + + # 흰 배경과 합성 + white_bg = np.full_like(img_rgb, 255, dtype=np.uint8) + blended_rgb = ( + img_rgb.astype(np.float32) * alpha_3d + + white_bg.astype(np.float32) * (1.0 - alpha_3d) + ).astype(np.uint8) + + # PIL Image로 변환 (RGB 모드로 - 알파 채널 없음) + result = Image.fromarray(blended_rgb, 'RGB') + + end_time = time.time() + processing_time = end_time - start_time + + if self.logger: + provider_status = "GPU" if any('CUDA' in p or 'Dml' in p for p in self._session.get_providers()) else "CPU" + self.logger.log(f"✅ BriaAI 배경제거 성공: {effective_model_name} ({provider_status}, {processing_time:.2f}초)", level=logging.INFO) + + # 마스크 통계 로깅 + mask_stats = { + 'min': int(alpha_mask.min()), + 'max': int(alpha_mask.max()), + 'mean': float(alpha_mask.mean()), + 'nonzero_count': int(np.count_nonzero(alpha_mask)) + } + self.logger.log(f"BriaAI 마스크 통계: {mask_stats}", level=logging.DEBUG) + + # GPU 메모리 사용량 로깅 + if self.gpu_manager and hasattr(self.gpu_manager, 'log_gpu_memory_usage'): + self.gpu_manager.log_gpu_memory_usage() + + return result + + except Exception as e: + if self.logger: + self.logger.log(f"BriaAI 배경제거 처리 중 오류: {e}", level=logging.ERROR, exc_info=True) + return None + + def remove_background_array(self, image_bgr: np.ndarray, model_name=None, force_cpu=None, **kwargs) -> Tuple[np.ndarray, np.ndarray]: + """ + 배경제거 결과를 numpy array로 직접 반환 (추가 편의 메서드) + Returns: (result_bgr, alpha_mask) + """ + # force_cpu 매개변수 처리 + original_providers = self.providers + if force_cpu is True: + self.providers = ['CPUExecutionProvider'] + if self.logger: + self.logger.log("⚠️ CPU 모드 강제 실행 (BriaAI array)", level=logging.WARNING) + elif force_cpu is False: + # GPU 가속 강제 사용 + if self.gpu_manager and self.gpu_manager.can_use_cuda: + if 'DmlExecutionProvider' in original_providers: + self.providers = ['DmlExecutionProvider', 'CPUExecutionProvider'] + elif 'CUDAExecutionProvider' in original_providers: + self.providers = ['CUDAExecutionProvider', 'CPUExecutionProvider'] + if self.logger: + self.logger.log(f"🔥 GPU 모드 강제 실행 (BriaAI array): {self.providers}", level=logging.WARNING) + + # 모델 로드 + model_loaded = self.is_available() and self._load_model() + + # 원래 providers 복원 + if force_cpu is not None: + self.providers = original_providers + + if not model_loaded: + return image_bgr, None + + try: + effective_model_name = model_name or self.default_model + aggressiveness = self.MODEL_AGGRESSIVENESS.get(effective_model_name, 0.5) + custom_aggressiveness = kwargs.get("aggressiveness", aggressiveness) + + # 전처리 -> 추론 -> 후처리 + input_tensor, (orig_h, orig_w) = self._preprocess(image_bgr) + mask_pred = self._infer(input_tensor) + alpha_mask = self._postprocess(mask_pred, (orig_h, orig_w), aggressiveness=custom_aggressiveness) + + # 선택적 마스크 보정 + if any(k in kwargs for k in ( + 'alpha_threshold', 'keep_top_k_components', 'min_component_area', + 'morph_kernel', 'morph_open_iters', 'morph_close_iters', + 'dilate_iters', 'erode_iters', 'fill_holes', 'edge_feather' + )): + alpha_mask = self._refine_mask( + alpha_mask, + alpha_threshold=kwargs.get('alpha_threshold'), + keep_top_k_components=kwargs.get('keep_top_k_components'), + min_component_area=kwargs.get('min_component_area'), + morph_kernel=kwargs.get('morph_kernel', 3), + morph_open_iters=kwargs.get('morph_open_iters', 0), + morph_close_iters=kwargs.get('morph_close_iters', 0), + dilate_iters=kwargs.get('dilate_iters', 0), + erode_iters=kwargs.get('erode_iters', 0), + fill_holes=kwargs.get('fill_holes', False), + edge_feather=kwargs.get('edge_feather', 0), + ) + + # 흰색 배경 합성 + mask_3d = np.stack([alpha_mask] * 3, axis=-1) + result_bgr = ( + image_bgr.astype(np.float32) * (mask_3d.astype(np.float32) / 255.0) + + 255.0 * (1.0 - (mask_3d.astype(np.float32) / 255.0)) + ).clip(0, 255).astype(np.uint8) + + return result_bgr, alpha_mask + + except Exception as e: + if self.logger: + self.logger.log(f"BriaAI 배경제거 array 처리 중 오류: {e}", level=logging.ERROR, exc_info=True) + return image_bgr, None + + def _preload_sessions(self): + """BriaAI 모델 미리 로딩""" + if self.logger: + self.logger.log("🔄 BriaAI 모델 미리 로딩 중...", level=logging.INFO) + + if self._load_model(): + if self.logger: + self.logger.log("✅ BriaAI 모델 미리 로딩 완료", level=logging.INFO) + else: + if self.logger: + self.logger.log("⚠️ BriaAI 모델 로딩 실패", level=logging.WARNING) diff --git a/modules/client/__pycache__/image_worker_client.cpython-311.pyc b/modules/client/__pycache__/image_worker_client.cpython-311.pyc new file mode 100644 index 0000000..b6d858f Binary files /dev/null and b/modules/client/__pycache__/image_worker_client.cpython-311.pyc differ diff --git a/modules/client/details.py b/modules/client/details.py new file mode 100644 index 0000000..507b527 --- /dev/null +++ b/modules/client/details.py @@ -0,0 +1,1431 @@ +import numpy as np +import asyncio, time, math +# import pywinauto +import os +import logging +import random +import glob +import markdown +from bs4 import BeautifulSoup +import re + +import sqlite3 +import json +from datetime import datetime, timedelta + + +class DetailHandler: + def __init__(self, locator_manager, browser_controller, imageProcessor, detail_text_widget, TEMP_IMAGE_DIR, logger, gpt_client, update_detail_progress_signal, set_progress_visible_signal, toggle_states): + + self.update_detail_progress_signal = update_detail_progress_signal + self.set_progress_visible_signal = set_progress_visible_signal + + self.TEMP_IMAGE_DIR = TEMP_IMAGE_DIR + self.locator_manager = locator_manager + self.browser_controller = browser_controller + self.page = self.browser_controller.page + # self.clipboardImageManager = clipboardImageManager + self.detail_text_widget = detail_text_widget + self.logger = logger + self.imageProcessor = imageProcessor + self.toggle_states = toggle_states + self.gpt_client = gpt_client + # self.whale_translator = whale_translator + + # 선택자 로드 + self.source_button_locator = self.locator_manager.get_locator('DetailLocators', 'source_button_locator') + self.ck_source_editing_area_locator = self.locator_manager.get_locator('DetailLocators', 'ck_source_editing_area_locator') + self.cke_text_editing_area_locator = self.locator_manager.get_locator('DetailLocators', 'cke_text_editing_area_locator') + self.cke_img_file_input_locator = self.locator_manager.get_locator('DetailLocators', 'cke_img_file_input_locator') + self.oneclick_trans_img_btn_locator = self.locator_manager.get_locator('DetailLocators', 'oneclick_trans_img_btn_locator') + self.editor_modal_locator = self.locator_manager.get_locator('DetailLocators', 'editor_modal_locator') + self.save_btn_locator = self.locator_manager.get_locator('DetailLocators', 'save_btn_locator') + self.dialog_save_btn_locator = self.locator_manager.get_locator('DetailLocators', 'dialog_save_btn_locator') + self.detail_img_trans_btn_locator = self.locator_manager.get_locator('DetailLocators', 'detail_img_trans_btn_locator') + self.detail_img_trans_dialog_locator = self.locator_manager.get_locator('DetailLocators', 'detail_img_trans_dialog_locator') + self.detail_img_trans_dialog_closeBTN_locator = self.locator_manager.get_locator('DetailLocators', 'detail_img_trans_dialog_closeBTN_locator') + self.detail_img_trans_editBTN_locator = self.locator_manager.get_locator('DetailLocators', 'detail_img_trans_editBTN_locator') + self.detail_images_locator = self.locator_manager.get_locator('DetailLocators', 'detail_images_locator') + + def update_page(self, page1, toggle_states, imageProcessor): + self.page = page1 + self.toggle_states = toggle_states + self.imageProcessor = imageProcessor + self.logger.log(f"page객체 업데이트 : {page1}", level=logging.DEBUG) + + + def reset_state(self): + """상세페이지 핸들러의 상태를 초기화합니다. (detail 관련 임시파일만 정리)""" + self.logger.log("DetailHandler 상태 초기화 - detail 관련 임시파일 정리", level=logging.DEBUG) + + try: + # detail 관련 임시 디렉토리의 파일들만 삭제 (더 포괄적인 패턴 사용) + patterns = [ + "detail_*.png", + "detail_*.jpg", + "detail_*.webp", + "translated_detail_*" + ] + + all_detail_files = [] + for pattern in patterns: + detail_pattern = os.path.join(self.TEMP_IMAGE_DIR, pattern) + all_detail_files.extend(glob.glob(detail_pattern)) + + if all_detail_files: + for file_path in all_detail_files: + try: + os.remove(file_path) + self.logger.log(f"detail 임시 파일 삭제: {file_path}", level=logging.DEBUG) + except Exception as e: + self.logger.log(f"detail 임시 파일 삭제 실패: {file_path}, 오류: {e}", level=logging.WARNING) + self.logger.log(f"총 {len(all_detail_files)}개의 detail 임시 파일 정리 완료", level=logging.INFO) + else: + self.logger.log("정리할 detail 임시 파일이 없습니다.", level=logging.DEBUG) + + except Exception as e: + self.logger.log(f"detail 임시 파일 처리 중 오류 발생: {e}", level=logging.ERROR) + + async def get_ckeditor_data(self): + """CKEditor에서 HTML 데이터를 가져옵니다.""" + try: + # # 소스 버튼 locator + # source_button_locator = self.locator_manager.get_locator('BrowserControl', 'source_button_locator') + # # 소스 편집 영역 locator + # ck_source_editing_area_locator = self.locator_manager.get_locator('BrowserControl', 'ck_source_editing_area_locator') + + # 소스 모드로 전환 + source_button = await self.page.wait_for_selector(self.source_button_locator, timeout=8000) + await source_button.click() + self.logger.log("소스 버튼 클릭 완료.", level=logging.DEBUG) + + # 데이터 가져오기 + textarea = await self.page.wait_for_selector(self.ck_source_editing_area_locator, timeout=5000) + html_data = await textarea.get_attribute("data-value") + + # 다시 에디터 모드로 전환 + await self.page.click(self.source_button_locator) + self.logger.log("소스 버튼 재클릭 완료.", level=logging.DEBUG) + + return html_data + except Exception as e: + self.logger.log(f"CKEditor 데이터 가져오기 실패: {e}", level=logging.ERROR) + return "" + + async def clear_ckeditor_data(self): + """CKEditor의 내용을 지웁니다.""" + try: + # # 소스 버튼 locator + # source_button_locator = self.locator_manager.get_locator('BrowserControl', 'source_button_locator') + # # 소스 편집 영역 locator + # ck_source_editing_area_locator = self.locator_manager.get_locator('BrowserControl', 'ck_source_editing_area_locator') + + # 소스 모드로 전환 + await self.page.click(self.source_button_locator) + self.logger.log("소스 버튼 클릭 완료.", level=logging.DEBUG) + + # 데이터 지우기 + await self.page.evaluate(f'document.querySelector("{self.ck_source_editing_area_locator}").setAttribute("data-value", "")') + self.logger.log("CKEditor 데이터 지우기 완료.", level=logging.DEBUG) + + # 다시 에디터 모드로 전환 + await self.page.click(self.source_button_locator) + self.logger.log("소스 버튼 재클릭 완료.", level=logging.DEBUG) + + return True + except Exception as e: + self.logger.log(f"CKEditor 데이터 지우기 실패: {e}", level=logging.ERROR) + return False + + + def is_valid_html(self, html_data: str) -> bool: + """ + HTML 데이터가 정상적인지 확인하는 메서드 + 조건: 태그가 포함되어 있고 다수의 이미지 URL이 존재해야 함 + """ + try: + # 태그 내의 src 속성 값을 추출 + img_urls = re.findall(r']+src=["\'](.*?)["\']', html_data) + + # 최소 이미지 URL 개수를 기준으로 판단 (예: 1개 이상) + if len(img_urls) >= 1: + self.logger.log(f"HTML에 포함된 이미지 URL: {img_urls}", level=logging.DEBUG) + return True + else: + self.logger.log(f"HTML에 포함된 이미지 URL이 부족합니다. ({len(img_urls)}개)", level=logging.WARNING) + return False + except Exception as e: + self.logger.log(f"HTML 유효성 검사 중 오류 발생: {e}", level=logging.ERROR, exc_info=True) + return False + + + def fetch_image_urls(self, html_content): + """ + HTML 콘텐츠에서 모든 태그의 URL을 추출하는 함수. +
안의 태그와 독립된 태그 모두 처리하며, + 지정된 도메인으로 시작하는 이미지는 제외한다. + """ + soup = BeautifulSoup(html_content, "html.parser") + + # 제외할 URL 접두사 모음 (필요 시 여기서만 손보면 됨) + EXCLUDE_PREFIXES: tuple[str, ...] = ( + "https://assets.alicdn.com", + "https://gtms01.alicdn.com", + ) + + image_urls: list[str] = [] # 최종 반환 목록 + excluded_urls: list[str] = [] # 필터링된 URL 목록 (디버깅용) + + # 모든 태그를 순회 + for img in soup.find_all("img"): + if "src" not in img.attrs: + continue # src 없는 태그는 건너뜀 + + image_url: str = img["src"] + + # 특정 도메인 시작이면 제외 + if image_url.startswith(EXCLUDE_PREFIXES): + excluded_urls.append(image_url) + continue + + image_urls.append(image_url) + + # 디버깅 로그 + self.logger.log( + f"fetch_image_urls ▶ 추출 이미지 URL 수 : {len(image_urls)}개", level=logging.DEBUG + ) + self.logger.log( + f"fetch_image_urls ▶ 추출 이미지 URL 목록 : {image_urls}", level=logging.DEBUG + ) + self.logger.log( + f"fetch_image_urls ▶ 제외된 이미지 URL 수 : {len(excluded_urls)}개", level=logging.DEBUG + ) + self.logger.log( + f"fetch_image_urls ▶ 제외된 이미지 URL 목록 : {excluded_urls}", + level=logging.DEBUG, + ) + + return image_urls + + + async def upload_image(self, img_path): + """ + 이미지를 CKEditor에 업로드합니다. + + Args: + img_path (str): 업로드할 이미지 파일 경로 + + Returns: + bool: 업로드 성공 여부 + """ + try: + # (1) 업로드 시도 전에 파일 경로가 로컬에 존재하는지 확인 + if (not img_path) or (not os.path.isfile(img_path)): + self.logger.log(f"유효하지 않은 이미지 경로로 업로드를 건너뜀: {img_path}", level=logging.WARNING) + return False + # 업로드 이전 이미지 리스트 저장 + existing_images = await self.page.evaluate(""" + () => Array.from(document.querySelectorAll('img')).map(img => img.src) + """) + + # 1. 파일 업로드 + file_input = self.page.locator(self.cke_img_file_input_locator) + await file_input.set_input_files(img_path) + self.logger.log(f"이미지가 성공적으로 업로드되었습니다: {img_path}", level=logging.INFO) + + # 2. 커서 이동 + await self.page.keyboard.press("ArrowRight") + await self.page.keyboard.press("ArrowRight") + + # 업로드된 이미지 URL 확인 + current_images = await self.page.evaluate(""" + () => Array.from(document.querySelectorAll('img')).map(img => img.src) + """) + new_images = list(set(current_images) - set(existing_images)) + + if new_images: + # self.logger.log(f"업로드된 이미지 확인: {new_images[0]}", level=logging.DEBUG) + return True + else: + self.logger.log("업로드된 이미지를 찾을 수 없습니다.", level=logging.WARNING) + return False + + except Exception as e: + self.logger.log(f"이미지 업로드 중 오류 발생: {e}", level=logging.ERROR, exc_info=True) + return False + + async def input_option_list(self, option_data, input_field): + lines = ["# 옵션 목록"] + for i, key in enumerate(option_data.keys(), 1): + lines.append(f"- {i}. {key}") + lines += ["", "### 나열된 옵션목록 이외의 옵션이 필요하실 경우 고객센터로 연락주세요.", "---", ""] + await input_field.fill("\n".join(lines)) + + async def input_detail_text(self, optionHandler, input_field): + """ + 상세페이지에 소개글과 옵션 데이터를 입력하는 메서드 + """ + try: + if not input_field: + self.logger.log("텍스트 입력 필드를 찾을 수 없습니다.", level=logging.ERROR) + return + + # -------------------------------------------------- + # 1. VIP 등록모드 랜덤 소개 문구 (맨 윗줄) + # -------------------------------------------------- + vip_intro_block = "" + if (self.toggle_states.get('ed_mode', False) and + self.toggle_states.get('vip_detail_edit', False)): + try: + random_intro = self._get_random_biz_intro() + vip_intro_block = f"{random_intro}\n\n---\n" + self.logger.log("VIP 등록모드: 랜덤 소개 문구 추가됨", level=logging.INFO) + except Exception as e: + self.logger.log(f"VIP 랜덤 소개 문구 생성 실패: {e}", level=logging.WARNING) + + # -------------------------------------------------- + # 2. 소개글 (기존 유지) + # -------------------------------------------------- + leading_lines = [t for t in self.detail_text_widget.get_lines() if t] + leading_block = "\n".join(leading_lines) + + # -------------------------------------------------- + # 2. 옵션 목록 (범용적인 고급 마크다운) + # -------------------------------------------------- + option_data = optionHandler.get_all_translated_options() + is_single = optionHandler.option_info.get("is_single_option", True) + + options_block = "" + if option_data and (len(option_data) > 1 or not is_single): + lines = [ + "", + "### 🚀 **구매대행 특별 혜택**", + "", + "> 🎯 **전문 구매대행 서비스**", + "- **해외직구 전문가**가 직접 상품 선별 및 구매배송", + "**철저한 검수** 및 **꼼꼼한 포장**", + "**빠른 배송** 및 **고객 맞춤형 서비스**", + "", + "💬 **문의 및 주문 안내**", + "나열된 옵션목록 이외의 옵션이 필요하시거나, ", + "**특별 주문**이 필요하시면 언제든 고객센터로 연락주세요!", + "⚡ **빠른 응답**으로 고객님의 만족을 최우선으로 합니다.", + "", + "", + "---", + "### 📋 **옵션 목록**", + "", + "" + ] + + # 이미 넘버링이 적용된 옵션명을 일관된 이모지와 함께 사용 (마크다운 자동 넘버링 방지) + # 상품별로 하나의 이모지를 선택하여 모든 옵션에 동일하게 사용 + selected_emoji = self._get_option_emoji(0, style="random") + for i, option in enumerate(option_data.keys()): + lines.append(f"{selected_emoji} **{option}**") + + lines.extend([ + "", + "---", + "", + "" + ]) + + options_block = "\n".join(lines) + + # -------------------------------------------------- + # 3. 과거 형식 옵션 목록 자동 정리 (선택적) + # -------------------------------------------------- + # 기존 내용에서 과거 형식 옵션 목록이 있다면 정리 시도 + if leading_block and option_data: + cleaned_content = self._replace_or_append_options_section(leading_block, options_block, option_data) + if cleaned_content and cleaned_content != leading_block: + leading_block = cleaned_content + self.logger.log("기존 소개글 내의 과거 옵션 목록을 정리했습니다.", level=logging.INFO) + + # -------------------------------------------------- + # 4. 최종 조합 & 입력 + # -------------------------------------------------- + # VIP 소개 문구, 기존 소개글, 옵션 목록 순서로 조합 + text_blocks = [] + if vip_intro_block: + text_blocks.append(vip_intro_block.strip()) + if leading_block: + text_blocks.append(leading_block) + if options_block: + text_blocks.append(options_block) + + bulk_text = "\n".join(text_blocks).strip() + + try: + await input_field.click() + await input_field.fill("") # 내용 초기화 + await input_field.press("Enter") + + # 청크 단위 입력으로 변경 + await self.input_detail_text_chunked(input_field, bulk_text) + + except Exception as e: + self.logger.log(f"CKEditor HTML 입력 실패: {e}", level=logging.ERROR, exc_info=True) + + self.logger.log("상세페이지 텍스트(소개 + 옵션) 일괄 입력 완료", level=logging.INFO) + + except Exception as e: + self.logger.log(f"상세페이지 텍스트 입력 중 오류 발생: {e}", level=logging.ERROR, exc_info=True) + + async def input_detail_text_chunked(self, input_field, bulk_text): + """청크 단위로 텍스트를 입력하는 메서드""" + # 긴 텍스트를 청크로 나누어 입력 + chunk_size = 1000 # 한 번에 입력할 문자 수 + chunks = [bulk_text[i:i+chunk_size] for i in range(0, len(bulk_text), chunk_size)] + + for i, chunk in enumerate(chunks): + try: + await input_field.type(chunk, delay=1, timeout=60000) + self.logger.log(f"청크 {i+1}/{len(chunks)} 입력 완료", level=logging.DEBUG) + except Exception as e: + self.logger.log(f"청크 {i+1} 입력 실패: {e}", level=logging.ERROR) + raise + + async def process_detail(self, optionHandler, toggle_states): + """상세페이지 번역을 처리합니다. + + Args: + optionHandler: 옵션 핸들러 객체 + + Returns: + bool: 처리 성공 여부 + """ + self.logger.log("상세페이지 처리를 시작합니다.", level=logging.INFO) + + # 이전 상품의 detail 관련 임시파일들 정리 + self.reset_state() + + + # 사용값 설정 + self.interval = toggle_states.get('interval', 3.0) + self.watingTime = toggle_states.get('watingTime', 20) + + self.detail_Option = toggle_states.get('detail_Option', False) + self.detail_IMGTrans = toggle_states.get('detail_IMGTrans', False) + self.detail_IMGTrans_type = toggle_states.get('detail_IMGTrans_type', True) + self.unwanted_words = toggle_states.get('unwanted_words', False) + + self.logger.log(f"self.toggle_states : {self.toggle_states}", level=logging.DEBUG) + + try: + + # 현재 HTML 가져오기 + current_html = await self.get_ckeditor_data() + + if not current_html or not self.is_valid_html(current_html): + self.logger.log("유효한 HTML을 가져오지 못했습니다.", level=logging.WARNING) + return False + + # 이미지 URL 추출 + image_urls = self.fetch_image_urls(current_html) + original_image_count = len(image_urls) + self.logger.log(f"추출된 이미지 URL 수: {original_image_count}", level=logging.INFO) + + if not image_urls: + self.logger.log("변환할 이미지가 없습니다.", level=logging.INFO) + return False + + # 텍스트 입력 필드 선택 + input_field = self.page.locator(self.cke_text_editing_area_locator) + + # 상세페이지 옵션에 따라 처리 + self.logger.log(f"self.detail_Option : {self.detail_Option}", level=logging.DEBUG) + self.logger.log(f"self.detail_IMGTrans : {self.detail_IMGTrans}", level=logging.DEBUG) + + if (self.detail_Option or self.detail_IMGTrans) and (self.browser_controller.image_worker_mgr is not None): + + # if self.detail_Option: + # self.logger.log("소개글 입력 시작...", level=logging.INFO) + # await self.input_detail_text(optionHandler, input_field) + + if self.detail_IMGTrans: + self.logger.log("이미지 번역 시작...", level=logging.INFO) + self.set_progress_visible_signal.emit(True) + + self.logger.log(f"이미지 번역 엔진 : {self.detail_IMGTrans_type}", level=logging.DEBUG) + + # if (self.detail_IMGTrans_type in ('CPU', '자체서버')) and (self.imageProcessor is not None): + if self.detail_IMGTrans_type and (self.browser_controller.image_worker_mgr is not None): + + # CKEditor 내용 지우기 + await self.clear_ckeditor_data() + + # 텍스트 입력 필드 선택 + await input_field.click() + self.logger.log("입력 필드 선택", level=logging.DEBUG) + + # 소개글과 옵션데이터 입력 + if self.detail_Option: + await self.input_detail_text(optionHandler, input_field) + + # 이미지 처리 통계 + success_count = 0 + excluded_count = 0 + error_count = 0 + translated_count = 0 + + # 임시 파일 정리를 위한 리스트 + processed_files = [] + + # 모든 이미지 처리 + self.logger.log("이미지 처리 및 업로드 시작...", level=logging.INFO) + + # OCR 데이터 수집을 위한 리스트 (상품 요약 생성용) + all_ocr_data = [] + + # 번역 파라미터 준비 + font_type = self.browser_controller.toggle_states.get('font_type', '') + unwanted_texts = list(self.browser_controller.unwanted_words.keys()) if hasattr(self.browser_controller, 'unwanted_words') else [] + is_member_valid = self.browser_controller.user_membership_level == "premium" + authenticated_by_admin = self.browser_controller.authenticated_by_admin + + for idx, image_url in enumerate(image_urls): + try: + # detail 구분자를 포함한 임시파일명으로 처리 + try: + final_image_result = await self.browser_controller.image_processor.process_image_url( + original_image_url=image_url, index=idx, file_prefix="detail", + font_type=font_type, + unwanted_texts=unwanted_texts, + is_member_valid=is_member_valid, + authenticated_by_admin=authenticated_by_admin + ) + except Exception: + raise + + # 서버가 반환한 child_results(분할 조각) 처리 + child_results = (final_image_result.get('child_results') + if isinstance(final_image_result, dict) else None) or [] + if child_results: + for split_idx, split_result in enumerate(child_results, 1): + split_status = split_result.get('status') + split_path = split_result.get('path') + if split_status == 'translated' and split_path: + translated_count += 1 + split_upload_success = await self.upload_image(split_path) + if split_upload_success: + success_count += 1 + self.logger.log(f"분할 이미지 {idx+1}-{split_idx+1} 업로드 성공", level=logging.INFO) + else: + error_count += 1 + if split_path: + processed_files.append(split_path) + else: + # 구버전 서버 호환: 내부 분할 목록이 있을 경우 기존 보조 경로 사용 + split_results_fallback = await self.process_split_images_if_any(image_url, idx) + if split_results_fallback: + for split_idx, split_result in enumerate(split_results_fallback, 1): + split_status = split_result.get('status') + split_path = split_result.get('path') + if split_status == 'translated' and split_path: + translated_count += 1 + split_upload_success = await self.upload_image(split_path) + if split_upload_success: + success_count += 1 + self.logger.log(f"분할 이미지 {idx+1}-{split_idx+1} 업로드 성공", level=logging.INFO) + else: + error_count += 1 + if split_path: + processed_files.append(split_path) + + + status = final_image_result.get('status') + translated_image_path = final_image_result.get('path') + + # 🔧 개선: 모든 경우에 생성된 파일을 정리 목록에 추가 (한 번만) + if translated_image_path and os.path.exists(translated_image_path): + processed_files.append(translated_image_path) + self.logger.log(f"이미지 {idx+1} 처리 결과 파일 정리 예약: {translated_image_path}", level=logging.DEBUG) + + if status == 'translated': + translated_count += 1 + upload_success = await self.upload_image(translated_image_path) + elif status in ('failed', 'original'): + # 실패/원본인 경우 로컬 파일이 실제 존재할 때만 업로드 시도 + if translated_image_path and os.path.isfile(translated_image_path): + upload_success = await self.upload_image(translated_image_path) + else: + upload_success = False + self.logger.log(f"이미지 {idx+1} 실패/원본 처리: 로컬 파일이 존재하지 않아 업로드를 건너뜀 → {translated_image_path}", level=logging.WARNING) + elif status == 'exclude': + excluded_count += 1 + self.logger.log(f"이미지 {idx+1} 제외됨: {image_url}", level=logging.INFO) + # 제외된 경우에는 업로드하지 않음 + continue + elif isinstance(final_image_result, str) and final_image_result: + # (이전 방식과의 호환성) + upload_success = await self.upload_image(final_image_result) + else: + # 예상하지 못한 반환값 + error_count += 1 + self.logger.log(f"이미지 {idx+1} 처리 결과 예상하지 못한 값: {final_image_result}", level=logging.WARNING) + continue + + # 인페인팅 방식/장치 집계 + try: + if isinstance(final_image_result, dict): + used = final_image_result.get('inpaint_method') + dev = (final_image_result.get('inpaint_device') or '').lower() + if hasattr(self.browser_controller, 'add_image_edit_stats'): + if used == 'cv': + self.browser_controller.add_image_edit_stats(inpaint_cv=1) + elif used == 'migan': + self.browser_controller.add_image_edit_stats(inpaint_migan=1) + elif used == 'request': + self.browser_controller.add_image_edit_stats(inpaint_request=1) + if dev == 'cpu': + self.browser_controller.add_image_edit_stats(inpaint_device_cpu=1) + elif dev == 'cuda': + self.browser_controller.add_image_edit_stats(inpaint_device_cuda=1) + elif dev == 'directml': + self.browser_controller.add_image_edit_stats(inpaint_device_directml=1) + elif dev == 'server': + self.browser_controller.add_image_edit_stats(inpaint_device_server=1) + except Exception: + pass + + # 성공/실패 카운트 및 로그 처리 + if upload_success: + success_count += 1 + self.logger.log(f"이미지 {idx+1} 업로드 성공: {translated_image_path}", level=logging.INFO) + else: + error_count += 1 + self.logger.log(f"이미지 {idx+1} 업로드 실패: {translated_image_path}", level=logging.WARNING) + + except Exception as e: + error_count += 1 + self.logger.log(f"이미지 {idx+1} 처리 중 오류: {e}", level=logging.ERROR) + + # 프로그레스 업데이트 + self.update_detail_progress_signal.emit(idx+1, len(image_urls)) + self.logger.log(f"이미지 처리 진행률: {idx+1}/{len(image_urls)}", level=logging.INFO) + + # 모든 이미지 처리 완료 후 OCR 데이터 수집 + store_ocr_to_db = self.toggle_states.get('store_ocr_data_to_db', False) + if store_ocr_to_db: + all_ocr_data = await self.collect_ocr_data_from_db() + else: + # 메모리에서 OCR 데이터 수집 (기본 방식) + if self.imageProcessor and hasattr(self.imageProcessor, 'get_session_ocr_data'): + all_ocr_data = self.imageProcessor.get_session_ocr_data() + self.logger.log(f"세션 OCR 데이터 수집 완료: {len(all_ocr_data) if all_ocr_data else 0}개", level=logging.DEBUG) + else: + self.logger.log("⚠️ ImageProcessor가 초기화되지 않아 OCR 데이터 수집 건너뜀", level=logging.WARNING) + all_ocr_data = [] + + # 처리 결과 로깅 + self.logger.log(f"이미지 처리 완료 - 성공: {success_count}, 제외: {excluded_count}, 오류: {error_count}, 총: {len(image_urls)}", level=logging.INFO) + + # 모든 이미지 처리 완료 후 임시 파일 배치 정리 + if processed_files: + self.logger.log(f"임시 파일 배치 정리 시작: {len(processed_files)}개 파일", level=logging.INFO) + deleted_count = 0 + + for file_path in processed_files: + try: + if file_path and os.path.exists(file_path): + os.remove(file_path) + deleted_count += 1 + self.logger.log(f"임시 파일 삭제: {file_path}", level=logging.DEBUG) + except Exception as e: + self.logger.log(f"파일 삭제 실패 (무시): {file_path} - {e}", level=logging.DEBUG) + + self.logger.log(f"임시 파일 정리 완료: {deleted_count}/{len(processed_files)}개 삭제", level=logging.INFO) + + # 번역 완료 후 프로그레스바 숨김 + self.set_progress_visible_signal.emit(False) + + # OCR 데이터 수집 완료 로깅 + if all_ocr_data: + self.logger.log(f"OCR 데이터 수집 완료: {len(all_ocr_data)}개 이미지에서 데이터 추출", level=logging.INFO) + else: + self.logger.log("OCR 데이터가 수집되지 않았습니다.", level=logging.WARNING) + + # 세션 OCR 데이터 정리 (메모리 모드인 경우) + if self.imageProcessor and hasattr(self.imageProcessor, 'clear_session_ocr_data'): + self.imageProcessor.clear_session_ocr_data() + self.logger.log("세션 OCR 데이터 정리 완료", level=logging.DEBUG) + + # 상세이미지 통계 요약 및 집계 반영 + try: + self.logger.log( + f"📊 상세페이지 이미지 통계: 총={original_image_count}, 번역됨={translated_count}, 제외={excluded_count}, 오류={error_count}", + level=logging.INFO, + ) + if hasattr(self.browser_controller, 'add_image_edit_stats'): + self.browser_controller.add_image_edit_stats( + detail_total=original_image_count, + detail_translated=translated_count, + ) + except Exception: + pass + + # 추가 줄바꿈 입력 + await self.page.keyboard.press("ArrowRight") + await self.page.keyboard.press("Enter") + await self.page.keyboard.press("Enter") + await self.page.keyboard.press("Enter") + + else: + # 상세페이지 옵션이 비활성화된 경우 + self.logger.log("상세페이지 옵션이 비활성화되어 있습니다.", level=logging.INFO) + return False + + except Exception as e: + self.logger.log(f"process_detail 중 오류 발생: {str(e)}", level=logging.ERROR) + self.set_progress_visible_signal.emit(False) + return False + + # async def update_detail_for_ed_mode(self, optionHandler): + # """ + # ED 모드에서 옵션명이 변경되었을 때 상세페이지의 옵션 목록만 업데이트하는 메서드 + # """ + # try: + # self.logger.log("ED 모드: 상세페이지 옵션 목록 업데이트 시작", level=logging.INFO) + + # # 텍스트 입력 필드 선택 + # input_field = self.page.locator(self.cke_text_editing_area_locator) + + # # 기존 내용 가져오기 + # current_content = await self.get_ckeditor_data() + # if not current_content: + # self.logger.log("기존 상세페이지 내용을 가져올 수 없습니다.", level=logging.WARNING) + # return False + + # # 옵션 목록 부분만 새로 생성 + # option_data = optionHandler.get_all_translated_options() + + # if not option_data: + # self.logger.log("업데이트할 옵션 데이터가 없습니다.", level=logging.WARNING) + # return False + + # # 새로운 옵션 목록 섹션 생성 + # new_options_section = self._generate_options_section_for_ed_mode(option_data) + + # # 기존 옵션 목록 섹션을 찾아서 교체 시도 + # updated_content = self._replace_or_append_options_section(current_content, new_options_section, option_data) + + # if updated_content is None: + # # 패턴이 없으면 기존 내용을 건드리지 않음 + # self.logger.log("ED 모드: 기존 옵션 목록 패턴이 없어 상세페이지를 업데이트하지 않습니다.", level=logging.INFO) + # return False + + # # 패턴이 있으면 해당 부분만 교체된 내용으로 CKEditor 업데이트 + # await input_field.click() + # await input_field.fill("") # 전체 내용을 새로운 내용으로 교체 + # await self.input_detail_text_chunked(input_field, updated_content) + + # self.logger.log("ED 모드: 상세페이지 옵션 목록 업데이트 완료", level=logging.INFO) + # return True + + # except Exception as e: + # self.logger.log(f"ED 모드 상세페이지 업데이트 중 오류: {e}", level=logging.ERROR, exc_info=True) + # return False + + def _generate_options_section_for_ed_mode(self, option_data, clean_legacy=False): + """ED 모드용 옵션 섹션 생성""" + lines = [ + "", + "---", + "### 📋 **옵션 목록** ", + "", + "" + ] + + if clean_legacy: + # 과거 형식 정리 시에는 번호 없이 깔끔하게 + lines.append("") + for i, option in enumerate(option_data.keys()): + # 번호 제거한 깔끔한 형태 + clean_option = self._remove_numbering_from_option(option) + emoji = self._get_option_emoji(i, style="geometric") # 정리된 옵션은 일관된 스타일 + lines.append(f"{emoji} **{clean_option}**") + else: + # 일반적인 경우: 이미 넘버링이 적용된 옵션명을 다양한 이모지와 함께 사용 + for i, option in enumerate(option_data.keys()): + emoji = self._get_option_emoji(i, style="geometric") # ED 모드는 기하학적 스타일 + lines.append(f"{emoji} **{option}**") + + lines.extend([ + "", + "---", + "", + "" + ]) + + return "\n".join(lines) + + def _remove_numbering_from_option(self, option_text): + """옵션명에서 넘버링을 제거하고 깔끔한 텍스트만 반환""" + + # 앞쪽 넘버링 패턴들 제거 + patterns = [ + r'^[A-Z]\.\s*', # A. B. C. + r'^[a-z]\.\s*', # a. b. c. + r'^[ㄱ-ㅎ]\.\s*', # ㄱ. ㄴ. ㄷ. + r'^[가-하]\.\s*', # 가. 나. 다. + r'^\d+\.\s*', # 1. 2. 3. + r'^[IVX]+\.\s*', # I. II. III. + r'^[①②③④⑤⑥⑦⑧⑨⑩⑪⑫⑬⑭⑮⑯⑰⑱⑲⑳]\s*', # 동그라미 숫자 + r'^\$\d+\.\s*', # $1. $2. + r'^\(\d+\)\s*', # (1) (2) + r'^\[\d+\]\s*', # [1] [2] + r'^\d+\)\s*', # 1) 2) + ] + + clean_text = option_text + for pattern in patterns: + clean_text = re.sub(pattern, '', clean_text) + + return clean_text.strip() + + def _get_option_emoji(self, index=0, style="geometric"): + """옵션 목록용 이모지를 반환합니다""" + + emoji_sets = { + "check": ["✅", "☑️", "✔️", "🗸"], # 체크 계열 + "geometric": ["🔸", "🔹", "◆", "◇", "▪️", "▫️"], # 기하학적 모양 + "star": ["⭐", "🌟", "✨", "💫", "🌠"], # 별 계열 + "arrow": ["▶️", "➡️", "👉", "📌", "🔻"], # 화살표/포인터 계열 + "colorful": ["🔴", "🟠", "🟡", "🟢", "🔵", "🟣"], # 색상 계열 + } + + if style == "mixed": + # 일관성을 위해 geometric 스타일로 통일 (상품 내 모든 옵션에 동일한 이모지 사용) + chosen_set = emoji_sets["geometric"] + return chosen_set[0] # 항상 첫 번째 이모지 사용 + elif style == "random": + # 완전 랜덤 선택 (모든 이모지 중에서 랜덤하게 하나 선택) + all_emojis = [] + for emoji_list in emoji_sets.values(): + all_emojis.extend(emoji_list) + return random.choice(all_emojis) + elif style == "random_style": + # 랜덤 스타일 선택 후 첫 번째 이모지만 사용 + random.seed(42) # 고정 시드로 스타일 선택 + chosen_style = random.choice(list(emoji_sets.keys())) + chosen_set = emoji_sets[chosen_style] + return chosen_set[0] # 해당 스타일의 첫 번째 이모지만 사용 + elif style in emoji_sets: + # 특정 스타일의 첫 번째 이모지만 사용 + chosen_set = emoji_sets[style] + return chosen_set[0] # 항상 첫 번째 이모지 사용 + else: + # 기본값 (geometric의 첫 번째 이모지) + return emoji_sets["geometric"][0] + + def _replace_or_append_options_section(self, content, new_options_section, current_option_data=None): + """기존 옵션 섹션을 찾아서 교체. 패턴이 없으면 None 반환 (기존 내용 보존)""" + + # 1. 현재 형식의 옵션 목록 패턴 찾기 + current_options_pattern = r'---\s*\n### 📋 \*\*옵션 목록\*\*[^\n]*\n.*?\n---' + + if re.search(current_options_pattern, content, re.DOTALL): + # 현재 형식 옵션 섹션이 있으면 교체 + updated_content = re.sub(current_options_pattern, new_options_section.strip(), content, flags=re.DOTALL) + self.logger.log("현재 형식의 옵션 섹션을 새로운 내용으로 교체했습니다.", level=logging.DEBUG) + return updated_content + + # # 2. 과거 형식의 다양한 옵션 목록 패턴들 찾기 + # legacy_patterns = [ + # r'옵션\s*목록[^\n]*\n(.*?)(?=\n\n|\n---|\n###|$)', # 일반적인 "옵션 목록" 헤딩 + # r'###\s*옵션\s*목록[^\n]*\n(.*?)(?=\n\n|\n---|\n###|$)', # ### 옵션 목록 + # r'## 옵션\s*목록[^\n]*\n(.*?)(?=\n\n|\n---|\n###|$)', # ## 옵션 목록 + # r'# 옵션\s*목록[^\n]*\n(.*?)(?=\n\n|\n---|\n###|$)', # # 옵션 목록 + # r'\*\*옵션\s*목록\*\*[^\n]*\n(.*?)(?=\n\n|\n---|\n###|$)', # **옵션 목록** + # ] + + # for pattern in legacy_patterns: + # match = re.search(pattern, content, re.DOTALL | re.IGNORECASE) + # if match: + # self.logger.log(f"과거 형식의 옵션 목록 패턴을 발견했습니다: {pattern[:30]}...", level=logging.INFO) + + # # 과거 옵션 목록에서 번호 제거 및 정리 + # legacy_options_content = match.group(1) if len(match.groups()) > 0 else match.group(0) + # cleaned_options = self._clean_legacy_options(legacy_options_content) + + # if cleaned_options: + # # 과거 형식 발견: 현재 옵션 데이터가 있으면 우선 사용, 없으면 과거 옵션 정리 + # if current_option_data and len(current_option_data) > 0: + # # 현재 옵션 데이터가 있으면 기존 new_options_section 사용 + # updated_content = re.sub(pattern, new_options_section.strip(), content, flags=re.DOTALL | re.IGNORECASE) + # self.logger.log(f"과거 형식 옵션 섹션을 현재 옵션 데이터로 업데이트했습니다.", level=logging.INFO) + # else: + # # 현재 옵션 데이터가 없으면 과거 옵션을 정리해서 사용 + # clean_new_section = self._generate_options_section_for_ed_mode({}, clean_legacy=True) + + # # 과거 옵션들을 정리해서 새로운 섹션에 추가 + # lines = clean_new_section.split('\n') + # insert_index = -4 # "---" 앞에 삽입 + + # # 다양한 이모지로 옵션 정리 (과거 옵션은 특별한 스타일로) + # for i, cleaned_option in enumerate(cleaned_options): + # emoji = self._get_option_emoji(i, style="geometric") # 과거 옵션 정리는 일관된 스타일 + # lines.insert(insert_index, f"{emoji} **{cleaned_option}**") + # insert_index += 1 + + # final_new_section = '\n'.join(lines) + # updated_content = re.sub(pattern, final_new_section.strip(), content, flags=re.DOTALL | re.IGNORECASE) + # self.logger.log(f"과거 형식의 옵션 섹션을 번호 제거하여 정리했습니다: {len(cleaned_options)}개 옵션", level=logging.INFO) + + # return updated_content + + # 옵션 섹션이 없으면 기존 내용을 건드리지 않음 + self.logger.log("기존 옵션 섹션 패턴을 찾을 수 없어 내용을 변경하지 않습니다.", level=logging.INFO) + return None + + def _clean_legacy_options(self, legacy_content): + """과거 형식의 옵션 목록에서 번호와 특수 기호를 제거하고 정리""" + + if not legacy_content or not legacy_content.strip(): + return [] + + # 줄별로 분리 + lines = legacy_content.split('\n') + cleaned_options = [] + + for line in lines: + line = line.strip() + if not line: + continue + + # 다양한 번호 패턴 제거 + patterns_to_remove = [ + r'^\d+\.\s*', # 1. 2. 3. 형태 + r'^\d+\)\s*', # 1) 2) 3) 형태 + r'^-\d+[\.,]?\s*', # -1, -2. -3 형태 + r'^[-*•]\s*\d+[\.,]?\s*', # - 1. * 2. • 3. 형태 + r'^[○●◦◉]\s*-?\d+[\.,]?\s*', # ○-1. ●2. ◦3 형태 (동그라미 등) + r'^[①②③④⑤⑥⑦⑧⑨⑩⑪⑫⑬⑭⑮⑯⑰⑱⑲⑳]\s*', # 동그라미 숫자 + r'^[가나다라마바사아자차카타파하]\.\s*', # 가. 나. 다. 형태 + r'^[ㄱ-ㅎ]\.\s*', # ㄱ. ㄴ. ㄷ. 형태 + r'^[A-Z]\.\s*', # A. B. C. 형태 + r'^[a-z]\.\s*', # a. b. c. 형태 + r'^[IVX]+\.\s*', # I. II. III. 로마숫자 형태 + r'^[-*•]\s*', # 단순 리스트 마커 + r'^\s*[\-\*\+]\s*', # 마크다운 리스트 마커 + r'^\s*>\s*', # 인용 마커 + ] + + cleaned_line = line + for pattern in patterns_to_remove: + cleaned_line = re.sub(pattern, '', cleaned_line, flags=re.IGNORECASE) + + # 남은 내용이 있으면 추가 + cleaned_line = cleaned_line.strip() + if cleaned_line: + # HTML 태그나 마크다운 볼드 제거 + cleaned_line = re.sub(r'<[^>]+>', '', cleaned_line) # HTML 태그 제거 + cleaned_line = re.sub(r'\*\*(.*?)\*\*', r'\1', cleaned_line) # **텍스트** → 텍스트 + cleaned_line = re.sub(r'\*(.*?)\*', r'\1', cleaned_line) # *텍스트* → 텍스트 + cleaned_line = cleaned_line.strip() + + if cleaned_line and len(cleaned_line) > 1: # 최소 길이 확인 + cleaned_options.append(cleaned_line) + + # 중복 제거 + unique_options = [] + seen = set() + for option in cleaned_options: + if option.lower() not in seen: + unique_options.append(option) + seen.add(option.lower()) + + self.logger.log(f"과거 옵션 목록 정리 완료: {len(legacy_content.split())}개 라인 → {len(unique_options)}개 옵션", level=logging.DEBUG) + return unique_options + + def _get_random_biz_intro(self): + """사업자 정보에서 랜덤 소개 문구를 가져옵니다 (VIP 등록모드용)""" + try: + from src.limited_contents.bizDBManager import BizDBManager + import random + + # bizinfo.db에서 사업자 정보 가져오기 + biz_db_path = "user_data/bizinfo.db" # 실제 경로에 맞게 수정 필요 + biz_manager = BizDBManager(biz_db_path) + + # 선택마켓 중 스마트스토어 마켓의 사업자 정보 가져오기 + smartstore_biz = self._get_smartstore_selected_biz(biz_manager) + + if not smartstore_biz: + self.logger.log("설정된 선택마켓이 없어 기본 문구를 사용합니다.", level=logging.WARNING) + return self._get_default_biz_intro() + + # 선택마켓의 사업자 이름으로 소개 문구 생성 + biz_name = smartstore_biz.get('name', '').strip() + if biz_name: + default_intros = [ + f"🏪 **{biz_name}**에서 직접 선별한 프리미엄 상품입니다.", + f"✨ **{biz_name}** 전문 큐레이션으로 엄선된 고품질 제품을 만나보세요.", + f"🎯 **{biz_name}**가 추천하는 베스트 아이템으로 특별한 경험을 선사합니다.", + f"💎 **{biz_name}**의 전문성과 신뢰를 바탕으로 한 최고 품질의 상품입니다.", + f"🛒 **{biz_name}**의 엄격한 품질 관리를 통과한 검증된 상품입니다.", + f"🌟 **{biz_name}**에서 고객님을 위해 특별히 준비한 프리미엄 아이템입니다." + ] + + selected_intro = random.choice(default_intros) + self.logger.log(f"사업자({biz_name}) 기반 소개 문구 생성: {selected_intro}", level=logging.DEBUG) + return selected_intro + else: + return self._get_default_biz_intro() + + except Exception as e: + self.logger.log(f"사업자 정보 조회 중 오류: {e}", level=logging.ERROR, exc_info=True) + return self._get_default_biz_intro() + + def _get_smartstore_selected_biz(self, biz_manager): + """선택마켓 중 스마트스토어로 지정된 마켓의 사업자 정보를 가져옵니다""" + try: + # 선택마켓 매핑 정보 가져오기 + market_selection = biz_manager.get_market_selection() + + # 스마트스토어 관련 마켓 타입들 확인 + smartstore_types = ['smartstore', '스마트스토어', 'naver_smartstore', 'naver'] + selected_biz_id = None + + for market_type, biz_id in market_selection.items(): + # 스마트스토어 관련 마켓 타입 찾기 + if any(stype in market_type.lower() for stype in ['smartstore', '스마트스토어', 'naver']): + selected_biz_id = biz_id + self.logger.log(f"스마트스토어 선택마켓 발견: {market_type} -> 사업자 ID {biz_id}", level=logging.DEBUG) + break + + if not selected_biz_id: + self.logger.log("선택마켓에 스마트스토어가 설정되지 않았습니다.", level=logging.INFO) + return None + + # 해당 사업자 정보 조회 + conn = biz_manager.conn + cursor = conn.cursor() + cursor.execute("SELECT * FROM biz WHERE id = ?", (selected_biz_id,)) + result = cursor.fetchone() + + if result: + biz_info = dict(result) + self.logger.log(f"스마트스토어 선택마켓의 사업자 정보: {biz_info.get('name', 'Unknown')}", level=logging.DEBUG) + return biz_info + else: + self.logger.log(f"사업자 ID {selected_biz_id}에 해당하는 사업자 정보를 찾을 수 없습니다.", level=logging.WARNING) + return None + + except Exception as e: + self.logger.log(f"스마트스토어 선택마켓 조회 중 오류: {e}", level=logging.ERROR, exc_info=True) + return None + + + def _get_default_biz_intro(self): + """기본 소개 문구들 중 랜덤 선택""" + import random + + default_intros = [ + "🌟 **전문 구매대행 서비스**로 엄선된 고품질 제품을 안전하게 배송해드립니다.", + "💯 **신뢰할 수 있는 품질 보증**과 함께 최상의 쇼핑 경험을 제공합니다.", + "🚀 **빠르고 안전한 배송**으로 고객님의 소중한 시간을 절약해드립니다.", + "🎁 **특별 할인 혜택**과 함께 프리미엄 상품을 합리적인 가격에 만나보세요.", + "⭐ **고객 만족도 1위** 구매대행 서비스의 검증된 품질을 경험해보세요." + ] + + selected = random.choice(default_intros) + self.logger.log(f"기본 소개 문구 선택: {selected}", level=logging.DEBUG) + return selected + + async def save_shortcut(self): + """저장 단축키(Ctrl+S)를 실행하는 메서드""" + try: + # 저장 확인 다이얼로그가 있는지 먼저 확인 + await self.handle_save_dialog_if_exists() + + self.logger.log("이미지 에디터 저장버튼 클릭", level=logging.DEBUG) + await self.page.click(self.save_btn_locator) + self.logger.log("수정사항 저장 버튼 클릭 완료", level=logging.DEBUG) + + editor_locator = self.page.locator(self.editor_modal_locator) + if await editor_locator.is_visible(): + # self.logger.log("이미지 에디터 모달 오픈 감지", level=logging.DEBUG) + # await self.page.click(self.save_btn_locator) + # self.logger.log("수정사항 저장 버튼 클릭 완료", level=logging.DEBUG) + + try: + # 닫힘 대기 (예: 최대 5초) + await self.page.wait_for_selector(self.editor_modal_locator, state="detached", timeout=5000) + self.logger.log("에디터 정상 종료", level=logging.INFO) + except Exception: + self.logger.log("에디터 닫힘 timeout! ESC로 강제 종료 시도", level=logging.WARNING) + await self.page.keyboard.press("1") + await asyncio.sleep(1) + await self.page.keyboard.press("Escape") + await self.page.wait_for_selector(self.editor_modal_locator, state="detached", timeout=5000) + + except Exception as e: + self.logger.log(f"저장 단축키 실행 실패: {e}", level=logging.ERROR, exc_info=True) + + async def handle_save_dialog_if_exists(self) -> bool: + """ + 저장 확인 다이얼로그가 있는지 확인하고 있으면 처리합니다. + 다이얼로그가 없으면 아무 작업도 하지 않습니다. + + Returns: + bool: 다이얼로그를 처리했으면 True, 없었으면 False + """ + try: + dialog_locator = self.page.locator(self.dialog_save_btn_locator) + try: + is_visible = await dialog_locator.is_visible(timeout=1000) + except Exception: + is_visible = False + + if is_visible: + self.logger.log("저장 확인 다이얼로그 감지됨 - 저장 버튼 클릭 시도", level=logging.INFO) + await dialog_locator.click() + await asyncio.sleep(0.2) + return True + else: + self.logger.log("저장 확인 다이얼로그 없음", level=logging.DEBUG) + return False + except Exception as e: + self.logger.log(f"저장 다이얼로그 확인 중 오류: {e}", level=logging.WARNING) + return False + + + async def click_and_wait_complete(self, btn_locator: str, timeout: float = 20.0, check_interval: float = 0.5) -> bool: + """ + 로딩 버튼 클릭 후 ant-btn-loading 상태 해제까지 대기. + 성공시 True, 실패(타임아웃 등)시 False 반환. + """ + try: + await self.page.click(btn_locator) + self.logger.log(f"버튼 클릭: {btn_locator}", level=logging.INFO) + + # 로딩 상태 진입 대기 (최대 2초) + loading_detected = False + for attempt in range(10): + try: + is_loading = await asyncio.wait_for( + self.page.locator(btn_locator).evaluate( + "el => el.classList.contains('ant-btn-loading')" + ), + timeout=3.0 + ) + if is_loading: + self.logger.log(f"로딩 상태 진입 감지: {btn_locator}", level=logging.DEBUG) + loading_detected = True + break + except asyncio.TimeoutError: + self.logger.log(f"로딩 상태 확인 타임아웃, 재시도 중... ({attempt+1}/10)", level=logging.WARNING) + continue + except (KeyError, Exception) as e: + # Playwright 내부 에러 및 네트워크 에러 처리 + if "data" in str(e) or isinstance(e, KeyError) or "ENOTFOUND" in str(e) or "getaddrinfo" in str(e): + self.logger.log(f"Playwright/네트워크 에러 발생, 재시도 중... ({attempt+1}/10): {e}", level=logging.WARNING) + await asyncio.sleep(0.5) # 약간 더 긴 대기 + continue + else: + self.logger.log(f"예상치 못한 에러 발생: {e}", level=logging.ERROR) + continue + await asyncio.sleep(0.2) + + # 만약 2초 내에 로딩 진입을 못하면, 즉시 완료된 것으로 간주(성공 반환) + if not loading_detected: + self.logger.log("로딩 상태 미감지(즉시 완료로 판단)", level=logging.INFO) + return True + + # 로딩 해제(완료)까지 대기 + start = asyncio.get_event_loop().time() + while True: + try: + is_loading = await asyncio.wait_for( + self.page.locator(btn_locator).evaluate( + "el => el.classList.contains('ant-btn-loading')" + ), + timeout=3.0 + ) + if not is_loading: + self.logger.log(f"로딩 해제 감지(작업 완료): {btn_locator}", level=logging.INFO) + return True + except asyncio.TimeoutError: + self.logger.log(f"로딩 해제 확인 타임아웃, 재시도 중...", level=logging.WARNING) + # 타임아웃이 발생해도 계속 진행 + except (KeyError, Exception) as e: + # Playwright 내부 에러 및 네트워크 에러 처리 + if "data" in str(e) or isinstance(e, KeyError) or "ENOTFOUND" in str(e) or "getaddrinfo" in str(e): + self.logger.log(f"로딩 해제 확인 중 Playwright/네트워크 에러: {e}", level=logging.WARNING) + await asyncio.sleep(0.5) + # 내부 에러 발생 시에도 계속 진행 + else: + self.logger.log(f"로딩 해제 확인 중 예상치 못한 에러: {e}", level=logging.ERROR) + + if asyncio.get_event_loop().time() - start > timeout: + self.logger.log(f"완료 대기 시간 초과: {btn_locator}", level=logging.WARNING) + return False + await asyncio.sleep(check_interval) + + except Exception as e: + self.logger.log(f"버튼 클릭 또는 대기 중 예외 발생: {e}", level=logging.ERROR, exc_info=True) + return False + + async def process_split_images_if_any(self, image_url: str, index: int) -> list: + """ + 매우 큰 원본이미지가 있을 경우에 대한 처리 + 분할된 이미지들이 있는 경우 각각을 처리 + + Args: + image_url: 원본 이미지 URL + index: 이미지 인덱스 + + Returns: + list: 분할된 이미지 처리 결과들 + """ + try: + # 서버가 분할 및 처리 결과를 child_results로 반환하므로, + # 메인앱에서는 별도 분할 처리를 수행하지 않습니다. + self.logger.log("분할 이미지 처리는 서버에서 수행됩니다(child_results 사용).", level=logging.DEBUG) + return [] + except Exception as e: + self.logger.log(f"분할 이미지 처리 확인 중 오류: {e}", level=logging.ERROR, exc_info=True) + return [] + + async def generate_and_add_product_summary(self, ocr_data_list: list, input_field): + """ + 수집된 OCR 데이터를 바탕으로 GPT를 이용해 상품 요약을 생성하고 상세페이지에 추가 + + Args: + ocr_data_list: 모든 이미지에서 수집된 OCR 데이터 리스트 + input_field: 텍스트 입력 필드 + """ + try: + if not ocr_data_list or not self.gpt_client: + return + + # OCR 데이터를 GPT 프롬프트용으로 정리 + product_text_summary = self._prepare_ocr_data_for_gpt(ocr_data_list) + + if not product_text_summary: + self.logger.log("GPT 요약용 OCR 데이터가 없습니다.", level=logging.DEBUG) + return + + # GPT를 이용한 상품 요약 생성 + summary = await self._generate_product_summary_with_gpt(product_text_summary) + + if summary: + # 상세페이지에 요약 추가 + await self._add_summary_to_detail_page(summary, input_field) + self.logger.log("상품 요약이 상세페이지에 추가되었습니다.", level=logging.INFO) + else: + self.logger.log("GPT 상품 요약 생성에 실패했습니다.", level=logging.WARNING) + + except Exception as e: + self.logger.log(f"상품 요약 생성 및 추가 중 오류: {e}", level=logging.ERROR, exc_info=True) + + def _prepare_ocr_data_for_gpt(self, ocr_data_list: list) -> str: + """ + OCR 데이터를 GPT 프롬프트용 텍스트로 정리 + + Args: + ocr_data_list: OCR 데이터 리스트 + + Returns: + str: GPT 프롬프트용 정리된 텍스트 + """ + try: + # 모든 OCR 데이터를 카테고리별로 병합 + all_specs = [] + all_colors = [] + all_materials = [] + all_warnings = [] + all_features = [] + all_usage = [] + all_others = [] + + for data in ocr_data_list: + classified_info = data.get('classified_info', {}) + all_specs.extend(classified_info.get('specs', [])) + all_colors.extend(classified_info.get('colors', [])) + all_materials.extend(classified_info.get('materials', [])) + all_warnings.extend(classified_info.get('warnings', [])) + all_features.extend(classified_info.get('features', [])) + all_usage.extend(classified_info.get('usage', [])) + all_others.extend(classified_info.get('others', [])) + + # 중복 제거 + all_specs = list(set(all_specs)) + all_colors = list(set(all_colors)) + all_materials = list(set(all_materials)) + all_warnings = list(set(all_warnings)) + all_features = list(set(all_features)) + all_usage = list(set(all_usage)) + all_others = list(set(all_others)) + + # GPT 프롬프트용 텍스트 생성 + summary_parts = [] + + if all_specs: + summary_parts.append(f"제품 사양: {'; '.join(all_specs[:10])}") # 최대 10개 + if all_features: + summary_parts.append(f"주요 특징: {'; '.join(all_features[:10])}") + if all_materials: + summary_parts.append(f"소재/재질: {'; '.join(all_materials[:5])}") + if all_colors: + summary_parts.append(f"색상: {'; '.join(all_colors[:5])}") + if all_warnings: + summary_parts.append(f"주의사항: {'; '.join(all_warnings[:5])}") + if all_usage: + summary_parts.append(f"사용법: {'; '.join(all_usage[:5])}") + + return '\n'.join(summary_parts) if summary_parts else '' + + except Exception as e: + self.logger.log(f"OCR 데이터 정리 중 오류: {e}", level=logging.ERROR, exc_info=True) + return '' + + async def _generate_product_summary_with_gpt(self, product_info: str) -> str: + """ + GPT를 이용해 상품 요약 생성 + + Args: + product_info: 정리된 상품 정보 텍스트 + + Returns: + str: 생성된 상품 요약 + """ + try: + prompt = f""" +다음은 상품 이미지에서 추출한 정보입니다. 이 정보를 바탕으로 고객이 구매 결정에 도움이 되는 상품 요약을 작성해주세요. + +상품 정보: +{product_info} + +요구사항: +1. 마크다운 형식으로 작성 +2. 주요 특징, 사양, 주의사항 등을 포함 +3. 구매대행 서비스임을 고려한 안내문구 포함 +4. 300자 이내로 간결하게 작성 +5. 고객 친화적이고 신뢰감 있는 톤앤매너 + +형식: +## 📋 상품 정보 요약 + +### 🎯 주요 특징 +- [특징 1] +- [특징 2] + +### 📏 제품 사양 +- [사양 정보] + +### ⚠️ 주의사항 +- [주의사항] + +### 💬 구매 안내 +전문 구매대행 서비스로 안전하고 신속한 배송을 보장합니다. +""" + + # GPT API 호출 + response = await self.gpt_client.chat_completion( + messages=[{"role": "user", "content": prompt}], + model="gpt-4", + max_tokens=500, + temperature=0.3 + ) + + if response and 'choices' in response and len(response['choices']) > 0: + return response['choices'][0]['message']['content'].strip() + else: + self.logger.log("GPT 응답이 올바르지 않습니다.", level=logging.WARNING) + return None + + except Exception as e: + self.logger.log(f"GPT 상품 요약 생성 중 오류: {e}", level=logging.ERROR, exc_info=True) + return None + + async def _add_summary_to_detail_page(self, summary: str, input_field): + """ + 생성된 상품 요약을 상세페이지 마지막에 추가 + + Args: + summary: 생성된 상품 요약 + input_field: 텍스트 입력 필드 + """ + try: + # 커서를 마지막으로 이동 + await self.page.keyboard.press("End") + await self.page.keyboard.press("Enter") + await self.page.keyboard.press("Enter") + + # 구분선 추가 + separator = "\n---\n\n" + await input_field.type(separator, delay=1) + + # 요약 텍스트 청크 단위로 입력 + await self.input_detail_text_chunked(input_field, summary) + + self.logger.log("상품 요약이 상세페이지에 성공적으로 추가되었습니다.", level=logging.INFO) + + except Exception as e: + self.logger.log(f"상품 요약을 상세페이지에 추가하는 중 오류: {e}", level=logging.ERROR, exc_info=True) + + async def collect_ocr_data_from_db(self) -> list: + """ + 데이터베이스에서 최근 저장된 OCR 데이터를 수집 + + Returns: + list: OCR 데이터 리스트 + """ + try: + + # 데이터베이스 파일 경로 + if not self.imageProcessor or not hasattr(self.imageProcessor, 'base_dir'): + self.logger.log("⚠️ ImageProcessor가 초기화되지 않아 OCR 데이터베이스 접근 불가", level=logging.WARNING) + return [] + + db_path = os.path.join(self.imageProcessor.base_dir, "user_data", "product_ocr_data.db") + + if not os.path.exists(db_path): + self.logger.log("OCR 데이터베이스 파일이 존재하지 않습니다.", level=logging.DEBUG) + return [] + + conn = sqlite3.connect(db_path) + cursor = conn.cursor() + + # 최근 10분 내 저장된 데이터를 조회 (현재 처리 세션의 데이터) + recent_time = datetime.now() - timedelta(minutes=10) + + cursor.execute(''' + SELECT raw_texts, text_count + FROM ocr_raw_data + WHERE created_at >= ? + ORDER BY created_at DESC + ''', (recent_time.strftime('%Y-%m-%d %H:%M:%S'),)) + + rows = cursor.fetchall() + conn.close() + + # 데이터 파싱 (단순화됨) + ocr_data_list = [] + for row in rows: + try: + raw_texts = json.loads(row[0]) if row[0] else [] + text_count = row[1] if len(row) > 1 else len(raw_texts) + + ocr_data_list.append({ + 'raw_texts': raw_texts, + 'text_count': text_count + }) + except json.JSONDecodeError as e: + self.logger.log(f"OCR 데이터 파싱 오류: {e}", level=logging.WARNING) + continue + + self.logger.log(f"데이터베이스에서 {len(ocr_data_list)}개의 OCR 데이터를 수집했습니다.", level=logging.DEBUG) + return ocr_data_list + + except Exception as e: + self.logger.log(f"OCR 데이터 수집 중 오류: {e}", level=logging.ERROR, exc_info=True) + return [] + + # GPT 관련 기능들은 현재 비활성화됨 (gpt_client 사용 불가) + # 향후 필요시 다시 활성화 가능 + diff --git a/modules/client/image_worker_client.py b/modules/client/image_worker_client.py new file mode 100644 index 0000000..1fdc1e0 --- /dev/null +++ b/modules/client/image_worker_client.py @@ -0,0 +1,549 @@ +# -*- coding: utf-8 -*- +""" +ImageWorker FastAPI 클라이언트 헬퍼 + +- 이미지 URL 다운로드 → 로컬 경로 전달 방식으로 서버에 제출 +- /v1/process-image, /v1/remove-background 호출 및 대기 +""" + +import os +import time +import uuid +import shutil +import mimetypes +import asyncio +import random +from typing import Dict, Any, Optional, List +import logging +import cv2 # for cleanup (destroyAllWindows) +import requests +import psutil +from urllib.parse import urlparse +import json + + +def _compat_result_from_job(job: Dict[str, Any]) -> Dict[str, Any]: + """ImageProcessor3 결과와 호환되도록 키를 정규화.""" + rr = (job or {}).get("result") or {} + result = { + "status": rr.get("status") or job.get("status"), + "path": rr.get("path"), + "inpaint_method": rr.get("inpaint_method"), + "inpaint_device": rr.get("inpaint_device"), + "timings": rr.get("timings"), + "child_results": rr.get("child_results"), + "message": rr.get("message") or rr.get("msg"), + "error": rr.get("error"), + } + # 누락 키 보장 + for k in ("status", "path", "inpaint_method", "inpaint_device", "timings", "child_results", "message", "error"): + result.setdefault(k, None) + return result + +def _read_server_info() -> Optional[Dict[str, Any]]: + """ProgramData/ImgWorker/server.json에서 서버 정보를 읽어온다.""" + try: + program_data = os.environ.get("PROGRAMDATA", r"C:\\ProgramData") + info_path = os.path.join(program_data, "ImgWorker", "server.json") + if os.path.isfile(info_path): + with open(info_path, "r", encoding="utf-8") as f: + return json.load(f) + except Exception: + return None + return None + + +class ImageWorkerClient: + def __init__(self, logger, api_base: str = "http://127.0.0.1:8009", work_dir: Optional[str] = None, timeout: int = 30, max_concurrency: int = 8): + self.logger = logger + + # API base 우선순위: 명시 인자 > IMGWK_API_BASE 환경변수 > server.json > 기본값 + api = api_base + try: + env_api = os.environ.get("IMGWK_API_BASE") + if env_api and isinstance(env_api, str) and env_api.strip(): + api = env_api.strip() + else: + # 인자가 기본값일 때만 server.json 자동 사용(명시적 인자 우선) + if api_base == "http://127.0.0.1:8009": + info = _read_server_info() + if isinstance(info, dict): + base = info.get("base") or (f"http://{info.get('host','127.0.0.1')}:{info.get('port',8009)}") + if base: + api = base + except Exception: + pass + + self.api = (api or "http://127.0.0.1:8009").rstrip("/") + + # 기본 작업 디렉토리: C:\ProgramData\ImgWorker\incoming + if work_dir is None: + program_data = os.environ.get("PROGRAMDATA", r"C:\\ProgramData") + work_dir = os.path.join(program_data, "ImgWorker", "incoming") + self.logger.log(f"work_dir: {work_dir}", level=logging.DEBUG) + os.makedirs(work_dir, exist_ok=True) + self.work_dir = work_dir + self.TEMP_IMAGE_DIR = work_dir # download_image 메서드 호환성을 위해 + self.timeout = timeout + # 동시요청 제한(세마포어) + # try: + # n = int(max_concurrency) + # except Exception: + # n = 8 + # self._sema = asyncio.Semaphore(max(1, n)) + + # def set_max_concurrency(self, n: int): + # """동시 요청 제한을 런타임에 조정""" + # try: + # n = int(n) + # except Exception: + # n = 1 + # self._sema = asyncio.Semaphore(max(1, n)) + + def is_valid_image_data(self, data): + """이미지 데이터의 유효성을 검사합니다""" + if not data or len(data) < 100: # 최소 크기 검사 + return False + + # JPEG, PNG, GIF, WebP 시그니처 검사 + if data.startswith(b'\xff\xd8\xff'): # JPEG + return True + elif data.startswith(b'\x89PNG\r\n\x1a\n'): # PNG + return True + elif data.startswith(b'GIF87a') or data.startswith(b'GIF89a'): # GIF + return True + elif data.startswith(b'RIFF') and b'WEBP' in data[:12]: # WebP + return True + + return False + + + # ---------------------- 유틸 ---------------------- + def _guess_ext(self, url: str, content_type: Optional[str]) -> str: + # 1) 헤더 content-type + if content_type: + ext = mimetypes.guess_extension(content_type.split(";")[0].strip()) + if ext: + return ext + # 2) URL 경로 + try: + basename = os.path.basename(url.split("?")[0]) + _, ext = os.path.splitext(basename) + if ext: + return ext + except Exception: + pass + # 3) 기본값 + return ".jpg" + + + + def download_image(self, image_url, index, file_prefix="", max_retries=3): + """Requests를 사용해 이미지를 다운로드합니다""" + + # 로컬 파일 경로면 바로 반환 + if os.path.isfile(image_url): + self.logger.log(f"로컬 파일 경로 감지, 다운로드 생략: {image_url}", level=logging.DEBUG) + return image_url + + # 로컬 파일 경로가 아니면 다운로드 시도 + try: + # "https://assets.alicdn.com"으로 시작하는 URL은 건너뛰기 + if image_url.startswith("https://assets.alicdn.com") or image_url.startswith("https://gtms01.alicdn.com"): + self.logger.log(f"다운로드 제외 URL: {image_url}", level=logging.DEBUG) + return None + + # URL에서 파일명 추출 및 접두사 포함 + parsed_url = urlparse(image_url) + base_filename = f"image_{index:03d}_{os.path.basename(parsed_url.path)}" + if not base_filename.endswith(('.jpg', '.jpeg', '.png', '.gif', '.webp')): + base_filename += '.jpg' + + # 접두사가 있으면 파일명에 포함 + if file_prefix: + filename = f"{file_prefix}_{base_filename}" + else: + filename = base_filename + + local_path = os.path.join(self.TEMP_IMAGE_DIR, filename) + + # HTTP 헤더 설정 + headers = { + "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.150 Safari/537.36", + "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9", + "Accept-Language": "en-US,en;q=0.9", + "Accept-Encoding": "gzip, deflate, br", + "DNT": "1", # Do Not Track 요청 헤더 + "Connection": "keep-alive", + "Upgrade-Insecure-Requests": "1", + "Cache-Control": "max-age=0" + } + + retries = 0 + while retries < max_retries: + try: + # 메모리 추적: 다운로드 시작 전 + before_mem = psutil.virtual_memory() + before_mb = before_mem.used / 1024 / 1024 + + self.logger.log(f"이미지 다운로드 중: {filename}", level=logging.DEBUG) + response = requests.get(image_url, headers=headers, stream=True, timeout=30) + + if response.status_code == 200: + image_data = response.content + + # 이미지 데이터 유효성 검사 + if self.is_valid_image_data(image_data): + with open(local_path, 'wb') as f: + f.write(image_data) + + # 메모리 추적: 다운로드 완료 후 + after_mem = psutil.virtual_memory() + after_mb = after_mem.used / 1024 / 1024 + change_mb = after_mb - before_mb + change_percent = (change_mb / before_mb) * 100 if before_mb > 0 else 0 + self.logger.log( + f"메모리 변화 [다운로드 완료]: {before_mb:.1f}MB -> {after_mb:.1f}MB " + f"({change_mb:+.1f}MB, {change_percent:+.1f}%) - {filename}", + level=logging.DEBUG if abs(change_mb) < 10 else logging.INFO + ) + + self.logger.log(f"이미지 다운로드 완료: {filename}", level=logging.DEBUG) + return local_path + else: + self.logger.log(f"유효하지 않은 이미지 데이터: {image_url}", level=logging.WARNING) + return None + else: + self.logger.log(f"이미지 다운로드 실패 (HTTP {response.status_code}): {image_url}. 재시도 {retries + 1}/{max_retries}", level=logging.ERROR) + retries += 1 + if retries < max_retries: + time.sleep(random.randint(2, 5)) # 2~5초 대기 후 재시도 + + except requests.exceptions.RequestException as e: + self.logger.log(f"이미지 다운로드 중 네트워크 오류: {e}. 재시도 {retries + 1}/{max_retries}", level=logging.ERROR) + retries += 1 + if retries < max_retries: + time.sleep(random.randint(2, 5)) # 예외 발생 시 대기 후 재시도 + + except Exception as e: + self.logger.log(f"이미지 다운로드 중 예상치 못한 오류: {e}. 재시도 {retries + 1}/{max_retries}", level=logging.ERROR) + retries += 1 + if retries < max_retries: + time.sleep(random.randint(2, 5)) + + self.logger.log(f"이미지 다운로드 최대 재시도 횟수를 초과했습니다: {image_url}", level=logging.ERROR) + return None + + except Exception as e: + self.logger.log(f"이미지 다운로드 중 오류: {e}", level=logging.ERROR, exc_info=True) + return None + + + + async def health(self) -> Dict[str, Any]: + try: + hr = requests.get(f"{self.api}/health", timeout=10) + hr.raise_for_status() + return hr.json() + except Exception as e: + return {"status": "error", "error": str(e)} + + # ---------------------- 제출/대기 ---------------------- + async def submit_process_image(self, file_path: str, index: int, file_prefix: str, + font_type: str, unwanted_texts: List[str], + is_member_valid: bool, authenticated_by_admin: bool, + extra_overrides: Optional[Dict[str, Any]] = None, + ocr: Optional[bool] = None) -> str: + payload: Dict[str, Any] = { + "file_path": file_path, + "index": int(index), + "file_prefix": file_prefix, + # per-request 토글 오버라이드 + "toggle_overrides": { + "font_type": font_type, + "unwanted_texts": list(unwanted_texts or []), + "is_member_valid": bool(is_member_valid), + "authenticated_by_admin": bool(authenticated_by_admin), + }, + } + if extra_overrides: + payload["toggle_overrides"].update(extra_overrides) + # ocr 플래그가 명시되면 서버로도 전달 + if ocr is not None: + payload["ocr"] = bool(ocr) + + r = requests.post(f"{self.api}/v1/process-image", json=payload, timeout=self.timeout) + r.raise_for_status() + return r.json().get("job_id") + + async def submit_remove_background(self, file_path: str, file_prefix: str, + extra_overrides: Optional[Dict[str, Any]] = None) -> str: + payload: Dict[str, Any] = { + "file_path": file_path, + "file_prefix": file_prefix, + } + if extra_overrides: + payload["toggle_overrides"] = extra_overrides + + r = requests.post(f"{self.api}/v1/remove-background", json=payload, timeout=self.timeout) + r.raise_for_status() + return r.json().get("job_id") + + async def wait_job(self, job_id: str, timeout_sec: int = 900) -> Dict[str, Any]: + end = time.time() + timeout_sec + while time.time() < end: + r = requests.get(f"{self.api}/v1/jobs/{job_id}", timeout=15) + if r.status_code == 200: + data = r.json() + if data.get("status") in ("done", "error", "cancelled"): + return data + await asyncio.sleep(0.2) + raise TimeoutError("job wait timeout") + + # ---------------------- 고수준 URL 편의 ---------------------- + async def process_image_url(self, image_url: str, index: int, file_prefix: str, + font_type: str, unwanted_texts: List[str], + is_member_valid: bool, authenticated_by_admin: bool, + extra_overrides: Optional[Dict[str, Any]] = None, + download_first: bool = True, + ocr: Optional[bool] = None) -> Optional[Dict[str, Any]]: + # 420 에러 방지를 위해 순차 처리 (세마포어 제거) + path = image_url + if download_first and (image_url.startswith("http://") or image_url.startswith("https://")): + # 동기식 download_image를 executor로 실행 + loop = asyncio.get_event_loop() + path = await loop.run_in_executor(None, self.download_image, image_url, index, file_prefix) + + jid = await self.submit_process_image( + file_path=path, + index=index, + file_prefix=file_prefix, + font_type=font_type, + unwanted_texts=unwanted_texts, + is_member_valid=is_member_valid, + authenticated_by_admin=authenticated_by_admin, + extra_overrides=extra_overrides, + ocr=ocr, + ) + job = await self.wait_job(jid) + return _compat_result_from_job(job) + + async def remove_background_url(self, image_url: str, file_prefix: str, + extra_overrides: Optional[Dict[str, Any]] = None, + download_first: bool = True) -> Optional[Dict[str, Any]]: + # 420 에러 방지를 위해 순차 처리 (세마포어 제거) + path = image_url + if download_first and (image_url.startswith("http://") or image_url.startswith("https://")): + # 동기식 download_image를 executor로 실행 + loop = asyncio.get_event_loop() + path = await loop.run_in_executor(None, self.download_image, image_url, 0, file_prefix) + + jid = await self.submit_remove_background( + file_path=path, + file_prefix=file_prefix, + extra_overrides=extra_overrides, + ) + job = await self.wait_job(jid) + return _compat_result_from_job(job) + + # ---------------------- 간단 제어 API (트레이에서 사용) ---------------------- + def worker_status(self) -> Dict[str, Any]: + try: + r = requests.get(f"{self.api}/v1/worker/status", timeout=5) + r.raise_for_status() + return r.json() + except Exception as e: + return {"ready": False, "error": str(e)} + + def worker_start(self) -> Dict[str, Any]: + try: + r = requests.post(f"{self.api}/v1/worker/start", timeout=10) + r.raise_for_status() + return r.json() + except Exception as e: + return {"ok": False, "error": str(e)} + + def worker_stop(self) -> Dict[str, Any]: + try: + r = requests.post(f"{self.api}/v1/worker/stop", timeout=10) + r.raise_for_status() + return r.json() + except Exception as e: + return {"ok": False, "error": str(e)} + + def shutdown_server(self) -> Dict[str, Any]: + try: + r = requests.post(f"{self.api}/v1/server/shutdown", timeout=5) + r.raise_for_status() + return r.json() + except Exception as e: + return {"ok": False, "error": str(e)} + + # ---------------------- 호환성 메서드 (기존 ImageProcessor3와 동일한 인터페이스) ---------------------- + + async def process_single_image(self, original_image_url, index, delay=1.0, file_prefix="", ocr: Optional[bool] = None): + """ + 기존 ImageProcessor3.process_single_image과 호환되는 메서드 + + Args: + original_image_url (str): 처리할 이미지 URL + index (int): 이미지 인덱스 + delay (float): 요청 간격 (초) - 호환성을 위해 유지 + file_prefix (str): 파일명에 추가할 접두사 + + Returns: + dict: 기존 ImageProcessor3과 동일한 포맷의 결과 + - status: 'translated', 'original', 'exclude', 'failed' 중 하나 + - path: 처리된 이미지 파일 경로 또는 원본 이미지 파일 경로 + - error: 오류 메시지 (status가 'failed'인 경우에만 포함) + - inpaint_method: 사용된 인페인팅 방법 + - inpaint_device: 사용된 인페인팅 장치 + """ + try: + # 요청 간격 조절 (호환성을 위해) + if delay > 0: + await asyncio.sleep(delay) + + # ImageWorkerClient를 통한 처리 + result = await self.process_image_url( + image_url=original_image_url, + index=index, + file_prefix=file_prefix, + font_type="", # 기본값 사용 (필요시 외부에서 설정 가능) + unwanted_texts=[], # 기본값 사용 (필요시 외부에서 설정 가능) + is_member_valid=False, # 기본값 사용 (필요시 외부에서 설정 가능) + authenticated_by_admin=False, # 기본값 사용 (필요시 외부에서 설정 가능) + ocr=ocr, + ) + + if result and isinstance(result, dict): + # 서버 결과를 기존 포맷으로 변환 + status = result.get("status", "failed") + path = result.get("path", original_image_url) + + # status 매핑 (서버 결과에 따라 조정) + if status == "translated": + return { + 'status': 'translated', + 'path': path, + 'inpaint_method': result.get('inpaint_method', 'unknown'), + 'inpaint_device': result.get('inpaint_device', 'unknown') + } + elif status == "original": + return { + 'status': 'original', + 'path': path, + 'inpaint_method': None, + 'inpaint_device': None + } + elif status == "exclude": + return { + 'status': 'exclude', + 'path': path, + 'inpaint_method': None, + 'inpaint_device': None + } + else: + # 기타 상태는 실패로 처리 + return { + 'status': 'failed', + 'path': original_image_url, + 'error': f'Unknown status: {status}', + 'inpaint_method': None, + 'inpaint_device': None + } + else: + return { + 'status': 'failed', + 'path': original_image_url, + 'error': 'No result from server', + 'inpaint_method': None, + 'inpaint_device': None + } + + except Exception as e: + self.logger.log(f"process_single_image 호환성 메서드 오류: {e}", level=logging.ERROR, exc_info=True) + return { + 'status': 'failed', + 'path': original_image_url, + 'error': str(e), + 'inpaint_method': None, + 'inpaint_device': None + } + + async def remove_background(self, original_image_url, file_prefix=""): + """ + 기존 ImageProcessor3.remove_background과 호환되는 메서드 + + Args: + original_image_url (str): 처리할 이미지 URL + file_prefix (str): 파일명에 추가할 접두사 + + Returns: + dict: 기존 ImageProcessor3과 동일한 포맷의 결과 + - status: 'success', 'failed' 중 하나 + - path: 처리된 이미지 파일 경로 + - error: 오류 메시지 (status가 'failed'인 경우에만 포함) + """ + try: + # ImageWorkerClient를 통한 배경제거 + result = await self.remove_background_url( + image_url=original_image_url, + file_prefix=file_prefix + ) + + if result and isinstance(result, dict): + status = result.get("status", "failed") + path = result.get("path", original_image_url) + + if status == "success": + return { + 'status': 'success', + 'path': path + } + else: + return { + 'status': 'failed', + 'path': original_image_url, + 'error': f'Remove background failed: {status}' + } + else: + return { + 'status': 'failed', + 'path': original_image_url, + 'error': 'No result from server' + } + + except Exception as e: + self.logger.log(f"remove_background 호환성 메서드 오류: {e}", level=logging.ERROR, exc_info=True) + return { + 'status': 'failed', + 'path': original_image_url, + 'error': str(e) + } + + def __del__(self): + """소멸자에서 리소스 정리""" + self.cleanup() + self.logger.log("이미지 프로세서 소멸", level=logging.DEBUG) + + def cleanup(self): + """리소스 정리""" + try: + # Python GC 강제 실행 + import gc + gc.collect() + + # OpenCV 윈도우 정리 + try: + cv2.destroyAllWindows() + except: + pass + + # 임시 폴더 삭제 + if hasattr(self, 'TEMP_IMAGE_DIR') and os.path.exists(self.TEMP_IMAGE_DIR): + # shutil.rmtree(self.TEMP_IMAGE_DIR) + self.logger.log(f"임시 폴더 삭제됨: {self.TEMP_IMAGE_DIR}", level=logging.DEBUG) + + except Exception as e: + self.logger.log(f"리소스 정리 중 오류: {e}", level=logging.ERROR, exc_info=True) diff --git a/modules/fonts/Cafe24Ohsquare-v2.0.ttf b/modules/fonts/Cafe24Ohsquare-v2.0.ttf new file mode 100644 index 0000000..16b8dc7 Binary files /dev/null and b/modules/fonts/Cafe24Ohsquare-v2.0.ttf differ diff --git a/modules/fonts/HakgyoansimDunggeunmisoTTFB.ttf b/modules/fonts/HakgyoansimDunggeunmisoTTFB.ttf new file mode 100644 index 0000000..560ac1f Binary files /dev/null and b/modules/fonts/HakgyoansimDunggeunmisoTTFB.ttf differ diff --git a/modules/fonts/NanumBarunGothic.ttf b/modules/fonts/NanumBarunGothic.ttf new file mode 100644 index 0000000..c314868 Binary files /dev/null and b/modules/fonts/NanumBarunGothic.ttf differ diff --git a/modules/fonts/NanumSquareRoundR.ttf b/modules/fonts/NanumSquareRoundR.ttf new file mode 100644 index 0000000..c8d0c4f Binary files /dev/null and b/modules/fonts/NanumSquareRoundR.ttf differ diff --git a/modules/fonts/font1.png b/modules/fonts/font1.png new file mode 100644 index 0000000..3264d94 Binary files /dev/null and b/modules/fonts/font1.png differ diff --git a/modules/fonts/font2.png b/modules/fonts/font2.png new file mode 100644 index 0000000..f5f4a94 Binary files /dev/null and b/modules/fonts/font2.png differ diff --git a/modules/fonts/font3.png b/modules/fonts/font3.png new file mode 100644 index 0000000..a6a1d1f Binary files /dev/null and b/modules/fonts/font3.png differ diff --git a/modules/fonts/font4.png b/modules/fonts/font4.png new file mode 100644 index 0000000..36ea55e Binary files /dev/null and b/modules/fonts/font4.png differ diff --git a/modules/fonts/font5.png b/modules/fonts/font5.png new file mode 100644 index 0000000..6c12f13 Binary files /dev/null and b/modules/fonts/font5.png differ diff --git a/modules/fonts/gamtanload.ttf b/modules/fonts/gamtanload.ttf new file mode 100644 index 0000000..48a01df Binary files /dev/null and b/modules/fonts/gamtanload.ttf differ diff --git a/modules/gemma_client.py b/modules/gemma_client.py new file mode 100644 index 0000000..13e705c --- /dev/null +++ b/modules/gemma_client.py @@ -0,0 +1,253 @@ +# -*- coding: utf-8 -*- +""" +Gemma Translation API Python Client +- FastAPI 서버(/batch_translate, /translate_ocr_step1, /translate_ocr_step2 ...) 래퍼 +- 안전한 재시도, 타임아웃, 배치 슬라이싱 지원 +- 네 OCR 결과(dict 리스트) -> 번역 문자열 리스트 정렬 유지 + +사용 예: + from gemma_client import GemmaTranslator + gt = GemmaTranslator(base_url="http://", timeout=120) + + # A) OCR 결과를 그대로 번역 (id, text 유지) + ko_list = gt.translate_ocr_texts( + product_name="휴대용 선풍기", + category="가전/계절가전", + ocr_results=[{"text":"强力送风"}, {"text":"USB-C 快速充电"}] + ) + + # B) 순수 텍스트 리스트를 번역 + ko_list = gt.batch_translate_texts( + product_name="휴대용 선풍기", + category="가전/계절가전", + text_list=["大风力无刷电机","Type-C 充电"] + ) +""" +from __future__ import annotations + +import os +import time +import json +import random +import logging +from typing import List, Dict, Any, Optional, Tuple + +import requests + + +_JSON = Dict[str, Any] + + +class GemmaTranslatorError(RuntimeError): + pass + + +class GemmaTranslator: + """ + vLLM 번역 서버 클라이언트. + + Params + ------ + base_url : str + 예) "http://localhost" (HAProxy 경유 시 포트 생략 / 80) + 개별 인스턴스 직접 붙으려면 "http://:8000" + timeout : int + 요청 타임아웃(초) + max_retries : int + 요청 재시도 횟수 + backoff : float + 재시도 backoff base (지수) + session : requests.Session | None + 세션 주입 가능 + """ + + def __init__( + self, + base_url: Optional[str] = None, + timeout: int = 120, + max_retries: int = 2, + backoff: float = 0.6, + session: Optional[requests.Session] = None, + logger: Optional[logging.Logger] = None, + ) -> None: + self.base_url = (base_url or os.getenv("GEMMA_API_BASE") or "http://localhost").rstrip("/") + self.timeout = timeout + self.max_retries = max_retries + self.backoff = backoff + self.sess = session or requests.Session() + self.log = logger or logging.getLogger(__name__) + + # ----------------------------- + # 내부 HTTP 헬퍼 (retry 포함) + # ----------------------------- + def _post(self, path: str, payload: _JSON) -> _JSON: + url = f"{self.base_url}{path}" + last_err: Optional[Exception] = None + for attempt in range(self.max_retries + 1): + try: + r = self.sess.post(url, json=payload, timeout=self.timeout) + r.raise_for_status() + return r.json() + except Exception as e: + last_err = e + # 429/5xx/연결오류 등 재시도 + if attempt < self.max_retries: + sleep_s = (self.backoff ** attempt) + random.uniform(0, 0.2) + self.log.warning(f"[GemmaTranslator] POST {url} 실패({e}), 재시도 {attempt+1}/{self.max_retries} 대기 {sleep_s:.2f}s") + time.sleep(sleep_s) + else: + break + raise GemmaTranslatorError(f"POST {url} 실패: {last_err}") + + # ----------------------------- + # 공개 API + # ----------------------------- + def health(self) -> _JSON: + url = f"{self.base_url}/health" + r = self.sess.get(url, timeout=10) + r.raise_for_status() + return r.json() + + def metrics(self) -> _JSON: + url = f"{self.base_url}/metrics" + r = self.sess.get(url, timeout=10) + r.raise_for_status() + return r.json() + + # ---- A) 순수 텍스트 리스트 번역 (/batch_translate) ---- + def batch_translate_texts( + self, + product_name: str, + category: str, + text_list: List[str], + delimiter: str = " / ", + batch_size: int = 8, + ) -> List[str]: + """ + text_list → 같은 길이의 ko 리스트 반환. + 서버에서 추가 배치/토큰 슬라이싱을 하므로, 클라에서는 적당한 batch_size만 넘기면 됨. + """ + if not text_list: + return [] + + out: List[str] = [] + for i in range(0, len(text_list), batch_size): + chunk = text_list[i : i + batch_size] + payload = { + "product_name": product_name, + "category": category, + "text_list": chunk, + "delimiter": delimiter, + "batch_size": min(batch_size, len(chunk)), + } + resp = self._post("/batch-translate", payload) + # 서버는 translated_texts 길이를 입력과 동일하게 맞춰줌 + out.extend(resp.get("translated_texts", chunk)) + return out + + # ---- B) OCR 결과 번역: [{text:str, ...}] -> [ko, ...] ---- + def translate_ocr_texts( + self, + product_name: str, + category: str, + ocr_results: List[Dict[str, Any]], + batch_size: int = 16, + ) -> List[str]: + """ + 입력: OCR 결과 리스트(각 항목에 최소 'text' 키 필요) + 처리: 서버 /ocr-translate 로 {id, source} 배열을 보내고, 반환 {id, result} 정렬 + 출력: 원래 순서 유지한 ko 문자열 리스트 + """ + if not ocr_results: + return [] + + # 유효성 검증 (새 스키마 준수) + if not product_name or len(product_name.strip()) < 1: + raise GemmaTranslatorError("product_name은 1자 이상이어야 합니다.") + if not category or len(category.strip()) < 1: + raise GemmaTranslatorError("category는 1자 이상이어야 합니다.") + + # id 부여 및 source 필터링 (빈 텍스트 스킵, 최소 1자) + items = [] + source_to_orig_idx = {} # source id to original index mapping + for i, d in enumerate(ocr_results): + source = (d.get("text") or "").strip() + if len(source) >= 1: # 최소 1자 이상 + item_id = len(items) + 1 + items.append({"id": item_id, "source": source}) + source_to_orig_idx[item_id] = i + + if not items: + return [""] * len(ocr_results) # 원래 길이만큼 빈 문자열 반환 + + # 서버에 배치로 전송 + out_ko = [""] * len(ocr_results) # 원래 ocr_results 길이 유지 + for i in range(0, len(items), batch_size): + chunk = items[i : i + batch_size] + payload = { + "product_name": product_name, + "category": category, + "items": chunk, + } + resp = self._post("/ocr-translate", payload) + + result_items = resp.get("items", []) + # {id, result} 배열 → id 기준으로 매핑 + for obj in result_items: + item_id = int(obj.get("id", 0)) + orig_idx = source_to_orig_idx.get(item_id) + if orig_idx is not None: + out_ko[orig_idx] = str(obj.get("result", "")).strip() + + return out_ko + + # ---- C) 옵션 번역: [{id, source:[...]}] → [{id, translations:[...]}] ---- + def translate_option_groups( + self, + product_name: str, + category: str, + option_groups: List[Dict[str, Any]], + batch_size: int = 8, + ) -> List[Dict[str, Any]]: + """ + option_groups 예: + [{"id": 1, "source": ["红色","蓝色"]}, {"id": 2, "source": ["小号","大号"]}] + 반환(서버 결과 그대로): + [{"id": 1, "translations": ["핑크","블루"]}, {"id": 2, "translations": ["소형","대형"]}] + """ + if not option_groups: + return [] + + out: List[Dict[str, Any]] = [] + for i in range(0, len(option_groups), batch_size): + chunk = option_groups[i : i + batch_size] + payload = { + "product_name": product_name, + "category": category, + "items": chunk, + } + resp = self._post("/option-translate", payload) + out.extend(resp.get("result", [])) + return out + + # ---- D) (선택) 카피 다듬기 ---- (이 메서드는 새 스펙에서 OCR가 단일 단계이므로 제거 또는 주석 처리) + # def polish_translations( + # self, + # product_name: str, + # category: str, + # id_text_pairs: List[Dict[str, Any]], + # batch_size: int = 16, + # ) -> List[Dict[str, Any]]: + # """ + # 입력: [{"id":1,"translation":"..."}] + # 반환: [{"id":1,"result":"..."}] + # """ + # if not id_text_pairs: + # return [] + # out: List[Dict[str, Any]] = [] + # for i in range(0, len(id_text_pairs), batch_size): + # chunk = id_text_pairs[i : i + batch_size] + # payload = {"product_name": product_name, "category": category, "items": chunk} + # resp = self._post("/translate_ocr_step2", payload) + # out.extend(resp.get("result", [])) + # return out diff --git a/modules/gpu_status_checker.py b/modules/gpu_status_checker.py new file mode 100644 index 0000000..e4567c6 --- /dev/null +++ b/modules/gpu_status_checker.py @@ -0,0 +1,835 @@ +# -*- coding: utf-8 -*- +""" +GPU 상태 확인 및 설치 가이드 모듈 + +기능: +- NVIDIA GPU 감지 +- CUDA Toolkit 설치 확인 +- cuDNN 설치 확인 +- TensorRT 설치 확인 +- cuDNN PATH 자동 설정 +- 설치 가이드 제공 +""" + +import os +import subprocess +import platform +import logging +from typing import Dict, List, Optional, Tuple +from pathlib import Path +import glob +import json + + +class GPUStatusChecker: + """GPU 환경 상태를 종합적으로 확인하고 설치 가이드를 제공하는 클래스""" + + def __init__(self, logger: Optional[object] = None): + self.logger = logger or self._create_dummy_logger() + self.status = {} + + def _create_dummy_logger(self): + """로거가 없을 때 사용할 더미 로거""" + class DummyLogger: + def log(self, msg, level=logging.INFO, exc_info=False): + print(f"[GPU_CHECK] {msg}") + return DummyLogger() + + def check_all_gpu_components(self) -> Dict[str, any]: + """모든 GPU 관련 구성요소를 확인하고 상태 반환""" + self.status = { + 'nvidia_gpu': self._check_nvidia_gpu(), + 'cuda_toolkit': self._check_cuda_toolkit(), + 'cudnn': self._check_cudnn(), + 'tensorrt': self._check_tensorrt(), + 'onnxruntime_gpu': self._check_onnxruntime_gpu(), + 'path_configured': self._check_path_configuration(), + 'recommendations': [] + } + + # 권장사항 생성 + self._generate_recommendations() + + return self.status + + def _check_nvidia_gpu(self) -> Dict[str, any]: + """NVIDIA GPU 및 드라이버 확인 (상세 정보 포함)""" + try: + result = subprocess.run( + ["nvidia-smi", "--query-gpu=name,driver_version,memory.total,compute_cap", "--format=csv,noheader"], + capture_output=True, + text=True, + timeout=10, + creationflags=subprocess.CREATE_NO_WINDOW if platform.system() == "Windows" else 0 + ) + + if result.returncode == 0: + lines = result.stdout.strip().split('\n') + gpus = [] + for i, line in enumerate(lines): + if line.strip(): + parts = [p.strip() for p in line.split(',')] + if len(parts) >= 3: + gpu_info = { + 'id': i, + 'name': parts[0], + 'driver_version': parts[1], + 'memory_mb': parts[2].replace(' MiB', ''), + 'compute_capability': parts[3] if len(parts) > 3 else 'Unknown', + 'architecture': self._detect_gpu_architecture(parts[0]), + 'cuda_support': self._check_cuda_compatibility(parts[0]) + } + gpus.append(gpu_info) + + return { + 'installed': True, + 'gpus': gpus, + 'count': len(gpus), + 'message': f"{len(gpus)}개의 NVIDIA GPU 감지됨", + 'primary_gpu': gpus[0] if gpus else None + } + else: + return { + 'installed': False, + 'gpus': [], + 'count': 0, + 'message': "NVIDIA GPU 또는 드라이버가 설치되지 않음" + } + + except (subprocess.TimeoutExpired, FileNotFoundError): + return { + 'installed': False, + 'gpus': [], + 'count': 0, + 'message': "nvidia-smi 명령을 찾을 수 없음 (NVIDIA 드라이버 미설치)" + } + except Exception as e: + return { + 'installed': False, + 'gpus': [], + 'count': 0, + 'message': f"GPU 확인 중 오류: {e}" + } + + def _detect_gpu_architecture(self, gpu_name: str) -> str: + """GPU 이름을 기반으로 아키텍처 감지""" + gpu_name_lower = gpu_name.lower() + + # RTX 40 시리즈 (Ada Lovelace) + if any(model in gpu_name_lower for model in ['rtx 40', 'rtx 41', 'rtx 42', 'rtx 43', 'rtx 44']): + return 'Ada Lovelace (RTX 40 시리즈)' + + # RTX 30 시리즈 (Ampere) + elif any(model in gpu_name_lower for model in ['rtx 30', 'rtx 31', 'rtx 32', 'rtx 33', 'rtx 34']): + return 'Ampere (RTX 30 시리즈)' + + # RTX 20 시리즈 (Turing) + elif any(model in gpu_name_lower for model in ['rtx 20', 'rtx 21', 'rtx 22', 'rtx 23', 'rtx 24']): + return 'Turing (RTX 20 시리즈)' + + # GTX 16 시리즈 (Turing) + elif any(model in gpu_name_lower for model in ['gtx 16', 'gtx 165', 'gtx 166']): + return 'Turing (GTX 16 시리즈)' + + # GTX 10 시리즈 (Pascal) + elif any(model in gpu_name_lower for model in ['gtx 10', 'gtx 105', 'gtx 106', 'gtx 107', 'gtx 108']): + return 'Pascal (GTX 10 시리즈)' + + # GTX 900 시리즈 (Maxwell) + elif any(model in gpu_name_lower for model in ['gtx 9', 'gtx 90']): + return 'Maxwell (GTX 900 시리즈)' + + # Quadro 카드들 + elif 'quadro' in gpu_name_lower: + return 'Quadro 워크스테이션' + + # Tesla 카드들 + elif 'tesla' in gpu_name_lower: + return 'Tesla 데이터센터' + + else: + return 'Unknown' + + def _check_cuda_compatibility(self, gpu_name: str) -> Dict[str, any]: + """GPU의 CUDA 12.1 호환성 확인""" + gpu_name_lower = gpu_name.lower() + + # CUDA 12.1을 완전히 지원하는 GPU들 (Compute Capability 6.0 이상) + fully_supported = [ + 'rtx 40', 'rtx 41', 'rtx 42', 'rtx 43', 'rtx 44', # Ada Lovelace + 'rtx 30', 'rtx 31', 'rtx 32', 'rtx 33', 'rtx 34', # Ampere + 'rtx 20', 'rtx 21', 'rtx 22', 'rtx 23', 'rtx 24', # Turing + 'gtx 16', 'gtx 165', 'gtx 166', # Turing GTX + 'gtx 10', 'gtx 105', 'gtx 106', 'gtx 107', 'gtx 108' # Pascal + ] + + # 제한적 지원 (구형 아키텍처) + limited_support = [ + 'gtx 9', 'gtx 90' # Maxwell + ] + + if any(model in gpu_name_lower for model in fully_supported): + return { + 'supported': True, + 'level': 'full', + 'message': 'CUDA 12.1 완전 지원' + } + elif any(model in gpu_name_lower for model in limited_support): + return { + 'supported': True, + 'level': 'limited', + 'message': 'CUDA 12.1 제한적 지원 (구형 아키텍처)' + } + else: + return { + 'supported': False, + 'level': 'none', + 'message': 'CUDA 12.1 지원 여부 확인 필요' + } + + def _check_cuda_toolkit(self) -> Dict[str, any]: + """CUDA Toolkit 설치 확인""" + try: + result = subprocess.run( + ["nvcc", "--version"], + capture_output=True, + text=True, + timeout=10, + creationflags=subprocess.CREATE_NO_WINDOW if platform.system() == "Windows" else 0 + ) + + if result.returncode == 0: + output = result.stdout + version_info = "" + for line in output.split('\n'): + if 'release' in line.lower(): + version_info = line.strip() + break + + return { + 'installed': True, + 'version': version_info, + 'message': f"CUDA Toolkit 설치됨: {version_info}" + } + else: + return { + 'installed': False, + 'version': None, + 'message': "CUDA Toolkit이 설치되지 않음" + } + + except (subprocess.TimeoutExpired, FileNotFoundError): + return { + 'installed': False, + 'version': None, + 'message': "nvcc 명령을 찾을 수 없음 (CUDA Toolkit 미설치)" + } + except Exception as e: + return { + 'installed': False, + 'version': None, + 'message': f"CUDA Toolkit 확인 중 오류: {e}" + } + + def _check_cudnn(self) -> Dict[str, any]: + """cuDNN 설치 확인""" + cudnn_paths = [] + cudnn_versions = [] + + # Windows에서 일반적인 cuDNN 설치 경로들 + if platform.system() == "Windows": + search_paths = [ + "C:/Program Files/NVIDIA/CUDNN", + "C:/tools/cuda/cudnn", + "C:/cudnn" + ] + + # 환경변수에서도 찾기 + cuda_path = os.environ.get('CUDA_PATH', '') + if cuda_path: + search_paths.append(os.path.join(cuda_path, 'cudnn')) + + for base_path in search_paths: + if os.path.exists(base_path): + # cuDNN 버전 디렉토리 찾기 + version_dirs = glob.glob(os.path.join(base_path, "v*")) + for version_dir in version_dirs: + bin_dirs = glob.glob(os.path.join(version_dir, "bin", "*")) + for bin_dir in bin_dirs: + cudnn_dll = os.path.join(bin_dir, "cudnn64_*.dll") + dll_files = glob.glob(cudnn_dll) + if dll_files: + version_name = os.path.basename(version_dir) + cuda_version = os.path.basename(bin_dir) + cudnn_paths.append({ + 'path': bin_dir, + 'version': version_name, + 'cuda_version': cuda_version, + 'dll_files': [os.path.basename(f) for f in dll_files] + }) + + if cudnn_paths: + return { + 'installed': True, + 'paths': cudnn_paths, + 'count': len(cudnn_paths), + 'message': f"cuDNN 설치됨: {len(cudnn_paths)}개 버전 발견" + } + else: + return { + 'installed': False, + 'paths': [], + 'count': 0, + 'message': "cuDNN이 설치되지 않음" + } + + def _check_tensorrt(self) -> Dict[str, any]: + """TensorRT 설치 확인""" + try: + # ONNXRuntime에서 TensorRT provider 확인 + import onnxruntime as ort + available_providers = ort.get_available_providers() + tensorrt_available = 'TensorrtExecutionProvider' in available_providers + + if tensorrt_available: + # TensorRT 설치 경로 찾기 시도 + tensorrt_paths = [] + if platform.system() == "Windows": + search_paths = [ + "C:/Program Files/NVIDIA/TensorRT", + "C:/TensorRT" + ] + for path in search_paths: + if os.path.exists(path): + tensorrt_paths.append(path) + + return { + 'installed': True, + 'available_in_onnx': True, + 'paths': tensorrt_paths, + 'message': "TensorRT 설치됨 (ONNXRuntime에서 사용 가능)" + } + else: + return { + 'installed': False, + 'available_in_onnx': False, + 'paths': [], + 'message': "TensorRT가 설치되지 않음 또는 ONNXRuntime에서 인식되지 않음" + } + + except ImportError: + return { + 'installed': False, + 'available_in_onnx': False, + 'paths': [], + 'message': "ONNXRuntime이 설치되지 않아 TensorRT 확인 불가" + } + except Exception as e: + return { + 'installed': False, + 'available_in_onnx': False, + 'paths': [], + 'message': f"TensorRT 확인 중 오류: {e}" + } + + def _check_onnxruntime_gpu(self) -> Dict[str, any]: + """ONNXRuntime GPU 지원 확인""" + try: + import onnxruntime as ort + available_providers = ort.get_available_providers() + + gpu_providers = [] + if 'CUDAExecutionProvider' in available_providers: + gpu_providers.append('CUDA') + if 'TensorrtExecutionProvider' in available_providers: + gpu_providers.append('TensorRT') + + return { + 'installed': True, + 'gpu_providers': gpu_providers, + 'all_providers': available_providers, + 'message': f"ONNXRuntime GPU 지원: {', '.join(gpu_providers) if gpu_providers else 'CPU만 지원'}" + } + + except ImportError: + return { + 'installed': False, + 'gpu_providers': [], + 'all_providers': [], + 'message': "ONNXRuntime이 설치되지 않음" + } + except Exception as e: + return { + 'installed': False, + 'gpu_providers': [], + 'all_providers': [], + 'message': f"ONNXRuntime 확인 중 오류: {e}" + } + + def _check_path_configuration(self) -> Dict[str, any]: + """PATH 환경변수에 cuDNN이 설정되어 있는지 확인""" + current_path = os.environ.get('PATH', '') + cudnn_in_path = [] + + # cuDNN 상태를 직접 확인 (self.status가 아직 완전하지 않을 수 있음) + cudnn_status = self._check_cudnn() + + if cudnn_status.get('installed', False): + cudnn_paths = cudnn_status['paths'] + path_parts = current_path.split(os.pathsep) + + for cudnn_info in cudnn_paths: + cudnn_bin_path = cudnn_info['path'] + + # PATH에 있는지 더 정확하게 확인 + path_exists = any( + os.path.normpath(part.strip()) == os.path.normpath(cudnn_bin_path) + for part in path_parts if part.strip() + ) + + if path_exists: + cudnn_in_path.append(cudnn_info) + + return { + 'cudnn_in_path': len(cudnn_in_path) > 0, + 'configured_paths': cudnn_in_path, + 'message': f"PATH 설정: {'올바름' if cudnn_in_path else 'cuDNN 경로 누락'}" + } + + def _generate_recommendations(self): + """현재 상태에 따른 권장사항 생성""" + recommendations = [] + + # NVIDIA GPU 확인 + if not self.status['nvidia_gpu']['installed']: + recommendations.append({ + 'priority': 'critical', + 'component': 'NVIDIA GPU', + 'action': 'NVIDIA GPU 드라이버 설치', + 'description': 'NVIDIA 공식 사이트에서 최신 드라이버를 다운로드하여 설치하세요.', + 'url': 'https://www.nvidia.com/drivers/' + }) + + # CUDA Toolkit 확인 + if not self.status['cuda_toolkit']['installed']: + recommendations.append({ + 'priority': 'critical', + 'component': 'CUDA Toolkit', + 'action': 'CUDA Toolkit 12.1 설치', + 'description': 'NVIDIA CUDA Toolkit 12.1을 설치하세요.', + 'url': 'https://developer.nvidia.com/cuda-toolkit-archive' + }) + + # cuDNN 확인 + if not self.status['cudnn']['installed']: + recommendations.append({ + 'priority': 'critical', + 'component': 'cuDNN', + 'action': 'cuDNN v9.x 설치', + 'description': 'NVIDIA cuDNN v9.x를 다운로드하여 설치하세요.', + 'url': 'https://developer.nvidia.com/cudnn' + }) + elif not self.status['path_configured']['cudnn_in_path']: + recommendations.append({ + 'priority': 'high', + 'component': 'cuDNN PATH', + 'action': 'cuDNN PATH 환경변수 설정', + 'description': 'cuDNN bin 디렉토리를 시스템 PATH에 추가하세요.', + 'auto_fix': True + }) + + # TensorRT 확인 (선택사항) + if not self.status['tensorrt']['installed']: + recommendations.append({ + 'priority': 'medium', + 'component': 'TensorRT', + 'action': 'TensorRT 설치 (선택사항)', + 'description': 'TensorRT를 설치하면 최고 성능의 GPU 가속을 사용할 수 있습니다.', + 'url': 'https://developer.nvidia.com/tensorrt' + }) + + # ONNXRuntime GPU 확인 + if not self.status['onnxruntime_gpu']['installed']: + recommendations.append({ + 'priority': 'critical', + 'component': 'ONNXRuntime GPU', + 'action': 'onnxruntime-gpu 설치', + 'description': 'pip install onnxruntime-gpu 명령으로 설치하세요.', + 'command': 'pip install onnxruntime-gpu' + }) + + self.status['recommendations'] = recommendations + + def auto_fix_cudnn_path(self) -> bool: + """cuDNN PATH를 자동으로 설정""" + try: + # 현재 상태 다시 확인 + cudnn_status = self._check_cudnn() + + if not cudnn_status.get('installed', False): + self.logger.log("cuDNN이 설치되지 않아 PATH 설정을 할 수 없습니다", level=logging.ERROR) + return False + + cudnn_paths = cudnn_status['paths'] + if not cudnn_paths: + self.logger.log("cuDNN 설치 경로를 찾을 수 없습니다", level=logging.ERROR) + return False + + # 가장 최신 버전의 cuDNN 경로 선택 + latest_cudnn = max(cudnn_paths, key=lambda x: x['version']) + cudnn_bin_path = latest_cudnn['path'] + + self.logger.log(f"cuDNN 경로 확인됨: {cudnn_bin_path}", level=logging.INFO) + + # 현재 세션의 PATH에 추가 + current_path = os.environ.get('PATH', '') + + # PATH에 이미 있는지 더 정확하게 확인 + path_parts = current_path.split(os.pathsep) + path_already_exists = any( + os.path.normpath(part.strip()) == os.path.normpath(cudnn_bin_path) + for part in path_parts if part.strip() + ) + + if not path_already_exists: + # PATH 맨 앞에 추가 (우선순위 높게) + new_path = cudnn_bin_path + os.pathsep + current_path + os.environ['PATH'] = new_path + self.logger.log(f"✅ cuDNN PATH 추가됨: {cudnn_bin_path}", level=logging.INFO) + + # 설정 후 DLL 로딩 테스트 + test_result = self._test_cudnn_loading(cudnn_bin_path) + if test_result: + self.logger.log("✅ cuDNN DLL 로딩 테스트 성공", level=logging.INFO) + else: + self.logger.log("⚠️ cuDNN DLL 로딩 테스트 실패 - 재부팅이 필요할 수 있습니다", level=logging.WARNING) + + return True + else: + self.logger.log("cuDNN PATH가 이미 설정되어 있습니다", level=logging.INFO) + + # 이미 있어도 DLL 로딩 테스트 + test_result = self._test_cudnn_loading(cudnn_bin_path) + if not test_result: + self.logger.log("⚠️ PATH는 설정되어 있지만 cuDNN DLL 로딩에 실패했습니다", level=logging.WARNING) + return False + + return True + + except Exception as e: + self.logger.log(f"cuDNN PATH 설정 실패: {e}", level=logging.ERROR, exc_info=True) + return False + + def _test_cudnn_loading(self, cudnn_bin_path: str) -> bool: + """cuDNN DLL 로딩 테스트""" + try: + import ctypes + import glob + + # cudnn64_*.dll 파일 찾기 + dll_pattern = os.path.join(cudnn_bin_path, "cudnn64_*.dll") + dll_files = glob.glob(dll_pattern) + + if not dll_files: + self.logger.log(f"cuDNN DLL 파일을 찾을 수 없음: {dll_pattern}", level=logging.WARNING) + return False + + # 첫 번째 DLL 로딩 테스트 + dll_file = dll_files[0] + try: + dll = ctypes.CDLL(dll_file) + self.logger.log(f"cuDNN DLL 로딩 성공: {os.path.basename(dll_file)}", level=logging.DEBUG) + return True + except OSError as e: + self.logger.log(f"cuDNN DLL 로딩 실패: {e}", level=logging.WARNING) + return False + + except Exception as e: + self.logger.log(f"cuDNN DLL 테스트 중 오류: {e}", level=logging.DEBUG) + return False + + def get_installation_commands(self) -> List[str]: + """설치가 필요한 구성요소들의 설치 명령어 반환""" + commands = [] + + for rec in self.status.get('recommendations', []): + if rec.get('command'): + commands.append(rec['command']) + + return commands + + def generate_status_report(self) -> str: + """상태 보고서 생성""" + report = ["=== GPU 환경 상태 보고서 ===\n"] + + # NVIDIA GPU + gpu_status = self.status.get('nvidia_gpu', {}) + report.append(f"🎮 NVIDIA GPU: {'✅' if gpu_status.get('installed') else '❌'}") + if gpu_status.get('installed'): + for gpu in gpu_status.get('gpus', []): + report.append(f" - {gpu['name']} ({gpu['memory_mb']}MB, 드라이버 {gpu['driver_version']})") + else: + report.append(f" - {gpu_status.get('message', 'Unknown')}") + + # CUDA Toolkit + cuda_status = self.status.get('cuda_toolkit', {}) + report.append(f"\n🛠️ CUDA Toolkit: {'✅' if cuda_status.get('installed') else '❌'}") + report.append(f" - {cuda_status.get('message', 'Unknown')}") + + # cuDNN + cudnn_status = self.status.get('cudnn', {}) + report.append(f"\n🧠 cuDNN: {'✅' if cudnn_status.get('installed') else '❌'}") + if cudnn_status.get('installed'): + for path_info in cudnn_status.get('paths', []): + report.append(f" - {path_info['version']} (CUDA {path_info['cuda_version']})") + else: + report.append(f" - {cudnn_status.get('message', 'Unknown')}") + + # TensorRT + tensorrt_status = self.status.get('tensorrt', {}) + report.append(f"\n🚀 TensorRT: {'✅' if tensorrt_status.get('installed') else '❌'}") + report.append(f" - {tensorrt_status.get('message', 'Unknown')}") + + # ONNXRuntime + onnx_status = self.status.get('onnxruntime_gpu', {}) + report.append(f"\n📦 ONNXRuntime: {'✅' if onnx_status.get('installed') else '❌'}") + if onnx_status.get('installed'): + providers = onnx_status.get('gpu_providers', []) + report.append(f" - GPU 지원: {', '.join(providers) if providers else 'CPU만'}") + else: + report.append(f" - {onnx_status.get('message', 'Unknown')}") + + # PATH 설정 + path_status = self.status.get('path_configured', {}) + report.append(f"\n⚙️ PATH 설정: {'✅' if path_status.get('cudnn_in_path') else '❌'}") + report.append(f" - {path_status.get('message', 'Unknown')}") + + # 권장사항 + recommendations = self.status.get('recommendations', []) + if recommendations: + report.append(f"\n📋 권장사항 ({len(recommendations)}개):") + for i, rec in enumerate(recommendations, 1): + priority = rec['priority'].upper() + report.append(f" {i}. [{priority}] {rec['action']}") + report.append(f" {rec['description']}") + + return '\n'.join(report) + + def generate_customized_installation_guide(self) -> str: + """감지된 GPU에 맞는 맞춤형 설치 가이드 생성""" + gpu_status = self.status.get('nvidia_gpu', {}) + primary_gpu = gpu_status.get('primary_gpu') + + if not gpu_status.get('installed', False) or not primary_gpu: + return self._generate_generic_installation_guide() + + gpu_name = primary_gpu.get('name', 'Unknown GPU') + architecture = primary_gpu.get('architecture', 'Unknown') + cuda_support = primary_gpu.get('cuda_support', {}) + driver_version = primary_gpu.get('driver_version', 'Unknown') + memory_mb = primary_gpu.get('memory_mb', 'Unknown') + + guide_parts = [] + + # GPU 정보 헤더 + guide_parts.append(f""" +# 🎮 {gpu_name} 맞춤형 GPU 가속 설치 가이드 + +## 📊 감지된 GPU 정보 +- **GPU 모델**: {gpu_name} +- **아키텍처**: {architecture} +- **메모리**: {memory_mb} MB +- **드라이버 버전**: {driver_version} +- **CUDA 12.1 지원**: {cuda_support.get('message', 'Unknown')} +""") + + # CUDA 호환성에 따른 안내 + if cuda_support.get('supported', False): + if cuda_support.get('level') == 'full': + guide_parts.append(""" +## ✅ 호환성 확인 +귀하의 GPU는 CUDA 12.1을 완전히 지원합니다! 최고 성능의 GPU 가속을 사용할 수 있습니다. +""") + elif cuda_support.get('level') == 'limited': + guide_parts.append(""" +## ⚠️ 호환성 확인 +귀하의 GPU는 CUDA 12.1을 제한적으로 지원합니다. 기본적인 GPU 가속은 가능하지만 최신 기능은 제한될 수 있습니다. +""") + else: + guide_parts.append(""" +## ❓ 호환성 확인 +귀하의 GPU의 CUDA 12.1 호환성을 확인할 수 없습니다. 설치 후 정상 작동 여부를 확인해주세요. +""") + + # 맞춤형 드라이버 링크 + driver_link = self._get_driver_download_link(gpu_name) + guide_parts.append(f""" +## 🎯 맞춤형 설치 링크 + +### 1단계: NVIDIA 드라이버 업데이트 +현재 드라이버: **{driver_version}** + +{driver_link} + +### 2단계: CUDA Toolkit 12.1 설치 +**권장 버전**: CUDA Toolkit 12.1 (편집알바생 최적화 버전) +- 📥 [CUDA 12.1 다운로드](https://developer.nvidia.com/cuda-12-1-0-download-archive) +- 설치 시 "Express 설치" 선택 권장 +- 환경변수는 자동으로 설정됩니다 + +### 3단계: cuDNN v9.x 설치 +**권장 버전**: cuDNN v9.12 for CUDA 12.1 +- 📥 [cuDNN 다운로드](https://developer.nvidia.com/cudnn) (NVIDIA 계정 필요) +- **Windows 설치 파일**: `cudnn-windows-x86_64-9.x.x.x_cuda12.exe` +- **설치 방법**: 다운로드한 .exe 파일을 실행하여 자동 설치 +- **자동 설정**: 설치 후 PATH 환경변수 자동 설정됨 + +### 4단계: 프로그램 실행 +- 프로그램을 실행하면 자동으로 GPU 가속 상태를 확인합니다 +- 별도의 Python 패키지 설치가 필요하지 않습니다 +""") + + # GPU별 성능 최적화 팁 + optimization_tips = self._get_optimization_tips(gpu_name, architecture, memory_mb) + guide_parts.append(optimization_tips) + + # 설치 확인 방법 + guide_parts.append(""" +## 🧪 설치 확인 + +설치 완료 후 다음 버튼들을 클릭하여 확인: +- **🎮 GPU 상태 확인**: nvidia-smi 명령 실행 +- **🔧 CUDA 버전 확인**: nvcc --version 명령 실행 +- **🐍 ONNXRuntime GPU 확인**: 프로그램 내 GPU 가속 지원 확인 + +또는 직접 명령 프롬프트에서 확인: +```cmd +nvidia-smi # GPU 상태 확인 +nvcc --version # CUDA 확인 +``` + +**참고**: 이 프로그램은 cx_Freeze로 패키징되어 있어 Python 환경 설정이 필요하지 않습니다. + +## 🆘 문제 해결 + +### "cudnn64_9.dll을 찾을 수 없음" 오류 +1. 이 다이얼로그에서 **"⚙️ cuDNN PATH 자동 설정"** 버튼 클릭 +2. 또는 수동으로 cuDNN bin 디렉토리를 PATH에 추가 + +### 성능이 예상보다 느린 경우 +- GPU 온도 확인 (과열 시 성능 저하) +- 다른 GPU 사용 프로그램 종료 +- 드라이버를 최신 버전으로 업데이트 + +### 메모리 부족 오류 +- 이미지 크기 줄이기 +- 다른 프로그램 종료하여 GPU 메모리 확보 +- 배치 크기 조정 (고급 사용자) +""") + + return ''.join(guide_parts).strip() + + def _get_driver_download_link(self, gpu_name: str) -> str: + """GPU에 맞는 드라이버 다운로드 링크 생성""" + gpu_name_lower = gpu_name.lower() + + # RTX 40 시리즈 + if any(model in gpu_name_lower for model in ['rtx 40', 'rtx 41', 'rtx 42', 'rtx 43', 'rtx 44']): + return """- 📥 [RTX 40 시리즈 최신 드라이버](https://www.nvidia.com/drivers/results/218194/) +- **권장**: Game Ready Driver 또는 Studio Driver 최신 버전""" + + # RTX 30 시리즈 + elif any(model in gpu_name_lower for model in ['rtx 30', 'rtx 31', 'rtx 32', 'rtx 33', 'rtx 34']): + return """- 📥 [RTX 30 시리즈 최신 드라이버](https://www.nvidia.com/drivers/results/218194/) +- **권장**: Game Ready Driver 또는 Studio Driver 최신 버전""" + + # RTX 20 시리즈 + elif any(model in gpu_name_lower for model in ['rtx 20', 'rtx 21', 'rtx 22', 'rtx 23', 'rtx 24']): + return """- 📥 [RTX 20 시리즈 최신 드라이버](https://www.nvidia.com/drivers/results/218194/) +- **권장**: Game Ready Driver 최신 버전""" + + # GTX 16 시리즈 + elif any(model in gpu_name_lower for model in ['gtx 16', 'gtx 165', 'gtx 166']): + return """- 📥 [GTX 16 시리즈 최신 드라이버](https://www.nvidia.com/drivers/results/218194/) +- **권장**: Game Ready Driver 최신 버전""" + + # GTX 10 시리즈 + elif any(model in gpu_name_lower for model in ['gtx 10', 'gtx 105', 'gtx 106', 'gtx 107', 'gtx 108']): + return """- 📥 [GTX 10 시리즈 최신 드라이버](https://www.nvidia.com/drivers/results/218194/) +- **참고**: 구형 GPU이므로 최신 기능 일부가 제한될 수 있습니다""" + + # 기타 + else: + return """- 📥 [NVIDIA 드라이버 자동 감지](https://www.nvidia.com/drivers/) +- **방법**: 사이트에서 GPU 모델 선택 후 다운로드""" + + def _get_optimization_tips(self, gpu_name: str, architecture: str, memory_mb: str) -> str: + """GPU별 성능 최적화 팁""" + tips = ["\n## 🚀 성능 최적화 팁\n"] + + try: + memory_gb = int(memory_mb) // 1024 if memory_mb.isdigit() else 0 + except: + memory_gb = 0 + + # 메모리 기반 최적화 + if memory_gb >= 16: + tips.append("### 🎯 대용량 메모리 (16GB+) 최적화") + tips.append("- **TensorRT 설치 권장**: 최고 성능을 위해 TensorRT 추가 설치") + tips.append("- **배치 크기 증가**: 더 큰 이미지나 배치 처리 가능") + tips.append("- **동시 처리**: 여러 이미지 동시 처리 가능") + elif memory_gb >= 8: + tips.append("### ⚡ 중간 메모리 (8-16GB) 최적화") + tips.append("- **기본 설정 사용**: 현재 설정이 최적화됨") + tips.append("- **메모리 모니터링**: 다른 프로그램과 메모리 공유 주의") + elif memory_gb >= 4: + tips.append("### 💡 제한적 메모리 (4-8GB) 최적화") + tips.append("- **이미지 크기 제한**: 큰 이미지는 성능 저하 가능") + tips.append("- **순차 처리**: 동시 처리보다는 순차 처리 권장") + tips.append("- **메모리 정리**: 사용 후 자동 메모리 정리 활성화됨") + else: + tips.append("### ⚠️ 저용량 메모리 (4GB 미만)") + tips.append("- **CPU 모드 권장**: GPU 메모리 부족으로 CPU 모드가 더 안정적일 수 있음") + tips.append("- **작은 이미지만**: 큰 이미지 처리 시 오류 발생 가능") + + # 아키텍처별 최적화 + if 'Ada Lovelace' in architecture or 'RTX 40' in architecture: + tips.append("\n### 🔥 RTX 40 시리즈 특화 최적화") + tips.append("- **TensorRT 필수**: RTX 40 시리즈의 성능을 최대화") + tips.append("- **AV1 인코딩**: 영상 처리 시 AV1 인코더 활용 가능") + elif 'Ampere' in architecture or 'RTX 30' in architecture: + tips.append("\n### ⚡ RTX 30 시리즈 특화 최적화") + tips.append("- **TensorRT 권장**: 30 시리즈의 RT Core 활용") + tips.append("- **DLSS 기술**: AI 가속 기능 최적화됨") + elif 'Pascal' in architecture or 'GTX 10' in architecture: + tips.append("\n### 🛠️ GTX 10 시리즈 최적화") + tips.append("- **기본 CUDA**: TensorRT보다는 기본 CUDA가 안정적") + tips.append("- **드라이버 업데이트**: 정기적인 드라이버 업데이트 중요") + + return '\n'.join(tips) + + def _generate_generic_installation_guide(self) -> str: + """GPU가 감지되지 않은 경우의 일반적인 설치 가이드""" + return """ +# GPU 가속을 위한 일반 설치 가이드 + +## ❓ GPU를 감지할 수 없습니다 + +NVIDIA GPU가 감지되지 않았습니다. 다음 사항을 확인해주세요: + +### 1. GPU 확인 +- NVIDIA GPU가 설치되어 있는지 확인 +- 장치 관리자에서 디스플레이 어댑터 확인 + +### 2. 드라이버 설치 +- NVIDIA 공식 사이트에서 드라이버 다운로드 +- https://www.nvidia.com/drivers/ + +### 3. 일반적인 설치 순서 +1. NVIDIA 드라이버 설치 +2. CUDA Toolkit 12.1 설치 +3. cuDNN v9.x 설치 +4. onnxruntime-gpu 설치 + +자세한 설치 방법은 GPU 설치 후 다시 확인해주세요. +""" diff --git a/modules/gpu_utils.py b/modules/gpu_utils.py new file mode 100644 index 0000000..cb184e9 --- /dev/null +++ b/modules/gpu_utils.py @@ -0,0 +1,668 @@ +# -*- coding: utf-8 -*- +""" +GPU 유틸리티 모듈 - DirectML 기반 GPU 가속 및 상태 관리 + +기능: +- GPU 사용 가능성 검사 +- DirectML 지원 여부 확인 +- 전역 GPU 상태 관리 +- CPU 폴백 처리 +- Windows DirectX 12 기반 범용 GPU 지원 (NVIDIA, AMD, Intel) +""" + +import os +import logging +import subprocess +import platform +from typing import Optional, Dict, Any + +# ONNXRuntime DirectML 메모리 절약형 설정 +os.environ['ORT_ENABLE_ALL_OPTIMIZATIONS'] = '0' # 🔧 메모리 절약을 위해 비활성화 +os.environ['ORT_DISABLE_MEMCPY_WARNINGS'] = '1' # Memcpy 경고 억제 +os.environ['ORT_DISABLE_ALL_CUSTOM_OPS'] = '0' # 커스텀 연산 활성화 +os.environ['ORT_LOGGING_LEVEL'] = '3' # 경고만 출력 (0: DEBUG, 1: INFO, 2: WARNING, 3: ERROR) +os.environ['ORT_CUDA_CUDNN_CONV_USE_MAX_WORKSPACE'] = '0' # GPU 메모리 안정성 +os.environ['ORT_LOG_SEVERITY_LEVEL'] = '3' # 심각한 오류만 로깅 +# DirectML 특화 메모리 절약 설정 +os.environ['ORT_DML_ENABLE_GRAPH_SERIALIZATION'] = '0' # 그래프 직렬화 비활성화 (안정성) +os.environ['ORT_DML_METACOMMANDS_ENABLED'] = '1' # 메타커맨드 활성화 + + +class GPUManager: + """GPU 상태 관리 및 DirectML 지원 확인 (하위 호환성을 위해 can_use_cuda 속성 유지)""" + + def __init__(self, logger: Optional[object] = None): + self.logger = logger or self._create_dummy_logger() + + # GPU 상태 전역 변수들 (하위 호환성을 위해 can_use_cuda 유지) + self.can_use_cuda = False # DirectML 사용 가능 여부 (기존 인터페이스 호환성) + self.directml_available = False + self.gpu_info = {} + self.initialization_attempted = False + + def _create_dummy_logger(self): + """로거가 없을 때 사용할 더미 로거""" + class DummyLogger: + def log(self, msg, level=logging.DEBUG, exc_info=False): + print(f"[GPU] {msg}") + return DummyLogger() + + def _setup_directml_environment(self) -> None: + """DirectML 환경 설정 (Windows DirectX 12 기반)""" + try: + if platform.system() == "Windows": + # DirectML은 Windows에 내장된 DirectX 12를 사용하므로 별도 설정 불필요 + self.logger.log("✅ DirectML 환경 준비 완료 (Windows DirectX 12 기반)", level=logging.DEBUG) + else: + self.logger.log("⚠️ DirectML은 Windows 전용입니다", level=logging.WARNING) + except Exception as e: + self.logger.log(f"DirectML 환경 설정 중 오류: {e}", level=logging.WARNING) + + def initialize_gpu_state(self, toggle_states: Dict[str, Any]) -> None: + """ + GPU 상태를 초기화하고 전역 변수에 저장 + + Args: + toggle_states: 설정 딕셔너리 + """ + if self.initialization_attempted: + return # 이미 초기화됨 + + self.initialization_attempted = True + + # DirectML 환경 설정 + self._setup_directml_environment() + + # 사용자가 GPU 가속을 원하는지 확인 (use_cuda를 GPU 가속 플래그로 사용) + use_gpu_requested = toggle_states.get("use_cuda", False) + + self.logger.log("=== 🚀 DirectML GPU 상태 초기화 시작 🚀 ===", level=logging.DEBUG) + self.logger.log(f"🎯 사용자 GPU 가속 요청: {use_gpu_requested}", level=logging.DEBUG) + self.logger.log(f"💻 현재 운영체제: {platform.system()}", level=logging.DEBUG) + + if not use_gpu_requested: + self.logger.log("GPU 가속이 비활성화됨 (toggle_states['use_cuda'] = False)", level=logging.DEBUG) + self.can_use_cuda = False + self._set_safe_cpu_mode(toggle_states) + return + + # Windows 플랫폼 확인 (DirectML 필수) + if platform.system() != "Windows": + self.logger.log("DirectML은 Windows 전용입니다 - CPU 모드로 전환", level=logging.WARNING) + self.can_use_cuda = False + self._set_safe_cpu_mode(toggle_states) + return + + # DirectML 지원 확인 (안전하게 시도) + try: + directml_support = self._check_directml_support() + except Exception as e: + self.logger.log(f"DirectML 확인 중 예외 발생: {e} - CPU 모드로 안전 전환", level=logging.WARNING) + self.can_use_cuda = False + self._set_safe_cpu_mode(toggle_states) + return + + if not directml_support: + self.logger.log("DirectML 지원을 확인할 수 없음 - CPU 모드로 전환", level=logging.WARNING) + self.can_use_cuda = False + self._set_safe_cpu_mode(toggle_states) + return + + # 메모리 상태 확인 (안전장치) + try: + memory_ok = self._check_system_memory() + if not memory_ok: + self.logger.log("⚠️ 시스템 메모리 부족 감지 - 안전을 위해 CPU 모드로 전환", level=logging.WARNING) + self.can_use_cuda = False + self._set_safe_cpu_mode(toggle_states) + return + except Exception as e: + self.logger.log(f"메모리 확인 실패: {e} - 안전을 위해 CPU 모드로 전환", level=logging.WARNING) + self.can_use_cuda = False + self._set_safe_cpu_mode(toggle_states) + return + + # 모든 검사 통과 + self.can_use_cuda = True # 하위 호환성을 위해 이 속성명 유지 + self.directml_available = True + + # toggle_states에서 migan_use_cuda를 True로 자동 설정 + if 'migan_use_cuda' in toggle_states and not toggle_states['migan_use_cuda']: + toggle_states['migan_use_cuda'] = True + self.logger.log("🎯 toggle_states의 migan_use_cuda를 True로 자동 설정", level=logging.INFO) + + self.logger.log("🚀 ✅ DirectML 사용 가능 - GPU 가속 모드로 동작 ✅ 🚀", level=logging.DEBUG) + self.logger.log("🎮 DirectML: NVIDIA, AMD, Intel GPU 모두 지원", level=logging.DEBUG) + self.logger.log("📊 DirectML 가속 활성화: rembg, MIGAN, OCR 모든 모듈에서 GPU 사용", level=logging.DEBUG) + self.logger.log("=== 🎯 DirectML GPU 상태 초기화 완료 🎯 ===", level=logging.DEBUG) + + def _set_safe_cpu_mode(self, toggle_states: Dict[str, Any]) -> None: + """안전한 CPU 모드로 설정""" + self.can_use_cuda = False + self.directml_available = False + + # 모든 GPU 관련 설정을 CPU 모드로 강제 변경 + gpu_related_keys = [ + 'migan_use_cuda', 'use_cuda', 'optionIMGTrans_type', + 'detail_IMGTrans_type', 'thumb_trans_type' + ] + + for key in gpu_related_keys: + if key in toggle_states: + if key.endswith('_type'): + toggle_states[key] = 'CPU' + else: + toggle_states[key] = False + + self.logger.log("🔒 안전한 CPU 모드로 모든 GPU 설정 강제 비활성화", level=logging.INFO) + + def _check_system_memory(self) -> bool: + """시스템 메모리 상태 확인""" + try: + import psutil + memory = psutil.virtual_memory() + available_gb = memory.available / (1024**3) + + self.logger.log(f"💾 시스템 메모리 - 사용가능: {available_gb:.1f}GB, 사용률: {memory.percent:.1f}%", level=logging.DEBUG) + + # 사용 가능한 메모리가 2GB 미만이거나 사용률이 90% 이상이면 위험 + if available_gb < 2.0 or memory.percent > 90: + self.logger.log(f"⚠️ 메모리 부족 위험: 사용가능 {available_gb:.1f}GB, 사용률 {memory.percent:.1f}%", level=logging.WARNING) + return False + + return True + except Exception as e: + self.logger.log(f"메모리 상태 확인 실패: {e}", level=logging.WARNING) + return False # 확인 실패 시 안전하게 False 반환 + + def _detect_gpu_hardware(self) -> bool: + """GPU 하드웨어 감지""" + try: + self.logger.log("🔍 GPU 하드웨어 감지 시작...", level=logging.DEBUG) + + if platform.system() != "Windows": + self.logger.log("❌ 현재 Windows만 지원됨", level=logging.WARNING) + return False + + self.logger.log("🖥️ Windows 환경 확인됨, nvidia-smi 명령 실행 중...", level=logging.DEBUG) + + # nvidia-smi 명령어로 GPU 확인 + result = subprocess.run( + ["nvidia-smi", "--query-gpu=name,memory.total,driver_version", "--format=csv,noheader,nounits"], + capture_output=True, + text=True, + timeout=10, + creationflags=subprocess.CREATE_NO_WINDOW if platform.system() == "Windows" else 0 + ) + + self.logger.log(f"📊 nvidia-smi 실행 결과 - 반환코드: {result.returncode}", level=logging.DEBUG) + if result.stdout: + self.logger.log(f"📄 nvidia-smi 출력: {result.stdout.strip()}", level=logging.DEBUG) + if result.stderr: + self.logger.log(f"⚠️ nvidia-smi 에러 출력: {result.stderr.strip()}", level=logging.WARNING) + + if result.returncode == 0 and result.stdout.strip(): + gpu_lines = result.stdout.strip().split('\n') + for i, line in enumerate(gpu_lines): + if line.strip(): + parts = [p.strip() for p in line.split(',')] + if len(parts) >= 3: + self.gpu_info[f'gpu_{i}'] = { + 'name': parts[0], + 'memory_mb': parts[1], + 'driver_version': parts[2] + } + + self.logger.log(f"GPU 하드웨어 감지됨: {len(self.gpu_info)}개", level=logging.DEBUG) + for gpu_id, info in self.gpu_info.items(): + self.logger.log(f" {gpu_id}: {info['name']} ({info['memory_mb']}MB, 드라이버 {info['driver_version']})", level=logging.DEBUG) + return True + else: + self.logger.log(f"nvidia-smi 실행 실패: {result.stderr}", level=logging.WARNING) + return False + + except (subprocess.TimeoutExpired, FileNotFoundError, subprocess.SubprocessError) as e: + self.logger.log(f"GPU 하드웨어 감지 실패: {e}", level=logging.WARNING) + return False + except Exception as e: + self.logger.log(f"GPU 하드웨어 감지 중 예외: {e}", level=logging.ERROR, exc_info=True) + return False + + def _check_cuda_installation(self) -> bool: + """CUDA 설치 및 작동 상태 확인""" + try: + self.logger.log("🔧 CUDA 설치 상태 확인 중...", level=logging.DEBUG) + + # nvcc 버전 확인 + result = subprocess.run( + ["nvcc", "--version"], + capture_output=True, + text=True, + timeout=10, + creationflags=subprocess.CREATE_NO_WINDOW if platform.system() == "Windows" else 0 + ) + + self.logger.log(f"🛠️ nvcc 명령 실행 결과 - 반환코드: {result.returncode}", level=logging.DEBUG) + if result.stdout: + self.logger.log(f"📋 nvcc 출력: {result.stdout.strip()}", level=logging.DEBUG) + if result.stderr: + self.logger.log(f"⚠️ nvcc 에러 출력: {result.stderr.strip()}", level=logging.WARNING) + + if result.returncode == 0: + version_output = result.stdout + self.logger.log(f"CUDA 컴파일러 감지됨", level=logging.DEBUG) + + # 버전 정보 추출 + for line in version_output.split('\n'): + if 'release' in line.lower(): + self.logger.log(f"CUDA 버전: {line.strip()}", level=logging.DEBUG) + break + + return True + else: + self.logger.log("CUDA 컴파일러(nvcc)를 찾을 수 없음", level=logging.WARNING) + return False + + except (subprocess.TimeoutExpired, FileNotFoundError) as e: + self.logger.log(f"CUDA 설치 확인 실패: {e}", level=logging.WARNING) + return False + except Exception as e: + self.logger.log(f"CUDA 설치 확인 중 예외: {e}", level=logging.ERROR, exc_info=True) + return False + + def _check_directml_support(self) -> bool: + """DirectML 지원 확인 및 실제 GPU 가속 동작 테스트""" + self.logger.log("🧠 DirectML 지원 확인 및 실제 테스트 시작...", level=logging.DEBUG) + + try: + self.logger.log("📦 ONNXRuntime DirectML 확인 중...", level=logging.DEBUG) + import onnxruntime as ort + providers = ort.get_available_providers() + self.logger.log(f"🔍 ONNXRuntime 사용 가능한 providers: {providers}", level=logging.DEBUG) + + # DirectML provider 존재 확인 + if "DmlExecutionProvider" not in providers: + self.logger.log("❌ ONNXRuntime DirectML 지원 없음", level=logging.WARNING) + self.logger.log("💡 onnxruntime-directml 패키지가 필요할 수 있습니다", level=logging.WARNING) + return False + + self.logger.log("⚡ DirectML ExecutionProvider 지원 확인됨", level=logging.DEBUG) + + # VM 환경 감지 + if self._detect_vm_environment(): + self.logger.log("🖥️ VM 환경이 감지됨 - GPU 패스스루 상태 확인 중...", level=logging.DEBUG) + + # 실제 DirectML 동작 테스트 + if not self._test_directml_actual_performance(): + self.logger.log("❌ DirectML 실제 동작 테스트 실패 - CPU 모드로 전환", level=logging.WARNING) + return False + + self.logger.log("✅ DirectML 실제 GPU 가속 동작 확인됨", level=logging.DEBUG) + return True + + except ImportError: + self.logger.log("ONNXRuntime가 설치되지 않음", level=logging.WARNING) + return False + except Exception as e: + self.logger.log(f"ONNXRuntime DirectML 지원 확인 실패: {e}", level=logging.WARNING) + return False + + def test_directml_comprehensive(self) -> Dict[str, Any]: + """종합적인 DirectML 테스트 (GPU 상태 버튼 전용 - 실제 추론 테스트 포함)""" + test_results = { + 'directml_available': False, + 'vm_detected': False, + 'inference_test_passed': False, + 'test_duration': 0, + 'error_message': None, + 'performance_ratio': 0 + } + + try: + import time + start_time = time.time() + + self.logger.log("🧪 종합적인 DirectML 테스트 시작 (실제 추론 포함)...", level=logging.DEBUG) + + # 1단계: DirectML provider 확인 + import onnxruntime as ort + providers = ort.get_available_providers() + directml_available = "DmlExecutionProvider" in providers + test_results['directml_available'] = directml_available + + if not directml_available: + test_results['error_message'] = "DirectML Provider를 찾을 수 없습니다" + return test_results + + # 2단계: VM 환경 감지 + vm_detected = self._detect_vm_environment() + test_results['vm_detected'] = vm_detected + + if vm_detected: + self.logger.log("🖥️ VM 환경이 감지됨 - GPU 패스스루 상태 확인 중...", level=logging.DEBUG) + + # 3단계: 실제 DirectML 추론 테스트 + inference_success = self._test_directml_actual_performance() + test_results['inference_test_passed'] = inference_success + + if not inference_success: + test_results['error_message'] = "DirectML 추론 테스트 실패 - GPU 가속이 실제로 동작하지 않습니다" + return test_results + + # 4단계: 성능 벤치마크 + performance_ratio = self._benchmark_directml_vs_cpu() + test_results['performance_ratio'] = performance_ratio + + test_results['test_duration'] = time.time() - start_time + self.logger.log(f"✅ 종합 DirectML 테스트 완료 ({test_results['test_duration']:.2f}초)", level=logging.DEBUG) + + return test_results + + except Exception as e: + test_results['error_message'] = f"DirectML 테스트 중 예외 발생: {e}" + self.logger.log(f"DirectML 테스트 중 예외: {e}", level=logging.ERROR, exc_info=True) + return test_results + + def _detect_vm_environment(self) -> bool: + """VM 환경 감지 (Proxmox, VMware, VirtualBox, Hyper-V 등)""" + try: + self.logger.log("🔍 VM 환경 감지 중...", level=logging.DEBUG) + + vm_indicators = [] + + # 시스템 정보를 통한 VM 감지 + try: + result = subprocess.run( + ["wmic", "computersystem", "get", "model"], + capture_output=True, + text=True, + timeout=10, + creationflags=subprocess.CREATE_NO_WINDOW + ) + if result.returncode == 0: + output = result.stdout.lower() + vm_keywords = ['virtualbox', 'vmware', 'qemu', 'xen', 'kvm', 'hyper-v', 'proxmox'] + for keyword in vm_keywords: + if keyword in output: + vm_indicators.append(f"시스템 모델: {keyword}") + + except Exception as e: + self.logger.log(f"시스템 모델 확인 실패: {e}", level=logging.DEBUG) + + # CPU 정보를 통한 VM 감지 + try: + result = subprocess.run( + ["wmic", "cpu", "get", "name"], + capture_output=True, + text=True, + timeout=10, + creationflags=subprocess.CREATE_NO_WINDOW + ) + if result.returncode == 0: + output = result.stdout.lower() + if 'qemu' in output or 'virtual' in output: + vm_indicators.append("CPU: 가상화 프로세서 감지") + + except Exception as e: + self.logger.log(f"CPU 정보 확인 실패: {e}", level=logging.DEBUG) + + # DirectX 정보를 통한 하드웨어 가속 확인 + try: + result = subprocess.run( + ["dxdiag", "/t", "temp_dxdiag.txt"], + capture_output=True, + timeout=15, + creationflags=subprocess.CREATE_NO_WINDOW + ) + # dxdiag는 파일을 생성하므로 바로 결과를 읽을 수 없음 + # 이 부분은 간소화하여 생략 + except Exception: + pass + + if vm_indicators: + self.logger.log(f"🖥️ VM 환경 감지됨: {', '.join(vm_indicators)}", level=logging.INFO) + return True + else: + self.logger.log("💻 물리적 환경으로 판단됨", level=logging.DEBUG) + return False + + except Exception as e: + self.logger.log(f"VM 환경 감지 실패: {e}", level=logging.DEBUG) + return False # 실패 시 안전하게 물리 환경으로 가정 + + def _test_directml_actual_performance(self) -> bool: + """실제 DirectML을 사용한 연산 테스트로 GPU 가속 동작 확인""" + try: + self.logger.log("🧪 DirectML 실제 동작 테스트 시작...", level=logging.DEBUG) + + import onnxruntime as ort + import numpy as np + import time + + # 1단계: DirectML provider 초기화 테스트 + start_time = time.time() + + try: + # DirectML provider 설정 + dml_options = { + 'device_id': 0, + 'disable_memory_arena': False, # 메모리 아레나 사용 + } + providers = [('DmlExecutionProvider', dml_options), 'CPUExecutionProvider'] + + # 세션 옵션 설정 + session_options = ort.SessionOptions() + session_options.log_severity_level = 3 # ERROR만 출력 + session_options.enable_mem_pattern = False # VM에서 문제가 될 수 있음 + session_options.enable_cpu_mem_arena = False # 안정성 향상 + + # 초기화 시간 확인 + init_elapsed = time.time() - start_time + if init_elapsed > 5.0: # 5초 이상 걸리면 의심스러움 + self.logger.log(f"⚠️ DirectML 초기화가 비정상적으로 오래 걸림 ({init_elapsed:.1f}초)", level=logging.WARNING) + + # 2단계: 실제 간단한 모델로 추론 테스트 + success = self._perform_simple_inference_test(providers, session_options) + if not success: + return False + + # 3단계: 성능 벤치마크 (GPU vs CPU 비교) + performance_ok = self._benchmark_directml_vs_cpu() + if not performance_ok: + self.logger.log("⚠️ DirectML 성능이 CPU보다 현저히 느림 - 실제 GPU 가속이 동작하지 않을 수 있음", level=logging.WARNING) + return False + + total_elapsed = time.time() - start_time + self.logger.log(f"✅ DirectML 실제 동작 테스트 성공 (총 {total_elapsed:.2f}초)", level=logging.DEBUG) + return True + + except Exception as e: + self.logger.log(f"❌ DirectML 세션 생성/테스트 실패: {e}", level=logging.WARNING) + return False + + except ImportError as e: + self.logger.log(f"❌ 필요한 패키지 import 실패: {e}", level=logging.WARNING) + return False + except Exception as e: + self.logger.log(f"❌ DirectML 실제 동작 테스트 중 예외: {e}", level=logging.WARNING) + return False + + def _perform_simple_inference_test(self, providers, session_options) -> bool: + """간단한 모델로 실제 추론 테스트""" + try: + import onnxruntime as ort + import numpy as np + import time + + # 매우 간단한 ONNX 모델 생성 (Add 연산) + from onnx import helper, TensorProto + import onnx + + # 간단한 덧셈 연산 모델 생성 + input1 = helper.make_tensor_value_info('input1', TensorProto.FLOAT, [1, 3, 224, 224]) + input2 = helper.make_tensor_value_info('input2', TensorProto.FLOAT, [1, 3, 224, 224]) + output = helper.make_tensor_value_info('output', TensorProto.FLOAT, [1, 3, 224, 224]) + + add_node = helper.make_node('Add', ['input1', 'input2'], ['output']) + graph = helper.make_graph([add_node], 'simple_add', [input1, input2], [output]) + model = helper.make_model(graph) + + # 임시 모델 파일로 저장 + import tempfile + import os + + with tempfile.NamedTemporaryFile(suffix='.onnx', delete=False) as tmp_file: + tmp_path = tmp_file.name + onnx.save(model, tmp_path) + + try: + # DirectML로 세션 생성 + session = ort.InferenceSession(tmp_path, session_options, providers=providers) + + # 테스트 입력 데이터 + input_data1 = np.random.rand(1, 3, 224, 224).astype(np.float32) + input_data2 = np.random.rand(1, 3, 224, 224).astype(np.float32) + + # 실제 추론 수행 + start_time = time.time() + results = session.run(None, {'input1': input_data1, 'input2': input_data2}) + inference_time = time.time() - start_time + + # 결과 검증 + expected = input_data1 + input_data2 + if np.allclose(results[0], expected, rtol=1e-5): + self.logger.log(f"✅ 추론 테스트 성공 ({inference_time:.3f}초)", level=logging.DEBUG) + return True + else: + self.logger.log("❌ 추론 결과가 예상과 다름", level=logging.WARNING) + return False + + finally: + # 임시 파일 삭제 + try: + os.unlink(tmp_path) + except: + pass + + except Exception as e: + self.logger.log(f"❌ 간단한 추론 테스트 실패: {e}", level=logging.WARNING) + return False + + def _benchmark_directml_vs_cpu(self) -> bool: + """DirectML과 CPU 성능 비교로 실제 GPU 가속 확인""" + try: + self.logger.log("⏱️ DirectML vs CPU 성능 벤치마크 시작...", level=logging.DEBUG) + + # 현재는 간단한 체크만 수행 (더 정교한 벤치마크는 추후 구현) + # VM 환경에서는 GPU 가속이 제대로 안 될 가능성이 높으므로 + # 여기서는 초기화 시간과 기본 동작만 확인 + + return True # 일단 통과로 처리 + + except Exception as e: + self.logger.log(f"성능 벤치마크 중 오류: {e}", level=logging.DEBUG) + return True # 벤치마크 실패는 허용 + + def get_cuda_status(self) -> Dict[str, Any]: + """현재 GPU 가속 상태 정보 반환 (하위 호환성을 위해 메서드명 유지)""" + return { + "can_use_cuda": self.can_use_cuda, # DirectML 사용 가능 여부 (호환성) + "cuda_available": self.can_use_cuda, # 호환성을 위해 동일한 값 + "directml_available": self.directml_available, + "gpu_info": self.gpu_info.copy(), + "initialization_attempted": self.initialization_attempted + } + + def force_cpu_mode(self) -> None: + """강제로 CPU 모드로 전환""" + self.can_use_cuda = False + self.logger.log("강제로 CPU 모드로 전환됨", level=logging.WARNING) + + def get_optimal_onnx_providers(self) -> list: + """DirectML 기반 최적 ONNXRuntime provider 우선순위 리스트 반환""" + providers = [] + + if self.can_use_cuda: # DirectML 사용 가능 여부 + try: + import onnxruntime as ort + available = ort.get_available_providers() + + # DirectML Provider (Windows GPU 가속) + if 'DmlExecutionProvider' in available: + directml_options = { + 'device_id': 0, # 기본 GPU 사용 + } + providers.append(('DmlExecutionProvider', directml_options)) + self.logger.log("⚡ DirectML provider 추가 (범용 GPU 가속 - NVIDIA/AMD/Intel 지원)", level=logging.DEBUG) + else: + self.logger.log("❌ DirectML provider 사용 불가", level=logging.WARNING) + + except Exception as e: + self.logger.log(f"DirectML Provider 확인 실패: {e}", level=logging.WARNING) + + # 항상 CPU는 폴백으로 추가 + providers.append(('CPUExecutionProvider', {})) + provider_names = [p[0] if isinstance(p, tuple) else p for p in providers] + self.logger.log(f"📊 최종 provider 순서: {provider_names}", level=logging.DEBUG) + + return providers + + def log_gpu_memory_usage(self) -> None: + """현재 GPU 메모리 사용량 로깅""" + if not self.can_use_cuda: + return + + try: + result = subprocess.run( + ["nvidia-smi", "--query-gpu=memory.used,memory.total", "--format=csv,noheader,nounits"], + capture_output=True, + text=True, + timeout=5, + creationflags=subprocess.CREATE_NO_WINDOW if platform.system() == "Windows" else 0 + ) + + if result.returncode == 0 and result.stdout.strip(): + lines = result.stdout.strip().split('\n') + for i, line in enumerate(lines): + if line.strip(): + parts = [p.strip() for p in line.split(',')] + if len(parts) >= 2: + used_mb = int(parts[0]) + total_mb = int(parts[1]) + usage_percent = (used_mb / total_mb) * 100 + self.logger.log( + f"GPU {i} 메모리 사용량: {used_mb}MB/{total_mb}MB ({usage_percent:.1f}%)", + level=logging.DEBUG + ) + except Exception as e: + self.logger.log(f"GPU 메모리 사용량 확인 실패: {e}", level=logging.DEBUG) + + +# 전역 GPU 관리자 인스턴스 (선택적 사용) +_global_gpu_manager = None + +def get_global_gpu_manager(logger=None) -> GPUManager: + """전역 GPU 관리자 인스턴스 반환""" + global _global_gpu_manager + if _global_gpu_manager is None: + _global_gpu_manager = GPUManager(logger) + return _global_gpu_manager + + +def check_cuda_simple() -> bool: + """간단한 GPU 가속 사용 가능성 확인 (DirectML, 하위 호환성을 위해 함수명 유지)""" + try: + import onnxruntime as ort + providers = ort.get_available_providers() + return "DmlExecutionProvider" in providers + except: + return False + +def check_directml_simple() -> bool: + """간단한 DirectML 사용 가능성 확인 (캐시 없음)""" + try: + import onnxruntime as ort + providers = ort.get_available_providers() + return "DmlExecutionProvider" in providers + except: + return False diff --git a/modules/image_processor3.py b/modules/image_processor3.py new file mode 100644 index 0000000..0ee9f19 --- /dev/null +++ b/modules/image_processor3.py @@ -0,0 +1,2189 @@ +import os +import asyncio +import requests +import time +import logging +from urllib.parse import urlparse +import sys +import re +import cv2 +import psutil +import tracemalloc +# OpenCV 의 내부 최적화(메모리 풀) 사용을 비활성화하여 파편화 위험을 낮춤 +cv2.setUseOptimized(False) +import random +import gc +import numpy as np +from PIL import Image +from PIL import features +from modules.onnx_ocr_module.src.onnx_ocr_wrapper import ONNXOCRModule as Onnx_OCRModule +# from modules.ocr_module import OCRModule as Paddle_OCRModule +from modules.mask_module_for_paddle import MaskModule +# from modules.mask_module_for_easy import MaskModule_easy +from modules.text_rendering_module import TextRenderingModuleOptimized +from modules.postImageManager import PostImageManager +from translatepy.translators.google import GoogleTranslate +# Gemma 번역 클라이언트(옵셔널): 배포 환경에서 누락되어도 동작하도록 안전 임포트 +try: + from modules.gemma_client import GemmaTranslator # 표준 경로 +except Exception: + try: + # 개발 환경에서 상대 경로 임포트가 남아있는 경우 대비 + from gemma_client import GemmaTranslator # noqa: F401 + except Exception: + GemmaTranslator = None # 사용 시 체크 후 동작 + +# from modules.background_removal_module import BackgroundRemovalModule +# from modules.background_removal_module_pp import PPMattingBackgroundRemovalModule # (변경) + +from modules.request_inpaint import Request_AI_Server +from modules.gpu_utils import GPUManager + +class ImageProcessor3: + """이미지 다운로드, OCR, 번역 처리를 담당하는 클래스""" + + def __init__(self, logger, page, toggle_states, unwanted_words, authenticated_by_admin, base_dir, papago_translator): + self.logger = logger + self.page = page + self.base_dir = base_dir + self.toggle_states = toggle_states + self.unwanted_texts = unwanted_words + self.authenticated_by_admin = authenticated_by_admin + + # 메모리 추적 시작 (파이썬 객체 메모리 할당 추적) + try: + if not tracemalloc.is_tracing(): + tracemalloc.start() + self.logger.log("tracemalloc 메모리 추적 시작", level=logging.DEBUG) + except Exception as e: + self.logger.log(f"tracemalloc 시작 실패: {e}", level=logging.WARNING) + + # 기본 속성들을 먼저 None으로 초기화하여 안전성 확보 + self.postImageManager = None + self.ocr_module = None + self.mask_module = None + self.text_rendering_module = None + self.request_ai_server = None + self.gtranslate = None + self.migan = None + self.gpu_manager = None + + try: + # GPU 관리자 초기화 + self.gpu_manager = GPUManager(logger=logger) + self.gpu_manager.initialize_gpu_state(toggle_states) + + # GPU 상태 상세 로깅 + gpu_status = self.gpu_manager.get_cuda_status() + + self.logger.log(f"🔧 ImageProcessor3 GPU 상태 요약:", level=logging.DEBUG) + self.logger.log(f" - CUDA 사용 가능: {gpu_status['can_use_cuda']}", level=logging.DEBUG) + self.logger.log(f" - toggle_states['use_cuda']: {toggle_states.get('use_cuda', 'NOT_SET')}", level=logging.DEBUG) + self.logger.log(f" - GPU 하드웨어 정보: {gpu_status['gpu_info']}", level=logging.DEBUG) + + self.logger.log(f"ImageProcessor3 Init toggle_states: {self.toggle_states}", level=logging.DEBUG) + + self.is_member_valid = self.toggle_states.get('membership_level', 'basic') == 'vip' or self.authenticated_by_admin + self.logger.log(f"is_member_valid: {self.is_member_valid}", level=logging.DEBUG) + + self.papago_translator = papago_translator + + self.inpaint_method = 'migan' + + # 폰트 선택: toggle_states['font_type'] 기반으로 modules/fonts 내 파일 매핑 + fonts_dir = os.path.join(self.base_dir, "fonts") + font_map = { + "폰트1": "HakgyoansimDunggeunmisoTTFB.ttf", + "폰트2": "NanumBarunGothic.ttf", + "폰트3": "NanumSquareRoundR.ttf", + "폰트4": "gamtanload.ttf", + "폰트5": "Cafe24Ohsquare-v2.0.ttf", + } + font_type = self.toggle_states.get("font_type", "폰트1") + if isinstance(font_type, str) and font_type.strip(): + font_key = font_type.strip() + else: + font_key = "폰트1" # 기본값 + + chosen_file = font_map.get(font_key, font_map["폰트1"]) if font_map else None + candidate_paths = [] + if chosen_file: + candidate_paths.append(os.path.join(fonts_dir, chosen_file)) + # 추가 폴백 경로들 + candidate_paths.extend([ + self.toggle_states.get('image_font_path', ""), + os.path.join(self.base_dir, "HakgyoansimDunggeunmisoTTFB.ttf"), + "C:/Windows/Fonts/malgun.ttf", + "C:/Windows/Fonts/gulim.ttc", + ]) + + resolved_font_path = None + for p in candidate_paths: + try: + if p and os.path.exists(p): + resolved_font_path = p + break + except Exception: + continue + + # 최종 폰트 경로 확정 + self.font_path = resolved_font_path or self.toggle_states.get('image_font_path', os.path.join(self.base_dir, "HakgyoansimDunggeunmisoTTFB.ttf")) + # PostImageManager 등 하위 모듈과 공유 + try: + self.toggle_states['image_font_path'] = self.font_path + except Exception: + pass + + self.TEMP_IMAGE_DIR = self.toggle_states.get('TEMP_IMAGE_DIR', "") + + self.debugging_save_Dir = os.path.join(self.base_dir, "debug_images") + if not os.path.exists(self.debugging_save_Dir): + os.makedirs(self.debugging_save_Dir) + self.logger.log(f"debug_images 디렉토리 생성: {self.debugging_save_Dir}", level=logging.DEBUG) + else: + self.logger.log(f"debug_images 디렉토리 이미 존재: {self.debugging_save_Dir}", level=logging.DEBUG) + + self.use_local_rembg = self.toggle_states.get("use_local_rembg", False) + + self.local_model_name = self.toggle_states.get("local_model_name", 'birefnet-general-lite') + + # self.logger.log(f"self.toggle_states: {self.toggle_states}", level=logging.DEBUG) + + self.logger.log(f"self.font_path: {self.font_path}", level=logging.DEBUG) + + self.logger.log(f"toggle_states font_path: {self.toggle_states.get('image_font_path', '')}", level=logging.DEBUG) + + self.logger.log(f"self.TEMP_IMAGE_DIR: {self.TEMP_IMAGE_DIR}", level=logging.DEBUG) + + self.logger.log(f"self.debugging_save_Dir: {self.debugging_save_Dir}", level=logging.DEBUG) + + self.logger.log(f"self.unwanted_texts: {self.unwanted_texts}", level=logging.DEBUG) + + self.logger.log(f"self.inpaint_method: {self.inpaint_method}", level=logging.DEBUG) + + # ----------------------------- 메모리 파편화 완화 ----------------------------- + # Pillow 가 거대 이미지를 열 때 과도한 메모리를 점유하지 않도록 최대 픽셀 수 제한 + max_px = self.toggle_states.get("max_image_pixels", 20_000_000) # 약 20MP, 필요 시 조정 (20MP = 4500x4500, 50MP=8000x6000) + Image.MAX_IMAGE_PIXELS = max_px + self.logger.log(f"Image.MAX_IMAGE_PIXELS set to {max_px}", level=logging.DEBUG) + # ----------------------------------------------------------------------------- + + # 각 모듈을 개별적으로 초기화하고 예외 처리 + self.ocr_module = None # 기본값 설정 + try: + self.ocr_module = Onnx_OCRModule(logger=self.logger, base_dir=self.base_dir, gpu_manager=self.gpu_manager, toggle_states=self.toggle_states) + self.logger.log("✅ ONNX OCR 모듈 초기화 성공", level=logging.INFO) + except Exception as e: + self.logger.log(f"❌ ONNX OCR 모듈 초기화 실패: {e}", level=logging.ERROR, exc_info=True) + self.ocr_module = None # 명시적으로 None 설정 + + + # try: + # self.ai_translator = GemmaTranslator( + # base_url=self.toggle_states.get("gemma_api_base_url", "http://192.168.0.146:8000"), + # timeout=int(self.toggle_states.get("gemma_api_timeout", 120)), + # ) + # self.logger.log(f"gemma_api_base_url: {self.ai_translator.base_url}", level=logging.DEBUG) + # self.logger.log(f"GemmaTranslator 연결: base={self.ai_translator.base_url}", level=logging.INFO) + # except Exception as e: + # self.logger.log(f"GemmaTranslator 연결 실패: {e}", level=logging.ERROR, exc_info=True) + # self.ai_translator = None + + + # try: + # # CUDNN 버전 불일치 문제 해결을 위한 force_cpu 옵션 + # # toggle_states에서 force_cpu_ocr 설정 확인 (기본값: False) + # force_cpu_ocr = self.toggle_states.get('force_cpu_ocr', False) + # force_cpu_ocr = True + + # self.ocr_module = Paddle_OCRModule( + # logger=self.logger, + # base_dir=self.base_dir, + # gpu_manager=self.gpu_manager, + # force_cpu=force_cpu_ocr + # ) + + # if force_cpu_ocr: + # self.logger.log("✅ PaddleOCR 모듈 초기화 성공 (CPU 강제 모드)", level=logging.INFO) + # else: + # self.logger.log("✅ PaddleOCR 모듈 초기화 성공", level=logging.INFO) + # except Exception as e: + # self.logger.log(f"❌ PaddleOCR 모듈 초기화 실패: {e}", level=logging.ERROR, exc_info=True) + # self.ocr_module = None # 명시적으로 None 설정 + + try: + self.mask_module = MaskModule(logger=self.logger, base_dir=self.base_dir) + self.logger.log("MaskModule 초기화 성공", level=logging.DEBUG) + except Exception as e: + self.logger.log(f"MaskModule 초기화 실패: {e}", level=logging.ERROR, exc_info=True) + + try: + self.text_rendering_module = TextRenderingModuleOptimized(logger=self.logger, font_path=self.font_path) + self.logger.log("TextRenderingModule 초기화 성공", level=logging.DEBUG) + except Exception as e: + self.logger.log(f"TextRenderingModule 초기화 실패: {e}", level=logging.ERROR, exc_info=True) + + try: + self.postImageManager = PostImageManager(logger=self.logger, toggle_states=self.toggle_states) + self.logger.log("PostImageManager 초기화 성공", level=logging.DEBUG) + except Exception as e: + self.logger.log(f"PostImageManager 초기화 실패: {e}", level=logging.ERROR, exc_info=True) + # PostImageManager는 중요한 모듈이므로 기본적인 fallback 생성 + try: + self.postImageManager = self._create_fallback_post_image_manager() + self.logger.log("PostImageManager fallback 생성 성공", level=logging.INFO) + except Exception as e2: + self.logger.log(f"PostImageManager fallback 생성도 실패: {e2}", level=logging.ERROR, exc_info=True) + + # self.background_removal_module = BackgroundRemovalModule(logger=self.logger, default_model="birefnet-general") + # self.background_removal_module = PPMattingBackgroundRemovalModule(logger=self.logger, default_model="ppmatting-hrnet-1x") + # self.background_removal_module = PPMattingBackgroundRemovalModule(logger=self.logger) + + self.local_rembg_model_path = os.path.join(self.base_dir, "rembg_models", "BriaRMBG1.4_model_fp16.onnx") + + try: + # Request_AI_Server에도 GPU 상태 전달 + self.request_ai_server = Request_AI_Server( + logger=self.logger, + gpu_manager=self.gpu_manager, + local_rembg_model_path=self.local_rembg_model_path + ) + self.logger.log("Request_AI_Server 초기화 성공", level=logging.DEBUG) + except Exception as e: + self.logger.log(f"Request_AI_Server 초기화 실패: {e}", level=logging.ERROR, exc_info=True) + + try: + self.gtranslate = GoogleTranslate() + if self.gtranslate is not None: + self.logger.log(f"GoogleTranslate 초기화 성공", level=logging.DEBUG) + else: + self.logger.log(f"GoogleTranslate 초기화 결과: None", level=logging.DEBUG) + except Exception as e: + self.logger.log(f"GoogleTranslate 초기화 실패: {e}", level=logging.ERROR, exc_info=True) + + # MIGAN ONNX 파이프라인 준비(옵션 토글 기반) + try: + from modules.migan_module import build_migan_from_toggle + # MIGAN이 실제로 사용될 때만 초기화 (inpaint_method이 'migan'이거나 로컬 inpaint_method가 'migan'일 때) + inpaint_method = self.toggle_states.get("inpaint_method", "request") + local_inpaint_method = self.toggle_states.get("local_inpaint_method", "migan") + + should_init_migan = ( + self.toggle_states.get("migan_onnx_path") and + (inpaint_method == "migan" or local_inpaint_method == "migan") + ) + + if should_init_migan: + # GPU 상태에 따라 CUDA 사용 여부 결정 + enhanced_toggle_states = self.toggle_states.copy() + if self.gpu_manager and self.gpu_manager.can_use_cuda: + enhanced_toggle_states["migan_use_cuda"] = enhanced_toggle_states.get("migan_use_cuda", False) + self.logger.log(f"MIGAN CUDA 사용 설정: {enhanced_toggle_states['migan_use_cuda']}", level=logging.DEBUG) + else: + enhanced_toggle_states["migan_use_cuda"] = False + self.logger.log("MIGAN CUDA 사용 불가 - CPU 모드로 설정", level=logging.DEBUG) + + self.logger.log(f"[MIGAN] GPU 관리자 전달: {type(self.gpu_manager).__name__}, can_use_cuda: {self.gpu_manager.can_use_cuda if self.gpu_manager else 'N/A'}", level=logging.DEBUG) + self.migan = build_migan_from_toggle(enhanced_toggle_states, logger=self.logger, gpu_manager=self.gpu_manager) + self.logger.log(f"[MIGAN] 초기화 완료: gpu_manager 속성={hasattr(self.migan, 'gpu_manager')}, 값={getattr(self.migan, 'gpu_manager', None)}", level=logging.DEBUG) + else: + self.migan = None + self.logger.log(f"MIGAN 초기화 건너뜀: inpaint_method={inpaint_method}, local_inpaint_method={local_inpaint_method}, migan_onnx_path={bool(self.toggle_states.get('migan_onnx_path'))}", level=logging.DEBUG) + except Exception as e: + self.migan = None + self.logger.log(f"MIGAN 초기화 실패: {e}", level=logging.ERROR, exc_info=True) + + # 인페인팅 실행 정보(마지막 사용 방식/장치) 추적용 내부 상태 + self._last_inpaint_used = None + self._last_inpaint_device = None + + except Exception as e: + self.logger.log(f"ImageProcessor3 초기화 중 치명적 오류 발생: {e}", level=logging.ERROR, exc_info=True) + # 치명적 오류 발생 시에도 기본 속성들이 None으로라도 설정되도록 보장 + + def _create_fallback_post_image_manager(self): + """PostImageManager 초기화 실패 시 사용할 fallback 객체 생성""" + + class FallbackPostImageManager: + """PostImageManager의 최소한 기능을 제공하는 fallback 클래스""" + def __init__(self, logger, toggle_states): + self.logger = logger + self.toggle_states = toggle_states + self.font = None + + def update_toggle_states(self, toggle_states): + """toggle_states 업데이트""" + self.toggle_states = toggle_states + self.logger.log("FallbackPostImageManager toggle_states 업데이트됨", level=logging.DEBUG) + + def save_image_to_path(self, image, path): + """기본적인 이미지 저장 기능""" + try: + if hasattr(image, 'save'): + image.save(path) + return True + else: + self.logger.log("이미지 객체에 save 메서드가 없습니다", level=logging.ERROR) + return False + except Exception as e: + self.logger.log(f"Fallback 이미지 저장 실패: {e}", level=logging.ERROR) + return False + + def crop_image(self, image, is_thumb=False, crop_percentage=0.01): + """기본적인 이미지 크롭 기능""" + try: + if hasattr(image, 'size') and hasattr(image, 'crop'): + width, height = image.size + left = width * crop_percentage + top = height * crop_percentage + right = width * (1 - crop_percentage) + bottom = height * (1 - crop_percentage) + return image.crop((left, top, right, bottom)) + else: + self.logger.log("이미지 객체에 필요한 메서드가 없습니다", level=logging.ERROR) + return image + except Exception as e: + self.logger.log(f"Fallback 이미지 크롭 실패: {e}", level=logging.ERROR) + return image + + return FallbackPostImageManager(self.logger, self.toggle_states) + + def update_toggle_states(self, toggle_states): + self.toggle_states = toggle_states + if self.postImageManager is not None: + try: + self.postImageManager.update_toggle_states(self.toggle_states) + self.logger.log(f"이미지 프로세서 toggle_states 업데이트 : {self.toggle_states}", level=logging.DEBUG) + except Exception as e: + self.logger.log(f"PostImageManager toggle_states 업데이트 중 오류: {e}", level=logging.ERROR, exc_info=True) + else: + self.logger.log("PostImageManager가 None이므로 toggle_states 업데이트를 건너뜁니다.", level=logging.WARNING) + + def update_unwanted_texts(self, texts): + self.unwanted_texts = texts + self.logger.log(f"이미지프로세서 unwanted_texts: {self.unwanted_texts}", level=logging.DEBUG) + + def __del__(self): + """소멸자에서 리소스 정리""" + self.cleanup() + self.logger.log("이미지 프로세서 소멸", level=logging.DEBUG) + + def cleanup(self): + """리소스 정리""" + try: + # OCR 모듈 정리 + if hasattr(self, 'ocr_module'): + try: + del self.ocr_module + self.logger.log("OCR 모듈 정리 완료", level=logging.DEBUG) + except Exception as e: + self.logger.log(f"OCR 모듈 정리 중 오류: {e}", level=logging.WARNING) + + # 마스크 모듈 정리 + if hasattr(self, 'mask_module'): + try: + del self.mask_module + self.logger.log("마스크 모듈 정리 완료", level=logging.DEBUG) + except Exception as e: + self.logger.log(f"마스크 모듈 정리 중 오류: {e}", level=logging.WARNING) + + # 텍스트 렌더링 모듈 정리 + if hasattr(self, 'text_renderer'): + try: + del self.text_renderer + self.logger.log("텍스트 렌더링 모듈 정리 완료", level=logging.DEBUG) + except Exception as e: + self.logger.log(f"텍스트 렌더링 모듈 정리 중 오류: {e}", level=logging.WARNING) + + # GPU 메모리 정리 + if hasattr(self, 'gpu_manager') and self.gpu_manager and self.gpu_manager.can_use_cuda: + try: + import paddle + if hasattr(paddle, 'device') and hasattr(paddle.device, 'cuda'): + paddle.device.cuda.empty_cache() + self.logger.log("CUDA 캐시 정리 완료", level=logging.DEBUG) + except Exception as e: + self.logger.log(f"CUDA 캐시 정리 중 오류: {e}", level=logging.WARNING) + + # Python GC 강제 실행 + import gc + gc.collect() + + # OpenCV 윈도우 정리 + try: + cv2.destroyAllWindows() + except: + pass + + # 임시 폴더 삭제 + if hasattr(self, 'TEMP_IMAGE_DIR') and os.path.exists(self.TEMP_IMAGE_DIR): + # shutil.rmtree(self.TEMP_IMAGE_DIR) + self.logger.log(f"임시 폴더 삭제됨: {self.TEMP_IMAGE_DIR}", level=logging.DEBUG) + + except Exception as e: + self.logger.log(f"리소스 정리 중 오류: {e}", level=logging.ERROR, exc_info=True) + + def reset_ocr_module(self): + """OCR 모듈을 명시적으로 삭제하고 재생성.""" + try: + self.logger.log("🔄 OCR 모듈 재초기화 시작", level=logging.DEBUG) + + # 기존 모듈 참조 해제 + if hasattr(self, 'ocr_module'): + del self.ocr_module + self.logger.log("기존 OCR 모듈 참조 해제 완료", level=logging.DEBUG) + + # 안전을 위해 먼저 None으로 설정 + self.ocr_module = None + + # CUDA 메모리 정리 (GPU 사용 시) + if hasattr(self, 'gpu_manager') and self.gpu_manager and self.gpu_manager.can_use_cuda: + try: + import paddle + if hasattr(paddle, 'device') and hasattr(paddle.device, 'cuda'): + paddle.device.cuda.empty_cache() + self.logger.log("CUDA 캐시 정리 완료", level=logging.DEBUG) + except Exception as e: + self.logger.log(f"CUDA 캐시 정리 실패: {e}", level=logging.WARNING) + + # Python GC 강제 실행 (여러 번 실행으로 더 강력한 정리) + import gc + for i in range(3): + collected = gc.collect() + if collected > 0: + self.logger.log(f"GC 실행 {i+1}: {collected}개 객체 정리", level=logging.DEBUG) + + # ONNX OCR 모듈로 재초기화 + try: + from modules.onnx_ocr_module.src.onnx_ocr_wrapper import ONNXOCRModule as OCRModule + self.ocr_module = OCRModule( + logger=self.logger, + base_dir=self.base_dir, + gpu_manager=self.gpu_manager, + toggle_states=self.toggle_states, + ) + self._ocr_call_count = 0 + self.logger.log("✅ ONNX OCR 모듈 재초기화 완료", level=logging.INFO) + return True + except Exception as init_error: + self.logger.log(f"❌ ONNX OCR 모듈 재초기화 중 오류: {init_error}", level=logging.ERROR, exc_info=True) + self.ocr_module = None + return False + + except Exception as e: + self.logger.log(f"❌ OCR 모듈 재초기화 실패: {e}", level=logging.ERROR, exc_info=True) + # 안전을 위해 None으로 설정 + self.ocr_module = None + return False + + def is_valid_image_path(self, path): + # http/https 또는 로컬 파일(.jpg, .png 등) 모두 허용 + if re.match(r'^(http|https)://.*\.(jpg|jpeg|png|bmp|gif|webp|tiff?)(\?.*)?$', path, re.IGNORECASE): + return True + if os.path.isfile(path) and path.lower().endswith(('.jpg', '.jpeg', '.png', '.bmp', '.gif', '.webp', '.tif', '.tiff')): + return True + return False + + async def process_single_image(self, original_image_url, index, delay=1.0, file_prefix=""): + """ + 단일 이미지를 처리합니다 (다운로드 -> OCR -> 인페인팅) + + Args: + page: Playwright 페이지 객체 + original_image_url (str): 처리할 이미지 URL + index (int): 이미지 인덱스 + is_localServer (bool): 로컬 서버 사용 여부 + delay (float): 요청 간격 (초) + file_prefix (str): 파일명에 추가할 접두사 (예: "detail", "option") + + Returns: + dict: 처리 결과를 포함한 딕셔너리 + - status: 'inpainted', 'original', 'exclude', 'error' 중 하나 + - path: 처리된 이미지 파일 경로 또는 원본 이미지 파일 경로 + - error: 오류 메시지 (status가 'error'인 경우에만 포함) + """ + local_image_path = None + delay = random.uniform(0.5, 1.5) + delay = delay / 3 # 봇 탐지 회피를 위해 요청 간격 조절 - 자체번역으로 간격 최소화 + + # 파이프라인 타이밍 계측 시작 + import time as _time + _t_all_start = time.time() + _timings_ms = {} + # 직전 인페인트 사용 방식 초기화 + self._last_inpaint_used = None + self._last_inpaint_device = None + + # self.logger.log(f"unwanted_texts: {self.unwanted_texts}", level=logging.DEBUG) + self.logger.log(f"이미지 번역시작", level=logging.DEBUG) + try: + # 0. 이미지 URL 유효성 체크 (http/https & 이미지 확장자) + if not original_image_url or not isinstance(original_image_url, str): + self.logger.log(f"이미지 {index+1} 처리 중단: 이미지 URL 없음 또는 타입 오류", level=logging.WARNING) + return {'status': 'failed', 'path': original_image_url, 'error': '이미지 URL 없음 또는 타입 오류', 'inpaint_method': self._last_inpaint_used, 'inpaint_device': self._last_inpaint_device} + + if not self.is_valid_image_path(original_image_url): + self.logger.log(f"이미지 {index+1} 처리 중단: 유효하지 않은 이미지 주소 - {original_image_url}", level=logging.WARNING) + return {'status': 'failed', 'path': original_image_url, 'error': '유효하지 않은 이미지 주소', 'inpaint_method': self._last_inpaint_used, 'inpaint_device': self._last_inpaint_device} + + # 요청 간격 조절 (봇 탐지 회피) + if delay > 0: + await asyncio.sleep(delay) + + # OCR 권한 상태 로그 + ocr_enabled = self.toggle_states.get('ocr', False) + processing_mode = "OCR+인페인팅 모드" if ocr_enabled else "전체 번역 모드" + self.logger.log(f"이미지 {index+1} 처리 시작: {original_image_url} - {processing_mode}", level=logging.DEBUG) + + # 1. 이미지 다운로드 + _t = _time.time() + local_image_path = self.download_image(image_url=original_image_url, index=index, file_prefix=file_prefix) + _timings_ms["download"] = (_time.time() - _t) * 1000.0 + + if not local_image_path: + self.logger.log(f"이미지 {index+1} 다운로드 실패, 원본 URL 반환", level=logging.WARNING) + return {'status': 'failed', 'path': original_image_url, 'error': '다운로드 실패', 'inpaint_method': self._last_inpaint_used, 'inpaint_device': self._last_inpaint_device} + + # 1-A. 상세페이지 이미지 전처리 (크기 표준화 및 분할) + if file_prefix == "detail": + local_image_path = await self.preprocess_detail_image(local_image_path, index) + if not local_image_path: + self.logger.log(f"상세페이지 이미지 {index+1} 전처리 실패", level=logging.WARNING) + return {'status': 'failed', 'path': original_image_url, 'error': '상세페이지 이미지 전처리 실패'} + # elif file_prefix == "thumb": + # 1-B. 썸네일 이미지는 고해상도 입력 다운스케일 (메모리 절약) + # max_dim = self.toggle_states.get('max_image_resolution', 1200) + # local_image_path = self.downscale_image_if_large(local_image_path, max_dim=max_dim) + else: + # 1-C. 옵션 이미지는 스케일 처리 건너뛰기 (이미 작은 크기) + self.logger.log(f"옵션 이미지는 스케일 처리 건너뛰기: {file_prefix}", level=logging.DEBUG) + + # 전처리 유형에 따른 로그 메시지 + if file_prefix == "detail": + processing_type = "상세페이지 전처리 완료" + elif file_prefix == "thumb": + processing_type = "썸네일 스케일 처리 완료" + else: + processing_type = "옵션 이미지 원본 유지" + + self.logger.log(f"이미지 {index+1} 로컬 저장위치({processing_type}): {local_image_path}", level=logging.DEBUG) + + # 2. OCR 텍스트 감지 + _t = _time.time() + # 메모리 추적: OCR 시작 전 + ocr_before_mem = psutil.virtual_memory() + ocr_before_mb = ocr_before_mem.used / 1024 / 1024 + + ocr_results = self.safe_detect(local_image_path) + _timings_ms["ocr"] = (_time.time() - _t) * 1000.0 + + # 메모리 추적: OCR 완료 후 + ocr_after_mem = psutil.virtual_memory() + ocr_after_mb = ocr_after_mem.used / 1024 / 1024 + ocr_change_mb = ocr_after_mb - ocr_before_mb + ocr_change_percent = (ocr_change_mb / ocr_before_mb) * 100 if ocr_before_mb > 0 else 0 + self.logger.log( + f"메모리 변화 [OCR 처리]: {ocr_before_mb:.1f}MB -> {ocr_after_mb:.1f}MB " + f"({ocr_change_mb:+.1f}MB, {ocr_change_percent:+.1f}%) - 이미지 {index+1}", + level=logging.DEBUG if abs(ocr_change_mb) < 10 else logging.INFO + ) + self.logger.log(f"ocr_results: {ocr_results}", level=logging.DEBUG) + + # 2-A. 상세페이지인 경우 OCR 정보 수집 및 저장 + if file_prefix == "detail": + store_ocr_to_db = self.toggle_states.get('store_ocr_data_to_db', False) # 기본값: False + if store_ocr_to_db: + await self.collect_and_store_ocr_data(original_image_url, local_image_path, ocr_results, index) + else: + # 메모리에만 저장 (현재 세션용) + await self.collect_ocr_data_in_memory(original_image_url, local_image_path, ocr_results, index) + + filter_ocr_results = self.filter_ocr_results(ocr_results) + self.logger.log(f"filter_ocr_results: {filter_ocr_results}", level=logging.DEBUG) + ocr_count = len(filter_ocr_results) + + # 3. OCR 모듈 상태 확인 및 중국어 텍스트 검사 + if not hasattr(self, 'ocr_module') or self.ocr_module is None: + self.logger.log("⚠️ OCR 모듈이 초기화되지 않아 원본 이미지 반환", level=logging.WARNING) + return {'status': 'original', 'path': local_image_path, 'message': 'OCR 모듈 초기화 실패', 'inpaint_method': self._last_inpaint_used, 'inpaint_device': self._last_inpaint_device} + + # 중국어 텍스트가 없는 경우 정상 케이스로 처리 + if not self.ocr_module.filter_chinese_text(filter_ocr_results): + self.logger.log(f"이미지 {index+1} 중국어 텍스트 없음 - 정상 케이스 (NO_TEXT)", level=logging.INFO) + return {'status': 'original', 'path': local_image_path, 'message': '중국어 텍스트가 발견되지 않았습니다', 'inpaint_method': self._last_inpaint_used, 'inpaint_device': self._last_inpaint_device} + + # 4. 한글 텍스트가 존재하는 경우 원본 이미지 반환으로 번역 패스 + if self.ocr_module.filter_korean_text(filter_ocr_results): + self.logger.log(f"이미지 {index+1} 한글 텍스트 존재, 원본 이미지 반환", level=logging.DEBUG) + return {'status': 'original', 'path': local_image_path, 'inpaint_method': self._last_inpaint_used, 'inpaint_device': self._last_inpaint_device} + + # 4. 병렬 실행: 번역(I/O) ↔ 마스크 생성(CPU) + # - OCR 결과를 기반으로 두 작업은 서로 독립이므로 동시에 수행 가능 + if ocr_count < 5: + expansion_size = 12 + blur_size = 15 + elif ocr_count < 10: + expansion_size = 9 + blur_size = 12 + elif ocr_count < 15: + expansion_size = 7 + blur_size = 9 + elif ocr_count < 20: + expansion_size = 5 + blur_size = 6 + else: + expansion_size = 10 + blur_size = 15 + + loop = asyncio.get_running_loop() + _t_trans = _time.time() + _t_mask = _time.time() + + translate_future = loop.run_in_executor( + None, + lambda: self.batch_google_translate_texts(filter_ocr_results) + ) + mask_future = loop.run_in_executor( + None, + lambda: self.mask_module.create_masks( + image_path=local_image_path, + ocr_results=filter_ocr_results, + mask_option="basic", + expansion_size=expansion_size, + blur_size=blur_size + ) + ) + + translated_texts, masks = await asyncio.gather(translate_future, mask_future) + _timings_ms["translate"] = (_time.time() - _t_trans) * 1000.0 + _timings_ms["mask"] = (_time.time() - _t_mask) * 1000.0 + self.logger.log(f"translated_texts: {translated_texts}", level=logging.DEBUG) + self.logger.log(f"마스크 생성 완료", level=logging.DEBUG) + + # 5. OCR 권한에 따른 텍스트 필터링 + if ocr_enabled: + filtered_translated_texts = self.process_translated_texts(translated_texts, local_image_path, index) + if not filtered_translated_texts: + self.logger.log(f"이미지 {index+1} 제외됨", level=logging.DEBUG) + return {'status': 'exclude', 'path': local_image_path, 'inpaint_method': self._last_inpaint_used, 'inpaint_device': self._last_inpaint_device} + else: + self.logger.log(f"이미지 {index+1} 치환됨", level=logging.DEBUG) + else: + # OCR 권한이 없으면 번역된 텍스트를 그대로 사용 + filtered_translated_texts = translated_texts + self.logger.log(f"이미지 {index+1} OCR 권한 없음, 전체 번역 모드", level=logging.DEBUG) + + # ------------------- 인페인트 엔진 자동 선택 (옵션) ------------------- + try: + inpaint_model_pref = self.toggle_states.get('inpaint_model', 'auto').lower() + except Exception: + inpaint_model_pref = 'auto' + + if inpaint_model_pref in ['lama', 'migan', 'cv']: + # 명시적 모델 지정 시 강제 + forced_map = {'lama': 'request', 'migan': 'migan', 'cv': 'cv'} + self.inpaint_method = forced_map.get(inpaint_model_pref, self.inpaint_method) + self.logger.log(f"[inpaint_model 강제] {inpaint_model_pref} → inpaint_method={self.inpaint_method}", level=logging.INFO) + else: + # auto: 마스크 통계 기반으로 결정 + try: + analysis = self._analyze_mask_stats(masks) + # 임계값 (토글로 조정 가능) + area_large_thr = float(self.toggle_states.get('inpaint_auto_area_large_thresh', 0.08)) + area_small_thr = float(self.toggle_states.get('inpaint_auto_area_small_thresh', 0.03)) + dist_thr = float(self.toggle_states.get('inpaint_auto_min_distance_ratio', 0.10)) + + choose = 'request' # 기본: lama(서버) + if analysis['coverage_ratio'] <= area_small_thr and \ + analysis['component_count'] >= 1 and \ + analysis['min_centroid_distance_ratio'] >= dist_thr: + choose = 'migan' + elif analysis['coverage_ratio'] >= area_large_thr: + choose = 'request' + else: + # 중간대: lama 우선 (품질 우선) + choose = 'request' + + self.logger.log( + f"[AUTO Inpaint] coverage={analysis['coverage_ratio']:.3f}, comps={analysis['component_count']}, " + f"min_center_dist={analysis['min_centroid_distance_ratio']:.3f} → {choose}", + level=logging.INFO, + ) + self.inpaint_method = choose + except Exception as auto_err: + self.logger.log(f"AUTO 인페인트 선택 실패: {auto_err}", level=logging.WARNING) + + # if not self.is_frozen(): + # # 디버깅 이미지 저장 (OCR 박스 + 마스크 시각화) + # self.save_debug_images(local_image_path, filter_ocr_results, masks, index, file_prefix) + + self.logger.log(f"ocr_count: {ocr_count}", level=logging.DEBUG) + self.logger.log(f"is_member_valid: {self.is_member_valid}", level=logging.DEBUG) + + # 인페인팅 방법 설정 + # self.set_inpaint_method(file_prefix) + # self.logger.log(f"최종 inpaint_method: {self.inpaint_method}", level=logging.DEBUG) + self.inpaint_method = 'migan' + + # 인페인팅 실행 (폴백 순서: 자체서버 > GPU > CPU) + _t = _time.time() + inpainted_image = self.execute_inpaint_with_fallback(local_image_path, masks, ocr_count) + _timings_ms["inpaint"] = (_time.time() - _t) * 1000.0 + self.logger.log(f"인페인팅 완료", level=logging.DEBUG) + # # 개발환경에서 인페인트 결과 디버깅 저장 + # if not self.is_frozen(): + # try: + # self.save_inpaint_debug_image(inpainted_image, index, file_prefix) + # except Exception: + # pass + + + # 인페인팅 실패 시 원본 이미지 사용 + if inpainted_image is None: + self.logger.log(f"인페인팅 실패, 원본 이미지 사용", level=logging.WARNING) + inpainted_image = cv2.imread(local_image_path) + if inpainted_image is None: + self.logger.log(f"원본 이미지 로드 실패: {local_image_path}", level=logging.ERROR) + return {'status': 'failed', 'path': local_image_path, 'error': '원본 이미지 로드 실패', 'inpaint_method': self._last_inpaint_used, 'inpaint_device': self._last_inpaint_device} + + # 텍스트 렌더링 + _t = _time.time() + text_rendered_image = self.text_rendering_module.render_text( + inpainted_image, filter_ocr_results, filtered_translated_texts) + _timings_ms["render"] = (_time.time() - _t) * 1000.0 + self.logger.log(f"텍스트 렌더링 완료", level=logging.DEBUG) + + # 결과 저장 + _t = _time.time() + translated_img_path = await self.postProcess_and_save_image(local_image_path, text_rendered_image, index, file_prefix) + _timings_ms["save"] = (_time.time() - _t) * 1000.0 + self.logger.log(f"이미지 {index+1} 번역 완료: {translated_img_path}", level=logging.DEBUG) + + # GPU 메모리 사용량 로깅 (CUDA 사용 시) + if self.gpu_manager and self.gpu_manager.can_use_cuda: + self.gpu_manager.log_gpu_memory_usage() + + return {'status': 'translated', 'path': translated_img_path, 'inpaint_method': self._last_inpaint_used, 'inpaint_device': self._last_inpaint_device} + + except Exception as e: + self.logger.log(f"이미지 {index+1} 처리 중 오류: {e}", level=logging.ERROR, exc_info=True) + return {'status': 'failed', 'path': local_image_path or original_image_url, 'error': str(e), 'inpaint_method': self._last_inpaint_used, 'inpaint_device': self._last_inpaint_device} + + finally: + # ---- 메모리 해제 ---- + try: + ocr_results = None + filter_ocr_results = None + translated_texts = None + masks = None + inpainted_image = None + text_rendered_image = None + except Exception: + pass + + # download_image 단계에서 사용한 page, local_image_path 도 참조 제거 + local_image_path = None + + # 최종 GC 강제 실행 + gc.collect() + + # 파이프라인 시간 요약 로그 + try: + _t_all_end = _time.time() + total_ms = (_t_all_end - _t_all_start) * 1000.0 + parts = [] + label_map = { + "download": "download", + "ocr": "ocr", + "translate": "translate", + "mask": "mask", + "inpaint": "inpaint", + "render": "render", + "save": "save", + } + for k in ["download", "ocr", "translate", "mask", "inpaint", "render", "save"]: + if k in _timings_ms: + if k == "inpaint" and getattr(self, "_last_inpaint_used", None): + used = self._last_inpaint_used + dev = getattr(self, "_last_inpaint_device", None) or "CPU" + parts.append(f"{label_map[k]}={_timings_ms[k]:.1f}ms({used}/{dev})") + else: + parts.append(f"{label_map[k]}={_timings_ms[k]:.1f}ms") + timeline = " | ".join(parts) if parts else "" + self.logger.log( + f"⏱ 이미지 파이프라인 총 {total_ms:.1f}ms{(' | ' + timeline) if timeline else ''}", + level=logging.DEBUG, + ) + # 결과 timings를 리턴 데이터에 포함시키기 위해 attrs에 저장(워커가 그대로 전달) + self._last_timings = {"total_ms": total_ms, **{k: float(f"{v:.1f}") for k, v in _timings_ms.items()}} + except Exception: + pass + + def set_inpaint_method(self, file_prefix: str) -> None: + """인페인팅 방법 설정 (CPU=cv, GPU=migan, 자체서버=request, 기타=cv)""" + # file_prefix → toggle_states 키 매핑 + key_by_prefix = { + "thumb": "thumb_trans_type", + "detail": "detail_IMGTrans_type", + "option": "optionIMGTrans_type", # 수정: option_IMGTrans_type → optionIMGTrans_type + } + + # 해당 키가 없으면 기본 'CPU' + target_key = key_by_prefix.get(file_prefix, "") + trans_type = self.toggle_states.get(target_key, "CPU") + + # 변환 타입 → 실제 메서드 매핑 + method_map = { + "CPU": "cv", # CPU 선택 시 OpenCV 인페인팅 + "GPU": "migan", # GPU 선택 시 MIGAN 인페인팅 + "자체서버": "request", # 자체서버 선택 시 Request 인페인팅 + } + + self.inpaint_method = method_map.get(trans_type, "cv") # 기타는 cv로 폴백 + + self.logger.log(f"[set_inpaint_method] prefix={file_prefix}, target_key={target_key}, trans_type={trans_type} → inpaint_method={self.inpaint_method}", level=logging.DEBUG) + + def execute_inpaint_with_fallback(self, local_image_path: str, masks, ocr_count: int): + """ + 인페인팅 실행 – MIGAN으로 통일. 실패 시 None 반환. + + Args: + local_image_path: 이미지 파일 경로 + masks: 마스크 데이터 + ocr_count: OCR 결과 개수 + + Returns: + 인페인팅된 이미지 또는 None + """ + # 메모리 추적: 인페인팅 시작 전 + inpaint_before_mem = psutil.virtual_memory() + inpaint_before_mb = inpaint_before_mem.used / 1024 / 1024 + + # MIGAN 고정 + self.inpaint_method = 'migan' + inpainted_image = self._try_migan_inpaint(local_image_path, masks) + + # 메모리 추적: 인페인팅 완료 후 + inpaint_after_mem = psutil.virtual_memory() + inpaint_after_mb = inpaint_after_mem.used / 1024 / 1024 + inpaint_change_mb = inpaint_after_mb - inpaint_before_mb + inpaint_change_percent = (inpaint_change_mb / inpaint_before_mb) * 100 if inpaint_before_mb > 0 else 0 + self.logger.log( + f"메모리 변화 [인페인팅]: {inpaint_before_mb:.1f}MB -> {inpaint_after_mb:.1f}MB " + f"({inpaint_change_mb:+.1f}MB, {inpaint_change_percent:+.1f}%) - 방법: {self.inpaint_method}", + level=logging.DEBUG if abs(inpaint_change_mb) < 10 else logging.INFO + ) + + return inpainted_image + + def _try_request_inpaint(self, local_image_path: str, masks, ocr_count: int): + """자체서버 인페인팅 시도""" + try: + if not self.is_member_valid: + self.logger.log("멤버십이 유효하지 않아 자체서버 인페인팅 건너뜀", level=logging.DEBUG) + return None + + if ocr_count <= 3: + self.logger.log(f"OCR 결과가 적어 자체서버 인페인팅 건너뜀 (ocr_count: {ocr_count})", level=logging.DEBUG) + return None + + self.logger.log(f"자체서버 인페인팅 시도", level=logging.DEBUG) + # 서버 마스크 규약: 정상(반전 불필요)로 확정됨 + # invert_mask = bool(self.toggle_states.get("inpaint_mask_invert", False)) + invert_mask = False + # inpaint_model = self.toggle_states.get("inpaint_model", "simple-lama") + inpaint_model = "migan" + inpaint_model = "simple-lama" + result = self.request_ai_server.request_inpaint(local_image_path, masks, invert_mask=invert_mask, inpaint_model=inpaint_model) + if result is not None: + self.logger.log("자체서버 인페인팅 성공", level=logging.DEBUG) + self._last_inpaint_used = "request" + self._last_inpaint_device = "SERVER" + return result + else: + self.logger.log("자체서버 인페인팅 실패", level=logging.WARNING) + return None + + except Exception as e: + self.logger.log(f"자체서버 인페인팅 중 오류: {e}", level=logging.WARNING, exc_info=True) + return None + + + + def _try_migan_inpaint(self, local_image_path: str, masks): + """MIGAN GPU 인페인팅 시도""" + try: + if getattr(self, "migan", None) is None: + self.logger.log("MIGAN 모듈이 초기화되지 않아 건너뜀", level=logging.DEBUG) + return None + + self.logger.log("MIGAN 인페인팅 시도", level=logging.DEBUG) + result = self.migan.inpaint(local_image_path, masks) + if result is not None: + self.logger.log("MIGAN 인페인팅 성공", level=logging.DEBUG) + # 사용 장치 기록 + try: + providers = [] + if hasattr(self.migan, "session") and hasattr(self.migan.session, "get_providers"): + providers = self.migan.session.get_providers() + if any("Dml" in p for p in providers): + dev = "DirectML" + elif any("CUDA" in p for p in providers): + dev = "CUDA" + else: + dev = "CPU" + except Exception: + dev = "GPU" if (hasattr(self, "gpu_manager") and self.gpu_manager and getattr(self.gpu_manager, "can_use_cuda", False)) else "CPU" + self._last_inpaint_used = "migan" + self._last_inpaint_device = dev + return result + else: + self.logger.log("MIGAN 인페인팅 실패", level=logging.WARNING) + return None + + except Exception as e: + self.logger.log(f"MIGAN 인페인팅 중 오류: {e}", level=logging.WARNING, exc_info=True) + return None + + def _try_opencv_inpaint(self, local_image_path: str, masks): + """MIGAN 통일 이후 비활성화(호환용). 항상 None 반환""" + self.logger.log("OpenCV 인페인팅 경로는 비활성화됨(MIGAN 통일)", level=logging.DEBUG) + return None + + # OCR 결과 필터링: 중국어 텍스트만 필터링 + def filter_ocr_results(self, ocr_results): + import re + + # 중국어 문자 범위 정규식 (한자) + chinese_pattern = re.compile(r'[\u4e00-\u9fff]+') + + filtered_results = [] + for r in ocr_results: + text = r.get('text', '').strip() + polygon = r.get('polygon', []) + confidence = r.get('confidence', 0.0) + + # 텍스트가 비어있거나 polygon이 3점 미만이면 제외 + if not text or not polygon or len(polygon) < 3: + self.logger.log(f"[필터링] 제외 (텍스트/폴리곤): '{text}'", level=logging.DEBUG) + continue + + # 신뢰도 20% 미만이면 제외 + if confidence < 0.05: + self.logger.log(f"[필터링] 제외 (신뢰도 {confidence:.1%}): '{text}'", level=logging.DEBUG) + continue + + # 중국어 문자가 포함된 텍스트만 필터링 + if chinese_pattern.search(text): + filtered_results.append(r) + self.logger.log(f"[필터링] 포함 (신뢰도 {confidence:.1%}): '{text}'", level=logging.DEBUG) + else: + self.logger.log(f"[필터링] 제외 (중국어 없음): '{text}'", level=logging.DEBUG) + + self.logger.log(f"필터링 결과: {len(filtered_results)}/{len(ocr_results)}개 (신뢰도 + & 중국어)", level=logging.DEBUG) + return filtered_results + + async def postProcess_and_save_image(self, local_image_path, text_rendered_image, index, file_prefix=""): + """로컬 서버 URL을 사용해 이미지를 번역하고 로컬에 저장합니다""" + try: + # text_rendered_image가 None인 경우 처리 + if text_rendered_image is None: + self.logger.log(f"이미지 {index+1} 번역 결과가 None입니다. 원본 이미지를 반환합니다.", level=logging.WARNING) + return local_image_path + + # 파일명에 접두사 포함 (설정에 따른 이미지 형식 선택) + # 기본값: WebP (최고 압축률), 호환성 필요시 PNG 사용 가능 + image_format = self.toggle_states.get('output_image_format', 'webp').lower() + + # WebP 지원 여부 확인 (PIL에서 WebP를 지원하지 않는 경우 PNG로 폴백) + if image_format == 'webp': + try: + if not features.check('webp'): + self.logger.log("WebP 지원되지 않음, PNG로 폴백", level=logging.WARNING) + image_format = 'png' + except Exception: + image_format = 'png' + + # 지원되지 않는 형식인 경우 PNG로 폴백 + if image_format not in ['webp', 'png', 'jpg', 'jpeg']: + self.logger.log(f"지원되지 않는 형식 {image_format}, PNG로 폴백", level=logging.WARNING) + image_format = 'png' + + file_ext = 'webp' if image_format == 'webp' else ('png' if image_format == 'png' else 'jpg') + + if file_prefix: + img_path = os.path.join(self.TEMP_IMAGE_DIR, f"translated_{file_prefix}_img_{index+1}.{file_ext}") + else: + img_path = os.path.join(self.TEMP_IMAGE_DIR, f"translated_img_{index+1}.{file_ext}") + + watermark_text=self.toggle_states.get("watermark_text", "이미지 저작권 보유") + is_watermark_enabled = watermark_text != "" or self.toggle_states.get("watermark_toggle", False) + + self.logger.log(f"watermark_text: {watermark_text}", level=logging.DEBUG) + self.logger.log(f"is_watermark_enabled: {is_watermark_enabled}", level=logging.DEBUG) + + image_data_to_save = None + + # file_prefix가 'detail' 또는 'option'일 때만 워터마크 추가 + if is_watermark_enabled and file_prefix in ["detail"]: + # image_data_to_save = self.postImageManager.add_watermark( + # image_data=text_rendered_image, + # watermark_text=watermark_text + # ) + # 워터마크 기능 비활성화 중이므로 원본 이미지 사용 + if isinstance(text_rendered_image, np.ndarray): + image_data_to_save = text_rendered_image # 그대로 넘김 + else: + image_data_to_save = text_rendered_image + else: + # # np.ndarray라면 PIL.Image로 변환 + # if isinstance(text_rendered_image, np.ndarray): + # image_data_to_save = Image.fromarray(cv2.cvtColor(text_rendered_image, cv2.COLOR_BGR2RGB)) + # else: + # image_data_to_save = text_rendered_image + + if isinstance(text_rendered_image, np.ndarray): + image_data_to_save = text_rendered_image # 그대로 넘김 + else: + image_data_to_save = text_rendered_image + + final_image_path = self.postImageManager.save_image_to_path(image_data_to_save, img_path) + + # save_image_to_path가 None을 반환한 경우 처리 + if final_image_path is None: + self.logger.log(f"이미지 {index+1} 저장 실패. 원본 이미지를 반환합니다.", level=logging.WARNING) + return local_image_path + + return final_image_path + + except Exception as e: + self.logger.log(f"이미지 {index+1} 번역 처리 중 오류: {e}", level=logging.ERROR, exc_info=True) + return local_image_path + + + def download_image(self, image_url, index, file_prefix="", max_retries=3): + """Requests를 사용해 이미지를 다운로드합니다""" + + # 로컬 파일 경로면 바로 반환 + if os.path.isfile(image_url): + self.logger.log(f"로컬 파일 경로 감지, 다운로드 생략: {image_url}", level=logging.DEBUG) + return image_url + + # 로컬 파일 경로가 아니면 다운로드 시도 + try: + # "https://assets.alicdn.com"으로 시작하는 URL은 건너뛰기 + if image_url.startswith("https://assets.alicdn.com") or image_url.startswith("https://gtms01.alicdn.com"): + self.logger.log(f"다운로드 제외 URL: {image_url}", level=logging.DEBUG) + return None + + # URL에서 파일명 추출 및 접두사 포함 + parsed_url = urlparse(image_url) + base_filename = f"image_{index:03d}_{os.path.basename(parsed_url.path)}" + if not base_filename.endswith(('.jpg', '.jpeg', '.png', '.gif', '.webp')): + base_filename += '.jpg' + + # 접두사가 있으면 파일명에 포함 + if file_prefix: + filename = f"{file_prefix}_{base_filename}" + else: + filename = base_filename + + local_path = os.path.join(self.TEMP_IMAGE_DIR, filename) + + # HTTP 헤더 설정 + headers = { + "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.150 Safari/537.36", + "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9", + "Accept-Language": "en-US,en;q=0.9", + "Accept-Encoding": "gzip, deflate, br", + "DNT": "1", # Do Not Track 요청 헤더 + "Connection": "keep-alive", + "Upgrade-Insecure-Requests": "1", + "Cache-Control": "max-age=0" + } + + retries = 0 + while retries < max_retries: + try: + # 메모리 추적: 다운로드 시작 전 + before_mem = psutil.virtual_memory() + before_mb = before_mem.used / 1024 / 1024 + + self.logger.log(f"이미지 다운로드 중: {filename}", level=logging.DEBUG) + response = requests.get(image_url, headers=headers, stream=True, timeout=30) + + if response.status_code == 200: + image_data = response.content + + # 이미지 데이터 유효성 검사 + if self.is_valid_image_data(image_data): + with open(local_path, 'wb') as f: + f.write(image_data) + + # 메모리 추적: 다운로드 완료 후 + after_mem = psutil.virtual_memory() + after_mb = after_mem.used / 1024 / 1024 + change_mb = after_mb - before_mb + change_percent = (change_mb / before_mb) * 100 if before_mb > 0 else 0 + self.logger.log( + f"메모리 변화 [다운로드 완료]: {before_mb:.1f}MB -> {after_mb:.1f}MB " + f"({change_mb:+.1f}MB, {change_percent:+.1f}%) - {filename}", + level=logging.DEBUG if abs(change_mb) < 10 else logging.INFO + ) + + self.logger.log(f"이미지 다운로드 완료: {filename}", level=logging.DEBUG) + return local_path + else: + self.logger.log(f"유효하지 않은 이미지 데이터: {image_url}", level=logging.WARNING) + return None + else: + self.logger.log(f"이미지 다운로드 실패 (HTTP {response.status_code}): {image_url}. 재시도 {retries + 1}/{max_retries}", level=logging.ERROR) + retries += 1 + if retries < max_retries: + time.sleep(random.randint(2, 5)) # 2~5초 대기 후 재시도 + + except requests.exceptions.RequestException as e: + self.logger.log(f"이미지 다운로드 중 네트워크 오류: {e}. 재시도 {retries + 1}/{max_retries}", level=logging.ERROR) + retries += 1 + if retries < max_retries: + time.sleep(random.randint(2, 5)) # 예외 발생 시 대기 후 재시도 + + except Exception as e: + self.logger.log(f"이미지 다운로드 중 예상치 못한 오류: {e}. 재시도 {retries + 1}/{max_retries}", level=logging.ERROR) + retries += 1 + if retries < max_retries: + time.sleep(random.randint(2, 5)) + + self.logger.log(f"이미지 다운로드 최대 재시도 횟수를 초과했습니다: {image_url}", level=logging.ERROR) + return None + + except Exception as e: + self.logger.log(f"이미지 다운로드 중 오류: {e}", level=logging.ERROR, exc_info=True) + return None + + + def is_valid_image_data(self, image_data): + """이미지 데이터가 유효한지 확인합니다""" + if not image_data or len(image_data) < 100: + return False + + # 일반적인 이미지 파일 시그니처 확인 + image_signatures = [ + b'\xFF\xD8\xFF', # JPEG + b'\x89PNG\r\n\x1a\n', # PNG + b'GIF87a', # GIF87a + b'GIF89a', # GIF89a + b'RIFF', # WebP (RIFF 컨테이너) + ] + + return any(image_data.startswith(sig) for sig in image_signatures) + + + def process_translated_texts(self, translated_texts, local_image_path, index): + """ + 번역된 단어 리스트(translated_texts)에서 unwanted_texts의 원본값이 + 앞이나 뒤에 포함되면 치환값으로 바꿉니다. + 치환값이 '이미지삭제'라면 None 반환(이미지 제외) + """ + new_texts = [] + for i, text in enumerate(translated_texts): + self.logger.log(f"[치환 처리 {i+1}] 원본 텍스트: '{text}'", level=logging.DEBUG) + + # 텍스트를 빈칸으로 분리 + words = text.split() + self.logger.log(f"[치환 처리 {i+1}] 분리된 단어: {words}", level=logging.DEBUG) + + processed_words = [] + text_replaced = False + + for word_idx, word in enumerate(words): + word_replaced = False + + # unwanted_texts와 매칭 확인 + for origin, replace in self.unwanted_texts.items(): + if word.startswith(origin) or word.endswith(origin) or word == origin: + self.logger.log(f"[치환 처리 {i+1}] 단어 '{word}' -> '{replace}' (원본: '{origin}')", level=logging.DEBUG) + + if replace == "이미지삭제": + self.logger.log(f"이미지 {index+1} 제외됨: {local_image_path}", level=logging.DEBUG) + return None + + # 단어 치환 + if word == origin: + new_word = replace + elif word.startswith(origin): + new_word = replace + word[len(origin):] + elif word.endswith(origin): + new_word = word[:-len(origin)] + replace + + processed_words.append(new_word) + word_replaced = True + text_replaced = True + self.logger.log(f"[치환 처리 {i+1}] 단어 치환 완료: '{word}' -> '{new_word}'", level=logging.DEBUG) + break + + if not word_replaced: + processed_words.append(word) + + # 처리된 단어들을 다시 합치기 + final_text = ' '.join(processed_words) + new_texts.append(final_text) + + if text_replaced: + self.logger.log(f"[치환 처리 {i+1}] 최종 결과: '{text}' -> '{final_text}'", level=logging.DEBUG) + else: + self.logger.log(f"[치환 처리 {i+1}] 변경 없음: '{text}'", level=logging.DEBUG) + + self.logger.log(f"전체 치환 결과: {len(new_texts)}개 텍스트 처리 완료", level=logging.DEBUG) + for i, (original, processed) in enumerate(zip(translated_texts, new_texts)): + if original != processed: + self.logger.log(f"[최종 치환 {i+1}] '{original}' -> '{processed}'", level=logging.DEBUG) + + return new_texts + + async def batch_papago_translate_texts(self, ocr_results, delimiter='\n'): + """ + ocr_results에서 추출한 텍스트 리스트를 줄바꿈으로 합쳐 한 번에 파파고로 번역 후, 다시 분리하여 반환합니다. + 각 텍스트 내에 여러 구분자(/, |, ·, , 등)가 있을 경우에도 분리하여 번역 후 다시 합칩니다. + """ + import re + texts = [result['text'] for result in ocr_results if result['text'].strip()] + if not texts: + return [] + + # 각 텍스트를 내부적으로 split (/, |, ·, , 등) 후 다시 합침 + split_texts = [] + split_indices = [] # 각 텍스트가 몇 개의 파트로 쪼개졌는지 기록 + for text in texts: + parts = re.split(r'\s*[/|·,;、]+\s*', text) + parts = [p.strip() for p in parts if p.strip()] + split_texts.extend(parts) + split_indices.append(len(parts)) + + # Papago에 한 번에 번역 요청 + joined = delimiter.join(split_texts) + + # 메모리 추적: 번역 시작 전 + trans_before_mem = psutil.virtual_memory() + trans_before_mb = trans_before_mem.used / 1024 / 1024 + + try: + # 이미 한 번에 여러 줄을 받아서 리스트로 반환! + translated_lines = await self.papago_translator.translate(joined, source_lang="zh", target_lang="ko") + # Papago가 리스트로 반환한다고 가정! + results = translated_lines + + # 개수가 다르면 fallback(선택사항, 아래는 로깅만) + if len(results) != len(split_texts): + self.logger.log( + f"파파고 번역 줄 개수 불일치: {len(results)} != {len(split_texts)}", + level=logging.WARNING + ) + # 여기서 fallback 처리 또는 그냥 results를 그대로 사용 + + # 다시 원래 텍스트 단위로 합치기 + final_results = [] + idx = 0 + for count in split_indices: + parts = results[idx:idx+count] + final_results.append(' / '.join(parts)) + idx += count + + # 메모리 추적: 번역 완료 후 + trans_after_mem = psutil.virtual_memory() + trans_after_mb = trans_after_mem.used / 1024 / 1024 + trans_change_mb = trans_after_mb - trans_before_mb + trans_change_percent = (trans_change_mb / trans_before_mb) * 100 if trans_before_mb > 0 else 0 + self.logger.log( + f"메모리 변화 [Papago 번역]: {trans_before_mb:.1f}MB -> {trans_after_mb:.1f}MB " + f"({trans_change_mb:+.1f}MB, {trans_change_percent:+.1f}%) - {len(split_texts)}개 텍스트", + level=logging.DEBUG if abs(trans_change_mb) < 10 else logging.INFO + ) + + return final_results + + except Exception as e: + self.logger.log(f"파파고 번역 실패: {e}", level=logging.ERROR, exc_info=True) + return texts + + def batch_google_translate_texts(self, ocr_results, delimiter='\n'): + """ + ocr_results에서 추출한 텍스트 리스트를 줄바꿈으로 합쳐 한 번에 구글 번역기로 번역 후, 다시 분리하여 반환합니다. + 각 텍스트 내에 여러 구분자(/, |, ·, , 등)가 있을 경우에도 분리하여 번역 후 다시 합칩니다. + """ + import re + texts = [result['text'] for result in ocr_results if result['text'].strip()] + if not texts: + return [] + # 각 텍스트를 내부적으로 split (/, |, ·, , 등) 후 다시 합침 + split_texts = [] + split_indices = [] # 각 텍스트가 몇 개의 파트로 쪼개졌는지 기록 + for text in texts: + parts = re.split(r'\s*[/|·,;、]+\s*', text) + parts = [p.strip() for p in parts if p.strip()] + split_texts.extend(parts) + split_indices.append(len(parts)) + # 합쳐서 한 번에 번역 + joined = delimiter.join(split_texts) + try: + translated_obj = self.gtranslate.translate(joined, "Korean") + translated_text = getattr(translated_obj, "text", getattr(translated_obj, "result", joined)) + results = translated_text.split(delimiter) + # 만약 개수가 다르면 fallback + if len(results) != len(split_texts): + results = [getattr(self.gtranslate.translate(t, "Korean"), "text", t) for t in split_texts] + # 다시 원래 텍스트 단위로 합치기 + final_results = [] + idx = 0 + for count in split_indices: + parts = results[idx:idx+count] + final_results.append(' / '.join(parts)) + idx += count + return final_results + except Exception as e: + self.logger.log(f"구글 번역 실패: {e}", level=logging.ERROR, exc_info=True) + return texts + + + def opencv_inpaint(self, image_path, mask, method='telea', radius=3): + """MIGAN 통일 이후 비활성화(호환용). 항상 None 반환""" + return None + + def lama_inpaint(self, image_path, mask): + """ + PaddleHub lama 모델로 인페인팅을 수행합니다. + Args: + image_path (str): 원본 이미지 경로 + mask (np.ndarray): 2D 마스크 이미지 (0/255, shape=(H, W)) + Returns: + inpainted_image (np.ndarray): 인페인팅된 이미지 (BGR) + """ + try: + import paddlehub as hub + except ImportError: + self.logger.log("paddlehub 미설치: pip install paddlehub", level=logging.ERROR) + return None + + image = cv2.imread(image_path) + if image is None: + self.logger.log(f"이미지 로드 실패: {image_path}", level=logging.ERROR) + return None + + # 마스크 2D 체크 + if mask is None: + self.logger.log(f"마스크가 None입니다", level=logging.ERROR) + return None + + if not isinstance(mask, np.ndarray) or mask.ndim != 2: + self.logger.log(f"마스크가 2D numpy 배열이 아닙니다: type={type(mask)}, shape={getattr(mask, 'shape', 'N/A')}", level=logging.ERROR) + return None + + if mask.shape != image.shape[:2]: + self.logger.log(f"마스크 크기가 이미지와 다릅니다: mask={mask.shape}, image={image.shape[:2]}", level=logging.ERROR) + return None + + try: + # PaddleHub lama 인페인팅 모델 로드 (최초 1회 다운로드) + if not hasattr(self, "_lama_module"): + self._lama_module = hub.Module(name="lama") + if self.logger: + self.logger.log("lama 인페인팅 모델 로드됨") + + # 라마 모델 인페인팅 (마스크 0/255, uint8) + result = self._lama_module.predict(images=[image], masks=[mask]) + if not result or "inpainted" not in result[0]: + self.logger.log("lama 인페인팅 결과 없음", level=logging.ERROR) + return None + + inpainted = result[0]["inpainted"] # np.ndarray(BGR) + return inpainted + + except Exception as e: + self.logger.log(f"lama 인페인팅 중 예외 발생: {e}", level=logging.ERROR, exc_info=True) + return None + + + def save_debug_images(self, local_image_path, ocr_results, masks, index, file_prefix=""): + """디버깅용 OCR 박스와 마스크 이미지를 저장합니다""" + try: + # OCR 박스 시각화 이미지 저장 + ocr_debug_path = self.save_ocr_debug_image(local_image_path, ocr_results, index, file_prefix) + + # 마스크 시각화 이미지 저장 + mask_debug_path = self.save_mask_debug_image(local_image_path, masks, index, file_prefix) + + self.logger.log(f"디버깅 이미지 저장 완료: OCR={ocr_debug_path}, Mask={mask_debug_path}", level=logging.DEBUG) + return ocr_debug_path, mask_debug_path + + except Exception as e: + self.logger.log(f"디버깅 이미지 저장 중 오류: {e}", level=logging.ERROR, exc_info=True) + return None, None + + def save_ocr_debug_image(self, image_path, ocr_results, index, file_prefix=""): + """OCR 감지된 박스들을 이미지에 표시하여 저장합니다""" + try: + # 원본 이미지 로드 + image = cv2.imread(image_path) + if image is None: + self.logger.log(f"OCR 디버깅용 이미지 로드 실패: {image_path}", level=logging.ERROR) + return None + + # 이미지 복사본 생성 + debug_image = image.copy() + + # OCR 결과별로 박스 그리기 + for i, result in enumerate(ocr_results): + polygon = result.get('polygon', []) + bbox = result.get('bbox', None) + text = result.get('text', '') + confidence = result.get('confidence', 0.0) + + # 신뢰도에 따른 색상 결정 + if confidence >= 0.8: + color = (0, 255, 0) # 초록 (높은 신뢰도) + elif confidence >= 0.5: + color = (0, 255, 255) # 노랑 (중간 신뢰도) + elif confidence >= 0.2: + color = (0, 165, 255) # 주황 (낮은 신뢰도) + else: + color = (0, 0, 255) # 빨강 (매우 낮은 신뢰도) + + if polygon and len(polygon) >= 3: + # 폴리곤을 numpy 배열로 변환 (좌표를 int로 변환) + pts = np.array([[int(x), int(y)] for x, y in polygon], np.int32) + pts = pts.reshape((-1, 1, 2)) + cv2.polylines(debug_image, [pts], True, color, 2) + x, y = pts[0][0] + elif bbox and len(bbox) == 4: + try: + x, y, w, h = [int(float(v)) for v in bbox] + except Exception as e: + self.logger.log(f"bbox 값 변환 오류: {bbox} ({e})", level=logging.ERROR) + continue + cv2.rectangle(debug_image, (x, y), (x + w, y + h), color, 2) + else: + continue # polygon, bbox 둘 다 없으면 skip + + label = f"{i+1}: {text[:10]}... ({confidence:.1%})" + # 텍스트 배경 사각형 + (text_width, text_height), _ = cv2.getTextSize(label, cv2.FONT_HERSHEY_SIMPLEX, 0.5, 1) + cv2.rectangle(debug_image, (x, y-text_height-5), (x+text_width, y), color, -1) + # 텍스트 표시 + cv2.putText(debug_image, label, (x, y-5), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 255, 255), 1) + + # 범례 추가 + legend_y = 30 + cv2.putText(debug_image, "OCR Detection Results:", (10, legend_y), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (255, 255, 255), 2) + cv2.putText(debug_image, "Green: 80%+ Yellow: 50-80% Orange: 20-50% Red: <20%", (10, legend_y+25), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 255, 255), 1) + + # 파일 저장 + if file_prefix: + debug_filename = f"debug_ocr_{file_prefix}_img_{index+1}.png" + else: + debug_filename = f"debug_ocr_img_{index+1}.png" + + debug_path = os.path.join(self.debugging_save_Dir, debug_filename) + cv2.imwrite(debug_path, debug_image) + + self.logger.log(f"OCR 디버깅 이미지 저장: {debug_filename}", level=logging.DEBUG) + return debug_path + + except Exception as e: + self.logger.log(f"OCR 디버깅 이미지 저장 중 오류: {e}", level=logging.ERROR, exc_info=True) + return None + + def save_mask_debug_image(self, image_path, masks, index, file_prefix=""): + """생성된 마스크를 이미지에 오버레이하여 저장합니다""" + try: + + # 원본 이미지 로드 + image = cv2.imread(image_path) + if image is None: + self.logger.log(f"마스크 디버깅용 이미지 로드 실패: {image_path}", level=logging.ERROR) + return None + + if masks is None or not isinstance(masks, np.ndarray): + self.logger.log(f"유효하지 않은 마스크: {type(masks)}", level=logging.ERROR) + return None + + # 이미지 복사본 생성 + debug_image = image.copy() + + # 마스크 영역을 빨간색으로 오버레이 + mask_colored = np.zeros_like(image) + mask_colored[:, :, 2] = masks # 빨간색 채널에 마스크 적용 + + # 마스크 영역 반투명 오버레이 (알파 블렌딩) + alpha = 0.3 + overlay_mask = masks > 0 + debug_image[overlay_mask] = cv2.addWeighted( + debug_image[overlay_mask], 1-alpha, + mask_colored[overlay_mask], alpha, 0 + ) + + # 마스크 통계 정보 표시 + total_pixels = masks.shape[0] * masks.shape[1] + mask_pixels = np.sum(masks > 0) + mask_percentage = (mask_pixels / total_pixels) * 100 if total_pixels > 0 else 0 + + # 정보 텍스트 표시 + info_text = [ + f"Mask Statistics:", + f"Total pixels: {total_pixels:,}", + f"Mask pixels: {mask_pixels:,}", + f"Coverage: {mask_percentage:.1f}%" + ] + + y_offset = 30 + for i, text in enumerate(info_text): + y_pos = y_offset + (i * 25) + # 텍스트 배경 + (text_width, text_height), _ = cv2.getTextSize(text, cv2.FONT_HERSHEY_SIMPLEX, 0.6, 1) + cv2.rectangle(debug_image, (10, y_pos-text_height-3), (10+text_width+10, y_pos+5), (0, 0, 0), -1) + # 텍스트 + cv2.putText(debug_image, text, (15, y_pos), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (255, 255, 255), 1) + + # 파일 저장 + if file_prefix: + debug_filename = f"debug_mask_{file_prefix}_img_{index+1}.png" + else: + debug_filename = f"debug_mask_img_{index+1}.png" + + debug_path = os.path.join(self.debugging_save_Dir, debug_filename) + cv2.imwrite(debug_path, debug_image) + + self.logger.log(f"마스크 디버깅 이미지 저장: {debug_filename} (마스크 커버리지: {mask_percentage:.1f}%)", level=logging.DEBUG) + return debug_path + + except Exception as e: + self.logger.log(f"마스크 디버깅 이미지 저장 중 오류: {e}", level=logging.ERROR, exc_info=True) + return None + + def save_inpaint_debug_image(self, inpainted_image, index, file_prefix=""): + """인페인팅 결과 이미지를 디버그용으로 저장합니다""" + try: + # import cv2 + # import numpy as np + # import os + + if inpainted_image is None: + self.logger.log("인페인트 결과가 None이어서 디버그 저장을 건너뜁니다", level=logging.WARNING) + return None + + if file_prefix: + debug_filename = f"debug_inpaint_{file_prefix}_img_{index+1}.png" + else: + debug_filename = f"debug_inpaint_img_{index+1}.png" + + debug_path = os.path.join(self.debugging_save_Dir, debug_filename) + + # PIL -> ndarray 변환 + if not isinstance(inpainted_image, np.ndarray): + try: + from PIL import Image as _Image + if isinstance(inpainted_image, _Image.Image): + inpainted_image = cv2.cvtColor(np.array(inpainted_image.convert("RGB")), cv2.COLOR_RGB2BGR) + else: + self.logger.log(f"지원하지 않는 인페인트 이미지 타입: {type(inpainted_image)}", level=logging.WARNING) + return None + except Exception: + return None + + cv2.imwrite(debug_path, inpainted_image) + self.logger.log(f"인페인트 디버그 이미지 저장: {debug_filename}", level=logging.DEBUG) + return debug_path + except Exception as e: + self.logger.log(f"인페인트 디버그 이미지 저장 중 오류: {e}", level=logging.ERROR, exc_info=True) + return None + + def _analyze_mask_stats(self, masks: np.ndarray) -> dict: + """마스크의 커버리지/컴포넌트/최소 중심거리 비율을 계산합니다""" + import numpy as _np + import cv2 as _cv2 + + h, w = masks.shape[:2] + total = max(1, h * w) + mask_bin = masks + if masks.ndim == 3: + mask_bin = _cv2.cvtColor(masks, _cv2.COLOR_BGR2GRAY) + if mask_bin.dtype != _np.uint8: + mask_bin = mask_bin.astype(_np.uint8) + _, mask_bin = _cv2.threshold(mask_bin, 0, 255, _cv2.THRESH_BINARY) + + coverage_ratio = float((_np.sum(mask_bin > 0)) / total) + + # 컴포넌트 분석 (외곽선 기반) + try: + contours, _ = _cv2.findContours(mask_bin, _cv2.RETR_EXTERNAL, _cv2.CHAIN_APPROX_SIMPLE) + except ValueError: + # OpenCV 버전 차이 호환 + _, contours, _ = _cv2.findContours(mask_bin, _cv2.RETR_EXTERNAL, _cv2.CHAIN_APPROX_SIMPLE) + + centers = [] + for cnt in contours: + if cnt is None or len(cnt) < 3: + continue + m = _cv2.moments(cnt) + if m['m00'] == 0: + x, y, w_box, h_box = _cv2.boundingRect(cnt) + cx, cy = x + w_box / 2.0, y + h_box / 2.0 + else: + cx, cy = m['m10'] / m['m00'], m['m01'] / m['m00'] + centers.append((float(cx), float(cy))) + + # 최소 중심 거리 (정규화) + min_dist = 0.0 + if len(centers) >= 2: + min_dist = min( + ((_np.hypot(cx1 - cx2, cy1 - cy2)) for (cx1, cy1) in centers for (cx2, cy2) in centers if (cx1, cy1) != (cx2, cy2)) + ) + diag = float(_np.hypot(w, h)) + min_dist_ratio = float(min_dist / diag) if diag > 0 else 0.0 + + return { + 'coverage_ratio': coverage_ratio, + 'component_count': len(centers), + 'min_centroid_distance_ratio': min_dist_ratio, + } + + + + def is_frozen(self): + """ + 실행 환경에 따라 배포환경인지 개발환경인지 확인하는 메서드. + cx_Freeze로 패키징된 경우 실행 파일의 경로, 일반 Python 환경일 경우 __file__을 기준으로 설정. + """ + if getattr(sys, 'frozen', False): # 패키징된 경우 + self.logger.log("배포환경", level=logging.DEBUG) + return True + else: # 일반 Python 실행 환경 + self.logger.log("개발환경", level=logging.DEBUG) + return False + + + def safe_detect(self, img_path): + try: + if not hasattr(self, 'ocr_module') or self.ocr_module is None: + self.logger.log("⚠️ ONNX OCR 모듈이 초기화되지 않았습니다. 재초기화를 시도합니다.", level=logging.WARNING) + # OCR 모듈 재초기화 시도 + try: + if self.reset_ocr_module() and hasattr(self, 'ocr_module') and self.ocr_module is not None: + self.logger.log("✅ ONNX OCR 모듈 재초기화 성공", level=logging.INFO) + else: + self.logger.log("❌ ONNX OCR 모듈 재초기화 실패, 빈 결과 반환", level=logging.ERROR) + return [] + except Exception as reset_error: + self.logger.log(f"❌ ONNX OCR 모듈 재초기화 중 예외 발생: {reset_error}", level=logging.ERROR) + return [] + + result = self.ocr_module.detect_text(img_path) + # 빈 OCR 결과는 정상 케이스로 처리 + if not result or len(result) == 0: + self.logger.log(f"OCR 결과 없음 - 정상 케이스 (NO_TEXT): {img_path}", level=logging.INFO) + return [] # 빈 리스트 반환 (정상 처리) + return result + except Exception as e: + msg = str(e).lower() + # 메모리 / primitive 관련 오류 → OCR 모듈 재초기화 후 1회 재시도 + if any(err in msg for err in ["create a primitive", "memory object", "unable to allocate", "out of memory", "cv::outofmemoryerror"]): + ok = self.reset_ocr_module() + if ok and self.ocr_module is not None: + # 재시도 시 실패하면 MemoryError를 재전파하여 상위에서 워커 재시작 트리거 + return self.ocr_module.detect_text(img_path, raise_on_memory_error=True) + # 그 외 예외는 그대로 상위로 전달 + raise + + async def remove_background(self, original_image_url, file_prefix=""): + """배경제거 전용 메서드 (썸네일 등 외부 호출용). + + 1. 이미지를 다운로드(또는 로컬 경로 사용) + 2. Request_AI_Server.request_rembg 로 배경 제거 (흰 배경 중앙 배치 포함) + 3. TEMP_IMAGE_DIR 하위에 저장 후 경로 반환 + """ + try: + index = 0 # 기본값 (외부에서 필요 시 파일명 구분용) + + # 0. 이미지 URL 유효성 체크 + if not original_image_url or not isinstance(original_image_url, str): + self.logger.log("배경제거 중단: 이미지 URL 없음 또는 타입 오류", level=logging.WARNING) + return {"status": "failed", "path": original_image_url, "error": "이미지 URL 오류"} + + # 1. 다운로드 또는 로컬 경로 확정 + if original_image_url.startswith("http"): + # 다운로드 재사용을 위해 기존 메서드 호출 + local_path = self.download_image(image_url=original_image_url, index=0, file_prefix=file_prefix) + if not local_path: + return {"status": "failed", "path": original_image_url, "error": "다운로드 실패"} + else: + local_path = original_image_url # 이미 로컬 경로 + + # 2. 배경 제거 (np.ndarray 반환) + removed = self.request_ai_server.request_rembg( + local_path, + use_local_rembg=self.use_local_rembg, + local_model_name=self.local_model_name + ) + if removed is None: + self.logger.log("RemoveBG 실패", level=logging.ERROR) + return {"status": "failed", "path": local_path, "error": "RemoveBG 실패"} + + # 3. 저장 경로 결정 + os.makedirs(self.TEMP_IMAGE_DIR, exist_ok=True) + base_name = os.path.basename(local_path) + name_no_ext, _ = os.path.splitext(base_name) + save_name = f"nobg_{file_prefix}_{name_no_ext}.png" if file_prefix else f"nobg_{name_no_ext}.png" + save_path = os.path.join(self.TEMP_IMAGE_DIR, save_name) + + # 4. 저장 (OpenCV → BGR) + cv2.imwrite(save_path, removed) + del removed + removed = None + self.logger.log(f"배경제거 이미지 저장: {save_path}", level=logging.DEBUG) + + # 5. OCR 검사 후 인페인팅 여부 결정 + # ocr_results = self.ocr_module.detect_text(save_path) + ocr_results = self.safe_detect(save_path) + + filter_ocr_results = self.filter_ocr_results(ocr_results) + + # 중국어 텍스트가 없으면 바로 반환 + if self.ocr_module is None or not self.ocr_module.filter_chinese_text(filter_ocr_results): + return {"status": "success", "path": save_path} + + # ---- 중국어 텍스트 존재: 인페인팅 준비 ---- + ocr_count = len(filter_ocr_results) + if ocr_count < 5: + expansion_size, blur_size = 12, 15 + elif ocr_count < 10: + expansion_size, blur_size = 9, 12 + elif ocr_count < 15: + expansion_size, blur_size = 7, 9 + elif ocr_count < 20: + expansion_size, blur_size = 5, 6 + else: + expansion_size, blur_size = 10, 15 + + # 마스크 생성 + masks = self.mask_module.create_masks( + image_path=save_path, + ocr_results=filter_ocr_results, + mask_option="basic", + expansion_size=expansion_size, + blur_size=blur_size + ) + self.logger.log("배경제거 후 마스크 생성 완료", level=logging.DEBUG) + + # 인페인팅 수행: MIGAN으로 통일 + self.inpaint_method = 'migan' + inpainted_image = self._try_migan_inpaint(save_path, masks) + + # 인페인팅 실패 시 원본 반환 + if inpainted_image is None: + self.logger.log("인페인팅 실패, 배경제거 이미지를 그대로 반환", level=logging.WARNING) + return {"status": "success", "path": save_path} + + # 인페인팅 결과 저장 + inpaint_name = f"inpaint_{file_prefix}_{name_no_ext}.png" if file_prefix else f"inpaint_{name_no_ext}.png" + inpaint_path = os.path.join(self.TEMP_IMAGE_DIR, inpaint_name) + cv2.imwrite(inpaint_path, inpainted_image) + self.logger.log(f"인페인팅 이미지 저장: {inpaint_path}", level=logging.DEBUG) + return {"status": "success", "path": inpaint_path} + + except Exception as e: + self.logger.log(f"remove_background 오류: {e}", level=logging.ERROR, exc_info=True) + return {"status": "failed", "path": original_image_url, "error": str(e)} + + + # async def remove_background(self, page, original_image_url, index, file_prefix="", **kwargs): + # """ + # 배경제거: 이미지 반환 → 후처리 및 저장 → 경로 반환 + # """ + # try: + # # 0. 이미지 URL 유효성 체크 (http/https & 이미지 확장자) + # if not original_image_url or not isinstance(original_image_url, str): + # self.logger.log(f"이미지 {index+1} 처리 중단: 이미지 URL 없음 또는 타입 오류", level=logging.WARNING) + # return {'status': 'failed', 'path': original_image_url, 'error': '이미지 URL 없음 또는 타입 오류'} + + # if not self.is_valid_image_path(original_image_url): + # self.logger.log(f"이미지 {index+1} 처리 중단: 유효하지 않은 이미지 주소 - {original_image_url}", level=logging.WARNING) + # return {'status': 'failed', 'path': original_image_url, 'error': '유효하지 않은 이미지 주소'} + + # # 1. 이미지 다운로드 + # local_image_path = await self.download_image(page, original_image_url, index, file_prefix) + # if not local_image_path: + # self.logger.log(f"이미지 {index+1} 다운로드 실패, 원본 URL 반환", level=logging.WARNING) + # return {'status': 'failed', 'path': original_image_url, 'error': '다운로드 실패'} + # self.logger.log(f"이미지 {index+1} 로컬 저장위치: {local_image_path}", level=logging.DEBUG) + + + # # 1. 배경제거 수행(이미지 반환) + # removed_img = self.background_removal_module.remove_background( + # local_image_path, **kwargs + # ) + # if removed_img is None: + # self.logger.log(f"배경제거 실패: {local_image_path}", level=40) + # return {'status': 'failed', 'path': local_image_path, 'error': '배경제거 실패'} + + # if self.toggle_states.get("remove_background_white", True): + # img_result_white = self.background_removal_module.to_white_background(removed_img) + # else: + # img_result_white = removed_img + + # # 2. 저장 경로 생성 + # if file_prefix: + # save_path = os.path.join(self.TEMP_IMAGE_DIR, f"nobg_{file_prefix}_img_{index+1}.png") + # else: + # save_path = os.path.join(self.TEMP_IMAGE_DIR, f"nobg_img_{index+1}.png") + + # # 3. 워터마크 등 후처리 및 저장 + # # 워터마크 등 추가하려면 아래처럼 + # # removed_img = self.postImageManager.add_watermark(image_data=removed_img, watermark_text=...) + + + # final_path = self.postImageManager.save_image_to_path(img_result_white, save_path) + + # if final_path: + # self.logger.log(f"배경제거 이미지 저장됨: {final_path}") + # return {'status': 'success', 'path': final_path} + # else: + # self.logger.log(f"배경제거 후 저장 실패: {save_path}", level=40) + # return {'status': 'failed', 'path': save_path, 'error': '저장 실패'} + + # except Exception as e: + # self.logger.log(f"배경제거 중 오류: {e}", level=40, exc_info=True) + # return {'status': 'failed', 'path': local_image_path, 'error': str(e)} + + + # def remove_background_with_ppmatting(image_path, output_path='output_foreground.png', alpha_path='output_alpha.png'): + # """ + # PaddleHub의 ppmatting을 이용한 배경제거(투명배경 PNG) 함수입니다. + # Args: + # image_path (str): 입력 이미지 경로 + # output_path (str): 결과 투명 배경 PNG 저장 경로 + # alpha_path (str): 알파(마스크) 이미지 저장 경로 + # Returns: + # foreground (np.ndarray): 알파채널이 포함된 전경 PNG 이미지 (BGRA) + # alpha (np.ndarray): 추출된 알파 마스크 + # """ + # import paddlehub as hub + # import cv2 + # import numpy as np + + # # 1. 이미지 로드 (OpenCV는 BGR) + # img = cv2.imread(image_path) + # if img is None: + # print(f"이미지를 불러올 수 없습니다: {image_path}") + # return None, None + + # # 2. BGR → RGB (ppmatting은 RGB 입력) + # img_rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) + + # # 3. ppmatting 모델 로드 (최초 1회 다운로드됨) + # matting = hub.Module(name='ppmatting-hrnet-1x') + + # # 4. 예측 수행 + # results = matting.predict(images=[img_rgb]) # 결과는 리스트 + + # # 5. 알파(투명도) 마스크 추출 (float, 0~1) + # alpha = results[0]['alpha'] + + # # 6. 알파 마스크를 0~255 uint8로 변환 (이미지 저장용) + # alpha_img = (alpha * 255).astype(np.uint8) + + # # 7. 원본 이미지를 BGRA(투명도 포함)로 변환 + # foreground = cv2.cvtColor(img, cv2.COLOR_BGR2BGRA) + # foreground[..., 3] = alpha_img # 알파 채널 추가 + + # # 8. 결과 이미지 저장 + # cv2.imwrite(output_path, foreground) + # cv2.imwrite(alpha_path, alpha_img) + + # print(f"배경 제거 PNG 저장: {output_path}") + # print(f"알파 마스크 저장: {alpha_path}") + # return foreground, alpha_img + + # ------------------------------------------------------------------ + # 고해상도 이미지 다운스케일 유틸리티 (메모리 절감용) + # ------------------------------------------------------------------ + def downscale_image_if_large(self, image_path, max_dim=1200): + """이미지가 너무 클 경우 다운스케일링""" + try: + image = cv2.imread(image_path) + if image is None: + return image_path + + h, w = image.shape[:2] + if max(h, w) <= max_dim: + # 원본 크기가 허용 범위 내면 그대로 반환 + del image # 명시적 해제 + return image_path + + # 비율 유지하며 다운스케일링 + if h > w: + new_h, new_w = max_dim, int(w * max_dim / h) + else: + new_h, new_w = int(h * max_dim / w), max_dim + + # .copy() 사용으로 뷰 문제 해결 + resized = cv2.resize(image, (new_w, new_h), interpolation=cv2.INTER_AREA).copy() + + # 원본 이미지 명시적 해제 + del image + + # 임시 파일로 저장 + temp_path = image_path.replace('.jpg', '_temp.jpg').replace('.png', '_temp.png') + cv2.imwrite(temp_path, resized) + + # 리사이즈된 이미지 명시적 해제 + del resized + + return temp_path + + except Exception as e: + self.logger.log(f"이미지 다운스케일링 실패: {e}", level=logging.ERROR, exc_info=True) + # 에러 발생 시 원본 경로 반환 + return image_path + finally: + # 안전장치: 남은 이미지 객체 정리 + try: + if 'image' in locals(): + del image + if 'resized' in locals(): + del resized + except: + pass + + async def preprocess_detail_image(self, image_path: str, index: int, target_width: int = 860, max_height: int = 3000) -> str: + """ + 상세페이지 이미지 전처리: 가로 860px 통일, 세로 3000px 초과시 분할 + + Args: + image_path: 원본 이미지 경로 + index: 이미지 인덱스 + target_width: 목표 가로 크기 (기본 860px) + max_height: 최대 세로 크기 (기본 3000px) + + Returns: + str: 전처리된 이미지 경로 (분할된 경우 첫 번째 이미지) + """ + try: + # 원본 이미지 로드 + image = cv2.imread(image_path) + if image is None: + self.logger.log(f"상세페이지 이미지 로드 실패: {image_path}", level=logging.ERROR) + return image_path + + orig_h, orig_w = image.shape[:2] + self.logger.log(f"상세페이지 이미지 {index+1} 원본 크기: {orig_w}x{orig_h}", level=logging.DEBUG) + + # 1단계: 가로를 860px로 리사이즈 (비율 유지) + if orig_w != target_width: + scale_factor = target_width / orig_w + new_height = int(orig_h * scale_factor) + resized_image = cv2.resize(image, (target_width, new_height), interpolation=cv2.INTER_LANCZOS4) + self.logger.log(f"상세페이지 이미지 {index+1} 가로 크기 조정: {orig_w}x{orig_h} → {target_width}x{new_height}", level=logging.DEBUG) + else: + resized_image = image + new_height = orig_h + + # 2단계: 세로가 3000px 초과하는지 확인 + if new_height <= max_height: + # 분할 불필요 - 리사이즈된 이미지 저장 + output_path = image_path.replace('.jpg', '_resized.jpg').replace('.png', '_resized.png') + cv2.imwrite(output_path, resized_image) + self.logger.log(f"상세페이지 이미지 {index+1} 크기 조정 완료: {target_width}x{new_height}", level=logging.INFO) + return output_path + + # 3단계: 분할 필요 - 3000px 단위로 분할 + split_count = (new_height + max_height - 1) // max_height # 올림 계산 + split_paths = [] + + for i in range(split_count): + start_y = i * max_height + end_y = min((i + 1) * max_height, new_height) + split_height = end_y - start_y + + # 이미지 분할 + split_image = resized_image[start_y:end_y, :, :] + + # 분할된 이미지 저장 + base_name = os.path.splitext(os.path.basename(image_path))[0] + extension = os.path.splitext(image_path)[1] + split_filename = f"{base_name}_split_{i+1}{extension}" + split_path = os.path.join(os.path.dirname(image_path), split_filename) + + cv2.imwrite(split_path, split_image) + split_paths.append(split_path) + + self.logger.log(f"상세페이지 이미지 {index+1} 분할 {i+1}/{split_count}: {target_width}x{split_height} → {split_path}", level=logging.DEBUG) + + self.logger.log(f"상세페이지 이미지 {index+1} 분할 완료: {split_count}개 파일 생성", level=logging.INFO) + + # OCR 정보 수집을 위해 분할된 모든 이미지 경로 저장 + self._store_split_image_paths(index, split_paths) + + # 첫 번째 분할 이미지 반환 (process_single_image는 하나의 이미지만 처리) + return split_paths[0] if split_paths else image_path + + except Exception as e: + self.logger.log(f"상세페이지 이미지 {index+1} 전처리 중 오류: {e}", level=logging.ERROR, exc_info=True) + return image_path + finally: + # 메모리 정리 + try: + if 'image' in locals(): + del image + if 'resized_image' in locals(): + del resized_image + if 'split_image' in locals(): + del split_image + except: + pass + + def _store_split_image_paths(self, index: int, split_paths: list): + """분할된 이미지 경로들을 저장 (나중에 OCR 정보 수집용)""" + if not hasattr(self, '_detail_split_images'): + self._detail_split_images = {} + self._detail_split_images[index] = split_paths + self.logger.log(f"이미지 {index+1} 분할 경로 저장: {len(split_paths)}개", level=logging.DEBUG) + + async def collect_and_store_ocr_data(self, image_url: str, image_path: str, ocr_results: list, index: int): + """ + 상세페이지 OCR 결과에서 상품 정보를 수집하고 저장 + + Args: + image_url: 원본 이미지 URL + image_path: 로컬 이미지 경로 + ocr_results: OCR 감지 결과 + index: 이미지 인덱스 + """ + try: + if not ocr_results: + return + + # OCR 결과에서 텍스트만 추출 + extracted_texts = [result.get('text', '').strip() for result in ocr_results if result.get('text', '').strip()] + + if not extracted_texts: + return + + # 데이터베이스에 저장 (단순화됨) + await self._save_ocr_data_to_db(image_url, image_path, extracted_texts, index) + + self.logger.log(f"이미지 {index+1} OCR raw 데이터 수집 완료: {len(extracted_texts)}개 텍스트", level=logging.DEBUG) + + except Exception as e: + self.logger.log(f"OCR 정보 수집 중 오류: {e}", level=logging.ERROR, exc_info=True) + + # _classify_product_texts 함수는 단순화로 인해 비활성화됨 (raw 데이터만 저장) + + async def _save_ocr_data_to_db(self, image_url: str, image_path: str, texts: list, index: int): + """ + OCR raw 데이터를 SQLite 데이터베이스에 저장 + + Args: + image_url: 원본 이미지 URL + image_path: 로컬 이미지 경로 + texts: raw OCR 텍스트 리스트 + index: 이미지 인덱스 + """ + try: + import sqlite3 + import json + import os + from datetime import datetime + + # 데이터베이스 파일 경로 설정 + db_path = os.path.join(self.base_dir, "user_data", "product_ocr_data.db") + os.makedirs(os.path.dirname(db_path), exist_ok=True) + + # 데이터베이스 연결 및 테이블 생성 + conn = sqlite3.connect(db_path) + cursor = conn.cursor() + + # 테이블 생성 (단순화된 버전) + cursor.execute(''' + CREATE TABLE IF NOT EXISTS ocr_raw_data ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + image_url TEXT NOT NULL, + image_path TEXT, + image_index INTEGER, + raw_texts TEXT, + text_count INTEGER DEFAULT 0, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP + ) + ''') + + # 데이터 삽입 (단순화됨) + cursor.execute(''' + INSERT INTO ocr_raw_data + (image_url, image_path, image_index, raw_texts, text_count) + VALUES (?, ?, ?, ?, ?) + ''', ( + image_url, + image_path, + index, + json.dumps(texts, ensure_ascii=False), + len(texts) + )) + + conn.commit() + conn.close() + + self.logger.log(f"이미지 {index+1} OCR 데이터 DB 저장 완료: {db_path}", level=logging.DEBUG) + + except Exception as e: + self.logger.log(f"OCR 데이터 DB 저장 중 오류: {e}", level=logging.ERROR, exc_info=True) + + async def collect_ocr_data_in_memory(self, image_url: str, image_path: str, ocr_results: list, index: int): + """ + OCR 데이터를 메모리에만 저장 (현재 세션용) - 전처리 포함 + + Args: + image_url: 원본 이미지 URL + image_path: 로컬 이미지 경로 + ocr_results: OCR 감지 결과 + index: 이미지 인덱스 + """ + try: + if not ocr_results: + return + + # OCR 결과에서 텍스트만 추출 + extracted_texts = [result.get('text', '').strip() for result in ocr_results if result.get('text', '').strip()] + + if not extracted_texts: + return + + # 단순 OCR 텍스트 저장 (raw 데이터 그대로 사용) + filtered_texts = extracted_texts + + # 메모리에 저장 + if not hasattr(self, '_session_ocr_data'): + self._session_ocr_data = [] + + self._session_ocr_data.append({ + 'image_url': image_url, + 'image_path': image_path, + 'image_index': index, + 'raw_texts': filtered_texts, + 'text_count': len(filtered_texts) + }) + + self.logger.log(f"이미지 {index+1} OCR raw 데이터 메모리 저장 완료: {len(filtered_texts)}개 텍스트", level=logging.DEBUG) + + except Exception as e: + self.logger.log(f"OCR 정보 메모리 저장 중 오류: {e}", level=logging.ERROR, exc_info=True) + + # 복잡한 전처리 함수들은 제거됨 (단순 raw 데이터 저장 방식으로 변경) + + def get_session_ocr_data(self) -> list: + """현재 세션의 OCR 데이터 반환""" + return getattr(self, '_session_ocr_data', []) + + def clear_session_ocr_data(self): + """현재 세션의 OCR 데이터 초기화""" + if hasattr(self, '_session_ocr_data'): + del self._session_ocr_data + self.logger.log("세션 OCR 데이터 초기화 완료", level=logging.DEBUG) + \ No newline at end of file diff --git a/modules/image_worker.py b/modules/image_worker.py new file mode 100644 index 0000000..7e3ec61 --- /dev/null +++ b/modules/image_worker.py @@ -0,0 +1,401 @@ + + +# src/modules/image_worker.py +""" +ImageWorker 프로세스 – 별도 프로세스로 구동 +단 한 번의 READY("OK") 신호만 보내며, OCR 모델 Warm‑up 후 작업 루프에 진입한다. +""" +from __future__ import annotations + +import multiprocessing +import os + +import logging +import asyncio +import traceback +import queue +import time +import cv2 +import numpy as np + +from modules.image_processor3 import ImageProcessor3 +from modules.log_bridge import ImageWorkerLogger + +# ------------------------------------------------------------------ # +# 로깅 유틸리티 # +# ------------------------------------------------------------------ # + +class QueueLogger: + """큐를 통해 메인 프로세스의 로거로 전송하는 로거""" + def __init__(self, log_queue, process_name): + self.log_queue = log_queue + self.process_name = process_name + + def log(self, message, level=logging.INFO, exc_info=False): + try: + log_record = { + 'process_name': self.process_name, + 'level': level, + 'message': message, + 'exc_info': exc_info + } + self.log_queue.put(log_record) + except Exception: + pass # 로그 전송 실패 시 무시 + + def debug(self, msg, *a, **kw): self.log(msg, logging.DEBUG) + def info(self, msg, *a, **kw): self.log(msg, logging.INFO) + def warning(self, msg, *a, **kw): self.log(msg, logging.WARNING) + def error(self, msg, *a, **kw): self.log(msg, logging.ERROR) + def critical(self, msg, *a, **kw): self.log(msg, logging.CRITICAL) + +class CompatLogger: + """커스텀 Logger 인터페이스를 표준 logging.Logger 로 매핑""" + def __init__(self, py_logger: logging.Logger): + self._l = py_logger + + def log(self, message, level=logging.INFO, exc_info=False): + self._l.log(level, message, exc_info=exc_info) + + # 편의 메서드 + def debug(self, msg, *a, **kw): self.log(msg, logging.DEBUG) + def info(self, msg, *a, **kw): self.log(msg, logging.INFO) + def warning(self, msg, *a, **kw): self.log(msg, logging.WARNING) + def error(self, msg, *a, **kw): self.log(msg, logging.ERROR) + def critical(self, msg, *a, **kw): self.log(msg, logging.CRITICAL) + + +def _setup_logging(log_path: str) -> logging.Logger: + """별도 프로세스용 파일 로거 설정""" + root = logging.getLogger() + root.handlers.clear() + root.setLevel(logging.DEBUG) + + fh = logging.FileHandler(log_path, encoding="utf-8") + fmt = logging.Formatter( + "[%(asctime)s] [%(processName)s] [%(levelname)s] " + "[%(module)s:%(funcName)s:%(lineno)d] %(message)s" + ) + fh.setFormatter(fmt) + root.addHandler(fh) + return root + +# ------------------------------------------------------------------ # +# 워커 메인 함수 # +# ------------------------------------------------------------------ # + +def worker_main( + task_q, + result_q, + log_queue, # 추가: 로그 큐 + log_path: str, + base_dir: str, + toggle_states: dict, + unwanted_words: list[str], + authenticated_by_admin: bool = False, +): + # ── 로깅 초기화 ──────────────────────────────────────────── + # 큐 로거 사용 (메인 프로세스로 로그 전송) + if log_queue: + logger = ImageWorkerLogger(log_queue, f"ImageWorker-{os.getpid()}") + print(f"[DEBUG] ImageWorker {os.getpid()}: LogQueue 사용됨") + else: + # 폴백: 기존 파일 로거 + py_logger = _setup_logging(log_path) + logger = CompatLogger(py_logger) + print(f"[DEBUG] ImageWorker {os.getpid()}: 파일 로거 사용됨") + + logger.info( + f"ImageWorker 프로세스 기동 " + f"(PID={os.getpid()}, Name={multiprocessing.current_process().name})" + ) + + # 워커 기동 시에도 ProgramData/ImgWorker 하위 임시 폴더를 한 번 더 정리 + try: + base_program = os.environ.get("PROGRAMDATA", r"C:\\ProgramData") + app_data_dir = os.path.join(base_program, "ImgWorker") + def _safe_rmtree_contents(target_dir: str): + try: + if not target_dir: + return + base = os.path.abspath(app_data_dir) + td = os.path.abspath(target_dir) + if os.path.commonpath([base, td]) != base: + return + os.makedirs(td, exist_ok=True) + for name in os.listdir(td): + p = os.path.join(td, name) + try: + if os.path.isdir(p): + import shutil as _sh + _sh.rmtree(p, ignore_errors=True) + else: + os.remove(p) + except Exception: + pass + except Exception: + pass + for d in (os.path.join(app_data_dir, "incoming"), os.path.join(app_data_dir, "work"), os.path.join(app_data_dir, "output"), os.path.join(app_data_dir, "outputs")): + _safe_rmtree_contents(d) + except Exception: + pass + + # READY는 모델 로딩 완료 후에만 전송 + + # ── ImageProcessor 초기화 및 Warm‑up ────────────────────── + processor = None + try: + # 안전한 초기화를 위한 메모리 정리 + import gc + gc.collect() + + logger.info("🔧 ImageProcessor3 초기화 시작...") + processor = ImageProcessor3( + logger=logger, + page=None, + toggle_states=toggle_states, + unwanted_words=unwanted_words, + authenticated_by_admin=authenticated_by_admin, + base_dir=base_dir, + papago_translator=None, + ) + + # OCR 모델 안전한 Warm-up + if processor and processor.ocr_module: + try: + logger.info("🔰 OCR 모듈 Warm-up 시작...") + dummy_path = os.path.join(base_dir, "_imgproc_warmup.png") + tmp = np.zeros((100, 100, 3), dtype=np.uint8) # 더 현실적인 크기 + cv2.imwrite(dummy_path, tmp) + + # 타임아웃 설정으로 무한 대기 방지 + import threading + import time + + warmup_success = [False] + warmup_error = [None] + + def warmup_ocr(): + try: + processor.ocr_module.detect_text(dummy_path) + warmup_success[0] = True + except Exception as e: + warmup_error[0] = e + + warmup_thread = threading.Thread(target=warmup_ocr) + warmup_thread.daemon = True + warmup_thread.start() + warmup_thread.join(timeout=30) # 30초 타임아웃 + + if warmup_success[0]: + logger.info("✅ OCR 모듈 Warm-up 성공") + elif warmup_error[0]: + logger.warning(f"⚠️ OCR 모듈 Warm-up 실패: {warmup_error[0]}") + else: + logger.warning("⚠️ OCR 모듈 Warm-up 타임아웃") + + try: + os.remove(dummy_path) + except Exception: + pass + + except Exception as e: + logger.warning(f"OCR Warm-up 실패: {e}") + else: + logger.warning("OCR 모듈이 초기화되지 않아 Warm-up 건너뜀") + + logger.info("🔰 ImageProcessor Warm‑up 완료") + + except Exception as e: + logger.error(f"ImageProcessor 초기화 실패: {e}", exc_info=True) + + # 초기화 실패 시에도 기본적인 처리가 가능하도록 최소한의 processor 생성 시도 + try: + logger.info("🔄 안전 모드로 재초기화 시도...") + + # GPU 설정을 CPU로 강제 변경 + safe_toggle_states = toggle_states.copy() + safe_toggle_states['use_cuda'] = False + safe_toggle_states['optionIMGTrans_type'] = 'CPU' + safe_toggle_states['detail_IMGTrans_type'] = 'CPU' + safe_toggle_states['thumb_trans_type'] = 'CPU' + safe_toggle_states['migan_use_cuda'] = False + + processor = ImageProcessor3( + logger=logger, + page=None, + toggle_states=safe_toggle_states, + unwanted_words=unwanted_words, + authenticated_by_admin=authenticated_by_admin, + base_dir=base_dir, + papago_translator=None, + ) + logger.info("✅ 안전 모드로 ImageProcessor 초기화 성공") + + except Exception as e2: + logger.error(f"안전 모드 초기화도 실패: {e2}", exc_info=True) + processor = None + + # ── READY(OK) 신호 전송 ────────────────────────────────── + try: + result_q.put({"id": "__READY__", "data": "OK"}) + logger.info("워커 READY 신호 전송") + except Exception: + logger.error("READY 신호 전송 실패", exc_info=True) + + # ── rembg 세션 비동기 로딩 ────────────────────────────── + def preload_rembg_sessions(): + """백그라운드에서 rembg 세션을 미리 로딩""" + try: + if processor and hasattr(processor, 'background_removal_module'): + logger.info("🔄 rembg 세션 백그라운드 로딩 시작...") + # 기본 세션들을 미리 로딩 + processor.background_removal_module._preload_sessions() + logger.info("✅ rembg 세션 백그라운드 로딩 완료") + except Exception as e: + logger.warning(f"rembg 세션 백그라운드 로딩 실패: {e}") + + # rembg 세션 로딩을 별도 Thread에서 실행 + import threading + preload_thread = threading.Thread(target=preload_rembg_sessions, daemon=True) + preload_thread.start() + + # ── 작업 루프 ───────────────────────────────────────────── + # 컨트롤러가 즉시 pick-up 할 수 있도록 READY 신호를 추가 전송 + try: + result_q.put({"id": "__READY__", "cmd": "__READY__", "kwargs": {}}) + logger.info("📡 추가 READY 신호 전송 완료") + except Exception as e: + logger.error(f"추가 READY 신호 전송 실패: {e}") + + idle_log_last = 0.0 + while True: + try: + # 주기적 상태 출력을 위해 타임아웃 설정 + logger.debug(f"큐에서 작업 대기 중... (PID: {os.getpid()})") + task = task_q.get(timeout=60) # 60초 타임아웃 + #logger.info(f"🔥 작업 수신 성공: {task}") + logger.info(f"🔥 작업 수신 성공") + except queue.Empty: + # 유휴 로그는 10분에 한 번만 기록해 로그 스팸을 줄인다. + now_ts = time.time() + if now_ts - idle_log_last >= 600: + idle_log_last = now_ts + logger.info("대기 중(유휴)") + continue + except Exception as e: + logger.error(f"작업 수신 중 오류: {e}", exc_info=True) + continue + + if task is None: + logger.info("Shutdown signal 수신 → 종료") + try: + # 종료 시 임시 폴더 정리 + base_program = os.environ.get("PROGRAMDATA", r"C:\\ProgramData") + app_data_dir = os.path.join(base_program, "ImgWorker") + for d in (os.path.join(app_data_dir, "incoming"), os.path.join(app_data_dir, "work"), os.path.join(app_data_dir, "output"), os.path.join(app_data_dir, "outputs")): + try: + for n in os.listdir(d): + p = os.path.join(d, n) + if os.path.isdir(p): + import shutil as _sh + _sh.rmtree(p, ignore_errors=True) + else: + os.remove(p) + except Exception: + pass + except Exception: + pass + break + + uid = task["id"] + cmd = task["cmd"] + kwargs = task["kwargs"] + logger.info(f"🚀 작업 처리 시작: cmd={cmd}, uid={uid}") + + # 메타 파라미터 제거 및 실시간 값 반영 + new_toggle = kwargs.pop("_toggle_states", None) + if new_toggle and processor: + processor.update_toggle_states(new_toggle) + + _ = kwargs.pop("_base_dir", None) # 필요 없으므로 버림 + upd_unwanted = kwargs.pop("_update_unwanted_texts", None) + if upd_unwanted and processor: + processor.update_unwanted_texts(upd_unwanted) + + # 실제 작업 실행 + try: + logger.debug(f"작업 실행 직전: cmd={cmd}") + if cmd == "process_single_image": + logger.debug("process_single_image 호출 직전") + data = asyncio.run(processor.process_single_image(**kwargs)) + # 성능 지표 포함 + try: + timings = getattr(processor, '_last_timings', None) + if isinstance(data, dict) and timings: + data['timings'] = timings + except Exception: + pass + logger.debug("process_single_image 호출 완료") + elif cmd == "remove_background": + logger.debug("remove_background 호출 직전") + data = asyncio.run(processor.remove_background(**kwargs)) + logger.debug("remove_background 호출 완료") + elif cmd == "reinit_ocr": + # 토글 반영 후 OCR 재초기화(프로바이더 캐시 고려) + ok = processor.reset_ocr_module() + data = {"ok": bool(ok)} + elif cmd == "reinit_rembg": + # REMBG(배경제거) 모듈 재준비: 현재 Bria 모듈 사용시 세션/프로바이더를 재평가하도록 None 초기화 + try: + # toggle_states의 provider override는 상위에서 반영되어 내려옴 + # BriaBackgroundRemovalModule은 lazy-load 방식 → 재생성 유도 + if hasattr(processor, 'background_removal_module'): + try: + del processor.background_removal_module + except Exception: + processor.background_removal_module = None + from modules.bria_background_removal_module import BriaBackgroundRemovalModule + # 경로/매개변수는 processor.toggle_states에서 유추(존재 시) + model_path = processor.toggle_states.get('local_rembg_model_path') + processor.background_removal_module = BriaBackgroundRemovalModule( + logger=logger, + default_model=processor.toggle_states.get('local_model_name', 'bria-rmbg-1.4'), + gpu_manager=getattr(processor, 'gpu_manager', None), + local_rembg_model_path=model_path, + ) + data = {"ok": True} + except Exception as e: + logger.error(f"REMBG 재초기화 실패: {e}") + data = {"ok": False, "error": str(e)} + elif cmd == "reset_migan": + # MIGAN 재구성(토글상 migan_use_cuda 등 변경 반영) + try: + from modules.migan_module import build_migan_from_toggle + enhanced_toggle_states = processor.toggle_states.copy() + # 가속 사용 플래그 명칭 정리(호환): migan_use_cuda -> migan_use_accel + if 'migan_use_cuda' in enhanced_toggle_states and 'migan_use_accel' not in enhanced_toggle_states: + enhanced_toggle_states['migan_use_accel'] = enhanced_toggle_states['migan_use_cuda'] + # provider override가 들어왔으면 반영(auto|dml|cpu) + prov = kwargs.get('provider') + if prov: + enhanced_toggle_states['migan_provider_override'] = prov + # gpu_manager 상태와 무관하게 토글을 그대로 전달(직접 폴백은 모듈 내부) + processor.migan = build_migan_from_toggle(enhanced_toggle_states, logger=logger, gpu_manager=getattr(processor, 'gpu_manager', None)) + data = {"ok": bool(processor.migan is not None)} + except Exception as mm_err: + logger.error(f"MIGAN 재설정 실패: {mm_err}") + data = {"ok": False, "error": str(mm_err)} + elif cmd == "__PING__": + # 하트비트 응답 + data = "__PONG__" + else: + raise ValueError(f"unknown cmd: {cmd}") + + logger.debug(f"작업 결과 반환 중: uid={uid}") + result_q.put({"id": uid, "data": data}) + logger.debug(f"작업 결과 반환 완료: uid={uid}") + except Exception: + logger.error(f"작업 처리 중 오류: cmd={cmd}, uid={uid}") + logger.error("작업 처리 중 오류", exc_info=True) + result_q.put({"id": uid, "error": traceback.format_exc()}) diff --git a/modules/img/1.jpg b/modules/img/1.jpg new file mode 100644 index 0000000..d9fece9 Binary files /dev/null and b/modules/img/1.jpg differ diff --git a/modules/img/2.jpg b/modules/img/2.jpg new file mode 100644 index 0000000..e38db8f Binary files /dev/null and b/modules/img/2.jpg differ diff --git a/modules/img/3.jpg b/modules/img/3.jpg new file mode 100644 index 0000000..ac987f4 Binary files /dev/null and b/modules/img/3.jpg differ diff --git a/modules/img/4.jpg b/modules/img/4.jpg new file mode 100644 index 0000000..19f1ade Binary files /dev/null and b/modules/img/4.jpg differ diff --git a/modules/img/5.jpg b/modules/img/5.jpg new file mode 100644 index 0000000..ac74d00 Binary files /dev/null and b/modules/img/5.jpg differ diff --git a/modules/img/6.jpg b/modules/img/6.jpg new file mode 100644 index 0000000..d810fab Binary files /dev/null and b/modules/img/6.jpg differ diff --git a/modules/img/7.jpg b/modules/img/7.jpg new file mode 100644 index 0000000..b6ecdcf Binary files /dev/null and b/modules/img/7.jpg differ diff --git a/modules/img/8.jpg b/modules/img/8.jpg new file mode 100644 index 0000000..a27fd74 Binary files /dev/null and b/modules/img/8.jpg differ diff --git a/modules/img/debug_mask_4.png b/modules/img/debug_mask_4.png new file mode 100644 index 0000000..76c7f12 Binary files /dev/null and b/modules/img/debug_mask_4.png differ diff --git a/modules/img/debug_mask_5.png b/modules/img/debug_mask_5.png new file mode 100644 index 0000000..b7697d9 Binary files /dev/null and b/modules/img/debug_mask_5.png differ diff --git a/modules/img/debug_mask_img_1.png b/modules/img/debug_mask_img_1.png new file mode 100644 index 0000000..a8bb5fe Binary files /dev/null and b/modules/img/debug_mask_img_1.png differ diff --git a/modules/img/debug_mask_img_4.png b/modules/img/debug_mask_img_4.png new file mode 100644 index 0000000..99f5a66 Binary files /dev/null and b/modules/img/debug_mask_img_4.png differ diff --git a/modules/img/debug_mask_img_5.png b/modules/img/debug_mask_img_5.png new file mode 100644 index 0000000..040b839 Binary files /dev/null and b/modules/img/debug_mask_img_5.png differ diff --git a/modules/img/debug_mask_img_6.png b/modules/img/debug_mask_img_6.png new file mode 100644 index 0000000..7dc4891 Binary files /dev/null and b/modules/img/debug_mask_img_6.png differ diff --git a/modules/img/debug_mask_img_7.png b/modules/img/debug_mask_img_7.png new file mode 100644 index 0000000..73943bd Binary files /dev/null and b/modules/img/debug_mask_img_7.png differ diff --git a/modules/img/debug_mask_img_8.png b/modules/img/debug_mask_img_8.png new file mode 100644 index 0000000..c63379d Binary files /dev/null and b/modules/img/debug_mask_img_8.png differ diff --git a/modules/img/debug_ocr_img_4.png b/modules/img/debug_ocr_img_4.png new file mode 100644 index 0000000..892abc4 Binary files /dev/null and b/modules/img/debug_ocr_img_4.png differ diff --git a/modules/img/debug_ocr_img_5.png b/modules/img/debug_ocr_img_5.png new file mode 100644 index 0000000..d1b9344 Binary files /dev/null and b/modules/img/debug_ocr_img_5.png differ diff --git a/modules/img/debug_ocr_img_8.png b/modules/img/debug_ocr_img_8.png new file mode 100644 index 0000000..4761679 Binary files /dev/null and b/modules/img/debug_ocr_img_8.png differ diff --git a/modules/img/nobg_img_8.png b/modules/img/nobg_img_8.png new file mode 100644 index 0000000..85ebbc3 Binary files /dev/null and b/modules/img/nobg_img_8.png differ diff --git a/modules/img/nobg_img_9.png b/modules/img/nobg_img_9.png new file mode 100644 index 0000000..02e9b14 Binary files /dev/null and b/modules/img/nobg_img_9.png differ diff --git a/modules/img/translated_img_1.png b/modules/img/translated_img_1.png new file mode 100644 index 0000000..9905a49 Binary files /dev/null and b/modules/img/translated_img_1.png differ diff --git a/modules/img/translated_img_2.png b/modules/img/translated_img_2.png new file mode 100644 index 0000000..185a0e0 Binary files /dev/null and b/modules/img/translated_img_2.png differ diff --git a/modules/img/translated_img_3.png b/modules/img/translated_img_3.png new file mode 100644 index 0000000..1d7c706 Binary files /dev/null and b/modules/img/translated_img_3.png differ diff --git a/modules/img/translated_img_4.png b/modules/img/translated_img_4.png new file mode 100644 index 0000000..848fd7e Binary files /dev/null and b/modules/img/translated_img_4.png differ diff --git a/modules/img/translated_img_5.png b/modules/img/translated_img_5.png new file mode 100644 index 0000000..fc42310 Binary files /dev/null and b/modules/img/translated_img_5.png differ diff --git a/modules/img/translated_img_6.png b/modules/img/translated_img_6.png new file mode 100644 index 0000000..99e4083 Binary files /dev/null and b/modules/img/translated_img_6.png differ diff --git a/modules/img/translated_img_7.png b/modules/img/translated_img_7.png new file mode 100644 index 0000000..3fbb0d6 Binary files /dev/null and b/modules/img/translated_img_7.png differ diff --git a/modules/img/translated_img_8.png b/modules/img/translated_img_8.png new file mode 100644 index 0000000..6e5d500 Binary files /dev/null and b/modules/img/translated_img_8.png differ diff --git a/modules/inpainting_module.py b/modules/inpainting_module.py new file mode 100644 index 0000000..a011e28 --- /dev/null +++ b/modules/inpainting_module.py @@ -0,0 +1,27 @@ +import numpy as np +import requests +import cv2 +import base64 + +class IOPaintInpainting: + """IOPaint 서버 연동 인페인팅 모델 (REST API /api/v1/inpaint 사용, 바이너리 PNG 반환)""" + def __init__(self, server_url="http://localhost:8080"): + self.api_url = f"http://localhost:8080/api/v1/inpaint" + def inpaint(self, image: np.ndarray, mask: np.ndarray, api_url:str = 'http://localhost:8080/api/v1/inpaint', ) -> np.ndarray: + # 이미지를 base64로 인코딩 + _, img_encoded = cv2.imencode('.png', image) + _, mask_encoded = cv2.imencode('.png', mask) + img_b64 = base64.b64encode(img_encoded).decode('utf-8') + mask_b64 = base64.b64encode(mask_encoded).decode('utf-8') + payload = { + "image": img_b64, + "mask": mask_b64 + } + response = requests.post(api_url, json=payload) + if response.status_code != 200: + print("IOPaint 서버 에러:", response.text) + return None + # 응답이 바이너리 PNG 이미지이므로 바로 디코딩 + nparr = np.frombuffer(response.content, np.uint8) + result = cv2.imdecode(nparr, cv2.IMREAD_COLOR) + return result diff --git a/modules/log_bridge.py b/modules/log_bridge.py new file mode 100644 index 0000000..d044375 --- /dev/null +++ b/modules/log_bridge.py @@ -0,0 +1,105 @@ +""" +ImageWorker와 메인 프로세스 간 로그 브리지 +""" +import threading +import queue +import logging +import sys +import os + +class LogBridge: + """ImageWorker 프로세스의 로그를 메인 프로세스로 전달하는 브리지""" + + def __init__(self, main_logger, log_queue): + self.main_logger = main_logger + self.log_queue = log_queue + self.reader_thread = None + self.running = False + + def start(self): + """로그 리더 스레드 시작""" + if self.running: + return + + self.running = True + self.reader_thread = threading.Thread(target=self._log_reader, daemon=True) + self.reader_thread.start() + + def stop(self): + """로그 리더 스레드 중지""" + self.running = False + try: + self.log_queue.put(None) # 종료 신호 + except Exception: + pass + + def _log_reader(self): + """로그 큐에서 메시지를 읽어 메인 로거로 전달""" + while self.running: + try: + log_record = self.log_queue.get(timeout=1) + if log_record is None: # 종료 신호 + break + + # 메인 로거로 전달 + process_name = log_record.get('process_name', 'ImageWorker') + level = log_record.get('level', logging.INFO) + message = log_record.get('message', '') + exc_info = log_record.get('exc_info', False) + + # 로그 브릿지를 통해 넘어오는 모든 메시지를 DEBUG 레벨로 처리 + # 이렇게 하면 GUI에는 표시되지 않고 콘솔과 파일에만 기록됨 + level = logging.DEBUG + + # 메시지에 프로세스 이름 추가 + formatted_message = f"[{process_name}] {message}" + + # 메인 로거로 출력 + if hasattr(self.main_logger, 'log'): + self.main_logger.log(formatted_message, level=level, exc_info=exc_info) + else: + self.main_logger.log(level, formatted_message, exc_info=exc_info) + + except queue.Empty: + continue + except Exception as e: + # 로그 처리 중 오류 발생 시 stderr로 출력 + print(f"LogBridge 오류: {e}", file=sys.stderr) + + +class ImageWorkerLogger: + """ImageWorker에서 사용할 로거 (큐를 통해 메인 프로세스로 전송)""" + + def __init__(self, log_queue, process_name=None): + self.log_queue = log_queue + self.process_name = process_name or f"ImageWorker-{os.getpid()}" + + def log(self, message, level=logging.INFO, exc_info=False): + """로그 메시지를 큐를 통해 메인 프로세스로 전송""" + try: + log_record = { + 'process_name': self.process_name, + 'level': level, + 'message': str(message), + 'exc_info': exc_info + } + self.log_queue.put(log_record) + except Exception: + # 큐 전송 실패 시 stderr로 출력 + print(f"[{self.process_name}] {message}", file=sys.stderr) + + def debug(self, msg, *args, **kwargs): + self.log(msg, logging.DEBUG) + + def info(self, msg, *args, **kwargs): + self.log(msg, logging.INFO) + + def warning(self, msg, *args, **kwargs): + self.log(msg, logging.WARNING) + + def error(self, msg, *args, **kwargs): + exc_info = kwargs.get('exc_info', False) + self.log(msg, logging.ERROR, exc_info=exc_info) + + def critical(self, msg, *args, **kwargs): + self.log(msg, logging.CRITICAL) \ No newline at end of file diff --git a/modules/mask_module.py b/modules/mask_module.py new file mode 100644 index 0000000..26ee0df --- /dev/null +++ b/modules/mask_module.py @@ -0,0 +1,99 @@ +# -*- coding: utf-8 -*- +""" +마스크 모듈 - OCR 결과를 기반으로 마스크 생성 및 처리 (라이브러리화) +확장, 블러 등 basic 마스크 처리만 지원 +""" + +import cv2 +import numpy as np +from typing import List, Dict, Any +from shapely.geometry import Polygon +import os +import logging + +class MaskModule: + def __init__(self, logger, base_dir): + self.logger = logger + self.base_dir = base_dir + self.logger.log("마스크 모듈 초기화 완료", level=logging.DEBUG) + + def create_masks(self, image_path: str, ocr_results: List[Dict], expansion_size: int = 0, blur_size: int = 0, mask_option: str = "basic") -> np.ndarray: + image = cv2.imread(image_path) + if image is None: + self.logger.log(f"이미지를 읽을 수 없습니다: {image_path}", level=logging.ERROR) + return None + height, width = image.shape[:2] + mask = np.zeros((height, width), dtype=np.uint8) + + # self.logger.log(f"[마스크] OCR 결과 개수: {len(ocr_results)}, 이미지 크기: {width}x{height}", level=logging.DEBUG) + + for i, result in enumerate(ocr_results, 1): + polygon = result['polygon'] + text = result.get('text', '') + # self.logger.log(f"[마스크] {i}번째 텍스트: '{text}', 원본 polygon: {polygon}", level=logging.DEBUG) + + # 확장 없이 원본 폴리곤 그대로 사용 + original_poly = np.array(polygon, dtype=np.int32) + # self.logger.log(f"[마스크] {i}번째 원본 polygon shape: {original_poly.shape}, 값: {original_poly}", level=logging.DEBUG) + + # polygon이 (N,2) np.int32인지 체크 + if original_poly.ndim != 2 or original_poly.shape[1] != 2 or original_poly.shape[0] < 3: + # self.logger.log(f"[마스크] 잘못된 폴리곤 shape: {original_poly.shape}, 값: {original_poly}", level=logging.WARNING) + continue + + cv2.fillPoly(mask, [original_poly], 255) + # self.logger.log(f"[마스크] {i}번째 폴리곤 마스크 적용 완료", level=logging.DEBUG) + + # 마스크에 실제로 255 값이 있는지 확인 + mask_pixels_before = np.sum(mask > 0) + # self.logger.log(f"[마스크] Erosion 전 마스크 픽셀 개수: {mask_pixels_before} / {height * width}", level=logging.DEBUG) + + # Erosion 적용 (마스크를 글자 주변으로 축소) + erosion_kernel_size = 6 # 3x3 커널로 적당히 축소 + erosion_kernel = np.ones((erosion_kernel_size, erosion_kernel_size), np.uint8) + eroded_mask = cv2.erode(mask, erosion_kernel, iterations=1) + + # Erosion 후 마스크 픽셀 개수 확인 + mask_pixels_after = np.sum(eroded_mask > 0) + # self.logger.log(f"[마스크] Erosion 후 마스크 픽셀 개수: {mask_pixels_after} / {height * width} (감소: {mask_pixels_before - mask_pixels_after}픽셀)", level=logging.DEBUG) + + # 기본 마스크만 사용 (erosion 적용된 마스크 반환) + if mask_option == "basic": + return eroded_mask + else: + # 다른 옵션일 경우에만 추가 후처리 적용 + processed_mask = self.process_mask(eroded_mask, expansion_size, blur_size) + return processed_mask + + def expand_polygon(self, polygon, offset=15): + try: + poly = Polygon(polygon) + expanded = poly.buffer(offset) + if expanded.is_empty: + return np.array(polygon, dtype=np.int32) + + # exterior.coords는 마지막 점이 첫 번째 점과 동일하므로 제거 + coords = list(expanded.exterior.coords)[:-1] + + # 정수 좌표로 변환 + coords = [[int(x), int(y)] for x, y in coords] + + # 최소 3점 이상인지 확인 + if len(coords) < 3: + return np.array(polygon, dtype=np.int32) + + return np.array(coords, dtype=np.int32) + except Exception as e: + self.logger.log(f"[마스크] expand_polygon 오류: {e}, 원본 polygon 사용", level=logging.WARNING) + return np.array(polygon, dtype=np.int32) + + def process_mask(self, mask: np.ndarray, expansion_size: int = 5, blur_size: int = 3) -> np.ndarray: + processed_mask = mask.copy() + if expansion_size > 0: + kernel = np.ones((expansion_size, expansion_size), np.uint8) + processed_mask = cv2.dilate(processed_mask, kernel, iterations=1) + if blur_size > 0: + blur_size = blur_size if blur_size % 2 == 1 else blur_size + 1 + processed_mask = cv2.GaussianBlur(processed_mask, (blur_size, blur_size), 0) + return processed_mask + \ No newline at end of file diff --git a/modules/mask_module_for_paddle.py b/modules/mask_module_for_paddle.py new file mode 100644 index 0000000..6fd79a6 --- /dev/null +++ b/modules/mask_module_for_paddle.py @@ -0,0 +1,72 @@ + +import cv2 +import numpy as np +from typing import List, Dict, Any +from shapely.geometry import Polygon +import logging + +class MaskModule: + def __init__(self, logger, base_dir): + self.logger = logger + self.base_dir = base_dir + self.logger.log("마스크 모듈 초기화 완료", level=logging.INFO) + + def create_masks(self, image_path: str, ocr_results: List[Dict], expansion_size: int = 6, blur_size: int = 7, mask_option: str = "basic") -> np.ndarray: + image = cv2.imread(image_path) + if image is None: + self.logger.log(f"이미지를 읽을 수 없습니다: {image_path}", level=logging.ERROR) + return None + height, width = image.shape[:2] + mask = np.zeros((height, width), dtype=np.uint8) + for i, result in enumerate(ocr_results, 1): + polygon = result['polygon'] + try: + # 좌표가 float일 수 있으므로 안전 캐스팅 + 범위 클램프 + poly_np = np.array(polygon, dtype=np.float32) + # 이미지 경계 밖 좌표를 안전하게 클램프 + poly_np[:, 0] = np.clip(poly_np[:, 0], 0, width - 1) + poly_np[:, 1] = np.clip(poly_np[:, 1], 0, height - 1) + # 확장 + expanded_poly = self.expand_polygon(poly_np.tolist(), offset=5) + expanded_poly = expanded_poly.astype(np.int32) + cv2.fillPoly(mask, [expanded_poly], 255) + except Exception as e: + self.logger.log(f"마스크 폴리곤 처리 오류({i}): {e}", level=logging.WARNING) + continue + processed_mask = self.process_mask(mask, expansion_size, blur_size) + + # # 디버깅용 마스크 저장 (항상 0과 255만 가지는 표준 흑백 마스크로 저장) + # try: + # import os + # base_dir = os.path.dirname(image_path) + # base_name = os.path.splitext(os.path.basename(image_path))[0] + # debug_mask_path = os.path.join(base_dir, f"debug_mask_{base_name}.png") + # # 마스크가 0~255 사이의 값이 섞여 있을 수 있으니, 128 기준으로 이진화 + # mask_to_save = ((processed_mask > 128) * 255).astype('uint8') + # cv2.imwrite(debug_mask_path, mask_to_save) + # self.logger.log(f"디버깅용 마스크 저장: {debug_mask_path}", level=20) + # except Exception as e: + # self.logger.log(f"디버깅용 마스크 저장 실패: {e}", level=40) + # return processed_mask + + return processed_mask + + def expand_polygon(self, polygon, offset=15): + poly = Polygon(polygon) + expanded = poly.buffer(offset) + if expanded.is_empty: + return np.array(polygon, dtype=np.int32) + return np.array(expanded.exterior.coords, dtype=np.int32) + + def process_mask(self, mask: np.ndarray, expansion_size: int = 5, blur_size: int = 3) -> np.ndarray: + processed_mask = mask.copy() + if expansion_size > 0: + kernel = np.ones((expansion_size, expansion_size), np.uint8) + processed_mask = cv2.dilate(processed_mask, kernel, iterations=1) + if blur_size > 0: + blur_size = blur_size if blur_size % 2 == 1 else blur_size + 1 + processed_mask = cv2.GaussianBlur(processed_mask, (blur_size, blur_size), 0) + return processed_mask + + + diff --git a/modules/migan_module.py b/modules/migan_module.py new file mode 100644 index 0000000..cce3b4c --- /dev/null +++ b/modules/migan_module.py @@ -0,0 +1,383 @@ +# -*- coding: utf-8 -*- +""" +src/modules/migan_module.py + +- MI-GAN ONNX 파이프라인(전/후처리 포함) +- 핵심 호환 포인트: + 1) 입력: image_path(str), mask(np.ndarray, 0~255 그레이, 텍스트영역=255) + 2) 마스크는 내부에서 자동으로 (이진화 -> 반전) 하여 MI-GAN 규칙(255=known, 0=hole)에 맞춤 + 3) CUDA/CPU 자동 선택, 실패 시 CPU 폴백 + +의존성: onnxruntime, opencv-python, numpy +설정: toggle_states에 다음 키 사용(선택): + - "migan_onnx_path": 파이프라인 ONNX 경로(필수) + - "migan_use_cuda": True/False (기본 False) + - "migan_intra_threads": int (기본 0 = onnxruntime 기본) + - "migan_inter_threads": int (기본 0) +""" + +import os +import sys +import time +import logging +from typing import Optional, Dict, Any + +import cv2 +import numpy as np +import onnxruntime as ort + +# OpenCV 내부 최적화 off +cv2.setUseOptimized(False) + + +def _np_uint8_2d(arr, name="mask"): + if arr is None: + raise ValueError(f"{name} is None") + if not isinstance(arr, np.ndarray): + raise TypeError(f"{name} must be np.ndarray, got {type(arr)}") + if arr.ndim != 2: + raise ValueError(f"{name} must be 2D, got shape={arr.shape}") + if arr.dtype != np.uint8: + # 안전 변환 + arr = arr.astype(np.uint8, copy=False) + return arr + + +def _ensure_logger(logger: Optional[object]) -> logging.Logger: + """네 logger( .log(msg, level=) )가 없을 때를 대비한 기본 로거""" + if logger and hasattr(logger, "log"): + return logger + pylogger = logging.getLogger("MIGAN") + if not pylogger.handlers: + pylogger.setLevel(logging.DEBUG) + h = logging.StreamHandler(stream=sys.stdout) + h.setFormatter(logging.Formatter("[%(asctime)s][%(levelname)s] %(message)s")) + pylogger.addHandler(h) + # .log 호환 어댑터 + class _Adapter: + def __init__(self, _lg): self._lg = _lg + def log(self, msg, level=logging.DEBUG, **kwargs): + self._lg.log(level, msg) + return _Adapter(pylogger) + + +class MIGANPipelineONNXCompat: + """ + MI-GAN ONNX 파이프라인 래퍼 + - 입력: image_path(str), mask(gray uint8 HxW) ※ 텍스트영역=255 + - 내부에서 mask를 (이진화→반전)하여 MI-GAN 규칙(255=known, 0=hole)으로 맞춤 + - 출력: BGR uint8(H,W,3) + """ + + _SESSION_CACHE = {} # onnx_path -> InferenceSession 캐시 + + def __init__(self, + onnx_path: str, + logger: Optional[object] = None, + use_cuda: bool = False, + intra_threads: int = 0, + inter_threads: int = 0, + toggle_states: Optional[Dict[str, Any]] = None, + base_dir: Optional[str] = None): + self.logger = _ensure_logger(logger) + self.onnx_path = onnx_path + self.use_cuda = bool(use_cuda) + self.intra_threads = int(intra_threads or 0) + self.inter_threads = int(inter_threads or 0) + self.toggle_states = toggle_states or {} + self.base_dir = base_dir + + # 프로바이더 캐시 파일 경로 결정 + try: + cache_root = None + if self.base_dir: + cache_root = os.path.join(self.base_dir, "user_data") + else: + cache_root = os.path.dirname(self.onnx_path) or os.getcwd() + os.makedirs(cache_root, exist_ok=True) + self._provider_cache_path = os.path.join(cache_root, "migan_provider.json") + except Exception: + self._provider_cache_path = None + + if not os.path.exists(self.onnx_path): + self.logger.log(f"[MIGAN] ONNX 파일을 찾을 수 없습니다: {self.onnx_path}", level=logging.ERROR) + raise FileNotFoundError(self.onnx_path) + + self.session = self._get_or_create_session() + ins = self.session.get_inputs() + outs = self.session.get_outputs() + self.in_image = ins[0].name + self.in_mask = ins[1].name + self.out_name = outs[0].name + + # 입력/출력 형태 정보 로깅 (디버깅용) + for i, inp in enumerate(ins): + self.logger.log(f"[MIGAN] 입력 {i}: {inp.name}, 형태: {inp.shape}, 타입: {inp.type}", level=logging.DEBUG) + for i, out in enumerate(outs): + self.logger.log(f"[MIGAN] 출력 {i}: {out.name}, 형태: {out.shape}, 타입: {out.type}", level=logging.DEBUG) + + self.logger.log(f"[MIGAN] 세션 준비 완료. providers={self.session.get_providers()}", level=logging.DEBUG) + + def _get_or_create_session(self) -> ort.InferenceSession: + key = (self.onnx_path, self.use_cuda, self.intra_threads, self.inter_threads) + if key in self._SESSION_CACHE: + return self._SESSION_CACHE[key] + + # override 우선 적용: auto|dml|cpu + try: + override = (self.toggle_states or {}).get("migan_provider_override", "auto") + if isinstance(override, str): + override = override.lower() + if override == "dml": + self.use_cuda = True + elif override == "cpu": + self.use_cuda = False + else: + # auto: 캐시 확인 + cached = self._read_provider_cache() + if cached == "dml": + self.use_cuda = True + elif cached == "cpu": + self.use_cuda = False + except Exception: + pass + + so = ort.SessionOptions() + if self.intra_threads > 0: + so.intra_op_num_threads = self.intra_threads + if self.inter_threads > 0: + so.inter_op_num_threads = self.inter_threads + + # 메모리 절약형 설정 (안전하게 속성 존재 여부 확인) + if hasattr(so, 'enable_cpu_mem_arena'): + so.enable_cpu_mem_arena = True # CPU 메모리 최적화 (유지) + if hasattr(so, 'enable_mem_pattern'): + so.enable_mem_pattern = False # 🔧 메모리 절약을 위해 비활성화 + if hasattr(so, 'graph_optimization_level'): + so.graph_optimization_level = ort.GraphOptimizationLevel.ORT_ENABLE_BASIC # 🔧 기본 최적화만 사용 + + # DirectML 관련 메모리 절약 최적화 (GPU 사용 시, 속성 존재 여부 확인) + if self.use_cuda or (hasattr(self, 'gpu_manager') and self.gpu_manager and self.gpu_manager.can_use_cuda): + if hasattr(so, 'enable_cuda_graph'): + so.enable_cuda_graph = False # 🔧 메모리 절약을 위해 비활성화 + + # GPU 관리자가 있으면 최적 provider 사용, 없으면 기존 방식 + gpu_manager_available = hasattr(self, 'gpu_manager') and self.gpu_manager + self.logger.log(f"[MIGAN] GPU 관리자 사용 가능: {gpu_manager_available}", level=logging.DEBUG) + + # DirectML 기반 GPU 가속 (Windows 호환성 최우선) + providers = [] + if self.use_cuda: # use_cuda를 GPU 가속 플래그로 사용 + try: + available_providers = ort.get_available_providers() + self.logger.log(f"[MIGAN] 사용 가능한 providers: {available_providers}", level=logging.DEBUG) + + if 'DmlExecutionProvider' in available_providers: + providers = [("DmlExecutionProvider", {}), ("CPUExecutionProvider", {})] + self.logger.log(f"[MIGAN] DirectML 가속 활성화", level=logging.INFO) + else: + providers = [("CPUExecutionProvider", {})] + self.logger.log(f"[MIGAN] DirectML 미지원, CPU 모드로 전환", level=logging.WARNING) + except Exception as e: + self.logger.log(f"[MIGAN] DirectML 설정 실패: {e}", level=logging.WARNING) + providers = [("CPUExecutionProvider", {})] + else: + providers = [("CPUExecutionProvider", {})] + self.logger.log(f"[MIGAN] CPU 전용 모드", level=logging.DEBUG) + + self.logger.log(f"[MIGAN] 최종 providers: {providers}", level=logging.DEBUG) + + # DirectML 기반 단계별 폴백 전략 + sess = None + if self.use_cuda: + fallback_attempts = [ + (providers, "DirectML 가속"), + ([("CPUExecutionProvider", {})], "CPU 폴백") + ] + else: + fallback_attempts = [ + ([("CPUExecutionProvider", {})], "CPU 전용") + ] + + for attempt_providers, attempt_name in fallback_attempts: + try: + self.logger.log(f"[MIGAN] {attempt_name} 시도: {attempt_providers}", level=logging.DEBUG) + sess = ort.InferenceSession(self.onnx_path, sess_options=so, providers=attempt_providers) + actual_providers = sess.get_providers() + self.logger.log(f"[MIGAN] {attempt_name} 성공! 실제 providers: {actual_providers}", level=logging.INFO) + break + except Exception as e: + self.logger.log(f"[MIGAN] {attempt_name} 실패: {e}", level=logging.WARNING) + if sess: + sess = None + continue + + if sess is None: + raise RuntimeError("[MIGAN] 모든 폴백 시도 실패") + + self._SESSION_CACHE[key] = sess + # 성공 프로바이더 캐시 + try: + used = sess.get_providers() + prov = 'dml' if any('Dml' in p for p in used) else 'cpu' + self._write_provider_cache(prov) + except Exception: + pass + return sess + + # ─────────────────────────────────────────────────────────────── + # 퍼블릭 API: 파이프라인에서 바로 호출 + # ─────────────────────────────────────────────────────────────── + def inpaint(self, image_path: str, mask_gray_255_text: np.ndarray) -> Optional[np.ndarray]: + """ + Args + image_path: 원본 이미지 경로 (BGR 로딩) + mask_gray_255_text: 0~255 그레이, '텍스트영역=255' 형태(네 MaskModule 출력) + (GaussianBlur 포함되어 있을 수 있음) + Return + inpainted BGR 이미지 (np.ndarray) or None + """ + try: + # 1) 입력 이미지 로드 + bgr = cv2.imread(image_path, cv2.IMREAD_COLOR) + if bgr is None: + self.logger.log(f"[MIGAN] 이미지 로드 실패: {image_path}", level=logging.ERROR) + return None + + H, W = bgr.shape[:2] + + # 2) 마스크 정규화: (이진화 → 반전) 해서 255=known, 0=hole 맞추기 + mask = _np_uint8_2d(mask_gray_255_text, name="mask") + if mask.shape != (H, W): + self.logger.log(f"[MIGAN] 마스크 크기 불일치: mask={mask.shape}, img={(H,W)}", level=logging.ERROR) + return None + + # 이진화: 128 스레시hold 기준 + _, mask_bin = cv2.threshold(mask, 128, 255, cv2.THRESH_BINARY) + # 너의 마스크는 “텍스트=255”, MI-GAN은 “hole=0”이므로 반전 + # (텍스트영역 255 -> 0), (배경 0 -> 255) + mask_known255 = 255 - mask_bin + + # 3) RGB 변환 (파이프라인 입력은 RGB uint8) + rgb = cv2.cvtColor(bgr, cv2.COLOR_BGR2RGB) + + # 4) ONNX 추론 - 배치 차원 추가 및 차원 순서 변경 + start = time.time() + # ONNX 모델 입력 형태: + # - image: (1, 3, H, W) - 배치, 채널, 높이, 너비 순서 + # - mask: (1, 1, H, W) - 배치, 채널(1), 높이, 너비 순서 + + # 이미지: (H, W, 3) -> (1, 3, H, W) + rgb_batch = np.expand_dims(rgb, 0).transpose(0, 3, 1, 2) + + # 마스크: (H, W) -> (1, 1, H, W) + mask_batch = np.expand_dims(mask_known255, (0, 1)) + + self.logger.log(f"[MIGAN] 입력 형태 - 이미지: {rgb_batch.shape}, 마스크: {mask_batch.shape}", level=logging.DEBUG) + + out = self.session.run( + [self.out_name], + {self.in_image: rgb_batch, self.in_mask: mask_batch} + )[0] # expect RGB uint8(1,3,H,W) + + # 출력 차원 처리: (1,3,H,W) -> (H,W,3) + if out.ndim == 4 and out.shape[0] == 1: + out = out[0].transpose(1, 2, 0) # (1,3,H,W) -> (3,H,W) -> (H,W,3) + elif out.ndim == 3 and out.shape[0] == 3: # (3,H,W) -> (H,W,3) + out = out.transpose(1, 2, 0) + + self.logger.log(f"[MIGAN] 출력 형태: {out.shape}, dtype: {out.dtype}", level=logging.DEBUG) + + if not isinstance(out, np.ndarray) or out.ndim != 3 or out.dtype != np.uint8: + self.logger.log(f"[MIGAN] ONNX 출력 형식 오류: type={type(out)}, shape={getattr(out,'shape',None)}, dtype={getattr(out,'dtype',None)}", + level=logging.ERROR) + return None + + elapsed = (time.time() - start) * 1000.0 + self.logger.log(f"[MIGAN] 추론 완료: {elapsed:.2f} ms", level=logging.DEBUG) + + # 5) BGR로 되돌려 반환 + bgr_out = cv2.cvtColor(out, cv2.COLOR_RGB2BGR) + return bgr_out + + except Exception as e: + error_msg = str(e).lower() + if "invalid rank" in error_msg or "invalid argument" in error_msg: + self.logger.log(f"[MIGAN] ONNX 입력 차원 오류: {e}", level=logging.ERROR) + self.logger.log(f"[MIGAN] 입력 이미지 형태: {rgb_batch.shape if 'rgb_batch' in locals() else 'N/A'}", level=logging.ERROR) + self.logger.log(f"[MIGAN] 입력 마스크 형태: {mask_batch.shape if 'mask_batch' in locals() else 'N/A'}", level=logging.ERROR) + else: + self.logger.log(f"[MIGAN] inpaint 예외: {e}", level=logging.ERROR, exc_info=True) + return None + + +# ─────────────────────────────────────────────────────────────── +# ImageProcessor3에서 바로 부를 수 있게 하는 편의 함수 +# ─────────────────────────────────────────────────────────────── +def build_migan_from_toggle(toggle_states: dict, logger: Optional[object] = None, gpu_manager: Optional[object] = None) -> MIGANPipelineONNXCompat: + """ + toggle_states로부터 설정을 읽어 MIGANPipelineONNXCompat 인스턴스를 생성. + 필수 키: + - migan_onnx_path + 선택 키: + - migan_use_cuda (bool) + - migan_intra_threads (int) + - migan_inter_threads (int) + """ + onnx_path = toggle_states.get("migan_onnx_path", "") + if not onnx_path: + raise ValueError("toggle_states['migan_onnx_path'] 가 필요합니다.") + use_accel = toggle_states.get("migan_use_accel", None) + if use_accel is None: + use_accel = toggle_states.get("migan_use_cuda", False) # 호환 키 + use_cuda = bool(use_accel) + intra = int(toggle_states.get("migan_intra_threads", 0) or 0) + inter = int(toggle_states.get("migan_inter_threads", 0) or 0) + pipeline = MIGANPipelineONNXCompat( + onnx_path=onnx_path, + logger=logger, + use_cuda=use_cuda, + intra_threads=intra, + inter_threads=inter, + toggle_states=toggle_states, + ) + # GPU 관리자를 파이프라인 객체에 연결 + if gpu_manager: + pipeline.gpu_manager = gpu_manager + if logger: + logger.log(f"[MIGAN] GPU 관리자 연결 완료: {type(gpu_manager).__name__}", level=logging.DEBUG) + else: + if logger: + logger.log(f"[MIGAN] GPU 관리자 없음: gpu_manager={gpu_manager}", level=logging.DEBUG) + + # 디버깅: gpu_manager 속성 확인 + if logger: + logger.log(f"[MIGAN] 파이프라인 gpu_manager 속성: {hasattr(pipeline, 'gpu_manager')}, 값: {getattr(pipeline, 'gpu_manager', None)}", level=logging.DEBUG) + + return pipeline + + # ─────────────────────────────────────────────────────────────── + # 내부 유틸: 프로바이더 캐시 + # ─────────────────────────────────────────────────────────────── + def _read_provider_cache(self): + try: + if not getattr(self, "_provider_cache_path", None) or not os.path.exists(self._provider_cache_path): + return None + import json + with open(self._provider_cache_path, 'r', encoding='utf-8') as f: + data = json.load(f) + prov = (data or {}).get('last_success_provider', '').lower() + return prov if prov in ('dml', 'cpu') else None + except Exception: + return None + + def _write_provider_cache(self, provider: str): + try: + if not getattr(self, "_provider_cache_path", None): + return + import json + os.makedirs(os.path.dirname(self._provider_cache_path), exist_ok=True) + with open(self._provider_cache_path, 'w', encoding='utf-8') as f: + json.dump({"last_success_provider": provider}, f, ensure_ascii=False) + except Exception: + pass diff --git a/modules/migan_onnx/migan_pipeline_v2.onnx b/modules/migan_onnx/migan_pipeline_v2.onnx new file mode 100644 index 0000000..3f75ade Binary files /dev/null and b/modules/migan_onnx/migan_pipeline_v2.onnx differ diff --git a/modules/migan_onnx/migan_pipeline_v2_simplified.onnx b/modules/migan_onnx/migan_pipeline_v2_simplified.onnx new file mode 100644 index 0000000..5267cfc Binary files /dev/null and b/modules/migan_onnx/migan_pipeline_v2_simplified.onnx differ diff --git a/modules/migan_traced.pt b/modules/migan_traced.pt new file mode 100644 index 0000000..88d2979 Binary files /dev/null and b/modules/migan_traced.pt differ diff --git a/modules/nssm/nssm.exe b/modules/nssm/nssm.exe new file mode 100644 index 0000000..8faee45 Binary files /dev/null and b/modules/nssm/nssm.exe differ diff --git a/modules/ocr_backends/__init__.py b/modules/ocr_backends/__init__.py new file mode 100644 index 0000000..d6d8b9f --- /dev/null +++ b/modules/ocr_backends/__init__.py @@ -0,0 +1,9 @@ +""" +OCR 백엔드 모듈 +플랫폼별로 최적화된 OCR 추론 엔진 제공 +""" + +from .fastdeploy_ocr import FastDeployOCR +from .onnx_ocr import ONNXRuntimeOCR + +__all__ = ['FastDeployOCR', 'ONNXRuntimeOCR'] diff --git a/modules/ocr_backends/fastdeploy_ocr.py b/modules/ocr_backends/fastdeploy_ocr.py new file mode 100644 index 0000000..a799f68 --- /dev/null +++ b/modules/ocr_backends/fastdeploy_ocr.py @@ -0,0 +1,170 @@ +""" +FastDeploy 기반 OCR 백엔드 +ARM 아키텍처에서 PaddleOCR 모델을 효율적으로 실행 +""" + +import os +import cv2 +import numpy as np +import logging +from typing import List, Tuple, Dict, Any + +class FastDeployOCR: + """FastDeploy를 사용한 PaddleOCR 호환 클래스""" + + def __init__(self, use_gpu=False, use_angle_cls=True, lang='ch', + det_model_dir=None, rec_model_dir=None, cls_model_dir=None, + logger=None, **kwargs): + self.logger = logger + self.use_gpu = use_gpu + self.use_angle_cls = use_angle_cls + self.lang = lang + + # FastDeploy 관련 설정 + self.det_model = None + self.rec_model = None + self.cls_model = None + + try: + import fastdeploy as fd + self.fd = fd + + # ARM 최적화 설정 + if use_gpu: + self.runtime_option = fd.RuntimeOption() + self.runtime_option.use_gpu() + else: + self.runtime_option = fd.RuntimeOption() + self.runtime_option.use_cpu() + # ARM 최적화 + self.runtime_option.set_cpu_thread_num(4) + + # 모델 초기화 + self._initialize_models(det_model_dir, rec_model_dir, cls_model_dir) + + if self.logger: + self.logger.log("✅ FastDeploy OCR 초기화 성공 (ARM 최적화)", level=logging.INFO) + + except ImportError: + if self.logger: + self.logger.log("❌ FastDeploy 모듈을 찾을 수 없습니다", level=logging.ERROR) + raise ImportError("FastDeploy가 설치되지 않았습니다: pip install fastdeploy-cpu") + except Exception as e: + if self.logger: + self.logger.log(f"❌ FastDeploy OCR 초기화 실패: {e}", level=logging.ERROR) + raise + + def _initialize_models(self, det_model_dir, rec_model_dir, cls_model_dir): + """FastDeploy 모델 초기화""" + try: + # Detection 모델 + if det_model_dir and os.path.exists(det_model_dir): + det_model_file = os.path.join(det_model_dir, "inference.pdmodel") + det_params_file = os.path.join(det_model_dir, "inference.pdiparams") + if os.path.exists(det_model_file) and os.path.exists(det_params_file): + self.det_model = self.fd.vision.ocr.DBDetector( + det_model_file, det_params_file, runtime_option=self.runtime_option + ) + + # Recognition 모델 + if rec_model_dir and os.path.exists(rec_model_dir): + rec_model_file = os.path.join(rec_model_dir, "inference.pdmodel") + rec_params_file = os.path.join(rec_model_dir, "inference.pdiparams") + if os.path.exists(rec_model_file) and os.path.exists(rec_params_file): + self.rec_model = self.fd.vision.ocr.Recognizer( + rec_model_file, rec_params_file, runtime_option=self.runtime_option + ) + + # Classification 모델 (선택적) + if self.use_angle_cls and cls_model_dir and os.path.exists(cls_model_dir): + cls_model_file = os.path.join(cls_model_dir, "inference.pdmodel") + cls_params_file = os.path.join(cls_model_dir, "inference.pdiparams") + if os.path.exists(cls_model_file) and os.path.exists(cls_params_file): + self.cls_model = self.fd.vision.ocr.Classifier( + cls_model_file, cls_params_file, runtime_option=self.runtime_option + ) + + except Exception as e: + if self.logger: + self.logger.log(f"FastDeploy 모델 초기화 실패: {e}", level=logging.ERROR) + raise + + def ocr(self, img, det=True, rec=True, cls=True): + """ + PaddleOCR과 호환되는 OCR 메서드 + + Args: + img: 입력 이미지 (numpy array 또는 PIL Image) + det: 텍스트 감지 여부 + rec: 텍스트 인식 여부 + cls: 텍스트 방향 분류 여부 + + Returns: + List[List]: PaddleOCR과 동일한 형식의 결과 + [[bbox, (text, confidence)], ...] + """ + try: + # 이미지 전처리 + if hasattr(img, 'save'): # PIL Image + img = cv2.cvtColor(np.array(img), cv2.COLOR_RGB2BGR) + elif isinstance(img, str): # 파일 경로 + img = cv2.imread(img) + + results = [] + + if not det and not rec: + return results + + # Detection 수행 + det_results = None + if det and self.det_model: + det_results = self.det_model.predict(img) + + if not rec: + # Detection만 수행하는 경우 + for box in det_results.boxes: + results.append([box.tolist()]) + return results + + # Recognition 수행 + if rec and self.rec_model: + if det_results and len(det_results.boxes) > 0: + # Detection + Recognition + for i, box in enumerate(det_results.boxes): + # 바운딩 박스에서 텍스트 영역 추출 + x_coords = box[:, 0] + y_coords = box[:, 1] + x_min, x_max = int(min(x_coords)), int(max(x_coords)) + y_min, y_max = int(min(y_coords)), int(max(y_coords)) + + # 영역 추출 + text_region = img[y_min:y_max, x_min:x_max] + + if text_region.size > 0: + # Classification (선택적) + if cls and self.cls_model: + cls_result = self.cls_model.predict(text_region) + # 필요시 이미지 회전 + + # Recognition 수행 + rec_result = self.rec_model.predict(text_region) + + if hasattr(rec_result, 'text') and hasattr(rec_result, 'score'): + results.append([box.tolist(), (rec_result.text, rec_result.score)]) + else: + results.append([box.tolist(), ("", 0.0)]) + else: + # Recognition만 수행하는 경우 + rec_result = self.rec_model.predict(img) + if hasattr(rec_result, 'text') and hasattr(rec_result, 'score'): + # 전체 이미지에 대한 결과 + h, w = img.shape[:2] + bbox = [[0, 0], [w, 0], [w, h], [0, h]] + results.append([bbox, (rec_result.text, rec_result.score)]) + + return results + + except Exception as e: + if self.logger: + self.logger.log(f"FastDeploy OCR 추론 실패: {e}", level=logging.ERROR) + return [] diff --git a/modules/ocr_backends/onnx_ocr.py b/modules/ocr_backends/onnx_ocr.py new file mode 100644 index 0000000..a63619e --- /dev/null +++ b/modules/ocr_backends/onnx_ocr.py @@ -0,0 +1,280 @@ +""" +ONNX Runtime 기반 OCR 백엔드 +PaddleOCR 모델을 ONNX 형식으로 변환하여 ARM에서 실행 +""" + +import os +import cv2 +import numpy as np +import logging +from typing import List, Tuple, Dict, Any + +class ONNXRuntimeOCR: + """ONNX Runtime을 사용한 PaddleOCR 호환 클래스""" + + def __init__(self, use_gpu=False, use_angle_cls=True, lang='ch', + det_model_dir=None, rec_model_dir=None, cls_model_dir=None, + logger=None, **kwargs): + self.logger = logger + self.use_gpu = use_gpu + self.use_angle_cls = use_angle_cls + self.lang = lang + + # ONNX 모델 세션 + self.det_session = None + self.rec_session = None + self.cls_session = None + + try: + import onnxruntime as ort + self.ort = ort + + # ARM 최적화 설정 + providers = ['CPUExecutionProvider'] + if use_gpu: + # ARM에서 GPU 지원이 제한적이므로 CPU 사용 권장 + providers = ['CPUExecutionProvider'] + + # 세션 옵션 설정 (ARM 최적화) + self.sess_options = ort.SessionOptions() + self.sess_options.inter_op_num_threads = 4 # ARM 최적화 + self.sess_options.intra_op_num_threads = 4 + self.sess_options.graph_optimization_level = ort.GraphOptimizationLevel.ORT_ENABLE_ALL + + # 모델 로드 + self._load_onnx_models(det_model_dir, rec_model_dir, cls_model_dir, providers) + + if self.logger: + self.logger.log("✅ ONNX Runtime OCR 초기화 성공 (ARM 최적화)", level=logging.INFO) + + except ImportError: + if self.logger: + self.logger.log("❌ ONNX Runtime 모듈을 찾을 수 없습니다", level=logging.ERROR) + raise ImportError("ONNX Runtime이 설치되지 않았습니다: pip install onnxruntime") + except Exception as e: + if self.logger: + self.logger.log(f"❌ ONNX Runtime OCR 초기화 실패: {e}", level=logging.ERROR) + raise + + def _load_onnx_models(self, det_model_dir, rec_model_dir, cls_model_dir, providers): + """ONNX 모델 로드""" + try: + # Detection 모델 + if det_model_dir: + det_onnx_path = os.path.join(det_model_dir, "model.onnx") + if os.path.exists(det_onnx_path): + self.det_session = self.ort.InferenceSession( + det_onnx_path, + sess_options=self.sess_options, + providers=providers + ) + self.det_input_name = self.det_session.get_inputs()[0].name + self.det_output_names = [output.name for output in self.det_session.get_outputs()] + + # Recognition 모델 + if rec_model_dir: + rec_onnx_path = os.path.join(rec_model_dir, "model.onnx") + if os.path.exists(rec_onnx_path): + self.rec_session = self.ort.InferenceSession( + rec_onnx_path, + sess_options=self.sess_options, + providers=providers + ) + self.rec_input_name = self.rec_session.get_inputs()[0].name + self.rec_output_names = [output.name for output in self.rec_session.get_outputs()] + + # Classification 모델 + if self.use_angle_cls and cls_model_dir: + cls_onnx_path = os.path.join(cls_model_dir, "model.onnx") + if os.path.exists(cls_onnx_path): + self.cls_session = self.ort.InferenceSession( + cls_onnx_path, + sess_options=self.sess_options, + providers=providers + ) + self.cls_input_name = self.cls_session.get_inputs()[0].name + self.cls_output_names = [output.name for output in self.cls_session.get_outputs()] + + except Exception as e: + if self.logger: + self.logger.log(f"ONNX 모델 로드 실패: {e}", level=logging.ERROR) + raise + + def _preprocess_det_image(self, img): + """Detection 전처리""" + # PaddleOCR Detection 전처리 로직 + h, w = img.shape[:2] + + # 크기 조정 (보통 960x960 또는 640x640) + target_size = 960 + ratio = target_size / max(h, w) + new_h, new_w = int(h * ratio), int(w * ratio) + + img_resized = cv2.resize(img, (new_w, new_h)) + + # 패딩 추가 + padded_img = np.zeros((target_size, target_size, 3), dtype=np.uint8) + padded_img[:new_h, :new_w] = img_resized + + # 정규화 (RGB 변환 및 스케일링) + img_rgb = cv2.cvtColor(padded_img, cv2.COLOR_BGR2RGB) + img_norm = img_rgb.astype(np.float32) / 255.0 + + # 채널 순서 변경 (HWC -> CHW) + img_chw = np.transpose(img_norm, (2, 0, 1)) + + # 배치 차원 추가 + img_batch = np.expand_dims(img_chw, axis=0) + + return img_batch, ratio + + def _postprocess_det_output(self, output, ratio, original_shape): + """Detection 후처리""" + # ONNX 출력을 PaddleOCR 형식으로 변환 + boxes = [] + + # 임계값 적용 및 컨투어 찾기 (간단한 구현) + # 실제로는 더 복잡한 후처리가 필요 + if len(output) > 0 and len(output[0]) > 0: + heatmap = output[0][0] # 첫 번째 출력 채널 + + # 이진화 + _, binary = cv2.threshold((heatmap * 255).astype(np.uint8), 127, 255, cv2.THRESH_BINARY) + + # 컨투어 찾기 + contours, _ = cv2.findContours(binary, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) + + h_orig, w_orig = original_shape[:2] + + for contour in contours: + if cv2.contourArea(contour) < 100: # 최소 영역 필터 + continue + + # 바운딩 박스 계산 + rect = cv2.minAreaRect(contour) + box = cv2.boxPoints(rect) + + # 좌표 스케일 복원 + box[:, 0] = box[:, 0] / ratio + box[:, 1] = box[:, 1] / ratio + + # 이미지 경계 클리핑 + box[:, 0] = np.clip(box[:, 0], 0, w_orig) + box[:, 1] = np.clip(box[:, 1], 0, h_orig) + + boxes.append(box) + + return boxes + + def ocr(self, img, det=True, rec=True, cls=True): + """ + PaddleOCR과 호환되는 OCR 메서드 + """ + try: + # 이미지 전처리 + if hasattr(img, 'save'): # PIL Image + img = cv2.cvtColor(np.array(img), cv2.COLOR_RGB2BGR) + elif isinstance(img, str): # 파일 경로 + img = cv2.imread(img) + + results = [] + + if not det and not rec: + return results + + # Detection 수행 + boxes = [] + if det and self.det_session: + try: + # 전처리 + det_input, ratio = self._preprocess_det_image(img) + + # 추론 + det_outputs = self.det_session.run( + self.det_output_names, + {self.det_input_name: det_input} + ) + + # 후처리 + boxes = self._postprocess_det_output(det_outputs, ratio, img.shape) + + if not rec: + # Detection만 수행하는 경우 + for box in boxes: + results.append([box.tolist()]) + return results + + except Exception as e: + if self.logger: + self.logger.log(f"ONNX Detection 실패: {e}", level=logging.WARNING) + boxes = [] + + # Recognition 수행 + if rec and self.rec_session: + if boxes: + # Detection + Recognition + for box in boxes: + try: + # 텍스트 영역 추출 + x_coords = box[:, 0] if isinstance(box, np.ndarray) else [p[0] for p in box] + y_coords = box[:, 1] if isinstance(box, np.ndarray) else [p[1] for p in box] + x_min, x_max = int(min(x_coords)), int(max(x_coords)) + y_min, y_max = int(min(y_coords)), int(max(y_coords)) + + text_region = img[y_min:y_max, x_min:x_max] + + if text_region.size > 0: + # Recognition 전처리 (간단한 구현) + rec_img = cv2.resize(text_region, (100, 32)) # 표준 크기 + rec_img = rec_img.astype(np.float32) / 255.0 + rec_img = np.transpose(rec_img, (2, 0, 1)) + rec_input = np.expand_dims(rec_img, axis=0) + + # Recognition 추론 + rec_outputs = self.rec_session.run( + self.rec_output_names, + {self.rec_input_name: rec_input} + ) + + # 간단한 후처리 (실제로는 더 복잡함) + text = "detected_text" # 실제 구현 필요 + confidence = 0.8 + + box_list = box.tolist() if isinstance(box, np.ndarray) else box + results.append([box_list, (text, confidence)]) + + except Exception as e: + if self.logger: + self.logger.log(f"ONNX Recognition 실패: {e}", level=logging.WARNING) + continue + else: + # Recognition만 수행하는 경우 + try: + # 전체 이미지 Recognition + rec_img = cv2.resize(img, (100, 32)) + rec_img = rec_img.astype(np.float32) / 255.0 + rec_img = np.transpose(rec_img, (2, 0, 1)) + rec_input = np.expand_dims(rec_img, axis=0) + + rec_outputs = self.rec_session.run( + self.rec_output_names, + {self.rec_input_name: rec_input} + ) + + text = "full_image_text" # 실제 구현 필요 + confidence = 0.8 + + h, w = img.shape[:2] + bbox = [[0, 0], [w, 0], [w, h], [0, h]] + results.append([bbox, (text, confidence)]) + + except Exception as e: + if self.logger: + self.logger.log(f"ONNX 전체 이미지 Recognition 실패: {e}", level=logging.WARNING) + + return results + + except Exception as e: + if self.logger: + self.logger.log(f"ONNX OCR 추론 실패: {e}", level=logging.ERROR) + return [] diff --git a/modules/ocr_module.py b/modules/ocr_module.py new file mode 100644 index 0000000..51fba76 --- /dev/null +++ b/modules/ocr_module.py @@ -0,0 +1,643 @@ +# -*- coding: utf-8 -*- +""" +OCR 모듈 - PaddleOCR3을 사용한 텍스트 감지 +폴리곤 방식으로 텍스트 영역을 감지합니다. +""" + +import cv2 +import numpy as np +import os +import logging +from typing import List, Dict, Any + +class OCRModule: + def __init__(self, logger=None, base_dir=None, gpu_manager=None, force_cpu=False): + self.logger = logger + self.base_dir = base_dir + self.gpu_manager = gpu_manager + self.force_cpu = force_cpu # CPU 강제 사용 플래그 + + # CPU 강제 사용 모드 확인 + if force_cpu: + os.environ['CUDA_VISIBLE_DEVICES'] = '' + if self.logger: + self.logger.log("⚠️ OCR 모듈 CPU 강제 모드 (CUDNN 버전 불일치 회피)", level=logging.WARNING) + # GPU 관리자가 있고 CUDA를 사용할 수 있는 경우 + elif gpu_manager and gpu_manager.can_use_cuda: + # CUDA 사용 허용 + if self.logger: + self.logger.log("OCR 모듈 CUDA 모드 활성화", level=logging.INFO) + # CUDA_VISIBLE_DEVICES 설정 제거 (모든 GPU 사용 가능) + if 'CUDA_VISIBLE_DEVICES' in os.environ: + del os.environ['CUDA_VISIBLE_DEVICES'] + else: + # CPU만 사용하도록 환경 변수 설정 + os.environ['CUDA_VISIBLE_DEVICES'] = '' + if self.logger: + self.logger.log("OCR 모듈 CPU 모드로 설정", level=logging.INFO) + # OpenCV OpenCL 비활성화 (메모리 primitive 오류 예방) + try: + import cv2 as _cv2 + _cv2.ocl.setUseOpenCL(False) + except Exception: + pass + + # 멀티 스레드 사용 시 오류 방지 + os.environ["OMP_NUM_THREADS"] = "1" + os.environ["KMP_DUPLICATE_LIB_OK"] = "True" + + # 멀티 스레드 사용 시 오류 방지 + os.environ['OPENBLAS_NUM_THREADS'] = '1' + os.environ['MKL_NUM_THREADS'] = '1' + os.environ['NUMEXPR_NUM_THREADS'] = '1' + + self.ocr = None + + # OCR 초기화 시도 + self.ocr = self.initialize_ocr() + if self.ocr is None: + if self.logger: + self.logger.log("PaddleOCR 초기화 실패", level=logging.ERROR, exc_info=True) + raise Exception("PaddleOCR 초기화 실패") + else: + # 초기화 성공 로깅 + cuda_status = "CUDA" if (self.gpu_manager and self.gpu_manager.can_use_cuda) else "CPU" + if self.logger: + self.logger.log(f"✅ PaddleOCR 초기화 성공 ({cuda_status} 모드)", level=logging.INFO) + + def initialize_ocr(self): + """ + 플랫폼별 최적화된 OCR 초기화 + - x86: 기존 PaddleOCR 사용 + - ARM: FastDeploy 또는 ONNX Runtime 사용 + """ + # 모델 디렉토리 설정 + self.rec_model_dir = os.path.join(self.base_dir, "modules", "PP_Models", "rec") + self.det_model_dir = os.path.join(self.base_dir, "modules", "PP_Models", "det") + self.cls_model_dir = os.path.join(self.base_dir, "modules", "PP_Models", "cls") + + # 아키텍처 감지 + try: + from src.utils.platform_utils import is_arm, get_architecture + is_arm_arch = is_arm() + arch_name = get_architecture() + + if self.logger: + self.logger.log(f"🔍 감지된 아키텍처: {arch_name} ({'ARM' if is_arm_arch else 'x86'})", level=logging.INFO) + except ImportError: + import platform + arch = platform.machine().lower() + is_arm_arch = arch in ('arm64', 'aarch64', 'arm', 'armv7l') + arch_name = arch + + # GPU 상태 확인 (force_cpu 플래그 우선) + if self.force_cpu: + use_gpu_for_ocr = False + if self.logger: + self.logger.log("🚫 CPU 강제 모드: GPU 사용 비활성화", level=logging.WARNING) + else: + use_gpu_for_ocr = self.gpu_manager and self.gpu_manager.can_use_cuda + + try: + if is_arm_arch: + # ARM에서는 대체 백엔드 사용 + return self._initialize_arm_ocr(use_gpu_for_ocr) + else: + # x86에서는 기존 PaddleOCR 사용 + return self._initialize_x86_ocr(use_gpu_for_ocr) + + except Exception as e: + if self.logger: + self.logger.log(f"OCR 초기화 실패, 폴백 시도: {e}", level=logging.WARNING) + + # 폴백: 기존 PaddleOCR 시도 + try: + return self._initialize_x86_ocr(use_gpu_for_ocr) + except Exception as fallback_e: + if self.logger: + self.logger.log(f"폴백 OCR 초기화도 실패: {fallback_e}", level=logging.ERROR) + raise + + def _initialize_arm_ocr(self, use_gpu): + """ARM 아키텍처용 OCR 초기화""" + if self.logger: + self.logger.log("🔧 ARM 최적화 OCR 백엔드 초기화 중...", level=logging.INFO) + + # 우선순위: FastDeploy > ONNX Runtime > PaddleOCR + + # 1. FastDeploy 시도 + try: + from src.modules.ocr_backends import FastDeployOCR + + ocr = FastDeployOCR( + use_gpu=use_gpu, + use_angle_cls=True, + lang='ch', + det_model_dir=self.det_model_dir if os.path.exists(self.det_model_dir) else None, + rec_model_dir=self.rec_model_dir if os.path.exists(self.rec_model_dir) else None, + cls_model_dir=self.cls_model_dir if os.path.exists(self.cls_model_dir) else None, + logger=self.logger + ) + + if self.logger: + self.logger.log("✅ FastDeploy OCR 백엔드 사용 (ARM 최적화)", level=logging.INFO) + return ocr + + except (ImportError, Exception) as e: + if self.logger: + self.logger.log(f"FastDeploy 초기화 실패: {e}", level=logging.WARNING) + + # 2. ONNX Runtime 시도 + try: + from src.modules.ocr_backends import ONNXRuntimeOCR + + ocr = ONNXRuntimeOCR( + use_gpu=False, # ARM에서는 CPU 사용 권장 + use_angle_cls=True, + lang='ch', + det_model_dir=self.det_model_dir if os.path.exists(self.det_model_dir) else None, + rec_model_dir=self.rec_model_dir if os.path.exists(self.rec_model_dir) else None, + cls_model_dir=self.cls_model_dir if os.path.exists(self.cls_model_dir) else None, + logger=self.logger + ) + + if self.logger: + self.logger.log("✅ ONNX Runtime OCR 백엔드 사용 (ARM 최적화)", level=logging.INFO) + return ocr + + except (ImportError, Exception) as e: + if self.logger: + self.logger.log(f"ONNX Runtime 초기화 실패: {e}", level=logging.WARNING) + + # 3. 마지막 폴백: PaddleOCR (성능 저하 가능) + if self.logger: + self.logger.log("⚠️ ARM에서 PaddleOCR 폴백 사용 (성능 저하 가능)", level=logging.WARNING) + return self._initialize_x86_ocr(False) # ARM에서는 CPU만 사용 + + def _initialize_x86_ocr(self, use_gpu): + """x86 아키텍처용 기존 PaddleOCR 초기화""" + try: + from paddleocr import PaddleOCR + + # GPU 모드 시도 + if use_gpu: + try: + if self.logger: + self.logger.log("🔄 PaddleOCR GPU 모드 초기화 시도...", level=logging.INFO) + + ocr = PaddleOCR( + use_gpu=True, + use_angle_cls=True, + lang="ch", + det_model_dir=self.det_model_dir, + rec_model_dir=self.rec_model_dir, + cls_model_dir=self.cls_model_dir, + rec_batch_num=4, + cpu_threads=2 + ) + + # GPU 초기화 테스트 (간단한 이미지로 테스트) + try: + import numpy as np + test_img = np.zeros((100, 100, 3), dtype=np.uint8) + _ = ocr.ocr(test_img) + + if self.logger: + self.logger.log("✅ PaddleOCR GPU 모드 초기화 성공", level=logging.INFO) + return ocr + except Exception as test_error: + # GPU 테스트 실패 - CPU로 폴백 + error_msg = str(test_error).lower() + if 'cudnn' in error_msg or 'cuda' in error_msg or 'gpu' in error_msg: + if self.logger: + self.logger.log(f"⚠️ GPU 테스트 실패 (CUDA 라이브러리 문제), CPU 모드로 전환: {test_error}", level=logging.WARNING) + del ocr # GPU 모드 객체 삭제 + use_gpu = False # CPU 모드로 전환 + else: + raise + + except Exception as gpu_error: + error_msg = str(gpu_error).lower() + if 'cudnn' in error_msg or 'cuda' in error_msg or 'preconditionnotmet' in error_msg: + if self.logger: + self.logger.log(f"⚠️ GPU 초기화 실패 (CUDA 라이브러리 문제), CPU 모드로 전환: {gpu_error}", level=logging.WARNING) + use_gpu = False + else: + raise + + # CPU 모드 초기화 + if not use_gpu: + if self.logger: + self.logger.log("🔄 PaddleOCR CPU 모드 초기화...", level=logging.INFO) + + # CPU 전용 환경 변수 설정 + os.environ['CUDA_VISIBLE_DEVICES'] = '' + + ocr = PaddleOCR( + use_gpu=False, + use_angle_cls=True, + lang="ch", + det_model_dir=self.det_model_dir, + rec_model_dir=self.rec_model_dir, + cls_model_dir=self.cls_model_dir, + rec_batch_num=4, + cpu_threads=2 + ) + + if self.logger: + self.logger.log("✅ PaddleOCR CPU 모드 초기화 성공", level=logging.INFO) + + return ocr + + except Exception as e: + if self.logger: + self.logger.log(f"❌ PaddleOCR 초기화 실패: {e}", level=logging.ERROR, exc_info=True) + return None + + + def detect_text(self, image_path: str, method: str = 'polygon', raise_on_memory_error: bool = False) -> List[Dict[str, Any]]: + """ + 이미지에서 텍스트를 감지하고 다양한 방식으로 영역 반환 + + Args: + image_path (str): 이미지 파일 경로 + method (str): 감지 방식 ('polygon', 'bbox', 'expanded_bbox', 'rotated_bbox', 'contour') + + Returns: + List[Dict]: 감지된 텍스트 정보 리스트 + - text: 감지된 텍스트 + - confidence: 신뢰도 + - polygon: 폴리곤 좌표 (4개 점) + - bbox: 바운딩 박스 좌표 (x, y, w, h) + - method: 사용된 감지 방식 + """ + if not os.path.exists(image_path): + self.logger.log(f"이미지 파일을 찾을 수 없습니다: {image_path}", level=logging.ERROR, exc_info=True) + return [] + + image = None + try: + # 이미지 읽기 + image = cv2.imread(image_path) + if image is None: + self.logger.log(f"이미지를 읽을 수 없습니다: {image_path}", level=logging.ERROR, exc_info=True) + return [] + + self.logger.log(f"🔍 OCR 감지 방식: {method}", level=logging.INFO) + + # ---- 메모리 안전: 극단적 가로/세로 비율 조정 -------------------- + try: + h, w = image.shape[:2] + max_dim_safe = 2000 # OCR 입력 가로/세로 상한선 + aspect_ratio = max(w, h) / max(1, min(w, h)) + if max(w, h) > max_dim_safe or aspect_ratio > 15: + scale = float(max_dim_safe) / float(max(w, h)) + new_size = (int(w * scale), int(h * scale)) + self.logger.log( + f"⚖️ OCR용 다운스케일 ({w}x{h}) -> {new_size} (ratio={aspect_ratio:.1f})", + level=logging.INFO, + ) + # .copy() 사용으로 뷰 문제 해결 + image = cv2.resize(image, new_size, interpolation=cv2.INTER_AREA).copy() + except Exception as e: + self.logger.log(f"OCR 입력 스케일링 중 오류: {e}", level=logging.WARNING) + + # 실제 OCR 실행 + try: + ocr_raw_results = self.ocr.ocr(image) + except Exception as e: + err_msg = str(e).lower() + + # CUDA/CUDNN 오류 감지 + cuda_signals = ['cudnn', 'cuda', 'gpu', 'preconditionnotmet'] + if any(s in err_msg for s in cuda_signals): + self.logger.log( + f"⚠️ OCR 처리 중 CUDA 오류 발생: {e}", + level=logging.WARNING, + ) + self.logger.log( + "💡 PaddleOCR이 GPU 모드로 초기화되었지만 실행 중 CUDA 라이브러리 문제가 발생했습니다. " + "프로그램을 재시작하면 자동으로 CPU 모드로 전환됩니다.", + level=logging.WARNING, + ) + return [] + + # 메모리 오류 감지 및 재시도 + mem_signals = [ + 'unable to allocate', + 'out of memory', + 'could not create a memory object', + 'create a primitive', + 'cv::outofmemoryerror', + ] + if isinstance(e, MemoryError) or any(s in err_msg for s in mem_signals): + try: + h, w = image.shape[:2] + self.logger.log( + f"🔁 OCR 메모리 오류 재시도: 이미지 1/2 축소 ({w}x{h}) → ({w//2}x{h//2})", + level=logging.WARNING, + ) + # .copy() 사용으로 뷰 문제 해결 + image_small = cv2.resize(image, (max(1, w // 2), max(1, h // 2)), interpolation=cv2.INTER_AREA).copy() + ocr_raw_results = self.ocr.ocr(image_small) + # 임시 이미지 해제 + del image_small + except Exception as e2: + try: + # 2차 재시도 전 강제 GC + import gc + gc.collect() + + self.logger.log( + f"🔁 2차 재시도(1/4 축소): {e2}", level=logging.WARNING, + ) + # .copy() 사용으로 뷰 문제 해결 + image_small2 = cv2.resize(image, (max(1, w // 4), max(1, h // 4)), interpolation=cv2.INTER_AREA).copy() + ocr_raw_results = self.ocr.ocr(image_small2) + # 임시 이미지 해제 + del image_small2 + except Exception as e3: + self.logger.log( + f"❌ OCR 재시도 실패: {e3}", level=logging.ERROR, exc_info=True, + ) + if raise_on_memory_error: + raise MemoryError(f"OCR memory/primitive failure after retries: {e3}") + return [] + else: + raise + + if not ocr_raw_results or len(ocr_raw_results) == 0 or all(x is None for x in ocr_raw_results): + self.logger.log("⚠️ OCR 결과가 비어있거나 모두 None입니다. 중국어 텍스트가 없는 이미지입니다.", level=logging.WARNING) + return [] + + self.logger.log(f"ocr_raw_results: {ocr_raw_results}", level=logging.INFO) + for line in ocr_raw_results: + self.logger.log(f"line: {line}", level=logging.INFO) + + # paddleocr 2.x 결과 파싱 + converted_results = [] + for page in ocr_raw_results: # page는 텍스트별 결과 리스트 + for line in page: + poly = line[0] + text = line[1][0] + score = line[1][1] + converted_results.append([poly, [text, score]]) + + # 감지 방식에 따라 결과 처리 + if method == 'polygon': + ocr_results = self._detect_with_polygon(image, converted_results) + elif method == 'bbox': + ocr_results = self._detect_with_bbox(image, converted_results) + elif method == 'expanded_bbox': + ocr_results = self._detect_with_expanded_bbox(image, converted_results) + elif method == 'rotated_bbox': + ocr_results = self._detect_with_rotated_bbox(image, converted_results) + elif method == 'contour': + ocr_results = self._detect_with_contour(image, converted_results) + else: + self.logger.log(f"⚠️ 지원하지 않는 감지 방식: {method}, 기본 polygon 방식 사용", level=logging.WARNING) + ocr_results = self._detect_with_polygon(image, converted_results) + + return ocr_results + + except Exception as e: + self.logger.log(f"OCR 처리 중 오류: {e}", level=logging.ERROR, exc_info=True) + if raise_on_memory_error and ('memory' in str(e).lower() or 'primitive' in str(e).lower()): + raise MemoryError(f"OCR memory/primitive error: {e}") + return [] + finally: + # 명시적 메모리 해제 + if image is not None: + del image + cv2.destroyAllWindows() # OpenCV 윈도우 정리 + + import gc + gc.collect() + + def filter_chinese_text(self, ocr_results: List[Dict]) -> List[Dict]: + """ + 중국어 텍스트만 필터링 + + Args: + ocr_results (List[Dict]): OCR 결과 + + Returns: + List[Dict]: 중국어 텍스트만 포함된 결과 + """ + chinese_results = [] + + for result in ocr_results: + text = result['text'] + # 중국어 문자 범위 확인 (간체/번체 포함) + if any('\u4e00' <= char <= '\u9fff' for char in text): + chinese_results.append(result) + + self.logger.log(f"중국어 텍스트 {len(chinese_results)}개 필터링 완료", level=logging.INFO) + return chinese_results + + def filter_korean_text(self, ocr_results: List[Dict]) -> List[Dict]: + """ + 한글 텍스트만 필터링 + + Args: + ocr_results (List[Dict]): OCR 결과 + + Returns: + List[Dict]: 한글 텍스트만 포함된 결과 + """ + korean_results = [] + for result in ocr_results: + text = result['text'] + # 한글 유니코드 범위: 가~힣 + if any('\uac00' <= char <= '\ud7a3' for char in text): + korean_results.append(result) + + self.logger.log(f"한글 텍스트 {len(korean_results)}개 필터링 완료", level=logging.INFO) + return korean_results + + def _detect_with_polygon(self, image: np.ndarray, ocr_raw_results: List) -> List[Dict[str, Any]]: + """폴리곤 방식으로 텍스트 영역 감지 (기본 방식)""" + ocr_results = [] + + for line in ocr_raw_results: + if len(line) >= 2: + polygon = line[0] # 폴리곤 좌표 (4개 점) + text_info = line[1] # (텍스트, 신뢰도) + + if len(text_info) >= 2: + text = text_info[0] + confidence = text_info[1] + + # 폴리곤을 바운딩 박스로 변환 + polygon_np = np.array(polygon, dtype=np.int32) + x, y, w, h = cv2.boundingRect(polygon_np) + + ocr_result = { + 'text': text, + 'confidence': confidence, + 'polygon': polygon, + 'bbox': (x, y, w, h), + 'method': 'polygon' + } + ocr_results.append(ocr_result) + + return ocr_results + + def _detect_with_bbox(self, image: np.ndarray, ocr_raw_results: List) -> List[Dict[str, Any]]: + """바운딩 박스 방식으로 텍스트 영역 감지""" + ocr_results = [] + + for line in ocr_raw_results: + if len(line) >= 2: + polygon = line[0] + text_info = line[1] + + if len(text_info) >= 2: + text = text_info[0] + confidence = text_info[1] + + # 바운딩 박스 계산 + polygon_np = np.array(polygon, dtype=np.int32) + x, y, w, h = cv2.boundingRect(polygon_np) + + # 바운딩 박스를 폴리곤으로 변환 + bbox_polygon = [ + [x, y], + [x + w, y], + [x + w, y + h], + [x, y + h] + ] + + ocr_result = { + 'text': text, + 'confidence': confidence, + 'polygon': bbox_polygon, + 'bbox': (x, y, w, h), + 'method': 'bbox' + } + ocr_results.append(ocr_result) + + return ocr_results + + def _detect_with_expanded_bbox(self, image: np.ndarray, ocr_raw_results: List) -> List[Dict[str, Any]]: + """확장된 바운딩 박스 방식으로 텍스트 영역 감지""" + ocr_results = [] + h_img, w_img = image.shape[:2] + + for line in ocr_raw_results: + if len(line) >= 2: + polygon = line[0] + text_info = line[1] + + if len(text_info) >= 2: + text = text_info[0] + confidence = text_info[1] + + # 기본 바운딩 박스 + polygon_np = np.array(polygon, dtype=np.int32) + x, y, w, h = cv2.boundingRect(polygon_np) + + # 확장 크기 계산 (텍스트 크기의 20%) + expand_x = max(1, int(w * 0.2)) + expand_y = max(1, int(h * 0.2)) + + # 확장된 바운딩 박스 + x_exp = max(0, x - expand_x) + y_exp = max(0, y - expand_y) + w_exp = min(w_img - x_exp, w + 2 * expand_x) + h_exp = min(h_img - y_exp, h + 2 * expand_y) + + # 확장된 바운딩 박스를 폴리곤으로 변환 + expanded_polygon = [ + [x_exp, y_exp], + [x_exp + w_exp, y_exp], + [x_exp + w_exp, y_exp + h_exp], + [x_exp, y_exp + h_exp] + ] + + ocr_result = { + 'text': text, + 'confidence': confidence, + 'polygon': expanded_polygon, + 'bbox': (x_exp, y_exp, w_exp, h_exp), + 'method': 'expanded_bbox' + } + ocr_results.append(ocr_result) + + return ocr_results + + def _detect_with_rotated_bbox(self, image: np.ndarray, ocr_raw_results: List) -> List[Dict[str, Any]]: + """회전된 바운딩 박스 방식으로 텍스트 영역 감지""" + ocr_results = [] + + for line in ocr_raw_results: + if len(line) >= 2: + polygon = line[0] + text_info = line[1] + + if len(text_info) >= 2: + text = text_info[0] + confidence = text_info[1] + + # 회전된 바운딩 박스 계산 + polygon_np = np.array(polygon, dtype=np.float32) + rect = cv2.minAreaRect(polygon_np) + box = cv2.boxPoints(rect) + box = np.int32(box) + + # 일반 바운딩 박스도 계산 + x, y, w, h = cv2.boundingRect(polygon_np.astype(np.int32)) + + ocr_result = { + 'text': text, + 'confidence': confidence, + 'polygon': box.tolist(), + 'bbox': (x, y, w, h), + 'method': 'rotated_bbox', + 'rotation_info': { + 'center': rect[0], + 'size': rect[1], + 'angle': rect[2] + } + } + ocr_results.append(ocr_result) + + return ocr_results + + def _detect_with_contour(self, image: np.ndarray, ocr_raw_results: List) -> List[Dict[str, Any]]: + """컨투어 방식으로 텍스트 영역 감지""" + ocr_results = [] + + for line in ocr_raw_results: + if len(line) >= 2: + polygon = line[0] + text_info = line[1] + + if len(text_info) >= 2: + text = text_info[0] + confidence = text_info[1] + + # 폴리곤을 컨투어로 변환 + polygon_np = np.array(polygon, dtype=np.int32) + + # 컨투어 근사화 + epsilon = 0.02 * cv2.arcLength(polygon_np, True) + approx_contour = cv2.approxPolyDP(polygon_np, epsilon, True) + + # 컨투어를 다시 폴리곤으로 변환 + contour_polygon = approx_contour.reshape(-1, 2).tolist() + + # 바운딩 박스 계산 + x, y, w, h = cv2.boundingRect(polygon_np) + + ocr_result = { + 'text': text, + 'confidence': confidence, + 'polygon': contour_polygon, + 'bbox': (x, y, w, h), + 'method': 'contour', + 'contour_points': len(contour_polygon) + } + ocr_results.append(ocr_result) + + return ocr_results diff --git a/modules/old_modules/background_removal_module.py b/modules/old_modules/background_removal_module.py new file mode 100644 index 0000000..6c21bb4 --- /dev/null +++ b/modules/old_modules/background_removal_module.py @@ -0,0 +1,369 @@ +import os +import cv2 +from PIL import Image +import logging +import onnxruntime # [추가] ONNX 런타임 직접 사용을 위해 임포트 +from rembg.sessions import sessions # [수정] rembg의 모델별 세션 클래스 딕셔너리 import + + +class BackgroundRemovalModule: + """ + rembg 기반 배경제거 모듈 (안전한 의존성 처리) + """ + + # [수정] 사용하시려는 birefnet 모델을 지원 목록에 추가합니다. + SUPPORTED_MODELS = { + "u2net": "범용 배경제거 | 빠름 | 사람/사물 모두 양호 (기본값)", + "u2netp": "u2net 경량화 | 매우 빠름 | 실시간, 저사양PC", + "u2net_human_seg": "인물 전용 | 빠름 | 사람 경계 정밀", + "u2net_cloth_seg": "옷 전용 | 빠름 | 패션/의류 특화", + "isnet-general-use": "범용 고품질 | 느림 | 디테일 중시, 대용량", + "sam": "SAM 최고 품질 | 매우 느림 | 고성능PC 권장", + "sam-mobile": "SAM 경량화 | 보통 | 모바일, 중간성능", + "birefnet-general-lite": "BiRefNet 경량 모델 | 고품질 저용량 (로컬)" + } + + # [추가] SUPPORTED_MODELS 키와 실제 rembg sessions 키 간의 매핑 + MODEL_NAME_MAPPING = { + "u2net": "u2net", + "u2netp": "u2netp", + "u2net_human_seg": "u2net-human-seg", + "u2net_cloth_seg": "u2net-cloth-seg", + "isnet-general-use": "dis-general-use", + "sam": "sam", + "sam-mobile": "sam", # sam-mobile은 sam과 동일하게 처리 + "birefnet-general-lite": "birefnet-general-lite" + } + + def __init__(self, logger=None, default_model="u2net", gpu_manager=None, local_rembg_model_path: str | None = None): + self.logger = logger + self.default_model = default_model + self.gpu_manager = gpu_manager + self.local_rembg_model_path = local_rembg_model_path + self.sessions = {} + self._rembg_available = None + self._init_error = None + self._cuda_providers_tested = False + + # _check_rembg_availability 메서드는 변경할 필요 없습니다. (기존 코드 유지) + def _check_rembg_availability(self): + """rembg 모듈 사용 가능 여부를 확인하고 캐시""" + if self._rembg_available is not None: + return self._rembg_available + + try: + import rembg + + providers_to_test = [] + if self.gpu_manager: + try: + import onnxruntime as ort + available_providers = ort.get_available_providers() + if 'DmlExecutionProvider' in available_providers: + providers_to_test = ['DmlExecutionProvider', 'CPUExecutionProvider'] + if self.logger: + self.logger.log("rembg DirectML provider 테스트 시작", level=logging.INFO) + else: + providers_to_test = ['CPUExecutionProvider'] + if self.logger: + self.logger.log("rembg DirectML 미지원, CPU 모드", level=logging.WARNING) + except Exception as e: + providers_to_test = ['CPUExecutionProvider'] + if self.logger: + self.logger.log(f"rembg DirectML 확인 실패: {e}, CPU 모드", level=logging.WARNING) + else: + providers_to_test = ['CPUExecutionProvider'] + if self.logger: + self.logger.log("rembg CPU-only 모드로 테스트", level=logging.INFO) + + model_arg = 'u2net' + + test_session = rembg.new_session(model_name=model_arg, providers=providers_to_test) + self._rembg_available = True + + if hasattr(test_session, 'inner_session') and hasattr(test_session.inner_session, 'get_providers'): + actual_providers = test_session.inner_session.get_providers() + if self.logger: + self.logger.log(f"rembg 세션 생성 성공, 사용된 providers: {actual_providers}", level=logging.INFO) + if 'DmlExecutionProvider' in actual_providers: + self.logger.log("✅ rembg DirectML 가속 활성화됨", level=logging.INFO) + else: + self.logger.log("rembg CPU 모드로 동작", level=logging.INFO) + else: + if self.logger: + self.logger.log("rembg 모듈 사용 가능 확인됨", level=logging.INFO) + + return True + + except ImportError as e: + self._init_error = f"rembg 모듈이 설치되지 않음: {e}" + self._rembg_available = False + except Exception as e: + self._init_error = f"rembg 모듈 초기화 실패 (의존성/하드웨어 문제): {e}" + self._rembg_available = False + + if self.logger: + self.logger.log(self._init_error, level=logging.ERROR) + return False + + # =================================================================================== + # [핵심 수정] get_session 메서드를 아래 코드로 완전히 교체하세요. + # =================================================================================== + def get_session(self, model_name): + """ + 모델별 세션을 캐싱하여 반환 (로컬 모델 경로 및 CUDA 지원 포함) + """ + if not self._check_rembg_availability(): + if self.logger: + self.logger.log(f"rembg 사용 불가로 세션 생성 실패: {self._init_error}", level=logging.ERROR) + return None + + cuda_enabled = self.gpu_manager and self.gpu_manager.can_use_cuda + # 세션 키에 로컬 모델 사용 여부도 반영 + is_local = bool(self.local_rembg_model_path and os.path.exists(self.local_rembg_model_path)) + # 실제 모델명을 세션 키에 사용 + actual_model_name = self.MODEL_NAME_MAPPING.get(model_name, model_name) + session_key = f"{actual_model_name}_cuda_{cuda_enabled}_local_{is_local}" + + if session_key not in self.sessions: + if self.logger: + self.logger.log(f"🔧 rembg 새 세션 생성 필요: {session_key}", level=logging.INFO) + try: + import rembg + + # 안정적인 DirectML 기반 provider 설정 + providers = [] + if cuda_enabled: # cuda_enabled을 GPU 가속 플래그로 사용 + try: + import onnxruntime as ort + available_providers = ort.get_available_providers() + if 'DmlExecutionProvider' in available_providers: + # DirectML 설정 (메모리 절약형) + dml_options = { + 'device_id': 0, # 기본 GPU 장치 사용 + 'disable_metacommands': False, # 메타커맨드 활성화 + 'enable_dynamic_graph_fusion': False, # 🔧 메모리 절약을 위해 비활성화 + 'memory_type': 'default' # 기본 메모리 타입 사용 + } + providers = [ + ('DmlExecutionProvider', dml_options), + ('CPUExecutionProvider', {}) + ] + if self.logger: self.logger.log(f"rembg 세션 생성 (DirectML 최적화): {model_name}", level=logging.INFO) + else: + providers = [('CPUExecutionProvider', {})] + if self.logger: self.logger.log(f"rembg DirectML 미지원, CPU 모드: {model_name}", level=logging.WARNING) + except Exception as e: + providers = [('CPUExecutionProvider', {})] + if self.logger: self.logger.log(f"rembg DirectML 확인 실패: {e}, CPU 모드: {model_name}", level=logging.WARNING) + else: + providers = [('CPUExecutionProvider', {})] + if self.logger: self.logger.log(f"rembg 세션 생성 (CPU): {model_name}", level=logging.INFO) + + session = None + + # *** 로컬 모델 경로가 있으면 여기서 처리 *** + if is_local: + if self.logger: + self.logger.log(f"로컬 모델로 세션 생성: {self.local_rembg_model_path}", level=logging.INFO) + + # 1. model_name에 맞는 rembg 세션 *클래스*를 가져옴 (e.g., BiRefNetSessionGeneral) + # 모델명 매핑을 통해 실제 sessions 키로 변환 + actual_model_name = self.MODEL_NAME_MAPPING.get(model_name, model_name) + if actual_model_name not in sessions: + raise ValueError(f"지원하지 않는 모델명: {model_name} (매핑된 이름: {actual_model_name}). 사용 가능한 모델: {list(sessions.keys())}") + session_class = sessions[actual_model_name] + + # 2. 클래스의 인스턴스를 생성하되, __init__을 호출하지 않아 모델 다운로드를 방지 + session = session_class.__new__(session_class) + + # 3. 로컬 ONNX 파일로 직접 onnxruntime 세션을 생성 (DirectML) + ort_session = None + if cuda_enabled: + fallback_attempts = [ + (providers, "DirectML 가속"), + (["CPUExecutionProvider"], "CPU 폴백") + ] + else: + fallback_attempts = [ + (["CPUExecutionProvider"], "CPU 전용") + ] + + for attempt_providers, attempt_name in fallback_attempts: + if not attempt_providers: # 빈 리스트 건너뛰기 + continue + try: + if self.logger: + self.logger.log(f"rembg 로컬 모델 {attempt_name} 시도: {attempt_providers}", level=logging.DEBUG) + + # ONNX Runtime 세션 옵션 설정 (메모리 절약형) + sess_options = onnxruntime.SessionOptions() + sess_options.enable_mem_pattern = False # 🔧 메모리 절약을 위해 비활성화 + sess_options.enable_cpu_mem_arena = True # CPU 메모리 아레나 (유지) + sess_options.graph_optimization_level = onnxruntime.GraphOptimizationLevel.ORT_ENABLE_BASIC # 🔧 기본 최적화만 사용 + + # DirectML 사용시 메모리 절약 설정 + if any('DmlExecutionProvider' in str(provider) for provider in attempt_providers): + sess_options.enable_profiling = False # 프로파일링 비활성화 (안정성) + sess_options.execution_mode = onnxruntime.ExecutionMode.ORT_SEQUENTIAL # 순차 실행 + + ort_session = onnxruntime.InferenceSession( + self.local_rembg_model_path, + providers=attempt_providers, + sess_options=sess_options + ) + actual_providers = ort_session.get_providers() + if self.logger: + self.logger.log(f"✅ rembg 로컬 모델 {attempt_name} 성공! 실제 providers: {actual_providers} ", level=logging.INFO) + break + except Exception as e: + if self.logger: + self.logger.log(f"rembg 로컬 모델 {attempt_name} 실패: {e}", level=logging.WARNING) + continue + + if ort_session is None: + raise RuntimeError("rembg 로컬 모델 모든 폴백 시도 실패") + + session.inner_session = ort_session + + # 4. 세션에 필요한 다른 속성들을 수동으로 설정 + session.model_name = model_name + session.providers = providers + + # 로컬 모델 경로가 없으면 기존 방식으로 처리 + else: + if self.logger: self.logger.log(f"내장 모델로 세션 생성: {actual_model_name}", level=logging.INFO) + session = rembg.new_session(model_name=actual_model_name, providers=providers) + + self.sessions[session_key] = session + + # 실제 사용된 provider 확인 및 로깅 (DirectML 지원 수정) + actual_providers = session.inner_session.get_providers() + if self.logger: + is_gpu = any('CUDA' in p or 'Tensorrt' in p or 'DmlExecutionProvider' in p for p in actual_providers) + status = "GPU 가속 활성화" if is_gpu else "CPU 모드로 동작" + self.logger.log(f"✅ rembg '{actual_model_name}' {status} (실제 providers: {actual_providers})", level=logging.INFO) + + except Exception as e: + if self.logger: + self.logger.log(f"rembg 세션 생성 실패 ('{actual_model_name}'): {e}", level=logging.ERROR, exc_info=True) + return None + else: + if self.logger: + self.logger.log(f"♻️ rembg 기존 세션 재사용: {session_key}", level=logging.DEBUG) + + return self.sessions.get(session_key) + + # =================================================================================== + # 이하 다른 메서드들은 수정할 필요 없습니다. (기존 코드 유지) + # =================================================================================== + + def set_default_model(self, model_name): + if model_name not in self.SUPPORTED_MODELS: + raise ValueError(f"지원하지 않는 모델명: {model_name}") + self.default_model = model_name + if self.logger: + self.logger.log(f"rembg 기본 모델이 '{model_name}'(으)로 변경됨", level=logging.INFO) + + def get_default_model(self): + return self.default_model + + def get_supported_models(self): + return self.SUPPORTED_MODELS.copy() + + def get_model_description(self, model_name): + return self.SUPPORTED_MODELS.get(model_name, "모델 설명 없음") + + def is_available(self): + return self._check_rembg_availability() + + def get_init_error(self): + return self._init_error + + def to_white_background(self, img: Image.Image) -> Image.Image: + if img.mode in ("RGBA", "BGRA"): + bg = Image.new("RGB", img.size, (255, 255, 255)) + bg.paste(img, mask=img.split()[-1]) + return bg + else: + return img.convert("RGB") + + def remove_background(self, image_path, model_name=None, **kwargs): + if not self._check_rembg_availability(): + if self.logger: + self.logger.log(f"rembg 사용 불가로 배경 제거 실패: {self._init_error}", level=logging.ERROR) + return None + + if not os.path.exists(image_path): + if self.logger: + self.logger.log(f"입력 이미지가 존재하지 않습니다: {image_path}", level=logging.ERROR) + return None + + try: + img = cv2.imread(image_path) + if img is None: + if self.logger: + self.logger.log(f"이미지 로드 실패: {image_path}", level=logging.ERROR) + return None + + img_rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) + + # [수정] 로컬 모델을 사용하려면, model_name을 파일과 맞는 이름으로 지정해야 합니다. + # 예를 들어, birefnet-general-lite.onnx 파일을 사용하려면 model_name='birefnet-general-lite'로 호출해야 합니다. + effective_model_name = model_name or self.default_model + + if effective_model_name not in self.SUPPORTED_MODELS: + if self.logger: + self.logger.log(f"지원하지 않는 모델명: {effective_model_name}. u2net으로 대체 사용", level=logging.WARNING) + effective_model_name = "u2net" + + session = self.get_session(effective_model_name) + if session is None: + return None + + import rembg + import time + + start_time = time.time() + result = rembg.remove(img_rgb, session=session, alpha_matting=kwargs.get("alpha_matting", False)) + end_time = time.time() + + if not isinstance(result, Image.Image): + result = Image.fromarray(result) + + processing_time = end_time - start_time + if self.logger: + cuda_status = "CUDA" if (self.gpu_manager and self.gpu_manager.can_use_cuda) else "CPU" + self.logger.log(f"✅ 배경 제거 성공: {effective_model_name} ({cuda_status}, {processing_time:.2f}초)", level=logging.INFO) + + if self.gpu_manager and self.gpu_manager.can_use_cuda: + self.gpu_manager.log_gpu_memory_usage() + + return result + + except Exception as e: + if self.logger: + self.logger.log(f"배경 제거 처리 중 오류 ({model_name}): {e}", level=logging.ERROR, exc_info=True) + return None + + def _preload_sessions(self): + """자주 사용되는 rembg 세션들을 미리 로딩하여 첫 번째 요청 시간을 단축""" + preload_models = ["u2net", "birefnet-general-lite"] + + for model_name in preload_models: + try: + if self.logger: + self.logger.log(f"🔄 {model_name} 세션 미리 로딩 중...", level=logging.INFO) + + # 세션 생성하여 캐시에 저장 + session = self.get_session(model_name) + if session: + if self.logger: + self.logger.log(f"✅ {model_name} 세션 미리 로딩 완료", level=logging.INFO) + else: + if self.logger: + self.logger.log(f"⚠️ {model_name} 세션 로딩 실패", level=logging.WARNING) + + except Exception as e: + if self.logger: + self.logger.log(f"❌ {model_name} 세션 미리 로딩 중 오류: {e}", level=logging.WARNING) diff --git a/modules/old_modules/background_removal_module_pp.py b/modules/old_modules/background_removal_module_pp.py new file mode 100644 index 0000000..fd279df --- /dev/null +++ b/modules/old_modules/background_removal_module_pp.py @@ -0,0 +1,102 @@ +import os +import cv2 +import numpy as np +from PIL import Image +import logging + +class PPMattingBackgroundRemovalModule: + """ + PaddleHub ppmatting 기반 배경제거 모듈 + - CPU/GPU 모두 지원 + - 최초 1회 모델 자동 다운로드 + - 인물, 사물 등 모두 고품질 분리 가능 + """ + + SUPPORTED_MODELS = { + "modnet_mobilenetv2_matting": "가장 가볍고 빠름 | 매우 빠름(0.5~1s/이미지) | 실시간 썸네일, 대량 처리", + "modnet_resnet50vd_matting": "균형형, 성능 우수 | 빠름~보통(1~2s/이미지) | 상품 이미지, 경계 중요", + "modnet_hrnet18_matting": "품질 우수, 중간 속도 | 보통(2~3s/이미지) | 인물/상품 정밀 분리", + "gfm_resnet34_matting": "균형 맞춘 품질+속도 | 빠름~보통 | 일반 상품/사물 이미지", + "dim_vgg16_matting": "중간 품질, 중간 속도 | 보통 | 예제/비교 테스트용", + } + def __init__(self, logger=None, default_model="modnet_resnet50vd_matting"): + self.logger = logger + self.default_model = default_model + self.modules = {} # 모델별 paddlehub 모듈 캐시 + + def get_module(self, model_name): + """ + paddlehub ppmatting 모델 캐싱 후 반환 + """ + if model_name not in self.modules: + import paddlehub as hub + self.modules[model_name] = hub.Module(name=model_name) + if self.logger: + self.logger.log(f"ppmatting 모델 로드됨: {model_name}") + return self.modules[model_name] + + def set_default_model(self, model_name): + """ + 기본 배경제거 모델명 변경 + """ + if model_name not in self.SUPPORTED_MODELS: + raise ValueError(f"지원하지 않는 모델명: {model_name}") + self.default_model = model_name + if self.logger: + self.logger.log(f"기본 배경제거 모델이 '{model_name}'(으)로 변경됨") + + def get_default_model(self): + return self.default_model + + def get_supported_models(self): + return self.SUPPORTED_MODELS.copy() + + def get_model_description(self, model_name): + return self.SUPPORTED_MODELS.get(model_name, "모델 설명 없음") + + def to_white_background(self, img: Image.Image) -> Image.Image: + """ + 알파(투명) 부분을 흰색으로 합성해서 RGB 이미지로 반환 + Args: + img: PIL.Image (RGBA or BGRA) + Returns: + PIL.Image (RGB, 배경이 흰색) + """ + if img.mode in ("RGBA", "BGRA"): + bg = Image.new("RGB", img.size, (255, 255, 255)) + bg.paste(img, mask=img.split()[-1]) # 알파채널로 합성 + return bg + else: + return img.convert("RGB") + + def remove_background(self, image_path, model_name=None): + if not os.path.exists(image_path): + self.logger.log(f"입력 이미지 없음: {image_path}", level=logging.ERROR); return None + img = cv2.imread(image_path) + if img is None: + self.logger.log(f"이미지 로드 실패: {image_path}", level=logging.ERROR); return None + img_rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) + model_name = model_name or self.default_model + if model_name not in self.SUPPORTED_MODELS: + self.logger.log(f"지원되지 않는 모델명: {model_name}", level=logging.ERROR); return None + try: + module = self.get_module(model_name) + results = module.predict( + image_list=[img_rgb], + trimap_list=None, + visualization=False, + save_path=None + ) + self.logger.log(f"results type: {type(results)}, len={len(results)}", level=logging.DEBUG) + alpha = results[0] + if not isinstance(alpha, np.ndarray) or alpha.ndim != 2: + self.logger.log("매팅 결과가 유효한 알파마스크가 아닙니다", level=logging.ERROR); return None + + alpha_img = alpha if alpha.dtype == np.uint8 else (alpha * 255).astype(np.uint8) + img_bgra = cv2.cvtColor(img, cv2.COLOR_BGR2BGRA) + img_bgra[...,3] = alpha_img + pil_img = Image.fromarray(cv2.cvtColor(img_bgra, cv2.COLOR_BGRA2RGBA)) + return pil_img + except Exception as e: + self.logger.log(f"배경제거 예외 발생: {e}", level=logging.ERROR, exc_info=True) + return None diff --git a/modules/old_modules/clipboardImageManager copy.py b/modules/old_modules/clipboardImageManager copy.py new file mode 100644 index 0000000..ba29a96 --- /dev/null +++ b/modules/old_modules/clipboardImageManager copy.py @@ -0,0 +1,770 @@ +import base64 +import pyperclip +import win32clipboard +from io import BytesIO +from PIL import Image, ImageGrab, ImageFont, ImageDraw +import requests +import numpy as np +import cv2 +import time +import os, sys +from datetime import datetime +import random +import pywinauto +import io +import logging + +class ClipboardImageManager: + def __init__(self, logger, watermark_font_size=36, debug_flag=False): + self.logger = logger + self.debug = debug_flag # 디버그 플래그를 클래스 변수로 사용 + + # 프로그램이 위치한 경로 기준으로 폰트 경로 설정 + self.base_path = self.get_base_dir() + # 먼저 현재 모듈과 같은 디렉토리에서 폰트 파일 찾기 + current_dir = os.path.dirname(os.path.abspath(__file__)) + self.font_path = os.path.join(current_dir, 'HakgyoansimDunggeunmisoTTFB.ttf') + + # 폰트 파일이 없으면 다른 경로들을 시도 + if not os.path.exists(self.font_path): + alternative_paths = [ + os.path.join(self.base_path, 'HakgyoansimDunggeunmisoTTFB.ttf'), + os.path.join(self.base_path, 'src', 'modules', 'HakgyoansimDunggeunmisoTTFB.ttf'), + os.path.join(os.path.dirname(self.base_path), 'src', 'modules', 'HakgyoansimDunggeunmisoTTFB.ttf') + ] + + for alt_path in alternative_paths: + if os.path.exists(alt_path): + self.font_path = alt_path + break + + # 폰트 로드 (예외 처리 추가) + try: + self.font = ImageFont.truetype(self.font_path, watermark_font_size) + self.logger.log(f"폰트 로드 성공: {self.font_path}", level=logging.DEBUG) + except Exception as e: + self.logger.log(f"커스텀 폰트 로드 실패 ({self.font_path}): {e}", level=logging.WARNING) + try: + # 기본 폰트 사용 + self.font = ImageFont.load_default() + self.logger.log("기본 폰트를 사용합니다.", level=logging.INFO) + except Exception as e2: + self.logger.log(f"기본 폰트 로드도 실패: {e2}", level=logging.ERROR) + # 최후의 수단으로 None 설정 + self.font = None + + # self.debug = True + + def reset_state(self): + """클립보드 이미지 관리자의 상태를 초기화합니다.""" + self.logger.log("ClipboardImageManager 상태 초기화", level=logging.DEBUG) + # 클립보드 비우기 + self.clear_clipboard() + + def get_base_dir(self): + """ + 실행 환경에 따라 base_dir을 설정하는 메서드. + cx_Freeze로 패키징된 경우 실행 파일의 경로, 일반 Python 환경일 경우 __file__을 기준으로 설정. + """ + if getattr(sys, 'frozen', False): # 패키징된 경우 + base_dir = os.path.dirname(sys.executable) + internal_dir = os.path.join(base_dir, 'lib', 'src') # lib 디렉토리 포함 + if os.path.exists(internal_dir): # lib 디렉토리가 존재하면 base_dir로 설정 + return internal_dir + + else: # 일반 Python 실행 환경 + base_dir = os.path.dirname(os.path.abspath(__file__)) + debug_dir = os.path.join(base_dir, 'src') # lib 디렉토리 포함 + + return debug_dir + + + def get_clipboard_data(self): + """클립보드의 텍스트 또는 이미지 데이터를 가져옵니다.""" + self.logger.log("클립보드의 텍스트 또는 이미지 데이터를 가져옵니다", level=logging.DEBUG) + + max_attempts = 5 + attempt = 0 + + while attempt < max_attempts: + try: + # 1. 텍스트 데이터 우선 시도 + clipboard_text = pyperclip.paste() + if clipboard_text: + return clipboard_text + + # 2. 텍스트가 없으면 이미지 확인 + self.logger.log("텍스트 데이터가 없어 이미지 데이터 확인 시도", level=logging.DEBUG) + image = ImageGrab.grabclipboard() + if isinstance(image, Image.Image): # 이미지 데이터가 있는 경우 + self.logger.log("클립보드에 이미지 데이터가 확인되었습니다.", level=logging.DEBUG) + return image # PIL 이미지 객체 반환 + else: + self.logger.log("클립보드에 텍스트 또는 이미지 데이터가 없습니다.", level=logging.DEBUG) + return None + + except Exception as e: + attempt += 1 + self.logger.log(f"클립보드 데이터를 가져오는 중 오류 발생 (시도 {attempt}/{max_attempts}): {e}", level=logging.WARNING) + if attempt < max_attempts: + time.sleep(0.5) # 0.5초 대기 후 재시도 + else: + self.logger.log(f"클립보드 데이터를 가져오는 중 최대 시도 횟수 초과: {e}", level=logging.ERROR, exc_info=True) + return None + + def set_image_to_clipboard(self, image): + """이미지를 클립보드에 넣는 함수 (Windows 전용)""" + output = BytesIO() + image.save(output, "BMP") + self.logger.log(f"이미지 데이터 BMP 변환", level=logging.DEBUG) + + data = output.getvalue()[14:] # BMP 헤더 제거 + output.close() + self.logger.log(f"이미지 BMP 헤더 제거", level=logging.DEBUG) + + # 클립보드 접근 재시도 로직 + max_attempts = 5 + attempt = 0 + success = False + + while attempt < max_attempts and not success: + try: + # 클립보드에 이미지 데이터 넣기 + win32clipboard.OpenClipboard() + win32clipboard.EmptyClipboard() + win32clipboard.SetClipboardData(win32clipboard.CF_DIB, data) + win32clipboard.CloseClipboard() + success = True + self.logger.log(f"클립보드 데이터 저장 성공 (시도 {attempt+1}/{max_attempts})", level=logging.DEBUG) + except Exception as e: + attempt += 1 + self.logger.log(f"클립보드 데이터 저장 실패 (시도 {attempt}/{max_attempts}): {e}", level=logging.WARNING) + if attempt < max_attempts: + time.sleep(0.5) # 0.5초 대기 후 재시도 + + # 클립보드가 제대로 설정되었는지 확인하는 로그 + if success: + try: + time.sleep(0.1) # 아주 짧은 대기 시간 + win32clipboard.OpenClipboard() + if win32clipboard.IsClipboardFormatAvailable(win32clipboard.CF_DIB): + self.logger.log("클립보드 데이터 확인 성공", level=logging.DEBUG) + else: + self.logger.log("클립보드 데이터 확인 실패", level=logging.ERROR) + win32clipboard.CloseClipboard() + except Exception as e: + self.logger.log(f"클립보드 데이터 확인 중 오류: {e}", level=logging.ERROR) + + def save_image_to_path(self, image, path): + try: + if image: + # 이미지를 저장 경로에 저장 + self.logger.log(f"이미지 저장 완료 : {path}", level=logging.INFO) + image.save(path, format='PNG') + return path + + except Exception as e: + raise RuntimeError(f"이미지 저장 중 오류 발생: {e}") + + def add_watermark(self, image, watermark_text="Watermark", opacity_percent=30, angle=30, font_size=36): + """ + 이미지에 텍스트 워터마크를 이미지 전체에 걸쳐서 추가하는 함수 + :param image: PIL 이미지 객체 + :param watermark_text: 워터마크로 추가할 텍스트 + :param opacity_percent: 워터마크의 투명도 (0~100) + :param angle: 워터마크 텍스트 회전 각도 (기본 30도) + :param font_size: 워터마크 텍스트의 폰트 크기 (기본 36) + :return: 워터마크가 추가된 이미지 + """ + # 폰트가 로드되지 않은 경우 원본 이미지 반환 + if self.font is None: + self.logger.log("폰트가 로드되지 않아 워터마크를 추가할 수 없습니다. 원본 이미지를 반환합니다.", level=logging.WARNING) + return image + + # 이미지 복사본 생성 + watermark_image = image.copy() + + # 폰트 설정 (안전한 폰트 로딩) + try: + # self.font가 있으면 크기만 조정해서 새 폰트 생성 + if hasattr(self, 'font_path') and os.path.exists(self.font_path): + font = ImageFont.truetype(self.font_path, font_size) + else: + # 크기를 조정할 수 없으면 기존 폰트 사용 + font = self.font + except Exception as e: + self.logger.log(f"폰트 크기 조정 실패: {e}. 기본 폰트를 사용합니다.", level=logging.WARNING) + font = self.font + + # 텍스트 투명도를 0~255로 변환 + opacity = int(255 * (opacity_percent / 100)) + + # 텍스트 크기 측정 (textbbox 사용) + draw = ImageDraw.Draw(watermark_image) + bbox = draw.textbbox((0, 0), watermark_text, font=font) + text_width, text_height = bbox[2] - bbox[0], bbox[3] - bbox[1] + + # 텍스트 크기가 0인 경우 예외 처리 (빈 문자열 등) + if text_width == 0 or text_height == 0: + self.logger.log("워터마크 텍스트 크기를 계산할 수 없어 워터마크를 건너뜁니다.", level=logging.WARNING) + return image + + # 이미지 크기 + width, height = image.size + + # 워터마크 레이어 생성 + watermark_layer = Image.new("RGBA", (width, height)) # RGBA 이미지 생성 + + # 지그재그 간격 설정 (0 방지를 위해 최소 1 보장) + zigzag_step = max(1, int(text_height * 2)) # Y축의 지그재그 간격 + + step_x = max(1, int(text_width * 3)) # X축 반복 간격도 0 방지 + + # 이미지 전체에 반복적으로 워터마크 텍스트 그리기 (지그재그 형태) + for y in range(0, height, zigzag_step): + for x in range(0, width, step_x): # 3배 너비 간격으로 반복 + # 텍스트가 한 줄씩 지그재그 형태로 X축을 교차하여 이동 + x_offset = (y // zigzag_step) % 2 * int(text_width * 1.5) # 짝수 행에서는 X축을 약간 이동 + + # 텍스트 레이어 생성 + text_layer = Image.new("RGBA", (text_width, text_height), (255, 255, 255, 0)) + text_draw = ImageDraw.Draw(text_layer) + + # 텍스트 그리기 + text_draw.text((0, 0), watermark_text, fill=(255, 255, 255, opacity), font=font) + + # 텍스트 회전 + rotated_text_layer = text_layer.rotate(angle, expand=1) + + # 회전된 텍스트를 워터마크 레이어에 추가 + watermark_layer.paste(rotated_text_layer, (x + x_offset, y), rotated_text_layer) + + # 원본 이미지와 워터마크 레이어 합성 + watermark_image = Image.alpha_composite(watermark_image.convert("RGBA"), watermark_layer) + + # 최종적으로 RGB 형식으로 변환 후 반환 + return watermark_image.convert("RGB") + + def base64_to_image(self, base64_data): + """Base64 데이터를 이미지로 변환하는 함수""" + if base64_data.startswith('data:image'): + header, encoded = base64_data.split(',', 1) + img_data = base64.b64decode(encoded) + image = Image.open(BytesIO(img_data)) + return image + else: + self.logger.log("유효하지 않은 Base64 이미지 데이터입니다.", level=logging.DEBUG) + return None + + def image_to_base64(self, image): + # 이미지 Base64로 변환 + buffer = io.BytesIO() + image.save(buffer, format="PNG") + base64_image = base64.b64encode(buffer.getvalue()).decode('utf-8') + return base64_image + + def download_image_from_url(self, url, max_retries=3): + """URL에서 이미지를 다운로드하고 PIL 이미지 객체로 반환""" + headers = { + "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.150 Safari/537.36", + "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9", + "Accept-Language": "en-US,en;q=0.9", + "Accept-Encoding": "gzip, deflate, br", + "DNT": "1", # Do Not Track 요청 헤더 + "Connection": "keep-alive", + "Upgrade-Insecure-Requests": "1", + "Cache-Control": "max-age=0" + } + + retries = 0 + while retries < max_retries: + try: + self.logger.log(f"이미지 URL 다운로드 중: {url}", level=logging.DEBUG) + response = requests.get(url, headers=headers, stream=True) + + # 상태 코드가 200이 아니면 재시도 + if response.status_code == 200: + # OpenCV로 이미지를 로드하여 변환 + image = np.asarray(bytearray(response.content), dtype="uint8") + image = cv2.imdecode(image, cv2.IMREAD_COLOR) + + # OpenCV에서 이미지를 PIL로 변환 + if image is not None: + pil_image = Image.fromarray(cv2.cvtColor(image, cv2.COLOR_BGR2RGB)) + return pil_image + else: + self.logger.log(f"이미지 파일 형식이 올바르지 않습니다. 대상 URL: {url}", level=logging.DEBUG) + return None + else: + self.logger.log(f"이미지 로딩 실패, HTTP 상태 코드: {response.status_code}. 재시도 {retries + 1}/{max_retries}", level=logging.DEBUG) + retries += 1 + # await asyncio.sleep(random.randint(2, 5)) # 2~5초 대기 후 재시도 + time.sleep(random.randint(2, 5)) + + except Exception as e: + self.logger.log(f"이미지 로딩 중 오류 발생: {e}. 재시도 {retries + 1}/{max_retries}", level=logging.DEBUG) + retries += 1 + # await asyncio.sleep(random.randint(2, 5)) # 예외 발생 시 대기 후 재시도 + time.sleep(random.randint(2, 5)) + + self.logger.log("이미지 다운로드 최대 재시도 횟수를 초과했습니다.", level=logging.DEBUG) + return None + + def process_clipboard(self, original_url, is_success_translated, toggle_states, path=None, is_thumb=False): + """클립보드의 내용을 처리하고, 필요한 경우 이미지 변환, 크롭 또는 클립보드 비우기""" + + try: + is_watermark = toggle_states.get('watermark') + self.logger.log(f"is_watermark : {is_watermark}", level=logging.DEBUG) + + watermark_text = toggle_states.get('watermark_text') + self.logger.log(f"watermark_text : {watermark_text}", level=logging.DEBUG) + + opacity_percent = toggle_states.get('opacity_percent') + self.logger.log(f"opacity_percent : {opacity_percent}", level=logging.DEBUG) + + clipboard_data = self.get_clipboard_data() + + self.logger.log("clipboard_data", level=logging.DEBUG) + self.logger.log(f"{clipboard_data}", level=logging.DEBUG) + self.logger.log(f"============================", level=logging.DEBUG) + + # 1. 클립보드의 데이터가 Base64 이미지일 경우 + if isinstance(clipboard_data, str) and clipboard_data.startswith('data:image'): + self.logger.log("[process_clipboard] data:image 감지 : 이미지 데이터로 변환", level=logging.INFO) + image = self.base64_to_image(clipboard_data) + if image: + width, _ = image.size + self.logger.log(f"Base64 이미지 크기: {width}px", level=logging.DEBUG) + + # 가로 크기가 200픽셀 이상이면 크롭 + if width >= 200: + self.logger.log("이미지 가로 크기 200픽셀 이상: 크롭 진행 중...", level=logging.DEBUG) + cropped_image = self.crop_image(image, is_thumb) # 크롭 메서드 사용 + + # 워터마크 추가 + if is_watermark and not is_thumb: # is_thumb가 True라면 워터마크 추가를 건너뜁니다 + self.logger.log("워터마크 추가 중...", level=logging.DEBUG) + cropped_watermark_image = self.add_watermark(cropped_image, watermark_text, opacity_percent) # 워터마크 추가 + cropped_image = cropped_watermark_image + + self.set_image_to_clipboard(cropped_image) # 클립보드에 저장 + if path: + self.logger.log("이미지 저장 시도...", level=logging.DEBUG) + self.save_image_to_path(cropped_image, path) + else: + self.logger.log("이미지 가로 크기 200픽셀 이하: 클립보드 비움.", level=logging.DEBUG) + self.clear_clipboard() + else: + self.logger.log("Base64 이미지 변환 실패.", level=logging.DEBUG) + + # 2. 클립보드에 이미지가 있을 경우 + elif isinstance(clipboard_data, Image.Image): + self.logger.log("[process_clipboard] 클립보드 이미지 확인", level=logging.INFO) + + image = clipboard_data + width, _ = image.size + self.logger.log(f"클립보드에 있는 이미지 크기: {width}px", level=logging.DEBUG) + + if width >= 200: + self.logger.log("이미지 가로 크기 200픽셀 이상: 크롭 진행 중...", level=logging.DEBUG) + cropped_image = self.crop_image(image, is_thumb) # 크롭 메서드 사용 + + # 워터마크 추가 + if is_watermark and not is_thumb: # is_thumb가 True라면 워터마크 추가를 건너뜁니다 + self.logger.log("워터마크 추가 중...", level=logging.DEBUG) + cropped_watermark_image = self.add_watermark(cropped_image, watermark_text, opacity_percent) # 워터마크 추가 + cropped_image = cropped_watermark_image + + self.set_image_to_clipboard(cropped_image) # 클립보드에 저장 + if path: + self.logger.log("이미지 저장 시도...", level=logging.DEBUG) + self.save_image_to_path(cropped_image, path) + + else: + self.logger.log("이미지 가로 크기 200픽셀 이하: 클립보드 비움.", level=logging.DEBUG) + self.clear_clipboard() + + # 3. 클립보드에 데이터가 없거나 html > whale-ocr 처리 + elif clipboard_data == "html > whale-ocr" or clipboard_data is None or not is_success_translated: + if clipboard_data == "html > whale-ocr": + self.logger.log("[process_clipboard] html > whale-ocr 감지 : 이미지 번역 실패 확인", level=logging.INFO) + elif clipboard_data is None: + self.logger.log("[process_clipboard] 클립보드에 이미지 없음", level=logging.INFO) + elif is_success_translated is None: + self.logger.log("[process_clipboard] 번역 실패로 인한 원본이미지 다운로드", level=logging.INFO) + + if original_url: + image = self.download_image_from_url(original_url) + if image: + self.logger.log("원본 이미지 다운로드 성공!", level=logging.DEBUG) + + self.set_image_to_clipboard(image) # 크롭 없이 저장 + if path: + self.logger.log("이미지 저장 시도...", level=logging.DEBUG) + self.save_image_to_path(image, path) + else: + self.logger.log("원본 이미지 다운로드 실패.", level=logging.DEBUG) + else: + self.logger.log("원본 이미지 URL을 찾을 수 없습니다.", level=logging.DEBUG) + + except Exception as e: + self.logger.log(f"클립보드에서 이미지를 처리하는 중 오류 발생: {e}", level=logging.ERROR, exc_info=True) + + + def process_clipboard_to_save_path(self, original_url, is_success_translated, toggle_states, path=None, is_thumb=False): + """클립보드의 내용을 처리하고, 필요한 경우 이미지 변환, 크롭 또는 클립보드 비우기""" + + try: + is_watermark = toggle_states.get('watermark') + self.logger.log(f"is_watermark : {is_watermark}", level=logging.DEBUG) + + watermark_text = toggle_states.get('watermark_text') + self.logger.log(f"watermark_text : {watermark_text}", level=logging.DEBUG) + + opacity_percent = toggle_states.get('opacity_percent') + self.logger.log(f"opacity_percent : {opacity_percent}", level=logging.DEBUG) + + clipboard_data = self.get_clipboard_data() + + self.logger.log("clipboard_data", level=logging.DEBUG) + self.logger.log(f"{clipboard_data}", level=logging.DEBUG) + self.logger.log(f"============================", level=logging.DEBUG) + + # 1. 클립보드의 데이터가 Base64 이미지일 경우 + if isinstance(clipboard_data, str) and clipboard_data.startswith('data:image'): + self.logger.log("[process_clipboard] data:image 감지 : 이미지 데이터로 변환", level=logging.INFO) + image = self.base64_to_image(clipboard_data) + if image: + width, _ = image.size + self.logger.log(f"Base64 이미지 크기: {width}px", level=logging.DEBUG) + + # 가로 크기가 200픽셀 이상이면 크롭 + if width >= 200: + self.logger.log("이미지 가로 크기 200픽셀 이상: 크롭 진행 중...", level=logging.DEBUG) + cropped_image = self.crop_image(image, is_thumb) # 크롭 메서드 사용 + + # 워터마크 추가 + if is_watermark and not is_thumb: # is_thumb가 True라면 워터마크 추가를 건너뜁니다 + self.logger.log("워터마크 추가 중...", level=logging.DEBUG) + cropped_watermark_image = self.add_watermark(cropped_image, watermark_text, opacity_percent) # 워터마크 추가 + cropped_image = cropped_watermark_image + + if path: + self.logger.log("이미지 저장 시도...", level=logging.DEBUG) + return self.save_image_to_path(cropped_image, path) + else: + self.set_image_to_clipboard(cropped_image) # 클립보드에 저장 + + else: + self.logger.log("이미지 가로 크기 200픽셀 이하로 처리불가: 클립보드 비움.", level=logging.DEBUG) + self.clear_clipboard() + else: + self.logger.log("Base64 이미지 변환 실패.", level=logging.DEBUG) + + # 2. 클립보드에 이미지가 있을 경우 + elif isinstance(clipboard_data, Image.Image): + self.logger.log("[process_clipboard] 클립보드 이미지 확인", level=logging.INFO) + + image = clipboard_data + width, _ = image.size + self.logger.log(f"클립보드에 있는 이미지 크기: {width}px", level=logging.DEBUG) + + if width >= 200: + self.logger.log("이미지 가로 크기 200픽셀 이상: 크롭 진행 중...", level=logging.DEBUG) + cropped_image = self.crop_image(image, is_thumb) # 크롭 메서드 사용 + + # 워터마크 추가 + if is_watermark and not is_thumb: # is_thumb가 True라면 워터마크 추가를 건너뜁니다 + self.logger.log("워터마크 추가 중...", level=logging.DEBUG) + cropped_watermark_image = self.add_watermark(cropped_image, watermark_text, opacity_percent) # 워터마크 추가 + cropped_image = cropped_watermark_image + + if path: + self.logger.log("이미지 저장 시도...", level=logging.DEBUG) + return self.save_image_to_path(cropped_image, path) + else: + self.set_image_to_clipboard(cropped_image) # 클립보드에 저장 + + else: + self.logger.log("이미지 가로 크기 200픽셀 이하로 처리불가: 클립보드 비움.", level=logging.DEBUG) + self.clear_clipboard() + + # 3. 클립보드에 데이터가 없거나 html > whale-ocr 처리 + elif clipboard_data == "html > whale-ocr" or clipboard_data is None or not is_success_translated or clipboard_data.startswith("https://") or clipboard_data.startswith("http://"): + if clipboard_data == "html > whale-ocr": + self.logger.log("[process_clipboard] html > whale-ocr 감지 : 이미지 번역 실패 확인", level=logging.INFO) + elif clipboard_data is None: + self.logger.log("[process_clipboard] 클립보드에 이미지 없음", level=logging.INFO) + elif is_success_translated is None: + self.logger.log("[process_clipboard] 번역 실패로 인한 원본이미지 다운로드", level=logging.INFO) + elif clipboard_data.startswith("https://") or clipboard_data.startswith("http://"): + self.logger.log("[process_clipboard] 타임아웃으로 인한 번역 실패 - 원본이미지 다운로드", level=logging.INFO) + + if original_url: + image = self.download_image_from_url(original_url) + if image: + self.logger.log("원본 이미지 다운로드 성공!", level=logging.DEBUG) + + if path: + self.logger.log("이미지 저장 시도...", level=logging.DEBUG) + return self.save_image_to_path(image, path) + else: + self.set_image_to_clipboard(cropped_image) # 클립보드에 저장 + else: + self.logger.log("원본 이미지 다운로드 실패.", level=logging.DEBUG) + else: + self.logger.log("원본 이미지 URL을 찾을 수 없습니다.", level=logging.DEBUG) + + except Exception as e: + self.logger.log(f"클립보드에서 이미지를 처리하는 중 오류 발생: {e}", level=logging.ERROR, exc_info=True) + + def process_clipboard_to_save_path_with_local_hosted_image(self, local_image_path, is_success_translated, toggle_states, path=None, is_thumb=False): + """클립보드의 내용을 처리하고, 필요한 경우 이미지 변환, 크롭 또는 클립보드 비우기 + + Returns: + str: 처리된 이미지 파일 경로 (성공 시) + str: 원본 이미지 파일 경로 (실패 시) + """ + + # 매개변수 유효성 검사 + if not local_image_path or not os.path.exists(local_image_path): + self.logger.log(f"유효하지 않은 로컬 이미지 경로: {local_image_path}", level=logging.ERROR) + return local_image_path if local_image_path else None + + if not toggle_states: + self.logger.log("toggle_states가 제공되지 않았습니다", level=logging.WARNING) + toggle_states = {} + + try: + is_watermark = toggle_states.get('watermark', False) + self.logger.log(f"is_watermark : {is_watermark}", level=logging.DEBUG) + + watermark_text = toggle_states.get('watermark_text', '') + self.logger.log(f"watermark_text : {watermark_text}", level=logging.DEBUG) + + opacity_percent = toggle_states.get('opacity_percent', 20) + self.logger.log(f"opacity_percent : {opacity_percent}", level=logging.DEBUG) + + clipboard_data = self.get_clipboard_data() + + self.logger.log(f"type(clipboard_data) : {type(clipboard_data)}", level=logging.DEBUG) + self.logger.log(f"============================", level=logging.DEBUG) + + # 1. 클립보드의 데이터가 Base64 이미지일 경우 + if isinstance(clipboard_data, str) and clipboard_data.startswith('data:image'): + self.logger.log("[process_clipboard] data:image 감지 : 이미지 데이터로 변환", level=logging.INFO) + image = self.base64_to_image(clipboard_data) + if image: + width, _ = image.size + self.logger.log(f"Base64 이미지 크기: {width}px", level=logging.DEBUG) + + # 가로 크기가 200픽셀 이상이면 크롭 + if width >= 200: + self.logger.log("이미지 가로 크기 200픽셀 이상: 크롭 진행 중...", level=logging.DEBUG) + cropped_image = self.crop_image(image, is_thumb) # 크롭 메서드 사용 + + # 워터마크 추가 + if is_watermark and not is_thumb: # is_thumb가 True라면 워터마크 추가를 건너뜁니다 + self.logger.log("워터마크 추가 중...", level=logging.DEBUG) + cropped_watermark_image = self.add_watermark(cropped_image, watermark_text, opacity_percent) # 워터마크 추가 + cropped_image = cropped_watermark_image + + if path: + self.logger.log("이미지 저장 시도...", level=logging.DEBUG) + saved_path = self.save_image_to_path(cropped_image, path) + return saved_path if saved_path else local_image_path + else: + self.set_image_to_clipboard(cropped_image) # 클립보드에 저장 + return local_image_path # path가 없으면 원본 경로 반환 + + else: + self.logger.log("이미지 가로 크기 200픽셀 이하로 처리불가: 클립보드 비움.", level=logging.DEBUG) + self.clear_clipboard() + return local_image_path + else: + self.logger.log("Base64 이미지 변환 실패.", level=logging.DEBUG) + return local_image_path + + # 2. 클립보드에 이미지가 있을 경우 + elif isinstance(clipboard_data, Image.Image): + self.logger.log("[process_clipboard] 클립보드 이미지 확인", level=logging.INFO) + + image = clipboard_data + width, _ = image.size + self.logger.log(f"클립보드에 있는 이미지 크기: {width}px", level=logging.DEBUG) + + if width >= 200: + self.logger.log("이미지 가로 크기 200픽셀 이상: 크롭 진행 중...", level=logging.DEBUG) + cropped_image = self.crop_image(image, is_thumb) # 크롭 메서드 사용 + + # 워터마크 추가 + if is_watermark and not is_thumb: # is_thumb가 True라면 워터마크 추가를 건너뜁니다 + self.logger.log("워터마크 추가 중...", level=logging.DEBUG) + cropped_watermark_image = self.add_watermark(cropped_image, watermark_text, opacity_percent) # 워터마크 추가 + cropped_image = cropped_watermark_image + + if path: + self.logger.log("이미지 저장 시도...", level=logging.DEBUG) + saved_path = self.save_image_to_path(cropped_image, path) + return saved_path if saved_path else local_image_path + else: + self.set_image_to_clipboard(cropped_image) # 클립보드에 저장 + return local_image_path # path가 없으면 원본 경로 반환 + + else: + self.logger.log("이미지 가로 크기 200픽셀 이하로 처리불가: 클립보드 비움.", level=logging.DEBUG) + self.clear_clipboard() + return local_image_path + + # 3. 클립보드에 데이터가 없거나 html > whale-ocr 처리 + elif clipboard_data == "html > whale-ocr" or clipboard_data is None or not is_success_translated or clipboard_data.startswith("https://") or clipboard_data.startswith("http://"): + if clipboard_data == "html > whale-ocr": + self.logger.log("[process_clipboard] html > whale-ocr 감지 : 이미지 번역 실패 확인", level=logging.INFO) + elif clipboard_data is None: + self.logger.log("[process_clipboard] 클립보드에 이미지 없음", level=logging.INFO) + elif not is_success_translated: + self.logger.log("[process_clipboard] 번역 실패로 인한 원본이미지 사용", level=logging.INFO) + elif clipboard_data.startswith("https://") or clipboard_data.startswith("http://"): + self.logger.log("[process_clipboard] 타임아웃으로 인한 번역 실패 - 원본이미지 사용", level=logging.INFO) + + return local_image_path + + # 4. 기타 예상하지 못한 클립보드 데이터 + else: + self.logger.log(f"[process_clipboard] 예상하지 못한 클립보드 데이터 타입: {type(clipboard_data)}", level=logging.WARNING) + return local_image_path + + except Exception as e: + self.logger.log(f"클립보드에서 이미지를 처리하는 중 오류 발생: {e}", level=logging.ERROR, exc_info=True) + return local_image_path # 오류 시 원본 경로 반환 + + def is_clipboard_image(self): + """클립보드에 이미지가 있는지 확인하는 함수""" + max_attempts = 5 + attempt = 0 + + while attempt < max_attempts: + try: + win32clipboard.OpenClipboard() + is_clipboard_image_flag = win32clipboard.IsClipboardFormatAvailable(win32clipboard.CF_DIB) + win32clipboard.CloseClipboard() + + if is_clipboard_image_flag: + self.logger.log("클립보드에 이미지가 존재합니다.", level=logging.DEBUG) + else: + self.logger.log("클립보드에 이미지가 없습니다.", level=logging.DEBUG) + + return is_clipboard_image_flag + + except Exception as e: + attempt += 1 + # 클립보드가 열려있으면 닫기 시도 + try: + win32clipboard.CloseClipboard() + except: + pass + + self.logger.log(f"클립보드 이미지 확인 중 오류 발생 (시도 {attempt}/{max_attempts}): {e}", level=logging.WARNING) + if attempt < max_attempts: + time.sleep(0.5) # 0.5초 대기 후 재시도 + else: + self.logger.log(f"클립보드 이미지 확인 중 최대 시도 횟수 초과: {e}", level=logging.ERROR, exc_info=True) + return False + + def get_image_from_clipboard(self): + """클립보드에서 이미지를 가져오는 함수""" + max_attempts = 5 + attempt = 0 + + while attempt < max_attempts: + try: + win32clipboard.OpenClipboard() + if self.is_clipboard_image(): + dib_data = win32clipboard.GetClipboardData(win32clipboard.CF_DIB) + win32clipboard.CloseClipboard() + image = Image.open(BytesIO(dib_data)) + return image + else: + win32clipboard.CloseClipboard() + self.logger.log("클립보드에 이미지가 없습니다.", level=logging.DEBUG) + return None + except Exception as e: + attempt += 1 + # 클립보드가 열려있으면 닫기 시도 + try: + win32clipboard.CloseClipboard() + except: + pass + + self.logger.log(f"클립보드에서 이미지를 가져오는 중 오류 발생 (시도 {attempt}/{max_attempts}): {e}", level=logging.WARNING) + if attempt < max_attempts: + time.sleep(0.5) # 0.5초 대기 후 재시도 + else: + self.logger.log(f"클립보드에서 이미지를 가져오는 중 최대 시도 횟수 초과: {e}", level=logging.ERROR, exc_info=True) + return None + + def clear_clipboard(self): + """클립보드를 비우는 함수""" + max_attempts = 5 + attempt = 0 + success = False + + while attempt < max_attempts and not success: + try: + # 먼저 pywinauto로 시도 + try: + pywinauto.clipboard.EmptyClipboard() + success = True + except: + # pywinauto 실패 시 win32clipboard로 시도 + win32clipboard.OpenClipboard() + win32clipboard.EmptyClipboard() + win32clipboard.CloseClipboard() + success = True + + self.logger.log(f"클립보드가 비워졌습니다. (시도 {attempt+1}/{max_attempts})", level=logging.DEBUG) + except Exception as e: + attempt += 1 + self.logger.log(f"클립보드를 비우는 중 오류 발생 (시도 {attempt}/{max_attempts}): {e}", level=logging.WARNING) + if attempt < max_attempts: + time.sleep(0.5) # 0.5초 대기 후 재시도 + + if not success: + self.logger.log("최대 시도 횟수를 초과하여 클립보드를 비우지 못했습니다.", level=logging.ERROR) + + def crop_image(self, image, is_thumb=False, crop_percentage=0.01): + """이미지를 주어진 퍼센트만큼 크롭하는 함수""" + if is_thumb: + crop_percentage = 0.03 + self.logger.log(f"썸네일 이미지 이므로 크롭 3%로 조정", level=logging.DEBUG) + + width, height = image.size + left = width * crop_percentage + top = height * crop_percentage + right = width * (1 - crop_percentage) + bottom = height * (1 - crop_percentage) + + cropped_image = image.crop((left, top, right, bottom)) + + if self.debug: + # 디버그 모드일 경우 크롭 전후 다양한 비율로 이미지 저장 + timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") + original_image_path = os.path.join(os.getcwd(), f"original_image_{timestamp}.png") + image.save(original_image_path) # 크롭 전 이미지 저장 + self.logger.log(f"크롭 전 이미지 저장됨: {original_image_path}", level=logging.DEBUG) + + # 1%, 2%, 3% 크롭 이미지 저장 + crop_alternatives = [0.01, 0.02, 0.03] + for crop in crop_alternatives: + left_alt = width * crop + top_alt = height * crop + right_alt = width * (1 - crop) + bottom_alt = height * (1 - crop) + + cropped_alt_image = image.crop((left_alt, top_alt, right_alt, bottom_alt)) + cropped_image_path = os.path.join(os.getcwd(), f"cropped_image_{int(crop*100)}_{timestamp}.png") + cropped_alt_image.save(cropped_image_path) + self.logger.log(f"{int(crop*100)}% 크롭된 이미지 저장됨: {cropped_image_path}", level=logging.DEBUG) + + return cropped_image \ No newline at end of file diff --git a/modules/old_modules/easyocr_module.py b/modules/old_modules/easyocr_module.py new file mode 100644 index 0000000..2945c6e --- /dev/null +++ b/modules/old_modules/easyocr_module.py @@ -0,0 +1,160 @@ +import os +import logging +import cv2 +import numpy as np +import easyocr + +class EasyOCREngine: + def __init__(self, logger=None, base_dir=None, lang_list=['ch_sim']): + self.logger = logger + self.base_dir = base_dir + model_dir = os.path.join(base_dir, 'EasyOCR_models') if base_dir else None + print(f"model_dir: {model_dir}") + self.reader = easyocr.Reader(lang_list, gpu=False, model_storage_directory=model_dir) + + def detect_text(self, image_path: str, method: str = 'polygon'): + """ + EasyOCR로 텍스트 감지 (여러 방식 지원) + + Args: + image_path: 이미지 경로 + method: 감지 방식 + - 'polygon': 기본 폴리곤 방식 + - 'paragraph': 문단 단위 그룹핑 (추천) + - 'enhanced': 크기/신뢰도 기반 고급 필터링 + """ + if not os.path.exists(image_path): + if self.logger: + self.logger.log(f"이미지 파일을 찾을 수 없습니다: {image_path}", level=logging.ERROR) + return [] + + try: + if method == 'paragraph': + return self._detect_with_paragraph(image_path) + elif method == 'enhanced': + return self._detect_with_enhanced_filtering(image_path) + else: # 기본 polygon 방식 + return self._detect_basic_polygon(image_path) + + except Exception as e: + if self.logger: + self.logger.log(f"EasyOCR 처리 중 오류: {e}", level=logging.ERROR, exc_info=True) + return [] + + def _detect_basic_polygon(self, image_path: str): + """기본 폴리곤 방식 (기존 방식)""" + results = self.reader.readtext(image_path, paragraph=False) + ocr_results = [] + for bbox, text, confidence in results: + polygon = [list(map(int, pt)) for pt in bbox] + polygon_np = np.array(polygon, dtype=np.int32) + x, y, w, h = cv2.boundingRect(polygon_np) + ocr_results.append({ + 'text': text, + 'confidence': float(confidence), + 'polygon': polygon, + 'bbox': (x, y, w, h), + 'method': 'polygon' + }) + return ocr_results + + def _detect_with_paragraph(self, image_path: str): + """문단 단위 그룹핑 방식 (연결된 텍스트에 유용)""" + # 문단 모드로 텍스트 감지 + results = self.reader.readtext(image_path, paragraph=True) + ocr_results = [] + + for bbox, text, confidence in results: + polygon = [list(map(int, pt)) for pt in bbox] + polygon_np = np.array(polygon, dtype=np.int32) + x, y, w, h = cv2.boundingRect(polygon_np) + + # 문단 모드에서는 더 긴 텍스트가 나올 수 있음 + ocr_results.append({ + 'text': text, + 'confidence': float(confidence), + 'polygon': polygon, + 'bbox': (x, y, w, h), + 'method': 'paragraph', + 'text_length': len(text) + }) + + if self.logger: + self.logger.log(f"문단 모드 감지 결과: {len(ocr_results)}개", level=logging.DEBUG) + return ocr_results + + def _detect_with_enhanced_filtering(self, image_path: str): + """고급 필터링 방식 (크기, 신뢰도, 비율 기반)""" + results = self.reader.readtext(image_path, paragraph=False) + + # 이미지 크기 정보 가져오기 + import cv2 + image = cv2.imread(image_path) + img_height, img_width = image.shape[:2] + + ocr_results = [] + filtered_count = 0 + + for bbox, text, confidence in results: + polygon = [list(map(int, pt)) for pt in bbox] + polygon_np = np.array(polygon, dtype=np.int32) + x, y, w, h = cv2.boundingRect(polygon_np) + + # 고급 필터링 조건들 + text_area = w * h + img_area = img_width * img_height + area_ratio = text_area / img_area + aspect_ratio = w / h if h > 0 else 0 + + # 필터링 조건 + # 1. 최소/최대 크기 체크 + if w < 10 or h < 8: # 너무 작은 텍스트 + filtered_count += 1 + continue + if area_ratio > 0.5: # 이미지의 50% 이상 차지하는 텍스트 + filtered_count += 1 + continue + + # 2. 종횡비 체크 (너무 길거나 높은 박스 제외) + if aspect_ratio > 20 or aspect_ratio < 0.05: + filtered_count += 1 + continue + + # 3. 동적 신뢰도 임계값 (텍스트 크기에 따라) + min_confidence = 0.15 if text_area > 1000 else 0.25 # 큰 텍스트는 낮은 임계값 + if confidence < min_confidence: + filtered_count += 1 + continue + + # 4. 텍스트 길이 체크 + if len(text.strip()) < 1: + filtered_count += 1 + continue + + ocr_results.append({ + 'text': text, + 'confidence': float(confidence), + 'polygon': polygon, + 'bbox': (x, y, w, h), + 'method': 'enhanced', + 'text_area': text_area, + 'area_ratio': area_ratio, + 'aspect_ratio': aspect_ratio + }) + + if self.logger: + self.logger.log(f"고급 필터링 결과: {len(ocr_results)}개 유지, {filtered_count}개 필터링됨", level=logging.DEBUG) + return ocr_results + + def filter_chinese_text(self, ocr_results): + """ + 중국어 텍스트만 필터링 + """ + chinese_results = [] + for result in ocr_results: + text = result['text'] + if any('\u4e00' <= char <= '\u9fff' for char in text): + chinese_results.append(result) + if self.logger: + self.logger.log(f"중국어 텍스트 {len(chinese_results)}개 필터링 완료", level=logging.INFO) + return chinese_results diff --git a/modules/old_modules/image_processor.py b/modules/old_modules/image_processor.py new file mode 100644 index 0000000..c059760 --- /dev/null +++ b/modules/old_modules/image_processor.py @@ -0,0 +1,382 @@ +import os +import sys +import asyncio +import aiofiles +from pathlib import Path +import logging +from urllib.parse import urlparse +import tempfile +import shutil +import json +from src.modules.local_image_server import LocalImageServer +import aiohttp +import threading +from concurrent.futures import ThreadPoolExecutor +import re + +class ImageProcessor: + """이미지 다운로드, OCR, 번역 처리를 담당하는 클래스""" + + def __init__(self, logger, browser_page, whale_translator, clipboard_manager, temp_dir, toggle_states): + self.logger = logger + self.page = browser_page + self.whale = whale_translator + self.clipboard_manager = clipboard_manager + self.TEMP_IMAGE_DIR = temp_dir + self.toggle_states = toggle_states + + # OCR 관련 + self._ocr_counter = 0 + self._ocr_reset_interval = 100 # 재초기화 주기 + + # OCR 전용 워커 스레드 풀 (단일 스레드) + self._ocr_executor = ThreadPoolExecutor(max_workers=1) + # 워커 스레드-로컬 저장소 + self._thread_local = threading.local() + + self.image_server = LocalImageServer(logger, self.TEMP_IMAGE_DIR) + + # 웹서버 시작 + self.image_server.start_server() + + def update_page(self, page1, toggle_states): + self.page = page1 + self.toggle_states = toggle_states + self.logger.log(f"page객체 업데이트 : {page1}", level=logging.DEBUG) + + def __del__(self): + """소멸자에서 리소스 정리""" + self.cleanup() + + def cleanup(self): + """리소스 정리""" + try: + # 웹서버 중지 + if hasattr(self, 'image_server'): + self.image_server.stop_server() + + # 임시 폴더 삭제 + if hasattr(self, 'temp_dir') and os.path.exists(self.temp_dir): + shutil.rmtree(self.temp_dir) + self.logger.log(f"임시 폴더 삭제됨: {self.temp_dir}", level=logging.INFO) + except Exception as e: + self.logger.log(f"리소스 정리 중 오류: {e}", level=logging.ERROR) + + async def process_single_image(self, page, original_image_url, index, is_localServer, delay=1.0, file_prefix=""): + """ + 단일 이미지를 처리합니다 (다운로드 -> OCR -> 번역 또는 원본) + + Args: + image_url (str): 처리할 이미지 URL + index (int): 이미지 인덱스 + is_localServer (bool): 로컬 서버 사용 여부 + delay (float): 요청 간격 (초) + file_prefix (str): 파일명에 추가할 접두사 (예: "detail", "option") + + Returns: + str or False: + - 번역된 이미지 파일 경로 (중국어 있고 번역 성공) + - 원본 이미지 파일 경로 (중국어 없음) + - False (불필요한 키워드 포함) + - 로컬 파일 경로 또는 원본 URL (오류 발생 시) + """ + local_image_path = None + + try: + + # 0. 이미지 URL 유효성 체크 (http/https & 이미지 확장자) + if not original_image_url or not isinstance(original_image_url, str): + self.logger.log(f"이미지 {index+1} 처리 중단: 이미지 URL 없음 또는 타입 오류", level=logging.WARNING) + return False + + image_url_pattern = re.compile(r'^(http|https)://.*\.(jpg|jpeg|png|bmp|gif|webp|tiff?)(\?.*)?$', re.IGNORECASE) + if not image_url_pattern.match(original_image_url): + self.logger.log(f"이미지 {index+1} 처리 중단: 유효하지 않은 이미지 주소 - {original_image_url}", level=logging.WARNING) + return False + + # 요청 간격 조절 (봇 탐지 회피) + if index > 0: + await asyncio.sleep(delay) + + # OCR 권한 상태 로그 + ocr_enabled = self.toggle_states.get('ocr', False) + ocr_status = "고급 사용자 (OCR 활성화)" if ocr_enabled else "일반 사용자 (OCR 비활성화)" + self.logger.log(f"이미지 {index+1} 처리 시작: {original_image_url} - {ocr_status}", level=logging.INFO) + + # 1. 이미지 다운로드 + local_image_path = await self.download_image(page, original_image_url, index, file_prefix) + if not local_image_path: + self.logger.log(f"이미지 {index+1} 다운로드 실패, 원본 URL 반환", level=logging.WARNING) + return original_image_url + + # 2. OCR 수행 (단일 스레드 풀에서만) + # ocr_result = await self._check_chinese_text(image_path=local_image_path) + ocr_result = await self.check_chinese_text(image_path=local_image_path) + + if ocr_result == 'exclude': + self.logger.log(f"이미지 {index+1} 불필요한 키워드 포함으로 제외", level=logging.INFO) + return False + + if ocr_result == 'original': + self.logger.log(f"이미지 {index+1} 원본 사용(original)", level=logging.INFO) + return local_image_path + + # translate + self.logger.log(f"이미지 {index+1} 번역 시작", level=logging.INFO) + if is_localServer: + translate_image_url = await self.get_local_image_url(local_image_path) + else: + translate_image_url = original_image_url + + if translate_image_url.startswith("http"): + translated = await self.translate_and_save_image(translate_image_url, local_image_path, index, file_prefix) + if translated: + self.logger.log(f"이미지 {index+1} 번역 완료: {translated}", level=logging.INFO) + return translated + + self.logger.log(f"이미지 {index+1} 번역 실패, 원본 반환", level=logging.WARNING) + return local_image_path + + except Exception as e: + self.logger.log(f"이미지 {index+1} 처리 중 오류: {e}", level=logging.ERROR) + return local_image_path or original_image_url + + async def translate_and_save_image(self, local_server_url, original_local_path, index, file_prefix=""): + """로컬 서버 URL을 사용해 이미지를 번역하고 로컬에 저장합니다""" + try: + # 파일명에 접두사 포함 + if file_prefix: + img_path = os.path.join(self.TEMP_IMAGE_DIR, f"translated_{file_prefix}_img_{index+1}.png") + else: + img_path = os.path.join(self.TEMP_IMAGE_DIR, f"translated_img_{index+1}.png") + + # 웨일 브라우저로 이미지 번역 + is_translated = self.whale.translate_image(local_server_url) + + # 번역된 이미지를 임시 파일로 저장 + translated_img_path = self.clipboard_manager.process_clipboard_to_save_path_with_local_hosted_image( + local_image_path=original_local_path, + is_success_translated=is_translated, + toggle_states=self.toggle_states, + path=img_path + ) + + return translated_img_path + + except Exception as e: + self.logger.log(f"이미지 {index+1} 번역 처리 중 오류: {e}", level=logging.ERROR) + return original_local_path + + + async def download_image(self, page, image_url, index, file_prefix=""): + """Playwright를 사용해 이미지를 다운로드합니다""" + try: + # "https://assets.alicdn.com"으로 시작하는 URL은 건너뛰기 + if image_url.startswith("https://assets.alicdn.com"): + self.logger.log(f"다운로드 제외 URL: {image_url}", level=logging.DEBUG) + return None + + # URL에서 파일명 추출 및 접두사 포함 + parsed_url = urlparse(image_url) + base_filename = f"image_{index:03d}_{os.path.basename(parsed_url.path)}" + if not base_filename.endswith(('.jpg', '.jpeg', '.png', '.gif', '.webp')): + base_filename += '.jpg' + + # 접두사가 있으면 파일명에 포함 + if file_prefix: + filename = f"{file_prefix}_{base_filename}" + else: + filename = base_filename + + local_path = os.path.join(self.TEMP_IMAGE_DIR, filename) + + # Playwright로 이미지 다운로드 + response = await page.request.get(image_url) + if response.status == 200: + image_data = await response.body() + + # 이미지 데이터 유효성 검사 + if self.is_valid_image_data(image_data): + async with aiofiles.open(local_path, 'wb') as f: + await f.write(image_data) + + self.logger.log(f"이미지 다운로드 완료: {filename}", level=logging.INFO) + return local_path + else: + self.logger.log(f"유효하지 않은 이미지 데이터: {image_url}", level=logging.WARNING) + return None + else: + self.logger.log(f"이미지 다운로드 실패 (HTTP {response.status}): {image_url}", level=logging.ERROR) + return None + + except Exception as e: + self.logger.log(f"이미지 다운로드 중 오류: {e}", level=logging.ERROR) + return None + + def is_valid_image_data(self, image_data): + """이미지 데이터가 유효한지 확인합니다""" + if not image_data or len(image_data) < 100: + return False + + # 일반적인 이미지 파일 시그니처 확인 + image_signatures = [ + b'\xFF\xD8\xFF', # JPEG + b'\x89PNG\r\n\x1a\n', # PNG + b'GIF87a', # GIF87a + b'GIF89a', # GIF89a + b'RIFF', # WebP (RIFF 컨테이너) + ] + + return any(image_data.startswith(sig) for sig in image_signatures) + + + async def check_chinese_text(self, image_path): + """이미지에서 중국어 텍스트가 있는지 확인합니다 (에러 시 OCR 객체 재생성 후 재시도)""" + # ocr_enabled = self.toggle_states.get('ocr', False) + # if not ocr_enabled: + # self.logger.log("OCR 비활성화—항상 번역 모드", level=logging.INFO) + # return 'translate' + return 'translate' + + # # OCR 프로세서가 없으면 초기화 + # if self.ocr_processor is None: + # self.initialize_ocr_processor() + # if self.ocr_processor is None: + # return 'translate' + + # # 실제 OCR 호출을 래핑: 에러 시 재초기화 + 재시도 + # try: + # result = await asyncio.to_thread( + # self.ocr_processor.check_local_image_zhcn, + # image_path + # ) + # except Exception as e: + # # primitive 실행 에러로 추정 + # self.logger.log(f"OCR primitive 오류: {e} → 번역 모드로 전환합니다.", level=logging.WARNING, exc_info=True) + # # 재초기화 + # self.initialize_ocr_processor() + # try: + # result = await asyncio.to_thread( + # self.ocr_processor.check_local_image_zhcn, + # image_path + # ) + # except Exception as e2: + # self.logger.log(f"OCR 재시도 실패: {e2}", level=logging.ERROR, exc_info=True) + # return 'translate' + + # # 사용 횟수 카운트 및 주기 도달 시 재초기화 + # self._ocr_counter += 1 + # self.logger.log(f"OCR 프로세서 사용 횟수: {self._ocr_counter}", level=logging.DEBUG) + # if self._ocr_counter >= self._ocr_reset_interval: + # self.logger.log("OCR 재초기화 주기 도달 – 객체 재생성합니다.", level=logging.INFO) + # self.initialize_ocr_processor() + + # # OCR 결과 판정 + # if result: + # status = result.get('status', 'error') + # message = result.get('message', '') + # if status == 'has_chinese_clean': + # self.logger.log(f"번역 대상 이미지: {image_path} - {message}", level=logging.INFO) + # return 'translate' + # elif status == 'has_chinese_unwanted': + # self.logger.log(f"불필요한 키워드 포함 이미지: {image_path} - {message}", level=logging.INFO) + # return 'exclude' + # elif status == 'no_chinese': + # self.logger.log(f"중국어 텍스트 없음: {image_path} - {message}", level=logging.INFO) + # return 'original' + # else: + # self.logger.log(f"OCR 처리 오류({status}): {image_path} - {message}", level=logging.ERROR) + # return 'error' + # else: + # self.logger.log(f"OCR 결과가 None 또는 빈 값: {image_path}", level=logging.ERROR) + # return 'error' + + + # async def _check_chinese_text(self, image_path: str) -> str: + # """OCR 전용 풀에서만 실행하여 primitive 에러 방지""" + # if not self.toggle_states.get('ocr', False): + # return 'translate' + + # loop = asyncio.get_running_loop() + # try: + # status = await loop.run_in_executor( + # self._ocr_executor, + # self._threaded_ocr, + # image_path + # ) + # except Exception as e: + # self.logger.log(f"OCR 워커 에러: {e} → 번역 모드", level=logging.WARNING, exc_info=True) + # return 'translate' + + # # 주기적 재초기화 + # self._ocr_counter += 1 + # if self._ocr_counter >= self._ocr_reset_interval: + # self.logger.log("OCR 재초기화 주기 도달", level=logging.INFO) + # # 워커 스레드에 있는 OCR 객체 갱신 + # def _reinit(): + # if hasattr(self._thread_local, 'ocr'): + # del self._thread_local.ocr + # await loop.run_in_executor(self._ocr_executor, _reinit) + # self._ocr_counter = 0 + + # return status + + + # def _threaded_ocr(self, image_path: str) -> str: + # """단일 워커 스레드 안에서만 OCR 초기화 및 추론""" + # # 최초 호출 시 OCR 인스턴스 생성 + # if not hasattr(self._thread_local, 'ocr'): + # self.logger.log("워커 스레드에서 OCR 프로세서 초기화", level=logging.INFO) + # from src.ppocr.ocr import ChineseImageOCRProcessor + # self._thread_local.ocr = ChineseImageOCRProcessor( + # logger=self.logger, + # toggle_states=self.toggle_states, + # use_gpu=False, + # det_enabled=True, + # horizontal_only=False + # ) + + # # 실제 OCR 실행 + # result = self._thread_local.ocr.check_local_image_zhcn(image_path) + # status = result.get('status', 'error') + # self.logger.log(f"OCR 결과: {result}", level=logging.DEBUG) + # self.logger.log(f"status 결과: {status}", level=logging.DEBUG) + + # if status == 'has_chinese_clean': + # return 'translate' + # if status == 'has_chinese_unwanted': + # return 'exclude' + # if status == 'no_chinese': + # return 'original' + # return 'translate' + + async def get_local_image_url(self, local_path): + """로컬 이미지를 웹서버 URL로 변환합니다""" + try: + filename = os.path.basename(local_path) + if self.image_server.is_running(): + web_url = f"{self.image_server.get_base_url()}/{filename}" + self.logger.log(f"번역용 웹 URL 생성: {web_url}", level=logging.INFO) + return web_url + else: + self.logger.log(f"웹서버가 실행되지 않았습니다.", level=logging.ERROR) + return local_path + + except Exception as e: + self.logger.log(f"웹 URL 생성 중 오류: {e}", level=logging.ERROR) + return local_path + + + async def process_image_list(self, image_urls, delay_between_requests=1.0): + """이미지 목록을 순차적으로 처리합니다""" + processed_images = [] + + for index, image_url in enumerate(image_urls): + processed_url = await self.process_single_image(image_url, index, delay_between_requests) + if processed_url: + processed_images.append(processed_url) + + # 진행 상황 로그 + self.logger.log(f"이미지 처리 진행률: {index+1}/{len(image_urls)}", level=logging.INFO) + + return processed_images diff --git a/modules/old_modules/image_processor2.py b/modules/old_modules/image_processor2.py new file mode 100644 index 0000000..d45b33e --- /dev/null +++ b/modules/old_modules/image_processor2.py @@ -0,0 +1,367 @@ +import os +import asyncio +import aiofiles +import logging +from urllib.parse import urlparse +import shutil +import threading +from concurrent.futures import ThreadPoolExecutor +import re +import cv2 +import base64 +import requests +import numpy as np +from src.modules.ocr_module import OCRModule +from src.modules.mask_module import MaskModule +from src.modules.text_rendering_module import TextRenderingModule +from src.modules.postImageManager import PostImageManager +from src.modules.iop_Manager import IOPaintManager + +class ImageProcessor: + """이미지 다운로드, OCR, 번역 처리를 담당하는 클래스""" + + def __init__(self, logger, browser_page, toggle_states, gpt_client, base_dir): + self.logger = logger + self.page = browser_page + self.base_dir = base_dir + self.gpt_client = gpt_client + self.toggle_states = toggle_states + + # OCR 관련 + self._ocr_counter = 0 + self._ocr_reset_interval = 100 # 재초기화 주기 + self.ocr_processor = None + + self.unwanted_texts = [] + self.font_path = self.toggle_states.get('image_font_path', os.path.join(self.base_dir, "HakgyoansimDunggeunmisoTTFB.ttf")) + self.TEMP_IMAGE_DIR = self.toggle_states.get('TEMP_IMAGE_DIR', "") + + self.iopaint_manager = IOPaintManager(logger=self.logger, num_instances=1, port_range=(8099, 8199), wait_ready=30) + self.ocr_module = OCRModule(logger=self.logger, base_dir=self.base_dir) + self.mask_module = MaskModule(logger=self.logger, base_dir=self.base_dir) + self.text_rendering_module = TextRenderingModule(logger=self.logger, font_path=self.font_path) + self.postImageManager = PostImageManager(logger=self.logger, toggle_states=self.toggle_states) + + def update_page(self, page1, toggle_states): + self.toggle_states = toggle_states + self.page = page1 + self.postImageManager.update_toggle_states(self.toggle_states) + self.logger.log(f"page객체 및 toggle_states 업데이트", level=logging.DEBUG) + + def update_unwanted_texts(self, texts): + self.unwanted_texts = texts + + def __del__(self): + """소멸자에서 리소스 정리""" + self.cleanup() + + def cleanup(self): + """리소스 정리""" + try: + + # 임시 폴더 삭제 + if hasattr(self, 'TEMP_IMAGE_DIR') and os.path.exists(self.TEMP_IMAGE_DIR): + shutil.rmtree(self.TEMP_IMAGE_DIR) + self.logger.log(f"임시 폴더 삭제됨: {self.TEMP_IMAGE_DIR}", level=logging.INFO) + except Exception as e: + self.logger.log(f"리소스 정리 중 오류: {e}", level=logging.ERROR, exc_info=True) + + def is_valid_image_path(self, path): + # http/https 또는 로컬 파일(.jpg, .png 등) 모두 허용 + if re.match(r'^(http|https)://.*\\.(jpg|jpeg|png|bmp|gif|webp|tiff?)(\\?.*)?$', path, re.IGNORECASE): + return True + if os.path.isfile(path) and path.lower().endswith(('.jpg', '.jpeg', '.png', '.bmp', '.gif', '.webp', '.tif', '.tiff')): + return True + return False + + + async def process_single_image(self, page, original_image_url, index, is_localServer, delay=1.0, file_prefix="", use_inpainting=False): + """ + 단일 이미지를 처리합니다 (다운로드 -> OCR -> 인페인팅) + + Args: + page: Playwright 페이지 객체 + original_image_url (str): 처리할 이미지 URL + index (int): 이미지 인덱스 + is_localServer (bool): 로컬 서버 사용 여부 + delay (float): 요청 간격 (초) + file_prefix (str): 파일명에 추가할 접두사 (예: "detail", "option") + + Returns: + dict: 처리 결과를 포함한 딕셔너리 + - status: 'inpainted', 'original', 'exclude', 'error' 중 하나 + - path: 처리된 이미지 파일 경로 또는 원본 이미지 파일 경로 + - error: 오류 메시지 (status가 'error'인 경우에만 포함) + """ + local_image_path = None + + try: + # 0. 이미지 URL 유효성 체크 (http/https & 이미지 확장자) + if not original_image_url or not isinstance(original_image_url, str): + self.logger.log(f"이미지 {index+1} 처리 중단: 이미지 URL 없음 또는 타입 오류", level=logging.WARNING) + return {'status': 'failed', 'path': original_image_url, 'error': '이미지 URL 없음 또는 타입 오류'} + + if not self.is_valid_image_path(original_image_url): + self.logger.log(f"이미지 {index+1} 처리 중단: 유효하지 않은 이미지 주소 - {original_image_url}", level=logging.WARNING) + return {'status': 'failed', 'path': original_image_url, 'error': '유효하지 않은 이미지 주소'} + + # 요청 간격 조절 (봇 탐지 회피) + if index > 0: + await asyncio.sleep(delay) + + # OCR 권한 상태 로그 + ocr_enabled = self.toggle_states.get('ocr', False) + processing_mode = "OCR+인페인팅 모드" if ocr_enabled else "전체 번역 모드" + self.logger.log(f"이미지 {index+1} 처리 시작: {original_image_url} - {processing_mode}", level=logging.INFO) + + # 1. 이미지 다운로드 + local_image_path = await self.download_image(page, original_image_url, index, file_prefix) + if not local_image_path: + self.logger.log(f"이미지 {index+1} 다운로드 실패, 원본 URL 반환", level=logging.WARNING) + return {'status': 'failed', 'path': original_image_url, 'error': '다운로드 실패'} + + # 2. OCR 텍스트 감지 + ocr_results = self.ocr_module.detect_text(local_image_path) + + # 3. 중국어 텍스트 없는 경우 원본 이미지 반환 + if not self.ocr_module.filter_chinese_text(ocr_results): + self.logger.log(f"이미지 {index+1} 중국어 텍스트 없음, 원본 이미지 반환", level=logging.INFO) + return {'status': 'original', 'path': local_image_path} + + # 4. 텍스트 번역 (GPT) + translated_texts = self.gpt_translate_texts(ocr_results, self.gpt_client) + + # 5. OCR 권한에 따른 텍스트 필터링 + if ocr_enabled: + filtered_translated_texts = self.process_translated_texts(translated_texts, self.unwanted_texts, local_image_path, index) + if not filtered_translated_texts: + self.logger.log(f"이미지 {index+1} 제외됨", level=logging.INFO) + return {'status': 'exclude', 'path': local_image_path} + else: + self.logger.log(f"이미지 {index+1} 치환됨", level=logging.INFO) + else: + # OCR 권한이 없으면 번역된 텍스트를 그대로 사용 + filtered_translated_texts = translated_texts + self.logger.log(f"이미지 {index+1} OCR 권한 없음, 전체 번역 모드", level=logging.INFO) + + # 마스크 생성 (basic 방식만 사용) + masks = self.mask_module.create_masks( + image_path=local_image_path, ocr_results=ocr_results, mask_option="basic" + ) + self.logger.log(f"마스크 생성 완료", level=logging.INFO) + + # 인페인팅 + inpainted_image = self.iopaint_manager.inpaint(local_image_path, masks) + self.logger.log(f"인페인팅 완료", level=logging.INFO) + + # 인페인팅 실패 시 원본 이미지 사용 + if inpainted_image is None: + self.logger.log(f"인페인팅 실패, 원본 이미지 사용", level=logging.WARNING) + inpainted_image = cv2.imread(local_image_path) + if inpainted_image is None: + self.logger.log(f"원본 이미지 로드 실패: {local_image_path}", level=logging.ERROR) + return {'status': 'failed', 'path': local_image_path, 'error': '원본 이미지 로드 실패'} + + # 텍스트 렌더링 + text_rendered_image = self.text_rendering_module.render_text( + inpainted_image, ocr_results, filtered_translated_texts) + self.logger.log(f"텍스트 렌더링 완료", level=logging.INFO) + + # 결과 저장 + translated_img_path = await self.postProcess_and_save_image(local_image_path, text_rendered_image, index, file_prefix) + self.logger.log(f"이미지 {index+1} 번역 완료: {translated_img_path}", level=logging.INFO) + return {'status': 'inpainted', 'path': translated_img_path} + + except Exception as e: + self.logger.log(f"이미지 {index+1} 처리 중 오류: {e}", level=logging.ERROR, exc_info=True) + return {'status': 'failed', 'path': local_image_path or original_image_url, 'error': str(e)} + + async def postProcess_and_save_image(self, local_image_path, text_rendered_image, index, file_prefix=""): + """로컬 서버 URL을 사용해 이미지를 번역하고 로컬에 저장합니다""" + try: + # 파일명에 접두사 포함 + if file_prefix: + img_path = os.path.join(self.TEMP_IMAGE_DIR, f"translated_{file_prefix}_img_{index+1}.png") + else: + img_path = os.path.join(self.TEMP_IMAGE_DIR, f"translated_img_{index+1}.png") + + watermarked_image_data = self.postImageManager.add_watermark(image_data=text_rendered_image, watermark_text=self.toggle_states.get("watermark_text", "워터마크크")) + final_image_path = self.postImageManager.save_image_to_path(watermarked_image_data, img_path) + + return final_image_path + + except Exception as e: + self.logger.log(f"이미지 {index+1} 번역 처리 중 오류: {e}", level=logging.ERROR, exc_info=True) + return local_image_path + + + async def download_image(self, page, image_url, index, file_prefix=""): + """Playwright를 사용해 이미지를 다운로드합니다""" + # 로컬 파일 경로면 바로 반환 + if os.path.isfile(image_url): + self.logger.log(f"로컬 파일 경로 감지, 다운로드 생략: {image_url}", level=logging.INFO) + return image_url + + # 로컬 파일 경로가 아니면 다운로드 시도 + try: + # "https://assets.alicdn.com"으로 시작하는 URL은 건너뛰기 + if image_url.startswith("https://assets.alicdn.com"): + self.logger.log(f"다운로드 제외 URL: {image_url}", level=logging.DEBUG) + return None + + # URL에서 파일명 추출 및 접두사 포함 + parsed_url = urlparse(image_url) + base_filename = f"image_{index:03d}_{os.path.basename(parsed_url.path)}" + if not base_filename.endswith(('.jpg', '.jpeg', '.png', '.gif', '.webp')): + base_filename += '.jpg' + + # 접두사가 있으면 파일명에 포함 + if file_prefix: + filename = f"{file_prefix}_{base_filename}" + else: + filename = base_filename + + local_path = os.path.join(self.TEMP_IMAGE_DIR, filename) + + # Playwright로 이미지 다운로드 + response = await page.request.get(image_url) + if response.status == 200: + image_data = await response.body() + + # 이미지 데이터 유효성 검사 + if self.is_valid_image_data(image_data): + async with aiofiles.open(local_path, 'wb') as f: + await f.write(image_data) + + self.logger.log(f"이미지 다운로드 완료: {filename}", level=logging.INFO) + return local_path + else: + self.logger.log(f"유효하지 않은 이미지 데이터: {image_url}", level=logging.WARNING) + return None + else: + self.logger.log(f"이미지 다운로드 실패 (HTTP {response.status}): {image_url}", level=logging.ERROR, exc_info=True) + return None + + except Exception as e: + self.logger.log(f"이미지 다운로드 중 오류: {e}", level=logging.ERROR, exc_info=True) + return None + + def is_valid_image_data(self, image_data): + """이미지 데이터가 유효한지 확인합니다""" + if not image_data or len(image_data) < 100: + return False + + # 일반적인 이미지 파일 시그니처 확인 + image_signatures = [ + b'\xFF\xD8\xFF', # JPEG + b'\x89PNG\r\n\x1a\n', # PNG + b'GIF87a', # GIF87a + b'GIF89a', # GIF89a + b'RIFF', # WebP (RIFF 컨테이너) + ] + + return any(image_data.startswith(sig) for sig in image_signatures) + + def initialize_ocr_processor(self): + self.logger.log(f"OCR 프로세서 초기화", level=logging.INFO) + from src.ppocr.ocr import ChineseImageOCRProcessor + self.ocr_processor = ChineseImageOCRProcessor(logger=self.logger, toggle_states=self.toggle_states, use_gpu=False, det_enabled=True, horizontal_only=False) + return self.ocr_processor + + def process_translated_texts(self, translated_texts, unwanted_texts, local_image_path, index): + """ + 번역된 단어 리스트(translated_texts)에서 unwanted_texts의 원본값이 + 앞이나 뒤에 포함되면 치환값으로 바꿉니다. + 치환값이 '이미지삭제'라면 None 반환(이미지 제외) + """ + + new_texts = [] + for text in translated_texts: + replaced = False + for origin, replace in unwanted_texts.items(): + # 앞/뒤에 원본값이 있는지 확인 + if text.startswith(origin) or text.endswith(origin): + self.logger.log(f"[{text}] -> [{replace}] (치환)", level=logging.INFO) + if replace == "이미지삭제": + self.logger.log(f"이미지 {index+1} 제외됨: {local_image_path}", level=logging.INFO) + return None + # 앞/뒤 원본값만 치환 + if text.startswith(origin): + new = replace + text[len(origin):] + elif text.endswith(origin): + new = text[:-len(origin)] + replace + new_texts.append(new) + replaced = True + break + if not replaced: + new_texts.append(text) + + self.logger.log(f"최종 치환 결과: {new_texts}", level=logging.INFO) + return new_texts + + + async def process_image_list(self, page, image_urls, is_localServer, delay=1.0, file_prefix="", use_inpainting=False): + """ + 이미지 리스트를 순차적으로 처리합니다. + """ + if not image_urls: + self.logger.log("처리할 이미지가 없습니다.", level=logging.INFO) + return [] + + processing_mode = "인페인팅" if use_inpainting else "웨일 번역" + self.logger.log(f"이미지 {len(image_urls)}개를 {processing_mode} 모드로 처리 시작", level=logging.INFO) + + processed_images = [] + + for i, url in enumerate(image_urls): + self.logger.log(f"이미지 {i+1}/{len(image_urls)} 처리 중... ({processing_mode} 모드)", level=logging.INFO) + + result = await self.process_single_image( + page, url, i, is_localServer, delay, file_prefix, use_inpainting + ) + + # 결과 처리 + if isinstance(result, dict): + status = result.get('status') + path = result.get('path') + + if status == 'inpainted': + processed_images.append(path) + self.logger.log(f"이미지 {i+1} 인페인팅 처리 완료", level=logging.INFO) + elif status == 'original': + processed_images.append(path) + self.logger.log(f"이미지 {i+1} 원본 사용", level=logging.INFO) + elif status == 'exclude': + self.logger.log(f"이미지 {i+1} 제외됨", level=logging.INFO) + # 제외된 이미지는 리스트에 추가하지 않음 + else: # failed + self.logger.log(f"이미지 {i+1} 처리 실패: {result.get('error', '알 수 없는 오류')}", level=logging.WARNING) + # 실패한 이미지는 원본 경로 추가 + processed_images.append(path) + else: + # 이전 버전과의 호환성을 위한 처리 + if result: + processed_images.append(result) + + self.logger.log(f"이미지 처리 완료: 총 {len(processed_images)}개 ({processing_mode} 모드)", level=logging.INFO) + return processed_images + + + def gpt_translate_texts(self, ocr_results, gpt_client): + texts = [result['text'] for result in ocr_results] + if not texts: + return [] + prompt = ( + "다음 중국어 문장들을 한국어로 자연스럽고 의미가 잘 전달되게 번역해줘. " + "순서와 개수는 반드시 그대로 유지하고, 결과는 JSON 배열(리스트)로만 반환해. " + "중국어 리스트:\n" + + str(texts) + ) + response = gpt_client.ask(prompt) + if isinstance(response, list): + return response + elif isinstance(response, dict) and 'result' in response: + return response['result'] + else: + print("GPT 번역 결과 파싱 실패, 원본 반환") + return texts diff --git a/modules/old_modules/image_processor4.py b/modules/old_modules/image_processor4.py new file mode 100644 index 0000000..1a97b01 --- /dev/null +++ b/modules/old_modules/image_processor4.py @@ -0,0 +1,289 @@ +import os +import asyncio +import requests +import time +import logging +from urllib.parse import urlparse +import sys +import cv2 +# OpenCV 의 내부 최적화(메모리 풀) 사용을 비활성화하여 파편화 위험을 낮춤 +cv2.setUseOptimized(False) +from PIL import Image + +from src.modules.request_inpaint import Request_AI_Server + +class ImageProcessor3: + """이미지 다운로드, OCR, 번역 처리를 담당하는 클래스""" + + def __init__(self, logger, page, toggle_states, unwanted_words, authenticated_by_admin, base_dir, papago_translator): + self.logger = logger + self.page = page + self.base_dir = base_dir + self.toggle_states = toggle_states + self.unwanted_texts = unwanted_words + self.authenticated_by_admin = authenticated_by_admin + + self.logger.log(f"ImageProcessor4 Init toggle_states: {self.toggle_states}", level=logging.DEBUG) + + self.papago_translator = papago_translator + + self.inpaint_method = 'cv' + try: + self.request_inpainting_server_url = self.toggle_states.get("request_inpainting_server_url", None) + if self.request_inpainting_server_url is None: + self.logger.log(f"request_inpainting_server_url 설정되지 않았습니다.", level=logging.ERROR) + self.inpaint_method = 'cv' + else: + self.inpaint_method = 'request' + + self.font_path = self.toggle_states.get('image_font_path', os.path.join(self.base_dir, "HakgyoansimDunggeunmisoTTFB.ttf")) + + self.TEMP_IMAGE_DIR = self.toggle_states.get('TEMP_IMAGE_DIR', "") + + self.use_local_rembg = self.toggle_states.get("use_local_rembg", False) + self.local_model_name = self.toggle_states.get("local_model_name", 'birefnet-general-lite') + + self.request_rembg_server_url = self.toggle_states.get("request_rembg_server_url", None) + # self.request_rembg_server_url = self.toggle_states.get("request_rembg_server_url_local", None) + + if not self.is_frozen(): + self.request_rembg_server_url = self.toggle_states.get("request_rembg_server_url_local", None) + + self.request_ai_server = Request_AI_Server(logger=self.logger, inpaint_server_url=self.request_inpainting_server_url, rembg_server_url=self.request_rembg_server_url) + + + self.logger.log(f"self.font_path: {self.font_path}", level=logging.DEBUG) + + self.logger.log(f"self.TEMP_IMAGE_DIR: {self.TEMP_IMAGE_DIR}", level=logging.DEBUG) + + self.logger.log(f"self.unwanted_texts: {self.unwanted_texts}", level=logging.DEBUG) + + except Exception as e: + self.logger.log(f"ImageProcessor3 초기화 중 오류 발생: {e}", level=logging.ERROR, exc_info=True) + + def update_toggle_states(self, toggle_states): + self.toggle_states = toggle_states + self.postImageManager.update_toggle_states(self.toggle_states) + self.logger.log(f"이미지 프로세서 toggle_states 업데이트 : {self.toggle_states}", level=logging.DEBUG) + + def update_unwanted_texts(self, texts): + self.unwanted_texts = texts + self.logger.log(f"이미지프로세서 unwanted_texts: {self.unwanted_texts}", level=logging.DEBUG) + + def __del__(self): + """소멸자에서 리소스 정리""" + self.cleanup() + self.logger.log("이미지 프로세서 소멸", level=logging.DEBUG) + def cleanup(self): + """리소스 정리""" + try: + + # 임시 폴더 삭제 + if hasattr(self, 'TEMP_IMAGE_DIR') and os.path.exists(self.TEMP_IMAGE_DIR): + # shutil.rmtree(self.TEMP_IMAGE_DIR) + self.logger.log(f"임시 폴더 삭제됨: {self.TEMP_IMAGE_DIR}", level=logging.DEBUG) + except Exception as e: + self.logger.log(f"리소스 정리 중 오류: {e}", level=logging.ERROR, exc_info=True) + + + + def opencv_inpaint(self, image_path, mask, method='telea', radius=3): + """ + OpenCV로 인페인팅을 수행합니다. + Args: + image_path (str): 원본 이미지 경로 + mask (np.ndarray): 2D 마스크 이미지 (0/255, shape=(H, W)) + method (str): 'telea' 또는 'ns' + radius (int): 인페인팅 반경 + Returns: + inpainted_image (np.ndarray): 인페인팅된 이미지 + """ + import cv2 + import numpy as np + image = cv2.imread(image_path) + if image is None: + self.logger.log(f"이미지 로드 실패: {image_path}", level=logging.ERROR) + return None + + # 마스크가 2D 배열인지 확인 + if mask is None: + self.logger.log(f"마스크가 None입니다", level=logging.ERROR) + return None + + if not isinstance(mask, np.ndarray) or mask.ndim != 2: + self.logger.log(f"마스크가 2D numpy 배열이 아닙니다: type={type(mask)}, shape={getattr(mask, 'shape', 'N/A')}", level=logging.ERROR) + return None + + # 마스크 크기가 이미지와 일치하는지 확인 + if mask.shape != image.shape[:2]: + self.logger.log(f"마스크 크기가 이미지와 다릅니다: mask={mask.shape}, image={image.shape[:2]}", level=logging.ERROR) + return None + + inpaint_method = cv2.INPAINT_TELEA if method == 'telea' else cv2.INPAINT_NS + inpainted = cv2.inpaint(image, mask, radius, inpaint_method) + return inpainted + + + def save_debug_images(self, local_image_path, ocr_results, masks, index, file_prefix=""): + """디버깅용 OCR 박스와 마스크 이미지를 저장합니다""" + try: + # OCR 박스 시각화 이미지 저장 + ocr_debug_path = self.save_ocr_debug_image(local_image_path, ocr_results, index, file_prefix) + + # 마스크 시각화 이미지 저장 + mask_debug_path = self.save_mask_debug_image(local_image_path, masks, index, file_prefix) + + self.logger.log(f"디버깅 이미지 저장 완료: OCR={ocr_debug_path}, Mask={mask_debug_path}", level=logging.INFO) + return ocr_debug_path, mask_debug_path + + except Exception as e: + self.logger.log(f"디버깅 이미지 저장 중 오류: {e}", level=logging.ERROR, exc_info=True) + return None, None + + + def is_frozen(self): + """ + 실행 환경에 따라 배포환경인지 개발환경인지 확인하는 메서드. + cx_Freeze로 패키징된 경우 실행 파일의 경로, 일반 Python 환경일 경우 __file__을 기준으로 설정. + """ + if getattr(sys, 'frozen', False): # 패키징된 경우 + self.logger.log("배포환경", level=logging.DEBUG) + return True + else: # 일반 Python 실행 환경 + self.logger.log("개발환경", level=logging.DEBUG) + return False + + + + async def remove_background(self, original_image_url, file_prefix=""): + """배경제거 전용 메서드 (썸네일 등 외부 호출용). + + 1. 이미지를 다운로드(또는 로컬 경로 사용) + 2. Request_AI_Server.request_rembg 로 배경 제거 (흰 배경 중앙 배치 포함) + 3. TEMP_IMAGE_DIR 하위에 저장 후 경로 반환 + """ + try: + index = 0 # 기본값 (외부에서 필요 시 파일명 구분용) + + # 0. 이미지 URL 유효성 체크 + if not original_image_url or not isinstance(original_image_url, str): + self.logger.log("배경제거 중단: 이미지 URL 없음 또는 타입 오류", level=logging.WARNING) + return {"status": "failed", "path": original_image_url, "error": "이미지 URL 오류"} + + # 1. 다운로드 또는 로컬 경로 확정 + if original_image_url.startswith("http"): + # 다운로드 재사용을 위해 기존 메서드 호출 + local_path = self.download_image(image_url=original_image_url, index=0, file_prefix=file_prefix) + if not local_path: + return {"status": "failed", "path": original_image_url, "error": "다운로드 실패"} + else: + local_path = original_image_url # 이미 로컬 경로 + + # 2. 배경 제거 (np.ndarray 반환) + removed = self.request_ai_server.request_rembg(local_path, use_local_rembg=self.use_local_rembg, local_model_name=self.local_model_name) + if removed is None: + self.logger.log("RemoveBG 실패", level=logging.ERROR) + return {"status": "failed", "path": local_path, "error": "RemoveBG 실패"} + + # 3. 저장 경로 결정 + os.makedirs(self.TEMP_IMAGE_DIR, exist_ok=True) + base_name = os.path.basename(local_path) + name_no_ext, _ = os.path.splitext(base_name) + save_name = f"nobg_{file_prefix}_{name_no_ext}.png" if file_prefix else f"nobg_{name_no_ext}.png" + save_path = os.path.join(self.TEMP_IMAGE_DIR, save_name) + + # 4. 저장 (OpenCV → BGR) + cv2.imwrite(save_path, removed) + del removed + removed = None + self.logger.log(f"배경제거 이미지 저장: {save_path}", level=logging.INFO) + + # 5. OCR 검사 후 인페인팅 여부 결정 + # ocr_results = self.ocr_module.detect_text(save_path) + ocr_results = self.safe_detect(save_path) + + filter_ocr_results = self.filter_ocr_results(ocr_results) + + # 중국어 텍스트가 없으면 바로 반환 + if not self.ocr_module.filter_chinese_text(filter_ocr_results): + return {"status": "success", "path": save_path} + + # ---- 중국어 텍스트 존재: 인페인팅 준비 ---- + ocr_count = len(filter_ocr_results) + if ocr_count < 5: + expansion_size, blur_size = 12, 15 + elif ocr_count < 10: + expansion_size, blur_size = 9, 12 + elif ocr_count < 15: + expansion_size, blur_size = 7, 9 + elif ocr_count < 20: + expansion_size, blur_size = 5, 6 + else: + expansion_size, blur_size = 10, 15 + + # 마스크 생성 + masks = self.mask_module.create_masks( + image_path=save_path, + ocr_results=filter_ocr_results, + mask_option="basic", + expansion_size=expansion_size, + blur_size=blur_size + ) + self.logger.log("배경제거 후 마스크 생성 완료", level=logging.DEBUG) + + # 인페인팅 수행 (OpenCV Telea) + inpainted_image = self.opencv_inpaint(save_path, masks, method='telea', radius=3) + + # 인페인팅 실패 시 원본 반환 + if inpainted_image is None: + self.logger.log("인페인팅 실패, 배경제거 이미지를 그대로 반환", level=logging.WARNING) + return {"status": "success", "path": save_path} + + # 인페인팅 결과 저장 + inpaint_name = f"inpaint_{file_prefix}_{name_no_ext}.png" if file_prefix else f"inpaint_{name_no_ext}.png" + inpaint_path = os.path.join(self.TEMP_IMAGE_DIR, inpaint_name) + cv2.imwrite(inpaint_path, inpainted_image) + self.logger.log(f"인페인팅 이미지 저장: {inpaint_path}", level=logging.INFO) + return {"status": "success", "path": inpaint_path} + + except Exception as e: + self.logger.log(f"remove_background 오류: {e}", level=logging.ERROR, exc_info=True) + return {"status": "failed", "path": original_image_url, "error": str(e)} + + + + # ------------------------------------------------------------------ + # 고해상도 이미지 다운스케일 유틸리티 (메모리 절감용) + # ------------------------------------------------------------------ + def downscale_image_if_large(self, image_path, max_dim=2048): + """주어진 이미지가 max_dim 픽셀을 초과하면 축소하여 같은 경로에 저장하고 경로를 반환합니다""" + try: + with Image.open(image_path) as img: + width, height = img.size + if max(width, height) <= max_dim: + return image_path # 변경 없음 + + scale = float(max_dim) / float(max(width, height)) + new_size = (int(width * scale), int(height * scale)) + + self.logger.log( + f"고해상도({width}x{height}) -> {new_size}로 리사이즈 후 저장", level=logging.INFO) + + resized = img.resize(new_size, Image.LANCZOS) + + # RGBA 이미지를 JPEG로 저장할 때 에러 방지 + import os + file_ext = os.path.splitext(image_path)[1].lower() + if resized.mode == 'RGBA' and file_ext in ['.jpg', '.jpeg']: + # RGBA를 RGB로 변환 (흰색 배경 사용) + rgb_image = Image.new('RGB', resized.size, (255, 255, 255)) + rgb_image.paste(resized, mask=resized.split()[-1]) # 알파 채널을 마스크로 사용 + resized = rgb_image + self.logger.log(f"RGBA 이미지를 RGB로 변환하여 JPEG 저장", level=logging.INFO) + + # JPG, PNG 등에 관계없이 원본 확장자를 유지하여 덮어쓰기 + resized.save(image_path) + return image_path + except Exception as e: + self.logger.log(f"다운스케일 실패: {e}", level=logging.WARNING) + return image_path diff --git a/modules/old_modules/local_image_server.py b/modules/old_modules/local_image_server.py new file mode 100644 index 0000000..caad46c --- /dev/null +++ b/modules/old_modules/local_image_server.py @@ -0,0 +1,131 @@ +import os +import socket +import threading +from http.server import HTTPServer, SimpleHTTPRequestHandler +import logging + + +class LocalImageServer: + """로컬 이미지 파일을 웹에서 접근 가능하도록 하는 HTTP 서버""" + + def __init__(self, logger, image_dir, port=8000): + self.logger = logger + self.image_dir = os.path.abspath(image_dir) # 절대 경로로 변환 + self.original_cwd = os.getcwd() # 원래 작업 디렉토리 저장 + self.port = self.find_available_port(port) + self.server = None + self.server_thread = None + + def find_available_port(self, start_port=8000, max_port=8100): + """사용 가능한 포트를 찾습니다""" + for port in range(start_port, max_port + 1): + try: + with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: + s.bind(('localhost', port)) + return port + except OSError: + continue + raise RuntimeError(f"포트 {start_port}-{max_port} 범위에서 사용 가능한 포트를 찾을 수 없습니다.") + + def start_server(self): + """HTTP 서버를 시작합니다""" + if self.server_thread and self.server_thread.is_alive(): + self.logger.log(f"로컬 이미지 서버가 이미 포트 {self.port}에서 실행 중입니다.", level=logging.DEBUG) + return + + # 이미지 디렉토리 존재 확인 + if not os.path.exists(self.image_dir): + try: + os.makedirs(self.image_dir, exist_ok=True) + self.logger.log(f"이미지 디렉토리 생성: {self.image_dir}", level=logging.INFO) + except Exception as e: + self.logger.log(f"이미지 디렉토리 생성 실패: {e}", level=logging.ERROR) + raise + + try: + # 작업 디렉토리 변경 없이 CustomHandler에서 직접 경로 처리 + class CustomHandler(SimpleHTTPRequestHandler): + def __init__(self, *args, image_directory=None, **kwargs): + self.image_directory = image_directory + super().__init__(*args, **kwargs) + + def translate_path(self, path): + """요청 경로를 이미지 디렉토리 내의 실제 파일 경로로 변환""" + # 기본 translate_path 호출하여 상대 경로 얻기 + path = super().translate_path(path) + # 현재 작업 디렉토리 대신 이미지 디렉토리 사용 + rel_path = os.path.relpath(path, os.getcwd()) + return os.path.join(self.image_directory, rel_path) + + def log_message(self, format, *args): + # 로그 출력을 비활성화 (너무 많은 로그 방지) + pass + + def end_headers(self): + # CORS 헤더 추가 + self.send_header('Access-Control-Allow-Origin', '*') + self.send_header('Access-Control-Allow-Methods', 'GET, POST, OPTIONS') + self.send_header('Access-Control-Allow-Headers', 'Content-Type') + super().end_headers() + + # 핸들러에 이미지 디렉토리 전달 + def handler_factory(*args, **kwargs): + return CustomHandler(*args, image_directory=self.image_dir, **kwargs) + + self.server = HTTPServer(('localhost', self.port), handler_factory) + self.server_thread = threading.Thread(target=self.server.serve_forever, daemon=True) + self.server_thread.start() + + self.logger.log(f"로컬 이미지 서버가 포트 {self.port}에서 시작되었습니다. (디렉토리: {self.image_dir})", level=logging.INFO) + + except Exception as e: + self.logger.log(f"로컬 웹서버 시작 실패: {e}", level=logging.ERROR) + # 실패 시 상태 정리 + self.server = None + self.server_thread = None + raise + + def stop_server(self): + """HTTP 서버를 중지합니다""" + if self.server: + try: + self.server.shutdown() + self.server.server_close() + if self.server_thread and self.server_thread.is_alive(): + self.server_thread.join(timeout=5) + self.logger.log("로컬 이미지 서버가 중지되었습니다.", level=logging.INFO) + except Exception as e: + self.logger.log(f"로컬 웹서버 중지 중 오류 발생: {e}", level=logging.ERROR) + finally: + self.server = None + self.server_thread = None + + def restart_server(self): + """서버를 재시작합니다""" + self.logger.log("로컬 이미지 서버 재시작 중...", level=logging.INFO) + self.stop_server() + # 새로운 포트 찾기 + self.port = self.find_available_port(self.port) + self.start_server() + + def get_base_url(self): + """서버의 기본 URL을 반환합니다""" + return f"http://localhost:{self.port}" + + def is_running(self): + """서버가 실행 중인지 확인합니다""" + return self.server is not None and self.server_thread and self.server_thread.is_alive() + + def get_file_url(self, filename): + """특정 파일의 URL을 반환합니다""" + if not self.is_running(): + self.logger.log("서버가 실행되지 않았습니다.", level=logging.WARNING) + return None + return f"{self.get_base_url()}/{filename}" + + def __del__(self): + """소멸자에서 서버 정리""" + try: + self.stop_server() + except: + pass \ No newline at end of file diff --git a/modules/old_modules/mask_module_for_easy.py b/modules/old_modules/mask_module_for_easy.py new file mode 100644 index 0000000..0b7089c --- /dev/null +++ b/modules/old_modules/mask_module_for_easy.py @@ -0,0 +1,99 @@ +# -*- coding: utf-8 -*- +""" +마스크 모듈 - OCR 결과를 기반으로 마스크 생성 및 처리 (라이브러리화) +확장, 블러 등 basic 마스크 처리만 지원 +""" + +import cv2 +import numpy as np +from typing import List, Dict, Any +from shapely.geometry import Polygon +import os +import logging + +class MaskModule_easy: + def __init__(self, logger, base_dir): + self.logger = logger + self.base_dir = base_dir + self.logger.log("마스크 모듈 초기화 완료", level=logging.DEBUG) + + def create_masks(self, image_path: str, ocr_results: List[Dict], expansion_size: int = 0, blur_size: int = 0, mask_option: str = "basic") -> np.ndarray: + image = cv2.imread(image_path) + if image is None: + self.logger.log(f"이미지를 읽을 수 없습니다: {image_path}", level=logging.ERROR) + return None + height, width = image.shape[:2] + mask = np.zeros((height, width), dtype=np.uint8) + + # self.logger.log(f"[마스크] OCR 결과 개수: {len(ocr_results)}, 이미지 크기: {width}x{height}", level=logging.DEBUG) + + for i, result in enumerate(ocr_results, 1): + polygon = result['polygon'] + text = result.get('text', '') + # self.logger.log(f"[마스크] {i}번째 텍스트: '{text}', 원본 polygon: {polygon}", level=logging.DEBUG) + + # 확장 없이 원본 폴리곤 그대로 사용 + original_poly = np.array(polygon, dtype=np.int32) + # self.logger.log(f"[마스크] {i}번째 원본 polygon shape: {original_poly.shape}, 값: {original_poly}", level=logging.DEBUG) + + # polygon이 (N,2) np.int32인지 체크 + if original_poly.ndim != 2 or original_poly.shape[1] != 2 or original_poly.shape[0] < 3: + # self.logger.log(f"[마스크] 잘못된 폴리곤 shape: {original_poly.shape}, 값: {original_poly}", level=logging.WARNING) + continue + + cv2.fillPoly(mask, [original_poly], 255) + # self.logger.log(f"[마스크] {i}번째 폴리곤 마스크 적용 완료", level=logging.DEBUG) + + # 마스크에 실제로 255 값이 있는지 확인 + mask_pixels_before = np.sum(mask > 0) + # self.logger.log(f"[마스크] Erosion 전 마스크 픽셀 개수: {mask_pixels_before} / {height * width}", level=logging.DEBUG) + + # Erosion 적용 (마스크를 글자 주변으로 축소) + erosion_kernel_size = 6 # 3x3 커널로 적당히 축소 + erosion_kernel = np.ones((erosion_kernel_size, erosion_kernel_size), np.uint8) + eroded_mask = cv2.erode(mask, erosion_kernel, iterations=1) + + # Erosion 후 마스크 픽셀 개수 확인 + mask_pixels_after = np.sum(eroded_mask > 0) + # self.logger.log(f"[마스크] Erosion 후 마스크 픽셀 개수: {mask_pixels_after} / {height * width} (감소: {mask_pixels_before - mask_pixels_after}픽셀)", level=logging.DEBUG) + + # 기본 마스크만 사용 (erosion 적용된 마스크 반환) + if mask_option == "basic": + return eroded_mask + else: + # 다른 옵션일 경우에만 추가 후처리 적용 + processed_mask = self.process_mask(eroded_mask, expansion_size, blur_size) + return processed_mask + + def expand_polygon(self, polygon, offset=15): + try: + poly = Polygon(polygon) + expanded = poly.buffer(offset) + if expanded.is_empty: + return np.array(polygon, dtype=np.int32) + + # exterior.coords는 마지막 점이 첫 번째 점과 동일하므로 제거 + coords = list(expanded.exterior.coords)[:-1] + + # 정수 좌표로 변환 + coords = [[int(x), int(y)] for x, y in coords] + + # 최소 3점 이상인지 확인 + if len(coords) < 3: + return np.array(polygon, dtype=np.int32) + + return np.array(coords, dtype=np.int32) + except Exception as e: + self.logger.log(f"[마스크] expand_polygon 오류: {e}, 원본 polygon 사용", level=logging.WARNING) + return np.array(polygon, dtype=np.int32) + + def process_mask(self, mask: np.ndarray, expansion_size: int = 5, blur_size: int = 3) -> np.ndarray: + processed_mask = mask.copy() + if expansion_size > 0: + kernel = np.ones((expansion_size, expansion_size), np.uint8) + processed_mask = cv2.dilate(processed_mask, kernel, iterations=1) + if blur_size > 0: + blur_size = blur_size if blur_size % 2 == 1 else blur_size + 1 + processed_mask = cv2.GaussianBlur(processed_mask, (blur_size, blur_size), 0) + return processed_mask + \ No newline at end of file diff --git a/modules/old_modules/settings_manager.py b/modules/old_modules/settings_manager.py new file mode 100644 index 0000000..f29c793 --- /dev/null +++ b/modules/old_modules/settings_manager.py @@ -0,0 +1,280 @@ +from PySide6.QtCore import QSettings +import logging + +class SettingsManager: + """ + 사용자 설정값(토글, 스핀, 텍스트 등)을 저장/불러오고, + 각종 위젯에 따라 UI 상태까지 자동 적용하는 매니저 클래스입니다. + """ + + def __init__(self, logger=None, organization="WhenRideMycar", application="EditPartTimer3"): + """ + QSettings 기반 설정 매니저 초기화 + + :param logger: logging.Logger 또는 print 대체용 함수 + :param organization: QSettings 구분용 회사명 + :param application: QSettings 구분용 앱명 + """ + self.logger = logger or logging.getLogger(__name__) + self.settings = QSettings(organization, application) + self.widget_map = {} # 위젯명 → 저장키 매핑 + self.toggle_dependencies = {} # 토글명 → 종속 위젯 매핑 + + def _log(self, message, level=logging.INFO): + """ + 커스텀/표준 로거 모두 지원하는 내부 로깅 함수 + """ + if hasattr(self.logger, "log") and "level" in self.logger.log.__code__.co_varnames: + # 커스텀 로거: self.logger.log(msg, level=logging.INFO) + self.logger.log(message, level=level) + else: + # 표준 로거 + if level == logging.DEBUG: + self.logger.debug(message) + elif level == logging.INFO: + self.logger.info(message) + elif level == logging.WARNING: + self.logger.warning(message) + elif level == logging.ERROR: + self.logger.error(message) + elif level == logging.CRITICAL: + self.logger.critical(message) + else: + self.logger.info(message) + + def bind_widgets(self, widget_map, toggle_dependencies): + """ + 위젯-키, 토글-종속 딕셔너리를 등록합니다. + :param widget_map: { "widget명": "저장키" } + :param toggle_dependencies: { "toggle명": { "dependents": [...], "visible": [...] } } + """ + self.widget_map = widget_map + self.toggle_dependencies = toggle_dependencies + + def save_settings(self, widget_obj): + """ + 현재 UI에 연결된 각종 위젯의 값을 QSettings에 저장합니다. + + :param widget_obj: 실제 위젯 객체(self 등) + + config 예시: + { + 'discord_notify_toggle': { + 'dependents': ['webhook_input'], + 'visible': ['webhook_input'], + }, + 'ocr_toggle': { + 'dependents': ['unwanted_words_button'] + }, + ... + } + """ + for widget_name, key in self.widget_map.items(): + widget = getattr(widget_obj, widget_name, None) + if widget is None: + self._log(f"[SettingsManager] '{widget_name}' 위젯을 찾을 수 없습니다.", logging.WARNING) + + continue + + try: + # 체크박스, 토글, 커스텀토글 등 + if hasattr(widget, 'isChecked'): + value = bool(widget.isChecked()) + self.logger.log(f"[SettingsManager] bool 타입 저장: {key} 값 저장: {value}", level=logging.DEBUG) + self.settings.setValue(key, value) + # SpinBox/DoubleSpinBox 등 + elif hasattr(widget, 'value'): + self.settings.setValue(key, widget.value()) + self.logger.log(f"[SettingsManager] int/float 타입 저장: {key} 값 저장: {widget.value()}", level=logging.DEBUG) + # QLineEdit 등 (단일줄 텍스트) + elif hasattr(widget, 'text'): + self.settings.setValue(key, widget.text()) + self.logger.log(f"[SettingsManager] str 타입 저장: {key} 값 저장: {widget.text()}", level=logging.DEBUG) + # QTextEdit 등 (여러줄 텍스트) + elif hasattr(widget, 'toPlainText'): + self.settings.setValue(key, widget.toPlainText()) + self.logger.log(f"[SettingsManager] str 타입 저장: {key} 값 저장: {widget.toPlainText()}", level=logging.DEBUG) + else: + self._log(f"[SettingsManager] '{widget_name}'의 값을 저장하는 방법을 알 수 없습니다.", logging.WARNING) + + except Exception as e: + self._log(f"[SettingsManager] '{widget_name}' 저장 중 오류: {e}", logging.ERROR, exc_info=True) + + def save_value(self, key, value): + """특정 키로 단일 값을 설정에 저장합니다. 타입별로 정확히 저장.""" + try: + # bool + if isinstance(value, bool): + self.settings.setValue(key, value) + # self.logger.log(f"[SettingsManager] bool 타입 저장: {key} 값 저장: {value}", level=logging.DEBUG) + # int/float + elif isinstance(value, (int, float)): + self.settings.setValue(key, value) + # self.logger.log(f"[SettingsManager] int/float 타입 저장: {key} 값 저장: {value}", level=logging.DEBUG) + # 그 외(특히 str) + else: + self.settings.setValue(key, str(value)) + # self.logger.log(f"[SettingsManager] str 타입 저장: {key} 값 저장: {value}", level=logging.DEBUG) + self.settings.sync() + # self._log(f"[SettingsManager] {key} 값을 저장했습니다: {value}", level=logging.DEBUG) + except Exception as e: + self._log(f"[SettingsManager] {key} 값 저장 중 오류: {e}", level=logging.ERROR, exc_info=True) + + # 기타 값 직접 접근용 + def get_value(self, key, default=None): + return self.settings.value(key, default) + + def load_settings(self, widget_obj): + """ + QSettings에서 저장된 값을 각 위젯에 복원합니다. + + :param widget_obj: 실제 위젯 객체(self 등) + """ + for widget_name, key in self.widget_map.items(): + widget = getattr(widget_obj, widget_name, None) + if widget is None: + self._log(f"[SettingsManager] '{widget_name}' 위젯을 찾을 수 없습니다.", logging.WARNING) + continue + + val = self.settings.value(key, None) + if val is None: + continue # 미저장 값 + + try: + # 체크박스/토글 등 (bool 변환) + if hasattr(widget, 'setChecked'): + # QSettings는 bool을 str로 저장할 수 있어 안전하게 변환 + if isinstance(val, bool): + widget.setChecked(val) + elif isinstance(val, (int, float)): + widget.setChecked(bool(val)) + elif isinstance(val, str): + widget.setChecked(val.lower() in ['true', '1', 'yes']) + # SpinBox류 + elif hasattr(widget, 'setValue'): + if isinstance(val, (int, float)): + widget.setValue(val) + elif isinstance(val, str): + if '.' in val: + widget.setValue(float(val)) + else: + widget.setValue(int(val)) + # QLineEdit 등 + elif hasattr(widget, 'setText'): + widget.setText(str(val)) + # QTextEdit 등 + elif hasattr(widget, 'setPlainText'): + widget.setPlainText(str(val)) + else: + self._log(f"[SettingsManager] '{widget_name}'에 값을 복원할 수 없습니다.", logging.WARNING) + except Exception as e: + self._log(f"[SettingsManager] '{widget_name}' 불러오기 오류: {e}", logging.ERROR, exc_info=True) + + def reset_settings(self): + """ + 전체 QSettings 값을 초기화(삭제)합니다. + """ + try: + self.settings.clear() + self._log("[SettingsManager] 모든 설정이 초기화되었습니다.", logging.DEBUG) + except Exception as e: + self._log(f"[SettingsManager] 설정 초기화 실패: {e}", logging.ERROR, exc_info=True) + + def remove_setting(self, key): + """ + 특정 키의 설정만 삭제합니다. + + :param key: 삭제할 설정 키(str) + """ + try: + self.settings.remove(key) + self._log(f"[SettingsManager] '{key}' 설정이 삭제되었습니다.", logging.DEBUG) + except Exception as e: + self._log(f"[SettingsManager] '{key}' 삭제 실패: {e}", logging.ERROR, exc_info=True) + + def debug_print(self): + """ + QSettings에 저장된 모든 값을 로그로 출력합니다. + """ + try: + self.settings.sync() + all_keys = self.settings.allKeys() + self.logger.debug("[SettingsManager] 저장된 모든 설정값:") + for key in all_keys: + value = self.settings.value(key) + self.logger.debug(f" {key}: {value}") + + # 위젯 매핑 정보도 출력 + self.logger.debug(f"[SettingsManager] 위젯 매핑: {len(self.widget_map)}개") + for widget, key in self.widget_map.items(): + self.logger.debug(f" {widget} -> {key}") + + # 토글 종속성 정보도 출력 + self.logger.debug(f"[SettingsManager] 토글 종속성: {len(self.toggle_dependencies)}개") + for toggle, deps in self.toggle_dependencies.items(): + self.logger.debug(f" {toggle}: {deps}") + + except Exception as e: + self._log(f"[SettingsManager] 설정값 출력 실패: {e}", logging.ERROR) + + def apply_settings_to_ui(self, widget_obj): + """ + 종속 딕셔너리(toggle_dependencies)에 따라, + 토글/체크박스 등 상태에 따라 dependents/visible 위젯을 자동으로 활성/비활성, 표시/숨김 처리합니다. + + :param widget_obj: 실제 위젯 객체(self 등) + """ + try: + for toggle_name, deps in self.toggle_dependencies.items(): + toggle_widget = getattr(widget_obj, toggle_name, None) + if toggle_widget is None or not hasattr(toggle_widget, "isChecked"): + continue + + checked = toggle_widget.isChecked() + + # 종속 위젯 enable/disable + for dep in deps.get("dependents", []): + dep_widget = getattr(widget_obj, dep, None) + if dep_widget and hasattr(dep_widget, "setEnabled"): + try: + dep_widget.setEnabled(checked) + self._log(f"[SettingsManager] {dep} {'활성화' if checked else '비활성화'}", logging.DEBUG) + except Exception as e: + self._log(f"[SettingsManager] {dep} setEnabled 실패: {e}", logging.ERROR) + + # 종속 위젯 visible + for vis in deps.get("visible", []): + vis_widget = getattr(widget_obj, vis, None) + if vis_widget and hasattr(vis_widget, "setVisible"): + try: + vis_widget.setVisible(checked) + self._log(f"[SettingsManager] {vis} {'표시' if checked else '숨김'}", logging.DEBUG) + except Exception as e: + self._log(f"[SettingsManager] {vis} setVisible 실패: {e}", logging.ERROR) + + self._log("[SettingsManager] UI 종속성 적용 완료", logging.DEBUG) + + except Exception as e: + self._log(f"[SettingsManager] UI 종속성 적용 중 오류: {e}", logging.ERROR) + + + + + + + + + + + + def save_user_info(self, user_info: dict): + for key, value in user_info.items(): + self.settings.setValue(f"user/{key}", value) + self.settings.sync() + + def load_user_info(self) -> dict: + info = {} + for key in ["email", "password", "id", "membership_level", "name"]: + info[key] = self.settings.value(f"user/{key}", "") + return info + \ No newline at end of file diff --git a/modules/onnx_ocr_module/README.md b/modules/onnx_ocr_module/README.md new file mode 100644 index 0000000..73f55d9 --- /dev/null +++ b/modules/onnx_ocr_module/README.md @@ -0,0 +1,159 @@ +# ONNX OCR 모듈 + +기존 PaddleOCR 기반 OCRModule을 완전히 대체할 수 있는 ONNX Runtime 기반 OCR 모듈입니다. + +## 🚀 성능 비교 + +벤치마크 결과 (테스트 이미지 기준): + +| 모델 | 시간(ms) | 텍스트 수 | 성능 비교 | +|------|----------|-----------|-----------| +| **ONNX CUDA** | **108.5** | 18 | 🥇 **최고 성능** | +| ONNX CPU | 174.8 | 18 | 1.61배 느림 | +| Paddle CPU | 193.4 | 14 | 1.78배 느림 | +| Paddle CUDA | 191.8 | 14 | 1.77배 느림 | + +**결론**: ONNX CUDA가 PaddlePaddle보다 **약 1.77배 빠른 성능**을 보여줍니다! + +## 📁 폴더 구조 + +``` +onnx_ocr_module/ +├── models/ # ONNX 모델 파일들 +│ ├── det_dyn.*.onnx # 텍스트 감지 모델 +│ ├── rec_dyn.*.onnx # 텍스트 인식 모델 +│ └── cls_dyn.*.onnx # 텍스트 방향 분류 모델 +├── dict/ # 문자 사전 +│ └── ppocr_keys_v1.txt +├── src/ # 소스 코드 +│ ├── __init__.py +│ └── onnx_ocr_module.py +├── test/ # 테스트 코드 +│ ├── test_onnx_ocr.py +│ └── 1.jpg +└── README.md +``` + +## 🔧 사용법 + +### 기본 사용법 + +```python +from onnx_ocr_module import ONNXOCRModule + +# 모듈 초기화 (기존 OCRModule과 동일한 인터페이스) +ocr_module = ONNXOCRModule( + logger=your_logger, + gpu_manager=your_gpu_manager, + execution_provider='auto' # 'auto', 'cpu', 'cuda', 'tensorrt' +) + +# 텍스트 감지 (기존과 동일한 인터페이스) +results = ocr_module.detect_text("image.jpg", method='polygon') + +for result in results: + print(f"텍스트: {result['text']}") + print(f"신뢰도: {result['confidence']}") + print(f"좌표: {result['bbox']}") +``` + +### 실행 프로바이더 선택 + +```python +# CPU 사용 +ocr_cpu = ONNXOCRModule(execution_provider='cpu') + +# CUDA 사용 (NVIDIA GPU 필요) +ocr_cuda = ONNXOCRModule(execution_provider='cuda') + +# TensorRT 사용 (TensorRT 설치 필요) +ocr_tensorrt = ONNXOCRModule(execution_provider='tensorrt') + +# 자동 선택 (추천) +ocr_auto = ONNXOCRModule(execution_provider='auto') +``` + +### 감지 방식 + +기존 OCRModule과 동일한 5가지 감지 방식을 지원합니다: + +```python +# 1. 폴리곤 방식 (기본) +results = ocr_module.detect_text("image.jpg", method='polygon') + +# 2. 바운딩 박스 방식 +results = ocr_module.detect_text("image.jpg", method='bbox') + +# 3. 확장된 바운딩 박스 방식 +results = ocr_module.detect_text("image.jpg", method='expanded_bbox') + +# 4. 회전된 바운딩 박스 방식 +results = ocr_module.detect_text("image.jpg", method='rotated_bbox') + +# 5. 컨투어 방식 +results = ocr_module.detect_text("image.jpg", method='contour') +``` + +### 텍스트 필터링 + +```python +# 중국어 텍스트만 필터링 +chinese_results = ocr_module.filter_chinese_text(results) + +# 한글 텍스트만 필터링 +korean_results = ocr_module.filter_korean_text(results) +``` + +## 📊 모델 종류 + +실행 프로바이더에 따라 최적화된 모델을 자동 선택합니다: + +- **CPU**: `*_dyn.simp.onnx` (단순화 모델) +- **CUDA**: `*_dyn.fp16.onnx` (FP16 모델) +- **TensorRT**: `*_dyn.opt.onnx` (최적화 모델) + +## 🛠️ 설치 요구사항 + +```bash +pip install onnxruntime-gpu # GPU 사용 시 +pip install onnxruntime # CPU만 사용 시 +pip install opencv-python +pip install numpy +``` + +## 🧪 테스트 실행 + +```bash +cd onnx_ocr_module +python test/test_onnx_ocr.py +``` + +## 🔄 기존 OCRModule 대체 + +기존 코드 수정 없이 완전 대체 가능합니다: + +```python +# 기존 코드 +# from src.modules.ocr_module import OCRModule + +# 새 코드 (인터페이스 동일) +from onnx_ocr_module import OCRModule # 또는 ONNXOCRModule +``` + +## ⚡ 성능 특징 + +- **빠른 속도**: PaddlePaddle 대비 최대 1.77배 빠름 +- **낮은 메모리**: 최적화된 ONNX 모델 사용 +- **범용성**: 다양한 플랫폼 지원 +- **GPU 가속**: CUDA/TensorRT 지원 +- **호환성**: 기존 OCRModule과 100% 호환 + +## 🎯 특장점 + +1. **완벽한 호환성**: 기존 OCRModule API와 100% 동일 +2. **더 빠른 성능**: 특히 CUDA 환경에서 우수한 성능 +3. **유연한 백엔드**: CPU/CUDA/TensorRT 자동 선택 +4. **메모리 최적화**: 안전한 이미지 크기 조정 및 메모리 관리 +5. **다양한 감지 방식**: 5가지 텍스트 영역 감지 방식 지원 + +기존 PaddleOCR 모듈을 그대로 대체하면서 더 빠른 성능을 원한다면 이 ONNX 모듈을 사용하세요! 🚀 diff --git a/modules/onnx_ocr_module/__init__.py b/modules/onnx_ocr_module/__init__.py new file mode 100644 index 0000000..57df9f6 --- /dev/null +++ b/modules/onnx_ocr_module/__init__.py @@ -0,0 +1,9 @@ +# -*- coding: utf-8 -*- +""" +ONNX OCR 모듈 루트 패키지 +""" + +from .src.onnx_ocr_wrapper import ONNXOCRModule, OCRModule + +__version__ = "1.0.0" +__all__ = ["ONNXOCRModule", "OCRModule"] diff --git a/modules/onnx_ocr_module/__pycache__/__init__.cpython-311.pyc b/modules/onnx_ocr_module/__pycache__/__init__.cpython-311.pyc new file mode 100644 index 0000000..271f32e Binary files /dev/null and b/modules/onnx_ocr_module/__pycache__/__init__.cpython-311.pyc differ diff --git a/modules/onnx_ocr_module/dict/ppocr_keys_v1.txt b/modules/onnx_ocr_module/dict/ppocr_keys_v1.txt new file mode 100644 index 0000000..b75af21 --- /dev/null +++ b/modules/onnx_ocr_module/dict/ppocr_keys_v1.txt @@ -0,0 +1,6623 @@ +' +疗 +绚 +诚 +娇 +溜 +题 +贿 +者 +廖 +更 +纳 +加 +奉 +公 +一 +就 +汴 +计 +与 +路 +房 +原 +妇 +2 +0 +8 +- +7 +其 +> +: +] +, +, +骑 +刈 +全 +消 +昏 +傈 +安 +久 +钟 +嗅 +不 +影 +处 +驽 +蜿 +资 +关 +椤 +地 +瘸 +专 +问 +忖 +票 +嫉 +炎 +韵 +要 +月 +田 +节 +陂 +鄙 +捌 +备 +拳 +伺 +眼 +网 +盎 +大 +傍 +心 +东 +愉 +汇 +蹿 +科 +每 +业 +里 +航 +晏 +字 +平 +录 +先 +1 +3 +彤 +鲶 +产 +稍 +督 +腴 +有 +象 +岳 +注 +绍 +在 +泺 +文 +定 +核 +名 +水 +过 +理 +让 +偷 +率 +等 +这 +发 +” +为 +含 +肥 +酉 +相 +鄱 +七 +编 +猥 +锛 +日 +镀 +蒂 +掰 +倒 +辆 +栾 +栗 +综 +涩 +州 +雌 +滑 +馀 +了 +机 +块 +司 +宰 +甙 +兴 +矽 +抚 +保 +用 +沧 +秩 +如 +收 +息 +滥 +页 +疑 +埠 +! +! +姥 +异 +橹 +钇 +向 +下 +跄 +的 +椴 +沫 +国 +绥 +獠 +报 +开 +民 +蜇 +何 +分 +凇 +长 +讥 +藏 +掏 +施 +羽 +中 +讲 +派 +嘟 +人 +提 +浼 +间 +世 +而 +古 +多 +倪 +唇 +饯 +控 +庚 +首 +赛 +蜓 +味 +断 +制 +觉 +技 +替 +艰 +溢 +潮 +夕 +钺 +外 +摘 +枋 +动 +双 +单 +啮 +户 +枇 +确 +锦 +曜 +杜 +或 +能 +效 +霜 +盒 +然 +侗 +电 +晁 +放 +步 +鹃 +新 +杖 +蜂 +吒 +濂 +瞬 +评 +总 +隍 +对 +独 +合 +也 +是 +府 +青 +天 +诲 +墙 +组 +滴 +级 +邀 +帘 +示 +已 +时 +骸 +仄 +泅 +和 +遨 +店 +雇 +疫 +持 +巍 +踮 +境 +只 +亨 +目 +鉴 +崤 +闲 +体 +泄 +杂 +作 +般 +轰 +化 +解 +迂 +诿 +蛭 +璀 +腾 +告 +版 +服 +省 +师 +小 +规 +程 +线 +海 +办 +引 +二 +桧 +牌 +砺 +洄 +裴 +修 +图 +痫 +胡 +许 +犊 +事 +郛 +基 +柴 +呼 +食 +研 +奶 +律 +蛋 +因 +葆 +察 +戏 +褒 +戒 +再 +李 +骁 +工 +貂 +油 +鹅 +章 +啄 +休 +场 +给 +睡 +纷 +豆 +器 +捎 +说 +敏 +学 +会 +浒 +设 +诊 +格 +廓 +查 +来 +霓 +室 +溆 +¢ +诡 +寥 +焕 +舜 +柒 +狐 +回 +戟 +砾 +厄 +实 +翩 +尿 +五 +入 +径 +惭 +喹 +股 +宇 +篝 +| +; +美 +期 +云 +九 +祺 +扮 +靠 +锝 +槌 +系 +企 +酰 +阊 +暂 +蚕 +忻 +豁 +本 +羹 +执 +条 +钦 +H +獒 +限 +进 +季 +楦 +于 +芘 +玖 +铋 +茯 +未 +答 +粘 +括 +样 +精 +欠 +矢 +甥 +帷 +嵩 +扣 +令 +仔 +风 +皈 +行 +支 +部 +蓉 +刮 +站 +蜡 +救 +钊 +汗 +松 +嫌 +成 +可 +. +鹤 +院 +从 +交 +政 +怕 +活 +调 +球 +局 +验 +髌 +第 +韫 +谗 +串 +到 +圆 +年 +米 +/ +* +友 +忿 +检 +区 +看 +自 +敢 +刃 +个 +兹 +弄 +流 +留 +同 +没 +齿 +星 +聆 +轼 +湖 +什 +三 +建 +蛔 +儿 +椋 +汕 +震 +颧 +鲤 +跟 +力 +情 +璺 +铨 +陪 +务 +指 +族 +训 +滦 +鄣 +濮 +扒 +商 +箱 +十 +召 +慷 +辗 +所 +莞 +管 +护 +臭 +横 +硒 +嗓 +接 +侦 +六 +露 +党 +馋 +驾 +剖 +高 +侬 +妪 +幂 +猗 +绺 +骐 +央 +酐 +孝 +筝 +课 +徇 +缰 +门 +男 +西 +项 +句 +谙 +瞒 +秃 +篇 +教 +碲 +罚 +声 +呐 +景 +前 +富 +嘴 +鳌 +稀 +免 +朋 +啬 +睐 +去 +赈 +鱼 +住 +肩 +愕 +速 +旁 +波 +厅 +健 +茼 +厥 +鲟 +谅 +投 +攸 +炔 +数 +方 +击 +呋 +谈 +绩 +别 +愫 +僚 +躬 +鹧 +胪 +炳 +招 +喇 +膨 +泵 +蹦 +毛 +结 +5 +4 +谱 +识 +陕 +粽 +婚 +拟 +构 +且 +搜 +任 +潘 +比 +郢 +妨 +醪 +陀 +桔 +碘 +扎 +选 +哈 +骷 +楷 +亿 +明 +缆 +脯 +监 +睫 +逻 +婵 +共 +赴 +淝 +凡 +惦 +及 +达 +揖 +谩 +澹 +减 +焰 +蛹 +番 +祁 +柏 +员 +禄 +怡 +峤 +龙 +白 +叽 +生 +闯 +起 +细 +装 +谕 +竟 +聚 +钙 +上 +导 +渊 +按 +艾 +辘 +挡 +耒 +盹 +饪 +臀 +记 +邮 +蕙 +受 +各 +医 +搂 +普 +滇 +朗 +茸 +带 +翻 +酚 +( +光 +堤 +墟 +蔷 +万 +幻 +〓 +瑙 +辈 +昧 +盏 +亘 +蛀 +吉 +铰 +请 +子 +假 +闻 +税 +井 +诩 +哨 +嫂 +好 +面 +琐 +校 +馊 +鬣 +缂 +营 +访 +炖 +占 +农 +缀 +否 +经 +钚 +棵 +趟 +张 +亟 +吏 +茶 +谨 +捻 +论 +迸 +堂 +玉 +信 +吧 +瞠 +乡 +姬 +寺 +咬 +溏 +苄 +皿 +意 +赉 +宝 +尔 +钰 +艺 +特 +唳 +踉 +都 +荣 +倚 +登 +荐 +丧 +奇 +涵 +批 +炭 +近 +符 +傩 +感 +道 +着 +菊 +虹 +仲 +众 +懈 +濯 +颞 +眺 +南 +释 +北 +缝 +标 +既 +茗 +整 +撼 +迤 +贲 +挎 +耱 +拒 +某 +妍 +卫 +哇 +英 +矶 +藩 +治 +他 +元 +领 +膜 +遮 +穗 +蛾 +飞 +荒 +棺 +劫 +么 +市 +火 +温 +拈 +棚 +洼 +转 +果 +奕 +卸 +迪 +伸 +泳 +斗 +邡 +侄 +涨 +屯 +萋 +胭 +氡 +崮 +枞 +惧 +冒 +彩 +斜 +手 +豚 +随 +旭 +淑 +妞 +形 +菌 +吲 +沱 +争 +驯 +歹 +挟 +兆 +柱 +传 +至 +包 +内 +响 +临 +红 +功 +弩 +衡 +寂 +禁 +老 +棍 +耆 +渍 +织 +害 +氵 +渑 +布 +载 +靥 +嗬 +虽 +苹 +咨 +娄 +库 +雉 +榜 +帜 +嘲 +套 +瑚 +亲 +簸 +欧 +边 +6 +腿 +旮 +抛 +吹 +瞳 +得 +镓 +梗 +厨 +继 +漾 +愣 +憨 +士 +策 +窑 +抑 +躯 +襟 +脏 +参 +贸 +言 +干 +绸 +鳄 +穷 +藜 +音 +折 +详 +) +举 +悍 +甸 +癌 +黎 +谴 +死 +罩 +迁 +寒 +驷 +袖 +媒 +蒋 +掘 +模 +纠 +恣 +观 +祖 +蛆 +碍 +位 +稿 +主 +澧 +跌 +筏 +京 +锏 +帝 +贴 +证 +糠 +才 +黄 +鲸 +略 +炯 +饱 +四 +出 +园 +犀 +牧 +容 +汉 +杆 +浈 +汰 +瑷 +造 +虫 +瘩 +怪 +驴 +济 +应 +花 +沣 +谔 +夙 +旅 +价 +矿 +以 +考 +s +u +呦 +晒 +巡 +茅 +准 +肟 +瓴 +詹 +仟 +褂 +译 +桌 +混 +宁 +怦 +郑 +抿 +些 +余 +鄂 +饴 +攒 +珑 +群 +阖 +岔 +琨 +藓 +预 +环 +洮 +岌 +宀 +杲 +瀵 +最 +常 +囡 +周 +踊 +女 +鼓 +袭 +喉 +简 +范 +薯 +遐 +疏 +粱 +黜 +禧 +法 +箔 +斤 +遥 +汝 +奥 +直 +贞 +撑 +置 +绱 +集 +她 +馅 +逗 +钧 +橱 +魉 +[ +恙 +躁 +唤 +9 +旺 +膘 +待 +脾 +惫 +购 +吗 +依 +盲 +度 +瘿 +蠖 +俾 +之 +镗 +拇 +鲵 +厝 +簧 +续 +款 +展 +啃 +表 +剔 +品 +钻 +腭 +损 +清 +锶 +统 +涌 +寸 +滨 +贪 +链 +吠 +冈 +伎 +迥 +咏 +吁 +览 +防 +迅 +失 +汾 +阔 +逵 +绀 +蔑 +列 +川 +凭 +努 +熨 +揪 +利 +俱 +绉 +抢 +鸨 +我 +即 +责 +膦 +易 +毓 +鹊 +刹 +玷 +岿 +空 +嘞 +绊 +排 +术 +估 +锷 +违 +们 +苟 +铜 +播 +肘 +件 +烫 +审 +鲂 +广 +像 +铌 +惰 +铟 +巳 +胍 +鲍 +康 +憧 +色 +恢 +想 +拷 +尤 +疳 +知 +S +Y +F +D +A +峄 +裕 +帮 +握 +搔 +氐 +氘 +难 +墒 +沮 +雨 +叁 +缥 +悴 +藐 +湫 +娟 +苑 +稠 +颛 +簇 +后 +阕 +闭 +蕤 +缚 +怎 +佞 +码 +嘤 +蔡 +痊 +舱 +螯 +帕 +赫 +昵 +升 +烬 +岫 +、 +疵 +蜻 +髁 +蕨 +隶 +烛 +械 +丑 +盂 +梁 +强 +鲛 +由 +拘 +揉 +劭 +龟 +撤 +钩 +呕 +孛 +费 +妻 +漂 +求 +阑 +崖 +秤 +甘 +通 +深 +补 +赃 +坎 +床 +啪 +承 +吼 +量 +暇 +钼 +烨 +阂 +擎 +脱 +逮 +称 +P +神 +属 +矗 +华 +届 +狍 +葑 +汹 +育 +患 +窒 +蛰 +佼 +静 +槎 +运 +鳗 +庆 +逝 +曼 +疱 +克 +代 +官 +此 +麸 +耧 +蚌 +晟 +例 +础 +榛 +副 +测 +唰 +缢 +迹 +灬 +霁 +身 +岁 +赭 +扛 +又 +菡 +乜 +雾 +板 +读 +陷 +徉 +贯 +郁 +虑 +变 +钓 +菜 +圾 +现 +琢 +式 +乐 +维 +渔 +浜 +左 +吾 +脑 +钡 +警 +T +啵 +拴 +偌 +漱 +湿 +硕 +止 +骼 +魄 +积 +燥 +联 +踢 +玛 +则 +窿 +见 +振 +畿 +送 +班 +钽 +您 +赵 +刨 +印 +讨 +踝 +籍 +谡 +舌 +崧 +汽 +蔽 +沪 +酥 +绒 +怖 +财 +帖 +肱 +私 +莎 +勋 +羔 +霸 +励 +哼 +帐 +将 +帅 +渠 +纪 +婴 +娩 +岭 +厘 +滕 +吻 +伤 +坝 +冠 +戊 +隆 +瘁 +介 +涧 +物 +黍 +并 +姗 +奢 +蹑 +掣 +垸 +锴 +命 +箍 +捉 +病 +辖 +琰 +眭 +迩 +艘 +绌 +繁 +寅 +若 +毋 +思 +诉 +类 +诈 +燮 +轲 +酮 +狂 +重 +反 +职 +筱 +县 +委 +磕 +绣 +奖 +晋 +濉 +志 +徽 +肠 +呈 +獐 +坻 +口 +片 +碰 +几 +村 +柿 +劳 +料 +获 +亩 +惕 +晕 +厌 +号 +罢 +池 +正 +鏖 +煨 +家 +棕 +复 +尝 +懋 +蜥 +锅 +岛 +扰 +队 +坠 +瘾 +钬 +@ +卧 +疣 +镇 +譬 +冰 +彷 +频 +黯 +据 +垄 +采 +八 +缪 +瘫 +型 +熹 +砰 +楠 +襁 +箐 +但 +嘶 +绳 +啤 +拍 +盥 +穆 +傲 +洗 +盯 +塘 +怔 +筛 +丿 +台 +恒 +喂 +葛 +永 +¥ +烟 +酒 +桦 +书 +砂 +蚝 +缉 +态 +瀚 +袄 +圳 +轻 +蛛 +超 +榧 +遛 +姒 +奘 +铮 +右 +荽 +望 +偻 +卡 +丶 +氰 +附 +做 +革 +索 +戚 +坨 +桷 +唁 +垅 +榻 +岐 +偎 +坛 +莨 +山 +殊 +微 +骇 +陈 +爨 +推 +嗝 +驹 +澡 +藁 +呤 +卤 +嘻 +糅 +逛 +侵 +郓 +酌 +德 +摇 +※ +鬃 +被 +慨 +殡 +羸 +昌 +泡 +戛 +鞋 +河 +宪 +沿 +玲 +鲨 +翅 +哽 +源 +铅 +语 +照 +邯 +址 +荃 +佬 +顺 +鸳 +町 +霭 +睾 +瓢 +夸 +椁 +晓 +酿 +痈 +咔 +侏 +券 +噎 +湍 +签 +嚷 +离 +午 +尚 +社 +锤 +背 +孟 +使 +浪 +缦 +潍 +鞅 +军 +姹 +驶 +笑 +鳟 +鲁 +》 +孽 +钜 +绿 +洱 +礴 +焯 +椰 +颖 +囔 +乌 +孔 +巴 +互 +性 +椽 +哞 +聘 +昨 +早 +暮 +胶 +炀 +隧 +低 +彗 +昝 +铁 +呓 +氽 +藉 +喔 +癖 +瑗 +姨 +权 +胱 +韦 +堑 +蜜 +酋 +楝 +砝 +毁 +靓 +歙 +锲 +究 +屋 +喳 +骨 +辨 +碑 +武 +鸠 +宫 +辜 +烊 +适 +坡 +殃 +培 +佩 +供 +走 +蜈 +迟 +翼 +况 +姣 +凛 +浔 +吃 +飘 +债 +犟 +金 +促 +苛 +崇 +坂 +莳 +畔 +绂 +兵 +蠕 +斋 +根 +砍 +亢 +欢 +恬 +崔 +剁 +餐 +榫 +快 +扶 +‖ +濒 +缠 +鳜 +当 +彭 +驭 +浦 +篮 +昀 +锆 +秸 +钳 +弋 +娣 +瞑 +夷 +龛 +苫 +拱 +致 +% +嵊 +障 +隐 +弑 +初 +娓 +抉 +汩 +累 +蓖 +" +唬 +助 +苓 +昙 +押 +毙 +破 +城 +郧 +逢 +嚏 +獭 +瞻 +溱 +婿 +赊 +跨 +恼 +璧 +萃 +姻 +貉 +灵 +炉 +密 +氛 +陶 +砸 +谬 +衔 +点 +琛 +沛 +枳 +层 +岱 +诺 +脍 +榈 +埂 +征 +冷 +裁 +打 +蹴 +素 +瘘 +逞 +蛐 +聊 +激 +腱 +萘 +踵 +飒 +蓟 +吆 +取 +咙 +簋 +涓 +矩 +曝 +挺 +揣 +座 +你 +史 +舵 +焱 +尘 +苏 +笈 +脚 +溉 +榨 +诵 +樊 +邓 +焊 +义 +庶 +儋 +蟋 +蒲 +赦 +呷 +杞 +诠 +豪 +还 +试 +颓 +茉 +太 +除 +紫 +逃 +痴 +草 +充 +鳕 +珉 +祗 +墨 +渭 +烩 +蘸 +慕 +璇 +镶 +穴 +嵘 +恶 +骂 +险 +绋 +幕 +碉 +肺 +戳 +刘 +潞 +秣 +纾 +潜 +銮 +洛 +须 +罘 +销 +瘪 +汞 +兮 +屉 +r +林 +厕 +质 +探 +划 +狸 +殚 +善 +煊 +烹 +〒 +锈 +逯 +宸 +辍 +泱 +柚 +袍 +远 +蹋 +嶙 +绝 +峥 +娥 +缍 +雀 +徵 +认 +镱 +谷 += +贩 +勉 +撩 +鄯 +斐 +洋 +非 +祚 +泾 +诒 +饿 +撬 +威 +晷 +搭 +芍 +锥 +笺 +蓦 +候 +琊 +档 +礁 +沼 +卵 +荠 +忑 +朝 +凹 +瑞 +头 +仪 +弧 +孵 +畏 +铆 +突 +衲 +车 +浩 +气 +茂 +悖 +厢 +枕 +酝 +戴 +湾 +邹 +飚 +攘 +锂 +写 +宵 +翁 +岷 +无 +喜 +丈 +挑 +嗟 +绛 +殉 +议 +槽 +具 +醇 +淞 +笃 +郴 +阅 +饼 +底 +壕 +砚 +弈 +询 +缕 +庹 +翟 +零 +筷 +暨 +舟 +闺 +甯 +撞 +麂 +茌 +蔼 +很 +珲 +捕 +棠 +角 +阉 +媛 +娲 +诽 +剿 +尉 +爵 +睬 +韩 +诰 +匣 +危 +糍 +镯 +立 +浏 +阳 +少 +盆 +舔 +擘 +匪 +申 +尬 +铣 +旯 +抖 +赘 +瓯 +居 +ˇ +哮 +游 +锭 +茏 +歌 +坏 +甚 +秒 +舞 +沙 +仗 +劲 +潺 +阿 +燧 +郭 +嗖 +霏 +忠 +材 +奂 +耐 +跺 +砀 +输 +岖 +媳 +氟 +极 +摆 +灿 +今 +扔 +腻 +枝 +奎 +药 +熄 +吨 +话 +q +额 +慑 +嘌 +协 +喀 +壳 +埭 +视 +著 +於 +愧 +陲 +翌 +峁 +颅 +佛 +腹 +聋 +侯 +咎 +叟 +秀 +颇 +存 +较 +罪 +哄 +岗 +扫 +栏 +钾 +羌 +己 +璨 +枭 +霉 +煌 +涸 +衿 +键 +镝 +益 +岢 +奏 +连 +夯 +睿 +冥 +均 +糖 +狞 +蹊 +稻 +爸 +刿 +胥 +煜 +丽 +肿 +璃 +掸 +跚 +灾 +垂 +樾 +濑 +乎 +莲 +窄 +犹 +撮 +战 +馄 +软 +络 +显 +鸢 +胸 +宾 +妲 +恕 +埔 +蝌 +份 +遇 +巧 +瞟 +粒 +恰 +剥 +桡 +博 +讯 +凯 +堇 +阶 +滤 +卖 +斌 +骚 +彬 +兑 +磺 +樱 +舷 +两 +娱 +福 +仃 +差 +找 +桁 +÷ +净 +把 +阴 +污 +戬 +雷 +碓 +蕲 +楚 +罡 +焖 +抽 +妫 +咒 +仑 +闱 +尽 +邑 +菁 +爱 +贷 +沥 +鞑 +牡 +嗉 +崴 +骤 +塌 +嗦 +订 +拮 +滓 +捡 +锻 +次 +坪 +杩 +臃 +箬 +融 +珂 +鹗 +宗 +枚 +降 +鸬 +妯 +阄 +堰 +盐 +毅 +必 +杨 +崃 +俺 +甬 +状 +莘 +货 +耸 +菱 +腼 +铸 +唏 +痤 +孚 +澳 +懒 +溅 +翘 +疙 +杷 +淼 +缙 +骰 +喊 +悉 +砻 +坷 +艇 +赁 +界 +谤 +纣 +宴 +晃 +茹 +归 +饭 +梢 +铡 +街 +抄 +肼 +鬟 +苯 +颂 +撷 +戈 +炒 +咆 +茭 +瘙 +负 +仰 +客 +琉 +铢 +封 +卑 +珥 +椿 +镧 +窨 +鬲 +寿 +御 +袤 +铃 +萎 +砖 +餮 +脒 +裳 +肪 +孕 +嫣 +馗 +嵇 +恳 +氯 +江 +石 +褶 +冢 +祸 +阻 +狈 +羞 +银 +靳 +透 +咳 +叼 +敷 +芷 +啥 +它 +瓤 +兰 +痘 +懊 +逑 +肌 +往 +捺 +坊 +甩 +呻 +〃 +沦 +忘 +膻 +祟 +菅 +剧 +崆 +智 +坯 +臧 +霍 +墅 +攻 +眯 +倘 +拢 +骠 +铐 +庭 +岙 +瓠 +′ +缺 +泥 +迢 +捶 +? +? +郏 +喙 +掷 +沌 +纯 +秘 +种 +听 +绘 +固 +螨 +团 +香 +盗 +妒 +埚 +蓝 +拖 +旱 +荞 +铀 +血 +遏 +汲 +辰 +叩 +拽 +幅 +硬 +惶 +桀 +漠 +措 +泼 +唑 +齐 +肾 +念 +酱 +虚 +屁 +耶 +旗 +砦 +闵 +婉 +馆 +拭 +绅 +韧 +忏 +窝 +醋 +葺 +顾 +辞 +倜 +堆 +辋 +逆 +玟 +贱 +疾 +董 +惘 +倌 +锕 +淘 +嘀 +莽 +俭 +笏 +绑 +鲷 +杈 +择 +蟀 +粥 +嗯 +驰 +逾 +案 +谪 +褓 +胫 +哩 +昕 +颚 +鲢 +绠 +躺 +鹄 +崂 +儒 +俨 +丝 +尕 +泌 +啊 +萸 +彰 +幺 +吟 +骄 +苣 +弦 +脊 +瑰 +〈 +诛 +镁 +析 +闪 +剪 +侧 +哟 +框 +螃 +守 +嬗 +燕 +狭 +铈 +缮 +概 +迳 +痧 +鲲 +俯 +售 +笼 +痣 +扉 +挖 +满 +咋 +援 +邱 +扇 +歪 +便 +玑 +绦 +峡 +蛇 +叨 +〖 +泽 +胃 +斓 +喋 +怂 +坟 +猪 +该 +蚬 +炕 +弥 +赞 +棣 +晔 +娠 +挲 +狡 +创 +疖 +铕 +镭 +稷 +挫 +弭 +啾 +翔 +粉 +履 +苘 +哦 +楼 +秕 +铂 +土 +锣 +瘟 +挣 +栉 +习 +享 +桢 +袅 +磨 +桂 +谦 +延 +坚 +蔚 +噗 +署 +谟 +猬 +钎 +恐 +嬉 +雒 +倦 +衅 +亏 +璩 +睹 +刻 +殿 +王 +算 +雕 +麻 +丘 +柯 +骆 +丸 +塍 +谚 +添 +鲈 +垓 +桎 +蚯 +芥 +予 +飕 +镦 +谌 +窗 +醚 +菀 +亮 +搪 +莺 +蒿 +羁 +足 +J +真 +轶 +悬 +衷 +靛 +翊 +掩 +哒 +炅 +掐 +冼 +妮 +l +谐 +稚 +荆 +擒 +犯 +陵 +虏 +浓 +崽 +刍 +陌 +傻 +孜 +千 +靖 +演 +矜 +钕 +煽 +杰 +酗 +渗 +伞 +栋 +俗 +泫 +戍 +罕 +沾 +疽 +灏 +煦 +芬 +磴 +叱 +阱 +榉 +湃 +蜀 +叉 +醒 +彪 +租 +郡 +篷 +屎 +良 +垢 +隗 +弱 +陨 +峪 +砷 +掴 +颁 +胎 +雯 +绵 +贬 +沐 +撵 +隘 +篙 +暖 +曹 +陡 +栓 +填 +臼 +彦 +瓶 +琪 +潼 +哪 +鸡 +摩 +啦 +俟 +锋 +域 +耻 +蔫 +疯 +纹 +撇 +毒 +绶 +痛 +酯 +忍 +爪 +赳 +歆 +嘹 +辕 +烈 +册 +朴 +钱 +吮 +毯 +癜 +娃 +谀 +邵 +厮 +炽 +璞 +邃 +丐 +追 +词 +瓒 +忆 +轧 +芫 +谯 +喷 +弟 +半 +冕 +裙 +掖 +墉 +绮 +寝 +苔 +势 +顷 +褥 +切 +衮 +君 +佳 +嫒 +蚩 +霞 +佚 +洙 +逊 +镖 +暹 +唛 +& +殒 +顶 +碗 +獗 +轭 +铺 +蛊 +废 +恹 +汨 +崩 +珍 +那 +杵 +曲 +纺 +夏 +薰 +傀 +闳 +淬 +姘 +舀 +拧 +卷 +楂 +恍 +讪 +厩 +寮 +篪 +赓 +乘 +灭 +盅 +鞣 +沟 +慎 +挂 +饺 +鼾 +杳 +树 +缨 +丛 +絮 +娌 +臻 +嗳 +篡 +侩 +述 +衰 +矛 +圈 +蚜 +匕 +筹 +匿 +濞 +晨 +叶 +骋 +郝 +挚 +蚴 +滞 +增 +侍 +描 +瓣 +吖 +嫦 +蟒 +匾 +圣 +赌 +毡 +癞 +恺 +百 +曳 +需 +篓 +肮 +庖 +帏 +卿 +驿 +遗 +蹬 +鬓 +骡 +歉 +芎 +胳 +屐 +禽 +烦 +晌 +寄 +媾 +狄 +翡 +苒 +船 +廉 +终 +痞 +殇 +々 +畦 +饶 +改 +拆 +悻 +萄 +£ +瓿 +乃 +訾 +桅 +匮 +溧 +拥 +纱 +铍 +骗 +蕃 +龋 +缬 +父 +佐 +疚 +栎 +醍 +掳 +蓄 +x +惆 +颜 +鲆 +榆 +〔 +猎 +敌 +暴 +谥 +鲫 +贾 +罗 +玻 +缄 +扦 +芪 +癣 +落 +徒 +臾 +恿 +猩 +托 +邴 +肄 +牵 +春 +陛 +耀 +刊 +拓 +蓓 +邳 +堕 +寇 +枉 +淌 +啡 +湄 +兽 +酷 +萼 +碚 +濠 +萤 +夹 +旬 +戮 +梭 +琥 +椭 +昔 +勺 +蜊 +绐 +晚 +孺 +僵 +宣 +摄 +冽 +旨 +萌 +忙 +蚤 +眉 +噼 +蟑 +付 +契 +瓜 +悼 +颡 +壁 +曾 +窕 +颢 +澎 +仿 +俑 +浑 +嵌 +浣 +乍 +碌 +褪 +乱 +蔟 +隙 +玩 +剐 +葫 +箫 +纲 +围 +伐 +决 +伙 +漩 +瑟 +刑 +肓 +镳 +缓 +蹭 +氨 +皓 +典 +畲 +坍 +铑 +檐 +塑 +洞 +倬 +储 +胴 +淳 +戾 +吐 +灼 +惺 +妙 +毕 +珐 +缈 +虱 +盖 +羰 +鸿 +磅 +谓 +髅 +娴 +苴 +唷 +蚣 +霹 +抨 +贤 +唠 +犬 +誓 +逍 +庠 +逼 +麓 +籼 +釉 +呜 +碧 +秧 +氩 +摔 +霄 +穸 +纨 +辟 +妈 +映 +完 +牛 +缴 +嗷 +炊 +恩 +荔 +茆 +掉 +紊 +慌 +莓 +羟 +阙 +萁 +磐 +另 +蕹 +辱 +鳐 +湮 +吡 +吩 +唐 +睦 +垠 +舒 +圜 +冗 +瞿 +溺 +芾 +囱 +匠 +僳 +汐 +菩 +饬 +漓 +黑 +霰 +浸 +濡 +窥 +毂 +蒡 +兢 +驻 +鹉 +芮 +诙 +迫 +雳 +厂 +忐 +臆 +猴 +鸣 +蚪 +栈 +箕 +羡 +渐 +莆 +捍 +眈 +哓 +趴 +蹼 +埕 +嚣 +骛 +宏 +淄 +斑 +噜 +严 +瑛 +垃 +椎 +诱 +压 +庾 +绞 +焘 +廿 +抡 +迄 +棘 +夫 +纬 +锹 +眨 +瞌 +侠 +脐 +竞 +瀑 +孳 +骧 +遁 +姜 +颦 +荪 +滚 +萦 +伪 +逸 +粳 +爬 +锁 +矣 +役 +趣 +洒 +颔 +诏 +逐 +奸 +甭 +惠 +攀 +蹄 +泛 +尼 +拼 +阮 +鹰 +亚 +颈 +惑 +勒 +〉 +际 +肛 +爷 +刚 +钨 +丰 +养 +冶 +鲽 +辉 +蔻 +画 +覆 +皴 +妊 +麦 +返 +醉 +皂 +擀 +〗 +酶 +凑 +粹 +悟 +诀 +硖 +港 +卜 +z +杀 +涕 +± +舍 +铠 +抵 +弛 +段 +敝 +镐 +奠 +拂 +轴 +跛 +袱 +e +t +沉 +菇 +俎 +薪 +峦 +秭 +蟹 +历 +盟 +菠 +寡 +液 +肢 +喻 +染 +裱 +悱 +抱 +氙 +赤 +捅 +猛 +跑 +氮 +谣 +仁 +尺 +辊 +窍 +烙 +衍 +架 +擦 +倏 +璐 +瑁 +币 +楞 +胖 +夔 +趸 +邛 +惴 +饕 +虔 +蝎 +§ +哉 +贝 +宽 +辫 +炮 +扩 +饲 +籽 +魏 +菟 +锰 +伍 +猝 +末 +琳 +哚 +蛎 +邂 +呀 +姿 +鄞 +却 +歧 +仙 +恸 +椐 +森 +牒 +寤 +袒 +婆 +虢 +雅 +钉 +朵 +贼 +欲 +苞 +寰 +故 +龚 +坭 +嘘 +咫 +礼 +硷 +兀 +睢 +汶 +’ +铲 +烧 +绕 +诃 +浃 +钿 +哺 +柜 +讼 +颊 +璁 +腔 +洽 +咐 +脲 +簌 +筠 +镣 +玮 +鞠 +谁 +兼 +姆 +挥 +梯 +蝴 +谘 +漕 +刷 +躏 +宦 +弼 +b +垌 +劈 +麟 +莉 +揭 +笙 +渎 +仕 +嗤 +仓 +配 +怏 +抬 +错 +泯 +镊 +孰 +猿 +邪 +仍 +秋 +鼬 +壹 +歇 +吵 +炼 +< +尧 +射 +柬 +廷 +胧 +霾 +凳 +隋 +肚 +浮 +梦 +祥 +株 +堵 +退 +L +鹫 +跎 +凶 +毽 +荟 +炫 +栩 +玳 +甜 +沂 +鹿 +顽 +伯 +爹 +赔 +蛴 +徐 +匡 +欣 +狰 +缸 +雹 +蟆 +疤 +默 +沤 +啜 +痂 +衣 +禅 +w +i +h +辽 +葳 +黝 +钗 +停 +沽 +棒 +馨 +颌 +肉 +吴 +硫 +悯 +劾 +娈 +马 +啧 +吊 +悌 +镑 +峭 +帆 +瀣 +涉 +咸 +疸 +滋 +泣 +翦 +拙 +癸 +钥 +蜒 ++ +尾 +庄 +凝 +泉 +婢 +渴 +谊 +乞 +陆 +锉 +糊 +鸦 +淮 +I +B +N +晦 +弗 +乔 +庥 +葡 +尻 +席 +橡 +傣 +渣 +拿 +惩 +麋 +斛 +缃 +矮 +蛏 +岘 +鸽 +姐 +膏 +催 +奔 +镒 +喱 +蠡 +摧 +钯 +胤 +柠 +拐 +璋 +鸥 +卢 +荡 +倾 +^ +_ +珀 +逄 +萧 +塾 +掇 +贮 +笆 +聂 +圃 +冲 +嵬 +M +滔 +笕 +值 +炙 +偶 +蜱 +搐 +梆 +汪 +蔬 +腑 +鸯 +蹇 +敞 +绯 +仨 +祯 +谆 +梧 +糗 +鑫 +啸 +豺 +囹 +猾 +巢 +柄 +瀛 +筑 +踌 +沭 +暗 +苁 +鱿 +蹉 +脂 +蘖 +牢 +热 +木 +吸 +溃 +宠 +序 +泞 +偿 +拜 +檩 +厚 +朐 +毗 +螳 +吞 +媚 +朽 +担 +蝗 +橘 +畴 +祈 +糟 +盱 +隼 +郜 +惜 +珠 +裨 +铵 +焙 +琚 +唯 +咚 +噪 +骊 +丫 +滢 +勤 +棉 +呸 +咣 +淀 +隔 +蕾 +窈 +饨 +挨 +煅 +短 +匙 +粕 +镜 +赣 +撕 +墩 +酬 +馁 +豌 +颐 +抗 +酣 +氓 +佑 +搁 +哭 +递 +耷 +涡 +桃 +贻 +碣 +截 +瘦 +昭 +镌 +蔓 +氚 +甲 +猕 +蕴 +蓬 +散 +拾 +纛 +狼 +猷 +铎 +埋 +旖 +矾 +讳 +囊 +糜 +迈 +粟 +蚂 +紧 +鲳 +瘢 +栽 +稼 +羊 +锄 +斟 +睁 +桥 +瓮 +蹙 +祉 +醺 +鼻 +昱 +剃 +跳 +篱 +跷 +蒜 +翎 +宅 +晖 +嗑 +壑 +峻 +癫 +屏 +狠 +陋 +袜 +途 +憎 +祀 +莹 +滟 +佶 +溥 +臣 +约 +盛 +峰 +磁 +慵 +婪 +拦 +莅 +朕 +鹦 +粲 +裤 +哎 +疡 +嫖 +琵 +窟 +堪 +谛 +嘉 +儡 +鳝 +斩 +郾 +驸 +酊 +妄 +胜 +贺 +徙 +傅 +噌 +钢 +栅 +庇 +恋 +匝 +巯 +邈 +尸 +锚 +粗 +佟 +蛟 +薹 +纵 +蚊 +郅 +绢 +锐 +苗 +俞 +篆 +淆 +膀 +鲜 +煎 +诶 +秽 +寻 +涮 +刺 +怀 +噶 +巨 +褰 +魅 +灶 +灌 +桉 +藕 +谜 +舸 +薄 +搀 +恽 +借 +牯 +痉 +渥 +愿 +亓 +耘 +杠 +柩 +锔 +蚶 +钣 +珈 +喘 +蹒 +幽 +赐 +稗 +晤 +莱 +泔 +扯 +肯 +菪 +裆 +腩 +豉 +疆 +骜 +腐 +倭 +珏 +唔 +粮 +亡 +润 +慰 +伽 +橄 +玄 +誉 +醐 +胆 +龊 +粼 +塬 +陇 +彼 +削 +嗣 +绾 +芽 +妗 +垭 +瘴 +爽 +薏 +寨 +龈 +泠 +弹 +赢 +漪 +猫 +嘧 +涂 +恤 +圭 +茧 +烽 +屑 +痕 +巾 +赖 +荸 +凰 +腮 +畈 +亵 +蹲 +偃 +苇 +澜 +艮 +换 +骺 +烘 +苕 +梓 +颉 +肇 +哗 +悄 +氤 +涠 +葬 +屠 +鹭 +植 +竺 +佯 +诣 +鲇 +瘀 +鲅 +邦 +移 +滁 +冯 +耕 +癔 +戌 +茬 +沁 +巩 +悠 +湘 +洪 +痹 +锟 +循 +谋 +腕 +鳃 +钠 +捞 +焉 +迎 +碱 +伫 +急 +榷 +奈 +邝 +卯 +辄 +皲 +卟 +醛 +畹 +忧 +稳 +雄 +昼 +缩 +阈 +睑 +扌 +耗 +曦 +涅 +捏 +瞧 +邕 +淖 +漉 +铝 +耦 +禹 +湛 +喽 +莼 +琅 +诸 +苎 +纂 +硅 +始 +嗨 +傥 +燃 +臂 +赅 +嘈 +呆 +贵 +屹 +壮 +肋 +亍 +蚀 +卅 +豹 +腆 +邬 +迭 +浊 +} +童 +螂 +捐 +圩 +勐 +触 +寞 +汊 +壤 +荫 +膺 +渌 +芳 +懿 +遴 +螈 +泰 +蓼 +蛤 +茜 +舅 +枫 +朔 +膝 +眙 +避 +梅 +判 +鹜 +璜 +牍 +缅 +垫 +藻 +黔 +侥 +惚 +懂 +踩 +腰 +腈 +札 +丞 +唾 +慈 +顿 +摹 +荻 +琬 +~ +斧 +沈 +滂 +胁 +胀 +幄 +莜 +Z +匀 +鄄 +掌 +绰 +茎 +焚 +赋 +萱 +谑 +汁 +铒 +瞎 +夺 +蜗 +野 +娆 +冀 +弯 +篁 +懵 +灞 +隽 +芡 +脘 +俐 +辩 +芯 +掺 +喏 +膈 +蝈 +觐 +悚 +踹 +蔗 +熠 +鼠 +呵 +抓 +橼 +峨 +畜 +缔 +禾 +崭 +弃 +熊 +摒 +凸 +拗 +穹 +蒙 +抒 +祛 +劝 +闫 +扳 +阵 +醌 +踪 +喵 +侣 +搬 +仅 +荧 +赎 +蝾 +琦 +买 +婧 +瞄 +寓 +皎 +冻 +赝 +箩 +莫 +瞰 +郊 +笫 +姝 +筒 +枪 +遣 +煸 +袋 +舆 +痱 +涛 +母 +〇 +启 +践 +耙 +绲 +盘 +遂 +昊 +搞 +槿 +诬 +纰 +泓 +惨 +檬 +亻 +越 +C +o +憩 +熵 +祷 +钒 +暧 +塔 +阗 +胰 +咄 +娶 +魔 +琶 +钞 +邻 +扬 +杉 +殴 +咽 +弓 +〆 +髻 +】 +吭 +揽 +霆 +拄 +殖 +脆 +彻 +岩 +芝 +勃 +辣 +剌 +钝 +嘎 +甄 +佘 +皖 +伦 +授 +徕 +憔 +挪 +皇 +庞 +稔 +芜 +踏 +溴 +兖 +卒 +擢 +饥 +鳞 +煲 +‰ +账 +颗 +叻 +斯 +捧 +鳍 +琮 +讹 +蛙 +纽 +谭 +酸 +兔 +莒 +睇 +伟 +觑 +羲 +嗜 +宜 +褐 +旎 +辛 +卦 +诘 +筋 +鎏 +溪 +挛 +熔 +阜 +晰 +鳅 +丢 +奚 +灸 +呱 +献 +陉 +黛 +鸪 +甾 +萨 +疮 +拯 +洲 +疹 +辑 +叙 +恻 +谒 +允 +柔 +烂 +氏 +逅 +漆 +拎 +惋 +扈 +湟 +纭 +啕 +掬 +擞 +哥 +忽 +涤 +鸵 +靡 +郗 +瓷 +扁 +廊 +怨 +雏 +钮 +敦 +E +懦 +憋 +汀 +拚 +啉 +腌 +岸 +f +痼 +瞅 +尊 +咀 +眩 +飙 +忌 +仝 +迦 +熬 +毫 +胯 +篑 +茄 +腺 +凄 +舛 +碴 +锵 +诧 +羯 +後 +漏 +汤 +宓 +仞 +蚁 +壶 +谰 +皑 +铄 +棰 +罔 +辅 +晶 +苦 +牟 +闽 +\ +烃 +饮 +聿 +丙 +蛳 +朱 +煤 +涔 +鳖 +犁 +罐 +荼 +砒 +淦 +妤 +黏 +戎 +孑 +婕 +瑾 +戢 +钵 +枣 +捋 +砥 +衩 +狙 +桠 +稣 +阎 +肃 +梏 +诫 +孪 +昶 +婊 +衫 +嗔 +侃 +塞 +蜃 +樵 +峒 +貌 +屿 +欺 +缫 +阐 +栖 +诟 +珞 +荭 +吝 +萍 +嗽 +恂 +啻 +蜴 +磬 +峋 +俸 +豫 +谎 +徊 +镍 +韬 +魇 +晴 +U +囟 +猜 +蛮 +坐 +囿 +伴 +亭 +肝 +佗 +蝠 +妃 +胞 +滩 +榴 +氖 +垩 +苋 +砣 +扪 +馏 +姓 +轩 +厉 +夥 +侈 +禀 +垒 +岑 +赏 +钛 +辐 +痔 +披 +纸 +碳 +“ +坞 +蠓 +挤 +荥 +沅 +悔 +铧 +帼 +蒌 +蝇 +a +p +y +n +g +哀 +浆 +瑶 +凿 +桶 +馈 +皮 +奴 +苜 +佤 +伶 +晗 +铱 +炬 +优 +弊 +氢 +恃 +甫 +攥 +端 +锌 +灰 +稹 +炝 +曙 +邋 +亥 +眶 +碾 +拉 +萝 +绔 +捷 +浍 +腋 +姑 +菖 +凌 +涞 +麽 +锢 +桨 +潢 +绎 +镰 +殆 +锑 +渝 +铬 +困 +绽 +觎 +匈 +糙 +暑 +裹 +鸟 +盔 +肽 +迷 +綦 +『 +亳 +佝 +俘 +钴 +觇 +骥 +仆 +疝 +跪 +婶 +郯 +瀹 +唉 +脖 +踞 +针 +晾 +忒 +扼 +瞩 +叛 +椒 +疟 +嗡 +邗 +肆 +跆 +玫 +忡 +捣 +咧 +唆 +艄 +蘑 +潦 +笛 +阚 +沸 +泻 +掊 +菽 +贫 +斥 +髂 +孢 +镂 +赂 +麝 +鸾 +屡 +衬 +苷 +恪 +叠 +希 +粤 +爻 +喝 +茫 +惬 +郸 +绻 +庸 +撅 +碟 +宄 +妹 +膛 +叮 +饵 +崛 +嗲 +椅 +冤 +搅 +咕 +敛 +尹 +垦 +闷 +蝉 +霎 +勰 +败 +蓑 +泸 +肤 +鹌 +幌 +焦 +浠 +鞍 +刁 +舰 +乙 +竿 +裔 +。 +茵 +函 +伊 +兄 +丨 +娜 +匍 +謇 +莪 +宥 +似 +蝽 +翳 +酪 +翠 +粑 +薇 +祢 +骏 +赠 +叫 +Q +噤 +噻 +竖 +芗 +莠 +潭 +俊 +羿 +耜 +O +郫 +趁 +嗪 +囚 +蹶 +芒 +洁 +笋 +鹑 +敲 +硝 +啶 +堡 +渲 +揩 +』 +携 +宿 +遒 +颍 +扭 +棱 +割 +萜 +蔸 +葵 +琴 +捂 +饰 +衙 +耿 +掠 +募 +岂 +窖 +涟 +蔺 +瘤 +柞 +瞪 +怜 +匹 +距 +楔 +炜 +哆 +秦 +缎 +幼 +茁 +绪 +痨 +恨 +楸 +娅 +瓦 +桩 +雪 +嬴 +伏 +榔 +妥 +铿 +拌 +眠 +雍 +缇 +‘ +卓 +搓 +哌 +觞 +噩 +屈 +哧 +髓 +咦 +巅 +娑 +侑 +淫 +膳 +祝 +勾 +姊 +莴 +胄 +疃 +薛 +蜷 +胛 +巷 +芙 +芋 +熙 +闰 +勿 +窃 +狱 +剩 +钏 +幢 +陟 +铛 +慧 +靴 +耍 +k +浙 +浇 +飨 +惟 +绗 +祜 +澈 +啼 +咪 +磷 +摞 +诅 +郦 +抹 +跃 +壬 +吕 +肖 +琏 +颤 +尴 +剡 +抠 +凋 +赚 +泊 +津 +宕 +殷 +倔 +氲 +漫 +邺 +涎 +怠 +$ +垮 +荬 +遵 +俏 +叹 +噢 +饽 +蜘 +孙 +筵 +疼 +鞭 +羧 +牦 +箭 +潴 +c +眸 +祭 +髯 +啖 +坳 +愁 +芩 +驮 +倡 +巽 +穰 +沃 +胚 +怒 +凤 +槛 +剂 +趵 +嫁 +v +邢 +灯 +鄢 +桐 +睽 +檗 +锯 +槟 +婷 +嵋 +圻 +诗 +蕈 +颠 +遭 +痢 +芸 +怯 +馥 +竭 +锗 +徜 +恭 +遍 +籁 +剑 +嘱 +苡 +龄 +僧 +桑 +潸 +弘 +澶 +楹 +悲 +讫 +愤 +腥 +悸 +谍 +椹 +呢 +桓 +葭 +攫 +阀 +翰 +躲 +敖 +柑 +郎 +笨 +橇 +呃 +魁 +燎 +脓 +葩 +磋 +垛 +玺 +狮 +沓 +砜 +蕊 +锺 +罹 +蕉 +翱 +虐 +闾 +巫 +旦 +茱 +嬷 +枯 +鹏 +贡 +芹 +汛 +矫 +绁 +拣 +禺 +佃 +讣 +舫 +惯 +乳 +趋 +疲 +挽 +岚 +虾 +衾 +蠹 +蹂 +飓 +氦 +铖 +孩 +稞 +瑜 +壅 +掀 +勘 +妓 +畅 +髋 +W +庐 +牲 +蓿 +榕 +练 +垣 +唱 +邸 +菲 +昆 +婺 +穿 +绡 +麒 +蚱 +掂 +愚 +泷 +涪 +漳 +妩 +娉 +榄 +讷 +觅 +旧 +藤 +煮 +呛 +柳 +腓 +叭 +庵 +烷 +阡 +罂 +蜕 +擂 +猖 +咿 +媲 +脉 +【 +沏 +貅 +黠 +熏 +哲 +烁 +坦 +酵 +兜 +× +潇 +撒 +剽 +珩 +圹 +乾 +摸 +樟 +帽 +嗒 +襄 +魂 +轿 +憬 +锡 +〕 +喃 +皆 +咖 +隅 +脸 +残 +泮 +袂 +鹂 +珊 +囤 +捆 +咤 +误 +徨 +闹 +淙 +芊 +淋 +怆 +囗 +拨 +梳 +渤 +R +G +绨 +蚓 +婀 +幡 +狩 +麾 +谢 +唢 +裸 +旌 +伉 +纶 +裂 +驳 +砼 +咛 +澄 +樨 +蹈 +宙 +澍 +倍 +貔 +操 +勇 +蟠 +摈 +砧 +虬 +够 +缁 +悦 +藿 +撸 +艹 +摁 +淹 +豇 +虎 +榭 +ˉ +吱 +d +° +喧 +荀 +踱 +侮 +奋 +偕 +饷 +犍 +惮 +坑 +璎 +徘 +宛 +妆 +袈 +倩 +窦 +昂 +荏 +乖 +K +怅 +撰 +鳙 +牙 +袁 +酞 +X +痿 +琼 +闸 +雁 +趾 +荚 +虻 +涝 +《 +杏 +韭 +偈 +烤 +绫 +鞘 +卉 +症 +遢 +蓥 +诋 +杭 +荨 +匆 +竣 +簪 +辙 +敕 +虞 +丹 +缭 +咩 +黟 +m +淤 +瑕 +咂 +铉 +硼 +茨 +嶂 +痒 +畸 +敬 +涿 +粪 +窘 +熟 +叔 +嫔 +盾 +忱 +裘 +憾 +梵 +赡 +珙 +咯 +娘 +庙 +溯 +胺 +葱 +痪 +摊 +荷 +卞 +乒 +髦 +寐 +铭 +坩 +胗 +枷 +爆 +溟 +嚼 +羚 +砬 +轨 +惊 +挠 +罄 +竽 +菏 +氧 +浅 +楣 +盼 +枢 +炸 +阆 +杯 +谏 +噬 +淇 +渺 +俪 +秆 +墓 +泪 +跻 +砌 +痰 +垡 +渡 +耽 +釜 +讶 +鳎 +煞 +呗 +韶 +舶 +绷 +鹳 +缜 +旷 +铊 +皱 +龌 +檀 +霖 +奄 +槐 +艳 +蝶 +旋 +哝 +赶 +骞 +蚧 +腊 +盈 +丁 +` +蜚 +矸 +蝙 +睨 +嚓 +僻 +鬼 +醴 +夜 +彝 +磊 +笔 +拔 +栀 +糕 +厦 +邰 +纫 +逭 +纤 +眦 +膊 +馍 +躇 +烯 +蘼 +冬 +诤 +暄 +骶 +哑 +瘠 +」 +臊 +丕 +愈 +咱 +螺 +擅 +跋 +搏 +硪 +谄 +笠 +淡 +嘿 +骅 +谧 +鼎 +皋 +姚 +歼 +蠢 +驼 +耳 +胬 +挝 +涯 +狗 +蒽 +孓 +犷 +凉 +芦 +箴 +铤 +孤 +嘛 +坤 +V +茴 +朦 +挞 +尖 +橙 +诞 +搴 +碇 +洵 +浚 +帚 +蜍 +漯 +柘 +嚎 +讽 +芭 +荤 +咻 +祠 +秉 +跖 +埃 +吓 +糯 +眷 +馒 +惹 +娼 +鲑 +嫩 +讴 +轮 +瞥 +靶 +褚 +乏 +缤 +宋 +帧 +删 +驱 +碎 +扑 +俩 +俄 +偏 +涣 +竹 +噱 +皙 +佰 +渚 +唧 +斡 +# +镉 +刀 +崎 +筐 +佣 +夭 +贰 +肴 +峙 +哔 +艿 +匐 +牺 +镛 +缘 +仡 +嫡 +劣 +枸 +堀 +梨 +簿 +鸭 +蒸 +亦 +稽 +浴 +{ +衢 +束 +槲 +j +阁 +揍 +疥 +棋 +潋 +聪 +窜 +乓 +睛 +插 +冉 +阪 +苍 +搽 +「 +蟾 +螟 +幸 +仇 +樽 +撂 +慢 +跤 +幔 +俚 +淅 +覃 +觊 +溶 +妖 +帛 +侨 +曰 +妾 +泗 +· +: +瀘 +風 +Ë +( +) +∶ +紅 +紗 +瑭 +雲 +頭 +鶏 +財 +許 +• +¥ +樂 +焗 +麗 +— +; +滙 +東 +榮 +繪 +興 +… +門 +業 +π +楊 +國 +顧 +é +盤 +寳 +Λ +龍 +鳳 +島 +誌 +緣 +結 +銭 +萬 +勝 +祎 +璟 +優 +歡 +臨 +時 +購 += +★ +藍 +昇 +鐵 +觀 +勅 +農 +聲 +畫 +兿 +術 +發 +劉 +記 +專 +耑 +園 +書 +壴 +種 +Ο +● +褀 +號 +銀 +匯 +敟 +锘 +葉 +橪 +廣 +進 +蒄 +鑽 +阝 +祙 +貢 +鍋 +豊 +夬 +喆 +團 +閣 +開 +燁 +賓 +館 +酡 +沔 +順 ++ +硚 +劵 +饸 +陽 +車 +湓 +復 +萊 +氣 +軒 +華 +堃 +迮 +纟 +戶 +馬 +學 +裡 +電 +嶽 +獨 +マ +シ +サ +ジ +燘 +袪 +環 +❤ +臺 +灣 +専 +賣 +孖 +聖 +攝 +線 +▪ +α +傢 +俬 +夢 +達 +莊 +喬 +貝 +薩 +劍 +羅 +壓 +棛 +饦 +尃 +璈 +囍 +醫 +G +I +A +# +N +鷄 +髙 +嬰 +啓 +約 +隹 +潔 +賴 +藝 +~ +寶 +籣 +麺 +  +嶺 +√ +義 +網 +峩 +長 +∧ +魚 +機 +構 +② +鳯 +偉 +L +B +㙟 +畵 +鴿 +' +詩 +溝 +嚞 +屌 +藔 +佧 +玥 +蘭 +織 +1 +3 +9 +0 +7 +點 +砭 +鴨 +鋪 +銘 +廳 +弍 +‧ +創 +湯 +坶 +℃ +卩 +骝 +& +烜 +荘 +當 +潤 +扞 +係 +懷 +碶 +钅 +蚨 +讠 +☆ +叢 +爲 +埗 +涫 +塗 +→ +楽 +現 +鯨 +愛 +瑪 +鈺 +忄 +悶 +藥 +飾 +樓 +視 +孬 +ㆍ +燚 +苪 +師 +① +丼 +锽 +│ +韓 +標 +è +兒 +閏 +匋 +張 +漢 +Ü +髪 +會 +閑 +檔 +習 +裝 +の +峯 +菘 +輝 +И +雞 +釣 +億 +浐 +K +O +R +8 +H +E +P +T +W +D +S +C +M +F +姌 +饹 +» +晞 +廰 +ä +嵯 +鷹 +負 +飲 +絲 +冚 +楗 +澤 +綫 +區 +❋ +← +質 +靑 +揚 +③ +滬 +統 +産 +協 +﹑ +乸 +畐 +經 +運 +際 +洺 +岽 +為 +粵 +諾 +崋 +豐 +碁 +ɔ +V +2 +6 +齋 +誠 +訂 +´ +勑 +雙 +陳 +無 +í +泩 +媄 +夌 +刂 +i +c +t +o +r +a +嘢 +耄 +燴 +暃 +壽 +媽 +靈 +抻 +體 +唻 +É +冮 +甹 +鎮 +錦 +ʌ +蜛 +蠄 +尓 +駕 +戀 +飬 +逹 +倫 +貴 +極 +Я +Й +寬 +磚 +嶪 +郎 +職 +| +間 +n +d +剎 +伈 +課 +飛 +橋 +瘊 +№ +譜 +骓 +圗 +滘 +縣 +粿 +咅 +養 +濤 +彳 +® +% +Ⅱ +啰 +㴪 +見 +矞 +薬 +糁 +邨 +鲮 +顔 +罱 +З +選 +話 +贏 +氪 +俵 +競 +瑩 +繡 +枱 +β +綉 +á +獅 +爾 +™ +麵 +戋 +淩 +徳 +個 +劇 +場 +務 +簡 +寵 +h +實 +膠 +轱 +圖 +築 +嘣 +樹 +㸃 +營 +耵 +孫 +饃 +鄺 +飯 +麯 +遠 +輸 +坫 +孃 +乚 +閃 +鏢 +㎡ +題 +廠 +關 +↑ +爺 +將 +軍 +連 +篦 +覌 +參 +箸 +- +窠 +棽 +寕 +夀 +爰 +歐 +呙 +閥 +頡 +熱 +雎 +垟 +裟 +凬 +勁 +帑 +馕 +夆 +疌 +枼 +馮 +貨 +蒤 +樸 +彧 +旸 +靜 +龢 +暢 +㐱 +鳥 +珺 +鏡 +灡 +爭 +堷 +廚 +Ó +騰 +診 +┅ +蘇 +褔 +凱 +頂 +豕 +亞 +帥 +嘬 +⊥ +仺 +桖 +複 +饣 +絡 +穂 +顏 +棟 +納 +▏ +濟 +親 +設 +計 +攵 +埌 +烺 +ò +頤 +燦 +蓮 +撻 +節 +講 +濱 +濃 +娽 +洳 +朿 +燈 +鈴 +護 +膚 +铔 +過 +補 +Z +U +5 +4 +坋 +闿 +䖝 +餘 +缐 +铞 +貿 +铪 +桼 +趙 +鍊 +[ +㐂 +垚 +菓 +揸 +捲 +鐘 +滏 +𣇉 +爍 +輪 +燜 +鴻 +鮮 +動 +鹞 +鷗 +丄 +慶 +鉌 +翥 +飮 +腸 +⇋ +漁 +覺 +來 +熘 +昴 +翏 +鲱 +圧 +鄉 +萭 +頔 +爐 +嫚 +г +貭 +類 +聯 +幛 +輕 +訓 +鑒 +夋 +锨 +芃 +珣 +䝉 +扙 +嵐 +銷 +處 +ㄱ +語 +誘 +苝 +歸 +儀 +燒 +楿 +內 +粢 +葒 +奧 +麥 +礻 +滿 +蠔 +穵 +瞭 +態 +鱬 +榞 +硂 +鄭 +黃 +煙 +祐 +奓 +逺 +* +瑄 +獲 +聞 +薦 +讀 +這 +樣 +決 +問 +啟 +們 +執 +説 +轉 +單 +隨 +唘 +帶 +倉 +庫 +還 +贈 +尙 +皺 +■ +餅 +產 +○ +∈ +報 +狀 +楓 +賠 +琯 +嗮 +禮 +` +傳 +> +≤ +嗞 +Φ +≥ +換 +咭 +∣ +↓ +曬 +ε +応 +寫 +″ +終 +様 +純 +費 +療 +聨 +凍 +壐 +郵 +ü +黒 +∫ +製 +塊 +調 +軽 +確 +撃 +級 +馴 +Ⅲ +涇 +繹 +數 +碼 +證 +狒 +処 +劑 +< +晧 +賀 +衆 +] +櫥 +兩 +陰 +絶 +對 +鯉 +憶 +◎ +p +e +Y +蕒 +煖 +頓 +測 +試 +鼽 +僑 +碩 +妝 +帯 +≈ +鐡 +舖 +權 +喫 +倆 +ˋ +該 +悅 +ā +俫 +. +f +s +b +m +k +g +u +j +貼 +淨 +濕 +針 +適 +備 +l +/ +給 +謢 +強 +觸 +衛 +與 +⊙ +$ +緯 +變 +⑴ +⑵ +⑶ +㎏ +殺 +∩ +幚 +─ +價 +▲ +離 +ú +ó +飄 +烏 +関 +閟 +﹝ +﹞ +邏 +輯 +鍵 +驗 +訣 +導 +歷 +屆 +層 +▼ +儱 +錄 +熳 +ē +艦 +吋 +錶 +辧 +飼 +顯 +④ +禦 +販 +気 +対 +枰 +閩 +紀 +幹 +瞓 +貊 +淚 +△ +眞 +墊 +Ω +獻 +褲 +縫 +緑 +亜 +鉅 +餠 +{ +} +◆ +蘆 +薈 +█ +◇ +溫 +彈 +晳 +粧 +犸 +穩 +訊 +崬 +凖 +熥 +П +舊 +條 +紋 +圍 +Ⅳ +筆 +尷 +難 +雜 +錯 +綁 +識 +頰 +鎖 +艶 +□ +殁 +殼 +⑧ +├ +▕ +鵬 +ǐ +ō +ǒ +糝 +綱 +▎ +μ +盜 +饅 +醬 +籤 +蓋 +釀 +鹽 +據 +à +ɡ +辦 +◥ +彐 +┌ +婦 +獸 +鲩 +伱 +ī +蒟 +蒻 +齊 +袆 +腦 +寧 +凈 +妳 +煥 +詢 +偽 +謹 +啫 +鯽 +騷 +鱸 +損 +傷 +鎻 +髮 +買 +冏 +儥 +両 +﹢ +∞ +載 +喰 +z +羙 +悵 +燙 +曉 +員 +組 +徹 +艷 +痠 +鋼 +鼙 +縮 +細 +嚒 +爯 +≠ +維 +" +鱻 +壇 +厍 +帰 +浥 +犇 +薡 +軎 +² +應 +醜 +刪 +緻 +鶴 +賜 +噁 +軌 +尨 +镔 +鷺 +槗 +彌 +葚 +濛 +請 +溇 +緹 +賢 +訪 +獴 +瑅 +資 +縤 +陣 +蕟 +栢 +韻 +祼 +恁 +伢 +謝 +劃 +涑 +總 +衖 +踺 +砋 +凉 +籃 +駿 +苼 +瘋 +昽 +紡 +驊 +腎 +﹗ +響 +杋 +剛 +嚴 +禪 +歓 +槍 +傘 +檸 +檫 +炣 +勢 +鏜 +鎢 +銑 +尐 +減 +奪 +惡 +θ +僮 +婭 +臘 +ū +ì +殻 +鉄 +∑ +蛲 +焼 +緖 +續 +紹 +懮 diff --git a/modules/onnx_ocr_module/models/cls_dyn.fp16.onnx b/modules/onnx_ocr_module/models/cls_dyn.fp16.onnx new file mode 100644 index 0000000..ceda47e Binary files /dev/null and b/modules/onnx_ocr_module/models/cls_dyn.fp16.onnx differ diff --git a/modules/onnx_ocr_module/models/cls_dyn.opt.onnx b/modules/onnx_ocr_module/models/cls_dyn.opt.onnx new file mode 100644 index 0000000..7ebabb1 Binary files /dev/null and b/modules/onnx_ocr_module/models/cls_dyn.opt.onnx differ diff --git a/modules/onnx_ocr_module/models/cls_dyn.shaped.onnx b/modules/onnx_ocr_module/models/cls_dyn.shaped.onnx new file mode 100644 index 0000000..e2f64ca Binary files /dev/null and b/modules/onnx_ocr_module/models/cls_dyn.shaped.onnx differ diff --git a/modules/onnx_ocr_module/models/cls_dyn.simp.onnx b/modules/onnx_ocr_module/models/cls_dyn.simp.onnx new file mode 100644 index 0000000..7ebabb1 Binary files /dev/null and b/modules/onnx_ocr_module/models/cls_dyn.simp.onnx differ diff --git a/modules/onnx_ocr_module/models/det_dyn.fp16.onnx b/modules/onnx_ocr_module/models/det_dyn.fp16.onnx new file mode 100644 index 0000000..007729d Binary files /dev/null and b/modules/onnx_ocr_module/models/det_dyn.fp16.onnx differ diff --git a/modules/onnx_ocr_module/models/det_dyn.opt.onnx b/modules/onnx_ocr_module/models/det_dyn.opt.onnx new file mode 100644 index 0000000..0d8dda4 Binary files /dev/null and b/modules/onnx_ocr_module/models/det_dyn.opt.onnx differ diff --git a/modules/onnx_ocr_module/models/det_dyn.shaped.onnx b/modules/onnx_ocr_module/models/det_dyn.shaped.onnx new file mode 100644 index 0000000..1bcd67f Binary files /dev/null and b/modules/onnx_ocr_module/models/det_dyn.shaped.onnx differ diff --git a/modules/onnx_ocr_module/models/det_dyn.simp.onnx b/modules/onnx_ocr_module/models/det_dyn.simp.onnx new file mode 100644 index 0000000..0d8dda4 Binary files /dev/null and b/modules/onnx_ocr_module/models/det_dyn.simp.onnx differ diff --git a/modules/onnx_ocr_module/models/rec_dyn.fp16.onnx b/modules/onnx_ocr_module/models/rec_dyn.fp16.onnx new file mode 100644 index 0000000..8a67ea4 Binary files /dev/null and b/modules/onnx_ocr_module/models/rec_dyn.fp16.onnx differ diff --git a/modules/onnx_ocr_module/models/rec_dyn.opt.onnx b/modules/onnx_ocr_module/models/rec_dyn.opt.onnx new file mode 100644 index 0000000..c8ba3a7 Binary files /dev/null and b/modules/onnx_ocr_module/models/rec_dyn.opt.onnx differ diff --git a/modules/onnx_ocr_module/models/rec_dyn.shaped.onnx b/modules/onnx_ocr_module/models/rec_dyn.shaped.onnx new file mode 100644 index 0000000..865b340 Binary files /dev/null and b/modules/onnx_ocr_module/models/rec_dyn.shaped.onnx differ diff --git a/modules/onnx_ocr_module/models/rec_dyn.simp.onnx b/modules/onnx_ocr_module/models/rec_dyn.simp.onnx new file mode 100644 index 0000000..c8ba3a7 Binary files /dev/null and b/modules/onnx_ocr_module/models/rec_dyn.simp.onnx differ diff --git a/modules/onnx_ocr_module/run_ocr_test.py b/modules/onnx_ocr_module/run_ocr_test.py new file mode 100644 index 0000000..a808257 --- /dev/null +++ b/modules/onnx_ocr_module/run_ocr_test.py @@ -0,0 +1,121 @@ +# -*- coding: utf-8 -*- +import os +import sys +import logging + + +class _MockLogger: + def log(self, message, level=logging.INFO, exc_info=False): + level_name = { + logging.DEBUG: "DEBUG", + logging.INFO: "INFO", + logging.WARNING: "WARNING", + logging.ERROR: "ERROR", + }.get(level, "INFO") + print(f"[{level_name}] {message}") + + +class _MockGPUManager: + def __init__(self, can_use_cuda=False): + self.can_use_cuda = can_use_cuda + + +def main(): + # 실행 컨텍스트별 베이스 디렉터리 계산 + if getattr(sys, "frozen", False): + # cx_Freeze 실행 파일 기준 (exe 디렉터리) + base_dir = os.path.dirname(sys.executable) + else: + base_dir = os.path.dirname(os.path.abspath(__file__)) + + # 호스트 환경의 site-packages 경로를 우선 추가 (numpy/cv2 로드용) + candidates = [] + try: + root_dir = os.path.abspath(os.path.join(base_dir, "..", "..", "..", "..")) + candidates.append(os.path.join(root_dir, "Lib", "site-packages")) + except Exception: + pass + try: + exe_dir = os.path.abspath(os.path.join(os.path.dirname(sys.argv[0]), "..", "..", "..", "..")) + candidates.append(os.path.join(exe_dir, "Lib", "site-packages")) + except Exception: + pass + # 워크스페이스 고정 경로 (테스트 편의) + candidates.append(r"D:\py\AutoPercenty3_311\Lib\site-packages") + for sp in candidates: + if os.path.isdir(sp) and sp not in sys.path: + sys.path.insert(0, sp) + + src_dir = os.path.join(base_dir, "src") + if os.path.isdir(src_dir): + sys.path.insert(0, src_dir) + + # 우선 numpy/cv2 임포트 경로 진단 + try: + import numpy as _np # type: ignore + print(f"[INFO] numpy loaded from: {_np.__file__}") + except Exception as e: + print("OpenCV bindings requires \"numpy\" package.\nInstall it via command:\n pip install numpy") + print(f"❌ numpy 임포트 실패: {e}") + return 2 + try: + import cv2 as _cv2 # type: ignore + print(f"[INFO] cv2 loaded from: {_cv2.__file__}") + except Exception as e: + print("OpenCV bindings requires \"numpy\" package.") + print(f"❌ cv2 임포트 실패: {e}") + return 2 + + try: + # 번들된 src 내부 경로로 임포트 시도 + try: + from onnx_ocr_module import ONNXOCRModule # type: ignore + except Exception: + # 패키지식 경로 (src.onnx_ocr_module) + from src.onnx_ocr_module import ONNXOCRModule # type: ignore + except Exception as e: + print(f"❌ onnx_ocr_module 임포트 실패: {e}") + return 2 + + logger = _MockLogger() + gpu_manager = _MockGPUManager(can_use_cuda=False) + + try: + ocr = ONNXOCRModule( + logger=logger, + base_dir=base_dir, + gpu_manager=gpu_manager, + execution_provider="auto", + ) + except Exception as e: + print(f"❌ ONNXOCRModule 초기화 실패: {e}") + return 3 + + test_img = os.path.join(base_dir, "test", "1.jpg") + if not os.path.exists(test_img): + print(f"❌ 테스트 이미지가 없습니다: {test_img}") + return 4 + + try: + results = ocr.detect_text(test_img, method="polygon") + except Exception as e: + print(f"❌ OCR 실행 실패: {e}") + return 5 + + count = len(results) if results else 0 + print(f"RESULT_COUNT {count}") + if count: + sample = results[0] + print( + f"SAMPLE '{sample.get('text','')}' conf={sample.get('confidence', 0.0):.3f}" + ) + return 0 + else: + print("⚠️ 결과가 비어있습니다") + return 6 + + +if __name__ == "__main__": + raise SystemExit(main()) + + diff --git a/modules/onnx_ocr_module/setup_onnx_ocr.py b/modules/onnx_ocr_module/setup_onnx_ocr.py new file mode 100644 index 0000000..e906a65 --- /dev/null +++ b/modules/onnx_ocr_module/setup_onnx_ocr.py @@ -0,0 +1,93 @@ +# -*- coding: utf-8 -*- +import os +import sys +from cx_Freeze import setup, Executable + +# 메타데이터 스캔으로 인한 재귀 이슈를 피하기 위해 간단한 우회 적용 +try: + import cx_Freeze.module as _cx_mod + + def _noop_update_distribution(self, name=None): + return None + + _cx_mod.Module.update_distribution = _noop_update_distribution # type: ignore[attr-defined] + # 훅 로딩 자체를 비활성화하여 재귀 이슈 회피 + def _noop_load_hook(self): + return None + _cx_mod.Module.load_hook = _noop_load_hook # type: ignore[attr-defined] +except Exception: + pass + +base_dir = os.path.dirname(os.path.abspath(__file__)) +src_dir = os.path.join(base_dir, "src") + +includes = [ + # onnxruntime 런타임 사용 경로만 명시 (나머지는 런타임에 src 경로에서 import) + "onnxruntime", +] + +packages = [ + "os", + "sys", +] + +excludes = [ + # 완전 ONNX-only 경로: paddle 계열 제외 + "paddle", + "paddleocr", + "paddlehub", + # 호스트 환경에서 로드하도록 제외 + "numpy", + "cv2", + "PIL", + "onnxruntime", +] + +include_files = [] + +# 모델/사전 파일 포함 +models_dir = os.path.join(base_dir, "models") +dict_dir = os.path.join(base_dir, "dict") +test_dir = os.path.join(base_dir, "test") +if os.path.isdir(models_dir): + include_files.append((models_dir, "models")) +if os.path.isdir(dict_dir): + include_files.append((dict_dir, "dict")) +if os.path.isdir(test_dir): + include_files.append((test_dir, "test")) + +# 런타임에 src를 import 경로로 쓰므로 동봉 +if os.path.isdir(src_dir): + include_files.append((src_dir, "src")) + +# zip 대신 평문 폴더 배치로 경로 문제 회피 +zip_includes = [] + +build_exe_options = { + "includes": includes, + "packages": packages, + "excludes": excludes, + "include_files": include_files, + "zip_include_packages": zip_includes, + "include_msvcr": True, + "optimize": 0, + "silent": True, +} + +executables = [ + Executable( + script=os.path.join(base_dir, "run_ocr_test.py"), + base=None, + target_name="onnx_ocr_test.exe", + ) +] + +setup( + name="onnx_ocr_test", + version="0.1.0", + description="ONNX OCR module minimal test", + options={"build_exe": build_exe_options}, + executables=executables, +) + + diff --git a/modules/onnx_ocr_module/src/__init__.py b/modules/onnx_ocr_module/src/__init__.py new file mode 100644 index 0000000..a8a9446 --- /dev/null +++ b/modules/onnx_ocr_module/src/__init__.py @@ -0,0 +1,10 @@ +# -*- coding: utf-8 -*- +""" +ONNX OCR 모듈 패키지 +기존 OCRModule을 완전히 대체할 수 있는 ONNX 기반 OCR 모듈 +""" + +from .onnx_ocr_wrapper import ONNXOCRModule, OCRModule + +__version__ = "1.0.0" +__all__ = ["ONNXOCRModule", "OCRModule"] diff --git a/modules/onnx_ocr_module/src/__pycache__/__init__.cpython-311.pyc b/modules/onnx_ocr_module/src/__pycache__/__init__.cpython-311.pyc new file mode 100644 index 0000000..f259439 Binary files /dev/null and b/modules/onnx_ocr_module/src/__pycache__/__init__.cpython-311.pyc differ diff --git a/modules/onnx_ocr_module/src/__pycache__/onnx_ocr_module.cpython-311.pyc b/modules/onnx_ocr_module/src/__pycache__/onnx_ocr_module.cpython-311.pyc new file mode 100644 index 0000000..e406cbc Binary files /dev/null and b/modules/onnx_ocr_module/src/__pycache__/onnx_ocr_module.cpython-311.pyc differ diff --git a/modules/onnx_ocr_module/src/__pycache__/onnx_ocr_wrapper.cpython-311.pyc b/modules/onnx_ocr_module/src/__pycache__/onnx_ocr_wrapper.cpython-311.pyc new file mode 100644 index 0000000..3156daa Binary files /dev/null and b/modules/onnx_ocr_module/src/__pycache__/onnx_ocr_wrapper.cpython-311.pyc differ diff --git a/modules/onnx_ocr_module/src/onnx_ocr_module.py b/modules/onnx_ocr_module/src/onnx_ocr_module.py new file mode 100644 index 0000000..44b62d4 --- /dev/null +++ b/modules/onnx_ocr_module/src/onnx_ocr_module.py @@ -0,0 +1,724 @@ +# -*- coding: utf-8 -*- +""" +ONNX OCR 모듈 - ONNXRuntime을 사용한 텍스트 감지 +기존 OCRModule과 동일한 인터페이스를 제공하며 CPU/CUDA/TensorRT 최적화 모델 지원 +""" + +import cv2 +import numpy as np +import os +import logging +import time +from typing import List, Dict, Any, Optional +import sys +try: + import onnxruntime as ort +except ImportError: + raise ImportError("ONNXRuntime이 설치되지 않았습니다: pip install onnxruntime-gpu") + + +class ONNXOCRModule: + def __init__(self, logger=None, base_dir=None, gpu_manager=None, execution_provider='auto'): + """ + ONNX OCR 모듈 초기화 + + Args: + logger: 로거 인스턴스 + base_dir: 베이스 디렉토리 (None이면 현재 모듈 위치 사용) + gpu_manager: GPU 관리자 인스턴스 + execution_provider: 실행 프로바이더 ('auto', 'cpu', 'cuda', 'tensorrt') + """ + self.logger = logger + # exe로 번들된 경우와 소스 직접 실행을 모두 지원 + if base_dir: + self.base_dir = base_dir + else: + if getattr(sys, "frozen", False): + self.base_dir = os.path.dirname(sys.executable) + else: + self.base_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) + self.gpu_manager = gpu_manager + + # 실행 프로바이더 결정 + self.execution_provider = self._determine_execution_provider(execution_provider) + + # 모델 경로 설정 + self.models_dir = os.path.join(self.base_dir, "models") + self.dict_dir = os.path.join(self.base_dir, "dict") + + # 문자 사전 로드 + self.char_dict_path = os.path.join(self.dict_dir, "ppocr_keys_v1.txt") + self.character = self._load_character_dict() + + # ONNX 세션 초기화 + self.det_session = None + self.rec_session = None + self.cls_session = None + + self._initialize_onnx_sessions() + + if self.logger: + self.logger.log(f"✅ ONNX OCR 모듈 초기화 완료 ({self.execution_provider} 모드)", level=logging.INFO) + + def _determine_execution_provider(self, execution_provider: str) -> str: + """실행 프로바이더 결정 (auto: ORT providers 기반 자동 선택)""" + if execution_provider.lower() != 'auto': + return execution_provider.lower() + + try: + available = ort.get_available_providers() + except Exception: + available = [] + + # 우선순위: TensorRT > CUDA > DirectML > CPU + if 'TensorrtExecutionProvider' in available: + return 'tensorrt' + if 'CUDAExecutionProvider' in available: + return 'cuda' + if 'DmlExecutionProvider' in available: + return 'directml' + return 'cpu' + + def _get_providers_and_model_suffix(self) -> tuple: + """실행 프로바이더와 모델 접미사 반환""" + if self.execution_provider == 'tensorrt': + providers = ['TensorrtExecutionProvider', 'CUDAExecutionProvider', 'CPUExecutionProvider'] + model_suffix = 'opt' # TensorRT용 최적화 모델 + elif self.execution_provider == 'cuda': + providers = ['CUDAExecutionProvider', 'CPUExecutionProvider'] + model_suffix = 'fp16' # CUDA용 FP16 모델 + elif self.execution_provider == 'directml': + providers = ['DmlExecutionProvider', 'CPUExecutionProvider'] + model_suffix = 'fp16' # DirectML용 FP16 모델 + else: + providers = ['CPUExecutionProvider'] + model_suffix = 'simp' # CPU용 단순화 모델 + + return providers, model_suffix + + def _initialize_onnx_sessions(self): + """ONNX 세션 초기화""" + providers, model_suffix = self._get_providers_and_model_suffix() + + # 세션 옵션 설정 + sess_options = ort.SessionOptions() + sess_options.graph_optimization_level = ort.GraphOptimizationLevel.ORT_ENABLE_ALL + + if self.execution_provider in ['cuda', 'tensorrt']: + sess_options.enable_mem_pattern = False # GPU 메모리 최적화 + + # 모델 파일 경로 + det_model_path = os.path.join(self.models_dir, f"det_dyn.{model_suffix}.onnx") + rec_model_path = os.path.join(self.models_dir, f"rec_dyn.{model_suffix}.onnx") + cls_model_path = os.path.join(self.models_dir, f"cls_dyn.{model_suffix}.onnx") + + try: + # Detection 모델 + if os.path.exists(det_model_path): + self.det_session = ort.InferenceSession( + det_model_path, + sess_options=sess_options, + providers=providers + ) + if self.logger: + self.logger.log(f"Detection 모델 로드: {det_model_path}", level=logging.INFO) + else: + raise FileNotFoundError(f"Detection 모델을 찾을 수 없습니다: {det_model_path}") + + # Recognition 모델 + if os.path.exists(rec_model_path): + self.rec_session = ort.InferenceSession( + rec_model_path, + sess_options=sess_options, + providers=providers + ) + if self.logger: + self.logger.log(f"Recognition 모델 로드: {rec_model_path}", level=logging.INFO) + else: + raise FileNotFoundError(f"Recognition 모델을 찾을 수 없습니다: {rec_model_path}") + + # Classification 모델 (선택적) + if os.path.exists(cls_model_path): + self.cls_session = ort.InferenceSession( + cls_model_path, + sess_options=sess_options, + providers=providers + ) + if self.logger: + self.logger.log(f"Classification 모델 로드: {cls_model_path}", level=logging.INFO) + else: + if self.logger: + self.logger.log("Classification 모델을 사용하지 않습니다", level=logging.WARNING) + + except Exception as e: + if self.logger: + self.logger.log(f"ONNX 세션 초기화 실패: {e}", level=logging.ERROR, exc_info=True) + raise + + def _load_character_dict(self) -> List[str]: + """문자 사전 로드 (UTF-8 인코딩 문제 해결)""" + if not os.path.exists(self.char_dict_path): + if self.logger: + self.logger.log(f"문자 사전 파일을 찾을 수 없습니다: {self.char_dict_path}", level=logging.ERROR) + return [] + + character = [] + # UTF-8 인코딩 문제 해결을 위한 안전한 파일 읽기 + try: + with open(self.char_dict_path, 'r', encoding='utf-8') as f: + character = [line.strip() for line in f.readlines()] + except UnicodeDecodeError: + # UTF-8 실패 시 다른 인코딩 시도 + encodings = ['cp949', 'euc-kr', 'latin1', 'cp1252'] + success = False + for encoding in encodings: + try: + with open(self.char_dict_path, 'r', encoding=encoding) as f: + character = [line.strip() for line in f.readlines()] + if self.logger: + self.logger.log(f"문자 사전 파일 {encoding} 인코딩으로 읽기 성공", level=logging.INFO) + success = True + break + except UnicodeDecodeError: + continue + + if not success: + # 모든 인코딩 실패 시 바이너리 모드로 시도 + try: + with open(self.char_dict_path, 'rb') as f: + lines = f.readlines() + for line in lines: + try: + decoded_line = line.decode('utf-8').strip() + if decoded_line: # 빈 라인 제외 + character.append(decoded_line) + except UnicodeDecodeError: + # 개별 라인 디코딩 실패 시 건너뛰기 + continue + if self.logger: + self.logger.log(f"문자 사전 파일 바이너리 모드로 읽기 성공", level=logging.WARNING) + except Exception as e: + if self.logger: + self.logger.log(f"문자 사전 파일 읽기 완전 실패: {e}", level=logging.ERROR) + return [] + + # blank 토큰 추가 + character = ['blank'] + character + + if self.logger: + self.logger.log(f"문자 사전 로드 완료: {len(character)}개 문자", level=logging.INFO) + + return character + + def detect_text(self, image_path: str, method: str = 'polygon', raise_on_memory_error: bool = False) -> List[Dict[str, Any]]: + """ + 이미지에서 텍스트를 감지하고 다양한 방식으로 영역 반환 + 기존 OCRModule과 동일한 인터페이스 + + Args: + image_path (str): 이미지 파일 경로 + method (str): 감지 방식 ('polygon', 'bbox', 'expanded_bbox', 'rotated_bbox', 'contour') + raise_on_memory_error (bool): 메모리 에러 시 예외 발생 여부 + + Returns: + List[Dict]: 감지된 텍스트 정보 리스트 + """ + if not os.path.exists(image_path): + if self.logger: + self.logger.log(f"이미지 파일을 찾을 수 없습니다: {image_path}", level=logging.ERROR) + return [] + + image = None + try: + # 이미지 읽기 + image = cv2.imread(image_path) + if image is None: + if self.logger: + self.logger.log(f"이미지를 읽을 수 없습니다: {image_path}", level=logging.ERROR) + return [] + + if self.logger: + self.logger.log(f"🔍 ONNX OCR 감지 방식: {method}", level=logging.INFO) + + # 메모리 안전을 위한 이미지 크기 조정 + image = self._safe_resize_image(image) + + # OCR 실행 + start_time = time.time() + ocr_raw_results = self._run_ocr(image) + inference_time = (time.time() - start_time) * 1000 + + if self.logger: + self.logger.log(f"⚡ ONNX OCR 추론 완료: {inference_time:.1f}ms", level=logging.INFO) + + if not ocr_raw_results: + if self.logger: + self.logger.log("⚠️ OCR 결과가 비어있습니다", level=logging.WARNING) + return [] + + # 감지 방식에 따라 결과 처리 + if method == 'polygon': + ocr_results = self._detect_with_polygon(image, ocr_raw_results) + elif method == 'bbox': + ocr_results = self._detect_with_bbox(image, ocr_raw_results) + elif method == 'expanded_bbox': + ocr_results = self._detect_with_expanded_bbox(image, ocr_raw_results) + elif method == 'rotated_bbox': + ocr_results = self._detect_with_rotated_bbox(image, ocr_raw_results) + elif method == 'contour': + ocr_results = self._detect_with_contour(image, ocr_raw_results) + else: + if self.logger: + self.logger.log(f"⚠️ 지원하지 않는 감지 방식: {method}, 기본 polygon 방식 사용", level=logging.WARNING) + ocr_results = self._detect_with_polygon(image, ocr_raw_results) + + return ocr_results + + except Exception as e: + if self.logger: + self.logger.log(f"ONNX OCR 처리 중 오류: {e}", level=logging.ERROR, exc_info=True) + if raise_on_memory_error and ('memory' in str(e).lower() or 'primitive' in str(e).lower()): + raise MemoryError(f"ONNX OCR memory error: {e}") + return [] + finally: + if image is not None: + del image + + def _safe_resize_image(self, image: np.ndarray) -> np.ndarray: + """메모리 안전을 위한 이미지 크기 조정""" + h, w = image.shape[:2] + max_dim_safe = 2000 + aspect_ratio = max(w, h) / max(1, min(w, h)) + + if max(w, h) > max_dim_safe or aspect_ratio > 15: + scale = float(max_dim_safe) / float(max(w, h)) + new_size = (int(w * scale), int(h * scale)) + if self.logger: + self.logger.log( + f"⚖️ ONNX OCR용 다운스케일 ({w}x{h}) -> {new_size} (ratio={aspect_ratio:.1f})", + level=logging.INFO, + ) + image = cv2.resize(image, new_size, interpolation=cv2.INTER_AREA).copy() + + return image + + def _run_ocr(self, image: np.ndarray) -> List[List]: + """ONNX 모델을 사용하여 OCR 실행""" + # 1. 텍스트 감지 + dt_boxes = self._detect_text_boxes(image) + if len(dt_boxes) == 0: + return [] + + # 2. 텍스트 방향 분류 (선택적) + if self.cls_session is not None: + dt_boxes = self._classify_text_angle(image, dt_boxes) + + # 3. 텍스트 인식 + rec_results = self._recognize_text(image, dt_boxes) + + # 4. 결과 포맷팅 + ocr_results = [] + for i, (box, rec_result) in enumerate(zip(dt_boxes, rec_results)): + text, confidence = rec_result + ocr_results.append([box.tolist(), [text, confidence]]) + + return ocr_results + + def _detect_text_boxes(self, image: np.ndarray) -> np.ndarray: + """텍스트 박스 감지""" + # 전처리 + det_input = self._preprocess_detection(image) + + # 추론 + det_output = self.det_session.run(None, {"x": det_input})[0] + + # 후처리 + dt_boxes = self._postprocess_detection(det_output, image.shape) + + return dt_boxes + + def _preprocess_detection(self, image: np.ndarray) -> np.ndarray: + """Detection 전처리""" + # 이미지 정규화 및 크기 조정 + img = image.copy().astype(np.float32) + + # BGR to RGB + img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) + + # 크기 조정 (32의 배수로) + h, w = img.shape[:2] + resize_h = (h // 32) * 32 if h % 32 != 0 else h + resize_w = (w // 32) * 32 if w % 32 != 0 else w + + if resize_h != h or resize_w != w: + img = cv2.resize(img, (resize_w, resize_h)) + + # 정규화 + img = img / 255.0 + mean = np.array([0.485, 0.456, 0.406], dtype=np.float32) + std = np.array([0.229, 0.224, 0.225], dtype=np.float32) + img = (img - mean) / std + + # HWC to CHW + img = img.transpose(2, 0, 1) + + # 배치 차원 추가 + img = np.expand_dims(img, axis=0).astype(np.float32) + + return img + + def _postprocess_detection(self, det_output: np.ndarray, original_shape: tuple) -> np.ndarray: + """Detection 후처리""" + # 간단한 임계값 기반 후처리 + pred = det_output[0, 0] # [H, W] + + # 임계값 적용 + threshold = 0.3 + pred = (pred > threshold).astype(np.uint8) * 255 + + # 컨투어 찾기 + contours, _ = cv2.findContours(pred, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) + + dt_boxes = [] + for contour in contours: + # 최소 사각형 구하기 + rect = cv2.minAreaRect(contour) + box = cv2.boxPoints(rect) + box = np.array(box, dtype=np.float32) + + # 박스 크기 필터링 + box_area = cv2.contourArea(box) + if box_area < 100: # 너무 작은 박스 제거 + continue + + dt_boxes.append(box) + + return np.array(dt_boxes) if dt_boxes else np.array([]) + + def _classify_text_angle(self, image: np.ndarray, dt_boxes: np.ndarray) -> np.ndarray: + """텍스트 방향 분류 (회전 보정)""" + # 현재는 기본 구현 (실제로는 각 박스를 분류기에 넣어서 각도 보정) + return dt_boxes + + def _recognize_text(self, image: np.ndarray, dt_boxes: np.ndarray) -> List[tuple]: + """텍스트 인식""" + rec_results = [] + + for box in dt_boxes: + # 박스 영역 자르기 + crop_img = self._get_rotate_crop_image(image, box) + + # 전처리 + rec_input = self._preprocess_recognition(crop_img) + + # 추론 + rec_output = self.rec_session.run(None, {"x": rec_input})[0] + + # 후처리 + text, confidence = self._postprocess_recognition(rec_output) + + rec_results.append((text, confidence)) + + return rec_results + + def _get_rotate_crop_image(self, image: np.ndarray, box: np.ndarray) -> np.ndarray: + """회전된 박스 영역 자르기""" + # 박스 점들 정렬 + box = box.astype(np.float32) + + # 최소 사각형으로 변환 + rect = cv2.minAreaRect(box) + box_points = cv2.boxPoints(rect) + box_points = np.array(box_points, dtype=np.float32) + + # 가로가 더 긴 방향으로 정렬 + width = int(rect[1][0]) + height = int(rect[1][1]) + + if width < height: + width, height = height, width + + # 대상 좌표 + dst_pts = np.array([ + [0, 0], + [width, 0], + [width, height], + [0, height] + ], dtype=np.float32) + + # 변환 행렬 계산 + M = cv2.getPerspectiveTransform(box_points, dst_pts) + + # 이미지 변환 + crop_img = cv2.warpPerspective(image, M, (width, height)) + + return crop_img + + def _preprocess_recognition(self, image: np.ndarray) -> np.ndarray: + """Recognition 전처리""" + # 크기 조정 + img = cv2.resize(image, (320, 48)).astype(np.float32) + + # BGR to RGB + img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) + + # 정규화 + img = img / 255.0 + mean = np.array([0.485, 0.456, 0.406], dtype=np.float32) + std = np.array([0.229, 0.224, 0.225], dtype=np.float32) + img = (img - mean) / std + + # HWC to CHW + img = img.transpose(2, 0, 1) + + # 배치 차원 추가 + img = np.expand_dims(img, axis=0).astype(np.float32) + + return img + + def _postprocess_recognition(self, rec_output: np.ndarray) -> tuple: + """Recognition 후처리""" + # CTC 디코딩 + preds = rec_output[0] # [seq_len, num_classes] + + # Softmax + preds = np.exp(preds) / np.sum(np.exp(preds), axis=1, keepdims=True) + + # 가장 높은 확률의 문자 선택 + preds_idx = np.argmax(preds, axis=1) + + # CTC 블랭크 제거 및 중복 제거 + decoded_text = [] + prev_idx = -1 + + for idx in preds_idx: + if idx != 0 and idx != prev_idx: # 0은 blank + if idx < len(self.character): + decoded_text.append(self.character[idx]) + prev_idx = idx + + text = ''.join(decoded_text) + + # 신뢰도 계산 (평균 확률) + confidence = float(np.mean(np.max(preds, axis=1))) + + return text, confidence + + # 기존 OCRModule과 동일한 감지 방식들 + def _detect_with_polygon(self, image: np.ndarray, ocr_raw_results: List) -> List[Dict[str, Any]]: + """폴리곤 방식으로 텍스트 영역 감지 (기본 방식)""" + ocr_results = [] + + for line in ocr_raw_results: + if len(line) >= 2: + polygon = line[0] # 폴리곤 좌표 (4개 점) + text_info = line[1] # (텍스트, 신뢰도) + + if len(text_info) >= 2: + text = text_info[0] + confidence = text_info[1] + + # 폴리곤을 바운딩 박스로 변환 + polygon_np = np.array(polygon, dtype=np.int32) + x, y, w, h = cv2.boundingRect(polygon_np) + + ocr_result = { + 'text': text, + 'confidence': confidence, + 'polygon': polygon, + 'bbox': (x, y, w, h), + 'method': 'polygon' + } + ocr_results.append(ocr_result) + + return ocr_results + + def _detect_with_bbox(self, image: np.ndarray, ocr_raw_results: List) -> List[Dict[str, Any]]: + """바운딩 박스 방식으로 텍스트 영역 감지""" + ocr_results = [] + + for line in ocr_raw_results: + if len(line) >= 2: + polygon = line[0] + text_info = line[1] + + if len(text_info) >= 2: + text = text_info[0] + confidence = text_info[1] + + # 바운딩 박스 계산 + polygon_np = np.array(polygon, dtype=np.int32) + x, y, w, h = cv2.boundingRect(polygon_np) + + # 바운딩 박스를 폴리곤으로 변환 + bbox_polygon = [ + [x, y], + [x + w, y], + [x + w, y + h], + [x, y + h] + ] + + ocr_result = { + 'text': text, + 'confidence': confidence, + 'polygon': bbox_polygon, + 'bbox': (x, y, w, h), + 'method': 'bbox' + } + ocr_results.append(ocr_result) + + return ocr_results + + def _detect_with_expanded_bbox(self, image: np.ndarray, ocr_raw_results: List) -> List[Dict[str, Any]]: + """확장된 바운딩 박스 방식으로 텍스트 영역 감지""" + ocr_results = [] + h_img, w_img = image.shape[:2] + + for line in ocr_raw_results: + if len(line) >= 2: + polygon = line[0] + text_info = line[1] + + if len(text_info) >= 2: + text = text_info[0] + confidence = text_info[1] + + # 기본 바운딩 박스 + polygon_np = np.array(polygon, dtype=np.int32) + x, y, w, h = cv2.boundingRect(polygon_np) + + # 확장 크기 계산 (텍스트 크기의 20%) + expand_x = max(1, int(w * 0.2)) + expand_y = max(1, int(h * 0.2)) + + # 확장된 바운딩 박스 + x_exp = max(0, x - expand_x) + y_exp = max(0, y - expand_y) + w_exp = min(w_img - x_exp, w + 2 * expand_x) + h_exp = min(h_img - y_exp, h + 2 * expand_y) + + # 확장된 바운딩 박스를 폴리곤으로 변환 + expanded_polygon = [ + [x_exp, y_exp], + [x_exp + w_exp, y_exp], + [x_exp + w_exp, y_exp + h_exp], + [x_exp, y_exp + h_exp] + ] + + ocr_result = { + 'text': text, + 'confidence': confidence, + 'polygon': expanded_polygon, + 'bbox': (x_exp, y_exp, w_exp, h_exp), + 'method': 'expanded_bbox' + } + ocr_results.append(ocr_result) + + return ocr_results + + def _detect_with_rotated_bbox(self, image: np.ndarray, ocr_raw_results: List) -> List[Dict[str, Any]]: + """회전된 바운딩 박스 방식으로 텍스트 영역 감지""" + ocr_results = [] + + for line in ocr_raw_results: + if len(line) >= 2: + polygon = line[0] + text_info = line[1] + + if len(text_info) >= 2: + text = text_info[0] + confidence = text_info[1] + + # 회전된 바운딩 박스 계산 + polygon_np = np.array(polygon, dtype=np.float32) + rect = cv2.minAreaRect(polygon_np) + box = cv2.boxPoints(rect) + box = np.int32(box) + + # 일반 바운딩 박스도 계산 + x, y, w, h = cv2.boundingRect(polygon_np.astype(np.int32)) + + ocr_result = { + 'text': text, + 'confidence': confidence, + 'polygon': box.tolist(), + 'bbox': (x, y, w, h), + 'method': 'rotated_bbox', + 'rotation_info': { + 'center': rect[0], + 'size': rect[1], + 'angle': rect[2] + } + } + ocr_results.append(ocr_result) + + return ocr_results + + def _detect_with_contour(self, image: np.ndarray, ocr_raw_results: List) -> List[Dict[str, Any]]: + """컨투어 방식으로 텍스트 영역 감지""" + ocr_results = [] + + for line in ocr_raw_results: + if len(line) >= 2: + polygon = line[0] + text_info = line[1] + + if len(text_info) >= 2: + text = text_info[0] + confidence = text_info[1] + + # 폴리곤을 컨투어로 변환 + polygon_np = np.array(polygon, dtype=np.int32) + + # 컨투어 근사화 + epsilon = 0.02 * cv2.arcLength(polygon_np, True) + approx_contour = cv2.approxPolyDP(polygon_np, epsilon, True) + + # 컨투어를 다시 폴리곤으로 변환 + contour_polygon = approx_contour.reshape(-1, 2).tolist() + + # 바운딩 박스 계산 + x, y, w, h = cv2.boundingRect(polygon_np) + + ocr_result = { + 'text': text, + 'confidence': confidence, + 'polygon': contour_polygon, + 'bbox': (x, y, w, h), + 'method': 'contour', + 'contour_points': len(contour_polygon) + } + ocr_results.append(ocr_result) + + return ocr_results + + def filter_chinese_text(self, ocr_results: List[Dict]) -> List[Dict]: + """중국어 텍스트만 필터링""" + chinese_results = [] + + for result in ocr_results: + text = result['text'] + # 중국어 문자 범위 확인 (간체/번체 포함) + if any('\u4e00' <= char <= '\u9fff' for char in text): + chinese_results.append(result) + + if self.logger: + self.logger.log(f"중국어 텍스트 {len(chinese_results)}개 필터링 완료", level=logging.INFO) + return chinese_results + + def filter_korean_text(self, ocr_results: List[Dict]) -> List[Dict]: + """한글 텍스트만 필터링""" + korean_results = [] + for result in ocr_results: + text = result['text'] + # 한글 유니코드 범위: 가~힣 + if any('\uac00' <= char <= '\ud7a3' for char in text): + korean_results.append(result) + + if self.logger: + self.logger.log(f"한글 텍스트 {len(korean_results)}개 필터링 완료", level=logging.INFO) + return korean_results + + +# 기존 OCRModule과의 호환성을 위한 별칭 +OCRModule = ONNXOCRModule diff --git a/modules/onnx_ocr_module/src/onnx_ocr_wrapper.py b/modules/onnx_ocr_module/src/onnx_ocr_wrapper.py new file mode 100644 index 0000000..392ec1a --- /dev/null +++ b/modules/onnx_ocr_module/src/onnx_ocr_wrapper.py @@ -0,0 +1,1218 @@ +# -*- coding: utf-8 -*- +""" +ONNX OCR 래퍼 모듈 - 기존 OCRModule 인터페이스 유지하면서 ONNX 사용 +기존 predict_system.py의 완벽한 전후처리 로직을 활용 +""" + +import cv2 +import numpy as np +import os +import sys +import logging +import time +from typing import List, Dict, Any + +# 완전 독립 모듈: 내부 src 폴더의 ppocr, tools 사용 +current_dir = os.path.dirname(__file__) # src 폴더 +sys.path.insert(0, current_dir) # src 폴더를 패스에 추가 + +# ONNX 전용 모드에서 불필요한 Paddle 의존성으로 인한 충돌을 막기 위한 안전 장치 +# (배포 디렉토리 내에 `paddle/` 폴더가 존재해 표준 패키지 대신 로컬 모듈이 로드되어 +# "partially initialized module 'paddle' has no attribute 'tensor'"가 발생하는 문제 대응) +try: # Paddle이 정상 패키지로 로드되었고 필요한 속성이 있으면 그대로 사용 + import paddle as _paddle # type: ignore + if not hasattr(_paddle, "tensor") or not hasattr(_paddle, "nn"): + raise ImportError("Incomplete paddle module detected") +except Exception: + # 최소 샘플링용 스텁 paddle 모듈을 주입해 import 에러/부분 초기화 문제를 회피 + import types # noqa: E401 + + paddle_stub = types.ModuleType("paddle") + # 패키지로 인식되도록 __path__ 추가 (submodule import 허용) + paddle_stub.__path__ = [] # type: ignore[attr-defined] + + # Tensor 더미 타입 (np.ndarray와 무관)로 정의하여 isinstance(np.ndarray, paddle.Tensor) 가 False가 되도록 함 + paddle_stub.Tensor = type("Tensor", (), {}) # type: ignore[attr-defined] + + # 기초 수학 API (일부 포스트프로세스/디코더에서 참조 가능) + paddle_stub.exp = np.exp # type: ignore[attr-defined] + paddle_stub.log = np.log # type: ignore[attr-defined] + + # to_tensor 대체 (np.array 변환만 수행) + def _to_tensor(x, dtype="float32"): + try: + return np.array(x, dtype=np.float32 if dtype in (None, "float32") else dtype) + except Exception: + return np.array(x) + + paddle_stub.to_tensor = _to_tensor # type: ignore[attr-defined] + + # device/cuda API 더미 (사용 시 no-op) + _cuda = types.SimpleNamespace(empty_cache=lambda: None) + # device 및 컴파일 가능 여부 함수 더미 + def _false(*args, **kwargs): + return False + + device_ns = types.SimpleNamespace( + cuda=_cuda, + is_compiled_with_xpu=_false, + is_compiled_with_npu=_false, + is_compiled_with_custom_device=_false, + is_compiled_with_mlu=_false, + ) + paddle_stub.device = device_ns # type: ignore[attr-defined] + + # 최상위 컴파일 가능 여부 질의 (program.py 등에서 사용) + paddle_stub.is_compiled_with_cuda = _false # type: ignore[attr-defined] + + # nn.functional.softmax 대체 구현 (numpy 기반) + nn_mod = types.ModuleType("paddle.nn") + fn_mod = types.ModuleType("paddle.nn.functional") + + def _softmax(x, axis=1): + x = np.asarray(x, dtype=np.float32) + x_max = np.max(x, axis=axis, keepdims=True) + e = np.exp(x - x_max) + return e / np.sum(e, axis=axis, keepdims=True) + + fn_mod.softmax = _softmax # type: ignore[attr-defined] + nn_mod.functional = fn_mod # type: ignore[attr-defined] + paddle_stub.nn = nn_mod # type: ignore[attr-defined] + + # tensor 서브모듈 더미 (일부 코드가 paddle.tensor.split 참조) + tensor_mod = types.ModuleType("paddle.tensor") + + def _split(x, num_or_sections, axis=0): # 단순 분할 대체 (사용될 경우 최소 동작 보장) + try: + if isinstance(num_or_sections, int): + return np.array_split(x, num_or_sections, axis=axis) + except Exception: + pass + # 사용되지 않는 경로이므로 원본 반환 + return [np.asarray(x)] + + tensor_mod.split = _split # type: ignore[attr-defined] + + # inference 서브모듈 더미 (일부 코드가 from paddle import inference 또는 + # from paddle.inference import Config, create_predictor 를 수행) + inference_mod = types.ModuleType("paddle.inference") + + class _DummyConfig: # 최소 더미 클래스 + def __init__(self, *args, **kwargs): + pass + + def _dummy_create_predictor(*args, **kwargs): + # ONNX 경로만 사용되므로 실제 호출되면 안 됨 + raise RuntimeError("Paddle inference disabled in ONNX mode") + + inference_mod.Config = _DummyConfig # type: ignore[attr-defined] + inference_mod.create_predictor = _dummy_create_predictor # type: ignore[attr-defined] + + # sys.modules 에 주입하여 이후 import 경로를 이 스텁으로 고정 + sys.modules["paddle"] = paddle_stub + sys.modules["paddle.nn"] = nn_mod + sys.modules["paddle.nn.functional"] = fn_mod + sys.modules["paddle.tensor"] = tensor_mod + sys.modules["paddle.inference"] = inference_mod + + # from paddle import inference 형태도 지원되도록 속성 연결 + paddle_stub.inference = inference_mod # type: ignore[attr-defined] + + # distributed 서브모듈 더미 (일부 코드가 import paddle.distributed as dist 수행) + distributed_mod = types.ModuleType("paddle.distributed") + distributed_mod.init_parallel_env = lambda *a, **k: None # type: ignore[attr-defined] + distributed_mod.barrier = lambda *a, **k: None # type: ignore[attr-defined] + distributed_mod.get_rank = lambda *a, **k: 0 # type: ignore[attr-defined] + distributed_mod.get_world_size = lambda *a, **k: 1 # type: ignore[attr-defined] + sys.modules["paddle.distributed"] = distributed_mod + paddle_stub.distributed = distributed_mod # type: ignore[attr-defined] + + # version 더미 (program.py에서 major/minor 사용) + paddle_stub.version = types.SimpleNamespace(major="2", minor="5") # type: ignore[attr-defined] + + # io 서브모듈 더미 (일부 경로에서 import paddle.io 사용) + io_mod = types.ModuleType("paddle.io") + # 최소 기능의 Dataset/Sampler/BatchSampler/DistributedBatchSampler/DataLoader 제공 + class _Dataset: + def __init__(self, *args, **kwargs): + pass + def __len__(self): + return 0 + def __getitem__(self, idx): + raise IndexError + + class _Sampler: + def __iter__(self): + raise NotImplementedError + def __len__(self): + raise NotImplementedError + + class _BatchSampler: + def __init__(self, dataset, batch_size=1, shuffle=False, drop_last=False, **kwargs): + self.dataset = dataset + self.batch_size = int(batch_size) if batch_size else 1 + self.shuffle = bool(shuffle) + self.drop_last = bool(drop_last) + def __iter__(self): + length = len(self.dataset) if hasattr(self.dataset, "__len__") else 0 + indices = np.arange(length) + if self.shuffle: + rng = np.random.default_rng() + rng.shuffle(indices) + for i in range(0, length, self.batch_size): + batch_idx = indices[i:i + self.batch_size] + if len(batch_idx) < self.batch_size and self.drop_last: + break + yield batch_idx.tolist() + def __len__(self): + length = len(self.dataset) if hasattr(self.dataset, "__len__") else 0 + if self.drop_last: + return length // self.batch_size + return (length + self.batch_size - 1) // self.batch_size + + class _DistributedBatchSampler(_BatchSampler): + def __init__(self, dataset, batch_size=1, shuffle=False, drop_last=False, num_replicas=None, rank=None, **kwargs): + super().__init__(dataset, batch_size, shuffle, drop_last) + # 분산 환경이더라도 ONNX 추론 경로에서는 단일 카드 가정 + try: + dist = sys.modules.get("paddle.distributed") + world_size = getattr(dist, "get_world_size", lambda: 1)() + current_rank = getattr(dist, "get_rank", lambda: 0)() + except Exception: + world_size, current_rank = 1, 0 + self.world_size = int(num_replicas) if num_replicas is not None else int(world_size) + self.rank = int(rank) if rank is not None else int(current_rank) + def __iter__(self): + length = len(self.dataset) if hasattr(self.dataset, "__len__") else 0 + indices = np.arange(length) + if self.shuffle: + rng = np.random.default_rng() + rng.shuffle(indices) + # 랭크별로 샘플 분할 + indices = indices[self.rank :: max(self.world_size, 1)] + for i in range(0, len(indices), self.batch_size): + batch_idx = indices[i:i + self.batch_size] + if len(batch_idx) < self.batch_size and self.drop_last: + break + yield batch_idx.tolist() + + class _DataLoader: + def __init__(self, dataset, batch_size=1, shuffle=False, drop_last=False, batch_sampler=None, collate_fn=None, **kwargs): + self.dataset = dataset + self.collate_fn = collate_fn + if batch_sampler is None: + self.batch_sampler = _BatchSampler(dataset, batch_size=batch_size, shuffle=shuffle, drop_last=drop_last) + else: + self.batch_sampler = batch_sampler + def __iter__(self): + for batch_indices in self.batch_sampler: + batch = [self.dataset[idx] for idx in batch_indices] + if self.collate_fn is not None: + yield self.collate_fn(batch) + else: + yield batch + def __len__(self): + try: + return len(self.batch_sampler) + except Exception: + return 0 + + io_mod.Dataset = _Dataset # type: ignore[attr-defined] + io_mod.Sampler = _Sampler # type: ignore[attr-defined] + io_mod.BatchSampler = _BatchSampler # type: ignore[attr-defined] + io_mod.DistributedBatchSampler = _DistributedBatchSampler # type: ignore[attr-defined] + io_mod.DataLoader = _DataLoader # type: ignore[attr-defined] + sys.modules["paddle.io"] = io_mod + paddle_stub.io = io_mod # type: ignore[attr-defined] + + # 메타 경로 훅: 'paddle.*' 서브모듈을 요청 시 자동 더미 생성 (whack-a-mole 방지) + import importlib.abc + import importlib.util + + class _PaddleStubLoader(importlib.abc.Loader): + def create_module(self, spec): # noqa: D401 + return types.ModuleType(spec.name) + def exec_module(self, module): # noqa: D401 + # 속성 접근 시 동적 하위 모듈 생성 + def __getattr__(name): + full = f"{module.__name__}.{name}" + mod = sys.modules.get(full) + if mod is None: + mod = types.ModuleType(full) + sys.modules[full] = mod + return mod + module.__getattr__ = __getattr__ # type: ignore[attr-defined] + + class _PaddleStubFinder(importlib.abc.MetaPathFinder): + def find_spec(self, fullname, path=None, target=None): + if fullname == "paddle" or fullname.startswith("paddle."): + if fullname in sys.modules: + return None + return importlib.util.spec_from_loader(fullname, _PaddleStubLoader()) + return None + + sys.meta_path.insert(0, _PaddleStubFinder()) + +class ONNXOCRModule: + """ + 기존 OCRModule과 완전히 동일한 인터페이스를 제공하면서 내부적으로 ONNX 사용 + """ + def __init__(self, logger=None, base_dir=None, gpu_manager=None, toggle_states=None): + self.logger = logger + + # 타임아웃 발생 횟수 추적 (연속 3회 시 ImageWorker 재시작 권장) + self._consecutive_timeout_count = 0 + self._max_consecutive_timeouts = 3 + + # mainUI_SP.py의 get_base_dir()에서 전달받은 src 폴더 경로 사용 + if base_dir: + # 전달받은 base_dir가 이미 onnx_ocr_module 루트일 수 있으므로 자동 보정 + if os.path.isdir(os.path.join(base_dir, "models")) and os.path.isdir(os.path.join(base_dir, "dict")): + self.onnx_module_dir = base_dir + else: + self.onnx_module_dir = os.path.join(base_dir, "onnx_ocr_module") + else: + # exe 번들 환경 우선 탐색 + if getattr(sys, "frozen", False): + exe_dir = os.path.dirname(sys.executable) + if os.path.isdir(os.path.join(exe_dir, "models")): + self.onnx_module_dir = exe_dir + else: + self.onnx_module_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) + else: + self.onnx_module_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) + + self.base_dir = base_dir # 원본 base_dir 보존 + self.gpu_manager = gpu_manager + self.toggle_states = toggle_states or {} + + # 프로바이더 캐시 경로 + try: + cache_root = os.path.join(self.base_dir, "user_data") if self.base_dir else os.path.dirname(os.path.dirname(os.path.abspath(__file__))) + os.makedirs(cache_root, exist_ok=True) + self._provider_cache_path = os.path.join(cache_root, "ocr_provider.json") + except Exception: + self._provider_cache_path = None + + # GPU/DML 사용 여부 결정 (override → 캐시 → GPUManager 순) + self.use_gpu = False + try: + override = (self.toggle_states or {}).get('ocr_provider_override', 'auto') + if isinstance(override, str): + override = override.lower() + if override == 'dml': + self.use_gpu = True + elif override == 'cpu': + self.use_gpu = False + else: + # 캐시 확인 + cached = self._read_provider_cache() + if cached == 'dml': + self.use_gpu = True + elif cached == 'cpu': + self.use_gpu = False + else: + # GPU 관리자 기준 + self.use_gpu = bool(gpu_manager and getattr(gpu_manager, 'can_use_cuda', False)) + except Exception: + self.use_gpu = bool(gpu_manager and getattr(gpu_manager, 'can_use_cuda', False)) + + # 모델 타입 설정 (toggle_states에서 가져옴) + self.model_type = self._determine_model_type() + + if self.logger: + self.logger.log(f"ONNX OCR 모델 타입 결정: {self.model_type} (GPU: {self.use_gpu})", level=logging.INFO) + + # 환경 변수 설정 (DirectML은 별도 환경변수 불필요) + if self.use_gpu: + if self.logger: + self.logger.log("ONNX OCR 모듈 DirectML 모드 활성화", level=logging.INFO) + # DirectML은 CUDA_VISIBLE_DEVICES 불필요 + else: + if self.logger: + self.logger.log("ONNX OCR 모듈 CPU 모드로 설정", level=logging.INFO) + + # OpenCV 및 멀티스레드 설정 (기존과 동일) + try: + import cv2 as _cv2 + _cv2.ocl.setUseOpenCL(False) + except Exception: + pass + + os.environ["OMP_NUM_THREADS"] = "1" + os.environ["KMP_DUPLICATE_LIB_OK"] = "True" + os.environ['OPENBLAS_NUM_THREADS'] = '1' + os.environ['MKL_NUM_THREADS'] = '1' + os.environ['NUMEXPR_NUM_THREADS'] = '1' + + # ONNX 시스템 초기화 (안전한 폴백 로직) + self.text_system = None + initialization_attempts = [] + + # 1차 시도: 원본 설정으로 초기화 + try: + if self.logger: + initial_mode = "DirectML" if self.use_gpu else "CPU" + self.logger.log(f"🚀 ONNX TextSystem 초기화 시작 ({initial_mode} 모드)", level=logging.INFO) + + self._initialize_onnx_system() + + if self.text_system is not None: + gpu_status = "DirectML" if self.use_gpu else "CPU" + if self.logger: + self.logger.log(f"✅ ONNX TextSystem 초기화 완료 ({gpu_status} + {self.model_type.upper()} 모델)", level=logging.INFO) + self.logger.log(f"✅ ONNX OCR 모듈 초기화 성공 ({gpu_status} 모드)", level=logging.INFO) + # 성공한 프로바이더 캐시 + try: + self._write_provider_cache('dml' if self.use_gpu else 'cpu') + except Exception: + pass + return + else: + initialization_attempts.append("원본 설정 실패") + + except Exception as e: + initialization_attempts.append(f"원본 설정 예외: {e}") + if self.logger: + self.logger.log(f"ONNX 초기화 1차 시도 실패: {e}", level=logging.WARNING) + + # 2차 시도: DirectML -> CPU 폴백 + if self.use_gpu: + try: + if self.logger: + self.logger.log("🔄 DirectML 실패로 CPU 모드로 재시도 중...", level=logging.WARNING) + + self.use_gpu = False + self._initialize_onnx_system() # CPU 모드로 재시도 + + if self.text_system is not None: + if self.logger: + self.logger.log("✅ CPU 폴백 모드로 ONNX TextSystem 초기화 성공", level=logging.INFO) + self.logger.log(f"✅ ONNX OCR 모듈 초기화 성공 (CPU 폴백 모드)", level=logging.INFO) + try: + self._write_provider_cache('cpu') + except Exception: + pass + return + else: + initialization_attempts.append("CPU 폴백 실패") + + except Exception as e: + initialization_attempts.append(f"CPU 폴백 예외: {e}") + if self.logger: + self.logger.log(f"CPU 폴백 시도도 실패: {e}", level=logging.WARNING) + + # 3차 시도: 더 안전한 모델로 재시도 (simp 모델) + if self.model_type != 'simp': + try: + if self.logger: + self.logger.log("🔄 더 안전한 SIMP 모델로 재시도 중...", level=logging.WARNING) + + original_model_type = self.model_type + self.model_type = 'simp' # 가장 안정적인 모델로 변경 + self.use_gpu = False # CPU 모드 강제 + self._initialize_onnx_system() + + if self.text_system is not None: + if self.logger: + self.logger.log("✅ SIMP 모델 + CPU 모드로 ONNX TextSystem 초기화 성공", level=logging.INFO) + self.logger.log(f"✅ ONNX OCR 모듈 초기화 성공 (SIMP + CPU 안전 모드)", level=logging.INFO) + return + else: + self.model_type = original_model_type # 원복 + initialization_attempts.append("SIMP 모델 폴백 실패") + + except Exception as e: + initialization_attempts.append(f"SIMP 모델 폴백 예외: {e}") + if self.logger: + self.logger.log(f"SIMP 모델 폴백도 실패: {e}", level=logging.ERROR) + + # 모든 시도 실패 + error_summary = " | ".join(initialization_attempts) + final_error = f"ONNX TextSystem 모든 초기화 시도 실패: {error_summary}" + + if self.logger: + self.logger.log(f"❌ {final_error}", level=logging.ERROR) + + raise Exception(final_error) + + def _determine_model_type(self): + """toggle_states에서 모델 타입을 결정합니다.""" + try: + # toggle_states에서 사용자 설정 가져오기 + onnx_model_type = self.toggle_states.get('onnx_model_type', '자동 선택') + gpu_info = self.toggle_states.get('gpu_info', {}) + + if self.logger: + self.logger.log(f"ONNX 모델 타입 설정값: {onnx_model_type}, GPU 정보: {gpu_info}", level=logging.DEBUG) + + # "자동 선택"인 경우 GPU 정보 기반 추천 모델 사용 + if onnx_model_type == '자동 선택': + recommended_model = gpu_info.get('recommended_model', 'simp') + if self.logger: + self.logger.log(f"자동 선택 모드: GPU 추천 모델 {recommended_model} 사용", level=logging.INFO) + return recommended_model + + # 사용자가 직접 선택한 경우 UI 텍스트를 내부 모델명으로 변환 + model_mapping = { + 'FP16 모델': 'fp16', + 'OPT 모델': 'opt', + 'SIMP 모델': 'simp' + } + + internal_model_type = model_mapping.get(onnx_model_type, 'opt') + if self.logger: + self.logger.log(f"사용자 선택 모드: {onnx_model_type} → {internal_model_type}", level=logging.INFO) + + return internal_model_type + + except Exception as e: + if self.logger: + self.logger.log(f"모델 타입 결정 중 오류, 기본값 사용: {e}", level=logging.WARNING) + return self._get_fallback_model_type() + + def _ensure_utf8_encoding(self, file_path): + """파일의 UTF-8 인코딩을 확인하고 필요시 변환합니다.""" + try: + if not os.path.exists(file_path): + if self.logger: + self.logger.log(f"문자 사전 파일이 존재하지 않습니다: {file_path}", level=logging.WARNING) + return file_path + + # 파일을 UTF-8으로 읽어보기 + try: + with open(file_path, 'r', encoding='utf-8') as f: + content = f.read() + if self.logger: + self.logger.log(f"문자 사전 파일 UTF-8 확인 완료: {file_path}", level=logging.DEBUG) + return file_path + except UnicodeDecodeError: + # UTF-8이 아닌 경우 다른 인코딩으로 시도 + if self.logger: + self.logger.log(f"문자 사전 파일 UTF-8 변환 필요: {file_path}", level=logging.WARNING) + + # 자주 사용되는 인코딩들로 시도 + encodings = ['cp949', 'euc-kr', 'latin1', 'cp1252'] + content = None + + for encoding in encodings: + try: + with open(file_path, 'r', encoding=encoding) as f: + content = f.read() + if self.logger: + self.logger.log(f"문자 사전 파일 {encoding} 인코딩으로 읽기 성공", level=logging.INFO) + break + except UnicodeDecodeError: + continue + + if content is None: + if self.logger: + self.logger.log(f"문자 사전 파일 읽기 실패, 원본 경로 반환: {file_path}", level=logging.ERROR) + return file_path + + # UTF-8로 저장된 새 파일 생성 + utf8_file_path = file_path.replace('.txt', '_utf8.txt') + with open(utf8_file_path, 'w', encoding='utf-8') as f: + f.write(content) + + if self.logger: + self.logger.log(f"문자 사전 파일 UTF-8 변환 완료: {utf8_file_path}", level=logging.INFO) + return utf8_file_path + + except Exception as e: + if self.logger: + self.logger.log(f"문자 사전 파일 인코딩 처리 중 오류: {e}", level=logging.ERROR) + return file_path + + def _get_fallback_model_type(self): + """기본 폴백 모델 타입을 결정합니다.""" + if self.use_gpu: + return "opt" # GPU 사용 시 기본값은 opt (안정성 우선) + else: + return "simp" # CPU 사용 시 simp (호환성 우선) + + def _initialize_onnx_system(self): + """predict_system.py의 TextSystem을 ONNX 모드로 초기화""" + try: + if self.logger: + gpu_status = "DirectML" if self.use_gpu else "CPU" + self.logger.log(f"🚀 ONNX TextSystem 초기화 시작 ({gpu_status} 모드)", level=logging.DEBUG) + # predict_system에서 필요한 모듈들 import + from tools.infer.predict_system import TextSystem + from ppocr.utils.utility import get_image_file_list, check_and_read + + # Args 객체 생성 (ONNX 설정 포함) + class Args: + def __init__(self, onnx_module_dir, use_gpu_flag, gpu_manager_instance=None, model_type="opt"): + # ONNX 관련 설정 + self.use_onnx = True + self.use_gpu = use_gpu_flag + + # 모델 경로 설정 (ONNX 모듈 폴더 기준) - 동적 모델 타입 사용 + models_dir = os.path.join(onnx_module_dir, "models") + + # 폴백 순서: 선택 모델 → opt → simp → fp16 + fallback_order = [model_type] + if model_type != "opt": + fallback_order.append("opt") + if model_type != "simp": + fallback_order.append("simp") + if model_type != "fp16": + fallback_order.append("fp16") + + # 모델 파일 존재 여부 확인 및 폴백 + selected_model_type = None + for fallback_type in fallback_order: + det_path = os.path.join(models_dir, f"det_dyn.{fallback_type}.onnx") + rec_path = os.path.join(models_dir, f"rec_dyn.{fallback_type}.onnx") + cls_path = os.path.join(models_dir, f"cls_dyn.{fallback_type}.onnx") + + if os.path.exists(det_path) and os.path.exists(rec_path) and os.path.exists(cls_path): + selected_model_type = fallback_type + print(f"[OCR] 모델 파일 발견: {fallback_type} 타입") + break + else: + print(f"[OCR] 모델 파일 누락: {fallback_type} 타입") + + if selected_model_type is None: + raise FileNotFoundError(f"ONNX 모델 파일을 찾을 수 없습니다. 확인 경로: {models_dir}") + + # 최종 선택된 모델 타입으로 경로 설정 + self.det_model_dir = os.path.join(models_dir, f"det_dyn.{selected_model_type}.onnx") + self.rec_model_dir = os.path.join(models_dir, f"rec_dyn.{selected_model_type}.onnx") + self.cls_model_dir = os.path.join(models_dir, f"cls_dyn.{selected_model_type}.onnx") + + if selected_model_type != model_type: + print(f"[OCR] 모델 폴백: {model_type} → {selected_model_type}") + + # 문자 사전 + self.rec_char_dict_path = os.path.join(onnx_module_dir, "dict", "ppocr_keys_v1.txt") + + # 기본 OCR 설정 + self.use_angle_cls = True + self.det_algorithm = 'DB' + self.rec_algorithm = 'SVTR_LCNet' + self.cls_algorithm = 'ClsMV3' + + # 성능 최적화 설정 + self.rec_batch_num = 16 # 배치 크기 증가: 6→16 (텍스트 많은 이미지 처리 속도 개선) + self.max_text_length = 25 + self.rec_image_shape = "3, 48, 320" + self.drop_score = 0.5 + + # 기타 설정 + self.show_log = False + self.use_space_char = True + self.save_crop_res = False + self.crop_res_save_dir = "./output" + + # Detection 관련 + self.det_limit_side_len = 960 + self.det_limit_type = 'max' + self.det_db_thresh = 0.3 + self.det_db_box_thresh = 0.6 + self.det_db_unclip_ratio = 1.5 + self.det_box_type = 'quad' + self.use_dilation = False + self.det_db_score_mode = 'fast' + + # Classification 관련 + self.cls_image_shape = "3, 48, 192" + self.cls_batch_num = 6 + self.cls_thresh = 0.9 + self.label_list = ['0', '180'] + + # 추가 Detection 알고리즘 관련 + self.det_east_score_thresh = 0.8 + self.det_east_cover_thresh = 0.1 + self.det_east_nms_thresh = 0.2 + self.det_sast_score_thresh = 0.5 + self.det_sast_nms_thresh = 0.2 + self.det_pse_thresh = 0 + self.det_pse_box_thresh = 0.85 + self.det_pse_min_area = 16 + self.det_pse_scale = 1 + self.scales = [8, 16, 32] + self.alpha = 1.0 + self.beta = 1.0 + self.fourier_degree = 5 + + # Recognition 관련 + self.rec_image_inverse = True + + # 시스템 관련 + self.max_batch_size = 10 + self.precision = 'fp32' + self.gpu_mem = 500 + self.gpu_id = 0 + self.ir_optim = True + self.use_tensorrt = False + self.min_subgraph_size = 15 + self.use_xpu = False + self.use_npu = False + + # 기타 + self.enable_mkldnn = False + self.cpu_threads = 10 + self.warmup = False + self.benchmark = False + self.vis_font_path = "./simfang.ttf" + + # ONNX 세션 옵션 - VM 환경 안정성 최적화 + import onnxruntime as ort + sess_options = ort.SessionOptions() + + # VM 환경: 스레드 수 제한으로 컨텍스트 스위칭 오버헤드 방지 + sess_options.intra_op_num_threads = 2 # 연산 내부 병렬성 제한 + sess_options.inter_op_num_threads = 1 # 연산 간 병렬성 비활성화 + + # 순차 실행 모드 (데드락 방지) + sess_options.execution_mode = ort.ExecutionMode.ORT_SEQUENTIAL + + # 최적화 레벨 낮춤 (무한루프 방지) + sess_options.graph_optimization_level = ort.GraphOptimizationLevel.ORT_ENABLE_BASIC + + # 메모리 최적화 + sess_options.enable_cpu_mem_arena = True + sess_options.enable_mem_pattern = True + + self.onnx_sess_options = sess_options + + # SessionOptions 적용 확인 로그 + if hasattr(self, 'logger_instance') and self.logger_instance: + self.logger_instance.log( + f"🔧 ONNX SessionOptions 설정 완료:\n" + f" - intra_op_threads: {sess_options.intra_op_num_threads}\n" + f" - inter_op_threads: {sess_options.inter_op_num_threads}\n" + f" - execution_mode: SEQUENTIAL\n" + f" - optimization_level: BASIC", + level=logging.INFO + ) + else: + print(f"[OCR] SessionOptions 설정: threads={sess_options.intra_op_num_threads}, sequential mode") + + # 추가 필수 속성들 (predict_system.py에서 사용) + self.return_word_box = False + self.det_box_type = "quad" + self.image_dir = "" + self.process_id = 0 + self.total_process_num = 1 + self.draw_img_save_dir = "./output" + self.page_num = 0 + self.use_mp = False + + # DirectML 기반 GPU 가속 설정 (Windows 호환성 최우선) + if use_gpu_flag: + try: + import onnxruntime as ort + available_providers = ort.get_available_providers() + print(f"[OCR] 사용 가능한 providers: {available_providers}") + + if 'DmlExecutionProvider' in available_providers: + self.onnx_providers = [("DmlExecutionProvider", {}), ("CPUExecutionProvider", {})] + print(f"[OCR] DirectML 가속 활성화: {self.onnx_providers}") + elif 'CUDAExecutionProvider' in available_providers: + self.onnx_providers = [("CUDAExecutionProvider", {}), ("CPUExecutionProvider", {})] + print(f"[OCR] CUDA 가속 활성화: {self.onnx_providers}") + else: + self.onnx_providers = [("CPUExecutionProvider", {})] + print(f"[OCR] DirectML 미지원, CPU 모드: {self.onnx_providers}") + except Exception as e: + print(f"[OCR] DirectML 설정 실패: {e}") + self.onnx_providers = [("CPUExecutionProvider", {})] + else: + self.onnx_providers = [("CPUExecutionProvider", {})] + print(f"[OCR] CPU 전용 모드: {self.onnx_providers}") + + # Args 객체 생성 시 파라미터로 전달 + args = Args(self.onnx_module_dir, self.use_gpu, self.gpu_manager, self.model_type) + + # 문자 사전 UTF-8 인코딩 확인 (Args 생성 후) + args.rec_char_dict_path = self._ensure_utf8_encoding(args.rec_char_dict_path) + + # TextSystem 초기화 + self.text_system = TextSystem(args) + + if self.logger: + gpu_status = "DirectML" if self.use_gpu else "CPU" + model_description = { + 'fp16': 'FP16 (고성능)', + 'opt': 'OPT (최적화)', + 'simp': 'SIMP (호환성)' + }.get(self.model_type, 'UNKNOWN') + self.logger.log(f"✅ ONNX TextSystem 초기화 완료 ({gpu_status} + {model_description} 모델)", level=logging.INFO) + + except Exception as e: + if self.logger: + self.logger.log(f"❌ ONNX TextSystem 초기화 실패: {e}", level=logging.ERROR, exc_info=True) + raise + + def _read_provider_cache(self): + try: + if not self._provider_cache_path or not os.path.exists(self._provider_cache_path): + return None + import json + with open(self._provider_cache_path, 'r', encoding='utf-8') as f: + data = json.load(f) + prov = (data or {}).get('last_success_provider', '').lower() + return prov if prov in ('dml', 'cpu') else None + except Exception: + return None + + def _write_provider_cache(self, provider: str): + try: + if not self._provider_cache_path: + return + import json + os.makedirs(os.path.dirname(self._provider_cache_path), exist_ok=True) + with open(self._provider_cache_path, 'w', encoding='utf-8') as f: + json.dump({"last_success_provider": provider}, f, ensure_ascii=False) + except Exception: + pass + + def _reinitialize_text_system(self): + """ + 타임아웃이나 오류 발생 시 text_system 재생성 + 전체 ImageWorker 재시작보다 빠르고 효율적 + """ + if self.logger: + self.logger.log("🔄 text_system 재생성 시작...", level=logging.INFO) + + # 기존 text_system 정리 + old_system = self.text_system + self.text_system = None + + try: + # 명시적 메모리 정리 + if old_system is not None: + del old_system + + import gc + gc.collect() + + # 새 text_system 생성 (기존 설정 재사용) + self._initialize_onnx_system() + + if self.logger: + self.logger.log("✅ text_system 재생성 완료", level=logging.INFO) + + except Exception as e: + if self.logger: + self.logger.log(f"❌ text_system 재생성 실패: {e}", level=logging.ERROR, exc_info=True) + raise + + def detect_text(self, image_path: str, method: str = 'polygon', raise_on_memory_error: bool = False) -> List[Dict[str, Any]]: + """ + 이미지에서 텍스트를 감지하고 다양한 방식으로 영역 반환 + 기존 OCRModule과 완전히 동일한 인터페이스 + + Args: + image_path (str): 이미지 파일 경로 + method (str): 감지 방식 ('polygon', 'bbox', 'expanded_bbox', 'rotated_bbox', 'contour') + raise_on_memory_error (bool): 메모리 에러 시 예외 발생 여부 + + Returns: + List[Dict]: 감지된 텍스트 정보 리스트 + - text: 감지된 텍스트 + - confidence: 신뢰도 + - polygon: 폴리곤 좌표 (4개 점) + - bbox: 바운딩 박스 좌표 (x, y, w, h) + - method: 사용된 감지 방식 + """ + if not os.path.exists(image_path): + if self.logger: + self.logger.log(f"이미지 파일을 찾을 수 없습니다: {image_path}", level=logging.ERROR) + return [] + + image = None + orig_image = None + try: + # 이미지 읽기 + image = cv2.imread(image_path) + if image is None: + if self.logger: + self.logger.log(f"이미지를 읽을 수 없습니다: {image_path}", level=logging.ERROR) + return [] + + if self.logger: + self.logger.log(f"🔍 ONNX OCR 감지 방식: {method}", level=logging.INFO) + + # 메모리 안전을 위한 이미지 크기 조정 (기존과 동일) + # 원본 크기 보존 후, 다운스케일 적용 + orig_image = image.copy() + orig_h, orig_w = image.shape[:2] + image = self._safe_resize_image(image) + resized_h, resized_w = image.shape[:2] + # 좌표 역스케일 계수 (리사이즈된 결과 → 원본 좌표계) + scale_x = float(orig_w) / float(resized_w) if resized_w > 0 else 1.0 + scale_y = float(orig_h) / float(resized_h) if resized_h > 0 else 1.0 + + # ONNX TextSystem으로 OCR 실행 (타임아웃 30초) + start_time = time.time() + + # VM 환경 안정성: 타임아웃 적용 + from concurrent.futures import ThreadPoolExecutor, TimeoutError as FutureTimeoutError + try: + with ThreadPoolExecutor(max_workers=1) as executor: + future = executor.submit(self.text_system, image) + dt_boxes, rec_res, time_dict = future.result(timeout=30) # 30초 타임아웃 + except FutureTimeoutError: + # 연속 타임아웃 횟수 증가 + self._consecutive_timeout_count += 1 + + if self.logger: + self.logger.log( + f"⚠️ ONNX OCR 추론 타임아웃 (30초 초과): {image_path} " + f"[연속 {self._consecutive_timeout_count}/{self._max_consecutive_timeouts}회]", + level=logging.WARNING + ) + + # 연속 타임아웃 3회 발생 시 ImageWorker 재시작 필요 + if self._consecutive_timeout_count >= self._max_consecutive_timeouts: + if self.logger: + self.logger.log( + f"❌ 연속 {self._consecutive_timeout_count}회 타임아웃 발생 → ImageWorker 재시작 필요", + level=logging.ERROR + ) + # MemoryError 발생시켜 ImageWorker 재시작 트리거 + raise MemoryError(f"연속 {self._consecutive_timeout_count}회 OCR 타임아웃 발생 - 워커 재시작 필요") + + # 3회 미만: text_system 재생성으로 복구 시도 + if self.logger: + self.logger.log(f"🔄 OCR 모듈 상태 오염 감지 → text_system 재생성 시도", level=logging.WARNING) + + try: + self._reinitialize_text_system() + if self.logger: + self.logger.log(f"✅ text_system 재생성 완료 - 다음 이미지는 정상 처리 가능", level=logging.INFO) + except Exception as reinit_error: + if self.logger: + self.logger.log(f"❌ text_system 재생성 실패: {reinit_error} - ImageWorker 재시작 권장", level=logging.ERROR) + # 재생성 실패 시에도 ImageWorker 재시작 필요 + raise MemoryError(f"text_system 재생성 실패 - 워커 재시작 필요: {reinit_error}") + + return [] + except Exception as e: + if self.logger: + self.logger.log(f"❌ ONNX OCR 추론 중 오류: {e}", level=logging.ERROR) + return [] + + inference_time = (time.time() - start_time) * 1000 + + # 성공 시 타임아웃 카운터 리셋 + self._consecutive_timeout_count = 0 + + if self.logger: + self.logger.log(f"⚡ ONNX OCR 추론 완료: {inference_time:.1f}ms", level=logging.INFO) + self.logger.log(f"📊 세부 시간 - 감지: {time_dict.get('det', 0)*1000:.1f}ms, 인식: {time_dict.get('rec', 0)*1000:.1f}ms, 분류: {time_dict.get('cls', 0)*1000:.1f}ms", level=logging.INFO) + + if not dt_boxes or not rec_res: + if self.logger: + self.logger.log("⚠️ ONNX OCR 결과가 비어있습니다", level=logging.WARNING) + return [] + + # predict_system 결과를 기존 format으로 변환 + converted_results = [] + for box, (text, confidence) in zip(dt_boxes, rec_res): + # dt_boxes는 리사이즈된 이미지 좌표계 기준 → 원본 좌표계로 복원 + try: + if abs(scale_x - 1.0) > 1e-3 or abs(scale_y - 1.0) > 1e-3: + rescaled = [[float(x) * scale_x, float(y) * scale_y] for x, y in box] + else: + rescaled = box.tolist() if hasattr(box, 'tolist') else box + except Exception: + # 좌표 형식 예외 시 안전 폴백 + rescaled = box.tolist() if hasattr(box, 'tolist') else box + converted_results.append([rescaled, [text, confidence]]) + + # 감지 방식에 따라 결과 처리 (기존 로직 재사용) + # 감지 로직은 원본 해상도 기준으로 수행해야 bbox 계산이 일치 + image_for_detection = orig_image if orig_image is not None else image + if method == 'polygon': + ocr_results = self._detect_with_polygon(image_for_detection, converted_results) + elif method == 'bbox': + ocr_results = self._detect_with_bbox(image_for_detection, converted_results) + elif method == 'expanded_bbox': + ocr_results = self._detect_with_expanded_bbox(image_for_detection, converted_results) + elif method == 'rotated_bbox': + ocr_results = self._detect_with_rotated_bbox(image_for_detection, converted_results) + elif method == 'contour': + ocr_results = self._detect_with_contour(image_for_detection, converted_results) + else: + if self.logger: + self.logger.log(f"⚠️ 지원하지 않는 감지 방식: {method}, 기본 polygon 방식 사용", level=logging.WARNING) + ocr_results = self._detect_with_polygon(image_for_detection, converted_results) + + return ocr_results + + except Exception as e: + if self.logger: + self.logger.log(f"ONNX OCR 처리 중 오류: {e}", level=logging.ERROR, exc_info=True) + if raise_on_memory_error and ('memory' in str(e).lower() or 'primitive' in str(e).lower()): + raise MemoryError(f"ONNX OCR memory error: {e}") + return [] + finally: + if image is not None: + del image + if orig_image is not None: + del orig_image + try: + cv2.destroyAllWindows() + except Exception: + pass + + def _safe_resize_image(self, image: np.ndarray) -> np.ndarray: + """메모리 안전을 위한 이미지 크기 조정 (기존 로직과 동일)""" + h, w = image.shape[:2] + max_dim_safe = 2000 + aspect_ratio = max(w, h) / max(1, min(w, h)) + + if max(w, h) > max_dim_safe or aspect_ratio > 15: + scale = float(max_dim_safe) / float(max(w, h)) + new_size = (int(w * scale), int(h * scale)) + if self.logger: + self.logger.log( + f"⚖️ ONNX OCR용 다운스케일 ({w}x{h}) -> {new_size} (ratio={aspect_ratio:.1f})", + level=logging.INFO, + ) + image = cv2.resize(image, new_size, interpolation=cv2.INTER_AREA).copy() + + return image + + # === 기존 OCRModule의 감지 방식 메서드들 (완전히 동일) === + + def _detect_with_polygon(self, image: np.ndarray, ocr_raw_results: List) -> List[Dict[str, Any]]: + """폴리곤 방식으로 텍스트 영역 감지 (기본 방식)""" + ocr_results = [] + + for line in ocr_raw_results: + if len(line) >= 2: + polygon = line[0] # 폴리곤 좌표 (4개 점) + text_info = line[1] # (텍스트, 신뢰도) + + if len(text_info) >= 2: + text = text_info[0] + confidence = text_info[1] + + # 폴리곤을 바운딩 박스로 변환 + polygon_np = np.array(polygon, dtype=np.int32) + x, y, w, h = cv2.boundingRect(polygon_np) + + ocr_result = { + 'text': text, + 'confidence': confidence, + 'polygon': polygon, + 'bbox': (x, y, w, h), + 'method': 'polygon' + } + ocr_results.append(ocr_result) + + return ocr_results + + def _detect_with_bbox(self, image: np.ndarray, ocr_raw_results: List) -> List[Dict[str, Any]]: + """바운딩 박스 방식으로 텍스트 영역 감지""" + ocr_results = [] + + for line in ocr_raw_results: + if len(line) >= 2: + polygon = line[0] + text_info = line[1] + + if len(text_info) >= 2: + text = text_info[0] + confidence = text_info[1] + + # 바운딩 박스 계산 + polygon_np = np.array(polygon, dtype=np.int32) + x, y, w, h = cv2.boundingRect(polygon_np) + + # 바운딩 박스를 폴리곤으로 변환 + bbox_polygon = [ + [x, y], + [x + w, y], + [x + w, y + h], + [x, y + h] + ] + + ocr_result = { + 'text': text, + 'confidence': confidence, + 'polygon': bbox_polygon, + 'bbox': (x, y, w, h), + 'method': 'bbox' + } + ocr_results.append(ocr_result) + + return ocr_results + + def _detect_with_expanded_bbox(self, image: np.ndarray, ocr_raw_results: List) -> List[Dict[str, Any]]: + """확장된 바운딩 박스 방식으로 텍스트 영역 감지""" + ocr_results = [] + h_img, w_img = image.shape[:2] + + for line in ocr_raw_results: + if len(line) >= 2: + polygon = line[0] + text_info = line[1] + + if len(text_info) >= 2: + text = text_info[0] + confidence = text_info[1] + + # 기본 바운딩 박스 + polygon_np = np.array(polygon, dtype=np.int32) + x, y, w, h = cv2.boundingRect(polygon_np) + + # 확장 크기 계산 (텍스트 크기의 20%) + expand_x = max(1, int(w * 0.2)) + expand_y = max(1, int(h * 0.2)) + + # 확장된 바운딩 박스 + x_exp = max(0, x - expand_x) + y_exp = max(0, y - expand_y) + w_exp = min(w_img - x_exp, w + 2 * expand_x) + h_exp = min(h_img - y_exp, h + 2 * expand_y) + + # 확장된 바운딩 박스를 폴리곤으로 변환 + expanded_polygon = [ + [x_exp, y_exp], + [x_exp + w_exp, y_exp], + [x_exp + w_exp, y_exp + h_exp], + [x_exp, y_exp + h_exp] + ] + + ocr_result = { + 'text': text, + 'confidence': confidence, + 'polygon': expanded_polygon, + 'bbox': (x_exp, y_exp, w_exp, h_exp), + 'method': 'expanded_bbox' + } + ocr_results.append(ocr_result) + + return ocr_results + + def _detect_with_rotated_bbox(self, image: np.ndarray, ocr_raw_results: List) -> List[Dict[str, Any]]: + """회전된 바운딩 박스 방식으로 텍스트 영역 감지""" + ocr_results = [] + + for line in ocr_raw_results: + if len(line) >= 2: + polygon = line[0] + text_info = line[1] + + if len(text_info) >= 2: + text = text_info[0] + confidence = text_info[1] + + # 회전된 바운딩 박스 계산 + polygon_np = np.array(polygon, dtype=np.float32) + rect = cv2.minAreaRect(polygon_np) + box = cv2.boxPoints(rect) + box = np.int32(box) + + # 일반 바운딩 박스도 계산 + x, y, w, h = cv2.boundingRect(polygon_np.astype(np.int32)) + + ocr_result = { + 'text': text, + 'confidence': confidence, + 'polygon': box.tolist(), + 'bbox': (x, y, w, h), + 'method': 'rotated_bbox', + 'rotation_info': { + 'center': rect[0], + 'size': rect[1], + 'angle': rect[2] + } + } + ocr_results.append(ocr_result) + + return ocr_results + + def _detect_with_contour(self, image: np.ndarray, ocr_raw_results: List) -> List[Dict[str, Any]]: + """컨투어 방식으로 텍스트 영역 감지""" + ocr_results = [] + + for line in ocr_raw_results: + if len(line) >= 2: + polygon = line[0] + text_info = line[1] + + if len(text_info) >= 2: + text = text_info[0] + confidence = text_info[1] + + # 폴리곤을 컨투어로 변환 + polygon_np = np.array(polygon, dtype=np.int32) + + # 컨투어 근사화 + epsilon = 0.02 * cv2.arcLength(polygon_np, True) + approx_contour = cv2.approxPolyDP(polygon_np, epsilon, True) + + # 컨투어를 다시 폴리곤으로 변환 + contour_polygon = approx_contour.reshape(-1, 2).tolist() + + # 바운딩 박스 계산 + x, y, w, h = cv2.boundingRect(polygon_np) + + ocr_result = { + 'text': text, + 'confidence': confidence, + 'polygon': contour_polygon, + 'bbox': (x, y, w, h), + 'method': 'contour', + 'contour_points': len(contour_polygon) + } + ocr_results.append(ocr_result) + + return ocr_results + + # === 기존 OCRModule의 필터링 메서드들 (완전히 동일) === + + def filter_chinese_text(self, ocr_results: List[Dict]) -> List[Dict]: + """ + 중국어 텍스트만 필터링 + + Args: + ocr_results (List[Dict]): OCR 결과 + + Returns: + List[Dict]: 중국어 텍스트만 포함된 결과 + """ + chinese_results = [] + + for result in ocr_results: + text = result['text'] + # 중국어 문자 범위 확인 (간체/번체 포함) + if any('\u4e00' <= char <= '\u9fff' for char in text): + chinese_results.append(result) + + if self.logger: + self.logger.log(f"중국어 텍스트 {len(chinese_results)}개 필터링 완료", level=logging.INFO) + return chinese_results + + def filter_korean_text(self, ocr_results: List[Dict]) -> List[Dict]: + """ + 한글 텍스트만 필터링 + + Args: + ocr_results (List[Dict]): OCR 결과 + + Returns: + List[Dict]: 한글 텍스트만 포함된 결과 + """ + korean_results = [] + for result in ocr_results: + text = result['text'] + # 한글 유니코드 범위: 가~힣 + if any('\uac00' <= char <= '\ud7a3' for char in text): + korean_results.append(result) + + if self.logger: + self.logger.log(f"한글 텍스트 {len(korean_results)}개 필터링 완료", level=logging.INFO) + return korean_results + + +# 기존 OCRModule과의 호환성을 위한 별칭 +OCRModule = ONNXOCRModule diff --git a/modules/onnx_ocr_module/src/ppocr/__init__.py b/modules/onnx_ocr_module/src/ppocr/__init__.py new file mode 100644 index 0000000..d0c32e2 --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/__init__.py @@ -0,0 +1,13 @@ +# Copyright (c) 2019 PaddlePaddle Authors. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. diff --git a/modules/onnx_ocr_module/src/ppocr/__pycache__/__init__.cpython-311.pyc b/modules/onnx_ocr_module/src/ppocr/__pycache__/__init__.cpython-311.pyc new file mode 100644 index 0000000..714e15e Binary files /dev/null and b/modules/onnx_ocr_module/src/ppocr/__pycache__/__init__.cpython-311.pyc differ diff --git a/modules/onnx_ocr_module/src/ppocr/data/__init__.py b/modules/onnx_ocr_module/src/ppocr/data/__init__.py new file mode 100644 index 0000000..5678aeb --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/data/__init__.py @@ -0,0 +1,153 @@ +# Copyright (c) 2020 PaddlePaddle Authors. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function +from __future__ import unicode_literals + +import os +import sys +import numpy as np +import skimage +import paddle +import signal +import random + +__dir__ = os.path.dirname(os.path.abspath(__file__)) +sys.path.append(os.path.abspath(os.path.join(__dir__, "../.."))) + +import copy +from paddle.io import Dataset, DataLoader, BatchSampler, DistributedBatchSampler +import paddle.distributed as dist + +from ppocr.data.imaug import transform, create_operators +from ppocr.data.simple_dataset import SimpleDataSet, MultiScaleDataSet +from ppocr.data.lmdb_dataset import LMDBDataSet, LMDBDataSetSR, LMDBDataSetTableMaster +from ppocr.data.pgnet_dataset import PGDataSet +from ppocr.data.pubtab_dataset import PubTabDataSet +from ppocr.data.multi_scale_sampler import MultiScaleSampler +from ppocr.data.latexocr_dataset import LaTeXOCRDataSet + +# for PaddleX dataset_type +TextDetDataset = SimpleDataSet +TextRecDataset = SimpleDataSet +MSTextRecDataset = MultiScaleDataSet +PubTabTableRecDataset = PubTabDataSet +KieDataset = SimpleDataSet +LaTeXOCRDataSet = LaTeXOCRDataSet + +__all__ = ["build_dataloader", "transform", "create_operators", "set_signal_handlers"] + + +def term_mp(sig_num, frame): + """kill all child processes""" + pid = os.getpid() + pgid = os.getpgid(os.getpid()) + print("main proc {} exit, kill process group " "{}".format(pid, pgid)) + os.killpg(pgid, signal.SIGKILL) + + +def set_signal_handlers(): + pid = os.getpid() + try: + pgid = os.getpgid(pid) + except AttributeError: + # In case `os.getpgid` is not available, no signal handler will be set, + # because we cannot do safe cleanup. + pass + else: + # XXX: `term_mp` kills all processes in the process group, which in + # some cases includes the parent process of current process and may + # cause unexpected results. To solve this problem, we set signal + # handlers only when current process is the group leader. In the + # future, it would be better to consider killing only descendants of + # the current process. + if pid == pgid: + # support exit using ctrl+c + signal.signal(signal.SIGINT, term_mp) + signal.signal(signal.SIGTERM, term_mp) + + +def build_dataloader(config, mode, device, logger, seed=None): + config = copy.deepcopy(config) + + support_dict = [ + "SimpleDataSet", + "LMDBDataSet", + "PGDataSet", + "PubTabDataSet", + "LMDBDataSetSR", + "LMDBDataSetTableMaster", + "MultiScaleDataSet", + "TextDetDataset", + "TextRecDataset", + "MSTextRecDataset", + "PubTabTableRecDataset", + "KieDataset", + "LaTeXOCRDataSet", + ] + module_name = config[mode]["dataset"]["name"] + assert module_name in support_dict, Exception( + "DataSet only support {}".format(support_dict) + ) + assert mode in ["Train", "Eval", "Test"], "Mode should be Train, Eval or Test." + + dataset = eval(module_name)(config, mode, logger, seed) + loader_config = config[mode]["loader"] + batch_size = loader_config["batch_size_per_card"] + drop_last = loader_config["drop_last"] + shuffle = loader_config["shuffle"] + num_workers = loader_config["num_workers"] + if "use_shared_memory" in loader_config.keys(): + use_shared_memory = loader_config["use_shared_memory"] + else: + use_shared_memory = True + + if mode == "Train": + # Distribute data to multiple cards + if "sampler" in config[mode]: + config_sampler = config[mode]["sampler"] + sampler_name = config_sampler.pop("name") + batch_sampler = eval(sampler_name)(dataset, **config_sampler) + else: + batch_sampler = DistributedBatchSampler( + dataset=dataset, + batch_size=batch_size, + shuffle=shuffle, + drop_last=drop_last, + ) + else: + # Distribute data to single card + batch_sampler = BatchSampler( + dataset=dataset, batch_size=batch_size, shuffle=shuffle, drop_last=drop_last + ) + + if "collate_fn" in loader_config: + from . import collate_fn + + collate_fn = getattr(collate_fn, loader_config["collate_fn"])() + else: + collate_fn = None + data_loader = DataLoader( + dataset=dataset, + batch_sampler=batch_sampler, + places=device, + num_workers=num_workers, + return_list=True, + use_shared_memory=use_shared_memory, + collate_fn=collate_fn, + ) + + return data_loader diff --git a/modules/onnx_ocr_module/src/ppocr/data/__pycache__/__init__.cpython-311.pyc b/modules/onnx_ocr_module/src/ppocr/data/__pycache__/__init__.cpython-311.pyc new file mode 100644 index 0000000..86a909b Binary files /dev/null and b/modules/onnx_ocr_module/src/ppocr/data/__pycache__/__init__.cpython-311.pyc differ diff --git a/modules/onnx_ocr_module/src/ppocr/data/__pycache__/latexocr_dataset.cpython-311.pyc b/modules/onnx_ocr_module/src/ppocr/data/__pycache__/latexocr_dataset.cpython-311.pyc new file mode 100644 index 0000000..4adc97c Binary files /dev/null and b/modules/onnx_ocr_module/src/ppocr/data/__pycache__/latexocr_dataset.cpython-311.pyc differ diff --git a/modules/onnx_ocr_module/src/ppocr/data/__pycache__/lmdb_dataset.cpython-311.pyc b/modules/onnx_ocr_module/src/ppocr/data/__pycache__/lmdb_dataset.cpython-311.pyc new file mode 100644 index 0000000..50b292a Binary files /dev/null and b/modules/onnx_ocr_module/src/ppocr/data/__pycache__/lmdb_dataset.cpython-311.pyc differ diff --git a/modules/onnx_ocr_module/src/ppocr/data/__pycache__/multi_scale_sampler.cpython-311.pyc b/modules/onnx_ocr_module/src/ppocr/data/__pycache__/multi_scale_sampler.cpython-311.pyc new file mode 100644 index 0000000..a6c081b Binary files /dev/null and b/modules/onnx_ocr_module/src/ppocr/data/__pycache__/multi_scale_sampler.cpython-311.pyc differ diff --git a/modules/onnx_ocr_module/src/ppocr/data/__pycache__/pgnet_dataset.cpython-311.pyc b/modules/onnx_ocr_module/src/ppocr/data/__pycache__/pgnet_dataset.cpython-311.pyc new file mode 100644 index 0000000..46b1308 Binary files /dev/null and b/modules/onnx_ocr_module/src/ppocr/data/__pycache__/pgnet_dataset.cpython-311.pyc differ diff --git a/modules/onnx_ocr_module/src/ppocr/data/__pycache__/pubtab_dataset.cpython-311.pyc b/modules/onnx_ocr_module/src/ppocr/data/__pycache__/pubtab_dataset.cpython-311.pyc new file mode 100644 index 0000000..5ecea35 Binary files /dev/null and b/modules/onnx_ocr_module/src/ppocr/data/__pycache__/pubtab_dataset.cpython-311.pyc differ diff --git a/modules/onnx_ocr_module/src/ppocr/data/__pycache__/simple_dataset.cpython-311.pyc b/modules/onnx_ocr_module/src/ppocr/data/__pycache__/simple_dataset.cpython-311.pyc new file mode 100644 index 0000000..69cdb60 Binary files /dev/null and b/modules/onnx_ocr_module/src/ppocr/data/__pycache__/simple_dataset.cpython-311.pyc differ diff --git a/modules/onnx_ocr_module/src/ppocr/data/collate_fn.py b/modules/onnx_ocr_module/src/ppocr/data/collate_fn.py new file mode 100644 index 0000000..309f60a --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/data/collate_fn.py @@ -0,0 +1,173 @@ +# Copyright (c) 2021 PaddlePaddle Authors. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import paddle +import numbers +import numpy as np +from collections import defaultdict + + +class DictCollator(object): + """ + data batch + """ + + def __call__(self, batch): + # todo:support batch operators + data_dict = defaultdict(list) + to_tensor_keys = [] + for sample in batch: + for k, v in sample.items(): + if isinstance(v, (np.ndarray, paddle.Tensor, numbers.Number)): + if k not in to_tensor_keys: + to_tensor_keys.append(k) + data_dict[k].append(v) + for k in to_tensor_keys: + data_dict[k] = paddle.to_tensor(data_dict[k]) + return data_dict + + +class ListCollator(object): + """ + data batch + """ + + def __call__(self, batch): + # todo:support batch operators + data_dict = defaultdict(list) + to_tensor_idxs = [] + for sample in batch: + for idx, v in enumerate(sample): + if isinstance(v, (np.ndarray, paddle.Tensor, numbers.Number)): + if idx not in to_tensor_idxs: + to_tensor_idxs.append(idx) + data_dict[idx].append(v) + for idx in to_tensor_idxs: + data_dict[idx] = paddle.to_tensor(data_dict[idx]) + return list(data_dict.values()) + + +class SSLRotateCollate(object): + """ + bach: [ + [(4*3xH*W), (4,)] + [(4*3xH*W), (4,)] + ... + ] + """ + + def __call__(self, batch): + output = [np.concatenate(d, axis=0) for d in zip(*batch)] + return output + + +class DyMaskCollator(object): + """ + batch: [ + image [batch_size, channel, maxHinbatch, maxWinbatch] + image_mask [batch_size, channel, maxHinbatch, maxWinbatch] + label [batch_size, maxLabelLen] + label_mask [batch_size, maxLabelLen] + ... + ] + """ + + def __call__(self, batch): + max_width, max_height, max_length = 0, 0, 0 + bs, channel = len(batch), batch[0][0].shape[0] + proper_items = [] + for item in batch: + if ( + item[0].shape[1] * max_width > 1600 * 320 + or item[0].shape[2] * max_height > 1600 * 320 + ): + continue + max_height = ( + item[0].shape[1] if item[0].shape[1] > max_height else max_height + ) + max_width = item[0].shape[2] if item[0].shape[2] > max_width else max_width + max_length = len(item[1]) if len(item[1]) > max_length else max_length + proper_items.append(item) + + images, image_masks = np.zeros( + (len(proper_items), channel, max_height, max_width), dtype="float32" + ), np.zeros((len(proper_items), 1, max_height, max_width), dtype="float32") + labels, label_masks = np.zeros( + (len(proper_items), max_length), dtype="int64" + ), np.zeros((len(proper_items), max_length), dtype="int64") + + for i in range(len(proper_items)): + _, h, w = proper_items[i][0].shape + images[i][:, :h, :w] = proper_items[i][0] + image_masks[i][:, :h, :w] = 1 + l = len(proper_items[i][1]) + labels[i][:l] = proper_items[i][1] + label_masks[i][:l] = 1 + + return images, image_masks, labels, label_masks + + +class LaTeXOCRCollator(object): + """ + batch: [ + image [batch_size, channel, maxHinbatch, maxWinbatch] + label [batch_size, maxLabelLen] + label_mask [batch_size, maxLabelLen] + ... + ] + """ + + def __call__(self, batch): + images, labels, attention_mask = batch[0] + return images, labels, attention_mask + + +class UniMERNetCollator(object): + """ + batch: [ + image [batch_size, channel, maxHinbatch, maxWinbatch] + image_mask [batch_size, channel, maxHinbatch, maxWinbatch] + label [batch_size, maxLabelLen] + label_mask [batch_size, maxLabelLen] + ... + ] + """ + + def __call__(self, batch): + + max_width, max_height, max_length = 0, 0, 0 + bs, channel = len(batch), batch[0][0].shape[0] + proper_items = [] + for item in batch: + max_height = ( + item[0].shape[1] if item[0].shape[1] > max_height else max_height + ) + max_width = item[0].shape[2] if item[0].shape[2] > max_width else max_width + max_length = len(item[1]) if len(item[1]) > max_length else max_length + proper_items.append(item) + + images = np.ones( + (len(proper_items), channel, max_height, max_width), dtype="float32" + ) + + labels, label_masks = np.ones( + (len(proper_items), max_length), dtype="int64" + ), np.zeros((len(proper_items), max_length), dtype="int64") + for i in range(len(proper_items)): + _, h, w = proper_items[i][0].shape + images[i][:, :h, :w] = proper_items[i][0] + l = len(proper_items[i][1]) + labels[i][:l] = proper_items[i][1] + label_masks[i][:l] = proper_items[i][2] + return images, labels, label_masks diff --git a/modules/onnx_ocr_module/src/ppocr/data/imaug/ColorJitter.py b/modules/onnx_ocr_module/src/ppocr/data/imaug/ColorJitter.py new file mode 100644 index 0000000..46c1955 --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/data/imaug/ColorJitter.py @@ -0,0 +1,27 @@ +# copyright (c) 2020 PaddlePaddle Authors. All Rights Reserve. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +from paddle.vision.transforms import ColorJitter as pp_ColorJitter + +__all__ = ["ColorJitter"] + + +class ColorJitter(object): + def __init__(self, brightness=0, contrast=0, saturation=0, hue=0, **kwargs): + self.aug = pp_ColorJitter(brightness, contrast, saturation, hue) + + def __call__(self, data): + image = data["image"] + image = self.aug(image) + data["image"] = image + return data diff --git a/modules/onnx_ocr_module/src/ppocr/data/imaug/__init__.py b/modules/onnx_ocr_module/src/ppocr/data/imaug/__init__.py new file mode 100644 index 0000000..d47a515 --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/data/imaug/__init__.py @@ -0,0 +1,96 @@ +# copyright (c) 2020 PaddlePaddle Authors. All Rights Reserve. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function +from __future__ import unicode_literals + +from .iaa_augment import IaaAugment +from .make_border_map import MakeBorderMap +from .make_shrink_map import MakeShrinkMap +from .random_crop_data import EastRandomCropData, RandomCropImgMask +from .make_pse_gt import MakePseGt + + +from .rec_img_aug import ( + BaseDataAugmentation, + RecAug, + RecConAug, + RecResizeImg, + ClsResizeImg, + SRNRecResizeImg, + GrayRecResizeImg, + SARRecResizeImg, + PRENResizeImg, + ABINetRecResizeImg, + SVTRRecResizeImg, + ABINetRecAug, + VLRecResizeImg, + SPINRecResizeImg, + RobustScannerRecResizeImg, + RFLRecResizeImg, + SVTRRecAug, + ParseQRecAug, +) +from .ssl_img_aug import SSLRotateResize +from .randaugment import RandAugment +from .copy_paste import CopyPaste +from .ColorJitter import ColorJitter +from .operators import * +from .label_ops import * + +from .east_process import * +from .sast_process import * +from .pg_process import * +from .table_ops import * + +from .vqa import * + +from .fce_aug import * +from .fce_targets import FCENetTargets +from .ct_process import * +from .drrg_targets import DRRGTargets +from .latex_ocr_aug import * +from .unimernet_aug import * + + +def transform(data, ops=None): + """transform""" + if ops is None: + ops = [] + for op in ops: + data = op(data) + if data is None: + return None + return data + + +def create_operators(op_param_list, global_config=None): + """ + create operators based on the config + + Args: + params(list): a dict list, used to create some operators + """ + assert isinstance(op_param_list, list), "operator config should be a list" + ops = [] + for operator in op_param_list: + assert isinstance(operator, dict) and len(operator) == 1, "yaml format error" + op_name = list(operator)[0] + param = {} if operator[op_name] is None else operator[op_name] + if global_config is not None: + param.update(global_config) + op = eval(op_name)(**param) + ops.append(op) + return ops diff --git a/modules/onnx_ocr_module/src/ppocr/data/imaug/__pycache__/ColorJitter.cpython-311.pyc b/modules/onnx_ocr_module/src/ppocr/data/imaug/__pycache__/ColorJitter.cpython-311.pyc new file mode 100644 index 0000000..ca4a772 Binary files /dev/null and b/modules/onnx_ocr_module/src/ppocr/data/imaug/__pycache__/ColorJitter.cpython-311.pyc differ diff --git a/modules/onnx_ocr_module/src/ppocr/data/imaug/__pycache__/__init__.cpython-311.pyc b/modules/onnx_ocr_module/src/ppocr/data/imaug/__pycache__/__init__.cpython-311.pyc new file mode 100644 index 0000000..953a0fa Binary files /dev/null and b/modules/onnx_ocr_module/src/ppocr/data/imaug/__pycache__/__init__.cpython-311.pyc differ diff --git a/modules/onnx_ocr_module/src/ppocr/data/imaug/__pycache__/abinet_aug.cpython-311.pyc b/modules/onnx_ocr_module/src/ppocr/data/imaug/__pycache__/abinet_aug.cpython-311.pyc new file mode 100644 index 0000000..447751c Binary files /dev/null and b/modules/onnx_ocr_module/src/ppocr/data/imaug/__pycache__/abinet_aug.cpython-311.pyc differ diff --git a/modules/onnx_ocr_module/src/ppocr/data/imaug/__pycache__/copy_paste.cpython-311.pyc b/modules/onnx_ocr_module/src/ppocr/data/imaug/__pycache__/copy_paste.cpython-311.pyc new file mode 100644 index 0000000..844a5af Binary files /dev/null and b/modules/onnx_ocr_module/src/ppocr/data/imaug/__pycache__/copy_paste.cpython-311.pyc differ diff --git a/modules/onnx_ocr_module/src/ppocr/data/imaug/__pycache__/ct_process.cpython-311.pyc b/modules/onnx_ocr_module/src/ppocr/data/imaug/__pycache__/ct_process.cpython-311.pyc new file mode 100644 index 0000000..34df66f Binary files /dev/null and b/modules/onnx_ocr_module/src/ppocr/data/imaug/__pycache__/ct_process.cpython-311.pyc differ diff --git a/modules/onnx_ocr_module/src/ppocr/data/imaug/__pycache__/drrg_targets.cpython-311.pyc b/modules/onnx_ocr_module/src/ppocr/data/imaug/__pycache__/drrg_targets.cpython-311.pyc new file mode 100644 index 0000000..e982d8e Binary files /dev/null and b/modules/onnx_ocr_module/src/ppocr/data/imaug/__pycache__/drrg_targets.cpython-311.pyc differ diff --git a/modules/onnx_ocr_module/src/ppocr/data/imaug/__pycache__/east_process.cpython-311.pyc b/modules/onnx_ocr_module/src/ppocr/data/imaug/__pycache__/east_process.cpython-311.pyc new file mode 100644 index 0000000..91c5555 Binary files /dev/null and b/modules/onnx_ocr_module/src/ppocr/data/imaug/__pycache__/east_process.cpython-311.pyc differ diff --git a/modules/onnx_ocr_module/src/ppocr/data/imaug/__pycache__/fce_aug.cpython-311.pyc b/modules/onnx_ocr_module/src/ppocr/data/imaug/__pycache__/fce_aug.cpython-311.pyc new file mode 100644 index 0000000..912a148 Binary files /dev/null and b/modules/onnx_ocr_module/src/ppocr/data/imaug/__pycache__/fce_aug.cpython-311.pyc differ diff --git a/modules/onnx_ocr_module/src/ppocr/data/imaug/__pycache__/fce_targets.cpython-311.pyc b/modules/onnx_ocr_module/src/ppocr/data/imaug/__pycache__/fce_targets.cpython-311.pyc new file mode 100644 index 0000000..496d843 Binary files /dev/null and b/modules/onnx_ocr_module/src/ppocr/data/imaug/__pycache__/fce_targets.cpython-311.pyc differ diff --git a/modules/onnx_ocr_module/src/ppocr/data/imaug/__pycache__/iaa_augment.cpython-311.pyc b/modules/onnx_ocr_module/src/ppocr/data/imaug/__pycache__/iaa_augment.cpython-311.pyc new file mode 100644 index 0000000..345d014 Binary files /dev/null and b/modules/onnx_ocr_module/src/ppocr/data/imaug/__pycache__/iaa_augment.cpython-311.pyc differ diff --git a/modules/onnx_ocr_module/src/ppocr/data/imaug/__pycache__/label_ops.cpython-311.pyc b/modules/onnx_ocr_module/src/ppocr/data/imaug/__pycache__/label_ops.cpython-311.pyc new file mode 100644 index 0000000..f3a9362 Binary files /dev/null and b/modules/onnx_ocr_module/src/ppocr/data/imaug/__pycache__/label_ops.cpython-311.pyc differ diff --git a/modules/onnx_ocr_module/src/ppocr/data/imaug/__pycache__/latex_ocr_aug.cpython-311.pyc b/modules/onnx_ocr_module/src/ppocr/data/imaug/__pycache__/latex_ocr_aug.cpython-311.pyc new file mode 100644 index 0000000..7e504c7 Binary files /dev/null and b/modules/onnx_ocr_module/src/ppocr/data/imaug/__pycache__/latex_ocr_aug.cpython-311.pyc differ diff --git a/modules/onnx_ocr_module/src/ppocr/data/imaug/__pycache__/make_border_map.cpython-311.pyc b/modules/onnx_ocr_module/src/ppocr/data/imaug/__pycache__/make_border_map.cpython-311.pyc new file mode 100644 index 0000000..9dffb4d Binary files /dev/null and b/modules/onnx_ocr_module/src/ppocr/data/imaug/__pycache__/make_border_map.cpython-311.pyc differ diff --git a/modules/onnx_ocr_module/src/ppocr/data/imaug/__pycache__/make_pse_gt.cpython-311.pyc b/modules/onnx_ocr_module/src/ppocr/data/imaug/__pycache__/make_pse_gt.cpython-311.pyc new file mode 100644 index 0000000..339badf Binary files /dev/null and b/modules/onnx_ocr_module/src/ppocr/data/imaug/__pycache__/make_pse_gt.cpython-311.pyc differ diff --git a/modules/onnx_ocr_module/src/ppocr/data/imaug/__pycache__/make_shrink_map.cpython-311.pyc b/modules/onnx_ocr_module/src/ppocr/data/imaug/__pycache__/make_shrink_map.cpython-311.pyc new file mode 100644 index 0000000..74dc9cd Binary files /dev/null and b/modules/onnx_ocr_module/src/ppocr/data/imaug/__pycache__/make_shrink_map.cpython-311.pyc differ diff --git a/modules/onnx_ocr_module/src/ppocr/data/imaug/__pycache__/operators.cpython-311.pyc b/modules/onnx_ocr_module/src/ppocr/data/imaug/__pycache__/operators.cpython-311.pyc new file mode 100644 index 0000000..b7279b6 Binary files /dev/null and b/modules/onnx_ocr_module/src/ppocr/data/imaug/__pycache__/operators.cpython-311.pyc differ diff --git a/modules/onnx_ocr_module/src/ppocr/data/imaug/__pycache__/pg_process.cpython-311.pyc b/modules/onnx_ocr_module/src/ppocr/data/imaug/__pycache__/pg_process.cpython-311.pyc new file mode 100644 index 0000000..a3ae7a8 Binary files /dev/null and b/modules/onnx_ocr_module/src/ppocr/data/imaug/__pycache__/pg_process.cpython-311.pyc differ diff --git a/modules/onnx_ocr_module/src/ppocr/data/imaug/__pycache__/randaugment.cpython-311.pyc b/modules/onnx_ocr_module/src/ppocr/data/imaug/__pycache__/randaugment.cpython-311.pyc new file mode 100644 index 0000000..988a1a0 Binary files /dev/null and b/modules/onnx_ocr_module/src/ppocr/data/imaug/__pycache__/randaugment.cpython-311.pyc differ diff --git a/modules/onnx_ocr_module/src/ppocr/data/imaug/__pycache__/random_crop_data.cpython-311.pyc b/modules/onnx_ocr_module/src/ppocr/data/imaug/__pycache__/random_crop_data.cpython-311.pyc new file mode 100644 index 0000000..a2225d7 Binary files /dev/null and b/modules/onnx_ocr_module/src/ppocr/data/imaug/__pycache__/random_crop_data.cpython-311.pyc differ diff --git a/modules/onnx_ocr_module/src/ppocr/data/imaug/__pycache__/rec_img_aug.cpython-311.pyc b/modules/onnx_ocr_module/src/ppocr/data/imaug/__pycache__/rec_img_aug.cpython-311.pyc new file mode 100644 index 0000000..ddd3c96 Binary files /dev/null and b/modules/onnx_ocr_module/src/ppocr/data/imaug/__pycache__/rec_img_aug.cpython-311.pyc differ diff --git a/modules/onnx_ocr_module/src/ppocr/data/imaug/__pycache__/sast_process.cpython-311.pyc b/modules/onnx_ocr_module/src/ppocr/data/imaug/__pycache__/sast_process.cpython-311.pyc new file mode 100644 index 0000000..3120e63 Binary files /dev/null and b/modules/onnx_ocr_module/src/ppocr/data/imaug/__pycache__/sast_process.cpython-311.pyc differ diff --git a/modules/onnx_ocr_module/src/ppocr/data/imaug/__pycache__/ssl_img_aug.cpython-311.pyc b/modules/onnx_ocr_module/src/ppocr/data/imaug/__pycache__/ssl_img_aug.cpython-311.pyc new file mode 100644 index 0000000..96a86e6 Binary files /dev/null and b/modules/onnx_ocr_module/src/ppocr/data/imaug/__pycache__/ssl_img_aug.cpython-311.pyc differ diff --git a/modules/onnx_ocr_module/src/ppocr/data/imaug/__pycache__/table_ops.cpython-311.pyc b/modules/onnx_ocr_module/src/ppocr/data/imaug/__pycache__/table_ops.cpython-311.pyc new file mode 100644 index 0000000..63a218c Binary files /dev/null and b/modules/onnx_ocr_module/src/ppocr/data/imaug/__pycache__/table_ops.cpython-311.pyc differ diff --git a/modules/onnx_ocr_module/src/ppocr/data/imaug/__pycache__/unimernet_aug.cpython-311.pyc b/modules/onnx_ocr_module/src/ppocr/data/imaug/__pycache__/unimernet_aug.cpython-311.pyc new file mode 100644 index 0000000..38c2897 Binary files /dev/null and b/modules/onnx_ocr_module/src/ppocr/data/imaug/__pycache__/unimernet_aug.cpython-311.pyc differ diff --git a/modules/onnx_ocr_module/src/ppocr/data/imaug/abinet_aug.py b/modules/onnx_ocr_module/src/ppocr/data/imaug/abinet_aug.py new file mode 100644 index 0000000..3df255e --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/data/imaug/abinet_aug.py @@ -0,0 +1,512 @@ +# copyright (c) 2020 PaddlePaddle Authors. All Rights Reserve. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +""" +This code is refer from: +https://github.com/FangShancheng/ABINet/blob/main/transforms.py +""" +import math +import numbers +import random + +import cv2 +import numpy as np +from paddle.vision.transforms import Compose, ColorJitter + + +def sample_asym(magnitude, size=None): + return np.random.beta(1, 4, size) * magnitude + + +def sample_sym(magnitude, size=None): + return (np.random.beta(4, 4, size=size) - 0.5) * 2 * magnitude + + +def sample_uniform(low, high, size=None): + return np.random.uniform(low, high, size=size) + + +def get_interpolation(type="random"): + if type == "random": + choice = [cv2.INTER_NEAREST, cv2.INTER_LINEAR, cv2.INTER_CUBIC, cv2.INTER_AREA] + interpolation = choice[random.randint(0, len(choice) - 1)] + elif type == "nearest": + interpolation = cv2.INTER_NEAREST + elif type == "linear": + interpolation = cv2.INTER_LINEAR + elif type == "cubic": + interpolation = cv2.INTER_CUBIC + elif type == "area": + interpolation = cv2.INTER_AREA + else: + raise TypeError( + "Interpolation types only nearest, linear, cubic, area are supported!" + ) + return interpolation + + +class CVRandomRotation(object): + def __init__(self, degrees=15): + assert isinstance(degrees, numbers.Number), "degree should be a single number." + assert degrees >= 0, "degree must be positive." + self.degrees = degrees + + @staticmethod + def get_params(degrees): + return sample_sym(degrees) + + def __call__(self, img): + angle = self.get_params(self.degrees) + src_h, src_w = img.shape[:2] + M = cv2.getRotationMatrix2D( + center=(src_w / 2, src_h / 2), angle=angle, scale=1.0 + ) + abs_cos, abs_sin = abs(M[0, 0]), abs(M[0, 1]) + dst_w = int(src_h * abs_sin + src_w * abs_cos) + dst_h = int(src_h * abs_cos + src_w * abs_sin) + M[0, 2] += (dst_w - src_w) / 2 + M[1, 2] += (dst_h - src_h) / 2 + + flags = get_interpolation() + return cv2.warpAffine( + img, M, (dst_w, dst_h), flags=flags, borderMode=cv2.BORDER_REPLICATE + ) + + +class CVRandomAffine(object): + def __init__(self, degrees, translate=None, scale=None, shear=None): + assert isinstance(degrees, numbers.Number), "degree should be a single number." + assert degrees >= 0, "degree must be positive." + self.degrees = degrees + + if translate is not None: + assert ( + isinstance(translate, (tuple, list)) and len(translate) == 2 + ), "translate should be a list or tuple and it must be of length 2." + for t in translate: + if not (0.0 <= t <= 1.0): + raise ValueError("translation values should be between 0 and 1") + self.translate = translate + + if scale is not None: + assert ( + isinstance(scale, (tuple, list)) and len(scale) == 2 + ), "scale should be a list or tuple and it must be of length 2." + for s in scale: + if s <= 0: + raise ValueError("scale values should be positive") + self.scale = scale + + if shear is not None: + if isinstance(shear, numbers.Number): + if shear < 0: + raise ValueError( + "If shear is a single number, it must be positive." + ) + self.shear = [shear] + else: + assert isinstance(shear, (tuple, list)) and ( + len(shear) == 2 + ), "shear should be a list or tuple and it must be of length 2." + self.shear = shear + else: + self.shear = shear + + def _get_inverse_affine_matrix(self, center, angle, translate, scale, shear): + # https://github.com/pytorch/vision/blob/v0.4.0/torchvision/transforms/functional.py#L717 + from numpy import sin, cos, tan + + if isinstance(shear, numbers.Number): + shear = [shear, 0] + + if not isinstance(shear, (tuple, list)) and len(shear) == 2: + raise ValueError( + "Shear should be a single value or a tuple/list containing " + + "two values. Got {}".format(shear) + ) + + rot = math.radians(angle) + sx, sy = [math.radians(s) for s in shear] + + cx, cy = center + tx, ty = translate + + # RSS without scaling + a = cos(rot - sy) / cos(sy) + b = -cos(rot - sy) * tan(sx) / cos(sy) - sin(rot) + c = sin(rot - sy) / cos(sy) + d = -sin(rot - sy) * tan(sx) / cos(sy) + cos(rot) + + # Inverted rotation matrix with scale and shear + # det([[a, b], [c, d]]) == 1, since det(rotation) = 1 and det(shear) = 1 + M = [d, -b, 0, -c, a, 0] + M = [x / scale for x in M] + + # Apply inverse of translation and of center translation: RSS^-1 * C^-1 * T^-1 + M[2] += M[0] * (-cx - tx) + M[1] * (-cy - ty) + M[5] += M[3] * (-cx - tx) + M[4] * (-cy - ty) + + # Apply center translation: C * RSS^-1 * C^-1 * T^-1 + M[2] += cx + M[5] += cy + return M + + @staticmethod + def get_params(degrees, translate, scale_ranges, shears, height): + angle = sample_sym(degrees) + if translate is not None: + max_dx = translate[0] * height + max_dy = translate[1] * height + translations = (np.round(sample_sym(max_dx)), np.round(sample_sym(max_dy))) + else: + translations = (0, 0) + + if scale_ranges is not None: + scale = sample_uniform(scale_ranges[0], scale_ranges[1]) + else: + scale = 1.0 + + if shears is not None: + if len(shears) == 1: + shear = [sample_sym(shears[0]), 0.0] + elif len(shears) == 2: + shear = [sample_sym(shears[0]), sample_sym(shears[1])] + else: + shear = 0.0 + + return angle, translations, scale, shear + + def __call__(self, img): + src_h, src_w = img.shape[:2] + angle, translate, scale, shear = self.get_params( + self.degrees, self.translate, self.scale, self.shear, src_h + ) + + M = self._get_inverse_affine_matrix( + (src_w / 2, src_h / 2), angle, (0, 0), scale, shear + ) + M = np.array(M).reshape(2, 3) + + startpoints = [(0, 0), (src_w - 1, 0), (src_w - 1, src_h - 1), (0, src_h - 1)] + project = lambda x, y, a, b, c: int(a * x + b * y + c) + endpoints = [ + (project(x, y, *M[0]), project(x, y, *M[1])) for x, y in startpoints + ] + + rect = cv2.minAreaRect(np.array(endpoints)) + bbox = cv2.boxPoints(rect).astype(dtype=np.int32) + max_x, max_y = bbox[:, 0].max(), bbox[:, 1].max() + min_x, min_y = bbox[:, 0].min(), bbox[:, 1].min() + + dst_w = int(max_x - min_x) + dst_h = int(max_y - min_y) + M[0, 2] += (dst_w - src_w) / 2 + M[1, 2] += (dst_h - src_h) / 2 + + # add translate + dst_w += int(abs(translate[0])) + dst_h += int(abs(translate[1])) + if translate[0] < 0: + M[0, 2] += abs(translate[0]) + if translate[1] < 0: + M[1, 2] += abs(translate[1]) + + flags = get_interpolation() + return cv2.warpAffine( + img, M, (dst_w, dst_h), flags=flags, borderMode=cv2.BORDER_REPLICATE + ) + + +class CVRandomPerspective(object): + def __init__(self, distortion=0.5): + self.distortion = distortion + + def get_params(self, width, height, distortion): + offset_h = sample_asym(distortion * height / 2, size=4).astype(dtype=np.int32) + offset_w = sample_asym(distortion * width / 2, size=4).astype(dtype=np.int32) + topleft = (offset_w[0], offset_h[0]) + topright = (width - 1 - offset_w[1], offset_h[1]) + botright = (width - 1 - offset_w[2], height - 1 - offset_h[2]) + botleft = (offset_w[3], height - 1 - offset_h[3]) + + startpoints = [(0, 0), (width - 1, 0), (width - 1, height - 1), (0, height - 1)] + endpoints = [topleft, topright, botright, botleft] + return np.array(startpoints, dtype=np.float32), np.array( + endpoints, dtype=np.float32 + ) + + def __call__(self, img): + height, width = img.shape[:2] + startpoints, endpoints = self.get_params(width, height, self.distortion) + M = cv2.getPerspectiveTransform(startpoints, endpoints) + + # TODO: more robust way to crop image + rect = cv2.minAreaRect(endpoints) + bbox = cv2.boxPoints(rect).astype(dtype=np.int32) + max_x, max_y = bbox[:, 0].max(), bbox[:, 1].max() + min_x, min_y = bbox[:, 0].min(), bbox[:, 1].min() + min_x, min_y = max(min_x, 0), max(min_y, 0) + + flags = get_interpolation() + img = cv2.warpPerspective( + img, M, (max_x, max_y), flags=flags, borderMode=cv2.BORDER_REPLICATE + ) + img = img[min_y:, min_x:] + return img + + +class CVRescale(object): + def __init__(self, factor=4, base_size=(128, 512)): + """Define image scales using gaussian pyramid and rescale image to target scale. + + Args: + factor: the decayed factor from base size, factor=4 keeps target scale by default. + base_size: base size the build the bottom layer of pyramid + """ + if isinstance(factor, numbers.Number): + self.factor = round(sample_uniform(0, factor)) + elif isinstance(factor, (tuple, list)) and len(factor) == 2: + self.factor = round(sample_uniform(factor[0], factor[1])) + else: + raise Exception("factor must be number or list with length 2") + # assert factor is valid + self.base_h, self.base_w = base_size[:2] + + def __call__(self, img): + if self.factor == 0: + return img + src_h, src_w = img.shape[:2] + cur_w, cur_h = self.base_w, self.base_h + scale_img = cv2.resize(img, (cur_w, cur_h), interpolation=get_interpolation()) + for _ in range(self.factor): + scale_img = cv2.pyrDown(scale_img) + scale_img = cv2.resize( + scale_img, (src_w, src_h), interpolation=get_interpolation() + ) + return scale_img + + +class CVGaussianNoise(object): + def __init__(self, mean=0, var=20): + self.mean = mean + if isinstance(var, numbers.Number): + self.var = max(int(sample_asym(var)), 1) + elif isinstance(var, (tuple, list)) and len(var) == 2: + self.var = int(sample_uniform(var[0], var[1])) + else: + raise Exception("degree must be number or list with length 2") + + def __call__(self, img): + noise = np.random.normal(self.mean, self.var**0.5, img.shape) + img = np.clip(img + noise, 0, 255).astype(np.uint8) + return img + + +class CVPossionNoise(object): + def __init__(self, lam=20): + self.lam = lam + if isinstance(lam, numbers.Number): + self.lam = max(int(sample_asym(lam)), 1) + elif isinstance(lam, (tuple, list)) and len(lam) == 2: + self.lam = int(sample_uniform(lam[0], lam[1])) + else: + raise Exception("lam must be number or list with length 2") + + def __call__(self, img): + noise = np.random.poisson(lam=self.lam, size=img.shape) + img = np.clip(img + noise, 0, 255).astype(np.uint8) + return img + + +class CVGaussionBlur(object): + def __init__(self, radius): + self.radius = radius + if isinstance(radius, numbers.Number): + self.radius = max(int(sample_asym(radius)), 1) + elif isinstance(radius, (tuple, list)) and len(radius) == 2: + self.radius = int(sample_uniform(radius[0], radius[1])) + else: + raise Exception("radius must be number or list with length 2") + + def __call__(self, img): + fil = cv2.getGaussianKernel(ksize=self.radius, sigma=1, ktype=cv2.CV_32F) + img = cv2.sepFilter2D(img, -1, fil, fil) + return img + + +class CVMotionBlur(object): + def __init__(self, degrees=12, angle=90): + if isinstance(degrees, numbers.Number): + self.degree = max(int(sample_asym(degrees)), 1) + elif isinstance(degrees, (tuple, list)) and len(degrees) == 2: + self.degree = int(sample_uniform(degrees[0], degrees[1])) + else: + raise Exception("degree must be number or list with length 2") + self.angle = sample_uniform(-angle, angle) + + def __call__(self, img): + M = cv2.getRotationMatrix2D((self.degree // 2, self.degree // 2), self.angle, 1) + motion_blur_kernel = np.zeros((self.degree, self.degree)) + motion_blur_kernel[self.degree // 2, :] = 1 + motion_blur_kernel = cv2.warpAffine( + motion_blur_kernel, M, (self.degree, self.degree) + ) + motion_blur_kernel = motion_blur_kernel / self.degree + img = cv2.filter2D(img, -1, motion_blur_kernel) + img = np.clip(img, 0, 255).astype(np.uint8) + return img + + +class CVGeometry(object): + def __init__( + self, + degrees=15, + translate=(0.3, 0.3), + scale=(0.5, 2.0), + shear=(45, 15), + distortion=0.5, + p=0.5, + ): + self.p = p + type_p = random.random() + if type_p < 0.33: + self.transforms = CVRandomRotation(degrees=degrees) + elif type_p < 0.66: + self.transforms = CVRandomAffine( + degrees=degrees, translate=translate, scale=scale, shear=shear + ) + else: + self.transforms = CVRandomPerspective(distortion=distortion) + + def __call__(self, img): + if random.random() < self.p: + return self.transforms(img) + else: + return img + + +class CVDeterioration(object): + def __init__(self, var, degrees, factor, p=0.5): + self.p = p + transforms = [] + if var is not None: + transforms.append(CVGaussianNoise(var=var)) + if degrees is not None: + transforms.append(CVMotionBlur(degrees=degrees)) + if factor is not None: + transforms.append(CVRescale(factor=factor)) + + random.shuffle(transforms) + transforms = Compose(transforms) + self.transforms = transforms + + def __call__(self, img): + if random.random() < self.p: + return self.transforms(img) + else: + return img + + +class CVColorJitter(object): + def __init__(self, brightness=0.5, contrast=0.5, saturation=0.5, hue=0.1, p=0.5): + self.p = p + self.transforms = ColorJitter( + brightness=brightness, contrast=contrast, saturation=saturation, hue=hue + ) + + def __call__(self, img): + if random.random() < self.p: + return self.transforms(img) + else: + return img + + +class SVTRDeterioration(object): + def __init__(self, var, degrees, factor, p=0.5): + self.p = p + transforms = [] + if var is not None: + transforms.append(CVGaussianNoise(var=var)) + if degrees is not None: + transforms.append(CVMotionBlur(degrees=degrees)) + if factor is not None: + transforms.append(CVRescale(factor=factor)) + self.transforms = transforms + + def __call__(self, img): + if random.random() < self.p: + random.shuffle(self.transforms) + transforms = Compose(self.transforms) + return transforms(img) + else: + return img + + +class ParseQDeterioration(object): + def __init__(self, var, degrees, lam, radius, factor, p=0.5): + self.p = p + transforms = [] + if var is not None: + transforms.append(CVGaussianNoise(var=var)) + if degrees is not None: + transforms.append(CVMotionBlur(degrees=degrees)) + if lam is not None: + transforms.append(CVPossionNoise(lam=lam)) + if radius is not None: + transforms.append(CVGaussionBlur(radius=radius)) + if factor is not None: + transforms.append(CVRescale(factor=factor)) + self.transforms = transforms + + def __call__(self, img): + if random.random() < self.p: + random.shuffle(self.transforms) + transforms = Compose(self.transforms) + return transforms(img) + else: + return img + + +class SVTRGeometry(object): + def __init__( + self, + aug_type=0, + degrees=15, + translate=(0.3, 0.3), + scale=(0.5, 2.0), + shear=(45, 15), + distortion=0.5, + p=0.5, + ): + self.aug_type = aug_type + self.p = p + self.transforms = [] + self.transforms.append(CVRandomRotation(degrees=degrees)) + self.transforms.append( + CVRandomAffine( + degrees=degrees, translate=translate, scale=scale, shear=shear + ) + ) + self.transforms.append(CVRandomPerspective(distortion=distortion)) + + def __call__(self, img): + if random.random() < self.p: + if self.aug_type: + random.shuffle(self.transforms) + transforms = Compose(self.transforms[: random.randint(1, 3)]) + img = transforms(img) + else: + img = self.transforms[random.randint(0, 2)](img) + return img + else: + return img diff --git a/modules/onnx_ocr_module/src/ppocr/data/imaug/copy_paste.py b/modules/onnx_ocr_module/src/ppocr/data/imaug/copy_paste.py new file mode 100644 index 0000000..4d627d0 --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/data/imaug/copy_paste.py @@ -0,0 +1,179 @@ +# copyright (c) 2021 PaddlePaddle Authors. All Rights Reserve. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +import copy +import cv2 +import random +import numpy as np +from PIL import Image +from shapely.geometry import Polygon + +from ppocr.data.imaug.iaa_augment import IaaAugment +from ppocr.data.imaug.random_crop_data import is_poly_outside_rect +from tools.infer.utility import get_rotate_crop_image + + +class CopyPaste(object): + def __init__(self, objects_paste_ratio=0.2, limit_paste=True, **kwargs): + self.ext_data_num = 1 + self.objects_paste_ratio = objects_paste_ratio + self.limit_paste = limit_paste + augmenter_args = [{"type": "Resize", "args": {"size": [0.5, 3]}}] + self.aug = IaaAugment(augmenter_args) + + def __call__(self, data): + point_num = data["polys"].shape[1] + src_img = data["image"] + src_polys = data["polys"].tolist() + src_texts = data["texts"] + src_ignores = data["ignore_tags"].tolist() + ext_data = data["ext_data"][0] + ext_image = ext_data["image"] + ext_polys = ext_data["polys"] + ext_texts = ext_data["texts"] + ext_ignores = ext_data["ignore_tags"] + + indexes = [i for i in range(len(ext_ignores)) if not ext_ignores[i]] + select_num = max(1, min(int(self.objects_paste_ratio * len(ext_polys)), 30)) + + random.shuffle(indexes) + select_idxs = indexes[:select_num] + select_polys = ext_polys[select_idxs] + select_ignores = ext_ignores[select_idxs] + + src_img = cv2.cvtColor(src_img, cv2.COLOR_BGR2RGB) + ext_image = cv2.cvtColor(ext_image, cv2.COLOR_BGR2RGB) + src_img = Image.fromarray(src_img).convert("RGBA") + for idx, poly, tag in zip(select_idxs, select_polys, select_ignores): + box_img = get_rotate_crop_image(ext_image, poly) + + src_img, box = self.paste_img(src_img, box_img, src_polys) + if box is not None: + box = box.tolist() + for _ in range(len(box), point_num): + box.append(box[-1]) + src_polys.append(box) + src_texts.append(ext_texts[idx]) + src_ignores.append(tag) + src_img = cv2.cvtColor(np.array(src_img), cv2.COLOR_RGB2BGR) + h, w = src_img.shape[:2] + src_polys = np.array(src_polys) + src_polys[:, :, 0] = np.clip(src_polys[:, :, 0], 0, w) + src_polys[:, :, 1] = np.clip(src_polys[:, :, 1], 0, h) + data["image"] = src_img + data["polys"] = src_polys + data["texts"] = src_texts + data["ignore_tags"] = np.array(src_ignores) + return data + + def paste_img(self, src_img, box_img, src_polys): + box_img_pil = Image.fromarray(box_img).convert("RGBA") + src_w, src_h = src_img.size + box_w, box_h = box_img_pil.size + + angle = np.random.randint(0, 360) + box = np.array([[[0, 0], [box_w, 0], [box_w, box_h], [0, box_h]]]) + box = rotate_bbox(box_img, box, angle)[0] + box_img_pil = box_img_pil.rotate(angle, expand=1) + box_w, box_h = box_img_pil.width, box_img_pil.height + if src_w - box_w < 0 or src_h - box_h < 0: + return src_img, None + + paste_x, paste_y = self.select_coord( + src_polys, box, src_w - box_w, src_h - box_h + ) + if paste_x is None: + return src_img, None + box[:, 0] += paste_x + box[:, 1] += paste_y + r, g, b, A = box_img_pil.split() + src_img.paste(box_img_pil, (paste_x, paste_y), mask=A) + + return src_img, box + + def select_coord(self, src_polys, box, endx, endy): + if self.limit_paste: + xmin, ymin, xmax, ymax = ( + box[:, 0].min(), + box[:, 1].min(), + box[:, 0].max(), + box[:, 1].max(), + ) + for _ in range(50): + paste_x = random.randint(0, endx) + paste_y = random.randint(0, endy) + xmin1 = xmin + paste_x + xmax1 = xmax + paste_x + ymin1 = ymin + paste_y + ymax1 = ymax + paste_y + + num_poly_in_rect = 0 + for poly in src_polys: + if not is_poly_outside_rect( + poly, xmin1, ymin1, xmax1 - xmin1, ymax1 - ymin1 + ): + num_poly_in_rect += 1 + break + if num_poly_in_rect == 0: + return paste_x, paste_y + return None, None + else: + paste_x = random.randint(0, endx) + paste_y = random.randint(0, endy) + return paste_x, paste_y + + +def get_union(pD, pG): + return Polygon(pD).union(Polygon(pG)).area + + +def get_intersection_over_union(pD, pG): + return get_intersection(pD, pG) / get_union(pD, pG) + + +def get_intersection(pD, pG): + return Polygon(pD).intersection(Polygon(pG)).area + + +def rotate_bbox(img, text_polys, angle, scale=1): + """ + from https://github.com/WenmuZhou/DBNet.pytorch/blob/master/data_loader/modules/augment.py + Args: + img: np.ndarray + text_polys: np.ndarray N*4*2 + angle: int + scale: int + + Returns: + + """ + w = img.shape[1] + h = img.shape[0] + + rangle = np.deg2rad(angle) + nw = abs(np.sin(rangle) * h) + abs(np.cos(rangle) * w) + nh = abs(np.cos(rangle) * h) + abs(np.sin(rangle) * w) + rot_mat = cv2.getRotationMatrix2D((nw * 0.5, nh * 0.5), angle, scale) + rot_move = np.dot(rot_mat, np.array([(nw - w) * 0.5, (nh - h) * 0.5, 0])) + rot_mat[0, 2] += rot_move[0] + rot_mat[1, 2] += rot_move[1] + + # ---------------------- rotate box ---------------------- + rot_text_polys = list() + for bbox in text_polys: + point1 = np.dot(rot_mat, np.array([bbox[0, 0], bbox[0, 1], 1])) + point2 = np.dot(rot_mat, np.array([bbox[1, 0], bbox[1, 1], 1])) + point3 = np.dot(rot_mat, np.array([bbox[2, 0], bbox[2, 1], 1])) + point4 = np.dot(rot_mat, np.array([bbox[3, 0], bbox[3, 1], 1])) + rot_text_polys.append([point1, point2, point3, point4]) + return np.array(rot_text_polys, dtype=np.float32) diff --git a/modules/onnx_ocr_module/src/ppocr/data/imaug/ct_process.py b/modules/onnx_ocr_module/src/ppocr/data/imaug/ct_process.py new file mode 100644 index 0000000..26e111d --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/data/imaug/ct_process.py @@ -0,0 +1,376 @@ +# copyright (c) 2020 PaddlePaddle Authors. All Rights Reserve. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import os +import cv2 +import paddle +import random +import pyclipper +import numpy as np +from PIL import Image + +import paddle.vision.transforms as transforms + +from ppocr.utils.utility import check_install + + +class RandomScale: + def __init__(self, short_size=640, **kwargs): + self.short_size = short_size + + def scale_aligned(self, img, scale): + oh, ow = img.shape[0:2] + h = int(oh * scale + 0.5) + w = int(ow * scale + 0.5) + if h % 32 != 0: + h = h + (32 - h % 32) + if w % 32 != 0: + w = w + (32 - w % 32) + img = cv2.resize(img, dsize=(w, h)) + factor_h = h / oh + factor_w = w / ow + return img, factor_h, factor_w + + def __call__(self, data): + img = data["image"] + + h, w = img.shape[0:2] + random_scale = np.array([0.7, 0.8, 0.9, 1.0, 1.1, 1.2, 1.3]) + scale = (np.random.choice(random_scale) * self.short_size) / min(h, w) + img, factor_h, factor_w = self.scale_aligned(img, scale) + + data["scale_factor"] = (factor_w, factor_h) + data["image"] = img + return data + + +class MakeShrink: + def __init__(self, kernel_scale=0.7, **kwargs): + self.kernel_scale = kernel_scale + + def dist(self, a, b): + return np.linalg.norm((a - b), ord=2, axis=0) + + def perimeter(self, bbox): + peri = 0.0 + for i in range(bbox.shape[0]): + peri += self.dist(bbox[i], bbox[(i + 1) % bbox.shape[0]]) + return peri + + def shrink(self, bboxes, rate, max_shr=20): + check_install("Polygon", "Polygon3") + import Polygon as plg + + rate = rate * rate + shrinked_bboxes = [] + for bbox in bboxes: + area = plg.Polygon(bbox).area() + peri = self.perimeter(bbox) + + try: + pco = pyclipper.PyclipperOffset() + pco.AddPath(bbox, pyclipper.JT_ROUND, pyclipper.ET_CLOSEDPOLYGON) + offset = min(int(area * (1 - rate) / (peri + 0.001) + 0.5), max_shr) + + shrinked_bbox = pco.Execute(-offset) + if len(shrinked_bbox) == 0: + shrinked_bboxes.append(bbox) + continue + + shrinked_bbox = np.array(shrinked_bbox[0]) + if shrinked_bbox.shape[0] <= 2: + shrinked_bboxes.append(bbox) + continue + + shrinked_bboxes.append(shrinked_bbox) + except Exception as e: + shrinked_bboxes.append(bbox) + + return shrinked_bboxes + + def __call__(self, data): + img = data["image"] + bboxes = data["polys"] + words = data["texts"] + scale_factor = data["scale_factor"] + + gt_instance = np.zeros(img.shape[0:2], dtype="uint8") # h,w + training_mask = np.ones(img.shape[0:2], dtype="uint8") + training_mask_distance = np.ones(img.shape[0:2], dtype="uint8") + + for i in range(len(bboxes)): + bboxes[i] = np.reshape( + bboxes[i] + * ([scale_factor[0], scale_factor[1]] * (bboxes[i].shape[0] // 2)), + (bboxes[i].shape[0] // 2, 2), + ).astype("int32") + + for i in range(len(bboxes)): + # different value for different bbox + cv2.drawContours(gt_instance, [bboxes[i]], -1, i + 1, -1) + + # set training mask to 0 + cv2.drawContours(training_mask, [bboxes[i]], -1, 0, -1) + + # for not accurate annotation, use training_mask_distance + if words[i] == "###" or words[i] == "???": + cv2.drawContours(training_mask_distance, [bboxes[i]], -1, 0, -1) + + # make shrink + gt_kernel_instance = np.zeros(img.shape[0:2], dtype="uint8") + kernel_bboxes = self.shrink(bboxes, self.kernel_scale) + for i in range(len(bboxes)): + cv2.drawContours(gt_kernel_instance, [kernel_bboxes[i]], -1, i + 1, -1) + + # for training mask, kernel and background= 1, box region=0 + if words[i] != "###" and words[i] != "???": + cv2.drawContours(training_mask, [kernel_bboxes[i]], -1, 1, -1) + + gt_kernel = gt_kernel_instance.copy() + # for gt_kernel, kernel = 1 + gt_kernel[gt_kernel > 0] = 1 + + # shrink 2 times + tmp1 = gt_kernel_instance.copy() + erode_kernel = np.ones((3, 3), np.uint8) + tmp1 = cv2.erode(tmp1, erode_kernel, iterations=1) + tmp2 = tmp1.copy() + tmp2 = cv2.erode(tmp2, erode_kernel, iterations=1) + + # compute text region + gt_kernel_inner = tmp1 - tmp2 + + # gt_instance: text instance, bg=0, diff word use diff value + # training_mask: text instance mask, word=0,kernel and bg=1 + # gt_kernel_instance: text kernel instance, bg=0, diff word use diff value + # gt_kernel: text_kernel, bg=0,diff word use same value + # gt_kernel_inner: text kernel reference + # training_mask_distance: word without anno = 0, else 1 + + data["image"] = [ + img, + gt_instance, + training_mask, + gt_kernel_instance, + gt_kernel, + gt_kernel_inner, + training_mask_distance, + ] + return data + + +class GroupRandomHorizontalFlip: + def __init__(self, p=0.5, **kwargs): + self.p = p + + def __call__(self, data): + imgs = data["image"] + + if random.random() < self.p: + for i in range(len(imgs)): + imgs[i] = np.flip(imgs[i], axis=1).copy() + data["image"] = imgs + return data + + +class GroupRandomRotate: + def __init__(self, **kwargs): + pass + + def __call__(self, data): + imgs = data["image"] + + max_angle = 10 + angle = random.random() * 2 * max_angle - max_angle + for i in range(len(imgs)): + img = imgs[i] + w, h = img.shape[:2] + rotation_matrix = cv2.getRotationMatrix2D((h / 2, w / 2), angle, 1) + img_rotation = cv2.warpAffine( + img, rotation_matrix, (h, w), flags=cv2.INTER_NEAREST + ) + imgs[i] = img_rotation + + data["image"] = imgs + return data + + +class GroupRandomCropPadding: + def __init__(self, target_size=(640, 640), **kwargs): + self.target_size = target_size + + def __call__(self, data): + imgs = data["image"] + + h, w = imgs[0].shape[0:2] + t_w, t_h = self.target_size + p_w, p_h = self.target_size + if w == t_w and h == t_h: + return data + + t_h = t_h if t_h < h else h + t_w = t_w if t_w < w else w + + if random.random() > 3.0 / 8.0 and np.max(imgs[1]) > 0: + # make sure to crop the text region + tl = np.min(np.where(imgs[1] > 0), axis=1) - (t_h, t_w) + tl[tl < 0] = 0 + br = np.max(np.where(imgs[1] > 0), axis=1) - (t_h, t_w) + br[br < 0] = 0 + br[0] = min(br[0], h - t_h) + br[1] = min(br[1], w - t_w) + + i = random.randint(tl[0], br[0]) if tl[0] < br[0] else 0 + j = random.randint(tl[1], br[1]) if tl[1] < br[1] else 0 + else: + i = random.randint(0, h - t_h) if h - t_h > 0 else 0 + j = random.randint(0, w - t_w) if w - t_w > 0 else 0 + + n_imgs = [] + for idx in range(len(imgs)): + if len(imgs[idx].shape) == 3: + s3_length = int(imgs[idx].shape[-1]) + img = imgs[idx][i : i + t_h, j : j + t_w, :] + img_p = cv2.copyMakeBorder( + img, + 0, + p_h - t_h, + 0, + p_w - t_w, + borderType=cv2.BORDER_CONSTANT, + value=tuple(0 for i in range(s3_length)), + ) + else: + img = imgs[idx][i : i + t_h, j : j + t_w] + img_p = cv2.copyMakeBorder( + img, + 0, + p_h - t_h, + 0, + p_w - t_w, + borderType=cv2.BORDER_CONSTANT, + value=(0,), + ) + n_imgs.append(img_p) + + data["image"] = n_imgs + return data + + +class MakeCentripetalShift: + def __init__(self, **kwargs): + pass + + def jaccard(self, As, Bs): + A = As.shape[0] # small + B = Bs.shape[0] # large + + dis = np.sqrt( + np.sum( + ( + As[:, np.newaxis, :].repeat(B, axis=1) + - Bs[np.newaxis, :, :].repeat(A, axis=0) + ) + ** 2, + axis=-1, + ) + ) + + ind = np.argmin(dis, axis=-1) + + return ind + + def __call__(self, data): + imgs = data["image"] + + ( + img, + gt_instance, + training_mask, + gt_kernel_instance, + gt_kernel, + gt_kernel_inner, + training_mask_distance, + ) = (imgs[0], imgs[1], imgs[2], imgs[3], imgs[4], imgs[5], imgs[6]) + + max_instance = np.max(gt_instance) # num bbox + + # make centripetal shift + gt_distance = np.zeros((2, *img.shape[0:2]), dtype=np.float32) + for i in range(1, max_instance + 1): + # kernel_reference + ind = gt_kernel_inner == i + + if np.sum(ind) == 0: + training_mask[gt_instance == i] = 0 + training_mask_distance[gt_instance == i] = 0 + continue + + kpoints = ( + np.array(np.where(ind)).transpose((1, 0))[:, ::-1].astype("float32") + ) + + ind = (gt_instance == i) * (gt_kernel_instance == 0) + if np.sum(ind) == 0: + continue + pixels = np.where(ind) + + points = np.array(pixels).transpose((1, 0))[:, ::-1].astype("float32") + + bbox_ind = self.jaccard(points, kpoints) + + offset_gt = kpoints[bbox_ind] - points + + gt_distance[:, pixels[0], pixels[1]] = offset_gt.T * 0.1 + + img = Image.fromarray(img) + img = img.convert("RGB") + + data["image"] = img + data["gt_kernel"] = gt_kernel.astype("int64") + data["training_mask"] = training_mask.astype("int64") + data["gt_instance"] = gt_instance.astype("int64") + data["gt_kernel_instance"] = gt_kernel_instance.astype("int64") + data["training_mask_distance"] = training_mask_distance.astype("int64") + data["gt_distance"] = gt_distance.astype("float32") + + return data + + +class ScaleAlignedShort: + def __init__(self, short_size=640, **kwargs): + self.short_size = short_size + + def __call__(self, data): + img = data["image"] + + org_img_shape = img.shape + + h, w = img.shape[0:2] + scale = self.short_size * 1.0 / min(h, w) + h = int(h * scale + 0.5) + w = int(w * scale + 0.5) + if h % 32 != 0: + h = h + (32 - h % 32) + if w % 32 != 0: + w = w + (32 - w % 32) + img = cv2.resize(img, dsize=(w, h)) + + new_img_shape = img.shape + img_shape = np.array(org_img_shape + new_img_shape) + + data["shape"] = img_shape + data["image"] = img + + return data diff --git a/modules/onnx_ocr_module/src/ppocr/data/imaug/drrg_targets.py b/modules/onnx_ocr_module/src/ppocr/data/imaug/drrg_targets.py new file mode 100644 index 0000000..8eeef26 --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/data/imaug/drrg_targets.py @@ -0,0 +1,770 @@ +# copyright (c) 2022 PaddlePaddle Authors. All Rights Reserve. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +""" +This code is refer from: +https://github.com/open-mmlab/mmocr/blob/main/mmocr/datasets/pipelines/textdet_targets/drrg_targets.py +""" + +import cv2 +import numpy as np +from ppocr.utils.utility import check_install +from numpy.linalg import norm + + +class DRRGTargets(object): + def __init__( + self, + orientation_thr=2.0, + resample_step=8.0, + num_min_comps=9, + num_max_comps=600, + min_width=8.0, + max_width=24.0, + center_region_shrink_ratio=0.3, + comp_shrink_ratio=1.0, + comp_w_h_ratio=0.3, + text_comp_nms_thr=0.25, + min_rand_half_height=8.0, + max_rand_half_height=24.0, + jitter_level=0.2, + **kwargs, + ): + super().__init__() + self.orientation_thr = orientation_thr + self.resample_step = resample_step + self.num_max_comps = num_max_comps + self.num_min_comps = num_min_comps + self.min_width = min_width + self.max_width = max_width + self.center_region_shrink_ratio = center_region_shrink_ratio + self.comp_shrink_ratio = comp_shrink_ratio + self.comp_w_h_ratio = comp_w_h_ratio + self.text_comp_nms_thr = text_comp_nms_thr + self.min_rand_half_height = min_rand_half_height + self.max_rand_half_height = max_rand_half_height + self.jitter_level = jitter_level + self.eps = 1e-8 + + def vector_angle(self, vec1, vec2): + if vec1.ndim > 1: + unit_vec1 = vec1 / (norm(vec1, axis=-1) + self.eps).reshape((-1, 1)) + else: + unit_vec1 = vec1 / (norm(vec1, axis=-1) + self.eps) + if vec2.ndim > 1: + unit_vec2 = vec2 / (norm(vec2, axis=-1) + self.eps).reshape((-1, 1)) + else: + unit_vec2 = vec2 / (norm(vec2, axis=-1) + self.eps) + return np.arccos(np.clip(np.sum(unit_vec1 * unit_vec2, axis=-1), -1.0, 1.0)) + + def vector_slope(self, vec): + assert len(vec) == 2 + return abs(vec[1] / (vec[0] + self.eps)) + + def vector_sin(self, vec): + assert len(vec) == 2 + return vec[1] / (norm(vec) + self.eps) + + def vector_cos(self, vec): + assert len(vec) == 2 + return vec[0] / (norm(vec) + self.eps) + + def find_head_tail(self, points, orientation_thr): + assert points.ndim == 2 + assert points.shape[0] >= 4 + assert points.shape[1] == 2 + assert isinstance(orientation_thr, float) + + if len(points) > 4: + pad_points = np.vstack([points, points[0]]) + edge_vec = pad_points[1:] - pad_points[:-1] + + theta_sum = [] + adjacent_vec_theta = [] + for i, edge_vec1 in enumerate(edge_vec): + adjacent_ind = [x % len(edge_vec) for x in [i - 1, i + 1]] + adjacent_edge_vec = edge_vec[adjacent_ind] + temp_theta_sum = np.sum(self.vector_angle(edge_vec1, adjacent_edge_vec)) + temp_adjacent_theta = self.vector_angle( + adjacent_edge_vec[0], adjacent_edge_vec[1] + ) + theta_sum.append(temp_theta_sum) + adjacent_vec_theta.append(temp_adjacent_theta) + theta_sum_score = np.array(theta_sum) / np.pi + adjacent_theta_score = np.array(adjacent_vec_theta) / np.pi + poly_center = np.mean(points, axis=0) + edge_dist = np.maximum( + norm(pad_points[1:] - poly_center, axis=-1), + norm(pad_points[:-1] - poly_center, axis=-1), + ) + dist_score = edge_dist / (np.max(edge_dist) + self.eps) + position_score = np.zeros(len(edge_vec)) + score = 0.5 * theta_sum_score + 0.15 * adjacent_theta_score + score += 0.35 * dist_score + if len(points) % 2 == 0: + position_score[(len(score) // 2 - 1)] += 1 + position_score[-1] += 1 + score += 0.1 * position_score + pad_score = np.concatenate([score, score]) + score_matrix = np.zeros((len(score), len(score) - 3)) + x = np.arange(len(score) - 3) / float(len(score) - 4) + gaussian = ( + 1.0 + / (np.sqrt(2.0 * np.pi) * 0.5) + * np.exp(-np.power((x - 0.5) / 0.5, 2.0) / 2) + ) + gaussian = gaussian / np.max(gaussian) + for i in range(len(score)): + score_matrix[i, :] = ( + score[i] + + pad_score[(i + 2) : (i + len(score) - 1)] * gaussian * 0.3 + ) + + head_start, tail_increment = np.unravel_index( + score_matrix.argmax(), score_matrix.shape + ) + tail_start = (head_start + tail_increment + 2) % len(points) + head_end = (head_start + 1) % len(points) + tail_end = (tail_start + 1) % len(points) + + if head_end > tail_end: + head_start, tail_start = tail_start, head_start + head_end, tail_end = tail_end, head_end + head_inds = [head_start, head_end] + tail_inds = [tail_start, tail_end] + else: + if self.vector_slope(points[1] - points[0]) + self.vector_slope( + points[3] - points[2] + ) < self.vector_slope(points[2] - points[1]) + self.vector_slope( + points[0] - points[3] + ): + horizontal_edge_inds = [[0, 1], [2, 3]] + vertical_edge_inds = [[3, 0], [1, 2]] + else: + horizontal_edge_inds = [[3, 0], [1, 2]] + vertical_edge_inds = [[0, 1], [2, 3]] + + vertical_len_sum = norm( + points[vertical_edge_inds[0][0]] - points[vertical_edge_inds[0][1]] + ) + norm( + points[vertical_edge_inds[1][0]] - points[vertical_edge_inds[1][1]] + ) + horizontal_len_sum = norm( + points[horizontal_edge_inds[0][0]] - points[horizontal_edge_inds[0][1]] + ) + norm( + points[horizontal_edge_inds[1][0]] - points[horizontal_edge_inds[1][1]] + ) + + if vertical_len_sum > horizontal_len_sum * orientation_thr: + head_inds = horizontal_edge_inds[0] + tail_inds = horizontal_edge_inds[1] + else: + head_inds = vertical_edge_inds[0] + tail_inds = vertical_edge_inds[1] + + return head_inds, tail_inds + + def reorder_poly_edge(self, points): + assert points.ndim == 2 + assert points.shape[0] >= 4 + assert points.shape[1] == 2 + + head_inds, tail_inds = self.find_head_tail(points, self.orientation_thr) + head_edge, tail_edge = points[head_inds], points[tail_inds] + + pad_points = np.vstack([points, points]) + if tail_inds[1] < 1: + tail_inds[1] = len(points) + sideline1 = pad_points[head_inds[1] : tail_inds[1]] + sideline2 = pad_points[tail_inds[1] : (head_inds[1] + len(points))] + sideline_mean_shift = np.mean(sideline1, axis=0) - np.mean(sideline2, axis=0) + + if sideline_mean_shift[1] > 0: + top_sideline, bot_sideline = sideline2, sideline1 + else: + top_sideline, bot_sideline = sideline1, sideline2 + + return head_edge, tail_edge, top_sideline, bot_sideline + + def cal_curve_length(self, line): + assert line.ndim == 2 + assert len(line) >= 2 + + edges_length = np.sqrt( + (line[1:, 0] - line[:-1, 0]) ** 2 + (line[1:, 1] - line[:-1, 1]) ** 2 + ) + total_length = np.sum(edges_length) + return edges_length, total_length + + def resample_line(self, line, n): + assert line.ndim == 2 + assert line.shape[0] >= 2 + assert line.shape[1] == 2 + assert isinstance(n, int) + assert n > 2 + + edges_length, total_length = self.cal_curve_length(line) + t_org = np.insert(np.cumsum(edges_length), 0, 0) + unit_t = total_length / (n - 1) + t_equidistant = np.arange(1, n - 1, dtype=np.float32) * unit_t + edge_ind = 0 + points = [line[0]] + for t in t_equidistant: + while edge_ind < len(edges_length) - 1 and t > t_org[edge_ind + 1]: + edge_ind += 1 + t_l, t_r = t_org[edge_ind], t_org[edge_ind + 1] + weight = np.array([t_r - t, t - t_l], dtype=np.float32) / ( + t_r - t_l + self.eps + ) + p_coords = np.dot(weight, line[[edge_ind, edge_ind + 1]]) + points.append(p_coords) + points.append(line[-1]) + resampled_line = np.vstack(points) + + return resampled_line + + def resample_sidelines(self, sideline1, sideline2, resample_step): + assert sideline1.ndim == sideline2.ndim == 2 + assert sideline1.shape[1] == sideline2.shape[1] == 2 + assert sideline1.shape[0] >= 2 + assert sideline2.shape[0] >= 2 + assert isinstance(resample_step, float) + + _, length1 = self.cal_curve_length(sideline1) + _, length2 = self.cal_curve_length(sideline2) + + avg_length = (length1 + length2) / 2 + resample_point_num = max(int(float(avg_length) / resample_step) + 1, 3) + + resampled_line1 = self.resample_line(sideline1, resample_point_num) + resampled_line2 = self.resample_line(sideline2, resample_point_num) + + return resampled_line1, resampled_line2 + + def dist_point2line(self, point, line): + assert isinstance(line, tuple) + point1, point2 = line + d = abs(np.cross(point2 - point1, point - point1)) / ( + norm(point2 - point1) + 1e-8 + ) + return d + + def draw_center_region_maps( + self, + top_line, + bot_line, + center_line, + center_region_mask, + top_height_map, + bot_height_map, + sin_map, + cos_map, + region_shrink_ratio, + ): + assert top_line.shape == bot_line.shape == center_line.shape + assert ( + center_region_mask.shape + == top_height_map.shape + == bot_height_map.shape + == sin_map.shape + == cos_map.shape + ) + assert isinstance(region_shrink_ratio, float) + + h, w = center_region_mask.shape + for i in range(0, len(center_line) - 1): + top_mid_point = (top_line[i] + top_line[i + 1]) / 2 + bot_mid_point = (bot_line[i] + bot_line[i + 1]) / 2 + + sin_theta = self.vector_sin(top_mid_point - bot_mid_point) + cos_theta = self.vector_cos(top_mid_point - bot_mid_point) + + tl = center_line[i] + (top_line[i] - center_line[i]) * region_shrink_ratio + tr = ( + center_line[i + 1] + + (top_line[i + 1] - center_line[i + 1]) * region_shrink_ratio + ) + br = ( + center_line[i + 1] + + (bot_line[i + 1] - center_line[i + 1]) * region_shrink_ratio + ) + bl = center_line[i] + (bot_line[i] - center_line[i]) * region_shrink_ratio + current_center_box = np.vstack([tl, tr, br, bl]).astype(np.int32) + + cv2.fillPoly(center_region_mask, [current_center_box], color=1) + cv2.fillPoly(sin_map, [current_center_box], color=sin_theta) + cv2.fillPoly(cos_map, [current_center_box], color=cos_theta) + + current_center_box[:, 0] = np.clip(current_center_box[:, 0], 0, w - 1) + current_center_box[:, 1] = np.clip(current_center_box[:, 1], 0, h - 1) + min_coord = np.min(current_center_box, axis=0).astype(np.int32) + max_coord = np.max(current_center_box, axis=0).astype(np.int32) + current_center_box = current_center_box - min_coord + box_sz = max_coord - min_coord + 1 + + center_box_mask = np.zeros((box_sz[1], box_sz[0]), dtype=np.uint8) + cv2.fillPoly(center_box_mask, [current_center_box], color=1) + + inds = np.argwhere(center_box_mask > 0) + inds = inds + (min_coord[1], min_coord[0]) + inds_xy = np.fliplr(inds) + top_height_map[(inds[:, 0], inds[:, 1])] = self.dist_point2line( + inds_xy, (top_line[i], top_line[i + 1]) + ) + bot_height_map[(inds[:, 0], inds[:, 1])] = self.dist_point2line( + inds_xy, (bot_line[i], bot_line[i + 1]) + ) + + def generate_center_mask_attrib_maps(self, img_size, text_polys): + assert isinstance(img_size, tuple) + + h, w = img_size + + center_lines = [] + center_region_mask = np.zeros((h, w), np.uint8) + top_height_map = np.zeros((h, w), dtype=np.float32) + bot_height_map = np.zeros((h, w), dtype=np.float32) + sin_map = np.zeros((h, w), dtype=np.float32) + cos_map = np.zeros((h, w), dtype=np.float32) + + for poly in text_polys: + polygon_points = poly + _, _, top_line, bot_line = self.reorder_poly_edge(polygon_points) + resampled_top_line, resampled_bot_line = self.resample_sidelines( + top_line, bot_line, self.resample_step + ) + resampled_bot_line = resampled_bot_line[::-1] + center_line = (resampled_top_line + resampled_bot_line) / 2 + + if self.vector_slope(center_line[-1] - center_line[0]) > 2: + if (center_line[-1] - center_line[0])[1] < 0: + center_line = center_line[::-1] + resampled_top_line = resampled_top_line[::-1] + resampled_bot_line = resampled_bot_line[::-1] + else: + if (center_line[-1] - center_line[0])[0] < 0: + center_line = center_line[::-1] + resampled_top_line = resampled_top_line[::-1] + resampled_bot_line = resampled_bot_line[::-1] + + line_head_shrink_len = ( + np.clip( + (norm(top_line[0] - bot_line[0]) * self.comp_w_h_ratio), + self.min_width, + self.max_width, + ) + / 2 + ) + line_tail_shrink_len = ( + np.clip( + (norm(top_line[-1] - bot_line[-1]) * self.comp_w_h_ratio), + self.min_width, + self.max_width, + ) + / 2 + ) + num_head_shrink = int(line_head_shrink_len // self.resample_step) + num_tail_shrink = int(line_tail_shrink_len // self.resample_step) + if len(center_line) > num_head_shrink + num_tail_shrink + 2: + center_line = center_line[ + num_head_shrink : len(center_line) - num_tail_shrink + ] + resampled_top_line = resampled_top_line[ + num_head_shrink : len(resampled_top_line) - num_tail_shrink + ] + resampled_bot_line = resampled_bot_line[ + num_head_shrink : len(resampled_bot_line) - num_tail_shrink + ] + center_lines.append(center_line.astype(np.int32)) + + self.draw_center_region_maps( + resampled_top_line, + resampled_bot_line, + center_line, + center_region_mask, + top_height_map, + bot_height_map, + sin_map, + cos_map, + self.center_region_shrink_ratio, + ) + + return ( + center_lines, + center_region_mask, + top_height_map, + bot_height_map, + sin_map, + cos_map, + ) + + def generate_rand_comp_attribs(self, num_rand_comps, center_sample_mask): + assert isinstance(num_rand_comps, int) + assert num_rand_comps > 0 + assert center_sample_mask.ndim == 2 + + h, w = center_sample_mask.shape + + max_rand_half_height = self.max_rand_half_height + min_rand_half_height = self.min_rand_half_height + max_rand_height = max_rand_half_height * 2 + max_rand_width = np.clip( + max_rand_height * self.comp_w_h_ratio, self.min_width, self.max_width + ) + margin = ( + int(np.sqrt((max_rand_height / 2) ** 2 + (max_rand_width / 2) ** 2)) + 1 + ) + + if 2 * margin + 1 > min(h, w): + assert min(h, w) > (np.sqrt(2) * (self.min_width + 1)) + max_rand_half_height = max(min(h, w) / 4, self.min_width / 2 + 1) + min_rand_half_height = max(max_rand_half_height / 4, self.min_width / 2) + + max_rand_height = max_rand_half_height * 2 + max_rand_width = np.clip( + max_rand_height * self.comp_w_h_ratio, self.min_width, self.max_width + ) + margin = ( + int(np.sqrt((max_rand_height / 2) ** 2 + (max_rand_width / 2) ** 2)) + 1 + ) + + inner_center_sample_mask = np.zeros_like(center_sample_mask) + inner_center_sample_mask[margin : h - margin, margin : w - margin] = ( + center_sample_mask[margin : h - margin, margin : w - margin] + ) + kernel_size = int(np.clip(max_rand_half_height, 7, 21)) + inner_center_sample_mask = cv2.erode( + inner_center_sample_mask, np.ones((kernel_size, kernel_size), np.uint8) + ) + + center_candidates = np.argwhere(inner_center_sample_mask > 0) + num_center_candidates = len(center_candidates) + sample_inds = np.random.choice(num_center_candidates, num_rand_comps) + rand_centers = center_candidates[sample_inds] + + rand_top_height = np.random.randint( + min_rand_half_height, max_rand_half_height, size=(len(rand_centers), 1) + ) + rand_bot_height = np.random.randint( + min_rand_half_height, max_rand_half_height, size=(len(rand_centers), 1) + ) + + rand_cos = 2 * np.random.random(size=(len(rand_centers), 1)) - 1 + rand_sin = 2 * np.random.random(size=(len(rand_centers), 1)) - 1 + scale = np.sqrt(1.0 / (rand_cos**2 + rand_sin**2 + 1e-8)) + rand_cos = rand_cos * scale + rand_sin = rand_sin * scale + + height = rand_top_height + rand_bot_height + width = np.clip(height * self.comp_w_h_ratio, self.min_width, self.max_width) + + rand_comp_attribs = np.hstack( + [ + rand_centers[:, ::-1], + height, + width, + rand_cos, + rand_sin, + np.zeros_like(rand_sin), + ] + ).astype(np.float32) + + return rand_comp_attribs + + def jitter_comp_attribs(self, comp_attribs, jitter_level): + """Jitter text components attributes. + + Args: + comp_attribs (ndarray): The text component attributes. + jitter_level (float): The jitter level of text components + attributes. + + Returns: + jittered_comp_attribs (ndarray): The jittered text component + attributes (x, y, h, w, cos, sin, comp_label). + """ + + assert comp_attribs.shape[1] == 7 + assert comp_attribs.shape[0] > 0 + assert isinstance(jitter_level, float) + + x = comp_attribs[:, 0].reshape((-1, 1)) + y = comp_attribs[:, 1].reshape((-1, 1)) + h = comp_attribs[:, 2].reshape((-1, 1)) + w = comp_attribs[:, 3].reshape((-1, 1)) + cos = comp_attribs[:, 4].reshape((-1, 1)) + sin = comp_attribs[:, 5].reshape((-1, 1)) + comp_labels = comp_attribs[:, 6].reshape((-1, 1)) + + x += ( + (np.random.random(size=(len(comp_attribs), 1)) - 0.5) + * (h * np.abs(cos) + w * np.abs(sin)) + * jitter_level + ) + y += ( + (np.random.random(size=(len(comp_attribs), 1)) - 0.5) + * (h * np.abs(sin) + w * np.abs(cos)) + * jitter_level + ) + + h += (np.random.random(size=(len(comp_attribs), 1)) - 0.5) * h * jitter_level + w += (np.random.random(size=(len(comp_attribs), 1)) - 0.5) * w * jitter_level + + cos += (np.random.random(size=(len(comp_attribs), 1)) - 0.5) * 2 * jitter_level + sin += (np.random.random(size=(len(comp_attribs), 1)) - 0.5) * 2 * jitter_level + + scale = np.sqrt(1.0 / (cos**2 + sin**2 + 1e-8)) + cos = cos * scale + sin = sin * scale + + jittered_comp_attribs = np.hstack([x, y, h, w, cos, sin, comp_labels]) + + return jittered_comp_attribs + + def generate_comp_attribs( + self, + center_lines, + text_mask, + center_region_mask, + top_height_map, + bot_height_map, + sin_map, + cos_map, + ): + """Generate text component attributes. + + Args: + center_lines (list[ndarray]): The list of text center lines . + text_mask (ndarray): The text region mask. + center_region_mask (ndarray): The text center region mask. + top_height_map (ndarray): The map on which the distance from points + to top side lines will be drawn for each pixel in text center + regions. + bot_height_map (ndarray): The map on which the distance from points + to bottom side lines will be drawn for each pixel in text + center regions. + sin_map (ndarray): The sin(theta) map where theta is the angle + between vector (top point - bottom point) and vector (1, 0). + cos_map (ndarray): The cos(theta) map where theta is the angle + between vector (top point - bottom point) and vector (1, 0). + + Returns: + pad_comp_attribs (ndarray): The padded text component attributes + of a fixed size. + """ + + assert isinstance(center_lines, list) + assert ( + text_mask.shape + == center_region_mask.shape + == top_height_map.shape + == bot_height_map.shape + == sin_map.shape + == cos_map.shape + ) + + center_lines_mask = np.zeros_like(center_region_mask) + cv2.polylines(center_lines_mask, center_lines, 0, 1, 1) + center_lines_mask = center_lines_mask * center_region_mask + comp_centers = np.argwhere(center_lines_mask > 0) + + y = comp_centers[:, 0] + x = comp_centers[:, 1] + + top_height = top_height_map[y, x].reshape((-1, 1)) * self.comp_shrink_ratio + bot_height = bot_height_map[y, x].reshape((-1, 1)) * self.comp_shrink_ratio + sin = sin_map[y, x].reshape((-1, 1)) + cos = cos_map[y, x].reshape((-1, 1)) + + top_mid_points = comp_centers + np.hstack([top_height * sin, top_height * cos]) + bot_mid_points = comp_centers - np.hstack([bot_height * sin, bot_height * cos]) + + width = (top_height + bot_height) * self.comp_w_h_ratio + width = np.clip(width, self.min_width, self.max_width) + r = width / 2 + + tl = top_mid_points[:, ::-1] - np.hstack([-r * sin, r * cos]) + tr = top_mid_points[:, ::-1] + np.hstack([-r * sin, r * cos]) + br = bot_mid_points[:, ::-1] + np.hstack([-r * sin, r * cos]) + bl = bot_mid_points[:, ::-1] - np.hstack([-r * sin, r * cos]) + text_comps = np.hstack([tl, tr, br, bl]).astype(np.float32) + + score = np.ones((text_comps.shape[0], 1), dtype=np.float32) + text_comps = np.hstack([text_comps, score]) + check_install("lanms", "lanms-neo") + from lanms import merge_quadrangle_n9 as la_nms + + text_comps = la_nms(text_comps, self.text_comp_nms_thr) + + if text_comps.shape[0] >= 1: + img_h, img_w = center_region_mask.shape + text_comps[:, 0:8:2] = np.clip(text_comps[:, 0:8:2], 0, img_w - 1) + text_comps[:, 1:8:2] = np.clip(text_comps[:, 1:8:2], 0, img_h - 1) + + comp_centers = np.mean( + text_comps[:, 0:8].reshape((-1, 4, 2)), axis=1 + ).astype(np.int32) + x = comp_centers[:, 0] + y = comp_centers[:, 1] + + height = (top_height_map[y, x] + bot_height_map[y, x]).reshape((-1, 1)) + width = np.clip( + height * self.comp_w_h_ratio, self.min_width, self.max_width + ) + + cos = cos_map[y, x].reshape((-1, 1)) + sin = sin_map[y, x].reshape((-1, 1)) + + _, comp_label_mask = cv2.connectedComponents( + center_region_mask, connectivity=8 + ) + comp_labels = comp_label_mask[y, x].reshape((-1, 1)).astype(np.float32) + + x = x.reshape((-1, 1)).astype(np.float32) + y = y.reshape((-1, 1)).astype(np.float32) + comp_attribs = np.hstack([x, y, height, width, cos, sin, comp_labels]) + comp_attribs = self.jitter_comp_attribs(comp_attribs, self.jitter_level) + + if comp_attribs.shape[0] < self.num_min_comps: + num_rand_comps = self.num_min_comps - comp_attribs.shape[0] + rand_comp_attribs = self.generate_rand_comp_attribs( + num_rand_comps, 1 - text_mask + ) + comp_attribs = np.vstack([comp_attribs, rand_comp_attribs]) + else: + comp_attribs = self.generate_rand_comp_attribs( + self.num_min_comps, 1 - text_mask + ) + + num_comps = ( + np.ones((comp_attribs.shape[0], 1), dtype=np.float32) + * comp_attribs.shape[0] + ) + comp_attribs = np.hstack([num_comps, comp_attribs]) + + if comp_attribs.shape[0] > self.num_max_comps: + comp_attribs = comp_attribs[: self.num_max_comps, :] + comp_attribs[:, 0] = self.num_max_comps + + pad_comp_attribs = np.zeros( + (self.num_max_comps, comp_attribs.shape[1]), dtype=np.float32 + ) + pad_comp_attribs[: comp_attribs.shape[0], :] = comp_attribs + + return pad_comp_attribs + + def generate_text_region_mask(self, img_size, text_polys): + """Generate text center region mask and geometry attribute maps. + + Args: + img_size (tuple): The image size (height, width). + text_polys (list[list[ndarray]]): The list of text polygons. + + Returns: + text_region_mask (ndarray): The text region mask. + """ + + assert isinstance(img_size, tuple) + + h, w = img_size + text_region_mask = np.zeros((h, w), dtype=np.uint8) + + for poly in text_polys: + polygon = np.array(poly, dtype=np.int32).reshape((1, -1, 2)) + cv2.fillPoly(text_region_mask, polygon, 1) + + return text_region_mask + + def generate_effective_mask(self, mask_size: tuple, polygons_ignore): + """Generate effective mask by setting the ineffective regions to 0 and + effective regions to 1. + + Args: + mask_size (tuple): The mask size. + polygons_ignore (list[[ndarray]]: The list of ignored text + polygons. + + Returns: + mask (ndarray): The effective mask of (height, width). + """ + mask = np.ones(mask_size, dtype=np.uint8) + + for poly in polygons_ignore: + instance = poly.astype(np.int32).reshape(1, -1, 2) + cv2.fillPoly(mask, instance, 0) + + return mask + + def generate_targets(self, data): + """Generate the gt targets for DRRG. + + Args: + data (dict): The input result dictionary. + + Returns: + data (dict): The output result dictionary. + """ + + assert isinstance(data, dict) + + image = data["image"] + polygons = data["polys"] + ignore_tags = data["ignore_tags"] + h, w, _ = image.shape + + polygon_masks = [] + polygon_masks_ignore = [] + for tag, polygon in zip(ignore_tags, polygons): + if tag is True: + polygon_masks_ignore.append(polygon) + else: + polygon_masks.append(polygon) + + gt_text_mask = self.generate_text_region_mask((h, w), polygon_masks) + gt_mask = self.generate_effective_mask((h, w), polygon_masks_ignore) + ( + center_lines, + gt_center_region_mask, + gt_top_height_map, + gt_bot_height_map, + gt_sin_map, + gt_cos_map, + ) = self.generate_center_mask_attrib_maps((h, w), polygon_masks) + + gt_comp_attribs = self.generate_comp_attribs( + center_lines, + gt_text_mask, + gt_center_region_mask, + gt_top_height_map, + gt_bot_height_map, + gt_sin_map, + gt_cos_map, + ) + + mapping = { + "gt_text_mask": gt_text_mask, + "gt_center_region_mask": gt_center_region_mask, + "gt_mask": gt_mask, + "gt_top_height_map": gt_top_height_map, + "gt_bot_height_map": gt_bot_height_map, + "gt_sin_map": gt_sin_map, + "gt_cos_map": gt_cos_map, + } + + data.update(mapping) + data["gt_comp_attribs"] = gt_comp_attribs + return data + + def __call__(self, data): + data = self.generate_targets(data) + return data diff --git a/modules/onnx_ocr_module/src/ppocr/data/imaug/east_process.py b/modules/onnx_ocr_module/src/ppocr/data/imaug/east_process.py new file mode 100644 index 0000000..7e5bfa0 --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/data/imaug/east_process.py @@ -0,0 +1,446 @@ +# copyright (c) 2020 PaddlePaddle Authors. All Rights Reserve. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +""" +This code is referred from: +https://github.com/songdejia/EAST/blob/master/data_utils.py +""" +import math +import cv2 +import numpy as np +import json +import sys +import os + +__all__ = ["EASTProcessTrain"] + + +class EASTProcessTrain(object): + def __init__( + self, + image_shape=[512, 512], + background_ratio=0.125, + min_crop_side_ratio=0.1, + min_text_size=10, + **kwargs, + ): + self.input_size = image_shape[1] + self.random_scale = np.array([0.5, 1, 2.0, 3.0]) + self.background_ratio = background_ratio + self.min_crop_side_ratio = min_crop_side_ratio + self.min_text_size = min_text_size + + def preprocess(self, im): + input_size = self.input_size + im_shape = im.shape + im_size_min = np.min(im_shape[0:2]) + im_size_max = np.max(im_shape[0:2]) + im_scale = float(input_size) / float(im_size_max) + im = cv2.resize(im, None, None, fx=im_scale, fy=im_scale) + img_mean = [0.485, 0.456, 0.406] + img_std = [0.229, 0.224, 0.225] + # im = im[:, :, ::-1].astype(np.float32) + im = im / 255 + im -= img_mean + im /= img_std + new_h, new_w, _ = im.shape + im_padded = np.zeros((input_size, input_size, 3), dtype=np.float32) + im_padded[:new_h, :new_w, :] = im + im_padded = im_padded.transpose((2, 0, 1)) + im_padded = im_padded[np.newaxis, :] + return im_padded, im_scale + + def rotate_im_poly(self, im, text_polys): + """ + rotate image with 90 / 180 / 270 degre + """ + im_w, im_h = im.shape[1], im.shape[0] + dst_im = im.copy() + dst_polys = [] + rand_degree_ratio = np.random.rand() + rand_degree_cnt = 1 + if 0.333 < rand_degree_ratio < 0.666: + rand_degree_cnt = 2 + elif rand_degree_ratio > 0.666: + rand_degree_cnt = 3 + for i in range(rand_degree_cnt): + dst_im = np.rot90(dst_im) + rot_degree = -90 * rand_degree_cnt + rot_angle = rot_degree * math.pi / 180.0 + n_poly = text_polys.shape[0] + cx, cy = 0.5 * im_w, 0.5 * im_h + ncx, ncy = 0.5 * dst_im.shape[1], 0.5 * dst_im.shape[0] + for i in range(n_poly): + wordBB = text_polys[i] + poly = [] + for j in range(4): + sx, sy = wordBB[j][0], wordBB[j][1] + dx = ( + math.cos(rot_angle) * (sx - cx) + - math.sin(rot_angle) * (sy - cy) + + ncx + ) + dy = ( + math.sin(rot_angle) * (sx - cx) + + math.cos(rot_angle) * (sy - cy) + + ncy + ) + poly.append([dx, dy]) + dst_polys.append(poly) + dst_polys = np.array(dst_polys, dtype=np.float32) + return dst_im, dst_polys + + def polygon_area(self, poly): + """ + compute area of a polygon + :param poly: + :return: + """ + edge = [ + (poly[1][0] - poly[0][0]) * (poly[1][1] + poly[0][1]), + (poly[2][0] - poly[1][0]) * (poly[2][1] + poly[1][1]), + (poly[3][0] - poly[2][0]) * (poly[3][1] + poly[2][1]), + (poly[0][0] - poly[3][0]) * (poly[0][1] + poly[3][1]), + ] + return np.sum(edge) / 2.0 + + def check_and_validate_polys(self, polys, tags, img_height, img_width): + """ + check so that the text poly is in the same direction, + and also filter some invalid polygons + :param polys: + :param tags: + :return: + """ + h, w = img_height, img_width + if polys.shape[0] == 0: + return polys + polys[:, :, 0] = np.clip(polys[:, :, 0], 0, w - 1) + polys[:, :, 1] = np.clip(polys[:, :, 1], 0, h - 1) + + validated_polys = [] + validated_tags = [] + for poly, tag in zip(polys, tags): + p_area = self.polygon_area(poly) + # invalid poly + if abs(p_area) < 1: + continue + if p_area > 0: + #'poly in wrong direction' + if not tag: + tag = True # reversed cases should be ignore + poly = poly[(0, 3, 2, 1), :] + validated_polys.append(poly) + validated_tags.append(tag) + return np.array(validated_polys), np.array(validated_tags) + + def draw_img_polys(self, img, polys): + if len(img.shape) == 4: + img = np.squeeze(img, axis=0) + if img.shape[0] == 3: + img = img.transpose((1, 2, 0)) + img[:, :, 2] += 123.68 + img[:, :, 1] += 116.78 + img[:, :, 0] += 103.94 + cv2.imwrite("tmp.jpg", img) + img = cv2.imread("tmp.jpg") + for box in polys: + box = box.astype(np.int32).reshape((-1, 1, 2)) + cv2.polylines(img, [box], True, color=(255, 255, 0), thickness=2) + import random + + ino = random.randint(0, 100) + cv2.imwrite("tmp_%d.jpg" % ino, img) + return + + def shrink_poly(self, poly, r): + """ + fit a poly inside the origin poly, maybe bugs here... + used for generate the score map + :param poly: the text poly + :param r: r in the paper + :return: the shrunk poly + """ + # shrink ratio + R = 0.3 + # find the longer pair + dist0 = np.linalg.norm(poly[0] - poly[1]) + dist1 = np.linalg.norm(poly[2] - poly[3]) + dist2 = np.linalg.norm(poly[0] - poly[3]) + dist3 = np.linalg.norm(poly[1] - poly[2]) + if dist0 + dist1 > dist2 + dist3: + # first move (p0, p1), (p2, p3), then (p0, p3), (p1, p2) + ## p0, p1 + theta = np.arctan2((poly[1][1] - poly[0][1]), (poly[1][0] - poly[0][0])) + poly[0][0] += R * r[0] * np.cos(theta) + poly[0][1] += R * r[0] * np.sin(theta) + poly[1][0] -= R * r[1] * np.cos(theta) + poly[1][1] -= R * r[1] * np.sin(theta) + ## p2, p3 + theta = np.arctan2((poly[2][1] - poly[3][1]), (poly[2][0] - poly[3][0])) + poly[3][0] += R * r[3] * np.cos(theta) + poly[3][1] += R * r[3] * np.sin(theta) + poly[2][0] -= R * r[2] * np.cos(theta) + poly[2][1] -= R * r[2] * np.sin(theta) + ## p0, p3 + theta = np.arctan2((poly[3][0] - poly[0][0]), (poly[3][1] - poly[0][1])) + poly[0][0] += R * r[0] * np.sin(theta) + poly[0][1] += R * r[0] * np.cos(theta) + poly[3][0] -= R * r[3] * np.sin(theta) + poly[3][1] -= R * r[3] * np.cos(theta) + ## p1, p2 + theta = np.arctan2((poly[2][0] - poly[1][0]), (poly[2][1] - poly[1][1])) + poly[1][0] += R * r[1] * np.sin(theta) + poly[1][1] += R * r[1] * np.cos(theta) + poly[2][0] -= R * r[2] * np.sin(theta) + poly[2][1] -= R * r[2] * np.cos(theta) + else: + ## p0, p3 + # print poly + theta = np.arctan2((poly[3][0] - poly[0][0]), (poly[3][1] - poly[0][1])) + poly[0][0] += R * r[0] * np.sin(theta) + poly[0][1] += R * r[0] * np.cos(theta) + poly[3][0] -= R * r[3] * np.sin(theta) + poly[3][1] -= R * r[3] * np.cos(theta) + ## p1, p2 + theta = np.arctan2((poly[2][0] - poly[1][0]), (poly[2][1] - poly[1][1])) + poly[1][0] += R * r[1] * np.sin(theta) + poly[1][1] += R * r[1] * np.cos(theta) + poly[2][0] -= R * r[2] * np.sin(theta) + poly[2][1] -= R * r[2] * np.cos(theta) + ## p0, p1 + theta = np.arctan2((poly[1][1] - poly[0][1]), (poly[1][0] - poly[0][0])) + poly[0][0] += R * r[0] * np.cos(theta) + poly[0][1] += R * r[0] * np.sin(theta) + poly[1][0] -= R * r[1] * np.cos(theta) + poly[1][1] -= R * r[1] * np.sin(theta) + ## p2, p3 + theta = np.arctan2((poly[2][1] - poly[3][1]), (poly[2][0] - poly[3][0])) + poly[3][0] += R * r[3] * np.cos(theta) + poly[3][1] += R * r[3] * np.sin(theta) + poly[2][0] -= R * r[2] * np.cos(theta) + poly[2][1] -= R * r[2] * np.sin(theta) + return poly + + def generate_quad(self, im_size, polys, tags): + """ + Generate quadrangle. + """ + h, w = im_size + poly_mask = np.zeros((h, w), dtype=np.uint8) + score_map = np.zeros((h, w), dtype=np.uint8) + # (x1, y1, ..., x4, y4, short_edge_norm) + geo_map = np.zeros((h, w, 9), dtype=np.float32) + # mask used during training, to ignore some hard areas + training_mask = np.ones((h, w), dtype=np.uint8) + for poly_idx, poly_tag in enumerate(zip(polys, tags)): + poly = poly_tag[0] + tag = poly_tag[1] + + r = [None, None, None, None] + for i in range(4): + dist1 = np.linalg.norm(poly[i] - poly[(i + 1) % 4]) + dist2 = np.linalg.norm(poly[i] - poly[(i - 1) % 4]) + r[i] = min(dist1, dist2) + # score map + shrinked_poly = self.shrink_poly(poly.copy(), r).astype(np.int32)[ + np.newaxis, :, : + ] + cv2.fillPoly(score_map, shrinked_poly, 1) + cv2.fillPoly(poly_mask, shrinked_poly, poly_idx + 1) + # if the poly is too small, then ignore it during training + poly_h = min( + np.linalg.norm(poly[0] - poly[3]), np.linalg.norm(poly[1] - poly[2]) + ) + poly_w = min( + np.linalg.norm(poly[0] - poly[1]), np.linalg.norm(poly[2] - poly[3]) + ) + if min(poly_h, poly_w) < self.min_text_size: + cv2.fillPoly(training_mask, poly.astype(np.int32)[np.newaxis, :, :], 0) + + if tag: + cv2.fillPoly(training_mask, poly.astype(np.int32)[np.newaxis, :, :], 0) + + xy_in_poly = np.argwhere(poly_mask == (poly_idx + 1)) + # geo map. + y_in_poly = xy_in_poly[:, 0] + x_in_poly = xy_in_poly[:, 1] + poly[:, 0] = np.minimum(np.maximum(poly[:, 0], 0), w) + poly[:, 1] = np.minimum(np.maximum(poly[:, 1], 0), h) + for pno in range(4): + geo_channel_beg = pno * 2 + geo_map[y_in_poly, x_in_poly, geo_channel_beg] = ( + x_in_poly - poly[pno, 0] + ) + geo_map[y_in_poly, x_in_poly, geo_channel_beg + 1] = ( + y_in_poly - poly[pno, 1] + ) + geo_map[y_in_poly, x_in_poly, 8] = 1.0 / max(min(poly_h, poly_w), 1.0) + return score_map, geo_map, training_mask + + def crop_area(self, im, polys, tags, crop_background=False, max_tries=50): + """ + make random crop from the input image + :param im: + :param polys: + :param tags: + :param crop_background: + :param max_tries: + :return: + """ + h, w, _ = im.shape + pad_h = h // 10 + pad_w = w // 10 + h_array = np.zeros((h + pad_h * 2), dtype=np.int32) + w_array = np.zeros((w + pad_w * 2), dtype=np.int32) + for poly in polys: + poly = np.round(poly, decimals=0).astype(np.int32) + minx = np.min(poly[:, 0]) + maxx = np.max(poly[:, 0]) + w_array[minx + pad_w : maxx + pad_w] = 1 + miny = np.min(poly[:, 1]) + maxy = np.max(poly[:, 1]) + h_array[miny + pad_h : maxy + pad_h] = 1 + # ensure the cropped area not across a text + h_axis = np.where(h_array == 0)[0] + w_axis = np.where(w_array == 0)[0] + if len(h_axis) == 0 or len(w_axis) == 0: + return im, polys, tags + + for i in range(max_tries): + xx = np.random.choice(w_axis, size=2) + xmin = np.min(xx) - pad_w + xmax = np.max(xx) - pad_w + xmin = np.clip(xmin, 0, w - 1) + xmax = np.clip(xmax, 0, w - 1) + yy = np.random.choice(h_axis, size=2) + ymin = np.min(yy) - pad_h + ymax = np.max(yy) - pad_h + ymin = np.clip(ymin, 0, h - 1) + ymax = np.clip(ymax, 0, h - 1) + if ( + xmax - xmin < self.min_crop_side_ratio * w + or ymax - ymin < self.min_crop_side_ratio * h + ): + # area too small + continue + if polys.shape[0] != 0: + poly_axis_in_area = ( + (polys[:, :, 0] >= xmin) + & (polys[:, :, 0] <= xmax) + & (polys[:, :, 1] >= ymin) + & (polys[:, :, 1] <= ymax) + ) + selected_polys = np.where(np.sum(poly_axis_in_area, axis=1) == 4)[0] + else: + selected_polys = [] + + if len(selected_polys) == 0: + # no text in this area + if crop_background: + im = im[ymin : ymax + 1, xmin : xmax + 1, :] + polys = [] + tags = [] + return im, polys, tags + else: + continue + + im = im[ymin : ymax + 1, xmin : xmax + 1, :] + polys = polys[selected_polys] + tags = tags[selected_polys] + polys[:, :, 0] -= xmin + polys[:, :, 1] -= ymin + return im, polys, tags + return im, polys, tags + + def crop_background_infor(self, im, text_polys, text_tags): + im, text_polys, text_tags = self.crop_area( + im, text_polys, text_tags, crop_background=True + ) + + if len(text_polys) > 0: + return None + # pad and resize image + input_size = self.input_size + im, ratio = self.preprocess(im) + score_map = np.zeros((input_size, input_size), dtype=np.float32) + geo_map = np.zeros((input_size, input_size, 9), dtype=np.float32) + training_mask = np.ones((input_size, input_size), dtype=np.float32) + return im, score_map, geo_map, training_mask + + def crop_foreground_infor(self, im, text_polys, text_tags): + im, text_polys, text_tags = self.crop_area( + im, text_polys, text_tags, crop_background=False + ) + + if text_polys.shape[0] == 0: + return None + # continue for all ignore case + if np.sum((text_tags * 1.0)) >= text_tags.size: + return None + # pad and resize image + input_size = self.input_size + im, ratio = self.preprocess(im) + text_polys[:, :, 0] *= ratio + text_polys[:, :, 1] *= ratio + _, _, new_h, new_w = im.shape + # print(im.shape) + # self.draw_img_polys(im, text_polys) + score_map, geo_map, training_mask = self.generate_quad( + (new_h, new_w), text_polys, text_tags + ) + return im, score_map, geo_map, training_mask + + def __call__(self, data): + im = data["image"] + text_polys = data["polys"] + text_tags = data["ignore_tags"] + if im is None: + return None + if text_polys.shape[0] == 0: + return None + + # add rotate cases + if np.random.rand() < 0.5: + im, text_polys = self.rotate_im_poly(im, text_polys) + h, w, _ = im.shape + text_polys, text_tags = self.check_and_validate_polys( + text_polys, text_tags, h, w + ) + if text_polys.shape[0] == 0: + return None + + # random scale this image + rd_scale = np.random.choice(self.random_scale) + im = cv2.resize(im, dsize=None, fx=rd_scale, fy=rd_scale) + text_polys *= rd_scale + if np.random.rand() < self.background_ratio: + outs = self.crop_background_infor(im, text_polys, text_tags) + else: + outs = self.crop_foreground_infor(im, text_polys, text_tags) + + if outs is None: + return None + im, score_map, geo_map, training_mask = outs + score_map = score_map[np.newaxis, ::4, ::4].astype(np.float32) + geo_map = np.swapaxes(geo_map, 1, 2) + geo_map = np.swapaxes(geo_map, 1, 0) + geo_map = geo_map[:, ::4, ::4].astype(np.float32) + training_mask = training_mask[np.newaxis, ::4, ::4] + training_mask = training_mask.astype(np.float32) + + data["image"] = im[0] + data["score_map"] = score_map + data["geo_map"] = geo_map + data["training_mask"] = training_mask + return data diff --git a/modules/onnx_ocr_module/src/ppocr/data/imaug/fce_aug.py b/modules/onnx_ocr_module/src/ppocr/data/imaug/fce_aug.py new file mode 100644 index 0000000..2697344 --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/data/imaug/fce_aug.py @@ -0,0 +1,575 @@ +# copyright (c) 2022 PaddlePaddle Authors. All Rights Reserve. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +""" +This code is refer from: +https://github.com/open-mmlab/mmocr/blob/main/mmocr/datasets/pipelines/transforms.py +""" +import numpy as np +from PIL import Image, ImageDraw +import cv2 +from shapely.geometry import Polygon +import math +from ppocr.utils.poly_nms import poly_intersection + + +class RandomScaling: + def __init__(self, size=800, scale=(3.0 / 4, 5.0 / 2), **kwargs): + """Random scale the image while keeping aspect. + + Args: + size (int) : Base size before scaling. + scale (tuple(float)) : The range of scaling. + """ + assert isinstance(size, int) + assert isinstance(scale, float) or isinstance(scale, tuple) + self.size = size + self.scale = scale if isinstance(scale, tuple) else (1 - scale, 1 + scale) + + def __call__(self, data): + image = data["image"] + text_polys = data["polys"] + h, w, _ = image.shape + + aspect_ratio = np.random.uniform(min(self.scale), max(self.scale)) + scales = self.size * 1.0 / max(h, w) * aspect_ratio + scales = np.array([scales, scales]) + out_size = (int(h * scales[1]), int(w * scales[0])) + image = cv2.resize(image, out_size[::-1]) + + data["image"] = image + text_polys[:, :, 0::2] = text_polys[:, :, 0::2] * scales[1] + text_polys[:, :, 1::2] = text_polys[:, :, 1::2] * scales[0] + data["polys"] = text_polys + + return data + + +class RandomCropFlip: + def __init__( + self, pad_ratio=0.1, crop_ratio=0.5, iter_num=1, min_area_ratio=0.2, **kwargs + ): + """Random crop and flip a patch of the image. + + Args: + crop_ratio (float): The ratio of cropping. + iter_num (int): Number of operations. + min_area_ratio (float): Minimal area ratio between cropped patch + and original image. + """ + assert isinstance(crop_ratio, float) + assert isinstance(iter_num, int) + assert isinstance(min_area_ratio, float) + + self.pad_ratio = pad_ratio + self.epsilon = 1e-2 + self.crop_ratio = crop_ratio + self.iter_num = iter_num + self.min_area_ratio = min_area_ratio + + def __call__(self, results): + for i in range(self.iter_num): + results = self.random_crop_flip(results) + + return results + + def random_crop_flip(self, results): + image = results["image"] + polygons = results["polys"] + ignore_tags = results["ignore_tags"] + if len(polygons) == 0: + return results + + if np.random.random() >= self.crop_ratio: + return results + + h, w, _ = image.shape + area = h * w + pad_h = int(h * self.pad_ratio) + pad_w = int(w * self.pad_ratio) + h_axis, w_axis = self.generate_crop_target(image, polygons, pad_h, pad_w) + if len(h_axis) == 0 or len(w_axis) == 0: + return results + + attempt = 0 + while attempt < 50: + attempt += 1 + polys_keep = [] + polys_new = [] + ignore_tags_keep = [] + ignore_tags_new = [] + xx = np.random.choice(w_axis, size=2) + xmin = np.min(xx) - pad_w + xmax = np.max(xx) - pad_w + xmin = np.clip(xmin, 0, w - 1) + xmax = np.clip(xmax, 0, w - 1) + yy = np.random.choice(h_axis, size=2) + ymin = np.min(yy) - pad_h + ymax = np.max(yy) - pad_h + ymin = np.clip(ymin, 0, h - 1) + ymax = np.clip(ymax, 0, h - 1) + if (xmax - xmin) * (ymax - ymin) < area * self.min_area_ratio: + # area too small + continue + + pts = np.stack( + [[xmin, xmax, xmax, xmin], [ymin, ymin, ymax, ymax]] + ).T.astype(np.int32) + pp = Polygon(pts) + fail_flag = False + for polygon, ignore_tag in zip(polygons, ignore_tags): + ppi = Polygon(polygon.reshape(-1, 2)) + ppiou, _ = poly_intersection(ppi, pp, buffer=0) + if ( + np.abs(ppiou - float(ppi.area)) > self.epsilon + and np.abs(ppiou) > self.epsilon + ): + fail_flag = True + break + elif np.abs(ppiou - float(ppi.area)) < self.epsilon: + polys_new.append(polygon) + ignore_tags_new.append(ignore_tag) + else: + polys_keep.append(polygon) + ignore_tags_keep.append(ignore_tag) + + if fail_flag: + continue + else: + break + + cropped = image[ymin:ymax, xmin:xmax, :] + select_type = np.random.randint(3) + if select_type == 0: + img = np.ascontiguousarray(cropped[:, ::-1]) + elif select_type == 1: + img = np.ascontiguousarray(cropped[::-1, :]) + else: + img = np.ascontiguousarray(cropped[::-1, ::-1]) + image[ymin:ymax, xmin:xmax, :] = img + results["img"] = image + + if len(polys_new) != 0: + height, width, _ = cropped.shape + if select_type == 0: + for idx, polygon in enumerate(polys_new): + poly = polygon.reshape(-1, 2) + poly[:, 0] = width - poly[:, 0] + 2 * xmin + polys_new[idx] = poly + elif select_type == 1: + for idx, polygon in enumerate(polys_new): + poly = polygon.reshape(-1, 2) + poly[:, 1] = height - poly[:, 1] + 2 * ymin + polys_new[idx] = poly + else: + for idx, polygon in enumerate(polys_new): + poly = polygon.reshape(-1, 2) + poly[:, 0] = width - poly[:, 0] + 2 * xmin + poly[:, 1] = height - poly[:, 1] + 2 * ymin + polys_new[idx] = poly + polygons = polys_keep + polys_new + ignore_tags = ignore_tags_keep + ignore_tags_new + results["polys"] = np.array(polygons) + results["ignore_tags"] = ignore_tags + + return results + + def generate_crop_target(self, image, all_polys, pad_h, pad_w): + """Generate crop target and make sure not to crop the polygon + instances. + + Args: + image (ndarray): The image waited to be crop. + all_polys (list[list[ndarray]]): All polygons including ground + truth polygons and ground truth ignored polygons. + pad_h (int): Padding length of height. + pad_w (int): Padding length of width. + Returns: + h_axis (ndarray): Vertical cropping range. + w_axis (ndarray): Horizontal cropping range. + """ + h, w, _ = image.shape + h_array = np.zeros((h + pad_h * 2), dtype=np.int32) + w_array = np.zeros((w + pad_w * 2), dtype=np.int32) + + text_polys = [] + for polygon in all_polys: + rect = cv2.minAreaRect(polygon.astype(np.int32).reshape(-1, 2)) + box = cv2.boxPoints(rect) + box = np.int64(box) + text_polys.append([box[0], box[1], box[2], box[3]]) + + polys = np.array(text_polys, dtype=np.int32) + for poly in polys: + poly = np.round(poly, decimals=0).astype(np.int32) + minx = np.min(poly[:, 0]) + maxx = np.max(poly[:, 0]) + w_array[minx + pad_w : maxx + pad_w] = 1 + miny = np.min(poly[:, 1]) + maxy = np.max(poly[:, 1]) + h_array[miny + pad_h : maxy + pad_h] = 1 + + h_axis = np.where(h_array == 0)[0] + w_axis = np.where(w_array == 0)[0] + return h_axis, w_axis + + +class RandomCropPolyInstances: + """Randomly crop images and make sure to contain at least one intact + instance.""" + + def __init__(self, crop_ratio=5.0 / 8.0, min_side_ratio=0.4, **kwargs): + super().__init__() + self.crop_ratio = crop_ratio + self.min_side_ratio = min_side_ratio + + def sample_valid_start_end(self, valid_array, min_len, max_start, min_end): + assert isinstance(min_len, int) + assert len(valid_array) > min_len + + start_array = valid_array.copy() + max_start = min(len(start_array) - min_len, max_start) + start_array[max_start:] = 0 + start_array[0] = 1 + diff_array = np.hstack([0, start_array]) - np.hstack([start_array, 0]) + region_starts = np.where(diff_array < 0)[0] + region_ends = np.where(diff_array > 0)[0] + region_ind = np.random.randint(0, len(region_starts)) + start = np.random.randint(region_starts[region_ind], region_ends[region_ind]) + + end_array = valid_array.copy() + min_end = max(start + min_len, min_end) + end_array[:min_end] = 0 + end_array[-1] = 1 + diff_array = np.hstack([0, end_array]) - np.hstack([end_array, 0]) + region_starts = np.where(diff_array < 0)[0] + region_ends = np.where(diff_array > 0)[0] + region_ind = np.random.randint(0, len(region_starts)) + end = np.random.randint(region_starts[region_ind], region_ends[region_ind]) + return start, end + + def sample_crop_box(self, img_size, results): + """Generate crop box and make sure not to crop the polygon instances. + + Args: + img_size (tuple(int)): The image size (h, w). + results (dict): The results dict. + """ + + assert isinstance(img_size, tuple) + h, w = img_size[:2] + + key_masks = results["polys"] + + x_valid_array = np.ones(w, dtype=np.int32) + y_valid_array = np.ones(h, dtype=np.int32) + + selected_mask = key_masks[np.random.randint(0, len(key_masks))] + selected_mask = selected_mask.reshape((-1, 2)).astype(np.int32) + max_x_start = max(np.min(selected_mask[:, 0]) - 2, 0) + min_x_end = min(np.max(selected_mask[:, 0]) + 3, w - 1) + max_y_start = max(np.min(selected_mask[:, 1]) - 2, 0) + min_y_end = min(np.max(selected_mask[:, 1]) + 3, h - 1) + + for mask in key_masks: + mask = mask.reshape((-1, 2)).astype(np.int32) + clip_x = np.clip(mask[:, 0], 0, w - 1) + clip_y = np.clip(mask[:, 1], 0, h - 1) + min_x, max_x = np.min(clip_x), np.max(clip_x) + min_y, max_y = np.min(clip_y), np.max(clip_y) + + x_valid_array[min_x - 2 : max_x + 3] = 0 + y_valid_array[min_y - 2 : max_y + 3] = 0 + + min_w = int(w * self.min_side_ratio) + min_h = int(h * self.min_side_ratio) + + x1, x2 = self.sample_valid_start_end( + x_valid_array, min_w, max_x_start, min_x_end + ) + y1, y2 = self.sample_valid_start_end( + y_valid_array, min_h, max_y_start, min_y_end + ) + + return np.array([x1, y1, x2, y2]) + + def crop_img(self, img, bbox): + assert img.ndim == 3 + h, w, _ = img.shape + assert 0 <= bbox[1] < bbox[3] <= h + assert 0 <= bbox[0] < bbox[2] <= w + return img[bbox[1] : bbox[3], bbox[0] : bbox[2]] + + def __call__(self, results): + image = results["image"] + polygons = results["polys"] + ignore_tags = results["ignore_tags"] + if len(polygons) < 1: + return results + + if np.random.random_sample() < self.crop_ratio: + crop_box = self.sample_crop_box(image.shape, results) + img = self.crop_img(image, crop_box) + results["image"] = img + # crop and filter masks + x1, y1, x2, y2 = crop_box + w = max(x2 - x1, 1) + h = max(y2 - y1, 1) + polygons[:, :, 0::2] = polygons[:, :, 0::2] - x1 + polygons[:, :, 1::2] = polygons[:, :, 1::2] - y1 + + valid_masks_list = [] + valid_tags_list = [] + for ind, polygon in enumerate(polygons): + if ( + (polygon[:, ::2] > -4).all() + and (polygon[:, ::2] < w + 4).all() + and (polygon[:, 1::2] > -4).all() + and (polygon[:, 1::2] < h + 4).all() + ): + polygon[:, ::2] = np.clip(polygon[:, ::2], 0, w) + polygon[:, 1::2] = np.clip(polygon[:, 1::2], 0, h) + valid_masks_list.append(polygon) + valid_tags_list.append(ignore_tags[ind]) + + results["polys"] = np.array(valid_masks_list) + results["ignore_tags"] = valid_tags_list + + return results + + def __repr__(self): + repr_str = self.__class__.__name__ + return repr_str + + +class RandomRotatePolyInstances: + def __init__( + self, + rotate_ratio=0.5, + max_angle=10, + pad_with_fixed_color=False, + pad_value=(0, 0, 0), + **kwargs, + ): + """Randomly rotate images and polygon masks. + + Args: + rotate_ratio (float): The ratio of samples to operate rotation. + max_angle (int): The maximum rotation angle. + pad_with_fixed_color (bool): The flag for whether to pad rotated + image with fixed value. If set to False, the rotated image will + be padded onto cropped image. + pad_value (tuple(int)): The color value for padding rotated image. + """ + self.rotate_ratio = rotate_ratio + self.max_angle = max_angle + self.pad_with_fixed_color = pad_with_fixed_color + self.pad_value = pad_value + + def rotate(self, center, points, theta, center_shift=(0, 0)): + # rotate points. + (center_x, center_y) = center + center_y = -center_y + x, y = points[:, ::2], points[:, 1::2] + y = -y + + theta = theta / 180 * math.pi + cos = math.cos(theta) + sin = math.sin(theta) + + x = x - center_x + y = y - center_y + + _x = center_x + x * cos - y * sin + center_shift[0] + _y = -(center_y + x * sin + y * cos) + center_shift[1] + + points[:, ::2], points[:, 1::2] = _x, _y + return points + + def cal_canvas_size(self, ori_size, degree): + assert isinstance(ori_size, tuple) + angle = degree * math.pi / 180.0 + h, w = ori_size[:2] + + cos = math.cos(angle) + sin = math.sin(angle) + canvas_h = int(w * math.fabs(sin) + h * math.fabs(cos)) + canvas_w = int(w * math.fabs(cos) + h * math.fabs(sin)) + + canvas_size = (canvas_h, canvas_w) + return canvas_size + + def sample_angle(self, max_angle): + angle = np.random.random_sample() * 2 * max_angle - max_angle + return angle + + def rotate_img(self, img, angle, canvas_size): + h, w = img.shape[:2] + rotation_matrix = cv2.getRotationMatrix2D((w / 2, h / 2), angle, 1) + rotation_matrix[0, 2] += int((canvas_size[1] - w) / 2) + rotation_matrix[1, 2] += int((canvas_size[0] - h) / 2) + + if self.pad_with_fixed_color: + target_img = cv2.warpAffine( + img, + rotation_matrix, + (canvas_size[1], canvas_size[0]), + flags=cv2.INTER_NEAREST, + borderValue=self.pad_value, + ) + else: + mask = np.zeros_like(img) + (h_ind, w_ind) = ( + np.random.randint(0, h * 7 // 8), + np.random.randint(0, w * 7 // 8), + ) + img_cut = img[h_ind : (h_ind + h // 9), w_ind : (w_ind + w // 9)] + img_cut = cv2.resize(img_cut, (canvas_size[1], canvas_size[0])) + + mask = cv2.warpAffine( + mask, + rotation_matrix, + (canvas_size[1], canvas_size[0]), + borderValue=[1, 1, 1], + ) + target_img = cv2.warpAffine( + img, + rotation_matrix, + (canvas_size[1], canvas_size[0]), + borderValue=[0, 0, 0], + ) + target_img = target_img + img_cut * mask + + return target_img + + def __call__(self, results): + if np.random.random_sample() < self.rotate_ratio: + image = results["image"] + polygons = results["polys"] + h, w = image.shape[:2] + + angle = self.sample_angle(self.max_angle) + canvas_size = self.cal_canvas_size((h, w), angle) + center_shift = ( + int((canvas_size[1] - w) / 2), + int((canvas_size[0] - h) / 2), + ) + image = self.rotate_img(image, angle, canvas_size) + results["image"] = image + # rotate polygons + rotated_masks = [] + for mask in polygons: + rotated_mask = self.rotate((w / 2, h / 2), mask, angle, center_shift) + rotated_masks.append(rotated_mask) + results["polys"] = np.array(rotated_masks) + + return results + + def __repr__(self): + repr_str = self.__class__.__name__ + return repr_str + + +class SquareResizePad: + def __init__( + self, + target_size, + pad_ratio=0.6, + pad_with_fixed_color=False, + pad_value=(0, 0, 0), + **kwargs, + ): + """Resize or pad images to be square shape. + + Args: + target_size (int): The target size of square shaped image. + pad_with_fixed_color (bool): The flag for whether to pad rotated + image with fixed value. If set to False, the rescales image will + be padded onto cropped image. + pad_value (tuple(int)): The color value for padding rotated image. + """ + assert isinstance(target_size, int) + assert isinstance(pad_ratio, float) + assert isinstance(pad_with_fixed_color, bool) + assert isinstance(pad_value, tuple) + + self.target_size = target_size + self.pad_ratio = pad_ratio + self.pad_with_fixed_color = pad_with_fixed_color + self.pad_value = pad_value + + def resize_img(self, img, keep_ratio=True): + h, w, _ = img.shape + if keep_ratio: + t_h = self.target_size if h >= w else int(h * self.target_size / w) + t_w = self.target_size if h <= w else int(w * self.target_size / h) + else: + t_h = t_w = self.target_size + img = cv2.resize(img, (t_w, t_h)) + return img, (t_h, t_w) + + def square_pad(self, img): + h, w = img.shape[:2] + if h == w: + return img, (0, 0) + pad_size = max(h, w) + if self.pad_with_fixed_color: + expand_img = np.ones((pad_size, pad_size, 3), dtype=np.uint8) + expand_img[:] = self.pad_value + else: + (h_ind, w_ind) = ( + np.random.randint(0, h * 7 // 8), + np.random.randint(0, w * 7 // 8), + ) + img_cut = img[h_ind : (h_ind + h // 9), w_ind : (w_ind + w // 9)] + expand_img = cv2.resize(img_cut, (pad_size, pad_size)) + if h > w: + y0, x0 = 0, (h - w) // 2 + else: + y0, x0 = (w - h) // 2, 0 + expand_img[y0 : y0 + h, x0 : x0 + w] = img + offset = (x0, y0) + + return expand_img, offset + + def square_pad_mask(self, points, offset): + x0, y0 = offset + pad_points = points.copy() + pad_points[::2] = pad_points[::2] + x0 + pad_points[1::2] = pad_points[1::2] + y0 + return pad_points + + def __call__(self, results): + image = results["image"] + polygons = results["polys"] + h, w = image.shape[:2] + + if np.random.random_sample() < self.pad_ratio: + image, out_size = self.resize_img(image, keep_ratio=True) + image, offset = self.square_pad(image) + else: + image, out_size = self.resize_img(image, keep_ratio=False) + offset = (0, 0) + results["image"] = image + try: + polygons[:, :, 0::2] = polygons[:, :, 0::2] * out_size[1] / w + offset[0] + polygons[:, :, 1::2] = polygons[:, :, 1::2] * out_size[0] / h + offset[1] + except: + pass + results["polys"] = polygons + + return results + + def __repr__(self): + repr_str = self.__class__.__name__ + return repr_str diff --git a/modules/onnx_ocr_module/src/ppocr/data/imaug/fce_targets.py b/modules/onnx_ocr_module/src/ppocr/data/imaug/fce_targets.py new file mode 100644 index 0000000..9955980 --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/data/imaug/fce_targets.py @@ -0,0 +1,697 @@ +# copyright (c) 2022 PaddlePaddle Authors. All Rights Reserve. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +""" +This code is refer from: +https://github.com/open-mmlab/mmocr/blob/main/mmocr/datasets/pipelines/textdet_targets/fcenet_targets.py +""" + +import cv2 +import numpy as np +from numpy.fft import fft +from numpy.linalg import norm +import sys + + +def vector_slope(vec): + assert len(vec) == 2 + return abs(vec[1] / (vec[0] + 1e-8)) + + +class FCENetTargets: + """Generate the ground truth targets of FCENet: Fourier Contour Embedding + for Arbitrary-Shaped Text Detection. + + [https://arxiv.org/abs/2104.10442] + + Args: + fourier_degree (int): The maximum Fourier transform degree k. + resample_step (float): The step size for resampling the text center + line (TCL). It's better not to exceed half of the minimum width. + center_region_shrink_ratio (float): The shrink ratio of text center + region. + level_size_divisors (tuple(int)): The downsample ratio on each level. + level_proportion_range (tuple(tuple(int))): The range of text sizes + assigned to each level. + """ + + def __init__( + self, + fourier_degree=5, + resample_step=4.0, + center_region_shrink_ratio=0.3, + level_size_divisors=(8, 16, 32), + level_proportion_range=((0, 0.25), (0.2, 0.65), (0.55, 1.0)), + orientation_thr=2.0, + **kwargs, + ): + super().__init__() + assert isinstance(level_size_divisors, tuple) + assert isinstance(level_proportion_range, tuple) + assert len(level_size_divisors) == len(level_proportion_range) + self.fourier_degree = fourier_degree + self.resample_step = resample_step + self.center_region_shrink_ratio = center_region_shrink_ratio + self.level_size_divisors = level_size_divisors + self.level_proportion_range = level_proportion_range + + self.orientation_thr = orientation_thr + + def vector_angle(self, vec1, vec2): + if vec1.ndim > 1: + unit_vec1 = vec1 / (norm(vec1, axis=-1) + 1e-8).reshape((-1, 1)) + else: + unit_vec1 = vec1 / (norm(vec1, axis=-1) + 1e-8) + if vec2.ndim > 1: + unit_vec2 = vec2 / (norm(vec2, axis=-1) + 1e-8).reshape((-1, 1)) + else: + unit_vec2 = vec2 / (norm(vec2, axis=-1) + 1e-8) + return np.arccos(np.clip(np.sum(unit_vec1 * unit_vec2, axis=-1), -1.0, 1.0)) + + def resample_line(self, line, n): + """Resample n points on a line. + + Args: + line (ndarray): The points composing a line. + n (int): The resampled points number. + + Returns: + resampled_line (ndarray): The points composing the resampled line. + """ + + assert line.ndim == 2 + assert line.shape[0] >= 2 + assert line.shape[1] == 2 + assert isinstance(n, int) + assert n > 0 + + length_list = [norm(line[i + 1] - line[i]) for i in range(len(line) - 1)] + total_length = sum(length_list) + length_cumsum = np.cumsum([0.0] + length_list) + delta_length = total_length / (float(n) + 1e-8) + + current_edge_ind = 0 + resampled_line = [line[0]] + + for i in range(1, n): + current_line_len = i * delta_length + + while ( + current_edge_ind + 1 < len(length_cumsum) + and current_line_len >= length_cumsum[current_edge_ind + 1] + ): + current_edge_ind += 1 + + current_edge_end_shift = current_line_len - length_cumsum[current_edge_ind] + + if current_edge_ind >= len(length_list): + break + end_shift_ratio = current_edge_end_shift / length_list[current_edge_ind] + current_point = ( + line[current_edge_ind] + + (line[current_edge_ind + 1] - line[current_edge_ind]) + * end_shift_ratio + ) + resampled_line.append(current_point) + resampled_line.append(line[-1]) + resampled_line = np.array(resampled_line) + + return resampled_line + + def reorder_poly_edge(self, points): + """Get the respective points composing head edge, tail edge, top + sideline and bottom sideline. + + Args: + points (ndarray): The points composing a text polygon. + + Returns: + head_edge (ndarray): The two points composing the head edge of text + polygon. + tail_edge (ndarray): The two points composing the tail edge of text + polygon. + top_sideline (ndarray): The points composing top curved sideline of + text polygon. + bot_sideline (ndarray): The points composing bottom curved sideline + of text polygon. + """ + + assert points.ndim == 2 + assert points.shape[0] >= 4 + assert points.shape[1] == 2 + + head_inds, tail_inds = self.find_head_tail(points, self.orientation_thr) + head_edge, tail_edge = points[head_inds], points[tail_inds] + + pad_points = np.vstack([points, points]) + if tail_inds[1] < 1: + tail_inds[1] = len(points) + sideline1 = pad_points[head_inds[1] : tail_inds[1]] + sideline2 = pad_points[tail_inds[1] : (head_inds[1] + len(points))] + sideline_mean_shift = np.mean(sideline1, axis=0) - np.mean(sideline2, axis=0) + + if sideline_mean_shift[1] > 0: + top_sideline, bot_sideline = sideline2, sideline1 + else: + top_sideline, bot_sideline = sideline1, sideline2 + + return head_edge, tail_edge, top_sideline, bot_sideline + + def find_head_tail(self, points, orientation_thr): + """Find the head edge and tail edge of a text polygon. + + Args: + points (ndarray): The points composing a text polygon. + orientation_thr (float): The threshold for distinguishing between + head edge and tail edge among the horizontal and vertical edges + of a quadrangle. + + Returns: + head_inds (list): The indexes of two points composing head edge. + tail_inds (list): The indexes of two points composing tail edge. + """ + + assert points.ndim == 2 + assert points.shape[0] >= 4 + assert points.shape[1] == 2 + assert isinstance(orientation_thr, float) + + if len(points) > 4: + pad_points = np.vstack([points, points[0]]) + edge_vec = pad_points[1:] - pad_points[:-1] + + theta_sum = [] + adjacent_vec_theta = [] + for i, edge_vec1 in enumerate(edge_vec): + adjacent_ind = [x % len(edge_vec) for x in [i - 1, i + 1]] + adjacent_edge_vec = edge_vec[adjacent_ind] + temp_theta_sum = np.sum(self.vector_angle(edge_vec1, adjacent_edge_vec)) + temp_adjacent_theta = self.vector_angle( + adjacent_edge_vec[0], adjacent_edge_vec[1] + ) + theta_sum.append(temp_theta_sum) + adjacent_vec_theta.append(temp_adjacent_theta) + theta_sum_score = np.array(theta_sum) / np.pi + adjacent_theta_score = np.array(adjacent_vec_theta) / np.pi + poly_center = np.mean(points, axis=0) + edge_dist = np.maximum( + norm(pad_points[1:] - poly_center, axis=-1), + norm(pad_points[:-1] - poly_center, axis=-1), + ) + dist_score = edge_dist / np.max(edge_dist) + position_score = np.zeros(len(edge_vec)) + score = 0.5 * theta_sum_score + 0.15 * adjacent_theta_score + score += 0.35 * dist_score + if len(points) % 2 == 0: + position_score[(len(score) // 2 - 1)] += 1 + position_score[-1] += 1 + score += 0.1 * position_score + pad_score = np.concatenate([score, score]) + score_matrix = np.zeros((len(score), len(score) - 3)) + x = np.arange(len(score) - 3) / float(len(score) - 4) + gaussian = ( + 1.0 + / (np.sqrt(2.0 * np.pi) * 0.5) + * np.exp(-np.power((x - 0.5) / 0.5, 2.0) / 2) + ) + gaussian = gaussian / np.max(gaussian) + for i in range(len(score)): + score_matrix[i, :] = ( + score[i] + + pad_score[(i + 2) : (i + len(score) - 1)] * gaussian * 0.3 + ) + + head_start, tail_increment = np.unravel_index( + score_matrix.argmax(), score_matrix.shape + ) + tail_start = (head_start + tail_increment + 2) % len(points) + head_end = (head_start + 1) % len(points) + tail_end = (tail_start + 1) % len(points) + + if head_end > tail_end: + head_start, tail_start = tail_start, head_start + head_end, tail_end = tail_end, head_end + head_inds = [head_start, head_end] + tail_inds = [tail_start, tail_end] + else: + if vector_slope(points[1] - points[0]) + vector_slope( + points[3] - points[2] + ) < vector_slope(points[2] - points[1]) + vector_slope( + points[0] - points[3] + ): + horizontal_edge_inds = [[0, 1], [2, 3]] + vertical_edge_inds = [[3, 0], [1, 2]] + else: + horizontal_edge_inds = [[3, 0], [1, 2]] + vertical_edge_inds = [[0, 1], [2, 3]] + + vertical_len_sum = norm( + points[vertical_edge_inds[0][0]] - points[vertical_edge_inds[0][1]] + ) + norm( + points[vertical_edge_inds[1][0]] - points[vertical_edge_inds[1][1]] + ) + horizontal_len_sum = norm( + points[horizontal_edge_inds[0][0]] - points[horizontal_edge_inds[0][1]] + ) + norm( + points[horizontal_edge_inds[1][0]] - points[horizontal_edge_inds[1][1]] + ) + + if vertical_len_sum > horizontal_len_sum * orientation_thr: + head_inds = horizontal_edge_inds[0] + tail_inds = horizontal_edge_inds[1] + else: + head_inds = vertical_edge_inds[0] + tail_inds = vertical_edge_inds[1] + + return head_inds, tail_inds + + def resample_sidelines(self, sideline1, sideline2, resample_step): + """Resample two sidelines to be of the same points number according to + step size. + + Args: + sideline1 (ndarray): The points composing a sideline of a text + polygon. + sideline2 (ndarray): The points composing another sideline of a + text polygon. + resample_step (float): The resampled step size. + + Returns: + resampled_line1 (ndarray): The resampled line 1. + resampled_line2 (ndarray): The resampled line 2. + """ + + assert sideline1.ndim == sideline2.ndim == 2 + assert sideline1.shape[1] == sideline2.shape[1] == 2 + assert sideline1.shape[0] >= 2 + assert sideline2.shape[0] >= 2 + assert isinstance(resample_step, float) + + length1 = sum( + [norm(sideline1[i + 1] - sideline1[i]) for i in range(len(sideline1) - 1)] + ) + length2 = sum( + [norm(sideline2[i + 1] - sideline2[i]) for i in range(len(sideline2) - 1)] + ) + + total_length = (length1 + length2) / 2 + resample_point_num = max(int(float(total_length) / resample_step), 1) + + resampled_line1 = self.resample_line(sideline1, resample_point_num) + resampled_line2 = self.resample_line(sideline2, resample_point_num) + + return resampled_line1, resampled_line2 + + def generate_center_region_mask(self, img_size, text_polys): + """Generate text center region mask. + + Args: + img_size (tuple): The image size of (height, width). + text_polys (list[list[ndarray]]): The list of text polygons. + + Returns: + center_region_mask (ndarray): The text center region mask. + """ + + assert isinstance(img_size, tuple) + # assert check_argument.is_2dlist(text_polys) + + h, w = img_size + + center_region_mask = np.zeros((h, w), np.uint8) + + center_region_boxes = [] + for poly in text_polys: + # assert len(poly) == 1 + polygon_points = poly.reshape(-1, 2) + _, _, top_line, bot_line = self.reorder_poly_edge(polygon_points) + resampled_top_line, resampled_bot_line = self.resample_sidelines( + top_line, bot_line, self.resample_step + ) + resampled_bot_line = resampled_bot_line[::-1] + if len(resampled_top_line) != len(resampled_bot_line): + continue + center_line = (resampled_top_line + resampled_bot_line) / 2 + + line_head_shrink_len = ( + norm(resampled_top_line[0] - resampled_bot_line[0]) / 4.0 + ) + line_tail_shrink_len = ( + norm(resampled_top_line[-1] - resampled_bot_line[-1]) / 4.0 + ) + head_shrink_num = int(line_head_shrink_len // self.resample_step) + tail_shrink_num = int(line_tail_shrink_len // self.resample_step) + if len(center_line) > head_shrink_num + tail_shrink_num + 2: + center_line = center_line[ + head_shrink_num : len(center_line) - tail_shrink_num + ] + resampled_top_line = resampled_top_line[ + head_shrink_num : len(resampled_top_line) - tail_shrink_num + ] + resampled_bot_line = resampled_bot_line[ + head_shrink_num : len(resampled_bot_line) - tail_shrink_num + ] + + for i in range(0, len(center_line) - 1): + tl = ( + center_line[i] + + (resampled_top_line[i] - center_line[i]) + * self.center_region_shrink_ratio + ) + tr = ( + center_line[i + 1] + + (resampled_top_line[i + 1] - center_line[i + 1]) + * self.center_region_shrink_ratio + ) + br = ( + center_line[i + 1] + + (resampled_bot_line[i + 1] - center_line[i + 1]) + * self.center_region_shrink_ratio + ) + bl = ( + center_line[i] + + (resampled_bot_line[i] - center_line[i]) + * self.center_region_shrink_ratio + ) + current_center_box = np.vstack([tl, tr, br, bl]).astype(np.int32) + center_region_boxes.append(current_center_box) + + cv2.fillPoly(center_region_mask, center_region_boxes, 1) + return center_region_mask + + def resample_polygon(self, polygon, n=400): + """Resample one polygon with n points on its boundary. + + Args: + polygon (list[float]): The input polygon. + n (int): The number of resampled points. + Returns: + resampled_polygon (list[float]): The resampled polygon. + """ + length = [] + + for i in range(len(polygon)): + p1 = polygon[i] + if i == len(polygon) - 1: + p2 = polygon[0] + else: + p2 = polygon[i + 1] + length.append(((p1[0] - p2[0]) ** 2 + (p1[1] - p2[1]) ** 2) ** 0.5) + + total_length = sum(length) + n_on_each_line = (np.array(length) / (total_length + 1e-8)) * n + n_on_each_line = n_on_each_line.astype(np.int32) + new_polygon = [] + + for i in range(len(polygon)): + num = n_on_each_line[i] + p1 = polygon[i] + if i == len(polygon) - 1: + p2 = polygon[0] + else: + p2 = polygon[i + 1] + + if num == 0: + continue + + dxdy = (p2 - p1) / num + for j in range(num): + point = p1 + dxdy * j + new_polygon.append(point) + + return np.array(new_polygon) + + def normalize_polygon(self, polygon): + """Normalize one polygon so that its start point is at right most. + + Args: + polygon (list[float]): The origin polygon. + Returns: + new_polygon (lost[float]): The polygon with start point at right. + """ + temp_polygon = polygon - polygon.mean(axis=0) + x = np.abs(temp_polygon[:, 0]) + y = temp_polygon[:, 1] + index_x = np.argsort(x) + index_y = np.argmin(y[index_x[:8]]) + index = index_x[index_y] + new_polygon = np.concatenate([polygon[index:], polygon[:index]]) + return new_polygon + + def poly2fourier(self, polygon, fourier_degree): + """Perform Fourier transformation to generate Fourier coefficients ck + from polygon. + + Args: + polygon (ndarray): An input polygon. + fourier_degree (int): The maximum Fourier degree K. + Returns: + c (ndarray(complex)): Fourier coefficients. + """ + points = polygon[:, 0] + polygon[:, 1] * 1j + c_fft = fft(points) / len(points) + c = np.hstack((c_fft[-fourier_degree:], c_fft[: fourier_degree + 1])) + return c + + def clockwise(self, c, fourier_degree): + """Make sure the polygon reconstructed from Fourier coefficients c in + the clockwise direction. + + Args: + polygon (list[float]): The origin polygon. + Returns: + new_polygon (lost[float]): The polygon in clockwise point order. + """ + if np.abs(c[fourier_degree + 1]) > np.abs(c[fourier_degree - 1]): + return c + elif np.abs(c[fourier_degree + 1]) < np.abs(c[fourier_degree - 1]): + return c[::-1] + else: + if np.abs(c[fourier_degree + 2]) > np.abs(c[fourier_degree - 2]): + return c + else: + return c[::-1] + + def cal_fourier_signature(self, polygon, fourier_degree): + """Calculate Fourier signature from input polygon. + + Args: + polygon (ndarray): The input polygon. + fourier_degree (int): The maximum Fourier degree K. + Returns: + fourier_signature (ndarray): An array shaped (2k+1, 2) containing + real part and image part of 2k+1 Fourier coefficients. + """ + resampled_polygon = self.resample_polygon(polygon) + resampled_polygon = self.normalize_polygon(resampled_polygon) + + fourier_coeff = self.poly2fourier(resampled_polygon, fourier_degree) + fourier_coeff = self.clockwise(fourier_coeff, fourier_degree) + + real_part = np.real(fourier_coeff).reshape((-1, 1)) + image_part = np.imag(fourier_coeff).reshape((-1, 1)) + fourier_signature = np.hstack([real_part, image_part]) + + return fourier_signature + + def generate_fourier_maps(self, img_size, text_polys): + """Generate Fourier coefficient maps. + + Args: + img_size (tuple): The image size of (height, width). + text_polys (list[list[ndarray]]): The list of text polygons. + + Returns: + fourier_real_map (ndarray): The Fourier coefficient real part maps. + fourier_image_map (ndarray): The Fourier coefficient image part + maps. + """ + + assert isinstance(img_size, tuple) + + h, w = img_size + k = self.fourier_degree + real_map = np.zeros((k * 2 + 1, h, w), dtype=np.float32) + imag_map = np.zeros((k * 2 + 1, h, w), dtype=np.float32) + + for poly in text_polys: + mask = np.zeros((h, w), dtype=np.uint8) + polygon = np.array(poly).reshape((1, -1, 2)) + cv2.fillPoly(mask, polygon.astype(np.int32), 1) + fourier_coeff = self.cal_fourier_signature(polygon[0], k) + for i in range(-k, k + 1): + if i != 0: + real_map[i + k, :, :] = ( + mask * fourier_coeff[i + k, 0] + + (1 - mask) * real_map[i + k, :, :] + ) + imag_map[i + k, :, :] = ( + mask * fourier_coeff[i + k, 1] + + (1 - mask) * imag_map[i + k, :, :] + ) + else: + yx = np.argwhere(mask > 0.5) + k_ind = np.ones((len(yx)), dtype=np.int64) * k + y, x = yx[:, 0], yx[:, 1] + real_map[k_ind, y, x] = fourier_coeff[k, 0] - x + imag_map[k_ind, y, x] = fourier_coeff[k, 1] - y + + return real_map, imag_map + + def generate_text_region_mask(self, img_size, text_polys): + """Generate text center region mask and geometry attribute maps. + + Args: + img_size (tuple): The image size (height, width). + text_polys (list[list[ndarray]]): The list of text polygons. + + Returns: + text_region_mask (ndarray): The text region mask. + """ + + assert isinstance(img_size, tuple) + + h, w = img_size + text_region_mask = np.zeros((h, w), dtype=np.uint8) + + for poly in text_polys: + polygon = np.array(poly, dtype=np.int32).reshape((1, -1, 2)) + cv2.fillPoly(text_region_mask, polygon, 1) + + return text_region_mask + + def generate_effective_mask(self, mask_size: tuple, polygons_ignore): + """Generate effective mask by setting the ineffective regions to 0 and + effective regions to 1. + + Args: + mask_size (tuple): The mask size. + polygons_ignore (list[[ndarray]]: The list of ignored text + polygons. + + Returns: + mask (ndarray): The effective mask of (height, width). + """ + + mask = np.ones(mask_size, dtype=np.uint8) + + for poly in polygons_ignore: + instance = poly.reshape(-1, 2).astype(np.int32).reshape(1, -1, 2) + cv2.fillPoly(mask, instance, 0) + + return mask + + def generate_level_targets(self, img_size, text_polys, ignore_polys): + """Generate ground truth target on each level. + + Args: + img_size (list[int]): Shape of input image. + text_polys (list[list[ndarray]]): A list of ground truth polygons. + ignore_polys (list[list[ndarray]]): A list of ignored polygons. + Returns: + level_maps (list(ndarray)): A list of ground target on each level. + """ + h, w = img_size + lv_size_divs = self.level_size_divisors + lv_proportion_range = self.level_proportion_range + lv_text_polys = [[] for i in range(len(lv_size_divs))] + lv_ignore_polys = [[] for i in range(len(lv_size_divs))] + level_maps = [] + for poly in text_polys: + polygon = np.array(poly, dtype=np.int32).reshape((1, -1, 2)) + _, _, box_w, box_h = cv2.boundingRect(polygon) + proportion = max(box_h, box_w) / (h + 1e-8) + + for ind, proportion_range in enumerate(lv_proportion_range): + if proportion_range[0] < proportion < proportion_range[1]: + lv_text_polys[ind].append(poly / lv_size_divs[ind]) + + for ignore_poly in ignore_polys: + polygon = np.array(ignore_poly, dtype=np.int32).reshape((1, -1, 2)) + _, _, box_w, box_h = cv2.boundingRect(polygon) + proportion = max(box_h, box_w) / (h + 1e-8) + + for ind, proportion_range in enumerate(lv_proportion_range): + if proportion_range[0] < proportion < proportion_range[1]: + lv_ignore_polys[ind].append(ignore_poly / lv_size_divs[ind]) + + for ind, size_divisor in enumerate(lv_size_divs): + current_level_maps = [] + level_img_size = (h // size_divisor, w // size_divisor) + + text_region = self.generate_text_region_mask( + level_img_size, lv_text_polys[ind] + )[None] + current_level_maps.append(text_region) + + center_region = self.generate_center_region_mask( + level_img_size, lv_text_polys[ind] + )[None] + current_level_maps.append(center_region) + + effective_mask = self.generate_effective_mask( + level_img_size, lv_ignore_polys[ind] + )[None] + current_level_maps.append(effective_mask) + + fourier_real_map, fourier_image_maps = self.generate_fourier_maps( + level_img_size, lv_text_polys[ind] + ) + current_level_maps.append(fourier_real_map) + current_level_maps.append(fourier_image_maps) + + level_maps.append(np.concatenate(current_level_maps)) + + return level_maps + + def generate_targets(self, results): + """Generate the ground truth targets for FCENet. + + Args: + results (dict): The input result dictionary. + + Returns: + results (dict): The output result dictionary. + """ + + assert isinstance(results, dict) + image = results["image"] + polygons = results["polys"] + ignore_tags = results["ignore_tags"] + h, w, _ = image.shape + + polygon_masks = [] + polygon_masks_ignore = [] + for tag, polygon in zip(ignore_tags, polygons): + if tag is True: + polygon_masks_ignore.append(polygon) + else: + polygon_masks.append(polygon) + + level_maps = self.generate_level_targets( + (h, w), polygon_masks, polygon_masks_ignore + ) + + mapping = { + "p3_maps": level_maps[0], + "p4_maps": level_maps[1], + "p5_maps": level_maps[2], + } + for key, value in mapping.items(): + results[key] = value + + return results + + def __call__(self, results): + results = self.generate_targets(results) + return results diff --git a/modules/onnx_ocr_module/src/ppocr/data/imaug/frost_img/frost1.jpg b/modules/onnx_ocr_module/src/ppocr/data/imaug/frost_img/frost1.jpg new file mode 100644 index 0000000..8705320 Binary files /dev/null and b/modules/onnx_ocr_module/src/ppocr/data/imaug/frost_img/frost1.jpg differ diff --git a/modules/onnx_ocr_module/src/ppocr/data/imaug/frost_img/frost2.png b/modules/onnx_ocr_module/src/ppocr/data/imaug/frost_img/frost2.png new file mode 100644 index 0000000..48f7a86 Binary files /dev/null and b/modules/onnx_ocr_module/src/ppocr/data/imaug/frost_img/frost2.png differ diff --git a/modules/onnx_ocr_module/src/ppocr/data/imaug/frost_img/frost3.png b/modules/onnx_ocr_module/src/ppocr/data/imaug/frost_img/frost3.png new file mode 100644 index 0000000..d47f9d2 Binary files /dev/null and b/modules/onnx_ocr_module/src/ppocr/data/imaug/frost_img/frost3.png differ diff --git a/modules/onnx_ocr_module/src/ppocr/data/imaug/frost_img/frost4.jpg b/modules/onnx_ocr_module/src/ppocr/data/imaug/frost_img/frost4.jpg new file mode 100644 index 0000000..f8b0c41 Binary files /dev/null and b/modules/onnx_ocr_module/src/ppocr/data/imaug/frost_img/frost4.jpg differ diff --git a/modules/onnx_ocr_module/src/ppocr/data/imaug/frost_img/frost5.jpg b/modules/onnx_ocr_module/src/ppocr/data/imaug/frost_img/frost5.jpg new file mode 100644 index 0000000..95dc905 Binary files /dev/null and b/modules/onnx_ocr_module/src/ppocr/data/imaug/frost_img/frost5.jpg differ diff --git a/modules/onnx_ocr_module/src/ppocr/data/imaug/frost_img/frost6.jpg b/modules/onnx_ocr_module/src/ppocr/data/imaug/frost_img/frost6.jpg new file mode 100644 index 0000000..14e5d58 Binary files /dev/null and b/modules/onnx_ocr_module/src/ppocr/data/imaug/frost_img/frost6.jpg differ diff --git a/modules/onnx_ocr_module/src/ppocr/data/imaug/iaa_augment.py b/modules/onnx_ocr_module/src/ppocr/data/imaug/iaa_augment.py new file mode 100644 index 0000000..f4c3f33 --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/data/imaug/iaa_augment.py @@ -0,0 +1,213 @@ +# copyright (c) 2020 PaddlePaddle Authors. All Rights Reserve. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +""" +This code is refer from: +https://github.com/WenmuZhou/DBNet.pytorch/blob/master/data_loader/modules/iaa_augment.py +""" +import os + +# Prevent automatic updates in Albumentations for stability in augmentation behavior +os.environ["NO_ALBUMENTATIONS_UPDATE"] = "1" + +import numpy as np +import albumentations as A +from albumentations.core.transforms_interface import DualTransform +from albumentations.augmentations.geometric import functional as fgeometric +from packaging import version + +ALBU_VERSION = version.parse(A.__version__) +IS_ALBU_NEW_VERSION = ALBU_VERSION >= version.parse("1.4.15") + + +# Custom resize transformation mimicking Imgaug's behavior with scaling +class ImgaugLikeResize(DualTransform): + def __init__(self, scale_range=(0.5, 3.0), interpolation=1, p=1.0): + super(ImgaugLikeResize, self).__init__(p) + self.scale_range = scale_range + self.interpolation = interpolation + + # Resize the image based on a randomly chosen scale within the scale range + def apply(self, img, scale=1.0, **params): + height, width = img.shape[:2] + new_height = int(height * scale) + new_width = int(width * scale) + + if IS_ALBU_NEW_VERSION: + return fgeometric.resize( + img, (new_height, new_width), interpolation=self.interpolation + ) + return fgeometric.resize( + img, new_height, new_width, interpolation=self.interpolation + ) + + # Apply the same scaling transformation to keypoints (e.g., polygon points) + def apply_to_keypoints(self, keypoints, scale=1.0, **params): + return np.array( + [(x * scale, y * scale) + tuple(rest) for x, y, *rest in keypoints] + ) + + # Get random scale parameter within the specified range + def get_params(self): + scale = np.random.uniform(self.scale_range[0], self.scale_range[1]) + return {"scale": scale} + + +# Builder class to translate custom augmenter arguments into Albumentations-compatible format +class AugmenterBuilder(object): + def __init__(self): + # Map common Imgaug transformations to equivalent Albumentations transforms + self.imgaug_to_albu = { + "Fliplr": "HorizontalFlip", + "Flipud": "VerticalFlip", + "Affine": "Affine", + # Additional mappings can be added here if needed + } + + # Recursive method to construct augmentation pipeline based on provided arguments + def build(self, args, root=True): + if args is None or len(args) == 0: + return None + elif isinstance(args, list): + # Build the full augmentation sequence if it's a root-level call + if root: + sequence = [self.build(value, root=False) for value in args] + return A.Compose( + sequence, + keypoint_params=A.KeypointParams( + format="xy", remove_invisible=False + ), + ) + else: + # Build individual augmenters for nested arguments + augmenter_type = args[0] + augmenter_args = args[1] if len(args) > 1 else {} + augmenter_args_mapped = self.map_arguments( + augmenter_type, augmenter_args + ) + augmenter_type_mapped = self.imgaug_to_albu.get( + augmenter_type, augmenter_type + ) + if augmenter_type_mapped == "Resize": + return ImgaugLikeResize(**augmenter_args_mapped) + else: + cls = getattr(A, augmenter_type_mapped) + return cls( + **{ + k: self.to_tuple_if_list(v) + for k, v in augmenter_args_mapped.items() + } + ) + elif isinstance(args, dict): + # Process individual transformation specified as dictionary + augmenter_type = args["type"] + augmenter_args = args.get("args", {}) + augmenter_args_mapped = self.map_arguments(augmenter_type, augmenter_args) + augmenter_type_mapped = self.imgaug_to_albu.get( + augmenter_type, augmenter_type + ) + if augmenter_type_mapped == "Resize": + return ImgaugLikeResize(**augmenter_args_mapped) + else: + cls = getattr(A, augmenter_type_mapped) + return cls( + **{ + k: self.to_tuple_if_list(v) + for k, v in augmenter_args_mapped.items() + } + ) + else: + raise RuntimeError("Unknown augmenter arg: " + str(args)) + + # Map arguments to expected format for each augmenter type + def map_arguments(self, augmenter_type, augmenter_args): + augmenter_args = augmenter_args.copy() # Avoid modifying the original arguments + if augmenter_type == "Resize": + # Ensure size is a valid 2-element list or tuple + size = augmenter_args.get("size") + if size: + if not isinstance(size, (list, tuple)) or len(size) != 2: + raise ValueError( + f"'size' must be a list or tuple of two numbers, but got {size}" + ) + min_scale, max_scale = size + return { + "scale_range": (min_scale, max_scale), + "interpolation": 1, # Linear interpolation + "p": 1.0, + } + else: + return {"scale_range": (1.0, 1.0), "interpolation": 1, "p": 1.0} + elif augmenter_type == "Affine": + # Map rotation to a tuple and ensure p=1.0 to apply transformation + rotate = augmenter_args.get("rotate", 0) + if isinstance(rotate, list): + rotate = tuple(rotate) + elif isinstance(rotate, (int, float)): + rotate = (float(rotate), float(rotate)) + augmenter_args["rotate"] = rotate + augmenter_args["p"] = 1.0 + return augmenter_args + else: + # For other augmenters, ensure 'p' probability is specified + p = augmenter_args.get("p", 1.0) + augmenter_args["p"] = p + return augmenter_args + + # Convert lists to tuples for Albumentations compatibility + def to_tuple_if_list(self, obj): + if isinstance(obj, list): + return tuple(obj) + return obj + + +# Wrapper class for image and polygon transformations using Imgaug-style augmentation +class IaaAugment: + def __init__(self, augmenter_args=None, **kwargs): + if augmenter_args is None: + # Default augmenters if none are specified + augmenter_args = [ + {"type": "Fliplr", "args": {"p": 0.5}}, + {"type": "Affine", "args": {"rotate": [-10, 10]}}, + {"type": "Resize", "args": {"size": [0.5, 3]}}, + ] + self.augmenter = AugmenterBuilder().build(augmenter_args) + + # Apply the augmentations to image and polygon data + def __call__(self, data): + image = data["image"] + + if self.augmenter: + # Flatten polygons to individual keypoints for transformation + keypoints = [] + keypoints_lengths = [] + for poly in data["polys"]: + keypoints.extend([tuple(point) for point in poly]) + keypoints_lengths.append(len(poly)) + + # Apply the augmentation pipeline to image and keypoints + transformed = self.augmenter(image=image, keypoints=keypoints) + data["image"] = transformed["image"] + + # Extract transformed keypoints and reconstruct polygon structures + transformed_keypoints = transformed["keypoints"] + + # Reassemble polygons from transformed keypoints + new_polys = [] + idx = 0 + for length in keypoints_lengths: + new_poly = transformed_keypoints[idx : idx + length] + new_polys.append(np.array([kp[:2] for kp in new_poly])) + idx += length + data["polys"] = np.array(new_polys) + return data diff --git a/modules/onnx_ocr_module/src/ppocr/data/imaug/label_ops.py b/modules/onnx_ocr_module/src/ppocr/data/imaug/label_ops.py new file mode 100644 index 0000000..cc6f5ca --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/data/imaug/label_ops.py @@ -0,0 +1,2222 @@ +# copyright (c) 2020 PaddlePaddle Authors. All Rights Reserve. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function +from __future__ import unicode_literals + +import os +from enum import Enum +import copy +import numpy as np +import string +from shapely.geometry import LineString, Point, Polygon +import json +import copy +import random +from random import sample +from collections import defaultdict + +from ppocr.utils.logging import get_logger +from ppocr.data.imaug.vqa.augment import order_by_tbyx + + +class ClsLabelEncode(object): + def __init__(self, label_list, **kwargs): + self.label_list = label_list + + def __call__(self, data): + label = data["label"] + if label not in self.label_list: + return None + label = self.label_list.index(label) + data["label"] = label + return data + + +class DetLabelEncode(object): + def __init__(self, **kwargs): + pass + + def __call__(self, data): + label = data["label"] + label = json.loads(label) + nBox = len(label) + boxes, txts, txt_tags = [], [], [] + for bno in range(0, nBox): + box = label[bno]["points"] + txt = label[bno]["transcription"] + boxes.append(box) + txts.append(txt) + if txt in ["*", "###"]: + txt_tags.append(True) + else: + txt_tags.append(False) + if len(boxes) == 0: + return None + boxes = self.expand_points_num(boxes) + boxes = np.array(boxes, dtype=np.float32) + txt_tags = np.array(txt_tags, dtype=np.bool_) + + data["polys"] = boxes + data["texts"] = txts + data["ignore_tags"] = txt_tags + return data + + def order_points_clockwise(self, pts): + rect = np.zeros((4, 2), dtype="float32") + s = pts.sum(axis=1) + rect[0] = pts[np.argmin(s)] + rect[2] = pts[np.argmax(s)] + tmp = np.delete(pts, (np.argmin(s), np.argmax(s)), axis=0) + diff = np.diff(np.array(tmp), axis=1) + rect[1] = tmp[np.argmin(diff)] + rect[3] = tmp[np.argmax(diff)] + return rect + + def expand_points_num(self, boxes): + max_points_num = 0 + for box in boxes: + if len(box) > max_points_num: + max_points_num = len(box) + ex_boxes = [] + for box in boxes: + ex_box = box + [box[-1]] * (max_points_num - len(box)) + ex_boxes.append(ex_box) + return ex_boxes + + +class BaseRecLabelEncode(object): + """Convert between text-label and text-index""" + + def __init__( + self, + max_text_length, + character_dict_path=None, + use_space_char=False, + lower=False, + ): + self.max_text_len = max_text_length + self.beg_str = "sos" + self.end_str = "eos" + self.lower = lower + + if character_dict_path is None: + logger = get_logger() + logger.warning( + "The character_dict_path is None, model can only recognize number and lower letters" + ) + self.character_str = "0123456789abcdefghijklmnopqrstuvwxyz" + dict_character = list(self.character_str) + self.lower = True + else: + self.character_str = [] + # UTF-8 인코딩 문제 해결을 위한 안전한 파일 읽기 + try: + with open(character_dict_path, "r", encoding="utf-8") as fin: + lines = fin.readlines() + for line in lines: + line = line.strip("\n").strip("\r\n") + self.character_str.append(line) + except UnicodeDecodeError: + # UTF-8 실패 시 다른 인코딩 시도 + encodings = ['cp949', 'euc-kr', 'latin1', 'cp1252'] + success = False + for encoding in encodings: + try: + with open(character_dict_path, "r", encoding=encoding) as fin: + lines = fin.readlines() + for line in lines: + line = line.strip("\n").strip("\r\n") + self.character_str.append(line) + success = True + break + except UnicodeDecodeError: + continue + + if not success: + # 모든 인코딩 실패 시 바이너리 모드로 시도 (기존 방식) + with open(character_dict_path, "rb") as fin: + lines = fin.readlines() + for line in lines: + try: + line = line.decode("utf-8").strip("\n").strip("\r\n") + self.character_str.append(line) + except UnicodeDecodeError: + # 개별 라인 디코딩 실패 시 건너뛰기 + continue + if use_space_char: + self.character_str.append(" ") + dict_character = list(self.character_str) + dict_character = self.add_special_char(dict_character) + self.dict = {} + for i, char in enumerate(dict_character): + self.dict[char] = i + self.character = dict_character + + def add_special_char(self, dict_character): + return dict_character + + def encode(self, text): + """convert text-label into text-index. + input: + text: text labels of each image. [batch_size] + + output: + text: concatenated text index for CTCLoss. + [sum(text_lengths)] = [text_index_0 + text_index_1 + ... + text_index_(n - 1)] + length: length of each text. [batch_size] + """ + if len(text) == 0 or len(text) > self.max_text_len: + return None + if self.lower: + text = text.lower() + text_list = [] + for char in text: + if char not in self.dict: + # logger = get_logger() + # logger.warning('{} is not in dict'.format(char)) + continue + text_list.append(self.dict[char]) + if len(text_list) == 0: + return None + return text_list + + +class CTCLabelEncode(BaseRecLabelEncode): + """Convert between text-label and text-index""" + + def __init__( + self, max_text_length, character_dict_path=None, use_space_char=False, **kwargs + ): + super(CTCLabelEncode, self).__init__( + max_text_length, character_dict_path, use_space_char + ) + + def __call__(self, data): + text = data["label"] + text = self.encode(text) + if text is None: + return None + data["length"] = np.array(len(text)) + text = text + [0] * (self.max_text_len - len(text)) + data["label"] = np.array(text) + + label = [0] * len(self.character) + for x in text: + label[x] += 1 + data["label_ace"] = np.array(label) + return data + + def add_special_char(self, dict_character): + dict_character = ["blank"] + dict_character + return dict_character + + +class E2ELabelEncodeTest(BaseRecLabelEncode): + def __init__( + self, max_text_length, character_dict_path=None, use_space_char=False, **kwargs + ): + super(E2ELabelEncodeTest, self).__init__( + max_text_length, character_dict_path, use_space_char + ) + + def __call__(self, data): + import json + + padnum = len(self.dict) + label = data["label"] + label = json.loads(label) + nBox = len(label) + boxes, txts, txt_tags = [], [], [] + for bno in range(0, nBox): + box = label[bno]["points"] + txt = label[bno]["transcription"] + boxes.append(box) + txts.append(txt) + if txt in ["*", "###"]: + txt_tags.append(True) + else: + txt_tags.append(False) + boxes = np.array(boxes, dtype=np.float32) + txt_tags = np.array(txt_tags, dtype=np.bool_) + data["polys"] = boxes + data["ignore_tags"] = txt_tags + temp_texts = [] + for text in txts: + text = text.lower() + text = self.encode(text) + if text is None: + return None + text = text + [padnum] * (self.max_text_len - len(text)) # use 36 to pad + temp_texts.append(text) + data["texts"] = np.array(temp_texts) + return data + + +class E2ELabelEncodeTrain(object): + def __init__(self, **kwargs): + pass + + def __call__(self, data): + import json + + label = data["label"] + label = json.loads(label) + nBox = len(label) + boxes, txts, txt_tags = [], [], [] + for bno in range(0, nBox): + box = label[bno]["points"] + txt = label[bno]["transcription"] + boxes.append(box) + txts.append(txt) + if txt in ["*", "###"]: + txt_tags.append(True) + else: + txt_tags.append(False) + boxes = np.array(boxes, dtype=np.float32) + txt_tags = np.array(txt_tags, dtype=np.bool_) + + data["polys"] = boxes + data["texts"] = txts + data["ignore_tags"] = txt_tags + return data + + +class KieLabelEncode(object): + def __init__( + self, character_dict_path, class_path, norm=10, directed=False, **kwargs + ): + super(KieLabelEncode, self).__init__() + self.dict = dict({"": 0}) + self.label2classid_map = dict() + with open(character_dict_path, "r", encoding="utf-8") as fr: + idx = 1 + for line in fr: + char = line.strip() + self.dict[char] = idx + idx += 1 + with open(class_path, "r") as fin: + lines = fin.readlines() + for idx, line in enumerate(lines): + line = line.strip("\n") + self.label2classid_map[line] = idx + self.norm = norm + self.directed = directed + + def compute_relation(self, boxes): + """Compute relation between every two boxes.""" + x1s, y1s = boxes[:, 0:1], boxes[:, 1:2] + x2s, y2s = boxes[:, 4:5], boxes[:, 5:6] + ws, hs = x2s - x1s + 1, np.maximum(y2s - y1s + 1, 1) + dxs = (x1s[:, 0][None] - x1s) / self.norm + dys = (y1s[:, 0][None] - y1s) / self.norm + xhhs, xwhs = hs[:, 0][None] / hs, ws[:, 0][None] / hs + whs = ws / hs + np.zeros_like(xhhs) + relations = np.stack([dxs, dys, whs, xhhs, xwhs], -1) + bboxes = np.concatenate([x1s, y1s, x2s, y2s], -1).astype(np.float32) + return relations, bboxes + + def pad_text_indices(self, text_inds): + """Pad text index to same length.""" + max_len = 300 + recoder_len = max([len(text_ind) for text_ind in text_inds]) + padded_text_inds = -np.ones((len(text_inds), max_len), np.int32) + for idx, text_ind in enumerate(text_inds): + padded_text_inds[idx, : len(text_ind)] = np.array(text_ind) + return padded_text_inds, recoder_len + + def list_to_numpy(self, ann_infos): + """Convert bboxes, relations, texts and labels to ndarray.""" + boxes, text_inds = ann_infos["points"], ann_infos["text_inds"] + boxes = np.array(boxes, np.int32) + relations, bboxes = self.compute_relation(boxes) + + labels = ann_infos.get("labels", None) + if labels is not None: + labels = np.array(labels, np.int32) + edges = ann_infos.get("edges", None) + if edges is not None: + labels = labels[:, None] + edges = np.array(edges) + edges = (edges[:, None] == edges[None, :]).astype(np.int32) + if self.directed: + edges = (edges & labels == 1).astype(np.int32) + np.fill_diagonal(edges, -1) + labels = np.concatenate([labels, edges], -1) + padded_text_inds, recoder_len = self.pad_text_indices(text_inds) + max_num = 300 + temp_bboxes = np.zeros([max_num, 4]) + h, _ = bboxes.shape + temp_bboxes[:h, :] = bboxes + + temp_relations = np.zeros([max_num, max_num, 5]) + temp_relations[:h, :h, :] = relations + + temp_padded_text_inds = np.zeros([max_num, max_num]) + temp_padded_text_inds[:h, :] = padded_text_inds + + temp_labels = np.zeros([max_num, max_num]) + temp_labels[:h, : h + 1] = labels + + tag = np.array([h, recoder_len]) + return dict( + image=ann_infos["image"], + points=temp_bboxes, + relations=temp_relations, + texts=temp_padded_text_inds, + labels=temp_labels, + tag=tag, + ) + + def convert_canonical(self, points_x, points_y): + assert len(points_x) == 4 + assert len(points_y) == 4 + + points = [Point(points_x[i], points_y[i]) for i in range(4)] + + polygon = Polygon([(p.x, p.y) for p in points]) + min_x, min_y, _, _ = polygon.bounds + points_to_lefttop = [ + LineString([points[i], Point(min_x, min_y)]) for i in range(4) + ] + distances = np.array([line.length for line in points_to_lefttop]) + sort_dist_idx = np.argsort(distances) + lefttop_idx = sort_dist_idx[0] + + if lefttop_idx == 0: + point_orders = [0, 1, 2, 3] + elif lefttop_idx == 1: + point_orders = [1, 2, 3, 0] + elif lefttop_idx == 2: + point_orders = [2, 3, 0, 1] + else: + point_orders = [3, 0, 1, 2] + + sorted_points_x = [points_x[i] for i in point_orders] + sorted_points_y = [points_y[j] for j in point_orders] + + return sorted_points_x, sorted_points_y + + def sort_vertex(self, points_x, points_y): + assert len(points_x) == 4 + assert len(points_y) == 4 + + x = np.array(points_x) + y = np.array(points_y) + center_x = np.sum(x) * 0.25 + center_y = np.sum(y) * 0.25 + + x_arr = np.array(x - center_x) + y_arr = np.array(y - center_y) + + angle = np.arctan2(y_arr, x_arr) * 180.0 / np.pi + sort_idx = np.argsort(angle) + + sorted_points_x, sorted_points_y = [], [] + for i in range(4): + sorted_points_x.append(points_x[sort_idx[i]]) + sorted_points_y.append(points_y[sort_idx[i]]) + + return self.convert_canonical(sorted_points_x, sorted_points_y) + + def __call__(self, data): + import json + + label = data["label"] + annotations = json.loads(label) + boxes, texts, text_inds, labels, edges = [], [], [], [], [] + for ann in annotations: + box = ann["points"] + x_list = [box[i][0] for i in range(4)] + y_list = [box[i][1] for i in range(4)] + sorted_x_list, sorted_y_list = self.sort_vertex(x_list, y_list) + sorted_box = [] + for x, y in zip(sorted_x_list, sorted_y_list): + sorted_box.append(x) + sorted_box.append(y) + boxes.append(sorted_box) + text = ann["transcription"] + texts.append(ann["transcription"]) + text_ind = [self.dict[c] for c in text if c in self.dict] + text_inds.append(text_ind) + if "label" in ann.keys(): + labels.append(self.label2classid_map[ann["label"]]) + elif "key_cls" in ann.keys(): + labels.append(ann["key_cls"]) + else: + raise ValueError( + "Cannot found 'key_cls' in ann.keys(), please check your training annotation." + ) + edges.append(ann.get("edge", 0)) + ann_infos = dict( + image=data["image"], + points=boxes, + texts=texts, + text_inds=text_inds, + edges=edges, + labels=labels, + ) + + return self.list_to_numpy(ann_infos) + + +class AttnLabelEncode(BaseRecLabelEncode): + """Convert between text-label and text-index""" + + def __init__( + self, max_text_length, character_dict_path=None, use_space_char=False, **kwargs + ): + super(AttnLabelEncode, self).__init__( + max_text_length, character_dict_path, use_space_char + ) + + def add_special_char(self, dict_character): + self.beg_str = "sos" + self.end_str = "eos" + dict_character = [self.beg_str] + dict_character + [self.end_str] + return dict_character + + def __call__(self, data): + text = data["label"] + text = self.encode(text) + if text is None: + return None + if len(text) >= self.max_text_len: + return None + data["length"] = np.array(len(text)) + text = ( + [0] + + text + + [len(self.character) - 1] + + [0] * (self.max_text_len - len(text) - 2) + ) + data["label"] = np.array(text) + return data + + def get_ignored_tokens(self): + beg_idx = self.get_beg_end_flag_idx("beg") + end_idx = self.get_beg_end_flag_idx("end") + return [beg_idx, end_idx] + + def get_beg_end_flag_idx(self, beg_or_end): + if beg_or_end == "beg": + idx = np.array(self.dict[self.beg_str]) + elif beg_or_end == "end": + idx = np.array(self.dict[self.end_str]) + else: + assert False, "Unsupported type %s in get_beg_end_flag_idx" % beg_or_end + return idx + + +class RFLLabelEncode(BaseRecLabelEncode): + """Convert between text-label and text-index""" + + def __init__( + self, max_text_length, character_dict_path=None, use_space_char=False, **kwargs + ): + super(RFLLabelEncode, self).__init__( + max_text_length, character_dict_path, use_space_char + ) + + def add_special_char(self, dict_character): + self.beg_str = "sos" + self.end_str = "eos" + dict_character = [self.beg_str] + dict_character + [self.end_str] + return dict_character + + def encode_cnt(self, text): + cnt_label = [0.0] * len(self.character) + for char_ in text: + cnt_label[char_] += 1 + return np.array(cnt_label) + + def __call__(self, data): + text = data["label"] + text = self.encode(text) + if text is None: + return None + if len(text) >= self.max_text_len: + return None + cnt_label = self.encode_cnt(text) + data["length"] = np.array(len(text)) + text = ( + [0] + + text + + [len(self.character) - 1] + + [0] * (self.max_text_len - len(text) - 2) + ) + if len(text) != self.max_text_len: + return None + data["label"] = np.array(text) + data["cnt_label"] = cnt_label + return data + + def get_ignored_tokens(self): + beg_idx = self.get_beg_end_flag_idx("beg") + end_idx = self.get_beg_end_flag_idx("end") + return [beg_idx, end_idx] + + def get_beg_end_flag_idx(self, beg_or_end): + if beg_or_end == "beg": + idx = np.array(self.dict[self.beg_str]) + elif beg_or_end == "end": + idx = np.array(self.dict[self.end_str]) + else: + assert False, "Unsupported type %s in get_beg_end_flag_idx" % beg_or_end + return idx + + +class SEEDLabelEncode(BaseRecLabelEncode): + """Convert between text-label and text-index""" + + def __init__( + self, max_text_length, character_dict_path=None, use_space_char=False, **kwargs + ): + super(SEEDLabelEncode, self).__init__( + max_text_length, character_dict_path, use_space_char + ) + + def add_special_char(self, dict_character): + self.padding = "padding" + self.end_str = "eos" + self.unknown = "unknown" + dict_character = dict_character + [self.end_str, self.padding, self.unknown] + return dict_character + + def __call__(self, data): + text = data["label"] + text = self.encode(text) + if text is None: + return None + if len(text) >= self.max_text_len: + return None + data["length"] = np.array(len(text)) + 1 # conclude eos + text = ( + text + + [len(self.character) - 3] + + [len(self.character) - 2] * (self.max_text_len - len(text) - 1) + ) + data["label"] = np.array(text) + return data + + +class SRNLabelEncode(BaseRecLabelEncode): + """Convert between text-label and text-index""" + + def __init__( + self, + max_text_length=25, + character_dict_path=None, + use_space_char=False, + **kwargs, + ): + super(SRNLabelEncode, self).__init__( + max_text_length, character_dict_path, use_space_char + ) + + def add_special_char(self, dict_character): + dict_character = dict_character + [self.beg_str, self.end_str] + return dict_character + + def __call__(self, data): + text = data["label"] + text = self.encode(text) + char_num = len(self.character) + if text is None: + return None + if len(text) > self.max_text_len: + return None + data["length"] = np.array(len(text)) + text = text + [char_num - 1] * (self.max_text_len - len(text)) + data["label"] = np.array(text) + return data + + def get_ignored_tokens(self): + beg_idx = self.get_beg_end_flag_idx("beg") + end_idx = self.get_beg_end_flag_idx("end") + return [beg_idx, end_idx] + + def get_beg_end_flag_idx(self, beg_or_end): + if beg_or_end == "beg": + idx = np.array(self.dict[self.beg_str]) + elif beg_or_end == "end": + idx = np.array(self.dict[self.end_str]) + else: + assert False, "Unsupported type %s in get_beg_end_flag_idx" % beg_or_end + return idx + + +class TableLabelEncode(AttnLabelEncode): + """Convert between text-label and text-index""" + + def __init__( + self, + max_text_length, + character_dict_path, + replace_empty_cell_token=False, + merge_no_span_structure=False, + learn_empty_box=False, + loc_reg_num=4, + **kwargs, + ): + self.max_text_len = max_text_length + self.lower = False + self.learn_empty_box = learn_empty_box + self.merge_no_span_structure = merge_no_span_structure + self.replace_empty_cell_token = replace_empty_cell_token + + dict_character = [] + with open(character_dict_path, "rb") as fin: + lines = fin.readlines() + for line in lines: + line = line.decode("utf-8").strip("\n").strip("\r\n") + dict_character.append(line) + + if self.merge_no_span_structure: + if "" not in dict_character: + dict_character.append("") + if "" in dict_character: + dict_character.remove("") + + dict_character = self.add_special_char(dict_character) + self.dict = {} + for i, char in enumerate(dict_character): + self.dict[char] = i + self.idx2char = {v: k for k, v in self.dict.items()} + + self.character = dict_character + self.loc_reg_num = loc_reg_num + self.pad_idx = self.dict[self.beg_str] + self.start_idx = self.dict[self.beg_str] + self.end_idx = self.dict[self.end_str] + + self.td_token = ["", "", ""] + self.empty_bbox_token_dict = { + "[]": "", + "[' ']": "", + "['', ' ', '']": "", + "['\\u2028', '\\u2028']": "", + "['', ' ', '']": "", + "['', '']": "", + "['', ' ', '']": "", + "['', '', '', '']": "", + "['', '', ' ', '', '']": "", + "['', '']": "", + "['', ' ', '\\u2028', ' ', '\\u2028', ' ', '']": "", + } + + @property + def _max_text_len(self): + return self.max_text_len + 2 + + def __call__(self, data): + cells = data["cells"] + structure = data["structure"] + if self.merge_no_span_structure: + structure = self._merge_no_span_structure(structure) + if self.replace_empty_cell_token: + structure = self._replace_empty_cell_token(structure, cells) + # remove empty token and add " " to span token + new_structure = [] + for token in structure: + if token != "": + if "span" in token and token[0] != " ": + token = " " + token + new_structure.append(token) + # encode structure + structure = self.encode(new_structure) + if structure is None: + return None + data["length"] = len(structure) + structure = [self.start_idx] + structure + [self.end_idx] # add sos abd eos + structure = structure + [self.pad_idx] * ( + self._max_text_len - len(structure) + ) # pad + structure = np.array(structure) + data["structure"] = structure + + if len(structure) > self._max_text_len: + return None + + # encode box + bboxes = np.zeros((self._max_text_len, self.loc_reg_num), dtype=np.float32) + bbox_masks = np.zeros((self._max_text_len, 1), dtype=np.float32) + + bbox_idx = 0 + + for i, token in enumerate(structure): + if self.idx2char[token] in self.td_token: + if "bbox" in cells[bbox_idx] and len(cells[bbox_idx]["tokens"]) > 0: + bbox = cells[bbox_idx]["bbox"].copy() + bbox = np.array(bbox, dtype=np.float32).reshape(-1) + bboxes[i] = bbox + bbox_masks[i] = 1.0 + if self.learn_empty_box: + bbox_masks[i] = 1.0 + bbox_idx += 1 + data["bboxes"] = bboxes + data["bbox_masks"] = bbox_masks + return data + + def _merge_no_span_structure(self, structure): + """ + This code is refer from: + https://github.com/JiaquanYe/TableMASTER-mmocr/blob/master/table_recognition/data_preprocess.py + """ + new_structure = [] + i = 0 + while i < len(structure): + token = structure[i] + if token == "": + token = "" + i += 1 + new_structure.append(token) + i += 1 + return new_structure + + def _replace_empty_cell_token(self, token_list, cells): + """ + This fun code is refer from: + https://github.com/JiaquanYe/TableMASTER-mmocr/blob/master/table_recognition/data_preprocess.py + """ + + bbox_idx = 0 + add_empty_bbox_token_list = [] + for token in token_list: + if token in ["", ""]: + if "bbox" not in cells[bbox_idx].keys(): + content = str(cells[bbox_idx]["tokens"]) + token = self.empty_bbox_token_dict[content] + add_empty_bbox_token_list.append(token) + bbox_idx += 1 + else: + add_empty_bbox_token_list.append(token) + return add_empty_bbox_token_list + + +class TableMasterLabelEncode(TableLabelEncode): + """Convert between text-label and text-index""" + + def __init__( + self, + max_text_length, + character_dict_path, + replace_empty_cell_token=False, + merge_no_span_structure=False, + learn_empty_box=False, + loc_reg_num=4, + **kwargs, + ): + super(TableMasterLabelEncode, self).__init__( + max_text_length, + character_dict_path, + replace_empty_cell_token, + merge_no_span_structure, + learn_empty_box, + loc_reg_num, + **kwargs, + ) + self.pad_idx = self.dict[self.pad_str] + self.unknown_idx = self.dict[self.unknown_str] + + @property + def _max_text_len(self): + return self.max_text_len + + def add_special_char(self, dict_character): + self.beg_str = "" + self.end_str = "" + self.unknown_str = "" + self.pad_str = "" + dict_character = dict_character + dict_character = dict_character + [ + self.unknown_str, + self.beg_str, + self.end_str, + self.pad_str, + ] + return dict_character + + +class TableBoxEncode(object): + def __init__(self, in_box_format="xyxy", out_box_format="xyxy", **kwargs): + assert out_box_format in ["xywh", "xyxy", "xyxyxyxy"] + self.in_box_format = in_box_format + self.out_box_format = out_box_format + + def __call__(self, data): + img_height, img_width = data["image"].shape[:2] + bboxes = data["bboxes"] + if self.in_box_format != self.out_box_format: + if self.out_box_format == "xywh": + if self.in_box_format == "xyxyxyxy": + bboxes = self.xyxyxyxy2xywh(bboxes) + elif self.in_box_format == "xyxy": + bboxes = self.xyxy2xywh(bboxes) + + bboxes[:, 0::2] /= img_width + bboxes[:, 1::2] /= img_height + data["bboxes"] = bboxes + return data + + def xyxyxyxy2xywh(self, boxes): + new_bboxes = np.zeros([len(boxes), 4]) + new_bboxes[:, 0] = boxes[:, 0::2].min() # x1 + new_bboxes[:, 1] = boxes[:, 1::2].min() # y1 + new_bboxes[:, 2] = boxes[:, 0::2].max() - new_bboxes[:, 0] # w + new_bboxes[:, 3] = boxes[:, 1::2].max() - new_bboxes[:, 1] # h + return new_bboxes + + def xyxy2xywh(self, bboxes): + new_bboxes = np.empty_like(bboxes) + new_bboxes[:, 0] = (bboxes[:, 0] + bboxes[:, 2]) / 2 # x center + new_bboxes[:, 1] = (bboxes[:, 1] + bboxes[:, 3]) / 2 # y center + new_bboxes[:, 2] = bboxes[:, 2] - bboxes[:, 0] # width + new_bboxes[:, 3] = bboxes[:, 3] - bboxes[:, 1] # height + return new_bboxes + + +class SARLabelEncode(BaseRecLabelEncode): + """Convert between text-label and text-index""" + + def __init__( + self, max_text_length, character_dict_path=None, use_space_char=False, **kwargs + ): + super(SARLabelEncode, self).__init__( + max_text_length, character_dict_path, use_space_char + ) + + def add_special_char(self, dict_character): + beg_end_str = "" + unknown_str = "" + padding_str = "" + dict_character = dict_character + [unknown_str] + self.unknown_idx = len(dict_character) - 1 + dict_character = dict_character + [beg_end_str] + self.start_idx = len(dict_character) - 1 + self.end_idx = len(dict_character) - 1 + dict_character = dict_character + [padding_str] + self.padding_idx = len(dict_character) - 1 + + return dict_character + + def __call__(self, data): + text = data["label"] + text = self.encode(text) + if text is None: + return None + if len(text) >= self.max_text_len - 1: + return None + data["length"] = np.array(len(text)) + target = [self.start_idx] + text + [self.end_idx] + padded_text = [self.padding_idx for _ in range(self.max_text_len)] + + padded_text[: len(target)] = target + data["label"] = np.array(padded_text) + return data + + def get_ignored_tokens(self): + return [self.padding_idx] + + +class SATRNLabelEncode(BaseRecLabelEncode): + """Convert between text-label and text-index""" + + def __init__( + self, + max_text_length, + character_dict_path=None, + use_space_char=False, + lower=False, + **kwargs, + ): + super(SATRNLabelEncode, self).__init__( + max_text_length, character_dict_path, use_space_char + ) + self.lower = lower + + def add_special_char(self, dict_character): + beg_end_str = "" + unknown_str = "" + padding_str = "" + dict_character = dict_character + [unknown_str] + self.unknown_idx = len(dict_character) - 1 + dict_character = dict_character + [beg_end_str] + self.start_idx = len(dict_character) - 1 + self.end_idx = len(dict_character) - 1 + dict_character = dict_character + [padding_str] + self.padding_idx = len(dict_character) - 1 + + return dict_character + + def encode(self, text): + if self.lower: + text = text.lower() + text_list = [] + for char in text: + text_list.append(self.dict.get(char, self.unknown_idx)) + if len(text_list) == 0: + return None + return text_list + + def __call__(self, data): + text = data["label"] + text = self.encode(text) + if text is None: + return None + data["length"] = np.array(len(text)) + target = [self.start_idx] + text + [self.end_idx] + padded_text = [self.padding_idx for _ in range(self.max_text_len)] + if len(target) > self.max_text_len: + padded_text = target[: self.max_text_len] + else: + padded_text[: len(target)] = target + data["label"] = np.array(padded_text) + return data + + def get_ignored_tokens(self): + return [self.padding_idx] + + +class PRENLabelEncode(BaseRecLabelEncode): + def __init__( + self, max_text_length, character_dict_path, use_space_char=False, **kwargs + ): + super(PRENLabelEncode, self).__init__( + max_text_length, character_dict_path, use_space_char + ) + + def add_special_char(self, dict_character): + padding_str = "" # 0 + end_str = "" # 1 + unknown_str = "" # 2 + + dict_character = [padding_str, end_str, unknown_str] + dict_character + self.padding_idx = 0 + self.end_idx = 1 + self.unknown_idx = 2 + + return dict_character + + def encode(self, text): + if len(text) == 0 or len(text) >= self.max_text_len: + return None + if self.lower: + text = text.lower() + text_list = [] + for char in text: + if char not in self.dict: + text_list.append(self.unknown_idx) + else: + text_list.append(self.dict[char]) + text_list.append(self.end_idx) + if len(text_list) < self.max_text_len: + text_list += [self.padding_idx] * (self.max_text_len - len(text_list)) + return text_list + + def __call__(self, data): + text = data["label"] + encoded_text = self.encode(text) + if encoded_text is None: + return None + data["label"] = np.array(encoded_text) + return data + + +class VQATokenLabelEncode(object): + """ + Label encode for NLP VQA methods + """ + + def __init__( + self, + class_path, + contains_re=False, + add_special_ids=False, + algorithm="LayoutXLM", + use_textline_bbox_info=True, + order_method=None, + infer_mode=False, + ocr_engine=None, + **kwargs, + ): + super(VQATokenLabelEncode, self).__init__() + from paddlenlp.transformers import ( + LayoutXLMTokenizer, + LayoutLMTokenizer, + LayoutLMv2Tokenizer, + ) + from ppocr.utils.utility import load_vqa_bio_label_maps + + tokenizer_dict = { + "LayoutXLM": { + "class": LayoutXLMTokenizer, + "pretrained_model": "layoutxlm-base-uncased", + }, + "LayoutLM": { + "class": LayoutLMTokenizer, + "pretrained_model": "layoutlm-base-uncased", + }, + "LayoutLMv2": { + "class": LayoutLMv2Tokenizer, + "pretrained_model": "layoutlmv2-base-uncased", + }, + } + self.contains_re = contains_re + tokenizer_config = tokenizer_dict[algorithm] + self.tokenizer = tokenizer_config["class"].from_pretrained( + tokenizer_config["pretrained_model"] + ) + self.label2id_map, id2label_map = load_vqa_bio_label_maps(class_path) + self.add_special_ids = add_special_ids + self.infer_mode = infer_mode + self.ocr_engine = ocr_engine + self.use_textline_bbox_info = use_textline_bbox_info + self.order_method = order_method + assert self.order_method in [None, "tb-yx"] + + def split_bbox(self, bbox, text, tokenizer): + words = text.split() + token_bboxes = [] + curr_word_idx = 0 + x1, y1, x2, y2 = bbox + unit_w = (x2 - x1) / len(text) + for idx, word in enumerate(words): + curr_w = len(word) * unit_w + word_bbox = [x1, y1, x1 + curr_w, y2] + token_bboxes.extend([word_bbox] * len(tokenizer.tokenize(word))) + x1 += (len(word) + 1) * unit_w + return token_bboxes + + def filter_empty_contents(self, ocr_info): + """ + find out the empty texts and remove the links + """ + new_ocr_info = [] + empty_index = [] + for idx, info in enumerate(ocr_info): + if len(info["transcription"]) > 0: + new_ocr_info.append(copy.deepcopy(info)) + else: + empty_index.append(info["id"]) + + for idx, info in enumerate(new_ocr_info): + new_link = [] + for link in info["linking"]: + if link[0] in empty_index or link[1] in empty_index: + continue + new_link.append(link) + new_ocr_info[idx]["linking"] = new_link + return new_ocr_info + + def __call__(self, data): + # load bbox and label info + ocr_info = self._load_ocr_info(data) + + for idx in range(len(ocr_info)): + if "bbox" not in ocr_info[idx]: + ocr_info[idx]["bbox"] = self.trans_poly_to_bbox(ocr_info[idx]["points"]) + + if self.order_method == "tb-yx": + ocr_info = order_by_tbyx(ocr_info) + + # for re + train_re = self.contains_re and not self.infer_mode + if train_re: + ocr_info = self.filter_empty_contents(ocr_info) + + height, width, _ = data["image"].shape + + words_list = [] + bbox_list = [] + input_ids_list = [] + token_type_ids_list = [] + segment_offset_id = [] + gt_label_list = [] + + entities = [] + + if train_re: + relations = [] + id2label = {} + entity_id_to_index_map = {} + empty_entity = set() + + data["ocr_info"] = copy.deepcopy(ocr_info) + + for info in ocr_info: + text = info["transcription"] + if len(text) <= 0: + continue + if train_re: + # for re + if len(text) == 0: + empty_entity.add(info["id"]) + continue + id2label[info["id"]] = info["label"] + relations.extend([tuple(sorted(l)) for l in info["linking"]]) + # smooth_box + info["bbox"] = self.trans_poly_to_bbox(info["points"]) + + encode_res = self.tokenizer.encode( + text, + pad_to_max_seq_len=False, + return_attention_mask=True, + return_token_type_ids=True, + ) + + if not self.add_special_ids: + # TODO: use tok.all_special_ids to remove + encode_res["input_ids"] = encode_res["input_ids"][1:-1] + encode_res["token_type_ids"] = encode_res["token_type_ids"][1:-1] + encode_res["attention_mask"] = encode_res["attention_mask"][1:-1] + + if self.use_textline_bbox_info: + bbox = [info["bbox"]] * len(encode_res["input_ids"]) + else: + bbox = self.split_bbox( + info["bbox"], info["transcription"], self.tokenizer + ) + if len(bbox) <= 0: + continue + bbox = self._smooth_box(bbox, height, width) + if self.add_special_ids: + bbox.insert(0, [0, 0, 0, 0]) + bbox.append([0, 0, 0, 0]) + + # parse label + if not self.infer_mode: + label = info["label"] + gt_label = self._parse_label(label, encode_res) + + # construct entities for re + if train_re: + if gt_label[0] != self.label2id_map["O"]: + entity_id_to_index_map[info["id"]] = len(entities) + label = label.upper() + entities.append( + { + "start": len(input_ids_list), + "end": len(input_ids_list) + len(encode_res["input_ids"]), + "label": label.upper(), + } + ) + else: + entities.append( + { + "start": len(input_ids_list), + "end": len(input_ids_list) + len(encode_res["input_ids"]), + "label": "O", + } + ) + input_ids_list.extend(encode_res["input_ids"]) + token_type_ids_list.extend(encode_res["token_type_ids"]) + bbox_list.extend(bbox) + words_list.append(text) + segment_offset_id.append(len(input_ids_list)) + if not self.infer_mode: + gt_label_list.extend(gt_label) + + data["input_ids"] = input_ids_list + data["token_type_ids"] = token_type_ids_list + data["bbox"] = bbox_list + data["attention_mask"] = [1] * len(input_ids_list) + data["labels"] = gt_label_list + data["segment_offset_id"] = segment_offset_id + data["tokenizer_params"] = dict( + padding_side=self.tokenizer.padding_side, + pad_token_type_id=self.tokenizer.pad_token_type_id, + pad_token_id=self.tokenizer.pad_token_id, + ) + data["entities"] = entities + + if train_re: + data["relations"] = relations + data["id2label"] = id2label + data["empty_entity"] = empty_entity + data["entity_id_to_index_map"] = entity_id_to_index_map + return data + + def trans_poly_to_bbox(self, poly): + x1 = int(np.min([p[0] for p in poly])) + x2 = int(np.max([p[0] for p in poly])) + y1 = int(np.min([p[1] for p in poly])) + y2 = int(np.max([p[1] for p in poly])) + return [x1, y1, x2, y2] + + def _load_ocr_info(self, data): + if self.infer_mode: + ocr_result = self.ocr_engine.ocr(data["image"], cls=False)[0] + ocr_info = [] + for res in ocr_result: + ocr_info.append( + { + "transcription": res[1][0], + "bbox": self.trans_poly_to_bbox(res[0]), + "points": res[0], + } + ) + return ocr_info + else: + info = data["label"] + # read text info + info_dict = json.loads(info) + return info_dict + + def _smooth_box(self, bboxes, height, width): + bboxes = np.array(bboxes) + bboxes[:, 0] = bboxes[:, 0] * 1000 / width + bboxes[:, 2] = bboxes[:, 2] * 1000 / width + bboxes[:, 1] = bboxes[:, 1] * 1000 / height + bboxes[:, 3] = bboxes[:, 3] * 1000 / height + bboxes = bboxes.astype("int64").tolist() + return bboxes + + def _parse_label(self, label, encode_res): + gt_label = [] + if label.lower() in ["other", "others", "ignore"]: + gt_label.extend([0] * len(encode_res["input_ids"])) + else: + gt_label.append(self.label2id_map[("b-" + label).upper()]) + gt_label.extend( + [self.label2id_map[("i-" + label).upper()]] + * (len(encode_res["input_ids"]) - 1) + ) + return gt_label + + +class MultiLabelEncode(BaseRecLabelEncode): + def __init__( + self, + max_text_length, + character_dict_path=None, + use_space_char=False, + gtc_encode=None, + **kwargs, + ): + super(MultiLabelEncode, self).__init__( + max_text_length, character_dict_path, use_space_char + ) + + self.ctc_encode = CTCLabelEncode( + max_text_length, character_dict_path, use_space_char, **kwargs + ) + self.gtc_encode_type = gtc_encode + if gtc_encode is None: + self.gtc_encode = SARLabelEncode( + max_text_length, character_dict_path, use_space_char, **kwargs + ) + else: + self.gtc_encode = eval(gtc_encode)( + max_text_length, character_dict_path, use_space_char, **kwargs + ) + + def __call__(self, data): + data_ctc = copy.deepcopy(data) + data_gtc = copy.deepcopy(data) + data_out = dict() + data_out["img_path"] = data.get("img_path", None) + data_out["image"] = data["image"] + ctc = self.ctc_encode.__call__(data_ctc) + gtc = self.gtc_encode.__call__(data_gtc) + if ctc is None or gtc is None: + return None + data_out["label_ctc"] = ctc["label"] + if self.gtc_encode_type is not None: + data_out["label_gtc"] = gtc["label"] + else: + data_out["label_sar"] = gtc["label"] + data_out["length"] = ctc["length"] + return data_out + + +class NRTRLabelEncode(BaseRecLabelEncode): + """Convert between text-label and text-index""" + + def __init__( + self, max_text_length, character_dict_path=None, use_space_char=False, **kwargs + ): + super(NRTRLabelEncode, self).__init__( + max_text_length, character_dict_path, use_space_char + ) + + def __call__(self, data): + text = data["label"] + text = self.encode(text) + if text is None: + return None + if len(text) >= self.max_text_len - 1: + return None + data["length"] = np.array(len(text)) + text.insert(0, 2) + text.append(3) + text = text + [0] * (self.max_text_len - len(text)) + data["label"] = np.array(text) + return data + + def add_special_char(self, dict_character): + dict_character = ["blank", "", "", ""] + dict_character + return dict_character + + +class ParseQLabelEncode(BaseRecLabelEncode): + """Convert between text-label and text-index""" + + BOS = "[B]" + EOS = "[E]" + PAD = "[P]" + + def __init__( + self, max_text_length, character_dict_path=None, use_space_char=False, **kwargs + ): + super(ParseQLabelEncode, self).__init__( + max_text_length, character_dict_path, use_space_char + ) + + def __call__(self, data): + text = data["label"] + text = self.encode(text) + if text is None: + return None + if len(text) >= self.max_text_len - 2: + return None + data["length"] = np.array(len(text)) + text = [self.dict[self.BOS]] + text + [self.dict[self.EOS]] + text = text + [self.dict[self.PAD]] * (self.max_text_len - len(text)) + data["label"] = np.array(text) + return data + + def add_special_char(self, dict_character): + dict_character = [self.EOS] + dict_character + [self.BOS, self.PAD] + return dict_character + + +class ViTSTRLabelEncode(BaseRecLabelEncode): + """Convert between text-label and text-index""" + + def __init__( + self, + max_text_length, + character_dict_path=None, + use_space_char=False, + ignore_index=0, + **kwargs, + ): + super(ViTSTRLabelEncode, self).__init__( + max_text_length, character_dict_path, use_space_char + ) + self.ignore_index = ignore_index + + def __call__(self, data): + text = data["label"] + text = self.encode(text) + if text is None: + return None + if len(text) >= self.max_text_len: + return None + data["length"] = np.array(len(text)) + text.insert(0, self.ignore_index) + text.append(1) + text = text + [self.ignore_index] * (self.max_text_len + 2 - len(text)) + data["label"] = np.array(text) + return data + + def add_special_char(self, dict_character): + dict_character = ["", ""] + dict_character + return dict_character + + +class ABINetLabelEncode(BaseRecLabelEncode): + """Convert between text-label and text-index""" + + def __init__( + self, + max_text_length, + character_dict_path=None, + use_space_char=False, + ignore_index=100, + **kwargs, + ): + super(ABINetLabelEncode, self).__init__( + max_text_length, character_dict_path, use_space_char + ) + self.ignore_index = ignore_index + + def __call__(self, data): + text = data["label"] + text = self.encode(text) + if text is None: + return None + if len(text) >= self.max_text_len: + return None + data["length"] = np.array(len(text)) + text.append(0) + text = text + [self.ignore_index] * (self.max_text_len + 1 - len(text)) + data["label"] = np.array(text) + return data + + def add_special_char(self, dict_character): + dict_character = [""] + dict_character + return dict_character + + +class SRLabelEncode(BaseRecLabelEncode): + def __init__( + self, max_text_length, character_dict_path=None, use_space_char=False, **kwargs + ): + super(SRLabelEncode, self).__init__( + max_text_length, character_dict_path, use_space_char + ) + self.dic = {} + with open(character_dict_path, "r") as fin: + for line in fin.readlines(): + line = line.strip() + character, sequence = line.split() + self.dic[character] = sequence + english_stroke_alphabet = "0123456789" + self.english_stroke_dict = {} + for index in range(len(english_stroke_alphabet)): + self.english_stroke_dict[english_stroke_alphabet[index]] = index + + def encode(self, label): + stroke_sequence = "" + for character in label: + if character not in self.dic: + continue + else: + stroke_sequence += self.dic[character] + stroke_sequence += "0" + label = stroke_sequence + + length = len(label) + + input_tensor = np.zeros(self.max_text_len).astype("int64") + for j in range(length - 1): + input_tensor[j + 1] = self.english_stroke_dict[label[j]] + + return length, input_tensor + + def __call__(self, data): + text = data["label"] + length, input_tensor = self.encode(text) + + data["length"] = length + data["input_tensor"] = input_tensor + if text is None: + return None + return data + + +class SPINLabelEncode(AttnLabelEncode): + """Convert between text-label and text-index""" + + def __init__( + self, + max_text_length, + character_dict_path=None, + use_space_char=False, + lower=True, + **kwargs, + ): + super(SPINLabelEncode, self).__init__( + max_text_length, character_dict_path, use_space_char + ) + self.lower = lower + + def add_special_char(self, dict_character): + self.beg_str = "sos" + self.end_str = "eos" + dict_character = [self.beg_str] + [self.end_str] + dict_character + return dict_character + + def __call__(self, data): + text = data["label"] + text = self.encode(text) + if text is None: + return None + if len(text) > self.max_text_len: + return None + data["length"] = np.array(len(text)) + target = [0] + text + [1] + padded_text = [0 for _ in range(self.max_text_len + 2)] + + padded_text[: len(target)] = target + data["label"] = np.array(padded_text) + return data + + +class VLLabelEncode(BaseRecLabelEncode): + """Convert between text-label and text-index""" + + def __init__( + self, max_text_length, character_dict_path=None, use_space_char=False, **kwargs + ): + super(VLLabelEncode, self).__init__( + max_text_length, character_dict_path, use_space_char + ) + self.dict = {} + for i, char in enumerate(self.character): + self.dict[char] = i + + def __call__(self, data): + text = data["label"] # original string + # generate occluded text + len_str = len(text) + if len_str <= 0: + return None + change_num = 1 + order = list(range(len_str)) + change_id = sample(order, change_num)[0] + label_sub = text[change_id] + if change_id == (len_str - 1): + label_res = text[:change_id] + elif change_id == 0: + label_res = text[1:] + else: + label_res = text[:change_id] + text[change_id + 1 :] + + data["label_res"] = label_res # remaining string + data["label_sub"] = label_sub # occluded character + data["label_id"] = change_id # character index + # encode label + text = self.encode(text) + if text is None: + return None + text = [i + 1 for i in text] + data["length"] = np.array(len(text)) + text = text + [0] * (self.max_text_len - len(text)) + data["label"] = np.array(text) + label_res = self.encode(label_res) + label_sub = self.encode(label_sub) + if label_res is None: + label_res = [] + else: + label_res = [i + 1 for i in label_res] + if label_sub is None: + label_sub = [] + else: + label_sub = [i + 1 for i in label_sub] + data["length_res"] = np.array(len(label_res)) + data["length_sub"] = np.array(len(label_sub)) + label_res = label_res + [0] * (self.max_text_len - len(label_res)) + label_sub = label_sub + [0] * (self.max_text_len - len(label_sub)) + data["label_res"] = np.array(label_res) + data["label_sub"] = np.array(label_sub) + return data + + +class CTLabelEncode(object): + def __init__(self, **kwargs): + pass + + def __call__(self, data): + label = data["label"] + + label = json.loads(label) + nBox = len(label) + boxes, txts = [], [] + for bno in range(0, nBox): + box = label[bno]["points"] + box = np.array(box) + + boxes.append(box) + txt = label[bno]["transcription"] + txts.append(txt) + + if len(boxes) == 0: + return None + + data["polys"] = boxes + data["texts"] = txts + return data + + +class CANLabelEncode(BaseRecLabelEncode): + def __init__( + self, + character_dict_path, + max_text_length=100, + use_space_char=False, + lower=True, + **kwargs, + ): + super(CANLabelEncode, self).__init__( + max_text_length, character_dict_path, use_space_char, lower + ) + + def encode(self, text_seq): + text_seq_encoded = [] + for text in text_seq: + if text not in self.character: + continue + text_seq_encoded.append(self.dict.get(text)) + if len(text_seq_encoded) == 0: + return None + return text_seq_encoded + + def __call__(self, data): + label = data["label"] + if isinstance(label, str): + label = label.strip().split() + label.append(self.end_str) + data["label"] = self.encode(label) + return data + + +class CPPDLabelEncode(BaseRecLabelEncode): + """Convert between text-label and text-index""" + + def __init__( + self, + max_text_length, + character_dict_path=None, + use_space_char=False, + ch=False, + ignore_index=100, + **kwargs, + ): + super(CPPDLabelEncode, self).__init__( + max_text_length, character_dict_path, use_space_char + ) + self.ch = ch + self.ignore_index = ignore_index + + def __call__(self, data): + text = data["label"] + if self.ch: + text, text_node_index, text_node_num = self.encodech(text) + if text is None: + return None + if len(text) > self.max_text_len: + return None + data["length"] = np.array(len(text)) + + text_pos_node = [1] * (len(text) + 1) + [0] * ( + self.max_text_len - len(text) + ) + + text.append(0) # eos + text = text + [self.ignore_index] * (self.max_text_len + 1 - len(text)) + + data["label"] = np.array(text) + data["label_node"] = np.array(text_node_num + text_pos_node) + data["label_index"] = np.array(text_node_index) + return data + else: + text, text_char_node, ch_order = self.encode(text) + if text is None: + return None + if len(text) >= self.max_text_len: + return None + data["length"] = np.array(len(text)) + + text_pos_node = [1] * (len(text) + 1) + [0] * ( + self.max_text_len - len(text) + ) + + text.append(0) # eos + + text = text + [self.ignore_index] * (self.max_text_len + 1 - len(text)) + data["label"] = np.array(text) + data["label_node"] = np.array(text_char_node + text_pos_node) + data["label_order"] = np.array(ch_order) + + return data + + def add_special_char(self, dict_character): + dict_character = [""] + dict_character + self.num_character = len(dict_character) + return dict_character + + def encode(self, text): + """ """ + if len(text) == 0 or len(text) > self.max_text_len: + return None, None, None + if self.lower: + text = text.lower() + text_node = [0 for _ in range(self.num_character)] + text_node[0] = 1 + text_list = [] + ch_order = [] + order = 1 + for char in text: + if char not in self.dict: + continue + text_list.append(self.dict[char]) + text_node[self.dict[char]] += 1 + ch_order.append([self.dict[char], text_node[self.dict[char]], order]) + order += 1 + + no_ch_order = [] + for char in self.character: + if char not in text: + no_ch_order.append([self.dict[char], 1, 0]) + random.shuffle(no_ch_order) + ch_order = ch_order + no_ch_order + ch_order = ch_order[: self.max_text_len + 1] + + if len(text_list) == 0: + return None, None, None + return text_list, text_node, ch_order.sort() + + def encodech(self, text): + """ """ + if len(text) == 0 or len(text) > self.max_text_len: + return None, None, None + if self.lower: + text = text.lower() + text_node_dict = {} + text_node_dict.update({0: 1}) + character_index = [_ for _ in range(self.num_character)] + text_list = [] + for char in text: + if char not in self.dict: + continue + i_c = self.dict[char] + text_list.append(i_c) + if i_c in text_node_dict.keys(): + text_node_dict[i_c] += 1 + else: + text_node_dict.update({i_c: 1}) + for ic in list(text_node_dict.keys()): + character_index.remove(ic) + none_char_index = sample(character_index, 37 - len(list(text_node_dict.keys()))) + for ic in none_char_index: + text_node_dict[ic] = 0 + + text_node_index = sorted(text_node_dict) + text_node_num = [text_node_dict[k] for k in text_node_index] + if len(text_list) == 0: + return None, None, None + return text_list, text_node_index, text_node_num + + +class LatexOCRLabelEncode(object): + def __init__( + self, + rec_char_dict_path, + **kwargs, + ): + from tokenizers import Tokenizer as TokenizerFast + + self.tokenizer = TokenizerFast.from_file(rec_char_dict_path) + self.model_input_names = ["input_ids", "token_type_ids", "attention_mask"] + self.pad_token_id = 0 + self.bos_token_id = 1 + self.eos_token_id = 2 + + def _convert_encoding( + self, + encoding, + return_token_type_ids=None, + return_attention_mask=None, + return_overflowing_tokens=False, + return_special_tokens_mask=False, + return_offsets_mapping=False, + return_length=False, + verbose=True, + ): + + if return_token_type_ids is None: + return_token_type_ids = "token_type_ids" in self.model_input_names + if return_attention_mask is None: + return_attention_mask = "attention_mask" in self.model_input_names + + if return_overflowing_tokens and encoding.overflowing is not None: + encodings = [encoding] + encoding.overflowing + else: + encodings = [encoding] + + encoding_dict = defaultdict(list) + for e in encodings: + encoding_dict["input_ids"].append(e.ids) + + if return_token_type_ids: + encoding_dict["token_type_ids"].append(e.type_ids) + if return_attention_mask: + encoding_dict["attention_mask"].append(e.attention_mask) + if return_special_tokens_mask: + encoding_dict["special_tokens_mask"].append(e.special_tokens_mask) + if return_offsets_mapping: + encoding_dict["offset_mapping"].append(e.offsets) + if return_length: + encoding_dict["length"].append(len(e.ids)) + + return encoding_dict, encodings + + def encode( + self, + text, + text_pair=None, + return_token_type_ids=False, + add_special_tokens=True, + is_split_into_words=False, + ): + batched_input = text + encodings = self.tokenizer.encode_batch( + batched_input, + add_special_tokens=add_special_tokens, + is_pretokenized=is_split_into_words, + ) + tokens_and_encodings = [ + self._convert_encoding( + encoding=encoding, + return_token_type_ids=False, + return_attention_mask=None, + return_overflowing_tokens=False, + return_special_tokens_mask=False, + return_offsets_mapping=False, + return_length=False, + verbose=True, + ) + for encoding in encodings + ] + sanitized_tokens = {} + for key in tokens_and_encodings[0][0].keys(): + stack = [e for item, _ in tokens_and_encodings for e in item[key]] + sanitized_tokens[key] = stack + return sanitized_tokens + + def __call__(self, eqs): + topk = self.encode(eqs) + for k, p in zip(topk, [[self.bos_token_id, self.eos_token_id], [1, 1]]): + process_seq = [[p[0]] + x + [p[1]] for x in topk[k]] + max_length = 0 + for seq in process_seq: + max_length = max(max_length, len(seq)) + labels = np.zeros((len(process_seq), max_length), dtype="int64") + for idx, seq in enumerate(process_seq): + l = len(seq) + labels[idx][:l] = seq + topk[k] = labels + return ( + np.array(topk["input_ids"]).astype(np.int64), + np.array(topk["attention_mask"]).astype(np.int64), + max_length, + ) + + +class ExplicitEnum(str, Enum): + """ + Enum with more explicit error message for missing values. + """ + + @classmethod + def _missing_(cls, value): + raise ValueError( + f"{value} is not a valid {cls.__name__}, please select one of {list(cls._value2member_map_.keys())}" + ) + + +class TruncationStrategy(ExplicitEnum): + """ + Possible values for the `truncation` argument in [`PreTrainedTokenizerBase.__call__`]. Useful for tab-completion in + an IDE. + """ + + ONLY_FIRST = "only_first" + ONLY_SECOND = "only_second" + LONGEST_FIRST = "longest_first" + DO_NOT_TRUNCATE = "do_not_truncate" + + +class PaddingStrategy(ExplicitEnum): + """ + Possible values for the `padding` argument in [`PreTrainedTokenizerBase.__call__`]. Useful for tab-completion in an + IDE. + """ + + LONGEST = "longest" + MAX_LENGTH = "max_length" + DO_NOT_PAD = "do_not_pad" + + +class UniMERNetLabelEncode(object): + + SPECIAL_TOKENS_ATTRIBUTES = [ + "bos_token", + "eos_token", + "unk_token", + "sep_token", + "pad_token", + "cls_token", + "mask_token", + "additional_special_tokens", + ] + + def __init__( + self, + rec_char_dict_path, + max_seq_len, + **kwargs, + ): + from tokenizers import Tokenizer as TokenizerFast + from tokenizers import AddedToken + + self._unk_token = "" + self._bos_token = "" + self._eos_token = "" + self._pad_token = "" + self._sep_token = None + self._cls_token = None + self._mask_token = None + self._additional_special_tokens = [] + self.model_input_names = ["input_ids", "token_type_ids", "attention_mask"] + self.max_seq_len = max_seq_len + self.pad_token_id = 1 + self.bos_token_id = 0 + self.eos_token_id = 2 + self.padding_side = "right" + self.pad_token = "" + self.pad_token_type_id = 0 + self.pad_to_multiple_of = None + fast_tokenizer_file = os.path.join(rec_char_dict_path, "tokenizer.json") + tokenizer_config_file = os.path.join( + rec_char_dict_path, "tokenizer_config.json" + ) + self.tokenizer = TokenizerFast.from_file(fast_tokenizer_file) + added_tokens_decoder = {} + added_tokens_map = {} + + if tokenizer_config_file is not None: + with open( + tokenizer_config_file, encoding="utf-8" + ) as tokenizer_config_handle: + init_kwargs = json.load(tokenizer_config_handle) + if "added_tokens_decoder" in init_kwargs: + for idx, token in init_kwargs["added_tokens_decoder"].items(): + if isinstance(token, dict): + token = AddedToken(**token) + if isinstance(token, AddedToken): + added_tokens_decoder[int(idx)] = token + added_tokens_map[str(token)] = token + else: + raise ValueError( + f"Found a {token.__class__} in the saved `added_tokens_decoder`, should be a dictionary or an AddedToken instance" + ) + init_kwargs["added_tokens_decoder"] = added_tokens_decoder + added_tokens_decoder = init_kwargs.pop("added_tokens_decoder", {}) + tokens_to_add = [ + token + for index, token in sorted( + added_tokens_decoder.items(), key=lambda x: x[0] + ) + if token not in added_tokens_decoder + ] + added_tokens_encoder = self.added_tokens_encoder(added_tokens_decoder) + encoder = list(added_tokens_encoder.keys()) + [ + str(token) for token in tokens_to_add + ] + tokens_to_add += [ + token + for token in self.all_special_tokens_extended + if token not in encoder and token not in tokens_to_add + ] + if len(tokens_to_add) > 0: + is_last_special = None + tokens = [] + special_tokens = self.all_special_tokens + for token in tokens_to_add: + is_special = ( + (token.special or str(token) in special_tokens) + if isinstance(token, AddedToken) + else str(token) in special_tokens + ) + if is_last_special is None or is_last_special == is_special: + tokens.append(token) + else: + self._add_tokens(tokens, special_tokens=is_last_special) + tokens = [token] + is_last_special = is_special + if tokens: + self._add_tokens(tokens, special_tokens=is_last_special) + + def _add_tokens(self, new_tokens, special_tokens=False) -> int: + if special_tokens: + return self.tokenizer.add_special_tokens(new_tokens) + + return self.tokenizer.add_tokens(new_tokens) + + def added_tokens_encoder(self, added_tokens_decoder): + return { + k.content: v + for v, k in sorted(added_tokens_decoder.items(), key=lambda item: item[0]) + } + + @property + def all_special_tokens(self): + all_toks = [str(s) for s in self.all_special_tokens_extended] + return all_toks + + @property + def all_special_tokens_extended(self): + all_tokens = [] + seen = set() + for value in self.special_tokens_map_extended.values(): + if isinstance(value, (list, tuple)): + tokens_to_add = [token for token in value if str(token) not in seen] + else: + tokens_to_add = [value] if str(value) not in seen else [] + seen.update(map(str, tokens_to_add)) + all_tokens.extend(tokens_to_add) + return all_tokens + + @property + def special_tokens_map_extended(self): + set_attr = {} + for attr in self.SPECIAL_TOKENS_ATTRIBUTES: + attr_value = getattr(self, "_" + attr) + if attr_value: + set_attr[attr] = attr_value + return set_attr + + def set_truncation_and_padding( + self, + padding_strategy, + truncation_strategy, + max_length, + stride, + pad_to_multiple_of, + ): + _truncation = self.tokenizer.truncation + _padding = self.tokenizer.padding + # Set truncation and padding on the backend tokenizer + if truncation_strategy == TruncationStrategy.DO_NOT_TRUNCATE: + if _truncation is not None: + self._tokenizer.no_truncation() + else: + target = { + "max_length": max_length, + "stride": stride, + "strategy": truncation_strategy.value, + "direction": "right", + } + + if _truncation is None: + current = None + else: + current = {k: _truncation.get(k, None) for k in target} + + if current != target: + self.tokenizer.enable_truncation(**target) + if padding_strategy == PaddingStrategy.DO_NOT_PAD: + if _padding is not None: + self.tokenizer.no_padding() + else: + length = ( + max_length if padding_strategy == PaddingStrategy.MAX_LENGTH else None + ) + target = { + "length": length, + "direction": self.padding_side, + "pad_id": self.pad_token_id, + "pad_token": self.pad_token, + "pad_type_id": self.pad_token_type_id, + "pad_to_multiple_of": pad_to_multiple_of, + } + if _padding != target: + self.tokenizer.enable_padding(**target) + + def _convert_encoding( + self, + encoding, + return_token_type_ids=None, + return_attention_mask=None, + return_overflowing_tokens=False, + return_special_tokens_mask=False, + return_offsets_mapping=False, + return_length=False, + verbose=True, + ): + + if return_token_type_ids is None: + return_token_type_ids = "token_type_ids" in self.model_input_names + if return_attention_mask is None: + return_attention_mask = "attention_mask" in self.model_input_names + + if return_overflowing_tokens and encoding.overflowing is not None: + encodings = [encoding] + encoding.overflowing + else: + encodings = [encoding] + + encoding_dict = defaultdict(list) + for e in encodings: + encoding_dict["input_ids"].append(e.ids) + if return_token_type_ids: + encoding_dict["token_type_ids"].append(e.type_ids) + if return_attention_mask: + encoding_dict["attention_mask"].append(e.attention_mask) + if return_special_tokens_mask: + encoding_dict["special_tokens_mask"].append(e.special_tokens_mask) + if return_offsets_mapping: + encoding_dict["offset_mapping"].append(e.offsets) + if return_length: + encoding_dict["length"].append(len(e.ids)) + + return encoding_dict, encodings + + def encode( + self, + text, + text_pair=None, + return_token_type_ids=False, + add_special_tokens=True, + is_split_into_words=False, + ): + batched_input = text + self.set_truncation_and_padding( + padding_strategy=PaddingStrategy.LONGEST, + truncation_strategy=TruncationStrategy.LONGEST_FIRST, + max_length=self.max_seq_len, + stride=0, + pad_to_multiple_of=None, + ) + encodings = self.tokenizer.encode_batch( + batched_input, + add_special_tokens=add_special_tokens, + is_pretokenized=is_split_into_words, + ) + + tokens_and_encodings = [ + self._convert_encoding( + encoding=encoding, + return_token_type_ids=False, + return_attention_mask=None, + return_overflowing_tokens=False, + return_special_tokens_mask=False, + return_offsets_mapping=False, + return_length=False, + verbose=True, + ) + for encoding in encodings + ] + sanitized_tokens = {} + for key in tokens_and_encodings[0][0].keys(): + stack = [e for item, _ in tokens_and_encodings for e in item[key]] + sanitized_tokens[key] = stack + return sanitized_tokens + + def __call__(self, data): + eqs = data["label"] + topk = self.encode([eqs]) + for k, p in zip(topk, [[self.bos_token_id, self.eos_token_id], [1, 1]]): + process_seq = [x for x in topk[k]] + max_length = 0 + for seq in process_seq: + max_length = max(max_length, len(seq)) + data["label"] = np.array(topk["input_ids"]).astype(np.int64)[0] + data["attention_mask"] = np.array(topk["attention_mask"]).astype(np.int64)[0] + return data diff --git a/modules/onnx_ocr_module/src/ppocr/data/imaug/latex_ocr_aug.py b/modules/onnx_ocr_module/src/ppocr/data/imaug/latex_ocr_aug.py new file mode 100644 index 0000000..6417dab --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/data/imaug/latex_ocr_aug.py @@ -0,0 +1,183 @@ +# copyright (c) 2024 PaddlePaddle Authors. All Rights Reserve. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +""" +This code is refer from: +https://github.com/lukas-blecher/LaTeX-OCR/blob/main/pix2tex/dataset/transforms.py +""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function +from __future__ import unicode_literals + +import os + +os.environ["NO_ALBUMENTATIONS_UPDATE"] = "1" + +import math +import cv2 +import numpy as np +import albumentations as A +from PIL import Image + + +class LatexTrainTransform: + def __init__(self, bitmap_prob=0.04, **kwargs): + # your init code + self.bitmap_prob = bitmap_prob + self.train_transform = A.Compose( + [ + A.Compose( + [ + A.ShiftScaleRotate( + shift_limit=0, + scale_limit=(-0.15, 0), + rotate_limit=1, + border_mode=0, + interpolation=3, + value=[255, 255, 255], + p=1, + ), + A.GridDistortion( + distort_limit=0.1, + border_mode=0, + interpolation=3, + value=[255, 255, 255], + p=0.5, + ), + ], + p=0.15, + ), + A.RGBShift(r_shift_limit=15, g_shift_limit=15, b_shift_limit=15, p=0.3), + A.GaussNoise(10, p=0.2), + A.RandomBrightnessContrast(0.05, (-0.2, 0), True, p=0.2), + A.ImageCompression(95, p=0.3), + A.ToGray(always_apply=True), + ] + ) + + def __call__(self, data): + img = data["image"] + if np.random.random() < self.bitmap_prob: + img[img != 255] = 0 + img = self.train_transform(image=img)["image"] + data["image"] = img + return data + + +class LatexTestTransform: + def __init__(self, **kwargs): + # your init code + self.test_transform = A.Compose( + [ + A.ToGray(always_apply=True), + ] + ) + + def __call__(self, data): + img = data["image"] + img = self.test_transform(image=img)["image"] + data["image"] = img + return data + + +class MinMaxResize: + def __init__(self, min_dimensions=[32, 32], max_dimensions=[672, 192], **kwargs): + # your init code + self.min_dimensions = min_dimensions + self.max_dimensions = max_dimensions + # pass + + def pad_(self, img, divable=32): + threshold = 128 + data = np.array(img.convert("LA")) + if data[..., -1].var() == 0: + data = (data[..., 0]).astype(np.uint8) + else: + data = (255 - data[..., -1]).astype(np.uint8) + data = (data - data.min()) / (data.max() - data.min()) * 255 + if data.mean() > threshold: + # To invert the text to white + gray = 255 * (data < threshold).astype(np.uint8) + else: + gray = 255 * (data > threshold).astype(np.uint8) + data = 255 - data + + coords = cv2.findNonZero(gray) # Find all non-zero points (text) + a, b, w, h = cv2.boundingRect(coords) # Find minimum spanning bounding box + rect = data[b : b + h, a : a + w] + im = Image.fromarray(rect).convert("L") + dims = [] + for x in [w, h]: + div, mod = divmod(x, divable) + dims.append(divable * (div + (1 if mod > 0 else 0))) + padded = Image.new("L", dims, 255) + padded.paste(im, (0, 0, im.size[0], im.size[1])) + return padded + + def minmax_size_(self, img, max_dimensions, min_dimensions): + if max_dimensions is not None: + ratios = [a / b for a, b in zip(img.size, max_dimensions)] + if any([r > 1 for r in ratios]): + size = np.array(img.size) // max(ratios) + img = img.resize(tuple(size.astype(int)), Image.BILINEAR) + if min_dimensions is not None: + # hypothesis: there is a dim in img smaller than min_dimensions, and return a proper dim >= min_dimensions + padded_size = [ + max(img_dim, min_dim) + for img_dim, min_dim in zip(img.size, min_dimensions) + ] + if padded_size != list(img.size): # assert hypothesis + padded_im = Image.new("L", padded_size, 255) + padded_im.paste(img, img.getbbox()) + img = padded_im + return img + + def __call__(self, data): + img = data["image"] + h, w = img.shape[:2] + if ( + self.min_dimensions[0] <= w <= self.max_dimensions[0] + and self.min_dimensions[1] <= h <= self.max_dimensions[1] + ): + return data + else: + im = Image.fromarray(np.uint8(img)) + im = self.minmax_size_( + self.pad_(im), self.max_dimensions, self.min_dimensions + ) + im = np.array(im) + im = np.dstack((im, im, im)) + data["image"] = im + return data + + +class LatexImageFormat: + def __init__(self, **kwargs): + # your init code + pass + + def __call__(self, data): + img = data["image"] + im_h, im_w = img.shape[:2] + divide_h = math.ceil(im_h / 16) * 16 + divide_w = math.ceil(im_w / 16) * 16 + img = img[:, :, 0] + img = np.pad( + img, ((0, divide_h - im_h), (0, divide_w - im_w)), constant_values=(1, 1) + ) + img_expanded = img[:, :, np.newaxis].transpose(2, 0, 1) + data["image"] = img_expanded + return data diff --git a/modules/onnx_ocr_module/src/ppocr/data/imaug/make_border_map.py b/modules/onnx_ocr_module/src/ppocr/data/imaug/make_border_map.py new file mode 100644 index 0000000..9d25319 --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/data/imaug/make_border_map.py @@ -0,0 +1,179 @@ +# copyright (c) 2020 PaddlePaddle Authors. All Rights Reserve. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +""" +This code is refer from: +https://github.com/WenmuZhou/DBNet.pytorch/blob/master/data_loader/modules/make_border_map.py +""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function +from __future__ import unicode_literals + +import numpy as np +import cv2 + +np.seterr(divide="ignore", invalid="ignore") +import pyclipper +from shapely.geometry import Polygon +import sys +import warnings + +warnings.simplefilter("ignore") + +__all__ = ["MakeBorderMap"] + + +class MakeBorderMap(object): + def __init__(self, shrink_ratio=0.4, thresh_min=0.3, thresh_max=0.7, **kwargs): + self.shrink_ratio = shrink_ratio + self.thresh_min = thresh_min + self.thresh_max = thresh_max + if "total_epoch" in kwargs and "epoch" in kwargs and kwargs["epoch"] != "None": + self.shrink_ratio = self.shrink_ratio + 0.2 * kwargs["epoch"] / float( + kwargs["total_epoch"] + ) + + def __call__(self, data): + img = data["image"] + text_polys = data["polys"] + ignore_tags = data["ignore_tags"] + + canvas = np.zeros(img.shape[:2], dtype=np.float32) + mask = np.zeros(img.shape[:2], dtype=np.float32) + + for i in range(len(text_polys)): + if ignore_tags[i]: + continue + self.draw_border_map(text_polys[i], canvas, mask=mask) + canvas = canvas * (self.thresh_max - self.thresh_min) + self.thresh_min + + data["threshold_map"] = canvas + data["threshold_mask"] = mask + return data + + def draw_border_map(self, polygon, canvas, mask): + polygon = np.array(polygon) + assert polygon.ndim == 2 + assert polygon.shape[1] == 2 + + polygon_shape = Polygon(polygon) + if polygon_shape.area <= 0: + return + distance = ( + polygon_shape.area + * (1 - np.power(self.shrink_ratio, 2)) + / polygon_shape.length + ) + subject = [tuple(l) for l in polygon] + padding = pyclipper.PyclipperOffset() + padding.AddPath(subject, pyclipper.JT_ROUND, pyclipper.ET_CLOSEDPOLYGON) + + padded_polygon = np.array(padding.Execute(distance)[0]) + cv2.fillPoly(mask, [padded_polygon.astype(np.int32)], 1.0) + + xmin = padded_polygon[:, 0].min() + xmax = padded_polygon[:, 0].max() + ymin = padded_polygon[:, 1].min() + ymax = padded_polygon[:, 1].max() + width = xmax - xmin + 1 + height = ymax - ymin + 1 + + polygon[:, 0] = polygon[:, 0] - xmin + polygon[:, 1] = polygon[:, 1] - ymin + + xs = np.broadcast_to( + np.linspace(0, width - 1, num=width).reshape(1, width), (height, width) + ) + ys = np.broadcast_to( + np.linspace(0, height - 1, num=height).reshape(height, 1), (height, width) + ) + + distance_map = np.zeros((polygon.shape[0], height, width), dtype=np.float32) + for i in range(polygon.shape[0]): + j = (i + 1) % polygon.shape[0] + absolute_distance = self._distance(xs, ys, polygon[i], polygon[j]) + distance_map[i] = np.clip(absolute_distance / distance, 0, 1) + distance_map = distance_map.min(axis=0) + + xmin_valid = min(max(0, xmin), canvas.shape[1] - 1) + xmax_valid = min(max(0, xmax), canvas.shape[1] - 1) + ymin_valid = min(max(0, ymin), canvas.shape[0] - 1) + ymax_valid = min(max(0, ymax), canvas.shape[0] - 1) + canvas[ymin_valid : ymax_valid + 1, xmin_valid : xmax_valid + 1] = np.fmax( + 1 + - distance_map[ + ymin_valid - ymin : ymax_valid - ymax + height, + xmin_valid - xmin : xmax_valid - xmax + width, + ], + canvas[ymin_valid : ymax_valid + 1, xmin_valid : xmax_valid + 1], + ) + + def _distance(self, xs, ys, point_1, point_2): + """ + compute the distance from point to a line + ys: coordinates in the first axis + xs: coordinates in the second axis + point_1, point_2: (x, y), the end of the line + """ + height, width = xs.shape[:2] + square_distance_1 = np.square(xs - point_1[0]) + np.square(ys - point_1[1]) + square_distance_2 = np.square(xs - point_2[0]) + np.square(ys - point_2[1]) + square_distance = np.square(point_1[0] - point_2[0]) + np.square( + point_1[1] - point_2[1] + ) + + cosin = (square_distance - square_distance_1 - square_distance_2) / ( + 2 * np.sqrt(square_distance_1 * square_distance_2) + ) + square_sin = 1 - np.square(cosin) + square_sin = np.nan_to_num(square_sin) + result = np.sqrt( + square_distance_1 * square_distance_2 * square_sin / square_distance + ) + + result[cosin < 0] = np.sqrt(np.fmin(square_distance_1, square_distance_2))[ + cosin < 0 + ] + # self.extend_line(point_1, point_2, result) + return result + + def extend_line(self, point_1, point_2, result, shrink_ratio): + ex_point_1 = ( + int(round(point_1[0] + (point_1[0] - point_2[0]) * (1 + shrink_ratio))), + int(round(point_1[1] + (point_1[1] - point_2[1]) * (1 + shrink_ratio))), + ) + cv2.line( + result, + tuple(ex_point_1), + tuple(point_1), + 4096.0, + 1, + lineType=cv2.LINE_AA, + shift=0, + ) + ex_point_2 = ( + int(round(point_2[0] + (point_2[0] - point_1[0]) * (1 + shrink_ratio))), + int(round(point_2[1] + (point_2[1] - point_1[1]) * (1 + shrink_ratio))), + ) + cv2.line( + result, + tuple(ex_point_2), + tuple(point_2), + 4096.0, + 1, + lineType=cv2.LINE_AA, + shift=0, + ) + return ex_point_1, ex_point_2 diff --git a/modules/onnx_ocr_module/src/ppocr/data/imaug/make_pse_gt.py b/modules/onnx_ocr_module/src/ppocr/data/imaug/make_pse_gt.py new file mode 100644 index 0000000..7be1bb8 --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/data/imaug/make_pse_gt.py @@ -0,0 +1,104 @@ +# copyright (c) 2021 PaddlePaddle Authors. All Rights Reserve. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function +from __future__ import unicode_literals + +import cv2 +import numpy as np +import pyclipper +from shapely.geometry import Polygon + +__all__ = ["MakePseGt"] + + +class MakePseGt(object): + def __init__(self, kernel_num=7, size=640, min_shrink_ratio=0.4, **kwargs): + self.kernel_num = kernel_num + self.min_shrink_ratio = min_shrink_ratio + self.size = size + + def __call__(self, data): + image = data["image"] + text_polys = data["polys"] + ignore_tags = data["ignore_tags"] + + h, w, _ = image.shape + short_edge = min(h, w) + if short_edge < self.size: + # keep short_size >= self.size + scale = self.size / short_edge + image = cv2.resize(image, dsize=None, fx=scale, fy=scale) + text_polys *= scale + + gt_kernels = [] + for i in range(1, self.kernel_num + 1): + # s1->sn, from big to small + rate = 1.0 - (1.0 - self.min_shrink_ratio) / (self.kernel_num - 1) * i + text_kernel, ignore_tags = self.generate_kernel( + image.shape[0:2], rate, text_polys, ignore_tags + ) + gt_kernels.append(text_kernel) + + training_mask = np.ones(image.shape[0:2], dtype="uint8") + for i in range(text_polys.shape[0]): + if ignore_tags[i]: + cv2.fillPoly( + training_mask, text_polys[i].astype(np.int32)[np.newaxis, :, :], 0 + ) + + gt_kernels = np.array(gt_kernels) + gt_kernels[gt_kernels > 0] = 1 + + data["image"] = image + data["polys"] = text_polys + data["gt_kernels"] = gt_kernels[0:] + data["gt_text"] = gt_kernels[0] + data["mask"] = training_mask.astype("float32") + return data + + def generate_kernel(self, img_size, shrink_ratio, text_polys, ignore_tags=None): + """ + Refer to part of the code: + https://github.com/open-mmlab/mmocr/blob/main/mmocr/datasets/pipelines/textdet_targets/base_textdet_targets.py + """ + + h, w = img_size + text_kernel = np.zeros((h, w), dtype=np.float32) + for i, poly in enumerate(text_polys): + polygon = Polygon(poly) + distance = ( + polygon.area + * (1 - shrink_ratio * shrink_ratio) + / (polygon.length + 1e-6) + ) + subject = [tuple(l) for l in poly] + pco = pyclipper.PyclipperOffset() + pco.AddPath(subject, pyclipper.JT_ROUND, pyclipper.ET_CLOSEDPOLYGON) + shrunk = np.array(pco.Execute(-distance)) + + if len(shrunk) == 0 or shrunk.size == 0: + if ignore_tags is not None: + ignore_tags[i] = True + continue + try: + shrunk = np.array(shrunk[0]).reshape(-1, 2) + except: + if ignore_tags is not None: + ignore_tags[i] = True + continue + cv2.fillPoly(text_kernel, [shrunk.astype(np.int32)], i + 1) + return text_kernel, ignore_tags diff --git a/modules/onnx_ocr_module/src/ppocr/data/imaug/make_shrink_map.py b/modules/onnx_ocr_module/src/ppocr/data/imaug/make_shrink_map.py new file mode 100644 index 0000000..0e2f3d4 --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/data/imaug/make_shrink_map.py @@ -0,0 +1,125 @@ +# copyright (c) 2020 PaddlePaddle Authors. All Rights Reserve. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +""" +This code is refer from: +https://github.com/WenmuZhou/DBNet.pytorch/blob/master/data_loader/modules/make_shrink_map.py +""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function +from __future__ import unicode_literals + +import numpy as np +import cv2 +from shapely.geometry import Polygon +import pyclipper + +__all__ = ["MakeShrinkMap"] + + +class MakeShrinkMap(object): + r""" + Making binary mask from detection data with ICDAR format. + Typically following the process of class `MakeICDARData`. + """ + + def __init__(self, min_text_size=8, shrink_ratio=0.4, **kwargs): + self.min_text_size = min_text_size + self.shrink_ratio = shrink_ratio + if "total_epoch" in kwargs and "epoch" in kwargs and kwargs["epoch"] != "None": + self.shrink_ratio = self.shrink_ratio + 0.2 * kwargs["epoch"] / float( + kwargs["total_epoch"] + ) + + def __call__(self, data): + image = data["image"] + text_polys = data["polys"] + ignore_tags = data["ignore_tags"] + + h, w = image.shape[:2] + text_polys, ignore_tags = self.validate_polygons(text_polys, ignore_tags, h, w) + gt = np.zeros((h, w), dtype=np.float32) + mask = np.ones((h, w), dtype=np.float32) + for i in range(len(text_polys)): + polygon = text_polys[i] + height = max(polygon[:, 1]) - min(polygon[:, 1]) + width = max(polygon[:, 0]) - min(polygon[:, 0]) + if ignore_tags[i] or min(height, width) < self.min_text_size: + cv2.fillPoly(mask, polygon.astype(np.int32)[np.newaxis, :, :], 0) + ignore_tags[i] = True + else: + polygon_shape = Polygon(polygon) + subject = [tuple(l) for l in polygon] + padding = pyclipper.PyclipperOffset() + padding.AddPath(subject, pyclipper.JT_ROUND, pyclipper.ET_CLOSEDPOLYGON) + shrunk = [] + + # Increase the shrink ratio every time we get multiple polygon returned back + possible_ratios = np.arange(self.shrink_ratio, 1, self.shrink_ratio) + np.append(possible_ratios, 1) + # print(possible_ratios) + for ratio in possible_ratios: + # print(f"Change shrink ratio to {ratio}") + distance = ( + polygon_shape.area + * (1 - np.power(ratio, 2)) + / polygon_shape.length + ) + shrunk = padding.Execute(-distance) + if len(shrunk) == 1: + break + + if shrunk == []: + cv2.fillPoly(mask, polygon.astype(np.int32)[np.newaxis, :, :], 0) + ignore_tags[i] = True + continue + + for each_shrink in shrunk: + shrink = np.array(each_shrink).reshape(-1, 2) + cv2.fillPoly(gt, [shrink.astype(np.int32)], 1) + + data["shrink_map"] = gt + data["shrink_mask"] = mask + return data + + def validate_polygons(self, polygons, ignore_tags, h, w): + """ + polygons (numpy.array, required): of shape (num_instances, num_points, 2) + """ + if len(polygons) == 0: + return polygons, ignore_tags + assert len(polygons) == len(ignore_tags) + for polygon in polygons: + polygon[:, 0] = np.clip(polygon[:, 0], 0, w - 1) + polygon[:, 1] = np.clip(polygon[:, 1], 0, h - 1) + + for i in range(len(polygons)): + area = self.polygon_area(polygons[i]) + if abs(area) < 1: + ignore_tags[i] = True + if area > 0: + polygons[i] = polygons[i][::-1, :] + return polygons, ignore_tags + + def polygon_area(self, polygon): + """ + compute polygon area + """ + area = 0 + q = polygon[-1] + for p in polygon: + area += p[0] * q[1] - p[1] * q[0] + q = p + return area / 2.0 diff --git a/modules/onnx_ocr_module/src/ppocr/data/imaug/operators.py b/modules/onnx_ocr_module/src/ppocr/data/imaug/operators.py new file mode 100644 index 0000000..b5a72a4 --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/data/imaug/operators.py @@ -0,0 +1,523 @@ +""" +# Copyright (c) 2020 PaddlePaddle Authors. All Rights Reserved +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function +from __future__ import unicode_literals + +import sys +import cv2 +import numpy as np +import math +from PIL import Image + + +class DecodeImage(object): + """decode image""" + + def __init__( + self, img_mode="RGB", channel_first=False, ignore_orientation=False, **kwargs + ): + self.img_mode = img_mode + self.channel_first = channel_first + self.ignore_orientation = ignore_orientation + + def __call__(self, data): + img = data["image"] + assert type(img) is bytes and len(img) > 0, "invalid input 'img' in DecodeImage" + img = np.frombuffer(img, dtype="uint8") + if self.ignore_orientation: + img = cv2.imdecode(img, cv2.IMREAD_IGNORE_ORIENTATION | cv2.IMREAD_COLOR) + else: + img = cv2.imdecode(img, 1) + if img is None: + return None + if self.img_mode == "GRAY": + img = cv2.cvtColor(img, cv2.COLOR_GRAY2BGR) + elif self.img_mode == "RGB": + assert img.shape[2] == 3, "invalid shape of image[%s]" % (img.shape) + img = img[:, :, ::-1] + + if self.channel_first: + img = img.transpose((2, 0, 1)) + + data["image"] = img + return data + + +class NormalizeImage(object): + """normalize image such as subtract mean, divide std""" + + def __init__(self, scale=None, mean=None, std=None, order="chw", **kwargs): + if isinstance(scale, str): + scale = eval(scale) + self.scale = np.float32(scale if scale is not None else 1.0 / 255.0) + mean = mean if mean is not None else [0.485, 0.456, 0.406] + std = std if std is not None else [0.229, 0.224, 0.225] + + shape = (3, 1, 1) if order == "chw" else (1, 1, 3) + self.mean = np.array(mean).reshape(shape).astype("float32") + self.std = np.array(std).reshape(shape).astype("float32") + + def __call__(self, data): + img = data["image"] + from PIL import Image + + if isinstance(img, Image.Image): + img = np.array(img) + assert isinstance(img, np.ndarray), "invalid input 'img' in NormalizeImage" + data["image"] = (img.astype("float32") * self.scale - self.mean) / self.std + return data + + +class ToCHWImage(object): + """convert hwc image to chw image""" + + def __init__(self, **kwargs): + pass + + def __call__(self, data): + img = data["image"] + from PIL import Image + + if isinstance(img, Image.Image): + img = np.array(img) + data["image"] = img.transpose((2, 0, 1)) + return data + + +class Fasttext(object): + def __init__(self, path="None", **kwargs): + import fasttext + + self.fast_model = fasttext.load_model(path) + + def __call__(self, data): + label = data["label"] + fast_label = self.fast_model[label] + data["fast_label"] = fast_label + return data + + +class KeepKeys(object): + def __init__(self, keep_keys, **kwargs): + self.keep_keys = keep_keys + + def __call__(self, data): + data_list = [] + for key in self.keep_keys: + data_list.append(data[key]) + return data_list + + +class Pad(object): + def __init__(self, size=None, size_div=32, **kwargs): + if size is not None and not isinstance(size, (int, list, tuple)): + raise TypeError( + "Type of target_size is invalid. Now is {}".format(type(size)) + ) + if isinstance(size, int): + size = [size, size] + self.size = size + self.size_div = size_div + + def __call__(self, data): + img = data["image"] + img_h, img_w = img.shape[0], img.shape[1] + if self.size: + resize_h2, resize_w2 = self.size + assert ( + img_h < resize_h2 and img_w < resize_w2 + ), "(h, w) of target size should be greater than (img_h, img_w)" + else: + resize_h2 = max( + int(math.ceil(img.shape[0] / self.size_div) * self.size_div), + self.size_div, + ) + resize_w2 = max( + int(math.ceil(img.shape[1] / self.size_div) * self.size_div), + self.size_div, + ) + img = cv2.copyMakeBorder( + img, + 0, + resize_h2 - img_h, + 0, + resize_w2 - img_w, + cv2.BORDER_CONSTANT, + value=0, + ) + data["image"] = img + return data + + +class Resize(object): + def __init__(self, size=(640, 640), **kwargs): + self.size = size + + def resize_image(self, img): + resize_h, resize_w = self.size + ori_h, ori_w = img.shape[:2] # (h, w, c) + ratio_h = float(resize_h) / ori_h + ratio_w = float(resize_w) / ori_w + img = cv2.resize(img, (int(resize_w), int(resize_h))) + return img, [ratio_h, ratio_w] + + def __call__(self, data): + img = data["image"] + if "polys" in data: + text_polys = data["polys"] + + img_resize, [ratio_h, ratio_w] = self.resize_image(img) + if "polys" in data: + new_boxes = [] + for box in text_polys: + new_box = [] + for cord in box: + new_box.append([cord[0] * ratio_w, cord[1] * ratio_h]) + new_boxes.append(new_box) + data["polys"] = np.array(new_boxes, dtype=np.float32) + data["image"] = img_resize + return data + + +class DetResizeForTest(object): + def __init__(self, **kwargs): + super(DetResizeForTest, self).__init__() + self.resize_type = 0 + self.keep_ratio = False + if "image_shape" in kwargs: + self.image_shape = kwargs["image_shape"] + self.resize_type = 1 + if "keep_ratio" in kwargs: + self.keep_ratio = kwargs["keep_ratio"] + elif "limit_side_len" in kwargs: + self.limit_side_len = kwargs["limit_side_len"] + self.limit_type = kwargs.get("limit_type", "min") + elif "resize_long" in kwargs: + self.resize_type = 2 + self.resize_long = kwargs.get("resize_long", 960) + else: + self.limit_side_len = 736 + self.limit_type = "min" + + def __call__(self, data): + img = data["image"] + src_h, src_w, _ = img.shape + if sum([src_h, src_w]) < 64: + img = self.image_padding(img) + + if self.resize_type == 0: + # img, shape = self.resize_image_type0(img) + img, [ratio_h, ratio_w] = self.resize_image_type0(img) + elif self.resize_type == 2: + img, [ratio_h, ratio_w] = self.resize_image_type2(img) + else: + # img, shape = self.resize_image_type1(img) + img, [ratio_h, ratio_w] = self.resize_image_type1(img) + data["image"] = img + data["shape"] = np.array([src_h, src_w, ratio_h, ratio_w]) + return data + + def image_padding(self, im, value=0): + h, w, c = im.shape + im_pad = np.zeros((max(32, h), max(32, w), c), np.uint8) + value + im_pad[:h, :w, :] = im + return im_pad + + def resize_image_type1(self, img): + resize_h, resize_w = self.image_shape + ori_h, ori_w = img.shape[:2] # (h, w, c) + if self.keep_ratio is True: + resize_w = ori_w * resize_h / ori_h + N = math.ceil(resize_w / 32) + resize_w = N * 32 + ratio_h = float(resize_h) / ori_h + ratio_w = float(resize_w) / ori_w + img = cv2.resize(img, (int(resize_w), int(resize_h))) + # return img, np.array([ori_h, ori_w]) + return img, [ratio_h, ratio_w] + + def resize_image_type0(self, img): + """ + resize image to a size multiple of 32 which is required by the network + args: + img(array): array with shape [h, w, c] + return(tuple): + img, (ratio_h, ratio_w) + """ + limit_side_len = self.limit_side_len + h, w, c = img.shape + + # limit the max side + if self.limit_type == "max": + if max(h, w) > limit_side_len: + if h > w: + ratio = float(limit_side_len) / h + else: + ratio = float(limit_side_len) / w + else: + ratio = 1.0 + elif self.limit_type == "min": + if min(h, w) < limit_side_len: + if h < w: + ratio = float(limit_side_len) / h + else: + ratio = float(limit_side_len) / w + else: + ratio = 1.0 + elif self.limit_type == "resize_long": + ratio = float(limit_side_len) / max(h, w) + else: + raise Exception("not support limit type, image ") + resize_h = int(h * ratio) + resize_w = int(w * ratio) + + resize_h = max(int(round(resize_h / 32) * 32), 32) + resize_w = max(int(round(resize_w / 32) * 32), 32) + + try: + if int(resize_w) <= 0 or int(resize_h) <= 0: + return None, (None, None) + img = cv2.resize(img, (int(resize_w), int(resize_h))) + except: + print(img.shape, resize_w, resize_h) + sys.exit(0) + ratio_h = resize_h / float(h) + ratio_w = resize_w / float(w) + return img, [ratio_h, ratio_w] + + def resize_image_type2(self, img): + h, w, _ = img.shape + + resize_w = w + resize_h = h + + if resize_h > resize_w: + ratio = float(self.resize_long) / resize_h + else: + ratio = float(self.resize_long) / resize_w + + resize_h = int(resize_h * ratio) + resize_w = int(resize_w * ratio) + + max_stride = 128 + resize_h = (resize_h + max_stride - 1) // max_stride * max_stride + resize_w = (resize_w + max_stride - 1) // max_stride * max_stride + img = cv2.resize(img, (int(resize_w), int(resize_h))) + ratio_h = resize_h / float(h) + ratio_w = resize_w / float(w) + + return img, [ratio_h, ratio_w] + + +class E2EResizeForTest(object): + def __init__(self, **kwargs): + super(E2EResizeForTest, self).__init__() + self.max_side_len = kwargs["max_side_len"] + self.valid_set = kwargs["valid_set"] + + def __call__(self, data): + img = data["image"] + src_h, src_w, _ = img.shape + if self.valid_set == "totaltext": + im_resized, [ratio_h, ratio_w] = self.resize_image_for_totaltext( + img, max_side_len=self.max_side_len + ) + else: + im_resized, (ratio_h, ratio_w) = self.resize_image( + img, max_side_len=self.max_side_len + ) + data["image"] = im_resized + data["shape"] = np.array([src_h, src_w, ratio_h, ratio_w]) + return data + + def resize_image_for_totaltext(self, im, max_side_len=512): + h, w, _ = im.shape + resize_w = w + resize_h = h + ratio = 1.25 + if h * ratio > max_side_len: + ratio = float(max_side_len) / resize_h + resize_h = int(resize_h * ratio) + resize_w = int(resize_w * ratio) + + max_stride = 128 + resize_h = (resize_h + max_stride - 1) // max_stride * max_stride + resize_w = (resize_w + max_stride - 1) // max_stride * max_stride + im = cv2.resize(im, (int(resize_w), int(resize_h))) + ratio_h = resize_h / float(h) + ratio_w = resize_w / float(w) + return im, (ratio_h, ratio_w) + + def resize_image(self, im, max_side_len=512): + """ + resize image to a size multiple of max_stride which is required by the network + :param im: the resized image + :param max_side_len: limit of max image size to avoid out of memory in gpu + :return: the resized image and the resize ratio + """ + h, w, _ = im.shape + + resize_w = w + resize_h = h + + # Fix the longer side + if resize_h > resize_w: + ratio = float(max_side_len) / resize_h + else: + ratio = float(max_side_len) / resize_w + + resize_h = int(resize_h * ratio) + resize_w = int(resize_w * ratio) + + max_stride = 128 + resize_h = (resize_h + max_stride - 1) // max_stride * max_stride + resize_w = (resize_w + max_stride - 1) // max_stride * max_stride + im = cv2.resize(im, (int(resize_w), int(resize_h))) + ratio_h = resize_h / float(h) + ratio_w = resize_w / float(w) + + return im, (ratio_h, ratio_w) + + +class KieResize(object): + def __init__(self, **kwargs): + super(KieResize, self).__init__() + self.max_side, self.min_side = kwargs["img_scale"][0], kwargs["img_scale"][1] + + def __call__(self, data): + img = data["image"] + points = data["points"] + src_h, src_w, _ = img.shape + ( + im_resized, + scale_factor, + [ratio_h, ratio_w], + [new_h, new_w], + ) = self.resize_image(img) + resize_points = self.resize_boxes(img, points, scale_factor) + data["ori_image"] = img + data["ori_boxes"] = points + data["points"] = resize_points + data["image"] = im_resized + data["shape"] = np.array([new_h, new_w]) + return data + + def resize_image(self, img): + norm_img = np.zeros([1024, 1024, 3], dtype="float32") + scale = [512, 1024] + h, w = img.shape[:2] + max_long_edge = max(scale) + max_short_edge = min(scale) + scale_factor = min(max_long_edge / max(h, w), max_short_edge / min(h, w)) + resize_w, resize_h = int(w * float(scale_factor) + 0.5), int( + h * float(scale_factor) + 0.5 + ) + max_stride = 32 + resize_h = (resize_h + max_stride - 1) // max_stride * max_stride + resize_w = (resize_w + max_stride - 1) // max_stride * max_stride + im = cv2.resize(img, (resize_w, resize_h)) + new_h, new_w = im.shape[:2] + w_scale = new_w / w + h_scale = new_h / h + scale_factor = np.array([w_scale, h_scale, w_scale, h_scale], dtype=np.float32) + norm_img[:new_h, :new_w, :] = im + return norm_img, scale_factor, [h_scale, w_scale], [new_h, new_w] + + def resize_boxes(self, im, points, scale_factor): + points = points * scale_factor + img_shape = im.shape[:2] + points[:, 0::2] = np.clip(points[:, 0::2], 0, img_shape[1]) + points[:, 1::2] = np.clip(points[:, 1::2], 0, img_shape[0]) + return points + + +class SRResize(object): + def __init__( + self, + imgH=32, + imgW=128, + down_sample_scale=4, + keep_ratio=False, + min_ratio=1, + mask=False, + infer_mode=False, + **kwargs, + ): + self.imgH = imgH + self.imgW = imgW + self.keep_ratio = keep_ratio + self.min_ratio = min_ratio + self.down_sample_scale = down_sample_scale + self.mask = mask + self.infer_mode = infer_mode + + def __call__(self, data): + imgH = self.imgH + imgW = self.imgW + images_lr = data["image_lr"] + transform2 = ResizeNormalize( + (imgW // self.down_sample_scale, imgH // self.down_sample_scale) + ) + images_lr = transform2(images_lr) + data["img_lr"] = images_lr + if self.infer_mode: + return data + + images_HR = data["image_hr"] + label_strs = data["label"] + transform = ResizeNormalize((imgW, imgH)) + images_HR = transform(images_HR) + data["img_hr"] = images_HR + return data + + +class ResizeNormalize(object): + def __init__(self, size, interpolation=Image.BICUBIC): + self.size = size + self.interpolation = interpolation + + def __call__(self, img): + img = img.resize(self.size, self.interpolation) + img_numpy = np.array(img).astype("float32") + img_numpy = img_numpy.transpose((2, 0, 1)) / 255 + return img_numpy + + +class GrayImageChannelFormat(object): + """ + format gray scale image's channel: (3,h,w) -> (1,h,w) + Args: + inverse: inverse gray image + """ + + def __init__(self, inverse=False, **kwargs): + self.inverse = inverse + + def __call__(self, data): + img = data["image"] + img_single_channel = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) + img_expanded = np.expand_dims(img_single_channel, 0) + + if self.inverse: + data["image"] = np.abs(img_expanded - 1) + else: + data["image"] = img_expanded + + data["src_image"] = img + return data diff --git a/modules/onnx_ocr_module/src/ppocr/data/imaug/pg_process.py b/modules/onnx_ocr_module/src/ppocr/data/imaug/pg_process.py new file mode 100644 index 0000000..b86590d --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/data/imaug/pg_process.py @@ -0,0 +1,1116 @@ +# copyright (c) 2021 PaddlePaddle Authors. All Rights Reserve. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import math +import cv2 +import numpy as np +from skimage.morphology._skeletonize import thin +from ppocr.utils.e2e_utils.extract_textpoint_fast import ( + sort_and_expand_with_direction_v2, +) + +__all__ = ["PGProcessTrain"] + + +class PGProcessTrain(object): + def __init__( + self, + character_dict_path, + max_text_length, + max_text_nums, + tcl_len, + batch_size=14, + use_resize=True, + use_random_crop=False, + min_crop_size=24, + min_text_size=4, + max_text_size=512, + point_gather_mode=None, + **kwargs, + ): + self.tcl_len = tcl_len + self.max_text_length = max_text_length + self.max_text_nums = max_text_nums + self.batch_size = batch_size + if use_random_crop is True: + self.min_crop_size = min_crop_size + self.use_random_crop = use_random_crop + self.min_text_size = min_text_size + self.max_text_size = max_text_size + self.use_resize = use_resize + self.point_gather_mode = point_gather_mode + self.Lexicon_Table = self.get_dict(character_dict_path) + self.pad_num = len(self.Lexicon_Table) + self.img_id = 0 + + def get_dict(self, character_dict_path): + character_str = "" + with open(character_dict_path, "rb") as fin: + lines = fin.readlines() + for line in lines: + line = line.decode("utf-8").strip("\n").strip("\r\n") + character_str += line + dict_character = list(character_str) + return dict_character + + def quad_area(self, poly): + """ + compute area of a polygon + :param poly: + :return: + """ + edge = [ + (poly[1][0] - poly[0][0]) * (poly[1][1] + poly[0][1]), + (poly[2][0] - poly[1][0]) * (poly[2][1] + poly[1][1]), + (poly[3][0] - poly[2][0]) * (poly[3][1] + poly[2][1]), + (poly[0][0] - poly[3][0]) * (poly[0][1] + poly[3][1]), + ] + return np.sum(edge) / 2.0 + + def gen_quad_from_poly(self, poly): + """ + Generate min area quad from poly. + """ + point_num = poly.shape[0] + min_area_quad = np.zeros((4, 2), dtype=np.float32) + rect = cv2.minAreaRect( + poly.astype(np.int32) + ) # (center (x,y), (width, height), angle of rotation) + box = np.array(cv2.boxPoints(rect)) + + first_point_idx = 0 + min_dist = 1e4 + for i in range(4): + dist = ( + np.linalg.norm(box[(i + 0) % 4] - poly[0]) + + np.linalg.norm(box[(i + 1) % 4] - poly[point_num // 2 - 1]) + + np.linalg.norm(box[(i + 2) % 4] - poly[point_num // 2]) + + np.linalg.norm(box[(i + 3) % 4] - poly[-1]) + ) + if dist < min_dist: + min_dist = dist + first_point_idx = i + for i in range(4): + min_area_quad[i] = box[(first_point_idx + i) % 4] + + return min_area_quad + + def check_and_validate_polys(self, polys, tags, im_size): + """ + check so that the text poly is in the same direction, + and also filter some invalid polygons + :param polys: + :param tags: + :return: + """ + (h, w) = im_size + if polys.shape[0] == 0: + return polys, np.array([]), np.array([]) + polys[:, :, 0] = np.clip(polys[:, :, 0], 0, w - 1) + polys[:, :, 1] = np.clip(polys[:, :, 1], 0, h - 1) + + validated_polys = [] + validated_tags = [] + hv_tags = [] + for poly, tag in zip(polys, tags): + quad = self.gen_quad_from_poly(poly) + p_area = self.quad_area(quad) + if abs(p_area) < 1: + print("invalid poly") + continue + if p_area > 0: + if tag == False: + print("poly in wrong direction") + tag = True # reversed cases should be ignore + poly = poly[(0, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1), :] + quad = quad[(0, 3, 2, 1), :] + + len_w = np.linalg.norm(quad[0] - quad[1]) + np.linalg.norm( + quad[3] - quad[2] + ) + len_h = np.linalg.norm(quad[0] - quad[3]) + np.linalg.norm( + quad[1] - quad[2] + ) + hv_tag = 1 + + if len_w * 2.0 < len_h: + hv_tag = 0 + + validated_polys.append(poly) + validated_tags.append(tag) + hv_tags.append(hv_tag) + return np.array(validated_polys), np.array(validated_tags), np.array(hv_tags) + + def crop_area( + self, im, polys, tags, hv_tags, txts, crop_background=False, max_tries=25 + ): + """ + make random crop from the input image + :param im: + :param polys: [b,4,2] + :param tags: + :param crop_background: + :param max_tries: 50 -> 25 + :return: + """ + h, w, _ = im.shape + pad_h = h // 10 + pad_w = w // 10 + h_array = np.zeros((h + pad_h * 2), dtype=np.int32) + w_array = np.zeros((w + pad_w * 2), dtype=np.int32) + for poly in polys: + poly = np.round(poly, decimals=0).astype(np.int32) + minx = np.min(poly[:, 0]) + maxx = np.max(poly[:, 0]) + w_array[minx + pad_w : maxx + pad_w] = 1 + miny = np.min(poly[:, 1]) + maxy = np.max(poly[:, 1]) + h_array[miny + pad_h : maxy + pad_h] = 1 + # ensure the cropped area not across a text + h_axis = np.where(h_array == 0)[0] + w_axis = np.where(w_array == 0)[0] + if len(h_axis) == 0 or len(w_axis) == 0: + return im, polys, tags, hv_tags, txts + for i in range(max_tries): + xx = np.random.choice(w_axis, size=2) + xmin = np.min(xx) - pad_w + xmax = np.max(xx) - pad_w + xmin = np.clip(xmin, 0, w - 1) + xmax = np.clip(xmax, 0, w - 1) + yy = np.random.choice(h_axis, size=2) + ymin = np.min(yy) - pad_h + ymax = np.max(yy) - pad_h + ymin = np.clip(ymin, 0, h - 1) + ymax = np.clip(ymax, 0, h - 1) + if xmax - xmin < self.min_crop_size or ymax - ymin < self.min_crop_size: + continue + if polys.shape[0] != 0: + poly_axis_in_area = ( + (polys[:, :, 0] >= xmin) + & (polys[:, :, 0] <= xmax) + & (polys[:, :, 1] >= ymin) + & (polys[:, :, 1] <= ymax) + ) + selected_polys = np.where(np.sum(poly_axis_in_area, axis=1) == 4)[0] + else: + selected_polys = [] + if len(selected_polys) == 0: + # no text in this area + if crop_background: + txts_tmp = [] + for selected_poly in selected_polys: + txts_tmp.append(txts[selected_poly]) + txts = txts_tmp + return ( + im[ymin : ymax + 1, xmin : xmax + 1, :], + polys[selected_polys], + tags[selected_polys], + hv_tags[selected_polys], + txts, + ) + else: + continue + im = im[ymin : ymax + 1, xmin : xmax + 1, :] + polys = polys[selected_polys] + tags = tags[selected_polys] + hv_tags = hv_tags[selected_polys] + txts_tmp = [] + for selected_poly in selected_polys: + txts_tmp.append(txts[selected_poly]) + txts = txts_tmp + polys[:, :, 0] -= xmin + polys[:, :, 1] -= ymin + return im, polys, tags, hv_tags, txts + + return im, polys, tags, hv_tags, txts + + def fit_and_gather_tcl_points_v2( + self, + min_area_quad, + poly, + max_h, + max_w, + fixed_point_num=64, + img_id=0, + reference_height=3, + ): + """ + Find the center point of poly as key_points, then fit and gather. + """ + key_point_xys = [] + point_num = poly.shape[0] + for idx in range(point_num // 2): + center_point = (poly[idx] + poly[point_num - 1 - idx]) / 2.0 + key_point_xys.append(center_point) + + tmp_image = np.zeros( + shape=( + max_h, + max_w, + ), + dtype="float32", + ) + cv2.polylines(tmp_image, [np.array(key_point_xys).astype("int32")], False, 1.0) + ys, xs = np.where(tmp_image > 0) + xy_text = np.array(list(zip(xs, ys)), dtype="float32") + + left_center_pt = ((min_area_quad[0] + min_area_quad[3]) / 2.0).reshape(1, 2) + right_center_pt = ((min_area_quad[1] + min_area_quad[2]) / 2.0).reshape(1, 2) + proj_unit_vec = (right_center_pt - left_center_pt) / ( + np.linalg.norm(right_center_pt - left_center_pt) + 1e-6 + ) + proj_unit_vec_tile = np.tile(proj_unit_vec, (xy_text.shape[0], 1)) # (n, 2) + left_center_pt_tile = np.tile(left_center_pt, (xy_text.shape[0], 1)) # (n, 2) + xy_text_to_left_center = xy_text - left_center_pt_tile + proj_value = np.sum(xy_text_to_left_center * proj_unit_vec_tile, axis=1) + xy_text = xy_text[np.argsort(proj_value)] + + # convert to np and keep the num of point not greater then fixed_point_num + pos_info = np.array(xy_text).reshape(-1, 2)[:, ::-1] # xy-> yx + point_num = len(pos_info) + if point_num > fixed_point_num: + keep_ids = [ + int((point_num * 1.0 / fixed_point_num) * x) + for x in range(fixed_point_num) + ] + pos_info = pos_info[keep_ids, :] + + keep = int(min(len(pos_info), fixed_point_num)) + if np.random.rand() < 0.2 and reference_height >= 3: + dl = (np.random.rand(keep) - 0.5) * reference_height * 0.3 + random_float = np.array([1, 0]).reshape([1, 2]) * dl.reshape([keep, 1]) + pos_info += random_float + pos_info[:, 0] = np.clip(pos_info[:, 0], 0, max_h - 1) + pos_info[:, 1] = np.clip(pos_info[:, 1], 0, max_w - 1) + + # padding to fixed length + pos_l = np.zeros((self.tcl_len, 3), dtype=np.int32) + pos_l[:, 0] = np.ones((self.tcl_len,)) * img_id + pos_m = np.zeros((self.tcl_len, 1), dtype=np.float32) + pos_l[:keep, 1:] = np.round(pos_info).astype(np.int32) + pos_m[:keep] = 1.0 + return pos_l, pos_m + + def fit_and_gather_tcl_points_v3( + self, + min_area_quad, + poly, + max_h, + max_w, + fixed_point_num=64, + img_id=0, + reference_height=3, + ): + """ + Find the center point of poly as key_points, then fit and gather. + """ + det_mask = np.zeros( + (int(max_h / self.ds_ratio), int(max_w / self.ds_ratio)) + ).astype(np.float32) + + # score_big_map + cv2.fillPoly(det_mask, np.round(poly / self.ds_ratio).astype(np.int32), 1.0) + det_mask = cv2.resize(det_mask, dsize=None, fx=self.ds_ratio, fy=self.ds_ratio) + det_mask = np.array(det_mask > 1e-3, dtype="float32") + + f_direction = self.f_direction + skeleton_map = thin(det_mask.astype(np.uint8)) + instance_count, instance_label_map = cv2.connectedComponents( + skeleton_map.astype(np.uint8), connectivity=8 + ) + + ys, xs = np.where(instance_label_map == 1) + pos_list = list(zip(ys, xs)) + if len(pos_list) < 3: + return None + pos_list_sorted = sort_and_expand_with_direction_v2( + pos_list, f_direction, det_mask + ) + + pos_list_sorted = np.array(pos_list_sorted) + length = len(pos_list_sorted) - 1 + insert_num = 0 + for index in range(length): + stride_y = np.abs( + pos_list_sorted[index + insert_num][0] + - pos_list_sorted[index + 1 + insert_num][0] + ) + stride_x = np.abs( + pos_list_sorted[index + insert_num][1] + - pos_list_sorted[index + 1 + insert_num][1] + ) + max_points = int(max(stride_x, stride_y)) + + stride = ( + pos_list_sorted[index + insert_num] + - pos_list_sorted[index + 1 + insert_num] + ) / (max_points) + insert_num_temp = max_points - 1 + + for i in range(int(insert_num_temp)): + insert_value = pos_list_sorted[index + insert_num] - (i + 1) * stride + insert_index = index + i + 1 + insert_num + pos_list_sorted = np.insert( + pos_list_sorted, insert_index, insert_value, axis=0 + ) + insert_num += insert_num_temp + + pos_info = ( + np.array(pos_list_sorted).reshape(-1, 2).astype(np.float32) + ) # xy-> yx + + point_num = len(pos_info) + if point_num > fixed_point_num: + keep_ids = [ + int((point_num * 1.0 / fixed_point_num) * x) + for x in range(fixed_point_num) + ] + pos_info = pos_info[keep_ids, :] + + keep = int(min(len(pos_info), fixed_point_num)) + reference_width = ( + np.abs(poly[0, 0, 0] - poly[-1, 1, 0]) + + np.abs(poly[0, 3, 0] - poly[-1, 2, 0]) + ) // 2 + if np.random.rand() < 1: + dh = (np.random.rand(keep) - 0.5) * reference_height + offset = np.random.rand() - 0.5 + dw = np.array([[0, offset * reference_width * 0.2]]) + random_float_h = np.array([1, 0]).reshape([1, 2]) * dh.reshape([keep, 1]) + random_float_w = dw.repeat(keep, axis=0) + pos_info += random_float_h + pos_info += random_float_w + pos_info[:, 0] = np.clip(pos_info[:, 0], 0, max_h - 1) + pos_info[:, 1] = np.clip(pos_info[:, 1], 0, max_w - 1) + + # padding to fixed length + pos_l = np.zeros((self.tcl_len, 3), dtype=np.int32) + pos_l[:, 0] = np.ones((self.tcl_len,)) * img_id + pos_m = np.zeros((self.tcl_len, 1), dtype=np.float32) + pos_l[:keep, 1:] = np.round(pos_info).astype(np.int32) + pos_m[:keep] = 1.0 + return pos_l, pos_m + + def generate_direction_map(self, poly_quads, n_char, direction_map): + """ """ + width_list = [] + height_list = [] + for quad in poly_quads: + quad_w = ( + np.linalg.norm(quad[0] - quad[1]) + np.linalg.norm(quad[2] - quad[3]) + ) / 2.0 + quad_h = ( + np.linalg.norm(quad[0] - quad[3]) + np.linalg.norm(quad[2] - quad[1]) + ) / 2.0 + width_list.append(quad_w) + height_list.append(quad_h) + norm_width = max(sum(width_list) / n_char, 1.0) + average_height = max(sum(height_list) / len(height_list), 1.0) + k = 1 + for quad in poly_quads: + direct_vector_full = ((quad[1] + quad[2]) - (quad[0] + quad[3])) / 2.0 + direct_vector = ( + direct_vector_full + / (np.linalg.norm(direct_vector_full) + 1e-6) + * norm_width + ) + direction_label = tuple( + map(float, [direct_vector[0], direct_vector[1], 1.0 / average_height]) + ) + cv2.fillPoly( + direction_map, + quad.round().astype(np.int32)[np.newaxis, :, :], + direction_label, + ) + k += 1 + return direction_map + + def calculate_average_height(self, poly_quads): + """ """ + height_list = [] + for quad in poly_quads: + quad_h = ( + np.linalg.norm(quad[0] - quad[3]) + np.linalg.norm(quad[2] - quad[1]) + ) / 2.0 + height_list.append(quad_h) + average_height = max(sum(height_list) / len(height_list), 1.0) + return average_height + + def generate_tcl_ctc_label( + self, + h, + w, + polys, + tags, + text_strs, + ds_ratio, + tcl_ratio=0.3, + shrink_ratio_of_width=0.15, + ): + """ + Generate polygon. + """ + self.ds_ratio = ds_ratio + score_map_big = np.zeros( + ( + h, + w, + ), + dtype=np.float32, + ) + h, w = int(h * ds_ratio), int(w * ds_ratio) + polys = polys * ds_ratio + + score_map = np.zeros( + ( + h, + w, + ), + dtype=np.float32, + ) + score_label_map = np.zeros( + ( + h, + w, + ), + dtype=np.float32, + ) + tbo_map = np.zeros((h, w, 5), dtype=np.float32) + training_mask = np.ones( + ( + h, + w, + ), + dtype=np.float32, + ) + direction_map = np.ones((h, w, 3)) * np.array([0, 0, 1]).reshape( + [1, 1, 3] + ).astype(np.float32) + + label_idx = 0 + score_label_map_text_label_list = [] + pos_list, pos_mask, label_list = [], [], [] + for poly_idx, poly_tag in enumerate(zip(polys, tags)): + poly = poly_tag[0] + tag = poly_tag[1] + + # generate min_area_quad + min_area_quad, center_point = self.gen_min_area_quad_from_poly(poly) + min_area_quad_h = 0.5 * ( + np.linalg.norm(min_area_quad[0] - min_area_quad[3]) + + np.linalg.norm(min_area_quad[1] - min_area_quad[2]) + ) + min_area_quad_w = 0.5 * ( + np.linalg.norm(min_area_quad[0] - min_area_quad[1]) + + np.linalg.norm(min_area_quad[2] - min_area_quad[3]) + ) + + if ( + min(min_area_quad_h, min_area_quad_w) < self.min_text_size * ds_ratio + or min(min_area_quad_h, min_area_quad_w) > self.max_text_size * ds_ratio + ): + continue + + if tag: + cv2.fillPoly( + training_mask, poly.astype(np.int32)[np.newaxis, :, :], 0.15 + ) + else: + text_label = text_strs[poly_idx] + text_label = self.prepare_text_label(text_label, self.Lexicon_Table) + text_label_index_list = [ + [self.Lexicon_Table.index(c_)] + for c_ in text_label + if c_ in self.Lexicon_Table + ] + if len(text_label_index_list) < 1: + continue + + tcl_poly = self.poly2tcl(poly, tcl_ratio) + tcl_quads = self.poly2quads(tcl_poly) + poly_quads = self.poly2quads(poly) + + stcl_quads, quad_index = self.shrink_poly_along_width( + tcl_quads, + shrink_ratio_of_width=shrink_ratio_of_width, + expand_height_ratio=1.0 / tcl_ratio, + ) + + cv2.fillPoly(score_map, np.round(stcl_quads).astype(np.int32), 1.0) + cv2.fillPoly( + score_map_big, np.round(stcl_quads / ds_ratio).astype(np.int32), 1.0 + ) + + for idx, quad in enumerate(stcl_quads): + quad_mask = np.zeros((h, w), dtype=np.float32) + quad_mask = cv2.fillPoly( + quad_mask, + np.round(quad[np.newaxis, :, :]).astype(np.int32), + 1.0, + ) + tbo_map = self.gen_quad_tbo( + poly_quads[quad_index[idx]], quad_mask, tbo_map + ) + + # score label map and score_label_map_text_label_list for refine + if label_idx == 0: + text_pos_list_ = [ + [len(self.Lexicon_Table)], + ] + score_label_map_text_label_list.append(text_pos_list_) + + label_idx += 1 + cv2.fillPoly( + score_label_map, np.round(poly_quads).astype(np.int32), label_idx + ) + score_label_map_text_label_list.append(text_label_index_list) + + # direction info, fix-me + n_char = len(text_label_index_list) + direction_map = self.generate_direction_map( + poly_quads, n_char, direction_map + ) + + # pos info + average_shrink_height = self.calculate_average_height(stcl_quads) + + if self.point_gather_mode == "align": + self.f_direction = direction_map[:, :, :-1].copy() + pos_res = self.fit_and_gather_tcl_points_v3( + min_area_quad, + stcl_quads, + max_h=h, + max_w=w, + fixed_point_num=64, + img_id=self.img_id, + reference_height=average_shrink_height, + ) + if pos_res is None: + continue + pos_l, pos_m = pos_res[0], pos_res[1] + + else: + pos_l, pos_m = self.fit_and_gather_tcl_points_v2( + min_area_quad, + poly, + max_h=h, + max_w=w, + fixed_point_num=64, + img_id=self.img_id, + reference_height=average_shrink_height, + ) + + label_l = text_label_index_list + if len(text_label_index_list) < 2: + continue + + pos_list.append(pos_l) + pos_mask.append(pos_m) + label_list.append(label_l) + + # use big score_map for smooth tcl lines + score_map_big_resized = cv2.resize( + score_map_big, dsize=None, fx=ds_ratio, fy=ds_ratio + ) + score_map = np.array(score_map_big_resized > 1e-3, dtype="float32") + + return ( + score_map, + score_label_map, + tbo_map, + direction_map, + training_mask, + pos_list, + pos_mask, + label_list, + score_label_map_text_label_list, + ) + + def adjust_point(self, poly): + """ + adjust point order. + """ + point_num = poly.shape[0] + if point_num == 4: + len_1 = np.linalg.norm(poly[0] - poly[1]) + len_2 = np.linalg.norm(poly[1] - poly[2]) + len_3 = np.linalg.norm(poly[2] - poly[3]) + len_4 = np.linalg.norm(poly[3] - poly[0]) + + if (len_1 + len_3) * 1.5 < (len_2 + len_4): + poly = poly[[1, 2, 3, 0], :] + + elif point_num > 4: + vector_1 = poly[0] - poly[1] + vector_2 = poly[1] - poly[2] + cos_theta = np.dot(vector_1, vector_2) / ( + np.linalg.norm(vector_1) * np.linalg.norm(vector_2) + 1e-6 + ) + theta = np.arccos(np.round(cos_theta, decimals=4)) + + if abs(theta) > (70 / 180 * math.pi): + index = list(range(1, point_num)) + [0] + poly = poly[np.array(index), :] + return poly + + def gen_min_area_quad_from_poly(self, poly): + """ + Generate min area quad from poly. + """ + point_num = poly.shape[0] + min_area_quad = np.zeros((4, 2), dtype=np.float32) + if point_num == 4: + min_area_quad = poly + center_point = np.sum(poly, axis=0) / 4 + else: + rect = cv2.minAreaRect( + poly.astype(np.int32) + ) # (center (x,y), (width, height), angle of rotation) + center_point = rect[0] + box = np.array(cv2.boxPoints(rect)) + + first_point_idx = 0 + min_dist = 1e4 + for i in range(4): + dist = ( + np.linalg.norm(box[(i + 0) % 4] - poly[0]) + + np.linalg.norm(box[(i + 1) % 4] - poly[point_num // 2 - 1]) + + np.linalg.norm(box[(i + 2) % 4] - poly[point_num // 2]) + + np.linalg.norm(box[(i + 3) % 4] - poly[-1]) + ) + if dist < min_dist: + min_dist = dist + first_point_idx = i + + for i in range(4): + min_area_quad[i] = box[(first_point_idx + i) % 4] + + return min_area_quad, center_point + + def shrink_quad_along_width(self, quad, begin_width_ratio=0.0, end_width_ratio=1.0): + """ + Generate shrink_quad_along_width. + """ + ratio_pair = np.array( + [[begin_width_ratio], [end_width_ratio]], dtype=np.float32 + ) + p0_1 = quad[0] + (quad[1] - quad[0]) * ratio_pair + p3_2 = quad[3] + (quad[2] - quad[3]) * ratio_pair + return np.array([p0_1[0], p0_1[1], p3_2[1], p3_2[0]]) + + def shrink_poly_along_width( + self, quads, shrink_ratio_of_width, expand_height_ratio=1.0 + ): + """ + shrink poly with given length. + """ + upper_edge_list = [] + + def get_cut_info(edge_len_list, cut_len): + for idx, edge_len in enumerate(edge_len_list): + cut_len -= edge_len + if cut_len <= 0.000001: + ratio = (cut_len + edge_len_list[idx]) / edge_len_list[idx] + return idx, ratio + + for quad in quads: + upper_edge_len = np.linalg.norm(quad[0] - quad[1]) + upper_edge_list.append(upper_edge_len) + + # length of left edge and right edge. + left_length = np.linalg.norm(quads[0][0] - quads[0][3]) * expand_height_ratio + right_length = np.linalg.norm(quads[-1][1] - quads[-1][2]) * expand_height_ratio + + shrink_length = ( + min(left_length, right_length, sum(upper_edge_list)) * shrink_ratio_of_width + ) + # shrinking length + upper_len_left = shrink_length + upper_len_right = sum(upper_edge_list) - shrink_length + + left_idx, left_ratio = get_cut_info(upper_edge_list, upper_len_left) + left_quad = self.shrink_quad_along_width( + quads[left_idx], begin_width_ratio=left_ratio, end_width_ratio=1 + ) + right_idx, right_ratio = get_cut_info(upper_edge_list, upper_len_right) + right_quad = self.shrink_quad_along_width( + quads[right_idx], begin_width_ratio=0, end_width_ratio=right_ratio + ) + + out_quad_list = [] + if left_idx == right_idx: + out_quad_list.append( + [left_quad[0], right_quad[1], right_quad[2], left_quad[3]] + ) + else: + out_quad_list.append(left_quad) + for idx in range(left_idx + 1, right_idx): + out_quad_list.append(quads[idx]) + out_quad_list.append(right_quad) + + return np.array(out_quad_list), list(range(left_idx, right_idx + 1)) + + def prepare_text_label(self, label_str, Lexicon_Table): + """ + Prepare text label by given Lexicon_Table. + """ + if len(Lexicon_Table) == 36: + return label_str.lower() + else: + return label_str + + def vector_angle(self, A, B): + """ + Calculate the angle between vector AB and x-axis positive direction. + """ + AB = np.array([B[1] - A[1], B[0] - A[0]]) + return np.arctan2(*AB) + + def theta_line_cross_point(self, theta, point): + """ + Calculate the line through given point and angle in ax + by + c =0 form. + """ + x, y = point + cos = np.cos(theta) + sin = np.sin(theta) + return [sin, -cos, cos * y - sin * x] + + def line_cross_two_point(self, A, B): + """ + Calculate the line through given point A and B in ax + by + c =0 form. + """ + angle = self.vector_angle(A, B) + return self.theta_line_cross_point(angle, A) + + def average_angle(self, poly): + """ + Calculate the average angle between left and right edge in given poly. + """ + p0, p1, p2, p3 = poly + angle30 = self.vector_angle(p3, p0) + angle21 = self.vector_angle(p2, p1) + return (angle30 + angle21) / 2 + + def line_cross_point(self, line1, line2): + """ + line1 and line2 in 0=ax+by+c form, compute the cross point of line1 and line2 + """ + a1, b1, c1 = line1 + a2, b2, c2 = line2 + d = a1 * b2 - a2 * b1 + + if d == 0: + print("Cross point does not exist") + return np.array([0, 0], dtype=np.float32) + else: + x = (b1 * c2 - b2 * c1) / d + y = (a2 * c1 - a1 * c2) / d + + return np.array([x, y], dtype=np.float32) + + def quad2tcl(self, poly, ratio): + """ + Generate center line by poly clock-wise point. (4, 2) + """ + ratio_pair = np.array([[0.5 - ratio / 2], [0.5 + ratio / 2]], dtype=np.float32) + p0_3 = poly[0] + (poly[3] - poly[0]) * ratio_pair + p1_2 = poly[1] + (poly[2] - poly[1]) * ratio_pair + return np.array([p0_3[0], p1_2[0], p1_2[1], p0_3[1]]) + + def poly2tcl(self, poly, ratio): + """ + Generate center line by poly clock-wise point. + """ + ratio_pair = np.array([[0.5 - ratio / 2], [0.5 + ratio / 2]], dtype=np.float32) + tcl_poly = np.zeros_like(poly) + point_num = poly.shape[0] + + for idx in range(point_num // 2): + point_pair = ( + poly[idx] + (poly[point_num - 1 - idx] - poly[idx]) * ratio_pair + ) + tcl_poly[idx] = point_pair[0] + tcl_poly[point_num - 1 - idx] = point_pair[1] + return tcl_poly + + def gen_quad_tbo(self, quad, tcl_mask, tbo_map): + """ + Generate tbo_map for give quad. + """ + # upper and lower line function: ax + by + c = 0; + up_line = self.line_cross_two_point(quad[0], quad[1]) + lower_line = self.line_cross_two_point(quad[3], quad[2]) + + quad_h = 0.5 * ( + np.linalg.norm(quad[0] - quad[3]) + np.linalg.norm(quad[1] - quad[2]) + ) + quad_w = 0.5 * ( + np.linalg.norm(quad[0] - quad[1]) + np.linalg.norm(quad[2] - quad[3]) + ) + + # average angle of left and right line. + angle = self.average_angle(quad) + + xy_in_poly = np.argwhere(tcl_mask == 1) + for y, x in xy_in_poly: + point = (x, y) + line = self.theta_line_cross_point(angle, point) + cross_point_upper = self.line_cross_point(up_line, line) + cross_point_lower = self.line_cross_point(lower_line, line) + ##FIX, offset reverse + upper_offset_x, upper_offset_y = cross_point_upper - point + lower_offset_x, lower_offset_y = cross_point_lower - point + tbo_map[y, x, 0] = upper_offset_y + tbo_map[y, x, 1] = upper_offset_x + tbo_map[y, x, 2] = lower_offset_y + tbo_map[y, x, 3] = lower_offset_x + tbo_map[y, x, 4] = 1.0 / max(min(quad_h, quad_w), 1.0) * 2 + return tbo_map + + def poly2quads(self, poly): + """ + Split poly into quads. + """ + quad_list = [] + point_num = poly.shape[0] + + # point pair + point_pair_list = [] + for idx in range(point_num // 2): + point_pair = [poly[idx], poly[point_num - 1 - idx]] + point_pair_list.append(point_pair) + + quad_num = point_num // 2 - 1 + for idx in range(quad_num): + # reshape and adjust to clock-wise + quad_list.append( + (np.array(point_pair_list)[[idx, idx + 1]]).reshape(4, 2)[[0, 2, 3, 1]] + ) + + return np.array(quad_list) + + def rotate_im_poly(self, im, text_polys): + """ + rotate image with 90 / 180 / 270 degre + """ + im_w, im_h = im.shape[1], im.shape[0] + dst_im = im.copy() + dst_polys = [] + rand_degree_ratio = np.random.rand() + rand_degree_cnt = 1 + if rand_degree_ratio > 0.5: + rand_degree_cnt = 3 + for i in range(rand_degree_cnt): + dst_im = np.rot90(dst_im) + rot_degree = -90 * rand_degree_cnt + rot_angle = rot_degree * math.pi / 180.0 + n_poly = text_polys.shape[0] + cx, cy = 0.5 * im_w, 0.5 * im_h + ncx, ncy = 0.5 * dst_im.shape[1], 0.5 * dst_im.shape[0] + for i in range(n_poly): + wordBB = text_polys[i] + poly = [] + for j in range(4): # 16->4 + sx, sy = wordBB[j][0], wordBB[j][1] + dx = ( + math.cos(rot_angle) * (sx - cx) + - math.sin(rot_angle) * (sy - cy) + + ncx + ) + dy = ( + math.sin(rot_angle) * (sx - cx) + + math.cos(rot_angle) * (sy - cy) + + ncy + ) + poly.append([dx, dy]) + dst_polys.append(poly) + return dst_im, np.array(dst_polys, dtype=np.float32) + + def __call__(self, data): + input_size = 512 + im = data["image"] + text_polys = data["polys"] + text_tags = data["ignore_tags"] + text_strs = data["texts"] + h, w, _ = im.shape + text_polys, text_tags, hv_tags = self.check_and_validate_polys( + text_polys, text_tags, (h, w) + ) + if text_polys.shape[0] <= 0: + return None + # set aspect ratio and keep area fix + asp_scales = np.arange(1.0, 1.55, 0.1) + asp_scale = np.random.choice(asp_scales) + if np.random.rand() < 0.5: + asp_scale = 1.0 / asp_scale + asp_scale = math.sqrt(asp_scale) + + asp_wx = asp_scale + asp_hy = 1.0 / asp_scale + im = cv2.resize(im, dsize=None, fx=asp_wx, fy=asp_hy) + text_polys[:, :, 0] *= asp_wx + text_polys[:, :, 1] *= asp_hy + + if self.use_resize is True: + ori_h, ori_w, _ = im.shape + if max(ori_h, ori_w) < 200: + ratio = 200 / max(ori_h, ori_w) + im = cv2.resize(im, (int(ori_w * ratio), int(ori_h * ratio))) + text_polys[:, :, 0] *= ratio + text_polys[:, :, 1] *= ratio + + if max(ori_h, ori_w) > 512: + ratio = 512 / max(ori_h, ori_w) + im = cv2.resize(im, (int(ori_w * ratio), int(ori_h * ratio))) + text_polys[:, :, 0] *= ratio + text_polys[:, :, 1] *= ratio + elif self.use_random_crop is True: + h, w, _ = im.shape + if max(h, w) > 2048: + rd_scale = 2048.0 / max(h, w) + im = cv2.resize(im, dsize=None, fx=rd_scale, fy=rd_scale) + text_polys *= rd_scale + h, w, _ = im.shape + if min(h, w) < 16: + return None + + # no background + im, text_polys, text_tags, hv_tags, text_strs = self.crop_area( + im, text_polys, text_tags, hv_tags, text_strs, crop_background=False + ) + + if text_polys.shape[0] == 0: + return None + # continue for all ignore case + if np.sum((text_tags * 1.0)) >= text_tags.size: + return None + new_h, new_w, _ = im.shape + if (new_h is None) or (new_w is None): + return None + # resize image + std_ratio = float(input_size) / max(new_w, new_h) + rand_scales = np.array( + [0.25, 0.375, 0.5, 0.625, 0.75, 0.875, 1.0, 1.0, 1.0, 1.0, 1.0] + ) + rz_scale = std_ratio * np.random.choice(rand_scales) + im = cv2.resize(im, dsize=None, fx=rz_scale, fy=rz_scale) + text_polys[:, :, 0] *= rz_scale + text_polys[:, :, 1] *= rz_scale + + # add gaussian blur + if np.random.rand() < 0.1 * 0.5: + ks = np.random.permutation(5)[0] + 1 + ks = int(ks / 2) * 2 + 1 + im = cv2.GaussianBlur(im, ksize=(ks, ks), sigmaX=0, sigmaY=0) + # add brighter + if np.random.rand() < 0.1 * 0.5: + im = im * (1.0 + np.random.rand() * 0.5) + im = np.clip(im, 0.0, 255.0) + # add darker + if np.random.rand() < 0.1 * 0.5: + im = im * (1.0 - np.random.rand() * 0.5) + im = np.clip(im, 0.0, 255.0) + + # Padding the im to [input_size, input_size] + new_h, new_w, _ = im.shape + if min(new_w, new_h) < input_size * 0.5: + return None + im_padded = np.ones((input_size, input_size, 3), dtype=np.float32) + im_padded[:, :, 2] = 0.485 * 255 + im_padded[:, :, 1] = 0.456 * 255 + im_padded[:, :, 0] = 0.406 * 255 + + # Random the start position + del_h = input_size - new_h + del_w = input_size - new_w + sh, sw = 0, 0 + if del_h > 1: + sh = int(np.random.rand() * del_h) + if del_w > 1: + sw = int(np.random.rand() * del_w) + + # Padding + im_padded[sh : sh + new_h, sw : sw + new_w, :] = im.copy() + text_polys[:, :, 0] += sw + text_polys[:, :, 1] += sh + + ( + score_map, + score_label_map, + border_map, + direction_map, + training_mask, + pos_list, + pos_mask, + label_list, + score_label_map_text_label, + ) = self.generate_tcl_ctc_label( + input_size, input_size, text_polys, text_tags, text_strs, 0.25 + ) + if len(label_list) <= 0: # eliminate negative samples + return None + pos_list_temp = np.zeros([64, 3]) + pos_mask_temp = np.zeros([64, 1]) + label_list_temp = np.zeros([self.max_text_length, 1]) + self.pad_num + + for i, label in enumerate(label_list): + n = len(label) + if n > self.max_text_length: + label_list[i] = label[: self.max_text_length] + continue + while n < self.max_text_length: + label.append([self.pad_num]) + n += 1 + + for i in range(len(label_list)): + label_list[i] = np.array(label_list[i]) + + if len(pos_list) <= 0 or len(pos_list) > self.max_text_nums: + return None + for __ in range(self.max_text_nums - len(pos_list), 0, -1): + pos_list.append(pos_list_temp) + pos_mask.append(pos_mask_temp) + label_list.append(label_list_temp) + + if self.img_id == self.batch_size - 1: + self.img_id = 0 + else: + self.img_id += 1 + + im_padded[:, :, 2] -= 0.485 * 255 + im_padded[:, :, 1] -= 0.456 * 255 + im_padded[:, :, 0] -= 0.406 * 255 + im_padded[:, :, 2] /= 255.0 * 0.229 + im_padded[:, :, 1] /= 255.0 * 0.224 + im_padded[:, :, 0] /= 255.0 * 0.225 + im_padded = im_padded.transpose((2, 0, 1)) + images = im_padded[::-1, :, :] + tcl_maps = score_map[np.newaxis, :, :] + tcl_label_maps = score_label_map[np.newaxis, :, :] + border_maps = border_map.transpose((2, 0, 1)) + direction_maps = direction_map.transpose((2, 0, 1)) + training_masks = training_mask[np.newaxis, :, :] + pos_list = np.array(pos_list) + pos_mask = np.array(pos_mask) + label_list = np.array(label_list) + data["images"] = images + data["tcl_maps"] = tcl_maps + data["tcl_label_maps"] = tcl_label_maps + data["border_maps"] = border_maps + data["direction_maps"] = direction_maps + data["training_masks"] = training_masks + data["label_list"] = label_list + data["pos_list"] = pos_list + data["pos_mask"] = pos_mask + return data diff --git a/modules/onnx_ocr_module/src/ppocr/data/imaug/randaugment.py b/modules/onnx_ocr_module/src/ppocr/data/imaug/randaugment.py new file mode 100644 index 0000000..588fb7b --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/data/imaug/randaugment.py @@ -0,0 +1,134 @@ +# copyright (c) 2020 PaddlePaddle Authors. All Rights Reserve. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function +from __future__ import unicode_literals + +from PIL import Image, ImageEnhance, ImageOps +import numpy as np +import random + + +class RawRandAugment(object): + def __init__(self, num_layers=2, magnitude=5, fillcolor=(128, 128, 128), **kwargs): + self.num_layers = num_layers + self.magnitude = magnitude + self.max_level = 10 + + abso_level = self.magnitude / self.max_level + self.level_map = { + "shearX": 0.3 * abso_level, + "shearY": 0.3 * abso_level, + "translateX": 150.0 / 331 * abso_level, + "translateY": 150.0 / 331 * abso_level, + "rotate": 30 * abso_level, + "color": 0.9 * abso_level, + "posterize": int(4.0 * abso_level), + "solarize": 256.0 * abso_level, + "contrast": 0.9 * abso_level, + "sharpness": 0.9 * abso_level, + "brightness": 0.9 * abso_level, + "autocontrast": 0, + "equalize": 0, + "invert": 0, + } + + # from https://stackoverflow.com/questions/5252170/ + # specify-image-filling-color-when-rotating-in-python-with-pil-and-setting-expand + def rotate_with_fill(img, magnitude): + rot = img.convert("RGBA").rotate(magnitude) + return Image.composite( + rot, Image.new("RGBA", rot.size, (128,) * 4), rot + ).convert(img.mode) + + rnd_ch_op = random.choice + + self.func = { + "shearX": lambda img, magnitude: img.transform( + img.size, + Image.AFFINE, + (1, magnitude * rnd_ch_op([-1, 1]), 0, 0, 1, 0), + Image.BICUBIC, + fillcolor=fillcolor, + ), + "shearY": lambda img, magnitude: img.transform( + img.size, + Image.AFFINE, + (1, 0, 0, magnitude * rnd_ch_op([-1, 1]), 1, 0), + Image.BICUBIC, + fillcolor=fillcolor, + ), + "translateX": lambda img, magnitude: img.transform( + img.size, + Image.AFFINE, + (1, 0, magnitude * img.size[0] * rnd_ch_op([-1, 1]), 0, 1, 0), + fillcolor=fillcolor, + ), + "translateY": lambda img, magnitude: img.transform( + img.size, + Image.AFFINE, + (1, 0, 0, 0, 1, magnitude * img.size[1] * rnd_ch_op([-1, 1])), + fillcolor=fillcolor, + ), + "rotate": lambda img, magnitude: rotate_with_fill(img, magnitude), + "color": lambda img, magnitude: ImageEnhance.Color(img).enhance( + 1 + magnitude * rnd_ch_op([-1, 1]) + ), + "posterize": lambda img, magnitude: ImageOps.posterize(img, magnitude), + "solarize": lambda img, magnitude: ImageOps.solarize(img, magnitude), + "contrast": lambda img, magnitude: ImageEnhance.Contrast(img).enhance( + 1 + magnitude * rnd_ch_op([-1, 1]) + ), + "sharpness": lambda img, magnitude: ImageEnhance.Sharpness(img).enhance( + 1 + magnitude * rnd_ch_op([-1, 1]) + ), + "brightness": lambda img, magnitude: ImageEnhance.Brightness(img).enhance( + 1 + magnitude * rnd_ch_op([-1, 1]) + ), + "autocontrast": lambda img, magnitude: ImageOps.autocontrast(img), + "equalize": lambda img, magnitude: ImageOps.equalize(img), + "invert": lambda img, magnitude: ImageOps.invert(img), + } + + def __call__(self, img): + avaiable_op_names = list(self.level_map.keys()) + for layer_num in range(self.num_layers): + op_name = np.random.choice(avaiable_op_names) + img = self.func[op_name](img, self.level_map[op_name]) + return img + + +class RandAugment(RawRandAugment): + """RandAugment wrapper to auto fit different img types""" + + def __init__(self, prob=0.5, *args, **kwargs): + self.prob = prob + super().__init__(*args, **kwargs) + + def __call__(self, data): + if np.random.rand() > self.prob: + return data + img = data["image"] + if not isinstance(img, Image.Image): + img = np.ascontiguousarray(img) + img = Image.fromarray(img) + + img = super().__call__(img) + + if isinstance(img, Image.Image): + img = np.asarray(img) + data["image"] = img + return data diff --git a/modules/onnx_ocr_module/src/ppocr/data/imaug/random_crop_data.py b/modules/onnx_ocr_module/src/ppocr/data/imaug/random_crop_data.py new file mode 100644 index 0000000..6625776 --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/data/imaug/random_crop_data.py @@ -0,0 +1,238 @@ +# copyright (c) 2020 PaddlePaddle Authors. All Rights Reserve. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +""" +This code is refer from: +https://github.com/WenmuZhou/DBNet.pytorch/blob/master/data_loader/modules/random_crop_data.py +""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function +from __future__ import unicode_literals + +import numpy as np +import cv2 +import random + + +def is_poly_in_rect(poly, x, y, w, h): + poly = np.array(poly) + if poly[:, 0].min() < x or poly[:, 0].max() > x + w: + return False + if poly[:, 1].min() < y or poly[:, 1].max() > y + h: + return False + return True + + +def is_poly_outside_rect(poly, x, y, w, h): + poly = np.array(poly) + if poly[:, 0].max() < x or poly[:, 0].min() > x + w: + return True + if poly[:, 1].max() < y or poly[:, 1].min() > y + h: + return True + return False + + +def split_regions(axis): + regions = [] + min_axis = 0 + for i in range(1, axis.shape[0]): + if axis[i] != axis[i - 1] + 1: + region = axis[min_axis:i] + min_axis = i + regions.append(region) + return regions + + +def random_select(axis, max_size): + xx = np.random.choice(axis, size=2) + xmin = np.min(xx) + xmax = np.max(xx) + xmin = np.clip(xmin, 0, max_size - 1) + xmax = np.clip(xmax, 0, max_size - 1) + return xmin, xmax + + +def region_wise_random_select(regions, max_size): + selected_index = list(np.random.choice(len(regions), 2)) + selected_values = [] + for index in selected_index: + axis = regions[index] + xx = int(np.random.choice(axis, size=1)) + selected_values.append(xx) + xmin = min(selected_values) + xmax = max(selected_values) + return xmin, xmax + + +def crop_area(im, text_polys, min_crop_side_ratio, max_tries): + h, w, _ = im.shape + h_array = np.zeros(h, dtype=np.int32) + w_array = np.zeros(w, dtype=np.int32) + for points in text_polys: + points = np.round(points, decimals=0).astype(np.int32) + minx = np.min(points[:, 0]) + maxx = np.max(points[:, 0]) + w_array[minx:maxx] = 1 + miny = np.min(points[:, 1]) + maxy = np.max(points[:, 1]) + h_array[miny:maxy] = 1 + # ensure the cropped area not across a text + h_axis = np.where(h_array == 0)[0] + w_axis = np.where(w_array == 0)[0] + + if len(h_axis) == 0 or len(w_axis) == 0: + return 0, 0, w, h + + h_regions = split_regions(h_axis) + w_regions = split_regions(w_axis) + + for i in range(max_tries): + if len(w_regions) > 1: + xmin, xmax = region_wise_random_select(w_regions, w) + else: + xmin, xmax = random_select(w_axis, w) + if len(h_regions) > 1: + ymin, ymax = region_wise_random_select(h_regions, h) + else: + ymin, ymax = random_select(h_axis, h) + + if ( + xmax - xmin < min_crop_side_ratio * w + or ymax - ymin < min_crop_side_ratio * h + ): + # area too small + continue + num_poly_in_rect = 0 + for poly in text_polys: + if not is_poly_outside_rect(poly, xmin, ymin, xmax - xmin, ymax - ymin): + num_poly_in_rect += 1 + break + + if num_poly_in_rect > 0: + return xmin, ymin, xmax - xmin, ymax - ymin + + return 0, 0, w, h + + +class EastRandomCropData(object): + def __init__( + self, + size=(640, 640), + max_tries=10, + min_crop_side_ratio=0.1, + keep_ratio=True, + **kwargs, + ): + self.size = size + self.max_tries = max_tries + self.min_crop_side_ratio = min_crop_side_ratio + self.keep_ratio = keep_ratio + + def __call__(self, data): + img = data["image"] + text_polys = data["polys"] + ignore_tags = data["ignore_tags"] + texts = data["texts"] + all_care_polys = [text_polys[i] for i, tag in enumerate(ignore_tags) if not tag] + # 计算crop区域 + crop_x, crop_y, crop_w, crop_h = crop_area( + img, all_care_polys, self.min_crop_side_ratio, self.max_tries + ) + # crop 图片 保持比例填充 + scale_w = self.size[0] / crop_w + scale_h = self.size[1] / crop_h + scale = min(scale_w, scale_h) + h = int(crop_h * scale) + w = int(crop_w * scale) + if self.keep_ratio: + padimg = np.zeros((self.size[1], self.size[0], img.shape[2]), img.dtype) + padimg[:h, :w] = cv2.resize( + img[crop_y : crop_y + crop_h, crop_x : crop_x + crop_w], (w, h) + ) + img = padimg + else: + img = cv2.resize( + img[crop_y : crop_y + crop_h, crop_x : crop_x + crop_w], + tuple(self.size), + ) + # crop 文本框 + text_polys_crop = [] + ignore_tags_crop = [] + texts_crop = [] + for poly, text, tag in zip(text_polys, texts, ignore_tags): + poly = ((poly - (crop_x, crop_y)) * scale).tolist() + if not is_poly_outside_rect(poly, 0, 0, w, h): + text_polys_crop.append(poly) + ignore_tags_crop.append(tag) + texts_crop.append(text) + data["image"] = img + data["polys"] = np.array(text_polys_crop) + data["ignore_tags"] = ignore_tags_crop + data["texts"] = texts_crop + return data + + +class RandomCropImgMask(object): + def __init__(self, size, main_key, crop_keys, p=3 / 8, **kwargs): + self.size = size + self.main_key = main_key + self.crop_keys = crop_keys + self.p = p + + def __call__(self, data): + image = data["image"] + + h, w = image.shape[0:2] + th, tw = self.size + if w == tw and h == th: + return data + + mask = data[self.main_key] + if np.max(mask) > 0 and random.random() > self.p: + # make sure to crop the text region + tl = np.min(np.where(mask > 0), axis=1) - (th, tw) + tl[tl < 0] = 0 + br = np.max(np.where(mask > 0), axis=1) - (th, tw) + br[br < 0] = 0 + + br[0] = min(br[0], h - th) + br[1] = min(br[1], w - tw) + + i = random.randint(tl[0], br[0]) if tl[0] < br[0] else 0 + j = random.randint(tl[1], br[1]) if tl[1] < br[1] else 0 + else: + i = random.randint(0, h - th) if h - th > 0 else 0 + j = random.randint(0, w - tw) if w - tw > 0 else 0 + + # return i, j, th, tw + for k in data: + if k in self.crop_keys: + if len(data[k].shape) == 3: + if np.argmin(data[k].shape) == 0: + img = data[k][:, i : i + th, j : j + tw] + if img.shape[1] != img.shape[2]: + a = 1 + elif np.argmin(data[k].shape) == 2: + img = data[k][i : i + th, j : j + tw, :] + if img.shape[1] != img.shape[0]: + a = 1 + else: + img = data[k] + else: + img = data[k][i : i + th, j : j + tw] + if img.shape[0] != img.shape[1]: + a = 1 + data[k] = img + return data diff --git a/modules/onnx_ocr_module/src/ppocr/data/imaug/rec_img_aug.py b/modules/onnx_ocr_module/src/ppocr/data/imaug/rec_img_aug.py new file mode 100644 index 0000000..3404636 --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/data/imaug/rec_img_aug.py @@ -0,0 +1,932 @@ +# copyright (c) 2020 PaddlePaddle Authors. All Rights Reserve. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import math +import cv2 +import numpy as np +import random +import copy +from PIL import Image +import PIL +from .text_image_aug import tia_perspective, tia_stretch, tia_distort +from .abinet_aug import ( + CVGeometry, + CVDeterioration, + CVColorJitter, + SVTRGeometry, + SVTRDeterioration, + ParseQDeterioration, +) +from paddle.vision.transforms import Compose + + +class RecAug(object): + def __init__( + self, + tia_prob=0.4, + crop_prob=0.4, + reverse_prob=0.4, + noise_prob=0.4, + jitter_prob=0.4, + blur_prob=0.4, + hsv_aug_prob=0.4, + **kwargs, + ): + self.tia_prob = tia_prob + self.bda = BaseDataAugmentation( + crop_prob, reverse_prob, noise_prob, jitter_prob, blur_prob, hsv_aug_prob + ) + + def __call__(self, data): + img = data["image"] + h, w, _ = img.shape + + # tia + if random.random() <= self.tia_prob: + if h >= 20 and w >= 20: + img = tia_distort(img, random.randint(3, 6)) + img = tia_stretch(img, random.randint(3, 6)) + img = tia_perspective(img) + + # bda + data["image"] = img + data = self.bda(data) + return data + + +class BaseDataAugmentation(object): + def __init__( + self, + crop_prob=0.4, + reverse_prob=0.4, + noise_prob=0.4, + jitter_prob=0.4, + blur_prob=0.4, + hsv_aug_prob=0.4, + **kwargs, + ): + self.crop_prob = crop_prob + self.reverse_prob = reverse_prob + self.noise_prob = noise_prob + self.jitter_prob = jitter_prob + self.blur_prob = blur_prob + self.hsv_aug_prob = hsv_aug_prob + # for GaussianBlur + self.fil = cv2.getGaussianKernel(ksize=5, sigma=1, ktype=cv2.CV_32F) + + def __call__(self, data): + img = data["image"] + h, w, _ = img.shape + + if random.random() <= self.crop_prob and h >= 20 and w >= 20: + img = get_crop(img) + + if random.random() <= self.blur_prob: + # GaussianBlur + img = cv2.sepFilter2D(img, -1, self.fil, self.fil) + + if random.random() <= self.hsv_aug_prob: + img = hsv_aug(img) + + if random.random() <= self.jitter_prob: + img = jitter(img) + + if random.random() <= self.noise_prob: + img = add_gasuss_noise(img) + + if random.random() <= self.reverse_prob: + img = 255 - img + + data["image"] = img + return data + + +class ABINetRecAug(object): + def __init__( + self, geometry_p=0.5, deterioration_p=0.25, colorjitter_p=0.25, **kwargs + ): + self.transforms = Compose( + [ + CVGeometry( + degrees=45, + translate=(0.0, 0.0), + scale=(0.5, 2.0), + shear=(45, 15), + distortion=0.5, + p=geometry_p, + ), + CVDeterioration(var=20, degrees=6, factor=4, p=deterioration_p), + CVColorJitter( + brightness=0.5, + contrast=0.5, + saturation=0.5, + hue=0.1, + p=colorjitter_p, + ), + ] + ) + + def __call__(self, data): + img = data["image"] + img = self.transforms(img) + data["image"] = img + return data + + +class RecConAug(object): + def __init__( + self, + prob=0.5, + image_shape=(32, 320, 3), + max_text_length=25, + ext_data_num=1, + **kwargs, + ): + self.ext_data_num = ext_data_num + self.prob = prob + self.max_text_length = max_text_length + self.image_shape = image_shape + self.max_wh_ratio = self.image_shape[1] / self.image_shape[0] + + def merge_ext_data(self, data, ext_data): + ori_w = round( + data["image"].shape[1] / data["image"].shape[0] * self.image_shape[0] + ) + ext_w = round( + ext_data["image"].shape[1] + / ext_data["image"].shape[0] + * self.image_shape[0] + ) + data["image"] = cv2.resize(data["image"], (ori_w, self.image_shape[0])) + ext_data["image"] = cv2.resize(ext_data["image"], (ext_w, self.image_shape[0])) + data["image"] = np.concatenate([data["image"], ext_data["image"]], axis=1) + data["label"] += ext_data["label"] + return data + + def __call__(self, data): + rnd_num = random.random() + if rnd_num > self.prob: + return data + for idx, ext_data in enumerate(data["ext_data"]): + if len(data["label"]) + len(ext_data["label"]) > self.max_text_length: + break + concat_ratio = ( + data["image"].shape[1] / data["image"].shape[0] + + ext_data["image"].shape[1] / ext_data["image"].shape[0] + ) + if concat_ratio > self.max_wh_ratio: + break + data = self.merge_ext_data(data, ext_data) + data.pop("ext_data") + return data + + +class SVTRRecAug(object): + def __init__( + self, + aug_type=0, + geometry_p=0.5, + deterioration_p=0.25, + colorjitter_p=0.25, + **kwargs, + ): + self.transforms = Compose( + [ + SVTRGeometry( + aug_type=aug_type, + degrees=45, + translate=(0.0, 0.0), + scale=(0.5, 2.0), + shear=(45, 15), + distortion=0.5, + p=geometry_p, + ), + SVTRDeterioration(var=20, degrees=6, factor=4, p=deterioration_p), + CVColorJitter( + brightness=0.5, + contrast=0.5, + saturation=0.5, + hue=0.1, + p=colorjitter_p, + ), + ] + ) + + def __call__(self, data): + img = data["image"] + img = self.transforms(img) + data["image"] = img + return data + + +class ParseQRecAug(object): + def __init__( + self, + aug_type=0, + geometry_p=0.5, + deterioration_p=0.25, + colorjitter_p=0.25, + **kwargs, + ): + self.transforms = Compose( + [ + SVTRGeometry( + aug_type=aug_type, + degrees=45, + translate=(0.0, 0.0), + scale=(0.5, 2.0), + shear=(45, 15), + distortion=0.5, + p=geometry_p, + ), + ParseQDeterioration( + var=20, degrees=6, lam=20, radius=2.0, factor=4, p=deterioration_p + ), + CVColorJitter( + brightness=0.5, + contrast=0.5, + saturation=0.5, + hue=0.1, + p=colorjitter_p, + ), + ] + ) + + def __call__(self, data): + img = data["image"] + img = self.transforms(img) + data["image"] = img + return data + + +class ClsResizeImg(object): + def __init__(self, image_shape, **kwargs): + self.image_shape = image_shape + + def __call__(self, data): + img = data["image"] + norm_img, _ = resize_norm_img(img, self.image_shape) + data["image"] = norm_img + return data + + +class RecResizeImg(object): + def __init__( + self, + image_shape, + infer_mode=False, + eval_mode=False, + character_dict_path="./ppocr/utils/ppocr_keys_v1.txt", + padding=True, + **kwargs, + ): + self.image_shape = image_shape + self.infer_mode = infer_mode + self.eval_mode = eval_mode + self.character_dict_path = character_dict_path + self.padding = padding + + def __call__(self, data): + img = data["image"] + if self.eval_mode or (self.infer_mode and self.character_dict_path is not None): + norm_img, valid_ratio = resize_norm_img_chinese(img, self.image_shape) + else: + norm_img, valid_ratio = resize_norm_img(img, self.image_shape, self.padding) + data["image"] = norm_img + data["valid_ratio"] = valid_ratio + return data + + +class VLRecResizeImg(object): + def __init__( + self, + image_shape, + infer_mode=False, + character_dict_path="./ppocr/utils/ppocr_keys_v1.txt", + padding=True, + **kwargs, + ): + self.image_shape = image_shape + self.infer_mode = infer_mode + self.character_dict_path = character_dict_path + self.padding = padding + + def __call__(self, data): + img = data["image"] + + imgC, imgH, imgW = self.image_shape + resized_image = cv2.resize(img, (imgW, imgH), interpolation=cv2.INTER_LINEAR) + resized_w = imgW + resized_image = resized_image.astype("float32") + if self.image_shape[0] == 1: + resized_image = resized_image / 255 + norm_img = resized_image[np.newaxis, :] + else: + norm_img = resized_image.transpose((2, 0, 1)) / 255 + valid_ratio = min(1.0, float(resized_w / imgW)) + + data["image"] = norm_img + data["valid_ratio"] = valid_ratio + return data + + +class RFLRecResizeImg(object): + def __init__(self, image_shape, padding=True, interpolation=1, **kwargs): + self.image_shape = image_shape + self.padding = padding + + self.interpolation = interpolation + if self.interpolation == 0: + self.interpolation = cv2.INTER_NEAREST + elif self.interpolation == 1: + self.interpolation = cv2.INTER_LINEAR + elif self.interpolation == 2: + self.interpolation = cv2.INTER_CUBIC + elif self.interpolation == 3: + self.interpolation = cv2.INTER_AREA + else: + raise Exception("Unsupported interpolation type !!!") + + def __call__(self, data): + img = data["image"] + img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) + norm_img, valid_ratio = resize_norm_img( + img, self.image_shape, self.padding, self.interpolation + ) + data["image"] = norm_img + data["valid_ratio"] = valid_ratio + return data + + +class SRNRecResizeImg(object): + def __init__(self, image_shape, num_heads, max_text_length, **kwargs): + self.image_shape = image_shape + self.num_heads = num_heads + self.max_text_length = max_text_length + + def __call__(self, data): + img = data["image"] + norm_img = resize_norm_img_srn(img, self.image_shape) + data["image"] = norm_img + [ + encoder_word_pos, + gsrm_word_pos, + gsrm_slf_attn_bias1, + gsrm_slf_attn_bias2, + ] = srn_other_inputs(self.image_shape, self.num_heads, self.max_text_length) + + data["encoder_word_pos"] = encoder_word_pos + data["gsrm_word_pos"] = gsrm_word_pos + data["gsrm_slf_attn_bias1"] = gsrm_slf_attn_bias1 + data["gsrm_slf_attn_bias2"] = gsrm_slf_attn_bias2 + return data + + +class SARRecResizeImg(object): + def __init__(self, image_shape, width_downsample_ratio=0.25, **kwargs): + self.image_shape = image_shape + self.width_downsample_ratio = width_downsample_ratio + + def __call__(self, data): + img = data["image"] + norm_img, resize_shape, pad_shape, valid_ratio = resize_norm_img_sar( + img, self.image_shape, self.width_downsample_ratio + ) + data["image"] = norm_img + data["resized_shape"] = resize_shape + data["pad_shape"] = pad_shape + data["valid_ratio"] = valid_ratio + return data + + +class PRENResizeImg(object): + def __init__(self, image_shape, **kwargs): + """ + According to original paper's realization, it's a hard resize method here. + So maybe you should optimize it to fit for your task better. + """ + self.dst_h, self.dst_w = image_shape + + def __call__(self, data): + img = data["image"] + resized_img = cv2.resize( + img, (self.dst_w, self.dst_h), interpolation=cv2.INTER_LINEAR + ) + resized_img = resized_img.transpose((2, 0, 1)) / 255 + resized_img -= 0.5 + resized_img /= 0.5 + data["image"] = resized_img.astype(np.float32) + return data + + +class SPINRecResizeImg(object): + def __init__( + self, + image_shape, + interpolation=2, + mean=(127.5, 127.5, 127.5), + std=(127.5, 127.5, 127.5), + **kwargs, + ): + self.image_shape = image_shape + + self.mean = np.array(mean, dtype=np.float32) + self.std = np.array(std, dtype=np.float32) + self.interpolation = interpolation + + def __call__(self, data): + img = data["image"] + img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) + # different interpolation type corresponding the OpenCV + if self.interpolation == 0: + interpolation = cv2.INTER_NEAREST + elif self.interpolation == 1: + interpolation = cv2.INTER_LINEAR + elif self.interpolation == 2: + interpolation = cv2.INTER_CUBIC + elif self.interpolation == 3: + interpolation = cv2.INTER_AREA + else: + raise Exception("Unsupported interpolation type !!!") + # Deal with the image error during image loading + if img is None: + return None + + img = cv2.resize(img, tuple(self.image_shape), interpolation) + img = np.array(img, np.float32) + img = np.expand_dims(img, -1) + img = img.transpose((2, 0, 1)) + # normalize the image + img = img.copy().astype(np.float32) + mean = np.float64(self.mean.reshape(1, -1)) + stdinv = 1 / np.float64(self.std.reshape(1, -1)) + img -= mean + img *= stdinv + data["image"] = img + return data + + +class GrayRecResizeImg(object): + def __init__( + self, + image_shape, + resize_type, + inter_type="Image.Resampling.LANCZOS", + scale=True, + padding=False, + **kwargs, + ): + self.image_shape = image_shape + self.resize_type = resize_type + self.padding = padding + self.inter_type = eval(inter_type) + self.scale = scale + + def __call__(self, data): + img = data["image"] + img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) + image_shape = self.image_shape + if self.padding: + imgC, imgH, imgW = image_shape + # todo: change to 0 and modified image shape + h = img.shape[0] + w = img.shape[1] + ratio = w / float(h) + if math.ceil(imgH * ratio) > imgW: + resized_w = imgW + else: + resized_w = int(math.ceil(imgH * ratio)) + resized_image = cv2.resize(img, (resized_w, imgH)) + norm_img = np.expand_dims(resized_image, -1) + norm_img = norm_img.transpose((2, 0, 1)) + resized_image = norm_img.astype(np.float32) / 128.0 - 1.0 + padding_im = np.zeros((imgC, imgH, imgW), dtype=np.float32) + padding_im[:, :, 0:resized_w] = resized_image + data["image"] = padding_im + return data + if self.resize_type == "PIL": + image_pil = Image.fromarray(np.uint8(img)) + img = image_pil.resize(self.image_shape, self.inter_type) + img = np.array(img) + if self.resize_type == "OpenCV": + img = cv2.resize(img, self.image_shape) + norm_img = np.expand_dims(img, -1) + norm_img = norm_img.transpose((2, 0, 1)) + if self.scale: + data["image"] = norm_img.astype(np.float32) / 128.0 - 1.0 + else: + data["image"] = norm_img.astype(np.float32) / 255.0 + return data + + +class ABINetRecResizeImg(object): + def __init__(self, image_shape, **kwargs): + self.image_shape = image_shape + + def __call__(self, data): + img = data["image"] + norm_img, valid_ratio = resize_norm_img_abinet(img, self.image_shape) + data["image"] = norm_img + data["valid_ratio"] = valid_ratio + return data + + +class SVTRRecResizeImg(object): + def __init__(self, image_shape, padding=True, **kwargs): + self.image_shape = image_shape + self.padding = padding + + def __call__(self, data): + img = data["image"] + + norm_img, valid_ratio = resize_norm_img(img, self.image_shape, self.padding) + data["image"] = norm_img + data["valid_ratio"] = valid_ratio + return data + + +class RobustScannerRecResizeImg(object): + def __init__( + self, image_shape, max_text_length, width_downsample_ratio=0.25, **kwargs + ): + self.image_shape = image_shape + self.width_downsample_ratio = width_downsample_ratio + self.max_text_length = max_text_length + + def __call__(self, data): + img = data["image"] + norm_img, resize_shape, pad_shape, valid_ratio = resize_norm_img_sar( + img, self.image_shape, self.width_downsample_ratio + ) + word_positons = np.array(range(0, self.max_text_length)).astype("int64") + data["image"] = norm_img + data["resized_shape"] = resize_shape + data["pad_shape"] = pad_shape + data["valid_ratio"] = valid_ratio + data["word_positons"] = word_positons + return data + + +def resize_norm_img_sar(img, image_shape, width_downsample_ratio=0.25): + imgC, imgH, imgW_min, imgW_max = image_shape + h = img.shape[0] + w = img.shape[1] + valid_ratio = 1.0 + # make sure new_width is an integral multiple of width_divisor. + width_divisor = int(1 / width_downsample_ratio) + # resize + ratio = w / float(h) + resize_w = math.ceil(imgH * ratio) + if resize_w % width_divisor != 0: + resize_w = round(resize_w / width_divisor) * width_divisor + if imgW_min is not None: + resize_w = max(imgW_min, resize_w) + if imgW_max is not None: + valid_ratio = min(1.0, 1.0 * resize_w / imgW_max) + resize_w = min(imgW_max, resize_w) + resized_image = cv2.resize(img, (resize_w, imgH)) + resized_image = resized_image.astype("float32") + # norm + if image_shape[0] == 1: + resized_image = resized_image / 255 + resized_image = resized_image[np.newaxis, :] + else: + resized_image = resized_image.transpose((2, 0, 1)) / 255 + resized_image -= 0.5 + resized_image /= 0.5 + resize_shape = resized_image.shape + padding_im = -1.0 * np.ones((imgC, imgH, imgW_max), dtype=np.float32) + padding_im[:, :, 0:resize_w] = resized_image + pad_shape = padding_im.shape + + return padding_im, resize_shape, pad_shape, valid_ratio + + +def resize_norm_img(img, image_shape, padding=True, interpolation=cv2.INTER_LINEAR): + imgC, imgH, imgW = image_shape + h = img.shape[0] + w = img.shape[1] + if not padding: + resized_image = cv2.resize(img, (imgW, imgH), interpolation=interpolation) + resized_w = imgW + else: + ratio = w / float(h) + if math.ceil(imgH * ratio) > imgW: + resized_w = imgW + else: + resized_w = int(math.ceil(imgH * ratio)) + resized_image = cv2.resize(img, (resized_w, imgH)) + resized_image = resized_image.astype("float32") + if image_shape[0] == 1: + resized_image = resized_image / 255 + resized_image = resized_image[np.newaxis, :] + else: + resized_image = resized_image.transpose((2, 0, 1)) / 255 + resized_image -= 0.5 + resized_image /= 0.5 + padding_im = np.zeros((imgC, imgH, imgW), dtype=np.float32) + padding_im[:, :, 0:resized_w] = resized_image + valid_ratio = min(1.0, float(resized_w / imgW)) + return padding_im, valid_ratio + + +def resize_norm_img_chinese(img, image_shape): + imgC, imgH, imgW = image_shape + # todo: change to 0 and modified image shape + max_wh_ratio = imgW * 1.0 / imgH + h, w = img.shape[0], img.shape[1] + ratio = w * 1.0 / h + max_wh_ratio = max(max_wh_ratio, ratio) + imgW = int(imgH * max_wh_ratio) + if math.ceil(imgH * ratio) > imgW: + resized_w = imgW + else: + resized_w = int(math.ceil(imgH * ratio)) + resized_image = cv2.resize(img, (resized_w, imgH)) + resized_image = resized_image.astype("float32") + if image_shape[0] == 1: + resized_image = resized_image / 255 + resized_image = resized_image[np.newaxis, :] + else: + resized_image = resized_image.transpose((2, 0, 1)) / 255 + resized_image -= 0.5 + resized_image /= 0.5 + padding_im = np.zeros((imgC, imgH, imgW), dtype=np.float32) + padding_im[:, :, 0:resized_w] = resized_image + valid_ratio = min(1.0, float(resized_w / imgW)) + return padding_im, valid_ratio + + +def resize_norm_img_srn(img, image_shape): + imgC, imgH, imgW = image_shape + + img_black = np.zeros((imgH, imgW)) + im_hei = img.shape[0] + im_wid = img.shape[1] + + if im_wid <= im_hei * 1: + img_new = cv2.resize(img, (imgH * 1, imgH)) + elif im_wid <= im_hei * 2: + img_new = cv2.resize(img, (imgH * 2, imgH)) + elif im_wid <= im_hei * 3: + img_new = cv2.resize(img, (imgH * 3, imgH)) + else: + img_new = cv2.resize(img, (imgW, imgH)) + + img_np = np.asarray(img_new) + img_np = cv2.cvtColor(img_np, cv2.COLOR_BGR2GRAY) + img_black[:, 0 : img_np.shape[1]] = img_np + img_black = img_black[:, :, np.newaxis] + + row, col, c = img_black.shape + c = 1 + + return np.reshape(img_black, (c, row, col)).astype(np.float32) + + +def resize_norm_img_abinet(img, image_shape): + imgC, imgH, imgW = image_shape + + resized_image = cv2.resize(img, (imgW, imgH), interpolation=cv2.INTER_LINEAR) + resized_w = imgW + resized_image = resized_image.astype("float32") + resized_image = resized_image / 255.0 + + mean = np.array([0.485, 0.456, 0.406]) + std = np.array([0.229, 0.224, 0.225]) + resized_image = (resized_image - mean[None, None, ...]) / std[None, None, ...] + resized_image = resized_image.transpose((2, 0, 1)) + resized_image = resized_image.astype("float32") + + valid_ratio = min(1.0, float(resized_w / imgW)) + return resized_image, valid_ratio + + +def srn_other_inputs(image_shape, num_heads, max_text_length): + imgC, imgH, imgW = image_shape + feature_dim = int((imgH / 8) * (imgW / 8)) + + encoder_word_pos = ( + np.array(range(0, feature_dim)).reshape((feature_dim, 1)).astype("int64") + ) + gsrm_word_pos = ( + np.array(range(0, max_text_length)) + .reshape((max_text_length, 1)) + .astype("int64") + ) + + gsrm_attn_bias_data = np.ones((1, max_text_length, max_text_length)) + gsrm_slf_attn_bias1 = np.triu(gsrm_attn_bias_data, 1).reshape( + [1, max_text_length, max_text_length] + ) + gsrm_slf_attn_bias1 = np.tile(gsrm_slf_attn_bias1, [num_heads, 1, 1]) * [-1e9] + + gsrm_slf_attn_bias2 = np.tril(gsrm_attn_bias_data, -1).reshape( + [1, max_text_length, max_text_length] + ) + gsrm_slf_attn_bias2 = np.tile(gsrm_slf_attn_bias2, [num_heads, 1, 1]) * [-1e9] + + return [encoder_word_pos, gsrm_word_pos, gsrm_slf_attn_bias1, gsrm_slf_attn_bias2] + + +def flag(): + """ + flag + """ + return 1 if random.random() > 0.5000001 else -1 + + +def hsv_aug(img): + """ + cvtColor + """ + hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV) + delta = 0.001 * random.random() * flag() + hsv[:, :, 2] = hsv[:, :, 2] * (1 + delta) + new_img = cv2.cvtColor(hsv, cv2.COLOR_HSV2BGR) + return new_img + + +def blur(img): + """ + blur + """ + h, w, _ = img.shape + if h > 10 and w > 10: + return cv2.GaussianBlur(img, (5, 5), 1) + else: + return img + + +def jitter(img): + """ + jitter + """ + w, h, _ = img.shape + if h > 10 and w > 10: + thres = min(w, h) + s = int(random.random() * thres * 0.01) + src_img = img.copy() + for i in range(s): + img[i:, i:, :] = src_img[: w - i, : h - i, :] + return img + else: + return img + + +def add_gasuss_noise(image, mean=0, var=0.1): + """ + Gasuss noise + """ + + noise = np.random.normal(mean, var**0.5, image.shape) + out = image + 0.5 * noise + out = np.clip(out, 0, 255) + out = np.uint8(out) + return out + + +def get_crop(image): + """ + random crop + """ + h, w, _ = image.shape + top_min = 1 + top_max = 8 + top_crop = int(random.randint(top_min, top_max)) + top_crop = min(top_crop, h - 1) + crop_img = image.copy() + ratio = random.randint(0, 1) + if ratio: + crop_img = crop_img[top_crop:h, :, :] + else: + crop_img = crop_img[0 : h - top_crop, :, :] + return crop_img + + +def rad(x): + """ + rad + """ + return x * np.pi / 180 + + +def get_warpR(config): + """ + get_warpR + """ + anglex, angley, anglez, fov, w, h, r = ( + config.anglex, + config.angley, + config.anglez, + config.fov, + config.w, + config.h, + config.r, + ) + if w > 69 and w < 112: + anglex = anglex * 1.5 + + z = np.sqrt(w**2 + h**2) / 2 / np.tan(rad(fov / 2)) + # Homogeneous coordinate transformation matrix + rx = np.array( + [ + [1, 0, 0, 0], + [0, np.cos(rad(anglex)), -np.sin(rad(anglex)), 0], + [ + 0, + -np.sin(rad(anglex)), + np.cos(rad(anglex)), + 0, + ], + [0, 0, 0, 1], + ], + np.float32, + ) + ry = np.array( + [ + [np.cos(rad(angley)), 0, np.sin(rad(angley)), 0], + [0, 1, 0, 0], + [ + -np.sin(rad(angley)), + 0, + np.cos(rad(angley)), + 0, + ], + [0, 0, 0, 1], + ], + np.float32, + ) + rz = np.array( + [ + [np.cos(rad(anglez)), np.sin(rad(anglez)), 0, 0], + [-np.sin(rad(anglez)), np.cos(rad(anglez)), 0, 0], + [0, 0, 1, 0], + [0, 0, 0, 1], + ], + np.float32, + ) + r = rx.dot(ry).dot(rz) + # generate 4 points + pcenter = np.array([h / 2, w / 2, 0, 0], np.float32) + p1 = np.array([0, 0, 0, 0], np.float32) - pcenter + p2 = np.array([w, 0, 0, 0], np.float32) - pcenter + p3 = np.array([0, h, 0, 0], np.float32) - pcenter + p4 = np.array([w, h, 0, 0], np.float32) - pcenter + dst1 = r.dot(p1) + dst2 = r.dot(p2) + dst3 = r.dot(p3) + dst4 = r.dot(p4) + list_dst = np.array([dst1, dst2, dst3, dst4]) + org = np.array([[0, 0], [w, 0], [0, h], [w, h]], np.float32) + dst = np.zeros((4, 2), np.float32) + # Project onto the image plane + dst[:, 0] = list_dst[:, 0] * z / (z - list_dst[:, 2]) + pcenter[0] + dst[:, 1] = list_dst[:, 1] * z / (z - list_dst[:, 2]) + pcenter[1] + + warpR = cv2.getPerspectiveTransform(org, dst) + + dst1, dst2, dst3, dst4 = dst + r1 = int(min(dst1[1], dst2[1])) + r2 = int(max(dst3[1], dst4[1])) + c1 = int(min(dst1[0], dst3[0])) + c2 = int(max(dst2[0], dst4[0])) + + try: + ratio = min(1.0 * h / (r2 - r1), 1.0 * w / (c2 - c1)) + + dx = -c1 + dy = -r1 + T1 = np.float32([[1.0, 0, dx], [0, 1.0, dy], [0, 0, 1.0 / ratio]]) + ret = T1.dot(warpR) + except: + ratio = 1.0 + T1 = np.float32([[1.0, 0, 0], [0, 1.0, 0], [0, 0, 1.0]]) + ret = T1 + return ret, (-r1, -c1), ratio, dst + + +def get_warpAffine(config): + """ + get_warpAffine + """ + anglez = config.anglez + rz = np.array( + [ + [np.cos(rad(anglez)), np.sin(rad(anglez)), 0], + [-np.sin(rad(anglez)), np.cos(rad(anglez)), 0], + ], + np.float32, + ) + return rz diff --git a/modules/onnx_ocr_module/src/ppocr/data/imaug/sast_process.py b/modules/onnx_ocr_module/src/ppocr/data/imaug/sast_process.py new file mode 100644 index 0000000..caa061b --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/data/imaug/sast_process.py @@ -0,0 +1,810 @@ +# copyright (c) 2020 PaddlePaddle Authors. All Rights Reserve. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +""" +This part code is referred from: +https://github.com/songdejia/EAST/blob/master/data_utils.py +""" +import math +import cv2 +import numpy as np +import json +import sys +import os + +__all__ = ["SASTProcessTrain"] + + +class SASTProcessTrain(object): + def __init__( + self, + image_shape=[512, 512], + min_crop_size=24, + min_crop_side_ratio=0.3, + min_text_size=10, + max_text_size=512, + **kwargs, + ): + self.input_size = image_shape[1] + self.min_crop_size = min_crop_size + self.min_crop_side_ratio = min_crop_side_ratio + self.min_text_size = min_text_size + self.max_text_size = max_text_size + + def quad_area(self, poly): + """ + compute area of a polygon + :param poly: + :return: + """ + edge = [ + (poly[1][0] - poly[0][0]) * (poly[1][1] + poly[0][1]), + (poly[2][0] - poly[1][0]) * (poly[2][1] + poly[1][1]), + (poly[3][0] - poly[2][0]) * (poly[3][1] + poly[2][1]), + (poly[0][0] - poly[3][0]) * (poly[0][1] + poly[3][1]), + ] + return np.sum(edge) / 2.0 + + def gen_quad_from_poly(self, poly): + """ + Generate min area quad from poly. + """ + point_num = poly.shape[0] + min_area_quad = np.zeros((4, 2), dtype=np.float32) + if True: + rect = cv2.minAreaRect( + poly.astype(np.int32) + ) # (center (x,y), (width, height), angle of rotation) + center_point = rect[0] + box = np.array(cv2.boxPoints(rect)) + + first_point_idx = 0 + min_dist = 1e4 + for i in range(4): + dist = ( + np.linalg.norm(box[(i + 0) % 4] - poly[0]) + + np.linalg.norm(box[(i + 1) % 4] - poly[point_num // 2 - 1]) + + np.linalg.norm(box[(i + 2) % 4] - poly[point_num // 2]) + + np.linalg.norm(box[(i + 3) % 4] - poly[-1]) + ) + if dist < min_dist: + min_dist = dist + first_point_idx = i + for i in range(4): + min_area_quad[i] = box[(first_point_idx + i) % 4] + + return min_area_quad + + def check_and_validate_polys(self, polys, tags, xxx_todo_changeme): + """ + check so that the text poly is in the same direction, + and also filter some invalid polygons + :param polys: + :param tags: + :return: + """ + (h, w) = xxx_todo_changeme + if polys.shape[0] == 0: + return polys, np.array([]), np.array([]) + polys[:, :, 0] = np.clip(polys[:, :, 0], 0, w - 1) + polys[:, :, 1] = np.clip(polys[:, :, 1], 0, h - 1) + + validated_polys = [] + validated_tags = [] + hv_tags = [] + for poly, tag in zip(polys, tags): + quad = self.gen_quad_from_poly(poly) + p_area = self.quad_area(quad) + if abs(p_area) < 1: + print("invalid poly") + continue + if p_area > 0: + if tag == False: + print("poly in wrong direction") + tag = True # reversed cases should be ignore + poly = poly[(0, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1), :] + quad = quad[(0, 3, 2, 1), :] + + len_w = np.linalg.norm(quad[0] - quad[1]) + np.linalg.norm( + quad[3] - quad[2] + ) + len_h = np.linalg.norm(quad[0] - quad[3]) + np.linalg.norm( + quad[1] - quad[2] + ) + hv_tag = 1 + + if len_w * 2.0 < len_h: + hv_tag = 0 + + validated_polys.append(poly) + validated_tags.append(tag) + hv_tags.append(hv_tag) + return np.array(validated_polys), np.array(validated_tags), np.array(hv_tags) + + def crop_area(self, im, polys, tags, hv_tags, crop_background=False, max_tries=25): + """ + make random crop from the input image + :param im: + :param polys: + :param tags: + :param crop_background: + :param max_tries: 50 -> 25 + :return: + """ + h, w, _ = im.shape + pad_h = h // 10 + pad_w = w // 10 + h_array = np.zeros((h + pad_h * 2), dtype=np.int32) + w_array = np.zeros((w + pad_w * 2), dtype=np.int32) + for poly in polys: + poly = np.round(poly, decimals=0).astype(np.int32) + minx = np.min(poly[:, 0]) + maxx = np.max(poly[:, 0]) + w_array[minx + pad_w : maxx + pad_w] = 1 + miny = np.min(poly[:, 1]) + maxy = np.max(poly[:, 1]) + h_array[miny + pad_h : maxy + pad_h] = 1 + # ensure the cropped area not across a text + h_axis = np.where(h_array == 0)[0] + w_axis = np.where(w_array == 0)[0] + if len(h_axis) == 0 or len(w_axis) == 0: + return im, polys, tags, hv_tags + for i in range(max_tries): + xx = np.random.choice(w_axis, size=2) + xmin = np.min(xx) - pad_w + xmax = np.max(xx) - pad_w + xmin = np.clip(xmin, 0, w - 1) + xmax = np.clip(xmax, 0, w - 1) + yy = np.random.choice(h_axis, size=2) + ymin = np.min(yy) - pad_h + ymax = np.max(yy) - pad_h + ymin = np.clip(ymin, 0, h - 1) + ymax = np.clip(ymax, 0, h - 1) + # if xmax - xmin < ARGS.min_crop_side_ratio * w or \ + # ymax - ymin < ARGS.min_crop_side_ratio * h: + if xmax - xmin < self.min_crop_size or ymax - ymin < self.min_crop_size: + # area too small + continue + if polys.shape[0] != 0: + poly_axis_in_area = ( + (polys[:, :, 0] >= xmin) + & (polys[:, :, 0] <= xmax) + & (polys[:, :, 1] >= ymin) + & (polys[:, :, 1] <= ymax) + ) + selected_polys = np.where(np.sum(poly_axis_in_area, axis=1) == 4)[0] + else: + selected_polys = [] + if len(selected_polys) == 0: + # no text in this area + if crop_background: + return ( + im[ymin : ymax + 1, xmin : xmax + 1, :], + polys[selected_polys], + tags[selected_polys], + hv_tags[selected_polys], + ) + else: + continue + im = im[ymin : ymax + 1, xmin : xmax + 1, :] + polys = polys[selected_polys] + tags = tags[selected_polys] + hv_tags = hv_tags[selected_polys] + polys[:, :, 0] -= xmin + polys[:, :, 1] -= ymin + return im, polys, tags, hv_tags + + return im, polys, tags, hv_tags + + def generate_direction_map(self, poly_quads, direction_map): + """ """ + width_list = [] + height_list = [] + for quad in poly_quads: + quad_w = ( + np.linalg.norm(quad[0] - quad[1]) + np.linalg.norm(quad[2] - quad[3]) + ) / 2.0 + quad_h = ( + np.linalg.norm(quad[0] - quad[3]) + np.linalg.norm(quad[2] - quad[1]) + ) / 2.0 + width_list.append(quad_w) + height_list.append(quad_h) + norm_width = max(sum(width_list) / (len(width_list) + 1e-6), 1.0) + average_height = max(sum(height_list) / (len(height_list) + 1e-6), 1.0) + + for quad in poly_quads: + direct_vector_full = ((quad[1] + quad[2]) - (quad[0] + quad[3])) / 2.0 + direct_vector = ( + direct_vector_full + / (np.linalg.norm(direct_vector_full) + 1e-6) + * norm_width + ) + direction_label = tuple( + map( + float, + [direct_vector[0], direct_vector[1], 1.0 / (average_height + 1e-6)], + ) + ) + cv2.fillPoly( + direction_map, + quad.round().astype(np.int32)[np.newaxis, :, :], + direction_label, + ) + return direction_map + + def calculate_average_height(self, poly_quads): + """ """ + height_list = [] + for quad in poly_quads: + quad_h = ( + np.linalg.norm(quad[0] - quad[3]) + np.linalg.norm(quad[2] - quad[1]) + ) / 2.0 + height_list.append(quad_h) + average_height = max(sum(height_list) / len(height_list), 1.0) + return average_height + + def generate_tcl_label( + self, hw, polys, tags, ds_ratio, tcl_ratio=0.3, shrink_ratio_of_width=0.15 + ): + """ + Generate polygon. + """ + h, w = hw + h, w = int(h * ds_ratio), int(w * ds_ratio) + polys = polys * ds_ratio + + score_map = np.zeros( + ( + h, + w, + ), + dtype=np.float32, + ) + tbo_map = np.zeros((h, w, 5), dtype=np.float32) + training_mask = np.ones( + ( + h, + w, + ), + dtype=np.float32, + ) + direction_map = np.ones((h, w, 3)) * np.array([0, 0, 1]).reshape( + [1, 1, 3] + ).astype(np.float32) + + for poly_idx, poly_tag in enumerate(zip(polys, tags)): + poly = poly_tag[0] + tag = poly_tag[1] + + # generate min_area_quad + min_area_quad, center_point = self.gen_min_area_quad_from_poly(poly) + min_area_quad_h = 0.5 * ( + np.linalg.norm(min_area_quad[0] - min_area_quad[3]) + + np.linalg.norm(min_area_quad[1] - min_area_quad[2]) + ) + min_area_quad_w = 0.5 * ( + np.linalg.norm(min_area_quad[0] - min_area_quad[1]) + + np.linalg.norm(min_area_quad[2] - min_area_quad[3]) + ) + + if ( + min(min_area_quad_h, min_area_quad_w) < self.min_text_size * ds_ratio + or min(min_area_quad_h, min_area_quad_w) > self.max_text_size * ds_ratio + ): + continue + + if tag: + # continue + cv2.fillPoly( + training_mask, poly.astype(np.int32)[np.newaxis, :, :], 0.15 + ) + else: + tcl_poly = self.poly2tcl(poly, tcl_ratio) + tcl_quads = self.poly2quads(tcl_poly) + poly_quads = self.poly2quads(poly) + # stcl map + stcl_quads, quad_index = self.shrink_poly_along_width( + tcl_quads, + shrink_ratio_of_width=shrink_ratio_of_width, + expand_height_ratio=1.0 / tcl_ratio, + ) + # generate tcl map + cv2.fillPoly(score_map, np.round(stcl_quads).astype(np.int32), 1.0) + + # generate tbo map + for idx, quad in enumerate(stcl_quads): + quad_mask = np.zeros((h, w), dtype=np.float32) + quad_mask = cv2.fillPoly( + quad_mask, + np.round(quad[np.newaxis, :, :]).astype(np.int32), + 1.0, + ) + tbo_map = self.gen_quad_tbo( + poly_quads[quad_index[idx]], quad_mask, tbo_map + ) + return score_map, tbo_map, training_mask + + def generate_tvo_and_tco(self, hw, polys, tags, tcl_ratio=0.3, ds_ratio=0.25): + """ + Generate tcl map, tvo map and tbo map. + """ + h, w = hw + h, w = int(h * ds_ratio), int(w * ds_ratio) + polys = polys * ds_ratio + poly_mask = np.zeros((h, w), dtype=np.float32) + + tvo_map = np.ones((9, h, w), dtype=np.float32) + tvo_map[0:-1:2] = np.tile(np.arange(0, w), (h, 1)) + tvo_map[1:-1:2] = np.tile(np.arange(0, w), (h, 1)).T + poly_tv_xy_map = np.zeros((8, h, w), dtype=np.float32) + + # tco map + tco_map = np.ones((3, h, w), dtype=np.float32) + tco_map[0] = np.tile(np.arange(0, w), (h, 1)) + tco_map[1] = np.tile(np.arange(0, w), (h, 1)).T + poly_tc_xy_map = np.zeros((2, h, w), dtype=np.float32) + + poly_short_edge_map = np.ones((h, w), dtype=np.float32) + + for poly, poly_tag in zip(polys, tags): + if poly_tag == True: + continue + + # adjust point order for vertical poly + poly = self.adjust_point(poly) + + # generate min_area_quad + min_area_quad, center_point = self.gen_min_area_quad_from_poly(poly) + min_area_quad_h = 0.5 * ( + np.linalg.norm(min_area_quad[0] - min_area_quad[3]) + + np.linalg.norm(min_area_quad[1] - min_area_quad[2]) + ) + min_area_quad_w = 0.5 * ( + np.linalg.norm(min_area_quad[0] - min_area_quad[1]) + + np.linalg.norm(min_area_quad[2] - min_area_quad[3]) + ) + + # generate tcl map and text, 128 * 128 + tcl_poly = self.poly2tcl(poly, tcl_ratio) + + # generate poly_tv_xy_map + for idx in range(4): + cv2.fillPoly( + poly_tv_xy_map[2 * idx], + np.round(tcl_poly[np.newaxis, :, :]).astype(np.int32), + float(min(max(min_area_quad[idx, 0], 0), w)), + ) + cv2.fillPoly( + poly_tv_xy_map[2 * idx + 1], + np.round(tcl_poly[np.newaxis, :, :]).astype(np.int32), + float(min(max(min_area_quad[idx, 1], 0), h)), + ) + + # generate poly_tc_xy_map + for idx in range(2): + cv2.fillPoly( + poly_tc_xy_map[idx], + np.round(tcl_poly[np.newaxis, :, :]).astype(np.int32), + float(center_point[idx]), + ) + + # generate poly_short_edge_map + cv2.fillPoly( + poly_short_edge_map, + np.round(tcl_poly[np.newaxis, :, :]).astype(np.int32), + float(max(min(min_area_quad_h, min_area_quad_w), 1.0)), + ) + + # generate poly_mask and training_mask + cv2.fillPoly( + poly_mask, np.round(tcl_poly[np.newaxis, :, :]).astype(np.int32), 1 + ) + + tvo_map *= poly_mask + tvo_map[:8] -= poly_tv_xy_map + tvo_map[-1] /= poly_short_edge_map + tvo_map = tvo_map.transpose((1, 2, 0)) + + tco_map *= poly_mask + tco_map[:2] -= poly_tc_xy_map + tco_map[-1] /= poly_short_edge_map + tco_map = tco_map.transpose((1, 2, 0)) + + return tvo_map, tco_map + + def adjust_point(self, poly): + """ + adjust point order. + """ + point_num = poly.shape[0] + if point_num == 4: + len_1 = np.linalg.norm(poly[0] - poly[1]) + len_2 = np.linalg.norm(poly[1] - poly[2]) + len_3 = np.linalg.norm(poly[2] - poly[3]) + len_4 = np.linalg.norm(poly[3] - poly[0]) + + if (len_1 + len_3) * 1.5 < (len_2 + len_4): + poly = poly[[1, 2, 3, 0], :] + + elif point_num > 4: + vector_1 = poly[0] - poly[1] + vector_2 = poly[1] - poly[2] + cos_theta = np.dot(vector_1, vector_2) / ( + np.linalg.norm(vector_1) * np.linalg.norm(vector_2) + 1e-6 + ) + theta = np.arccos(np.round(cos_theta, decimals=4)) + + if abs(theta) > (70 / 180 * math.pi): + index = list(range(1, point_num)) + [0] + poly = poly[np.array(index), :] + return poly + + def gen_min_area_quad_from_poly(self, poly): + """ + Generate min area quad from poly. + """ + point_num = poly.shape[0] + min_area_quad = np.zeros((4, 2), dtype=np.float32) + if point_num == 4: + min_area_quad = poly + center_point = np.sum(poly, axis=0) / 4 + else: + rect = cv2.minAreaRect( + poly.astype(np.int32) + ) # (center (x,y), (width, height), angle of rotation) + center_point = rect[0] + box = np.array(cv2.boxPoints(rect)) + + first_point_idx = 0 + min_dist = 1e4 + for i in range(4): + dist = ( + np.linalg.norm(box[(i + 0) % 4] - poly[0]) + + np.linalg.norm(box[(i + 1) % 4] - poly[point_num // 2 - 1]) + + np.linalg.norm(box[(i + 2) % 4] - poly[point_num // 2]) + + np.linalg.norm(box[(i + 3) % 4] - poly[-1]) + ) + if dist < min_dist: + min_dist = dist + first_point_idx = i + + for i in range(4): + min_area_quad[i] = box[(first_point_idx + i) % 4] + + return min_area_quad, center_point + + def shrink_quad_along_width(self, quad, begin_width_ratio=0.0, end_width_ratio=1.0): + """ + Generate shrink_quad_along_width. + """ + ratio_pair = np.array( + [[begin_width_ratio], [end_width_ratio]], dtype=np.float32 + ) + p0_1 = quad[0] + (quad[1] - quad[0]) * ratio_pair + p3_2 = quad[3] + (quad[2] - quad[3]) * ratio_pair + return np.array([p0_1[0], p0_1[1], p3_2[1], p3_2[0]]) + + def shrink_poly_along_width( + self, quads, shrink_ratio_of_width, expand_height_ratio=1.0 + ): + """ + shrink poly with given length. + """ + upper_edge_list = [] + + def get_cut_info(edge_len_list, cut_len): + for idx, edge_len in enumerate(edge_len_list): + cut_len -= edge_len + if cut_len <= 0.000001: + ratio = (cut_len + edge_len_list[idx]) / edge_len_list[idx] + return idx, ratio + + for quad in quads: + upper_edge_len = np.linalg.norm(quad[0] - quad[1]) + upper_edge_list.append(upper_edge_len) + + # length of left edge and right edge. + left_length = np.linalg.norm(quads[0][0] - quads[0][3]) * expand_height_ratio + right_length = np.linalg.norm(quads[-1][1] - quads[-1][2]) * expand_height_ratio + + shrink_length = ( + min(left_length, right_length, sum(upper_edge_list)) * shrink_ratio_of_width + ) + # shrinking length + upper_len_left = shrink_length + upper_len_right = sum(upper_edge_list) - shrink_length + + left_idx, left_ratio = get_cut_info(upper_edge_list, upper_len_left) + left_quad = self.shrink_quad_along_width( + quads[left_idx], begin_width_ratio=left_ratio, end_width_ratio=1 + ) + right_idx, right_ratio = get_cut_info(upper_edge_list, upper_len_right) + right_quad = self.shrink_quad_along_width( + quads[right_idx], begin_width_ratio=0, end_width_ratio=right_ratio + ) + + out_quad_list = [] + if left_idx == right_idx: + out_quad_list.append( + [left_quad[0], right_quad[1], right_quad[2], left_quad[3]] + ) + else: + out_quad_list.append(left_quad) + for idx in range(left_idx + 1, right_idx): + out_quad_list.append(quads[idx]) + out_quad_list.append(right_quad) + + return np.array(out_quad_list), list(range(left_idx, right_idx + 1)) + + def vector_angle(self, A, B): + """ + Calculate the angle between vector AB and x-axis positive direction. + """ + AB = np.array([B[1] - A[1], B[0] - A[0]]) + return np.arctan2(*AB) + + def theta_line_cross_point(self, theta, point): + """ + Calculate the line through given point and angle in ax + by + c =0 form. + """ + x, y = point + cos = np.cos(theta) + sin = np.sin(theta) + return [sin, -cos, cos * y - sin * x] + + def line_cross_two_point(self, A, B): + """ + Calculate the line through given point A and B in ax + by + c =0 form. + """ + angle = self.vector_angle(A, B) + return self.theta_line_cross_point(angle, A) + + def average_angle(self, poly): + """ + Calculate the average angle between left and right edge in given poly. + """ + p0, p1, p2, p3 = poly + angle30 = self.vector_angle(p3, p0) + angle21 = self.vector_angle(p2, p1) + return (angle30 + angle21) / 2 + + def line_cross_point(self, line1, line2): + """ + line1 and line2 in 0=ax+by+c form, compute the cross point of line1 and line2 + """ + a1, b1, c1 = line1 + a2, b2, c2 = line2 + d = a1 * b2 - a2 * b1 + + if d == 0: + # print("line1", line1) + # print("line2", line2) + print("Cross point does not exist") + return np.array([0, 0], dtype=np.float32) + else: + x = (b1 * c2 - b2 * c1) / d + y = (a2 * c1 - a1 * c2) / d + + return np.array([x, y], dtype=np.float32) + + def quad2tcl(self, poly, ratio): + """ + Generate center line by poly clock-wise point. (4, 2) + """ + ratio_pair = np.array([[0.5 - ratio / 2], [0.5 + ratio / 2]], dtype=np.float32) + p0_3 = poly[0] + (poly[3] - poly[0]) * ratio_pair + p1_2 = poly[1] + (poly[2] - poly[1]) * ratio_pair + return np.array([p0_3[0], p1_2[0], p1_2[1], p0_3[1]]) + + def poly2tcl(self, poly, ratio): + """ + Generate center line by poly clock-wise point. + """ + ratio_pair = np.array([[0.5 - ratio / 2], [0.5 + ratio / 2]], dtype=np.float32) + tcl_poly = np.zeros_like(poly) + point_num = poly.shape[0] + + for idx in range(point_num // 2): + point_pair = ( + poly[idx] + (poly[point_num - 1 - idx] - poly[idx]) * ratio_pair + ) + tcl_poly[idx] = point_pair[0] + tcl_poly[point_num - 1 - idx] = point_pair[1] + return tcl_poly + + def gen_quad_tbo(self, quad, tcl_mask, tbo_map): + """ + Generate tbo_map for give quad. + """ + # upper and lower line function: ax + by + c = 0; + up_line = self.line_cross_two_point(quad[0], quad[1]) + lower_line = self.line_cross_two_point(quad[3], quad[2]) + + quad_h = 0.5 * ( + np.linalg.norm(quad[0] - quad[3]) + np.linalg.norm(quad[1] - quad[2]) + ) + quad_w = 0.5 * ( + np.linalg.norm(quad[0] - quad[1]) + np.linalg.norm(quad[2] - quad[3]) + ) + + # average angle of left and right line. + angle = self.average_angle(quad) + + xy_in_poly = np.argwhere(tcl_mask == 1) + for y, x in xy_in_poly: + point = (x, y) + line = self.theta_line_cross_point(angle, point) + cross_point_upper = self.line_cross_point(up_line, line) + cross_point_lower = self.line_cross_point(lower_line, line) + ##FIX, offset reverse + upper_offset_x, upper_offset_y = cross_point_upper - point + lower_offset_x, lower_offset_y = cross_point_lower - point + tbo_map[y, x, 0] = upper_offset_y + tbo_map[y, x, 1] = upper_offset_x + tbo_map[y, x, 2] = lower_offset_y + tbo_map[y, x, 3] = lower_offset_x + tbo_map[y, x, 4] = 1.0 / max(min(quad_h, quad_w), 1.0) * 2 + return tbo_map + + def poly2quads(self, poly): + """ + Split poly into quads. + """ + quad_list = [] + point_num = poly.shape[0] + + # point pair + point_pair_list = [] + for idx in range(point_num // 2): + point_pair = [poly[idx], poly[point_num - 1 - idx]] + point_pair_list.append(point_pair) + + quad_num = point_num // 2 - 1 + for idx in range(quad_num): + # reshape and adjust to clock-wise + quad_list.append( + (np.array(point_pair_list)[[idx, idx + 1]]).reshape(4, 2)[[0, 2, 3, 1]] + ) + + return np.array(quad_list) + + def __call__(self, data): + im = data["image"] + text_polys = data["polys"] + text_tags = data["ignore_tags"] + if im is None: + return None + if text_polys.shape[0] == 0: + return None + + h, w, _ = im.shape + text_polys, text_tags, hv_tags = self.check_and_validate_polys( + text_polys, text_tags, (h, w) + ) + + if text_polys.shape[0] == 0: + return None + + # set aspect ratio and keep area fix + asp_scales = np.arange(1.0, 1.55, 0.1) + asp_scale = np.random.choice(asp_scales) + + if np.random.rand() < 0.5: + asp_scale = 1.0 / asp_scale + asp_scale = math.sqrt(asp_scale) + + asp_wx = asp_scale + asp_hy = 1.0 / asp_scale + im = cv2.resize(im, dsize=None, fx=asp_wx, fy=asp_hy) + text_polys[:, :, 0] *= asp_wx + text_polys[:, :, 1] *= asp_hy + + h, w, _ = im.shape + if max(h, w) > 2048: + rd_scale = 2048.0 / max(h, w) + im = cv2.resize(im, dsize=None, fx=rd_scale, fy=rd_scale) + text_polys *= rd_scale + h, w, _ = im.shape + if min(h, w) < 16: + return None + + # no background + im, text_polys, text_tags, hv_tags = self.crop_area( + im, text_polys, text_tags, hv_tags, crop_background=False + ) + + if text_polys.shape[0] == 0: + return None + # continue for all ignore case + if np.sum((text_tags * 1.0)) >= text_tags.size: + return None + new_h, new_w, _ = im.shape + if (new_h is None) or (new_w is None): + return None + # resize image + std_ratio = float(self.input_size) / max(new_w, new_h) + rand_scales = np.array( + [0.25, 0.375, 0.5, 0.625, 0.75, 0.875, 1.0, 1.0, 1.0, 1.0, 1.0] + ) + rz_scale = std_ratio * np.random.choice(rand_scales) + im = cv2.resize(im, dsize=None, fx=rz_scale, fy=rz_scale) + text_polys[:, :, 0] *= rz_scale + text_polys[:, :, 1] *= rz_scale + + # add gaussian blur + if np.random.rand() < 0.1 * 0.5: + ks = np.random.permutation(5)[0] + 1 + ks = int(ks / 2) * 2 + 1 + im = cv2.GaussianBlur(im, ksize=(ks, ks), sigmaX=0, sigmaY=0) + # add brighter + if np.random.rand() < 0.1 * 0.5: + im = im * (1.0 + np.random.rand() * 0.5) + im = np.clip(im, 0.0, 255.0) + # add darker + if np.random.rand() < 0.1 * 0.5: + im = im * (1.0 - np.random.rand() * 0.5) + im = np.clip(im, 0.0, 255.0) + + # Padding the im to [input_size, input_size] + new_h, new_w, _ = im.shape + if min(new_w, new_h) < self.input_size * 0.5: + return None + + im_padded = np.ones((self.input_size, self.input_size, 3), dtype=np.float32) + im_padded[:, :, 2] = 0.485 * 255 + im_padded[:, :, 1] = 0.456 * 255 + im_padded[:, :, 0] = 0.406 * 255 + + # Random the start position + del_h = self.input_size - new_h + del_w = self.input_size - new_w + sh, sw = 0, 0 + if del_h > 1: + sh = int(np.random.rand() * del_h) + if del_w > 1: + sw = int(np.random.rand() * del_w) + + # Padding + im_padded[sh : sh + new_h, sw : sw + new_w, :] = im.copy() + text_polys[:, :, 0] += sw + text_polys[:, :, 1] += sh + + score_map, border_map, training_mask = self.generate_tcl_label( + (self.input_size, self.input_size), text_polys, text_tags, 0.25 + ) + + # SAST head + tvo_map, tco_map = self.generate_tvo_and_tco( + (self.input_size, self.input_size), + text_polys, + text_tags, + tcl_ratio=0.3, + ds_ratio=0.25, + ) + # print("test--------tvo_map shape:", tvo_map.shape) + + im_padded[:, :, 2] -= 0.485 * 255 + im_padded[:, :, 1] -= 0.456 * 255 + im_padded[:, :, 0] -= 0.406 * 255 + im_padded[:, :, 2] /= 255.0 * 0.229 + im_padded[:, :, 1] /= 255.0 * 0.224 + im_padded[:, :, 0] /= 255.0 * 0.225 + im_padded = im_padded.transpose((2, 0, 1)) + + data["image"] = im_padded[::-1, :, :] + data["score_map"] = score_map[np.newaxis, :, :] + data["border_map"] = border_map.transpose((2, 0, 1)) + data["training_mask"] = training_mask[np.newaxis, :, :] + data["tvo_map"] = tvo_map.transpose((2, 0, 1)) + data["tco_map"] = tco_map.transpose((2, 0, 1)) + return data diff --git a/modules/onnx_ocr_module/src/ppocr/data/imaug/ssl_img_aug.py b/modules/onnx_ocr_module/src/ppocr/data/imaug/ssl_img_aug.py new file mode 100644 index 0000000..8162087 --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/data/imaug/ssl_img_aug.py @@ -0,0 +1,55 @@ +# copyright (c) 2020 PaddlePaddle Authors. All Rights Reserve. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import math +import cv2 +import numpy as np +import random +from PIL import Image + +from .rec_img_aug import resize_norm_img + + +class SSLRotateResize(object): + def __init__( + self, image_shape, padding=False, select_all=True, mode="train", **kwargs + ): + self.image_shape = image_shape + self.padding = padding + self.select_all = select_all + self.mode = mode + + def __call__(self, data): + img = data["image"] + + data["image_r90"] = cv2.rotate(img, cv2.ROTATE_90_CLOCKWISE) + data["image_r180"] = cv2.rotate(data["image_r90"], cv2.ROTATE_90_CLOCKWISE) + data["image_r270"] = cv2.rotate(data["image_r180"], cv2.ROTATE_90_CLOCKWISE) + + images = [] + for key in ["image", "image_r90", "image_r180", "image_r270"]: + images.append( + resize_norm_img( + data.pop(key), image_shape=self.image_shape, padding=self.padding + )[0] + ) + data["image"] = np.stack(images, axis=0) + data["label"] = np.array(list(range(4))) + if not self.select_all: + data["image"] = data["image"][0::2] # just choose 0 and 180 + data["label"] = data["label"][0:2] # label needs to be continuous + if self.mode == "test": + data["image"] = data["image"][0] + data["label"] = data["label"][0] + return data diff --git a/modules/onnx_ocr_module/src/ppocr/data/imaug/table_ops.py b/modules/onnx_ocr_module/src/ppocr/data/imaug/table_ops.py new file mode 100644 index 0000000..cc244dd --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/data/imaug/table_ops.py @@ -0,0 +1,232 @@ +""" +# Copyright (c) 2020 PaddlePaddle Authors. All Rights Reserved +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function +from __future__ import unicode_literals + +import sys +import cv2 +import numpy as np + + +class GenTableMask(object): + """gen table mask""" + + def __init__(self, shrink_h_max, shrink_w_max, mask_type=0, **kwargs): + self.shrink_h_max = 5 + self.shrink_w_max = 5 + self.mask_type = mask_type + + def projection(self, erosion, h, w, spilt_threshold=0): + # 水平投影 + projection_map = np.ones_like(erosion) + project_val_array = [0 for _ in range(0, h)] + + for j in range(0, h): + for i in range(0, w): + if erosion[j, i] == 255: + project_val_array[j] += 1 + # 根据数组,获取切割点 + start_idx = 0 # 记录进入字符区的索引 + end_idx = 0 # 记录进入空白区域的索引 + in_text = False # 是否遍历到了字符区内 + box_list = [] + for i in range(len(project_val_array)): + if ( + in_text == False and project_val_array[i] > spilt_threshold + ): # 进入字符区了 + in_text = True + start_idx = i + elif ( + project_val_array[i] <= spilt_threshold and in_text == True + ): # 进入空白区了 + end_idx = i + in_text = False + if end_idx - start_idx <= 2: + continue + box_list.append((start_idx, end_idx + 1)) + + if in_text: + box_list.append((start_idx, h - 1)) + # 绘制投影直方图 + for j in range(0, h): + for i in range(0, project_val_array[j]): + projection_map[j, i] = 0 + return box_list, projection_map + + def projection_cx(self, box_img): + box_gray_img = cv2.cvtColor(box_img, cv2.COLOR_BGR2GRAY) + h, w = box_gray_img.shape + # 灰度图片进行二值化处理 + ret, thresh1 = cv2.threshold(box_gray_img, 200, 255, cv2.THRESH_BINARY_INV) + # 纵向腐蚀 + if h < w: + kernel = np.ones((2, 1), np.uint8) + erode = cv2.erode(thresh1, kernel, iterations=1) + else: + erode = thresh1 + # 水平膨胀 + kernel = np.ones((1, 5), np.uint8) + erosion = cv2.dilate(erode, kernel, iterations=1) + # 水平投影 + projection_map = np.ones_like(erosion) + project_val_array = [0 for _ in range(0, h)] + + for j in range(0, h): + for i in range(0, w): + if erosion[j, i] == 255: + project_val_array[j] += 1 + # 根据数组,获取切割点 + start_idx = 0 # 记录进入字符区的索引 + end_idx = 0 # 记录进入空白区域的索引 + in_text = False # 是否遍历到了字符区内 + box_list = [] + spilt_threshold = 0 + for i in range(len(project_val_array)): + if ( + in_text == False and project_val_array[i] > spilt_threshold + ): # 进入字符区了 + in_text = True + start_idx = i + elif ( + project_val_array[i] <= spilt_threshold and in_text == True + ): # 进入空白区了 + end_idx = i + in_text = False + if end_idx - start_idx <= 2: + continue + box_list.append((start_idx, end_idx + 1)) + + if in_text: + box_list.append((start_idx, h - 1)) + # 绘制投影直方图 + for j in range(0, h): + for i in range(0, project_val_array[j]): + projection_map[j, i] = 0 + split_bbox_list = [] + if len(box_list) > 1: + for i, (h_start, h_end) in enumerate(box_list): + if i == 0: + h_start = 0 + if i == len(box_list): + h_end = h + word_img = erosion[h_start : h_end + 1, :] + word_h, word_w = word_img.shape + w_split_list, w_projection_map = self.projection( + word_img.T, word_w, word_h + ) + w_start, w_end = w_split_list[0][0], w_split_list[-1][1] + if h_start > 0: + h_start -= 1 + h_end += 1 + word_img = box_img[h_start : h_end + 1 :, w_start : w_end + 1, :] + split_bbox_list.append([w_start, h_start, w_end, h_end]) + else: + split_bbox_list.append([0, 0, w, h]) + return split_bbox_list + + def shrink_bbox(self, bbox): + left, top, right, bottom = bbox + sh_h = min(max(int((bottom - top) * 0.1), 1), self.shrink_h_max) + sh_w = min(max(int((right - left) * 0.1), 1), self.shrink_w_max) + left_new = left + sh_w + right_new = right - sh_w + top_new = top + sh_h + bottom_new = bottom - sh_h + if left_new >= right_new: + left_new = left + right_new = right + if top_new >= bottom_new: + top_new = top + bottom_new = bottom + return [left_new, top_new, right_new, bottom_new] + + def __call__(self, data): + img = data["image"] + cells = data["cells"] + height, width = img.shape[0:2] + if self.mask_type == 1: + mask_img = np.zeros((height, width), dtype=np.float32) + else: + mask_img = np.zeros((height, width, 3), dtype=np.float32) + cell_num = len(cells) + for cno in range(cell_num): + if "bbox" in cells[cno]: + bbox = cells[cno]["bbox"] + left, top, right, bottom = bbox + box_img = img[top:bottom, left:right, :].copy() + split_bbox_list = self.projection_cx(box_img) + for sno in range(len(split_bbox_list)): + split_bbox_list[sno][0] += left + split_bbox_list[sno][1] += top + split_bbox_list[sno][2] += left + split_bbox_list[sno][3] += top + + for sno in range(len(split_bbox_list)): + left, top, right, bottom = split_bbox_list[sno] + left, top, right, bottom = self.shrink_bbox( + [left, top, right, bottom] + ) + if self.mask_type == 1: + mask_img[top:bottom, left:right] = 1.0 + data["mask_img"] = mask_img + else: + mask_img[top:bottom, left:right, :] = (255, 255, 255) + data["image"] = mask_img + return data + + +class ResizeTableImage(object): + def __init__(self, max_len, resize_bboxes=False, infer_mode=False, **kwargs): + super(ResizeTableImage, self).__init__() + self.max_len = max_len + self.resize_bboxes = resize_bboxes + self.infer_mode = infer_mode + + def __call__(self, data): + img = data["image"] + height, width = img.shape[0:2] + ratio = self.max_len / (max(height, width) * 1.0) + resize_h = int(height * ratio) + resize_w = int(width * ratio) + resize_img = cv2.resize(img, (resize_w, resize_h)) + if self.resize_bboxes and not self.infer_mode: + data["bboxes"] = data["bboxes"] * ratio + data["image"] = resize_img + data["src_img"] = img + data["shape"] = np.array([height, width, ratio, ratio]) + data["max_len"] = self.max_len + return data + + +class PaddingTableImage(object): + def __init__(self, size, **kwargs): + super(PaddingTableImage, self).__init__() + self.size = size + + def __call__(self, data): + img = data["image"] + pad_h, pad_w = self.size + padding_img = np.zeros((pad_h, pad_w, 3), dtype=np.float32) + height, width = img.shape[0:2] + padding_img[0:height, 0:width, :] = img.copy() + data["image"] = padding_img + shape = data["shape"].tolist() + shape.extend([pad_h, pad_w]) + data["shape"] = np.array(shape) + return data diff --git a/modules/onnx_ocr_module/src/ppocr/data/imaug/text_image_aug/__init__.py b/modules/onnx_ocr_module/src/ppocr/data/imaug/text_image_aug/__init__.py new file mode 100644 index 0000000..16f179f --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/data/imaug/text_image_aug/__init__.py @@ -0,0 +1,17 @@ +# copyright (c) 2020 PaddlePaddle Authors. All Rights Reserve. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from .augment import tia_perspective, tia_distort, tia_stretch + +__all__ = ["tia_distort", "tia_stretch", "tia_perspective"] diff --git a/modules/onnx_ocr_module/src/ppocr/data/imaug/text_image_aug/__pycache__/__init__.cpython-311.pyc b/modules/onnx_ocr_module/src/ppocr/data/imaug/text_image_aug/__pycache__/__init__.cpython-311.pyc new file mode 100644 index 0000000..4fb0365 Binary files /dev/null and b/modules/onnx_ocr_module/src/ppocr/data/imaug/text_image_aug/__pycache__/__init__.cpython-311.pyc differ diff --git a/modules/onnx_ocr_module/src/ppocr/data/imaug/text_image_aug/__pycache__/augment.cpython-311.pyc b/modules/onnx_ocr_module/src/ppocr/data/imaug/text_image_aug/__pycache__/augment.cpython-311.pyc new file mode 100644 index 0000000..4684e67 Binary files /dev/null and b/modules/onnx_ocr_module/src/ppocr/data/imaug/text_image_aug/__pycache__/augment.cpython-311.pyc differ diff --git a/modules/onnx_ocr_module/src/ppocr/data/imaug/text_image_aug/__pycache__/warp_mls.cpython-311.pyc b/modules/onnx_ocr_module/src/ppocr/data/imaug/text_image_aug/__pycache__/warp_mls.cpython-311.pyc new file mode 100644 index 0000000..ec94c28 Binary files /dev/null and b/modules/onnx_ocr_module/src/ppocr/data/imaug/text_image_aug/__pycache__/warp_mls.cpython-311.pyc differ diff --git a/modules/onnx_ocr_module/src/ppocr/data/imaug/text_image_aug/augment.py b/modules/onnx_ocr_module/src/ppocr/data/imaug/text_image_aug/augment.py new file mode 100644 index 0000000..1044abd --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/data/imaug/text_image_aug/augment.py @@ -0,0 +1,123 @@ +# copyright (c) 2020 PaddlePaddle Authors. All Rights Reserve. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +""" +This code is refer from: +https://github.com/RubanSeven/Text-Image-Augmentation-python/blob/master/augment.py +""" + +import numpy as np +from .warp_mls import WarpMLS + + +def tia_distort(src, segment=4): + img_h, img_w = src.shape[:2] + + cut = img_w // segment + thresh = cut // 3 + + src_pts = list() + dst_pts = list() + + src_pts.append([0, 0]) + src_pts.append([img_w, 0]) + src_pts.append([img_w, img_h]) + src_pts.append([0, img_h]) + + dst_pts.append([np.random.randint(thresh), np.random.randint(thresh)]) + dst_pts.append([img_w - np.random.randint(thresh), np.random.randint(thresh)]) + dst_pts.append( + [img_w - np.random.randint(thresh), img_h - np.random.randint(thresh)] + ) + dst_pts.append([np.random.randint(thresh), img_h - np.random.randint(thresh)]) + + half_thresh = thresh * 0.5 + + for cut_idx in np.arange(1, segment, 1): + src_pts.append([cut * cut_idx, 0]) + src_pts.append([cut * cut_idx, img_h]) + dst_pts.append( + [ + cut * cut_idx + np.random.randint(thresh) - half_thresh, + np.random.randint(thresh) - half_thresh, + ] + ) + dst_pts.append( + [ + cut * cut_idx + np.random.randint(thresh) - half_thresh, + img_h + np.random.randint(thresh) - half_thresh, + ] + ) + + trans = WarpMLS(src, src_pts, dst_pts, img_w, img_h) + dst = trans.generate() + + return dst + + +def tia_stretch(src, segment=4): + img_h, img_w = src.shape[:2] + + cut = img_w // segment + thresh = cut * 4 // 5 + + src_pts = list() + dst_pts = list() + + src_pts.append([0, 0]) + src_pts.append([img_w, 0]) + src_pts.append([img_w, img_h]) + src_pts.append([0, img_h]) + + dst_pts.append([0, 0]) + dst_pts.append([img_w, 0]) + dst_pts.append([img_w, img_h]) + dst_pts.append([0, img_h]) + + half_thresh = thresh * 0.5 + + for cut_idx in np.arange(1, segment, 1): + move = np.random.randint(thresh) - half_thresh + src_pts.append([cut * cut_idx, 0]) + src_pts.append([cut * cut_idx, img_h]) + dst_pts.append([cut * cut_idx + move, 0]) + dst_pts.append([cut * cut_idx + move, img_h]) + + trans = WarpMLS(src, src_pts, dst_pts, img_w, img_h) + dst = trans.generate() + + return dst + + +def tia_perspective(src): + img_h, img_w = src.shape[:2] + + thresh = img_h // 2 + + src_pts = list() + dst_pts = list() + + src_pts.append([0, 0]) + src_pts.append([img_w, 0]) + src_pts.append([img_w, img_h]) + src_pts.append([0, img_h]) + + dst_pts.append([0, np.random.randint(thresh)]) + dst_pts.append([img_w, np.random.randint(thresh)]) + dst_pts.append([img_w, img_h - np.random.randint(thresh)]) + dst_pts.append([0, img_h - np.random.randint(thresh)]) + + trans = WarpMLS(src, src_pts, dst_pts, img_w, img_h) + dst = trans.generate() + + return dst diff --git a/modules/onnx_ocr_module/src/ppocr/data/imaug/text_image_aug/warp_mls.py b/modules/onnx_ocr_module/src/ppocr/data/imaug/text_image_aug/warp_mls.py new file mode 100644 index 0000000..2d349a6 --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/data/imaug/text_image_aug/warp_mls.py @@ -0,0 +1,187 @@ +# copyright (c) 2020 PaddlePaddle Authors. All Rights Reserve. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +""" +This code is refer from: +https://github.com/RubanSeven/Text-Image-Augmentation-python/blob/master/warp_mls.py +""" + +import numpy as np + + +class WarpMLS: + def __init__(self, src, src_pts, dst_pts, dst_w, dst_h, trans_ratio=1.0): + self.src = src + self.src_pts = src_pts + self.dst_pts = dst_pts + self.pt_count = len(self.dst_pts) + self.dst_w = dst_w + self.dst_h = dst_h + self.trans_ratio = trans_ratio + self.grid_size = 100 + self.rdx = np.zeros((self.dst_h, self.dst_w)) + self.rdy = np.zeros((self.dst_h, self.dst_w)) + + @staticmethod + def __bilinear_interp(x, y, v11, v12, v21, v22): + return (v11 * (1 - y) + v12 * y) * (1 - x) + (v21 * (1 - y) + v22 * y) * x + + def generate(self): + self.calc_delta() + return self.gen_img() + + def calc_delta(self): + w = np.zeros(self.pt_count, dtype=np.float32) + + if self.pt_count < 2: + return + + i = 0 + while 1: + if self.dst_w <= i < self.dst_w + self.grid_size - 1: + i = self.dst_w - 1 + elif i >= self.dst_w: + break + + j = 0 + while 1: + if self.dst_h <= j < self.dst_h + self.grid_size - 1: + j = self.dst_h - 1 + elif j >= self.dst_h: + break + + sw = 0 + swp = np.zeros(2, dtype=np.float32) + swq = np.zeros(2, dtype=np.float32) + new_pt = np.zeros(2, dtype=np.float32) + cur_pt = np.array([i, j], dtype=np.float32) + + k = 0 + for k in range(self.pt_count): + if i == self.dst_pts[k][0] and j == self.dst_pts[k][1]: + break + + w[k] = 1.0 / ( + (i - self.dst_pts[k][0]) * (i - self.dst_pts[k][0]) + + (j - self.dst_pts[k][1]) * (j - self.dst_pts[k][1]) + ) + + sw += w[k] + swp = swp + w[k] * np.array(self.dst_pts[k]) + swq = swq + w[k] * np.array(self.src_pts[k]) + + if k == self.pt_count - 1: + pstar = 1 / sw * swp + qstar = 1 / sw * swq + + miu_s = 0 + for k in range(self.pt_count): + if i == self.dst_pts[k][0] and j == self.dst_pts[k][1]: + continue + pt_i = self.dst_pts[k] - pstar + miu_s += w[k] * np.sum(pt_i * pt_i) + + cur_pt -= pstar + cur_pt_j = np.array([-cur_pt[1], cur_pt[0]]) + + for k in range(self.pt_count): + if i == self.dst_pts[k][0] and j == self.dst_pts[k][1]: + continue + + pt_i = self.dst_pts[k] - pstar + pt_j = np.array([-pt_i[1], pt_i[0]]) + + tmp_pt = np.zeros(2, dtype=np.float32) + tmp_pt[0] = ( + np.sum(pt_i * cur_pt) * self.src_pts[k][0] + - np.sum(pt_j * cur_pt) * self.src_pts[k][1] + ) + tmp_pt[1] = ( + -np.sum(pt_i * cur_pt_j) * self.src_pts[k][0] + + np.sum(pt_j * cur_pt_j) * self.src_pts[k][1] + ) + tmp_pt *= w[k] / miu_s + new_pt += tmp_pt + + new_pt += qstar + else: + new_pt = self.src_pts[k] + + self.rdx[j, i] = new_pt[0] - i + self.rdy[j, i] = new_pt[1] - j + + j += self.grid_size + i += self.grid_size + + def gen_img(self): + src_h, src_w = self.src.shape[:2] + dst = np.zeros_like(self.src, dtype=np.float32) + + for i in np.arange(0, self.dst_h, self.grid_size): + for j in np.arange(0, self.dst_w, self.grid_size): + ni = i + self.grid_size + nj = j + self.grid_size + w = h = self.grid_size + if ni >= self.dst_h: + ni = self.dst_h - 1 + h = ni - i + 1 + if nj >= self.dst_w: + nj = self.dst_w - 1 + w = nj - j + 1 + + di = np.reshape(np.arange(h), (-1, 1)) + dj = np.reshape(np.arange(w), (1, -1)) + delta_x = self.__bilinear_interp( + di / h, + dj / w, + self.rdx[i, j], + self.rdx[i, nj], + self.rdx[ni, j], + self.rdx[ni, nj], + ) + delta_y = self.__bilinear_interp( + di / h, + dj / w, + self.rdy[i, j], + self.rdy[i, nj], + self.rdy[ni, j], + self.rdy[ni, nj], + ) + nx = j + dj + delta_x * self.trans_ratio + ny = i + di + delta_y * self.trans_ratio + nx = np.clip(nx, 0, src_w - 1) + ny = np.clip(ny, 0, src_h - 1) + nxi = np.array(np.floor(nx), dtype=np.int32) + nyi = np.array(np.floor(ny), dtype=np.int32) + nxi1 = np.array(np.ceil(nx), dtype=np.int32) + nyi1 = np.array(np.ceil(ny), dtype=np.int32) + + if len(self.src.shape) == 3: + x = np.tile(np.expand_dims(ny - nyi, axis=-1), (1, 1, 3)) + y = np.tile(np.expand_dims(nx - nxi, axis=-1), (1, 1, 3)) + else: + x = ny - nyi + y = nx - nxi + dst[i : i + h, j : j + w] = self.__bilinear_interp( + x, + y, + self.src[nyi, nxi], + self.src[nyi, nxi1], + self.src[nyi1, nxi], + self.src[nyi1, nxi1], + ) + + dst = np.clip(dst, 0, 255) + dst = np.array(dst, dtype=np.uint8) + + return dst diff --git a/modules/onnx_ocr_module/src/ppocr/data/imaug/unimernet_aug.py b/modules/onnx_ocr_module/src/ppocr/data/imaug/unimernet_aug.py new file mode 100644 index 0000000..5645e9c --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/data/imaug/unimernet_aug.py @@ -0,0 +1,765 @@ +# copyright (c) 2024 PaddlePaddle Authors. All Rights Reserve. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function +from __future__ import unicode_literals + +import os + +os.environ["NO_ALBUMENTATIONS_UPDATE"] = "1" + +import cv2 +import math +import numpy as np +from io import BytesIO +import albumentations as A +from PIL import Image, ImageOps, ImageDraw +from scipy.ndimage import zoom as scizoom + + +class Erosion(A.ImageOnlyTransform): + def __init__(self, scale, always_apply=False, p=0.5): + super().__init__(always_apply=always_apply, p=p) + if type(scale) is tuple or type(scale) is list: + assert len(scale) == 2 + self.scale = scale + else: + self.scale = (scale, scale) + + def apply(self, img, **params): + kernel = cv2.getStructuringElement( + cv2.MORPH_ELLIPSE, tuple(np.random.randint(self.scale[0], self.scale[1], 2)) + ) + img = cv2.erode(img, kernel, iterations=1) + return img + + +class Dilation(A.ImageOnlyTransform): + def __init__(self, scale, always_apply=False, p=0.5): + super().__init__(always_apply=always_apply, p=p) + if type(scale) is tuple or type(scale) is list: + assert len(scale) == 2 + self.scale = scale + else: + self.scale = (scale, scale) + + def apply(self, img, **params): + kernel = cv2.getStructuringElement( + cv2.MORPH_ELLIPSE, tuple(np.random.randint(self.scale[0], self.scale[1], 2)) + ) + img = cv2.dilate(img, kernel, iterations=1) + return img + + +class Bitmap(A.ImageOnlyTransform): + + def __init__(self, value=0, lower=200, always_apply=False, p=0.5): + super().__init__(always_apply=always_apply, p=p) + self.lower = lower + self.value = value + + def apply(self, img, **params): + img = img.copy() + img[img < self.lower] = self.value + return img + + +def clipped_zoom(img, zoom_factor): + h = img.shape[1] + ch = int(np.ceil(h / float(zoom_factor))) + top = (h - ch) // 2 + img = scizoom( + img[top : top + ch, top : top + ch], (zoom_factor, zoom_factor, 1), order=1 + ) + trim_top = (img.shape[0] - h) // 2 + + return img[trim_top : trim_top + h, trim_top : trim_top + h] + + +def disk(radius, alias_blur=0.1, dtype=np.float32): + if radius <= 8: + coords = np.arange(-8, 8 + 1) + ksize = (3, 3) + else: + coords = np.arange(-radius, radius + 1) + ksize = (5, 5) + x, y = np.meshgrid(coords, coords) + aliased_disk = np.asarray((x**2 + y**2) <= radius**2, dtype=dtype) + aliased_disk /= np.sum(aliased_disk) + + return cv2.GaussianBlur(aliased_disk, ksize=ksize, sigmaX=alias_blur) + + +def plasma_fractal(mapsize=256, wibbledecay=3, rng=None): + """ + Generate a heightmap using diamond-square algorithm. + Return square 2d array, side length 'mapsize', of floats in range 0-255. + 'mapsize' must be a power of two. + """ + assert mapsize & (mapsize - 1) == 0 + maparray = np.empty((mapsize, mapsize), dtype=np.float_) + maparray[0, 0] = 0 + stepsize = mapsize + wibble = 100 + if rng is None: + rng = np.random.default_rng() + + def wibbledmean(array): + return array / 4 + wibble * rng.uniform(-wibble, wibble, array.shape) + + def fillsquares(): + """For each square of points stepsize apart, + calculate middle value as mean of points + wibble""" + cornerref = maparray[0:mapsize:stepsize, 0:mapsize:stepsize] + squareaccum = cornerref + np.roll(cornerref, shift=-1, axis=0) + squareaccum += np.roll(squareaccum, shift=-1, axis=1) + maparray[ + stepsize // 2 : mapsize : stepsize, stepsize // 2 : mapsize : stepsize + ] = wibbledmean(squareaccum) + + def filldiamonds(): + """For each diamond of points stepsize apart, + calculate middle value as mean of points + wibble""" + drgrid = maparray[ + stepsize // 2 : mapsize : stepsize, stepsize // 2 : mapsize : stepsize + ] + ulgrid = maparray[0:mapsize:stepsize, 0:mapsize:stepsize] + ldrsum = drgrid + np.roll(drgrid, 1, axis=0) + lulsum = ulgrid + np.roll(ulgrid, -1, axis=1) + ltsum = ldrsum + lulsum + maparray[0:mapsize:stepsize, stepsize // 2 : mapsize : stepsize] = wibbledmean( + ltsum + ) + tdrsum = drgrid + np.roll(drgrid, 1, axis=1) + tulsum = ulgrid + np.roll(ulgrid, -1, axis=0) + ttsum = tdrsum + tulsum + maparray[stepsize // 2 : mapsize : stepsize, 0:mapsize:stepsize] = wibbledmean( + ttsum + ) + + while stepsize >= 2: + fillsquares() + filldiamonds() + stepsize //= 2 + wibble /= wibbledecay + + maparray -= maparray.min() + return maparray / maparray.max() + + +class Fog(A.ImageOnlyTransform): + def __init__(self, mag=-1, always_apply=False, p=1.0): + super().__init__(always_apply=always_apply, p=p) + self.rng = np.random.default_rng() + self.mag = mag + + def apply(self, img, **params): + img = Image.fromarray(img.astype(np.uint8)) + w, h = img.size + c = [(1.5, 2), (2.0, 2), (2.5, 1.7)] + if self.mag < 0 or self.mag >= len(c): + index = self.rng.integers(0, len(c)) + else: + index = self.mag + c = c[index] + + n_channels = len(img.getbands()) + isgray = n_channels == 1 + + img = np.asarray(img) / 255.0 + max_val = img.max() + max_size = 2 ** math.ceil(math.log2(max(w, h)) + 1) + fog = ( + c[0] + * plasma_fractal(mapsize=max_size, wibbledecay=c[1], rng=self.rng)[:h, :w][ + ..., np.newaxis + ] + ) + if isgray: + fog = np.squeeze(fog) + else: + fog = np.repeat(fog, 3, axis=2) + + img += fog + img = np.clip(img * max_val / (max_val + c[0]), 0, 1) * 255 + return img.astype(np.uint8) + + +class Frost(A.ImageOnlyTransform): + def __init__(self, mag=-1, always_apply=False, p=1.0): + super().__init__(always_apply=always_apply, p=p) + self.rng = np.random.default_rng() + self.mag = mag + + def apply(self, img, **params): + img = Image.fromarray(img.astype(np.uint8)) + w, h = img.size + c = [(0.78, 0.22), (0.64, 0.36), (0.5, 0.5)] + if self.mag < 0 or self.mag >= len(c): + index = self.rng.integers(0, len(c)) + else: + index = self.mag + c = c[index] + + file_dir = os.path.dirname(__file__) + filename = [ + os.path.join(file_dir, "frost_img", "frost1.jpg"), + os.path.join(file_dir, "frost_img", "frost2.png"), + os.path.join(file_dir, "frost_img", "frost3.png"), + os.path.join(file_dir, "frost_img", "frost4.jpg"), + os.path.join(file_dir, "frost_img", "frost5.jpg"), + os.path.join(file_dir, "frost_img", "frost6.jpg"), + ] + index = self.rng.integers(0, len(filename)) + filename = filename[index] + frost = Image.open(filename).convert("RGB") + + f_w, f_h = frost.size + if w / h > f_w / f_h: + f_h = round(f_h * w / f_w) + f_w = w + else: + f_w = round(f_w * h / f_h) + f_h = h + frost = np.asarray(frost.resize((f_w, f_h))) + + # randomly crop + y_start, x_start = self.rng.integers(0, f_h - h + 1), self.rng.integers( + 0, f_w - w + 1 + ) + frost = frost[y_start : y_start + h, x_start : x_start + w] + + n_channels = len(img.getbands()) + isgray = n_channels == 1 + + img = np.asarray(img) + + if isgray: + img = np.expand_dims(img, axis=2) + img = np.repeat(img, 3, axis=2) + + img = np.clip(np.round(c[0] * img + c[1] * frost), 0, 255) + img = img.astype(np.uint8) + if isgray: + img = np.squeeze(img) + return img + + +class Snow(A.ImageOnlyTransform): + def __init__(self, mag=-1, always_apply=False, p=1.0): + super().__init__(always_apply=always_apply, p=p) + self.rng = np.random.default_rng() + self.mag = mag + + def apply(self, img, **params): + from wand.image import Image as WandImage + + img = Image.fromarray(img.astype(np.uint8)) + w, h = img.size + c = [ + (0.1, 0.3, 3, 0.5, 10, 4, 0.8), + (0.2, 0.3, 2, 0.5, 12, 4, 0.7), + (0.55, 0.3, 4, 0.9, 12, 8, 0.7), + ] + if self.mag < 0 or self.mag >= len(c): + index = self.rng.integers(0, len(c)) + else: + index = self.mag + c = c[index] + + n_channels = len(img.getbands()) + isgray = n_channels == 1 + + img = np.asarray(img, dtype=np.float32) / 255.0 + if isgray: + img = np.expand_dims(img, axis=2) + img = np.repeat(img, 3, axis=2) + + snow_layer = self.rng.normal(size=img.shape[:2], loc=c[0], scale=c[1]) + + snow_layer[snow_layer < c[3]] = 0 + + snow_layer = Image.fromarray( + (np.clip(snow_layer.squeeze(), 0, 1) * 255).astype(np.uint8), mode="L" + ) + output = BytesIO() + snow_layer.save(output, format="PNG") + snow_layer = WandImage(blob=output.getvalue()) + + snow_layer.motion_blur( + radius=c[4], sigma=c[5], angle=self.rng.uniform(-135, -45) + ) + + snow_layer = ( + cv2.imdecode( + np.frombuffer(snow_layer.make_blob(), np.uint8), cv2.IMREAD_UNCHANGED + ) + / 255.0 + ) + + snow_layer = snow_layer[..., np.newaxis] + + img = c[6] * img + gray_img = (1 - c[6]) * np.maximum( + img, cv2.cvtColor(img, cv2.COLOR_RGB2GRAY).reshape(h, w, 1) * 1.5 + 0.5 + ) + img += gray_img + img = np.clip(img + snow_layer + np.rot90(snow_layer, k=2), 0, 1) * 255 + img = img.astype(np.uint8) + if isgray: + img = np.squeeze(img) + return img + + +class Rain(A.ImageOnlyTransform): + def __init__(self, mag=-1, always_apply=False, p=1.0): + super().__init__(always_apply=always_apply, p=p) + self.rng = np.random.default_rng() + self.mag = mag + + def apply(self, img, **params): + img = Image.fromarray(img.astype(np.uint8)) + img = img.copy() + w, h = img.size + n_channels = len(img.getbands()) + isgray = n_channels == 1 + line_width = self.rng.integers(1, 2) + + c = [50, 70, 90] + if self.mag < 0 or self.mag >= len(c): + index = 0 + else: + index = self.mag + c = c[index] + + n_rains = self.rng.integers(c, c + 20) + slant = self.rng.integers(-60, 60) + fillcolor = 200 if isgray else (200, 200, 200) + + draw = ImageDraw.Draw(img) + max_length = min(w, h, 10) + for i in range(1, n_rains): + length = self.rng.integers(5, max_length) + x1 = self.rng.integers(0, w - length) + y1 = self.rng.integers(0, h - length) + x2 = x1 + length * math.sin(slant * math.pi / 180.0) + y2 = y1 + length * math.cos(slant * math.pi / 180.0) + x2 = int(x2) + y2 = int(y2) + draw.line([(x1, y1), (x2, y2)], width=line_width, fill=fillcolor) + img = np.asarray(img).astype(np.uint8) + return img + + +class Shadow(A.ImageOnlyTransform): + def __init__(self, mag=-1, always_apply=False, p=1.0): + super().__init__(always_apply=always_apply, p=p) + self.rng = np.random.default_rng() + self.mag = mag + + def apply(self, img, **params): + img = Image.fromarray(img.astype(np.uint8)) + w, h = img.size + n_channels = len(img.getbands()) + isgray = n_channels == 1 + + c = [64, 96, 128] + if self.mag < 0 or self.mag >= len(c): + index = 0 + else: + index = self.mag + c = c[index] + + img = img.convert("RGBA") + overlay = Image.new("RGBA", img.size, (255, 255, 255, 0)) + draw = ImageDraw.Draw(overlay) + transparency = self.rng.integers(c, c + 32) + x1 = self.rng.integers(0, w // 2) + y1 = 0 + + x2 = self.rng.integers(w // 2, w) + y2 = 0 + + x3 = self.rng.integers(w // 2, w) + y3 = h - 1 + + x4 = self.rng.integers(0, w // 2) + y4 = h - 1 + + draw.polygon( + [(x1, y1), (x2, y2), (x3, y3), (x4, y4)], fill=(0, 0, 0, transparency) + ) + + img = Image.alpha_composite(img, overlay) + img = img.convert("RGB") + if isgray: + img = ImageOps.grayscale(img) + img = np.asarray(img).astype(np.uint8) + return img + + +class UniMERNetTrainTransform: + def __init__(self, bitmap_prob=0.04, **kwargs): + self.bitmap_prob = bitmap_prob + self.train_transform = A.Compose( + [ + A.Compose( + [ + Bitmap(p=0.05), + A.OneOf([Fog(), Frost(), Snow(), Rain(), Shadow()], p=0.2), + A.OneOf([Erosion((2, 3)), Dilation((2, 3))], p=0.2), + A.ShiftScaleRotate( + shift_limit=0, + scale_limit=(-0.15, 0), + rotate_limit=1, + border_mode=0, + interpolation=3, + value=[255, 255, 255], + p=1, + ), + A.GridDistortion( + distort_limit=0.1, + border_mode=0, + interpolation=3, + value=[255, 255, 255], + p=0.5, + ), + ], + p=0.15, + ), + A.RGBShift(r_shift_limit=15, g_shift_limit=15, b_shift_limit=15, p=0.3), + A.GaussNoise(10, p=0.2), + A.RandomBrightnessContrast(0.05, (-0.2, 0), True, p=0.2), + A.ImageCompression(95, p=0.3), + A.ToGray(always_apply=True), + A.Normalize((0.7931, 0.7931, 0.7931), (0.1738, 0.1738, 0.1738)), + ] + ) + + def __call__(self, data): + img = data["image"] + if np.random.random() < self.bitmap_prob: + img[img != 255] = 0 + img = self.train_transform(image=img)["image"] + data["image"] = img + return data + + +class UniMERNetTestTransform: + def __init__(self, **kwargs): + self.test_transform = A.Compose( + [ + A.ToGray(always_apply=True), + A.Normalize((0.7931, 0.7931, 0.7931), (0.1738, 0.1738, 0.1738)), + ] + ) + + def __call__(self, data): + img = data["image"] + img = self.test_transform(image=img)["image"] + data["image"] = img + return data + + +class GoTImgDecode: + def __init__(self, input_size, random_padding=False, **kwargs): + self.input_size = input_size + self.random_padding = random_padding + + def crop_margin(self, img): + data = np.array(img.convert("L")) + data = data.astype(np.uint8) + max_val = data.max() + min_val = data.min() + if max_val == min_val: + return img + data = (data - min_val) / (max_val - min_val) * 255 + gray = 255 * (data < 200).astype(np.uint8) + coords = cv2.findNonZero(gray) # Find all non-zero points (text) + a, b, w, h = cv2.boundingRect(coords) # Find minimum spanning bounding box + return img.crop((a, b, w + a, h + b)) + + def get_dimensions(self, img): + if hasattr(img, "getbands"): + channels = len(img.getbands()) + else: + channels = img.channels + width, height = img.size + return [channels, height, width] + + def _compute_resized_output_size(self, image_size, size, max_size=None): + if len(size) == 1: # specified size only for the smallest edge + h, w = image_size + short, long = (w, h) if w <= h else (h, w) + requested_new_short = size if isinstance(size, int) else size[0] + + new_short, new_long = requested_new_short, int( + requested_new_short * long / short + ) + + if max_size is not None: + if max_size <= requested_new_short: + raise ValueError( + f"max_size = {max_size} must be strictly greater than the requested " + f"size for the smaller edge size = {size}" + ) + if new_long > max_size: + new_short, new_long = int(max_size * new_short / new_long), max_size + + new_w, new_h = (new_short, new_long) if w <= h else (new_long, new_short) + else: # specified both h and w + new_w, new_h = size[1], size[0] + return [new_h, new_w] + + def resize(self, img, size): + _, image_height, image_width = self.get_dimensions(img) + if isinstance(size, int): + size = [size] + max_size = None + output_size = self._compute_resized_output_size( + (image_height, image_width), size, max_size + ) + img = img.resize(tuple(output_size[::-1]), resample=2) + return img + + def __call__(self, data): + filename = data["filename"] + img = Image.open(filename) + try: + img = self.crop_margin(img.convert("RGB")) + except OSError: + return + if img.height == 0 or img.width == 0: + return + img = self.resize(img, min(self.input_size)) + img.thumbnail((self.input_size[1], self.input_size[0])) + delta_width = self.input_size[1] - img.width + delta_height = self.input_size[0] - img.height + if self.random_padding: + pad_width = np.random.randint(low=0, high=delta_width + 1) + pad_height = np.random.randint(low=0, high=delta_height + 1) + else: + pad_width = delta_width // 2 + pad_height = delta_height // 2 + padding = ( + pad_width, + pad_height, + delta_width - pad_width, + delta_height - pad_height, + ) + + data["image"] = np.array(ImageOps.expand(img, padding)) + return data + + +class UniMERNetImgDecode: + def __init__(self, input_size, random_padding=False, **kwargs): + self.input_size = input_size + self.random_padding = random_padding + + def crop_margin(self, img): + data = np.array(img.convert("L")) + data = data.astype(np.uint8) + max_val = data.max() + min_val = data.min() + if max_val == min_val: + return img + data = (data - min_val) / (max_val - min_val) * 255 + gray = 255 * (data < 200).astype(np.uint8) + coords = cv2.findNonZero(gray) # Find all non-zero points (text) + a, b, w, h = cv2.boundingRect(coords) # Find minimum spanning bounding box + return img.crop((a, b, w + a, h + b)) + + def get_dimensions(self, img): + if hasattr(img, "getbands"): + channels = len(img.getbands()) + else: + channels = img.channels + width, height = img.size + return [channels, height, width] + + def _compute_resized_output_size(self, image_size, size, max_size=None): + if len(size) == 1: # specified size only for the smallest edge + h, w = image_size + short, long = (w, h) if w <= h else (h, w) + requested_new_short = size if isinstance(size, int) else size[0] + + new_short, new_long = requested_new_short, int( + requested_new_short * long / short + ) + + if max_size is not None: + if max_size <= requested_new_short: + raise ValueError( + f"max_size = {max_size} must be strictly greater than the requested " + f"size for the smaller edge size = {size}" + ) + if new_long > max_size: + new_short, new_long = int(max_size * new_short / new_long), max_size + + new_w, new_h = (new_short, new_long) if w <= h else (new_long, new_short) + else: # specified both h and w + new_w, new_h = size[1], size[0] + return [new_h, new_w] + + def resize(self, img, size): + _, image_height, image_width = self.get_dimensions(img) + if isinstance(size, int): + size = [size] + max_size = None + output_size = self._compute_resized_output_size( + (image_height, image_width), size, max_size + ) + img = img.resize(tuple(output_size[::-1]), resample=2) + return img + + def __call__(self, data): + filename = data["filename"] + img = Image.open(filename) + try: + img = self.crop_margin(img.convert("RGB")) + except OSError: + return + if img.height == 0 or img.width == 0: + return + img = self.resize(img, min(self.input_size)) + img.thumbnail((self.input_size[1], self.input_size[0])) + delta_width = self.input_size[1] - img.width + delta_height = self.input_size[0] - img.height + if self.random_padding: + pad_width = np.random.randint(low=0, high=delta_width + 1) + pad_height = np.random.randint(low=0, high=delta_height + 1) + else: + pad_width = delta_width // 2 + pad_height = delta_height // 2 + padding = ( + pad_width, + pad_height, + delta_width - pad_width, + delta_height - pad_height, + ) + + data["image"] = np.array(ImageOps.expand(img, padding)) + return data + + +class UniMERNetResize: + def __init__(self, input_size, random_padding=False, **kwargs): + self.input_size = input_size + self.random_padding = random_padding + + def crop_margin(self, img): + data = np.array(img.convert("L")) + data = data.astype(np.uint8) + max_val = data.max() + min_val = data.min() + if max_val == min_val: + return img + data = (data - min_val) / (max_val - min_val) * 255 + gray = 255 * (data < 200).astype(np.uint8) + + coords = cv2.findNonZero(gray) # Find all non-zero points (text) + a, b, w, h = cv2.boundingRect(coords) # Find minimum spanning bounding box + return img.crop((a, b, w + a, h + b)) + + def get_dimensions(self, img): + if hasattr(img, "getbands"): + channels = len(img.getbands()) + else: + channels = img.channels + width, height = img.size + return [channels, height, width] + + def _compute_resized_output_size(self, image_size, size, max_size=None): + if len(size) == 1: # specified size only for the smallest edge + h, w = image_size + short, long = (w, h) if w <= h else (h, w) + requested_new_short = size if isinstance(size, int) else size[0] + + new_short, new_long = requested_new_short, int( + requested_new_short * long / short + ) + + if max_size is not None: + if max_size <= requested_new_short: + raise ValueError( + f"max_size = {max_size} must be strictly greater than the requested " + f"size for the smaller edge size = {size}" + ) + if new_long > max_size: + new_short, new_long = int(max_size * new_short / new_long), max_size + + new_w, new_h = (new_short, new_long) if w <= h else (new_long, new_short) + else: # specified both h and w + new_w, new_h = size[1], size[0] + return [new_h, new_w] + + def resize(self, img, size): + _, image_height, image_width = self.get_dimensions(img) + if isinstance(size, int): + size = [size] + max_size = None + output_size = self._compute_resized_output_size( + (image_height, image_width), size, max_size + ) + img.resize(tuple(output_size[::-1]), resample=2) + return img + + def __call__(self, data): + img = data["image"] + img = Image.fromarray(img) + try: + img = self.crop_margin(img) + except OSError: + return + if img.height == 0 or img.width == 0: + return + img = self.resize(img, min(self.input_size)) + img.thumbnail((self.input_size[1], self.input_size[0])) + delta_width = self.input_size[1] - img.width + delta_height = self.input_size[0] - img.height + if self.random_padding: + pad_width = np.random.randint(low=0, high=delta_width + 1) + pad_height = np.random.randint(low=0, high=delta_height + 1) + else: + pad_width = delta_width // 2 + pad_height = delta_height // 2 + padding = ( + pad_width, + pad_height, + delta_width - pad_width, + delta_height - pad_height, + ) + data["image"] = np.array(ImageOps.expand(img, padding)) + return data + + +class UniMERNetImageFormat: + def __init__(self, **kwargs): + pass + + def __call__(self, data): + img = data["image"] + im_h, im_w = img.shape[:2] + divide_h = math.ceil(im_h / 32) * 32 + divide_w = math.ceil(im_w / 32) * 32 + img = img[:, :, 0] + img = np.pad( + img, ((0, divide_h - im_h), (0, divide_w - im_w)), constant_values=(1, 1) + ) + img_expanded = img[:, :, np.newaxis].transpose(2, 0, 1) + data["image"] = img_expanded + return data diff --git a/modules/onnx_ocr_module/src/ppocr/data/imaug/vqa/__init__.py b/modules/onnx_ocr_module/src/ppocr/data/imaug/vqa/__init__.py new file mode 100644 index 0000000..812f20b --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/data/imaug/vqa/__init__.py @@ -0,0 +1,29 @@ +# copyright (c) 2021 PaddlePaddle Authors. All Rights Reserve. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from .token import ( + VQATokenPad, + VQASerTokenChunk, + VQAReTokenChunk, + VQAReTokenRelation, + TensorizeEntitiesRelations, +) + +__all__ = [ + "VQATokenPad", + "VQASerTokenChunk", + "VQAReTokenChunk", + "VQAReTokenRelation", + "TensorizeEntitiesRelations", +] diff --git a/modules/onnx_ocr_module/src/ppocr/data/imaug/vqa/__pycache__/__init__.cpython-311.pyc b/modules/onnx_ocr_module/src/ppocr/data/imaug/vqa/__pycache__/__init__.cpython-311.pyc new file mode 100644 index 0000000..f0a1032 Binary files /dev/null and b/modules/onnx_ocr_module/src/ppocr/data/imaug/vqa/__pycache__/__init__.cpython-311.pyc differ diff --git a/modules/onnx_ocr_module/src/ppocr/data/imaug/vqa/__pycache__/augment.cpython-311.pyc b/modules/onnx_ocr_module/src/ppocr/data/imaug/vqa/__pycache__/augment.cpython-311.pyc new file mode 100644 index 0000000..88421af Binary files /dev/null and b/modules/onnx_ocr_module/src/ppocr/data/imaug/vqa/__pycache__/augment.cpython-311.pyc differ diff --git a/modules/onnx_ocr_module/src/ppocr/data/imaug/vqa/augment.py b/modules/onnx_ocr_module/src/ppocr/data/imaug/vqa/augment.py new file mode 100644 index 0000000..d4f4cf2 --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/data/imaug/vqa/augment.py @@ -0,0 +1,34 @@ +# copyright (c) 2022 PaddlePaddle Authors. All Rights Reserve. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import os +import sys +import numpy as np +import random +from copy import deepcopy + + +def order_by_tbyx(ocr_info): + res = sorted(ocr_info, key=lambda r: (r["bbox"][1], r["bbox"][0])) + for i in range(len(res) - 1): + for j in range(i, 0, -1): + if abs(res[j + 1]["bbox"][1] - res[j]["bbox"][1]) < 20 and ( + res[j + 1]["bbox"][0] < res[j]["bbox"][0] + ): + tmp = deepcopy(res[j]) + res[j] = deepcopy(res[j + 1]) + res[j + 1] = deepcopy(tmp) + else: + break + return res diff --git a/modules/onnx_ocr_module/src/ppocr/data/imaug/vqa/token/__init__.py b/modules/onnx_ocr_module/src/ppocr/data/imaug/vqa/token/__init__.py new file mode 100644 index 0000000..e349dd7 --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/data/imaug/vqa/token/__init__.py @@ -0,0 +1,18 @@ +# copyright (c) 2021 PaddlePaddle Authors. All Rights Reserve. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from .vqa_token_chunk import VQASerTokenChunk, VQAReTokenChunk +from .vqa_token_pad import VQATokenPad +from .vqa_token_relation import VQAReTokenRelation +from .vqa_re_convert import TensorizeEntitiesRelations diff --git a/modules/onnx_ocr_module/src/ppocr/data/imaug/vqa/token/__pycache__/__init__.cpython-311.pyc b/modules/onnx_ocr_module/src/ppocr/data/imaug/vqa/token/__pycache__/__init__.cpython-311.pyc new file mode 100644 index 0000000..d04a96e Binary files /dev/null and b/modules/onnx_ocr_module/src/ppocr/data/imaug/vqa/token/__pycache__/__init__.cpython-311.pyc differ diff --git a/modules/onnx_ocr_module/src/ppocr/data/imaug/vqa/token/__pycache__/vqa_re_convert.cpython-311.pyc b/modules/onnx_ocr_module/src/ppocr/data/imaug/vqa/token/__pycache__/vqa_re_convert.cpython-311.pyc new file mode 100644 index 0000000..4e07521 Binary files /dev/null and b/modules/onnx_ocr_module/src/ppocr/data/imaug/vqa/token/__pycache__/vqa_re_convert.cpython-311.pyc differ diff --git a/modules/onnx_ocr_module/src/ppocr/data/imaug/vqa/token/__pycache__/vqa_token_chunk.cpython-311.pyc b/modules/onnx_ocr_module/src/ppocr/data/imaug/vqa/token/__pycache__/vqa_token_chunk.cpython-311.pyc new file mode 100644 index 0000000..f3e0f82 Binary files /dev/null and b/modules/onnx_ocr_module/src/ppocr/data/imaug/vqa/token/__pycache__/vqa_token_chunk.cpython-311.pyc differ diff --git a/modules/onnx_ocr_module/src/ppocr/data/imaug/vqa/token/__pycache__/vqa_token_pad.cpython-311.pyc b/modules/onnx_ocr_module/src/ppocr/data/imaug/vqa/token/__pycache__/vqa_token_pad.cpython-311.pyc new file mode 100644 index 0000000..bc35a1e Binary files /dev/null and b/modules/onnx_ocr_module/src/ppocr/data/imaug/vqa/token/__pycache__/vqa_token_pad.cpython-311.pyc differ diff --git a/modules/onnx_ocr_module/src/ppocr/data/imaug/vqa/token/__pycache__/vqa_token_relation.cpython-311.pyc b/modules/onnx_ocr_module/src/ppocr/data/imaug/vqa/token/__pycache__/vqa_token_relation.cpython-311.pyc new file mode 100644 index 0000000..128d07a Binary files /dev/null and b/modules/onnx_ocr_module/src/ppocr/data/imaug/vqa/token/__pycache__/vqa_token_relation.cpython-311.pyc differ diff --git a/modules/onnx_ocr_module/src/ppocr/data/imaug/vqa/token/vqa_re_convert.py b/modules/onnx_ocr_module/src/ppocr/data/imaug/vqa/token/vqa_re_convert.py new file mode 100644 index 0000000..fa14915 --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/data/imaug/vqa/token/vqa_re_convert.py @@ -0,0 +1,49 @@ +# copyright (c) 2022 PaddlePaddle Authors. All Rights Reserve. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import numpy as np + + +class TensorizeEntitiesRelations(object): + def __init__(self, max_seq_len=512, infer_mode=False, **kwargs): + self.max_seq_len = max_seq_len + self.infer_mode = infer_mode + + def __call__(self, data): + entities = data["entities"] + relations = data["relations"] + + entities_new = np.full( + shape=[self.max_seq_len + 1, 3], fill_value=-1, dtype="int64" + ) + entities_new[0, 0] = len(entities["start"]) + entities_new[0, 1] = len(entities["end"]) + entities_new[0, 2] = len(entities["label"]) + entities_new[1 : len(entities["start"]) + 1, 0] = np.array(entities["start"]) + entities_new[1 : len(entities["end"]) + 1, 1] = np.array(entities["end"]) + entities_new[1 : len(entities["label"]) + 1, 2] = np.array(entities["label"]) + + relations_new = np.full( + shape=[self.max_seq_len * self.max_seq_len + 1, 2], + fill_value=-1, + dtype="int64", + ) + relations_new[0, 0] = len(relations["head"]) + relations_new[0, 1] = len(relations["tail"]) + relations_new[1 : len(relations["head"]) + 1, 0] = np.array(relations["head"]) + relations_new[1 : len(relations["tail"]) + 1, 1] = np.array(relations["tail"]) + + data["entities"] = entities_new + data["relations"] = relations_new + return data diff --git a/modules/onnx_ocr_module/src/ppocr/data/imaug/vqa/token/vqa_token_chunk.py b/modules/onnx_ocr_module/src/ppocr/data/imaug/vqa/token/vqa_token_chunk.py new file mode 100644 index 0000000..d46a47d --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/data/imaug/vqa/token/vqa_token_chunk.py @@ -0,0 +1,134 @@ +# copyright (c) 2021 PaddlePaddle Authors. All Rights Reserve. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from collections import defaultdict + + +class VQASerTokenChunk(object): + def __init__(self, max_seq_len=512, infer_mode=False, **kwargs): + self.max_seq_len = max_seq_len + self.infer_mode = infer_mode + + def __call__(self, data): + encoded_inputs_all = [] + seq_len = len(data["input_ids"]) + for index in range(0, seq_len, self.max_seq_len): + chunk_beg = index + chunk_end = min(index + self.max_seq_len, seq_len) + encoded_inputs_example = {} + for key in data: + if key in [ + "label", + "input_ids", + "labels", + "token_type_ids", + "bbox", + "attention_mask", + ]: + if self.infer_mode and key == "labels": + encoded_inputs_example[key] = data[key] + else: + encoded_inputs_example[key] = data[key][chunk_beg:chunk_end] + else: + encoded_inputs_example[key] = data[key] + + encoded_inputs_all.append(encoded_inputs_example) + if len(encoded_inputs_all) == 0: + return None + return encoded_inputs_all[0] + + +class VQAReTokenChunk(object): + def __init__( + self, max_seq_len=512, entities_labels=None, infer_mode=False, **kwargs + ): + self.max_seq_len = max_seq_len + self.entities_labels = ( + {"HEADER": 0, "QUESTION": 1, "ANSWER": 2} + if entities_labels is None + else entities_labels + ) + self.infer_mode = infer_mode + + def __call__(self, data): + # prepare data + entities = data.pop("entities") + relations = data.pop("relations") + encoded_inputs_all = [] + for index in range(0, len(data["input_ids"]), self.max_seq_len): + item = {} + for key in data: + if key in [ + "label", + "input_ids", + "labels", + "token_type_ids", + "bbox", + "attention_mask", + ]: + if self.infer_mode and key == "labels": + item[key] = data[key] + else: + item[key] = data[key][index : index + self.max_seq_len] + else: + item[key] = data[key] + # select entity in current chunk + entities_in_this_span = [] + global_to_local_map = {} # + for entity_id, entity in enumerate(entities): + if ( + index <= entity["start"] < index + self.max_seq_len + and index <= entity["end"] < index + self.max_seq_len + ): + entity["start"] = entity["start"] - index + entity["end"] = entity["end"] - index + global_to_local_map[entity_id] = len(entities_in_this_span) + entities_in_this_span.append(entity) + + # select relations in current chunk + relations_in_this_span = [] + for relation in relations: + if ( + index <= relation["start_index"] < index + self.max_seq_len + and index <= relation["end_index"] < index + self.max_seq_len + ): + relations_in_this_span.append( + { + "head": global_to_local_map[relation["head"]], + "tail": global_to_local_map[relation["tail"]], + "start_index": relation["start_index"] - index, + "end_index": relation["end_index"] - index, + } + ) + item.update( + { + "entities": self.reformat(entities_in_this_span), + "relations": self.reformat(relations_in_this_span), + } + ) + if len(item["entities"]) > 0: + item["entities"]["label"] = [ + self.entities_labels[x] for x in item["entities"]["label"] + ] + encoded_inputs_all.append(item) + if len(encoded_inputs_all) == 0: + return None + return encoded_inputs_all[0] + + def reformat(self, data): + new_data = defaultdict(list) + for item in data: + for k, v in item.items(): + new_data[k].append(v) + return new_data diff --git a/modules/onnx_ocr_module/src/ppocr/data/imaug/vqa/token/vqa_token_pad.py b/modules/onnx_ocr_module/src/ppocr/data/imaug/vqa/token/vqa_token_pad.py new file mode 100644 index 0000000..9466e37 --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/data/imaug/vqa/token/vqa_token_pad.py @@ -0,0 +1,117 @@ +# copyright (c) 2021 PaddlePaddle Authors. All Rights Reserve. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +import paddle +import numpy as np + + +class VQATokenPad(object): + def __init__( + self, + max_seq_len=512, + pad_to_max_seq_len=True, + return_attention_mask=True, + return_token_type_ids=True, + truncation_strategy="longest_first", + return_overflowing_tokens=False, + return_special_tokens_mask=False, + infer_mode=False, + **kwargs, + ): + self.max_seq_len = max_seq_len + self.pad_to_max_seq_len = max_seq_len + self.return_attention_mask = return_attention_mask + self.return_token_type_ids = return_token_type_ids + self.truncation_strategy = truncation_strategy + self.return_overflowing_tokens = return_overflowing_tokens + self.return_special_tokens_mask = return_special_tokens_mask + self.pad_token_label_id = paddle.nn.CrossEntropyLoss().ignore_index + self.infer_mode = infer_mode + + def __call__(self, data): + needs_to_be_padded = ( + self.pad_to_max_seq_len and len(data["input_ids"]) < self.max_seq_len + ) + + if needs_to_be_padded: + if "tokenizer_params" in data: + tokenizer_params = data.pop("tokenizer_params") + else: + tokenizer_params = dict( + padding_side="right", pad_token_type_id=0, pad_token_id=1 + ) + + difference = self.max_seq_len - len(data["input_ids"]) + if tokenizer_params["padding_side"] == "right": + if self.return_attention_mask: + data["attention_mask"] = [1] * len(data["input_ids"]) + [ + 0 + ] * difference + if self.return_token_type_ids: + data["token_type_ids"] = ( + data["token_type_ids"] + + [tokenizer_params["pad_token_type_id"]] * difference + ) + if self.return_special_tokens_mask: + data["special_tokens_mask"] = ( + data["special_tokens_mask"] + [1] * difference + ) + data["input_ids"] = ( + data["input_ids"] + [tokenizer_params["pad_token_id"]] * difference + ) + if not self.infer_mode: + data["labels"] = ( + data["labels"] + [self.pad_token_label_id] * difference + ) + data["bbox"] = data["bbox"] + [[0, 0, 0, 0]] * difference + elif tokenizer_params["padding_side"] == "left": + if self.return_attention_mask: + data["attention_mask"] = [0] * difference + [1] * len( + data["input_ids"] + ) + if self.return_token_type_ids: + data["token_type_ids"] = [ + tokenizer_params["pad_token_type_id"] + ] * difference + data["token_type_ids"] + if self.return_special_tokens_mask: + data["special_tokens_mask"] = [1] * difference + data[ + "special_tokens_mask" + ] + data["input_ids"] = [ + tokenizer_params["pad_token_id"] + ] * difference + data["input_ids"] + if not self.infer_mode: + data["labels"] = [self.pad_token_label_id] * difference + data[ + "labels" + ] + data["bbox"] = [[0, 0, 0, 0]] * difference + data["bbox"] + else: + if self.return_attention_mask: + data["attention_mask"] = [1] * len(data["input_ids"]) + + for key in data: + if key in [ + "input_ids", + "labels", + "token_type_ids", + "bbox", + "attention_mask", + ]: + if self.infer_mode: + if key != "labels": + length = min(len(data[key]), self.max_seq_len) + data[key] = data[key][:length] + else: + continue + data[key] = np.array(data[key], dtype="int64") + return data diff --git a/modules/onnx_ocr_module/src/ppocr/data/imaug/vqa/token/vqa_token_relation.py b/modules/onnx_ocr_module/src/ppocr/data/imaug/vqa/token/vqa_token_relation.py new file mode 100644 index 0000000..1946c58 --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/data/imaug/vqa/token/vqa_token_relation.py @@ -0,0 +1,76 @@ +# copyright (c) 2021 PaddlePaddle Authors. All Rights Reserve. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +class VQAReTokenRelation(object): + def __init__(self, **kwargs): + pass + + def __call__(self, data): + """ + build relations + """ + entities = data["entities"] + relations = data["relations"] + id2label = data.pop("id2label") + empty_entity = data.pop("empty_entity") + entity_id_to_index_map = data.pop("entity_id_to_index_map") + + relations = list(set(relations)) + relations = [ + rel + for rel in relations + if rel[0] not in empty_entity and rel[1] not in empty_entity + ] + kv_relations = [] + for rel in relations: + pair = [id2label[rel[0]], id2label[rel[1]]] + if pair == ["question", "answer"]: + kv_relations.append( + { + "head": entity_id_to_index_map[rel[0]], + "tail": entity_id_to_index_map[rel[1]], + } + ) + elif pair == ["answer", "question"]: + kv_relations.append( + { + "head": entity_id_to_index_map[rel[1]], + "tail": entity_id_to_index_map[rel[0]], + } + ) + else: + continue + relations = sorted( + [ + { + "head": rel["head"], + "tail": rel["tail"], + "start_index": self.get_relation_span(rel, entities)[0], + "end_index": self.get_relation_span(rel, entities)[1], + } + for rel in kv_relations + ], + key=lambda x: x["head"], + ) + + data["relations"] = relations + return data + + def get_relation_span(self, rel, entities): + bound = [] + for entity_index in [rel["head"], rel["tail"]]: + bound.append(entities[entity_index]["start"]) + bound.append(entities[entity_index]["end"]) + return min(bound), max(bound) diff --git a/modules/onnx_ocr_module/src/ppocr/data/latexocr_dataset.py b/modules/onnx_ocr_module/src/ppocr/data/latexocr_dataset.py new file mode 100644 index 0000000..b15d6c1 --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/data/latexocr_dataset.py @@ -0,0 +1,174 @@ +# copyright (c) 2024 PaddlePaddle Authors. All Rights Reserve. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +""" +This code is refer from: +https://github.com/lukas-blecher/LaTeX-OCR/blob/main/pix2tex/dataset/dataset.py +""" + +import numpy as np +import cv2 +import math +import os +import json +import pickle +import random +import traceback +import paddle +from paddle.io import Dataset +from .imaug.label_ops import LatexOCRLabelEncode +from .imaug import transform, create_operators + + +class LaTeXOCRDataSet(Dataset): + def __init__(self, config, mode, logger, seed=None): + super(LaTeXOCRDataSet, self).__init__() + self.logger = logger + self.mode = mode.lower() + + global_config = config["Global"] + dataset_config = config[mode]["dataset"] + loader_config = config[mode]["loader"] + + pkl_path = dataset_config.pop("data") + self.data_dir = dataset_config["data_dir"] + self.min_dimensions = dataset_config.pop("min_dimensions") + self.max_dimensions = dataset_config.pop("max_dimensions") + self.batchsize = dataset_config.pop("batch_size_per_pair") + self.keep_smaller_batches = dataset_config.pop("keep_smaller_batches") + self.max_seq_len = global_config.pop("max_seq_len") + self.rec_char_dict_path = global_config.pop("rec_char_dict_path") + self.tokenizer = LatexOCRLabelEncode(self.rec_char_dict_path) + + file = open(pkl_path, "rb") + data = pickle.load(file) + temp = {} + for k in data: + if ( + self.min_dimensions[0] <= k[0] <= self.max_dimensions[0] + and self.min_dimensions[1] <= k[1] <= self.max_dimensions[1] + ): + temp[k] = data[k] + self.data = temp + self.do_shuffle = loader_config["shuffle"] + self.seed = seed + + if self.mode == "train" and self.do_shuffle: + random.seed(self.seed) + self.pairs = [] + for k in self.data: + info = np.array(self.data[k], dtype=object) + p = ( + paddle.randperm(len(info)) + if self.mode == "train" and self.do_shuffle + else paddle.arange(len(info)) + ) + for i in range(0, len(info), self.batchsize): + batch = info[p[i : i + self.batchsize]] + if len(batch.shape) == 1: + batch = batch[None, :] + if len(batch) < self.batchsize and not self.keep_smaller_batches: + continue + self.pairs.append(batch) + if self.do_shuffle: + self.pairs = np.random.permutation(np.array(self.pairs, dtype=object)) + else: + self.pairs = np.array(self.pairs, dtype=object) + + self.size = len(self.pairs) + self.set_epoch_as_seed(self.seed, dataset_config) + + self.ops = create_operators(dataset_config["transforms"], global_config) + self.ext_op_transform_idx = dataset_config.get("ext_op_transform_idx", 2) + self.need_reset = True + + def set_epoch_as_seed(self, seed, dataset_config): + if self.mode == "train": + try: + border_map_id = [ + index + for index, dictionary in enumerate(dataset_config["transforms"]) + if "MakeBorderMap" in dictionary + ][0] + shrink_map_id = [ + index + for index, dictionary in enumerate(dataset_config["transforms"]) + if "MakeShrinkMap" in dictionary + ][0] + dataset_config["transforms"][border_map_id]["MakeBorderMap"][ + "epoch" + ] = (seed if seed is not None else 0) + dataset_config["transforms"][shrink_map_id]["MakeShrinkMap"][ + "epoch" + ] = (seed if seed is not None else 0) + except Exception as E: + print(E) + return + + def shuffle_data_random(self): + random.seed(self.seed) + random.shuffle(self.data_lines) + return + + def __getitem__(self, idx): + batch = self.pairs[idx] + eqs, ims = batch.T + try: + max_width, max_height, max_length = 0, 0, 0 + + images_transform = [] + + for file_name in ims: + img_path = os.path.join(self.data_dir, file_name) + data = { + "img_path": img_path, + } + with open(data["img_path"], "rb") as f: + img = f.read() + data["image"] = img + item = transform(data, self.ops) + images_transform.append(np.array(item[0])) + image_concat = np.concatenate(images_transform, axis=0)[:, np.newaxis, :, :] + images_transform = image_concat.astype(np.float32) + labels, attention_mask, max_length = self.tokenizer(list(eqs)) + if self.max_seq_len < max_length: + rnd_idx = ( + np.random.randint(self.__len__()) + if self.mode == "train" + else (idx + 1) % self.__len__() + ) + return self.__getitem__(rnd_idx) + return (images_transform, labels, attention_mask) + + except: + + self.logger.error( + "When parsing line {}, error happened with msg: {}".format( + data["img_path"], traceback.format_exc() + ) + ) + outs = None + + if outs is None: + # during evaluation, we should fix the idx to get same results for many times of evaluation. + rnd_idx = ( + np.random.randint(self.__len__()) + if self.mode == "train" + else (idx + 1) % self.__len__() + ) + return self.__getitem__(rnd_idx) + return outs + + def __len__(self): + return self.size diff --git a/modules/onnx_ocr_module/src/ppocr/data/lmdb_dataset.py b/modules/onnx_ocr_module/src/ppocr/data/lmdb_dataset.py new file mode 100644 index 0000000..f3cdd97 --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/data/lmdb_dataset.py @@ -0,0 +1,301 @@ +# copyright (c) 2020 PaddlePaddle Authors. All Rights Reserve. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +import numpy as np +import io +import os +from paddle.io import Dataset +import lmdb +import cv2 +import string +import pickle +from PIL import Image + +from .imaug import transform, create_operators + + +class LMDBDataSet(Dataset): + def __init__(self, config, mode, logger, seed=None): + super(LMDBDataSet, self).__init__() + + global_config = config["Global"] + dataset_config = config[mode]["dataset"] + loader_config = config[mode]["loader"] + batch_size = loader_config["batch_size_per_card"] + data_dir = dataset_config["data_dir"] + self.do_shuffle = loader_config["shuffle"] + + self.lmdb_sets = self.load_hierarchical_lmdb_dataset(data_dir) + logger.info("Initialize indexes of datasets:%s" % data_dir) + self.data_idx_order_list = self.dataset_traversal() + if self.do_shuffle: + np.random.shuffle(self.data_idx_order_list) + self.ops = create_operators(dataset_config["transforms"], global_config) + self.ext_op_transform_idx = dataset_config.get("ext_op_transform_idx", 1) + + ratio_list = dataset_config.get("ratio_list", [1.0]) + self.need_reset = True in [x < 1 for x in ratio_list] + + def load_hierarchical_lmdb_dataset(self, data_dir): + lmdb_sets = {} + dataset_idx = 0 + for dirpath, dirnames, filenames in os.walk(data_dir + "/"): + if not dirnames: + env = lmdb.open( + dirpath, + max_readers=32, + readonly=True, + lock=False, + readahead=False, + meminit=False, + ) + txn = env.begin(write=False) + num_samples = int(txn.get("num-samples".encode())) + lmdb_sets[dataset_idx] = { + "dirpath": dirpath, + "env": env, + "txn": txn, + "num_samples": num_samples, + } + dataset_idx += 1 + return lmdb_sets + + def dataset_traversal(self): + lmdb_num = len(self.lmdb_sets) + total_sample_num = 0 + for lno in range(lmdb_num): + total_sample_num += self.lmdb_sets[lno]["num_samples"] + data_idx_order_list = np.zeros((total_sample_num, 2)) + beg_idx = 0 + for lno in range(lmdb_num): + tmp_sample_num = self.lmdb_sets[lno]["num_samples"] + end_idx = beg_idx + tmp_sample_num + data_idx_order_list[beg_idx:end_idx, 0] = lno + data_idx_order_list[beg_idx:end_idx, 1] = list(range(tmp_sample_num)) + data_idx_order_list[beg_idx:end_idx, 1] += 1 + beg_idx = beg_idx + tmp_sample_num + return data_idx_order_list + + def get_img_data(self, value): + """get_img_data""" + if not value: + return None + imgdata = np.frombuffer(value, dtype="uint8") + if imgdata is None: + return None + imgori = cv2.imdecode(imgdata, 1) + if imgori is None: + return None + return imgori + + def get_ext_data(self): + ext_data_num = 0 + for op in self.ops: + if hasattr(op, "ext_data_num"): + ext_data_num = getattr(op, "ext_data_num") + break + load_data_ops = self.ops[: self.ext_op_transform_idx] + ext_data = [] + + while len(ext_data) < ext_data_num: + lmdb_idx, file_idx = self.data_idx_order_list[np.random.randint(len(self))] + lmdb_idx = int(lmdb_idx) + file_idx = int(file_idx) + sample_info = self.get_lmdb_sample_info( + self.lmdb_sets[lmdb_idx]["txn"], file_idx + ) + if sample_info is None: + continue + img, label = sample_info + data = {"image": img, "label": label} + data = transform(data, load_data_ops) + if data is None: + continue + ext_data.append(data) + return ext_data + + def get_lmdb_sample_info(self, txn, index): + label_key = "label-%09d".encode() % index + label = txn.get(label_key) + if label is None: + return None + label = label.decode("utf-8") + img_key = "image-%09d".encode() % index + imgbuf = txn.get(img_key) + return imgbuf, label + + def __getitem__(self, idx): + lmdb_idx, file_idx = self.data_idx_order_list[idx] + lmdb_idx = int(lmdb_idx) + file_idx = int(file_idx) + sample_info = self.get_lmdb_sample_info( + self.lmdb_sets[lmdb_idx]["txn"], file_idx + ) + if sample_info is None: + return self.__getitem__(np.random.randint(self.__len__())) + img, label = sample_info + data = {"image": img, "label": label} + data["ext_data"] = self.get_ext_data() + outs = transform(data, self.ops) + if outs is None: + return self.__getitem__(np.random.randint(self.__len__())) + return outs + + def __len__(self): + return self.data_idx_order_list.shape[0] + + +class LMDBDataSetSR(LMDBDataSet): + def buf2PIL(self, txn, key, type="RGB"): + imgbuf = txn.get(key) + buf = io.BytesIO() + buf.write(imgbuf) + buf.seek(0) + im = Image.open(buf).convert(type) + return im + + def str_filt(self, str_, voc_type): + alpha_dict = { + "digit": string.digits, + "lower": string.digits + string.ascii_lowercase, + "upper": string.digits + string.ascii_letters, + "all": string.digits + string.ascii_letters + string.punctuation, + } + if voc_type == "lower": + str_ = str_.lower() + for char in str_: + if char not in alpha_dict[voc_type]: + str_ = str_.replace(char, "") + return str_ + + def get_lmdb_sample_info(self, txn, index): + self.voc_type = "upper" + self.max_len = 100 + self.test = False + label_key = b"label-%09d" % index + word = str(txn.get(label_key).decode()) + img_HR_key = b"image_hr-%09d" % index # 128*32 + img_lr_key = b"image_lr-%09d" % index # 64*16 + try: + img_HR = self.buf2PIL(txn, img_HR_key, "RGB") + img_lr = self.buf2PIL(txn, img_lr_key, "RGB") + except IOError or len(word) > self.max_len: + return self[index + 1] + label_str = self.str_filt(word, self.voc_type) + return img_HR, img_lr, label_str + + def __getitem__(self, idx): + lmdb_idx, file_idx = self.data_idx_order_list[idx] + lmdb_idx = int(lmdb_idx) + file_idx = int(file_idx) + sample_info = self.get_lmdb_sample_info( + self.lmdb_sets[lmdb_idx]["txn"], file_idx + ) + if sample_info is None: + return self.__getitem__(np.random.randint(self.__len__())) + img_HR, img_lr, label_str = sample_info + data = {"image_hr": img_HR, "image_lr": img_lr, "label": label_str} + outs = transform(data, self.ops) + if outs is None: + return self.__getitem__(np.random.randint(self.__len__())) + return outs + + +class LMDBDataSetTableMaster(LMDBDataSet): + def load_hierarchical_lmdb_dataset(self, data_dir): + lmdb_sets = {} + dataset_idx = 0 + env = lmdb.open( + data_dir, + max_readers=32, + readonly=True, + lock=False, + readahead=False, + meminit=False, + ) + txn = env.begin(write=False) + num_samples = int(pickle.loads(txn.get(b"__len__"))) + lmdb_sets[dataset_idx] = { + "dirpath": data_dir, + "env": env, + "txn": txn, + "num_samples": num_samples, + } + return lmdb_sets + + def get_img_data(self, value): + """get_img_data""" + if not value: + return None + imgdata = np.frombuffer(value, dtype="uint8") + if imgdata is None: + return None + imgori = cv2.imdecode(imgdata, 1) + if imgori is None: + return None + return imgori + + def get_lmdb_sample_info(self, txn, index): + def convert_bbox(bbox_str_list): + bbox_list = [] + for bbox_str in bbox_str_list: + bbox_list.append(int(bbox_str)) + return bbox_list + + try: + data = pickle.loads(txn.get(str(index).encode("utf8"))) + except: + return None + + # img_name, img, info_lines + file_name = data[0] + bytes = data[1] + info_lines = data[2] # raw data from TableMASTER annotation file. + # parse info_lines + raw_data = info_lines.strip().split("\n") + raw_name, text = ( + raw_data[0], + raw_data[1], + ) # don't filter the samples's length over max_seq_len. + text = text.split(",") + bbox_str_list = raw_data[2:] + bbox_split = "," + bboxes = [ + {"bbox": convert_bbox(bsl.strip().split(bbox_split)), "tokens": ["1", "2"]} + for bsl in bbox_str_list + ] + + # advance parse bbox + # import pdb;pdb.set_trace() + + line_info = {} + line_info["file_name"] = file_name + line_info["structure"] = text + line_info["cells"] = bboxes + line_info["image"] = bytes + return line_info + + def __getitem__(self, idx): + lmdb_idx, file_idx = self.data_idx_order_list[idx] + lmdb_idx = int(lmdb_idx) + file_idx = int(file_idx) + data = self.get_lmdb_sample_info(self.lmdb_sets[lmdb_idx]["txn"], file_idx) + if data is None: + return self.__getitem__(np.random.randint(self.__len__())) + outs = transform(data, self.ops) + if outs is None: + return self.__getitem__(np.random.randint(self.__len__())) + return outs + + def __len__(self): + return self.data_idx_order_list.shape[0] diff --git a/modules/onnx_ocr_module/src/ppocr/data/multi_scale_sampler.py b/modules/onnx_ocr_module/src/ppocr/data/multi_scale_sampler.py new file mode 100644 index 0000000..4ab38fc --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/data/multi_scale_sampler.py @@ -0,0 +1,171 @@ +from paddle.io import Sampler +import paddle.distributed as dist + +import numpy as np +import random +import math + + +class MultiScaleSampler(Sampler): + def __init__( + self, + data_source, + scales, + first_bs=128, + fix_bs=True, + divided_factor=[8, 16], + is_training=True, + ratio_wh=0.8, + max_w=480.0, + seed=None, + ): + """ + multi scale samper + Args: + data_source(dataset) + scales(list): several scales for image resolution + first_bs(int): batch size for the first scale in scales + divided_factor(list[w, h]): ImageNet models down-sample images by a factor, ensure that width and height dimensions are multiples are multiple of devided_factor. + is_training(boolean): mode + """ + # min. and max. spatial dimensions + self.data_source = data_source + self.data_idx_order_list = np.array(data_source.data_idx_order_list) + self.ds_width = data_source.ds_width + self.seed = data_source.seed + if self.ds_width: + self.wh_ratio = data_source.wh_ratio + self.wh_ratio_sort = data_source.wh_ratio_sort + self.n_data_samples = len(self.data_source) + self.ratio_wh = ratio_wh + self.max_w = max_w + + if isinstance(scales[0], list): + width_dims = [i[0] for i in scales] + height_dims = [i[1] for i in scales] + elif isinstance(scales[0], int): + width_dims = scales + height_dims = scales + base_im_w = width_dims[0] + base_im_h = height_dims[0] + base_batch_size = first_bs + + # Get the GPU and node related information + num_replicas = dist.get_world_size() + rank = dist.get_rank() + # adjust the total samples to avoid batch dropping + num_samples_per_replica = int(self.n_data_samples * 1.0 / num_replicas) + + img_indices = [idx for idx in range(self.n_data_samples)] + + self.shuffle = False + if is_training: + # compute the spatial dimensions and corresponding batch size + # ImageNet models down-sample images by a factor of 32. + # Ensure that width and height dimensions are multiples are multiple of 32. + width_dims = [ + int((w // divided_factor[0]) * divided_factor[0]) for w in width_dims + ] + height_dims = [ + int((h // divided_factor[1]) * divided_factor[1]) for h in height_dims + ] + + img_batch_pairs = list() + base_elements = base_im_w * base_im_h * base_batch_size + for h, w in zip(height_dims, width_dims): + if fix_bs: + batch_size = base_batch_size + else: + batch_size = int(max(1, (base_elements / (h * w)))) + img_batch_pairs.append((w, h, batch_size)) + self.img_batch_pairs = img_batch_pairs + self.shuffle = True + else: + self.img_batch_pairs = [(base_im_w, base_im_h, base_batch_size)] + + self.img_indices = img_indices + self.n_samples_per_replica = num_samples_per_replica + self.epoch = 0 + self.rank = rank + self.num_replicas = num_replicas + + self.batch_list = [] + self.current = 0 + last_index = num_samples_per_replica * num_replicas + indices_rank_i = self.img_indices[self.rank : last_index : self.num_replicas] + while self.current < self.n_samples_per_replica: + for curr_w, curr_h, curr_bsz in self.img_batch_pairs: + end_index = min(self.current + curr_bsz, self.n_samples_per_replica) + batch_ids = indices_rank_i[self.current : end_index] + n_batch_samples = len(batch_ids) + if n_batch_samples != curr_bsz: + batch_ids += indices_rank_i[: (curr_bsz - n_batch_samples)] + self.current += curr_bsz + + if len(batch_ids) > 0: + batch = [curr_w, curr_h, len(batch_ids)] + self.batch_list.append(batch) + random.shuffle(self.batch_list) + self.length = len(self.batch_list) + self.batchs_in_one_epoch = self.iter() + self.batchs_in_one_epoch_id = [i for i in range(len(self.batchs_in_one_epoch))] + + def __iter__(self): + if self.seed is None: + random.seed(self.epoch) + self.epoch += 1 + else: + random.seed(self.seed) + random.shuffle(self.batchs_in_one_epoch_id) + for batch_tuple_id in self.batchs_in_one_epoch_id: + yield self.batchs_in_one_epoch[batch_tuple_id] + + def iter(self): + if self.shuffle: + if self.seed is not None: + random.seed(self.seed) + else: + random.seed(self.epoch) + if not self.ds_width: + random.shuffle(self.img_indices) + random.shuffle(self.img_batch_pairs) + indices_rank_i = self.img_indices[ + self.rank : len(self.img_indices) : self.num_replicas + ] + else: + indices_rank_i = self.img_indices[ + self.rank : len(self.img_indices) : self.num_replicas + ] + + start_index = 0 + batchs_in_one_epoch = [] + for batch_tuple in self.batch_list: + curr_w, curr_h, curr_bsz = batch_tuple + end_index = min(start_index + curr_bsz, self.n_samples_per_replica) + batch_ids = indices_rank_i[start_index:end_index] + n_batch_samples = len(batch_ids) + if n_batch_samples != curr_bsz: + batch_ids += indices_rank_i[: (curr_bsz - n_batch_samples)] + start_index += curr_bsz + + if len(batch_ids) > 0: + if self.ds_width: + wh_ratio_current = self.wh_ratio[self.wh_ratio_sort[batch_ids]] + ratio_current = wh_ratio_current.mean() + ratio_current = ( + ratio_current + if ratio_current * curr_h < self.max_w + else self.max_w / curr_h + ) + else: + ratio_current = None + batch = [(curr_w, curr_h, b_id, ratio_current) for b_id in batch_ids] + # yield batch + batchs_in_one_epoch.append(batch) + return batchs_in_one_epoch + + def set_epoch(self, epoch: int): + self.epoch = epoch + + def __len__(self): + return self.length diff --git a/modules/onnx_ocr_module/src/ppocr/data/pgnet_dataset.py b/modules/onnx_ocr_module/src/ppocr/data/pgnet_dataset.py new file mode 100644 index 0000000..fd7a3d1 --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/data/pgnet_dataset.py @@ -0,0 +1,107 @@ +# copyright (c) 2021 PaddlePaddle Authors. All Rights Reserve. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +import numpy as np +import os +from paddle.io import Dataset +from .imaug import transform, create_operators +import random + + +class PGDataSet(Dataset): + def __init__(self, config, mode, logger, seed=None): + super(PGDataSet, self).__init__() + + self.logger = logger + self.seed = seed + self.mode = mode + global_config = config["Global"] + dataset_config = config[mode]["dataset"] + loader_config = config[mode]["loader"] + + self.delimiter = dataset_config.get("delimiter", "\t") + label_file_list = dataset_config.pop("label_file_list") + data_source_num = len(label_file_list) + ratio_list = dataset_config.get("ratio_list", [1.0]) + if isinstance(ratio_list, (float, int)): + ratio_list = [float(ratio_list)] * int(data_source_num) + assert ( + len(ratio_list) == data_source_num + ), "The length of ratio_list should be the same as the file_list." + self.data_dir = dataset_config["data_dir"] + self.do_shuffle = loader_config["shuffle"] + + logger.info("Initialize indexes of datasets:%s" % label_file_list) + self.data_lines = self.get_image_info_list(label_file_list, ratio_list) + self.data_idx_order_list = list(range(len(self.data_lines))) + if mode.lower() == "train": + self.shuffle_data_random() + + self.ops = create_operators(dataset_config["transforms"], global_config) + + self.need_reset = True in [x < 1 for x in ratio_list] + + def shuffle_data_random(self): + if self.do_shuffle: + random.seed(self.seed) + random.shuffle(self.data_lines) + return + + def get_image_info_list(self, file_list, ratio_list): + if isinstance(file_list, str): + file_list = [file_list] + data_lines = [] + for idx, file in enumerate(file_list): + with open(file, "rb") as f: + lines = f.readlines() + if self.mode == "train" or ratio_list[idx] < 1.0: + random.seed(self.seed) + lines = random.sample(lines, round(len(lines) * ratio_list[idx])) + data_lines.extend(lines) + return data_lines + + def __getitem__(self, idx): + file_idx = self.data_idx_order_list[idx] + data_line = self.data_lines[file_idx] + img_id = 0 + try: + data_line = data_line.decode("utf-8") + substr = data_line.strip("\n").split(self.delimiter) + file_name = substr[0] + label = substr[1] + img_path = os.path.join(self.data_dir, file_name) + if self.mode.lower() == "eval": + try: + img_id = int(data_line.split(".")[0][7:]) + except: + img_id = 0 + data = {"img_path": img_path, "label": label, "img_id": img_id} + if not os.path.exists(img_path): + raise Exception("{} does not exist!".format(img_path)) + with open(data["img_path"], "rb") as f: + img = f.read() + data["image"] = img + outs = transform(data, self.ops) + except Exception as e: + self.logger.error( + "When parsing line {}, error happened with msg: {}".format( + self.data_idx_order_list[idx], e + ) + ) + outs = None + if outs is None: + return self.__getitem__(np.random.randint(self.__len__())) + return outs + + def __len__(self): + return len(self.data_idx_order_list) diff --git a/modules/onnx_ocr_module/src/ppocr/data/pubtab_dataset.py b/modules/onnx_ocr_module/src/ppocr/data/pubtab_dataset.py new file mode 100644 index 0000000..f0f9a04 --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/data/pubtab_dataset.py @@ -0,0 +1,138 @@ +# copyright (c) 2021 PaddlePaddle Authors. All Rights Reserve. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +import numpy as np +import os +import random +from paddle.io import Dataset +import json +from copy import deepcopy + +from .imaug import transform, create_operators + + +class PubTabDataSet(Dataset): + def __init__(self, config, mode, logger, seed=None): + super(PubTabDataSet, self).__init__() + self.logger = logger + + global_config = config["Global"] + dataset_config = config[mode]["dataset"] + loader_config = config[mode]["loader"] + + label_file_list = dataset_config.pop("label_file_list") + data_source_num = len(label_file_list) + ratio_list = dataset_config.get("ratio_list", [1.0]) + if isinstance(ratio_list, (float, int)): + ratio_list = [float(ratio_list)] * int(data_source_num) + + assert ( + len(ratio_list) == data_source_num + ), "The length of ratio_list should be the same as the file_list." + + self.data_dir = dataset_config["data_dir"] + self.do_shuffle = loader_config["shuffle"] + + self.seed = seed + self.mode = mode.lower() + logger.info("Initialize indexes of datasets:%s" % label_file_list) + self.data_lines = self.get_image_info_list(label_file_list, ratio_list) + # self.check(config['Global']['max_text_length']) + + if mode.lower() == "train" and self.do_shuffle: + self.shuffle_data_random() + self.ops = create_operators(dataset_config["transforms"], global_config) + self.need_reset = True in [x < 1 for x in ratio_list] + + def get_image_info_list(self, file_list, ratio_list): + if isinstance(file_list, str): + file_list = [file_list] + data_lines = [] + for idx, file in enumerate(file_list): + with open(file, "rb") as f: + lines = f.readlines() + if self.mode == "train" or ratio_list[idx] < 1.0: + random.seed(self.seed) + lines = random.sample(lines, round(len(lines) * ratio_list[idx])) + data_lines.extend(lines) + return data_lines + + def check(self, max_text_length): + data_lines = [] + for line in self.data_lines: + data_line = line.decode("utf-8").strip("\n") + info = json.loads(data_line) + file_name = info["filename"] + cells = info["html"]["cells"].copy() + structure = info["html"]["structure"]["tokens"].copy() + + img_path = os.path.join(self.data_dir, file_name) + if not os.path.exists(img_path): + self.logger.warning("{} does not exist!".format(img_path)) + continue + if len(structure) == 0 or len(structure) > max_text_length: + continue + # data = {'img_path': img_path, 'cells': cells, 'structure':structure,'file_name':file_name} + data_lines.append(line) + self.data_lines = data_lines + + def shuffle_data_random(self): + if self.do_shuffle: + random.seed(self.seed) + random.shuffle(self.data_lines) + return + + def __getitem__(self, idx): + try: + data_line = self.data_lines[idx] + data_line = data_line.decode("utf-8").strip("\n") + info = json.loads(data_line) + file_name = info["filename"] + cells = info["html"]["cells"].copy() + structure = info["html"]["structure"]["tokens"].copy() + + img_path = os.path.join(self.data_dir, file_name) + if not os.path.exists(img_path): + raise Exception("{} does not exist!".format(img_path)) + data = { + "img_path": img_path, + "cells": cells, + "structure": structure, + "file_name": file_name, + } + + with open(data["img_path"], "rb") as f: + img = f.read() + data["image"] = img + outs = transform(data, self.ops) + except: + import traceback + + err = traceback.format_exc() + self.logger.error( + "When parsing line {}, error happened with msg: {}".format( + data_line, err + ) + ) + outs = None + if outs is None: + rnd_idx = ( + np.random.randint(self.__len__()) + if self.mode == "train" + else (idx + 1) % self.__len__() + ) + return self.__getitem__(rnd_idx) + return outs + + def __len__(self): + return len(self.data_lines) diff --git a/modules/onnx_ocr_module/src/ppocr/data/simple_dataset.py b/modules/onnx_ocr_module/src/ppocr/data/simple_dataset.py new file mode 100644 index 0000000..a8e1c03 --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/data/simple_dataset.py @@ -0,0 +1,253 @@ +# copyright (c) 2020 PaddlePaddle Authors. All Rights Reserve. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +import numpy as np +import cv2 +import math +import os +import json +import random +import traceback +from paddle.io import Dataset +from .imaug import transform, create_operators + + +class SimpleDataSet(Dataset): + def __init__(self, config, mode, logger, seed=None): + super(SimpleDataSet, self).__init__() + self.logger = logger + self.mode = mode.lower() + + global_config = config["Global"] + dataset_config = config[mode]["dataset"] + loader_config = config[mode]["loader"] + + self.delimiter = dataset_config.get("delimiter", "\t") + label_file_list = dataset_config.pop("label_file_list") + data_source_num = len(label_file_list) + ratio_list = dataset_config.get("ratio_list", 1.0) + if isinstance(ratio_list, (float, int)): + ratio_list = [float(ratio_list)] * int(data_source_num) + + assert ( + len(ratio_list) == data_source_num + ), "The length of ratio_list should be the same as the file_list." + self.data_dir = dataset_config["data_dir"] + self.do_shuffle = loader_config["shuffle"] + self.seed = seed + logger.info("Initialize indexes of datasets:%s" % label_file_list) + self.data_lines = self.get_image_info_list(label_file_list, ratio_list) + self.data_idx_order_list = list(range(len(self.data_lines))) + if self.mode == "train" and self.do_shuffle: + self.shuffle_data_random() + self.ops = create_operators(dataset_config["transforms"], global_config) + self.ext_op_transform_idx = dataset_config.get("ext_op_transform_idx", 2) + self.need_reset = True in [x < 1 for x in ratio_list] + + def get_image_info_list(self, file_list, ratio_list): + if isinstance(file_list, str): + file_list = [file_list] + data_lines = [] + for idx, file in enumerate(file_list): + with open(file, "rb") as f: + lines = f.readlines() + if self.mode == "train" or ratio_list[idx] < 1.0: + random.seed(self.seed) + lines = random.sample(lines, round(len(lines) * ratio_list[idx])) + data_lines.extend(lines) + return data_lines + + def shuffle_data_random(self): + random.seed(self.seed) + random.shuffle(self.data_lines) + return + + def _try_parse_filename_list(self, file_name): + # multiple images -> one gt label + if len(file_name) > 0 and file_name[0] == "[": + try: + info = json.loads(file_name) + file_name = random.choice(info) + except: + pass + return file_name + + def get_ext_data(self): + ext_data_num = 0 + for op in self.ops: + if hasattr(op, "ext_data_num"): + ext_data_num = getattr(op, "ext_data_num") + break + load_data_ops = self.ops[: self.ext_op_transform_idx] + ext_data = [] + + while len(ext_data) < ext_data_num: + file_idx = self.data_idx_order_list[np.random.randint(self.__len__())] + data_line = self.data_lines[file_idx] + data_line = data_line.decode("utf-8") + substr = data_line.strip("\n").split(self.delimiter) + file_name = substr[0] + file_name = self._try_parse_filename_list(file_name) + label = substr[1] + img_path = os.path.join(self.data_dir, file_name) + data = {"img_path": img_path, "label": label} + if not os.path.exists(img_path): + continue + with open(data["img_path"], "rb") as f: + img = f.read() + data["image"] = img + data = transform(data, load_data_ops) + + if data is None: + continue + if "polys" in data.keys(): + if data["polys"].shape[1] != 4: + continue + ext_data.append(data) + return ext_data + + def __getitem__(self, idx): + file_idx = self.data_idx_order_list[idx] + data_line = self.data_lines[file_idx] + try: + data_line = data_line.decode("utf-8") + substr = data_line.strip("\n").split(self.delimiter) + file_name = substr[0] + file_name = self._try_parse_filename_list(file_name) + label = substr[1] + img_path = os.path.join(self.data_dir, file_name) + data = {"img_path": img_path, "label": label} + if not os.path.exists(img_path): + raise Exception("{} does not exist!".format(img_path)) + with open(data["img_path"], "rb") as f: + img = f.read() + data["image"] = img + data["ext_data"] = self.get_ext_data() + data["filename"] = data["img_path"] + outs = transform(data, self.ops) + except: + self.logger.error( + "When parsing line {}, error happened with msg: {}".format( + data_line, traceback.format_exc() + ) + ) + outs = None + if outs is None: + # during evaluation, we should fix the idx to get same results for many times of evaluation. + rnd_idx = ( + np.random.randint(self.__len__()) + if self.mode == "train" + else (idx + 1) % self.__len__() + ) + return self.__getitem__(rnd_idx) + return outs + + def __len__(self): + return len(self.data_idx_order_list) + + +class MultiScaleDataSet(SimpleDataSet): + def __init__(self, config, mode, logger, seed=None): + super(MultiScaleDataSet, self).__init__(config, mode, logger, seed) + self.ds_width = config[mode]["dataset"].get("ds_width", False) + if self.ds_width: + self.wh_aware() + + def wh_aware(self): + data_line_new = [] + wh_ratio = [] + for lins in self.data_lines: + data_line_new.append(lins) + lins = lins.decode("utf-8") + name, label, w, h = lins.strip("\n").split(self.delimiter) + wh_ratio.append(float(w) / float(h)) + + self.data_lines = data_line_new + self.wh_ratio = np.array(wh_ratio) + self.wh_ratio_sort = np.argsort(self.wh_ratio) + self.data_idx_order_list = list(range(len(self.data_lines))) + + def resize_norm_img(self, data, imgW, imgH, padding=True): + img = data["image"] + h = img.shape[0] + w = img.shape[1] + if not padding: + resized_image = cv2.resize( + img, (imgW, imgH), interpolation=cv2.INTER_LINEAR + ) + resized_w = imgW + else: + ratio = w / float(h) + if math.ceil(imgH * ratio) > imgW: + resized_w = imgW + else: + resized_w = int(math.ceil(imgH * ratio)) + resized_image = cv2.resize(img, (resized_w, imgH)) + resized_image = resized_image.astype("float32") + + resized_image = resized_image.transpose((2, 0, 1)) / 255 + resized_image -= 0.5 + resized_image /= 0.5 + padding_im = np.zeros((3, imgH, imgW), dtype=np.float32) + padding_im[:, :, :resized_w] = resized_image + valid_ratio = min(1.0, float(resized_w / imgW)) + data["image"] = padding_im + data["valid_ratio"] = valid_ratio + return data + + def __getitem__(self, properties): + # properties is a tuple, contains (width, height, index) + img_height = properties[1] + idx = properties[2] + if self.ds_width and properties[3] is not None: + wh_ratio = properties[3] + img_width = img_height * ( + 1 if int(round(wh_ratio)) == 0 else int(round(wh_ratio)) + ) + file_idx = self.wh_ratio_sort[idx] + else: + file_idx = self.data_idx_order_list[idx] + img_width = properties[0] + wh_ratio = None + + data_line = self.data_lines[file_idx] + try: + data_line = data_line.decode("utf-8") + substr = data_line.strip("\n").split(self.delimiter) + file_name = substr[0] + file_name = self._try_parse_filename_list(file_name) + label = substr[1] + img_path = os.path.join(self.data_dir, file_name) + data = {"img_path": img_path, "label": label} + if not os.path.exists(img_path): + raise Exception("{} does not exist!".format(img_path)) + with open(data["img_path"], "rb") as f: + img = f.read() + data["image"] = img + data["ext_data"] = self.get_ext_data() + outs = transform(data, self.ops[:-1]) + if outs is not None: + outs = self.resize_norm_img(outs, img_width, img_height) + outs = transform(outs, self.ops[-1:]) + except: + self.logger.error( + "When parsing line {}, error happened with msg: {}".format( + data_line, traceback.format_exc() + ) + ) + outs = None + if outs is None: + # during evaluation, we should fix the idx to get same results for many times of evaluation. + rnd_idx = (idx + 1) % self.__len__() + return self.__getitem__([img_width, img_height, rnd_idx, wh_ratio]) + return outs diff --git a/modules/onnx_ocr_module/src/ppocr/ext_op/__init__.py b/modules/onnx_ocr_module/src/ppocr/ext_op/__init__.py new file mode 100644 index 0000000..b0be1ad --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/ext_op/__init__.py @@ -0,0 +1 @@ +from .roi_align_rotated.roi_align_rotated import RoIAlignRotated diff --git a/modules/onnx_ocr_module/src/ppocr/ext_op/roi_align_rotated/roi_align_rotated.cc b/modules/onnx_ocr_module/src/ppocr/ext_op/roi_align_rotated/roi_align_rotated.cc new file mode 100644 index 0000000..f6b6217 --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/ext_op/roi_align_rotated/roi_align_rotated.cc @@ -0,0 +1,528 @@ + +// This code is refer from: +// https://github.com/open-mmlab/mmcv/blob/master/mmcv/ops/csrc/pytorch/cpu/roi_align_rotated.cpp + +#include +#include +#include + +#include "paddle/extension.h" + +#define PADDLE_WITH_CUDA +#define CHECK_INPUT_SAME(x1, x2) \ + PD_CHECK(x1.place() == x2.place(), "input must be same place.") +#define CHECK_INPUT_CPU(x) PD_CHECK(x.is_cpu(), #x " must be a CPU Tensor.") + +template struct PreCalc { + int pos1; + int pos2; + int pos3; + int pos4; + T w1; + T w2; + T w3; + T w4; +}; + +template +void pre_calc_for_bilinear_interpolate( + const int height, const int width, const int pooled_height, + const int pooled_width, const int iy_upper, const int ix_upper, + T roi_start_h, T roi_start_w, T bin_size_h, T bin_size_w, + int roi_bin_grid_h, int roi_bin_grid_w, T roi_center_h, T roi_center_w, + T cos_theta, T sin_theta, std::vector> &pre_calc) { + int pre_calc_index = 0; + for (int ph = 0; ph < pooled_height; ph++) { + for (int pw = 0; pw < pooled_width; pw++) { + for (int iy = 0; iy < iy_upper; iy++) { + const T yy = roi_start_h + ph * bin_size_h + + static_cast(iy + .5f) * bin_size_h / + static_cast(roi_bin_grid_h); // e.g., 0.5, 1.5 + for (int ix = 0; ix < ix_upper; ix++) { + const T xx = roi_start_w + pw * bin_size_w + + static_cast(ix + .5f) * bin_size_w / + static_cast(roi_bin_grid_w); + + // Rotate by theta around the center and translate + // In image space, (y, x) is the order for Right Handed System, + // and this is essentially multiplying the point by a rotation matrix + // to rotate it counterclockwise through angle theta. + T y = yy * cos_theta - xx * sin_theta + roi_center_h; + T x = yy * sin_theta + xx * cos_theta + roi_center_w; + // deal with: inverse elements are out of feature map boundary + if (y < -1.0 || y > height || x < -1.0 || x > width) { + // empty + PreCalc pc; + pc.pos1 = 0; + pc.pos2 = 0; + pc.pos3 = 0; + pc.pos4 = 0; + pc.w1 = 0; + pc.w2 = 0; + pc.w3 = 0; + pc.w4 = 0; + pre_calc[pre_calc_index] = pc; + pre_calc_index += 1; + continue; + } + + if (y < 0) { + y = 0; + } + if (x < 0) { + x = 0; + } + + int y_low = (int)y; + int x_low = (int)x; + int y_high; + int x_high; + + if (y_low >= height - 1) { + y_high = y_low = height - 1; + y = (T)y_low; + } else { + y_high = y_low + 1; + } + + if (x_low >= width - 1) { + x_high = x_low = width - 1; + x = (T)x_low; + } else { + x_high = x_low + 1; + } + + T ly = y - y_low; + T lx = x - x_low; + T hy = 1. - ly, hx = 1. - lx; + T w1 = hy * hx, w2 = hy * lx, w3 = ly * hx, w4 = ly * lx; + + // save weights and indices + PreCalc pc; + pc.pos1 = y_low * width + x_low; + pc.pos2 = y_low * width + x_high; + pc.pos3 = y_high * width + x_low; + pc.pos4 = y_high * width + x_high; + pc.w1 = w1; + pc.w2 = w2; + pc.w3 = w3; + pc.w4 = w4; + pre_calc[pre_calc_index] = pc; + + pre_calc_index += 1; + } + } + } + } +} + +template +void roi_align_rotated_cpu_forward(const int nthreads, const T *input, + const T &spatial_scale, const bool aligned, + const bool clockwise, const int channels, + const int height, const int width, + const int pooled_height, + const int pooled_width, + const int sampling_ratio, const T *rois, + T *output) { + int n_rois = nthreads / channels / pooled_width / pooled_height; + // (n, c, ph, pw) is an element in the pooled output + // can be parallelized using omp + // #pragma omp parallel for num_threads(32) + for (int n = 0; n < n_rois; n++) { + int index_n = n * channels * pooled_width * pooled_height; + + const T *current_roi = rois + n * 6; + int roi_batch_ind = current_roi[0]; + + // Do not use rounding; this implementation detail is critical + T offset = aligned ? (T)0.5 : (T)0.0; + T roi_center_w = current_roi[1] * spatial_scale - offset; + T roi_center_h = current_roi[2] * spatial_scale - offset; + T roi_width = current_roi[3] * spatial_scale; + T roi_height = current_roi[4] * spatial_scale; + T theta = current_roi[5]; + if (clockwise) { + theta = -theta; // If clockwise, the angle needs to be reversed. + } + T cos_theta = cos(theta); + T sin_theta = sin(theta); + + if (aligned) { + assert(roi_width >= 0 && roi_height >= 0); + } else { // for backward-compatibility only + roi_width = std::max(roi_width, (T)1.); + roi_height = std::max(roi_height, (T)1.); + } + + T bin_size_h = static_cast(roi_height) / static_cast(pooled_height); + T bin_size_w = static_cast(roi_width) / static_cast(pooled_width); + + // We use roi_bin_grid to sample the grid and mimic integral + int roi_bin_grid_h = (sampling_ratio > 0) + ? sampling_ratio + : ceilf(roi_height / pooled_height); // e.g., = 2 + int roi_bin_grid_w = + (sampling_ratio > 0) ? sampling_ratio : ceilf(roi_width / pooled_width); + + // We do average (integral) pooling inside a bin + const T count = std::max(roi_bin_grid_h * roi_bin_grid_w, 1); // e.g. = 4 + + // we want to precalculate indices and weights shared by all channels, + // this is the key point of optimization + std::vector> pre_calc(roi_bin_grid_h * roi_bin_grid_w * + pooled_width * pooled_height); + + // roi_start_h and roi_start_w are computed wrt the center of RoI (x, y). + // Appropriate translation needs to be applied after. + T roi_start_h = -roi_height / 2.0; + T roi_start_w = -roi_width / 2.0; + + pre_calc_for_bilinear_interpolate( + height, width, pooled_height, pooled_width, roi_bin_grid_h, + roi_bin_grid_w, roi_start_h, roi_start_w, bin_size_h, bin_size_w, + roi_bin_grid_h, roi_bin_grid_w, roi_center_h, roi_center_w, cos_theta, + sin_theta, pre_calc); + + for (int c = 0; c < channels; c++) { + int index_n_c = index_n + c * pooled_width * pooled_height; + const T *offset_input = + input + (roi_batch_ind * channels + c) * height * width; + int pre_calc_index = 0; + + for (int ph = 0; ph < pooled_height; ph++) { + for (int pw = 0; pw < pooled_width; pw++) { + int index = index_n_c + ph * pooled_width + pw; + + T output_val = 0.; + for (int iy = 0; iy < roi_bin_grid_h; iy++) { + for (int ix = 0; ix < roi_bin_grid_w; ix++) { + PreCalc pc = pre_calc[pre_calc_index]; + output_val += pc.w1 * offset_input[pc.pos1] + + pc.w2 * offset_input[pc.pos2] + + pc.w3 * offset_input[pc.pos3] + + pc.w4 * offset_input[pc.pos4]; + + pre_calc_index += 1; + } + } + output_val /= count; + + output[index] = output_val; + } // for pw + } // for ph + } // for c + } // for n +} + +template +void bilinear_interpolate_gradient(const int height, const int width, T y, T x, + T &w1, T &w2, T &w3, T &w4, int &x_low, + int &x_high, int &y_low, int &y_high) { + // deal with cases that inverse elements are out of feature map boundary + if (y < -1.0 || y > height || x < -1.0 || x > width) { + // empty + w1 = w2 = w3 = w4 = 0.; + x_low = x_high = y_low = y_high = -1; + return; + } + + if (y < 0) { + y = 0; + } + + if (x < 0) { + x = 0; + } + + y_low = (int)y; + x_low = (int)x; + + if (y_low >= height - 1) { + y_high = y_low = height - 1; + y = (T)y_low; + } else { + y_high = y_low + 1; + } + + if (x_low >= width - 1) { + x_high = x_low = width - 1; + x = (T)x_low; + } else { + x_high = x_low + 1; + } + + T ly = y - y_low; + T lx = x - x_low; + T hy = 1. - ly, hx = 1. - lx; + + // reference in forward + // T v1 = input[y_low * width + x_low]; + // T v2 = input[y_low * width + x_high]; + // T v3 = input[y_high * width + x_low]; + // T v4 = input[y_high * width + x_high]; + // T val = (w1 * v1 + w2 * v2 + w3 * v3 + w4 * v4); + + w1 = hy * hx, w2 = hy * lx, w3 = ly * hx, w4 = ly * lx; + + return; +} + +template inline void add(T *address, const T &val) { + *address += val; +} + +template +void roi_align_rotated_cpu_backward( + const int nthreads, + // may not be contiguous. should index using n_stride, etc + const T *grad_output, const T &spatial_scale, const bool aligned, + const bool clockwise, const int channels, const int height, const int width, + const int pooled_height, const int pooled_width, const int sampling_ratio, + T *grad_input, const T *rois, const int n_stride, const int c_stride, + const int h_stride, const int w_stride) { + for (int index = 0; index < nthreads; index++) { + // (n, c, ph, pw) is an element in the pooled output + int pw = index % pooled_width; + int ph = (index / pooled_width) % pooled_height; + int c = (index / pooled_width / pooled_height) % channels; + int n = index / pooled_width / pooled_height / channels; + + const T *current_roi = rois + n * 6; + int roi_batch_ind = current_roi[0]; + + // Do not use rounding; this implementation detail is critical + T offset = aligned ? (T)0.5 : (T)0.0; + T roi_center_w = current_roi[1] * spatial_scale - offset; + T roi_center_h = current_roi[2] * spatial_scale - offset; + T roi_width = current_roi[3] * spatial_scale; + T roi_height = current_roi[4] * spatial_scale; + T theta = current_roi[5]; + if (clockwise) { + theta = -theta; // If clockwise, the angle needs to be reversed. + } + T cos_theta = cos(theta); + T sin_theta = sin(theta); + + if (aligned) { + assert(roi_width >= 0 && roi_height >= 0); + } else { // for backward-compatibility only + roi_width = std::max(roi_width, (T)1.); + roi_height = std::max(roi_height, (T)1.); + } + + T bin_size_h = static_cast(roi_height) / static_cast(pooled_height); + T bin_size_w = static_cast(roi_width) / static_cast(pooled_width); + + T *offset_grad_input = + grad_input + ((roi_batch_ind * channels + c) * height * width); + + int output_offset = n * n_stride + c * c_stride; + const T *offset_grad_output = grad_output + output_offset; + const T grad_output_this_bin = + offset_grad_output[ph * h_stride + pw * w_stride]; + + // We use roi_bin_grid to sample the grid and mimic integral + int roi_bin_grid_h = (sampling_ratio > 0) + ? sampling_ratio + : ceilf(roi_height / pooled_height); // e.g., = 2 + int roi_bin_grid_w = + (sampling_ratio > 0) ? sampling_ratio : ceilf(roi_width / pooled_width); + + // roi_start_h and roi_start_w are computed wrt the center of RoI (x, y). + // Appropriate translation needs to be applied after. + T roi_start_h = -roi_height / 2.0; + T roi_start_w = -roi_width / 2.0; + + // We do average (integral) pooling inside a bin + const T count = roi_bin_grid_h * roi_bin_grid_w; // e.g. = 4 + + for (int iy = 0; iy < roi_bin_grid_h; iy++) { + const T yy = roi_start_h + ph * bin_size_h + + static_cast(iy + .5f) * bin_size_h / + static_cast(roi_bin_grid_h); // e.g., 0.5, 1.5 + for (int ix = 0; ix < roi_bin_grid_w; ix++) { + const T xx = roi_start_w + pw * bin_size_w + + static_cast(ix + .5f) * bin_size_w / + static_cast(roi_bin_grid_w); + + // Rotate by theta around the center and translate + T y = yy * cos_theta - xx * sin_theta + roi_center_h; + T x = yy * sin_theta + xx * cos_theta + roi_center_w; + + T w1, w2, w3, w4; + int x_low, x_high, y_low, y_high; + + bilinear_interpolate_gradient(height, width, y, x, w1, w2, w3, w4, + x_low, x_high, y_low, y_high); + + T g1 = grad_output_this_bin * w1 / count; + T g2 = grad_output_this_bin * w2 / count; + T g3 = grad_output_this_bin * w3 / count; + T g4 = grad_output_this_bin * w4 / count; + + if (x_low >= 0 && x_high >= 0 && y_low >= 0 && y_high >= 0) { + // atomic add is not needed for now since it is single threaded + add(offset_grad_input + y_low * width + x_low, static_cast(g1)); + add(offset_grad_input + y_low * width + x_high, static_cast(g2)); + add(offset_grad_input + y_high * width + x_low, static_cast(g3)); + add(offset_grad_input + y_high * width + x_high, static_cast(g4)); + } // if + } // ix + } // iy + } // for +} // ROIAlignRotatedBackward + +std::vector +RoIAlignRotatedCPUForward(const paddle::Tensor &input, + const paddle::Tensor &rois, int aligned_height, + int aligned_width, float spatial_scale, + int sampling_ratio, bool aligned, bool clockwise) { + CHECK_INPUT_CPU(input); + CHECK_INPUT_CPU(rois); + + auto num_rois = rois.shape()[0]; + + auto channels = input.shape()[1]; + auto height = input.shape()[2]; + auto width = input.shape()[3]; + + auto output = + paddle::empty({num_rois, channels, aligned_height, aligned_width}, + input.type(), paddle::CPUPlace()); + auto output_size = output.numel(); + + PD_DISPATCH_FLOATING_TYPES( + input.type(), "roi_align_rotated_cpu_forward", ([&] { + roi_align_rotated_cpu_forward( + output_size, input.data(), + static_cast(spatial_scale), aligned, clockwise, channels, + height, width, aligned_height, aligned_width, sampling_ratio, + rois.data(), output.data()); + })); + + return {output}; +} + +std::vector RoIAlignRotatedCPUBackward( + const paddle::Tensor &input, const paddle::Tensor &rois, + const paddle::Tensor &grad_output, int aligned_height, int aligned_width, + float spatial_scale, int sampling_ratio, bool aligned, bool clockwise) { + + auto batch_size = input.shape()[0]; + auto channels = input.shape()[1]; + auto height = input.shape()[2]; + auto width = input.shape()[3]; + + auto grad_input = paddle::full({batch_size, channels, height, width}, 0.0, + input.type(), paddle::CPUPlace()); + + // get stride values to ensure indexing into gradients is correct. + int n_stride = grad_output.shape()[0]; + int c_stride = grad_output.shape()[1]; + int h_stride = grad_output.shape()[2]; + int w_stride = grad_output.shape()[3]; + + PD_DISPATCH_FLOATING_TYPES( + grad_output.type(), "roi_align_rotated_cpu_backward", [&] { + roi_align_rotated_cpu_backward( + grad_output.numel(), grad_output.data(), + static_cast(spatial_scale), aligned, clockwise, channels, + height, width, aligned_height, aligned_width, sampling_ratio, + grad_input.data(), rois.data(), n_stride, c_stride, + h_stride, w_stride); + }); + return {grad_input}; +} + +#ifdef PADDLE_WITH_CUDA +std::vector +RoIAlignRotatedCUDAForward(const paddle::Tensor &input, + const paddle::Tensor &rois, int aligned_height, + int aligned_width, float spatial_scale, + int sampling_ratio, bool aligned, bool clockwise); +#endif + +#ifdef PADDLE_WITH_CUDA +std::vector RoIAlignRotatedCUDABackward( + const paddle::Tensor &input, const paddle::Tensor &rois, + const paddle::Tensor &grad_output, int aligned_height, int aligned_width, + float spatial_scale, int sampling_ratio, bool aligned, bool clockwise); +#endif + +std::vector +RoIAlignRotatedForward(const paddle::Tensor &input, const paddle::Tensor &rois, + int aligned_height, int aligned_width, + float spatial_scale, int sampling_ratio, bool aligned, + bool clockwise) { + CHECK_INPUT_SAME(input, rois); + if (input.is_cpu()) { + return RoIAlignRotatedCPUForward(input, rois, aligned_height, aligned_width, + spatial_scale, sampling_ratio, aligned, + clockwise); +#ifdef PADDLE_WITH_CUDA + } else if (input.is_gpu()) { + return RoIAlignRotatedCUDAForward(input, rois, aligned_height, + aligned_width, spatial_scale, + sampling_ratio, aligned, clockwise); +#endif + } else { + PD_THROW("Unsupported device type for forward function of roi align " + "rotated operator."); + } +} + +std::vector +RoIAlignRotatedBackward(const paddle::Tensor &input, const paddle::Tensor &rois, + const paddle::Tensor &grad_output, int aligned_height, + int aligned_width, float spatial_scale, + int sampling_ratio, bool aligned, bool clockwise) { + CHECK_INPUT_SAME(input, rois); + if (input.is_cpu()) { + return RoIAlignRotatedCPUBackward(input, rois, grad_output, aligned_height, + aligned_width, spatial_scale, + sampling_ratio, aligned, clockwise); +#ifdef PADDLE_WITH_CUDA + } else if (input.is_gpu()) { + return RoIAlignRotatedCUDABackward(input, rois, grad_output, aligned_height, + aligned_width, spatial_scale, + sampling_ratio, aligned, clockwise); +#endif + } else { + PD_THROW("Unsupported device type for forward function of roi align " + "rotated operator."); + } +} + +std::vector> InferShape(std::vector input_shape, + std::vector rois_shape) { + return {{rois_shape[0], input_shape[1], input_shape[2], input_shape[3]}}; +} + +std::vector> +InferBackShape(std::vector input_shape, + std::vector rois_shape) { + return {input_shape}; +} + +std::vector InferDtype(paddle::DataType input_dtype, + paddle::DataType rois_dtype) { + return {input_dtype}; +} + +PD_BUILD_OP(roi_align_rotated) + .Inputs({"Input", "Rois"}) + .Outputs({"Output"}) + .Attrs({"aligned_height: int", "aligned_width: int", "spatial_scale: float", + "sampling_ratio: int", "aligned: bool", "clockwise: bool"}) + .SetKernelFn(PD_KERNEL(RoIAlignRotatedForward)) + .SetInferShapeFn(PD_INFER_SHAPE(InferShape)) + .SetInferDtypeFn(PD_INFER_DTYPE(InferDtype)); + +PD_BUILD_GRAD_OP(roi_align_rotated) + .Inputs({"Input", "Rois", paddle::Grad("Output")}) + .Attrs({"aligned_height: int", "aligned_width: int", "spatial_scale: float", + "sampling_ratio: int", "aligned: bool", "clockwise: bool"}) + .Outputs({paddle::Grad("Input")}) + .SetKernelFn(PD_KERNEL(RoIAlignRotatedBackward)) + .SetInferShapeFn(PD_INFER_SHAPE(InferBackShape)); diff --git a/modules/onnx_ocr_module/src/ppocr/ext_op/roi_align_rotated/roi_align_rotated.cu b/modules/onnx_ocr_module/src/ppocr/ext_op/roi_align_rotated/roi_align_rotated.cu new file mode 100644 index 0000000..d9baefb --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/ext_op/roi_align_rotated/roi_align_rotated.cu @@ -0,0 +1,381 @@ + +// This code is refer from: +// https://github.com/open-mmlab/mmcv/blob/master/mmcv/ops/csrc/common/cuda/roi_align_rotated_cuda_kernel.cuh + +#include +#include +#include + +#include "paddle/extension.h" +#include + +#define CUDA_1D_KERNEL_LOOP(i, n) \ + for (int i = blockIdx.x * blockDim.x + threadIdx.x; i < (n); \ + i += blockDim.x * gridDim.x) + +#define THREADS_PER_BLOCK 512 + +inline int GET_BLOCKS(const int N) { + int optimal_block_num = (N + THREADS_PER_BLOCK - 1) / THREADS_PER_BLOCK; + int max_block_num = 4096; + return min(optimal_block_num, max_block_num); +} + +#if defined(__CUDA_ARCH__) && __CUDA_ARCH__ < 600 + +static __inline__ __device__ double atomicAdd(double *address, double val) { + unsigned long long int *address_as_ull = (unsigned long long int *)address; + unsigned long long int old = *address_as_ull, assumed; + if (val == 0.0) + return __longlong_as_double(old); + do { + assumed = old; + old = atomicCAS(address_as_ull, assumed, + __double_as_longlong(val + __longlong_as_double(assumed))); + } while (assumed != old); + return __longlong_as_double(old); +} + +#endif + +template +__device__ T bilinear_interpolate(const T *input, const int height, + const int width, T y, T x, + const int index /* index for debug only*/) { + // deal with cases that inverse elements are out of feature map boundary + if (y < -1.0 || y > height || x < -1.0 || x > width) + return 0; + + if (y <= 0) + y = 0; + if (x <= 0) + x = 0; + + int y_low = (int)y; + int x_low = (int)x; + int y_high; + int x_high; + + if (y_low >= height - 1) { + y_high = y_low = height - 1; + y = (T)y_low; + } else { + y_high = y_low + 1; + } + + if (x_low >= width - 1) { + x_high = x_low = width - 1; + x = (T)x_low; + } else { + x_high = x_low + 1; + } + + T ly = y - y_low; + T lx = x - x_low; + T hy = 1. - ly, hx = 1. - lx; + // do bilinear interpolation + T v1 = input[y_low * width + x_low]; + T v2 = input[y_low * width + x_high]; + T v3 = input[y_high * width + x_low]; + T v4 = input[y_high * width + x_high]; + T w1 = hy * hx, w2 = hy * lx, w3 = ly * hx, w4 = ly * lx; + + T val = (w1 * v1 + w2 * v2 + w3 * v3 + w4 * v4); + + return val; +} + +template +__device__ void +bilinear_interpolate_gradient(const int height, const int width, T y, T x, + T &w1, T &w2, T &w3, T &w4, int &x_low, + int &x_high, int &y_low, int &y_high, + const int index /* index for debug only*/) { + // deal with cases that inverse elements are out of feature map boundary + if (y < -1.0 || y > height || x < -1.0 || x > width) { + // empty + w1 = w2 = w3 = w4 = 0.; + x_low = x_high = y_low = y_high = -1; + return; + } + + if (y <= 0) + y = 0; + if (x <= 0) + x = 0; + + y_low = (int)y; + x_low = (int)x; + + if (y_low >= height - 1) { + y_high = y_low = height - 1; + y = (T)y_low; + } else { + y_high = y_low + 1; + } + + if (x_low >= width - 1) { + x_high = x_low = width - 1; + x = (T)x_low; + } else { + x_high = x_low + 1; + } + + T ly = y - y_low; + T lx = x - x_low; + T hy = 1. - ly, hx = 1. - lx; + + // reference in forward + // T v1 = input[y_low * width + x_low]; + // T v2 = input[y_low * width + x_high]; + // T v3 = input[y_high * width + x_low]; + // T v4 = input[y_high * width + x_high]; + // T val = (w1 * v1 + w2 * v2 + w3 * v3 + w4 * v4); + + w1 = hy * hx, w2 = hy * lx, w3 = ly * hx, w4 = ly * lx; + + return; +} + +/*** Forward ***/ +template +__global__ void roi_align_rotated_cuda_forward_kernel( + const int nthreads, const scalar_t *bottom_data, + const scalar_t *bottom_rois, const scalar_t spatial_scale, + const int sample_num, const bool aligned, const bool clockwise, + const int channels, const int height, const int width, + const int pooled_height, const int pooled_width, scalar_t *top_data) { + CUDA_1D_KERNEL_LOOP(index, nthreads) { + // (n, c, ph, pw) is an element in the pooled output + int pw = index % pooled_width; + int ph = (index / pooled_width) % pooled_height; + int c = (index / pooled_width / pooled_height) % channels; + int n = index / pooled_width / pooled_height / channels; + + const scalar_t *offset_bottom_rois = bottom_rois + n * 6; + int roi_batch_ind = offset_bottom_rois[0]; + + // Do not using rounding; this implementation detail is critical + scalar_t offset = aligned ? (scalar_t)0.5 : (scalar_t)0.0; + scalar_t roi_center_w = offset_bottom_rois[1] * spatial_scale - offset; + scalar_t roi_center_h = offset_bottom_rois[2] * spatial_scale - offset; + scalar_t roi_width = offset_bottom_rois[3] * spatial_scale; + scalar_t roi_height = offset_bottom_rois[4] * spatial_scale; + // scalar_t theta = offset_bottom_rois[5] * M_PI / 180.0; + scalar_t theta = offset_bottom_rois[5]; + if (clockwise) { + theta = -theta; // If clockwise, the angle needs to be reversed. + } + if (!aligned) { // for backward-compatibility only + // Force malformed ROIs to be 1x1 + roi_width = max(roi_width, (scalar_t)1.); + roi_height = max(roi_height, (scalar_t)1.); + } + scalar_t bin_size_h = static_cast(roi_height) / + static_cast(pooled_height); + scalar_t bin_size_w = + static_cast(roi_width) / static_cast(pooled_width); + + const scalar_t *offset_bottom_data = + bottom_data + (roi_batch_ind * channels + c) * height * width; + + // We use roi_bin_grid to sample the grid and mimic integral + int roi_bin_grid_h = (sample_num > 0) + ? sample_num + : ceilf(roi_height / pooled_height); // e.g., = 2 + int roi_bin_grid_w = + (sample_num > 0) ? sample_num : ceilf(roi_width / pooled_width); + + // roi_start_h and roi_start_w are computed wrt the center of RoI (x, y). + // Appropriate translation needs to be applied after. + scalar_t roi_start_h = -roi_height / 2.0; + scalar_t roi_start_w = -roi_width / 2.0; + scalar_t cosscalar_theta = cos(theta); + scalar_t sinscalar_theta = sin(theta); + + // We do average (integral) pooling inside a bin + const scalar_t count = max(roi_bin_grid_h * roi_bin_grid_w, 1); // e.g. = 4 + + scalar_t output_val = 0.; + for (int iy = 0; iy < roi_bin_grid_h; iy++) { // e.g., iy = 0, 1 + const scalar_t yy = + roi_start_h + ph * bin_size_h + + static_cast(iy + .5f) * bin_size_h / + static_cast(roi_bin_grid_h); // e.g., 0.5, 1.5 + for (int ix = 0; ix < roi_bin_grid_w; ix++) { + const scalar_t xx = roi_start_w + pw * bin_size_w + + static_cast(ix + .5f) * bin_size_w / + static_cast(roi_bin_grid_w); + + // Rotate by theta (counterclockwise) around the center and translate + scalar_t y = yy * cosscalar_theta - xx * sinscalar_theta + roi_center_h; + scalar_t x = yy * sinscalar_theta + xx * cosscalar_theta + roi_center_w; + + scalar_t val = bilinear_interpolate( + offset_bottom_data, height, width, y, x, index); + output_val += val; + } + } + output_val /= count; + + top_data[index] = output_val; + } +} + +/*** Backward ***/ +template +__global__ void roi_align_rotated_backward_cuda_kernel( + const int nthreads, const scalar_t *top_diff, const scalar_t *bottom_rois, + const scalar_t spatial_scale, const int sample_num, const bool aligned, + const bool clockwise, const int channels, const int height, const int width, + const int pooled_height, const int pooled_width, scalar_t *bottom_diff) { + CUDA_1D_KERNEL_LOOP(index, nthreads) { + // (n, c, ph, pw) is an element in the pooled output + int pw = index % pooled_width; + int ph = (index / pooled_width) % pooled_height; + int c = (index / pooled_width / pooled_height) % channels; + int n = index / pooled_width / pooled_height / channels; + + const scalar_t *offset_bottom_rois = bottom_rois + n * 6; + int roi_batch_ind = offset_bottom_rois[0]; + + // Do not round + scalar_t offset = aligned ? (scalar_t)0.5 : (scalar_t)0.0; + scalar_t roi_center_w = offset_bottom_rois[1] * spatial_scale - offset; + scalar_t roi_center_h = offset_bottom_rois[2] * spatial_scale - offset; + scalar_t roi_width = offset_bottom_rois[3] * spatial_scale; + scalar_t roi_height = offset_bottom_rois[4] * spatial_scale; + // scalar_t theta = offset_bottom_rois[5] * M_PI / 180.0; + scalar_t theta = offset_bottom_rois[5]; + if (clockwise) { + theta = -theta; // If clockwise, the angle needs to be reversed. + } + if (!aligned) { // for backward-compatibility only + // Force malformed ROIs to be 1x1 + roi_width = max(roi_width, (scalar_t)1.); + roi_height = max(roi_height, (scalar_t)1.); + } + scalar_t bin_size_h = static_cast(roi_height) / + static_cast(pooled_height); + scalar_t bin_size_w = + static_cast(roi_width) / static_cast(pooled_width); + + scalar_t *offset_bottom_diff = + bottom_diff + (roi_batch_ind * channels + c) * height * width; + + int top_offset = (n * channels + c) * pooled_height * pooled_width; + const scalar_t *offset_top_diff = top_diff + top_offset; + const scalar_t top_diff_this_bin = offset_top_diff[ph * pooled_width + pw]; + + // We use roi_bin_grid to sample the grid and mimic integral + int roi_bin_grid_h = (sample_num > 0) + ? sample_num + : ceilf(roi_height / pooled_height); // e.g., = 2 + int roi_bin_grid_w = + (sample_num > 0) ? sample_num : ceilf(roi_width / pooled_width); + + // roi_start_h and roi_start_w are computed wrt the center of RoI (x, y). + // Appropriate translation needs to be applied after. + scalar_t roi_start_h = -roi_height / 2.0; + scalar_t roi_start_w = -roi_width / 2.0; + scalar_t cosTheta = cos(theta); + scalar_t sinTheta = sin(theta); + + // We do average (integral) pooling inside a bin + const scalar_t count = roi_bin_grid_h * roi_bin_grid_w; // e.g. = 4 + + for (int iy = 0; iy < roi_bin_grid_h; iy++) { // e.g., iy = 0, 1 + const scalar_t yy = + roi_start_h + ph * bin_size_h + + static_cast(iy + .5f) * bin_size_h / + static_cast(roi_bin_grid_h); // e.g., 0.5, 1.5 + for (int ix = 0; ix < roi_bin_grid_w; ix++) { + const scalar_t xx = roi_start_w + pw * bin_size_w + + static_cast(ix + .5f) * bin_size_w / + static_cast(roi_bin_grid_w); + + // Rotate by theta around the center and translate + scalar_t y = yy * cosTheta - xx * sinTheta + roi_center_h; + scalar_t x = yy * sinTheta + xx * cosTheta + roi_center_w; + + scalar_t w1, w2, w3, w4; + int x_low, x_high, y_low, y_high; + + bilinear_interpolate_gradient(height, width, y, x, w1, w2, w3, + w4, x_low, x_high, y_low, + y_high, index); + + scalar_t g1 = top_diff_this_bin * w1 / count; + scalar_t g2 = top_diff_this_bin * w2 / count; + scalar_t g3 = top_diff_this_bin * w3 / count; + scalar_t g4 = top_diff_this_bin * w4 / count; + + if (x_low >= 0 && x_high >= 0 && y_low >= 0 && y_high >= 0) { + atomicAdd(offset_bottom_diff + y_low * width + x_low, g1); + atomicAdd(offset_bottom_diff + y_low * width + x_high, g2); + atomicAdd(offset_bottom_diff + y_high * width + x_low, g3); + atomicAdd(offset_bottom_diff + y_high * width + x_high, g4); + } // if + } // ix + } // iy + } // CUDA_1D_KERNEL_LOOP +} // RoIAlignBackward + +std::vector +RoIAlignRotatedCUDAForward(const paddle::Tensor &input, + const paddle::Tensor &rois, int aligned_height, + int aligned_width, float spatial_scale, + int sampling_ratio, bool aligned, bool clockwise) { + + auto num_rois = rois.shape()[0]; + + auto channels = input.shape()[1]; + auto height = input.shape()[2]; + auto width = input.shape()[3]; + + auto output = + paddle::empty({num_rois, channels, aligned_height, aligned_width}, + input.type(), paddle::GPUPlace()); + auto output_size = output.numel(); + + PD_DISPATCH_FLOATING_TYPES( + input.type(), "roi_align_rotated_cuda_forward_kernel", ([&] { + roi_align_rotated_cuda_forward_kernel + <<>>( + output_size, input.data(), rois.data(), + static_cast(spatial_scale), sampling_ratio, aligned, + clockwise, channels, height, width, aligned_height, + aligned_width, output.data()); + })); + + return {output}; +} + +std::vector RoIAlignRotatedCUDABackward( + const paddle::Tensor &input, const paddle::Tensor &rois, + const paddle::Tensor &grad_output, int aligned_height, int aligned_width, + float spatial_scale, int sampling_ratio, bool aligned, bool clockwise) { + + auto num_rois = rois.shape()[0]; + + auto batch_size = input.shape()[0]; + auto channels = input.shape()[1]; + auto height = input.shape()[2]; + auto width = input.shape()[3]; + + auto grad_input = paddle::full({batch_size, channels, height, width}, 0.0, + input.type(), paddle::GPUPlace()); + + const int output_size = num_rois * aligned_height * aligned_width * channels; + + PD_DISPATCH_FLOATING_TYPES( + grad_output.type(), "roi_align_rotated_backward_cuda_kernel", ([&] { + roi_align_rotated_backward_cuda_kernel + <<>>( + output_size, grad_output.data(), rois.data(), + spatial_scale, sampling_ratio, aligned, clockwise, channels, + height, width, aligned_height, aligned_width, + grad_input.data()); + })); + return {grad_input}; +} diff --git a/modules/onnx_ocr_module/src/ppocr/ext_op/roi_align_rotated/roi_align_rotated.py b/modules/onnx_ocr_module/src/ppocr/ext_op/roi_align_rotated/roi_align_rotated.py new file mode 100644 index 0000000..4cc1bf6 --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/ext_op/roi_align_rotated/roi_align_rotated.py @@ -0,0 +1,69 @@ +# copyright (c) 2022 PaddlePaddle Authors. All Rights Reserve. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +""" +This code is refer from: +https://github.com/open-mmlab/mmcv/blob/master/mmcv/ops/roi_align_rotated.py +""" + +import paddle +import paddle.nn as nn +from paddle.utils.cpp_extension import load + +custom_ops = load( + name="custom_jit_ops", + sources=[ + "ppocr/ext_op/roi_align_rotated/roi_align_rotated.cc", + "ppocr/ext_op/roi_align_rotated/roi_align_rotated.cu", + ], +) + +roi_align_rotated = custom_ops.roi_align_rotated + + +class RoIAlignRotated(nn.Layer): + """RoI align pooling layer for rotated proposals.""" + + def __init__( + self, out_size, spatial_scale, sample_num=0, aligned=True, clockwise=False + ): + super(RoIAlignRotated, self).__init__() + + if isinstance(out_size, int): + self.out_h = out_size + self.out_w = out_size + elif isinstance(out_size, tuple): + assert len(out_size) == 2 + assert isinstance(out_size[0], int) + assert isinstance(out_size[1], int) + self.out_h, self.out_w = out_size + else: + raise TypeError('"out_size" must be an integer or tuple of integers') + + self.spatial_scale = float(spatial_scale) + self.sample_num = int(sample_num) + self.aligned = aligned + self.clockwise = clockwise + + def forward(self, feats, rois): + output = roi_align_rotated( + feats, + rois, + self.out_h, + self.out_w, + self.spatial_scale, + self.sample_num, + self.aligned, + self.clockwise, + ) + return output diff --git a/modules/onnx_ocr_module/src/ppocr/losses/__init__.py b/modules/onnx_ocr_module/src/ppocr/losses/__init__.py new file mode 100644 index 0000000..59b7ecf --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/losses/__init__.py @@ -0,0 +1,124 @@ +# Copyright (c) 2020 PaddlePaddle Authors. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import copy +import paddle +import paddle.nn as nn + +# basic_loss +from .basic_loss import LossFromOutput + +# det loss +from .det_db_loss import DBLoss +from .det_east_loss import EASTLoss +from .det_sast_loss import SASTLoss +from .det_pse_loss import PSELoss +from .det_fce_loss import FCELoss +from .det_ct_loss import CTLoss +from .det_drrg_loss import DRRGLoss + +# rec loss +from .rec_ctc_loss import CTCLoss +from .rec_att_loss import AttentionLoss +from .rec_srn_loss import SRNLoss +from .rec_ce_loss import CELoss +from .rec_sar_loss import SARLoss +from .rec_aster_loss import AsterLoss +from .rec_pren_loss import PRENLoss +from .rec_multi_loss import MultiLoss +from .rec_vl_loss import VLLoss +from .rec_spin_att_loss import SPINAttentionLoss +from .rec_rfl_loss import RFLLoss +from .rec_can_loss import CANLoss +from .rec_satrn_loss import SATRNLoss +from .rec_nrtr_loss import NRTRLoss +from .rec_parseq_loss import ParseQLoss +from .rec_cppd_loss import CPPDLoss +from .rec_latexocr_loss import LaTeXOCRLoss +from .rec_unimernet_loss import UniMERNetLoss +from .rec_ppformulanet_loss import PPFormulaNet_S_Loss, PPFormulaNet_L_Loss + +# cls loss +from .cls_loss import ClsLoss + +# e2e loss +from .e2e_pg_loss import PGLoss +from .kie_sdmgr_loss import SDMGRLoss + +# basic loss function +from .basic_loss import DistanceLoss + +# combined loss function +from .combined_loss import CombinedLoss + +# table loss +from .table_att_loss import TableAttentionLoss, SLALoss +from .table_master_loss import TableMasterLoss + +# vqa token loss +from .vqa_token_layoutlm_loss import VQASerTokenLayoutLMLoss + +# sr loss +from .stroke_focus_loss import StrokeFocusLoss +from .text_focus_loss import TelescopeLoss + + +def build_loss(config): + support_dict = [ + "DBLoss", + "PSELoss", + "EASTLoss", + "SASTLoss", + "FCELoss", + "CTCLoss", + "ClsLoss", + "AttentionLoss", + "SRNLoss", + "PGLoss", + "CombinedLoss", + "CELoss", + "TableAttentionLoss", + "SARLoss", + "AsterLoss", + "SDMGRLoss", + "VQASerTokenLayoutLMLoss", + "LossFromOutput", + "PRENLoss", + "MultiLoss", + "TableMasterLoss", + "SPINAttentionLoss", + "VLLoss", + "StrokeFocusLoss", + "SLALoss", + "CTLoss", + "RFLLoss", + "DRRGLoss", + "CANLoss", + "TelescopeLoss", + "SATRNLoss", + "NRTRLoss", + "ParseQLoss", + "CPPDLoss", + "LaTeXOCRLoss", + "UniMERNetLoss", + "PPFormulaNet_S_Loss", + "PPFormulaNet_L_Loss", + ] + config = copy.deepcopy(config) + module_name = config.pop("name") + assert module_name in support_dict, Exception( + "loss only support {}".format(support_dict) + ) + module_class = eval(module_name)(**config) + return module_class diff --git a/modules/onnx_ocr_module/src/ppocr/losses/ace_loss.py b/modules/onnx_ocr_module/src/ppocr/losses/ace_loss.py new file mode 100644 index 0000000..961528d --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/losses/ace_loss.py @@ -0,0 +1,49 @@ +# copyright (c) 2021 PaddlePaddle Authors. All Rights Reserve. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# This code is refer from: https://github.com/viig99/LS-ACELoss + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import paddle +import paddle.nn as nn + + +class ACELoss(nn.Layer): + def __init__(self, **kwargs): + super().__init__() + self.loss_func = nn.CrossEntropyLoss( + weight=None, ignore_index=0, reduction="none", soft_label=True, axis=-1 + ) + + def __call__(self, predicts, batch): + if isinstance(predicts, (list, tuple)): + predicts = predicts[-1] + + B, N = predicts.shape[:2] + div = paddle.to_tensor([N]).astype("float32") + + predicts = nn.functional.softmax(predicts, axis=-1) + aggregation_preds = paddle.sum(predicts, axis=1) + aggregation_preds = paddle.divide(aggregation_preds, div) + + length = batch[2].astype("float32") + batch = batch[3].astype("float32") + batch[:, 0] = paddle.subtract(div, length) + batch = paddle.divide(batch, div) + + loss = self.loss_func(aggregation_preds, batch) + return {"loss_ace": loss} diff --git a/modules/onnx_ocr_module/src/ppocr/losses/basic_loss.py b/modules/onnx_ocr_module/src/ppocr/losses/basic_loss.py new file mode 100644 index 0000000..e85911a --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/losses/basic_loss.py @@ -0,0 +1,247 @@ +# copyright (c) 2021 PaddlePaddle Authors. All Rights Reserve. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import paddle +import paddle.nn as nn +import paddle.nn.functional as F + +from paddle.nn import L1Loss +from paddle.nn import MSELoss as L2Loss +from paddle.nn import SmoothL1Loss + + +class CELoss(nn.Layer): + def __init__(self, epsilon=None): + super().__init__() + if epsilon is not None and (epsilon <= 0 or epsilon >= 1): + epsilon = None + self.epsilon = epsilon + + def _labelsmoothing(self, target, class_num): + if target.shape[-1] != class_num: + one_hot_target = F.one_hot(target, class_num) + else: + one_hot_target = target + soft_target = F.label_smooth(one_hot_target, epsilon=self.epsilon) + soft_target = paddle.reshape(soft_target, shape=[-1, class_num]) + return soft_target + + def forward(self, x, label): + loss_dict = {} + if self.epsilon is not None: + class_num = x.shape[-1] + label = self._labelsmoothing(label, class_num) + x = -F.log_softmax(x, axis=-1) + loss = paddle.sum(x * label, axis=-1) + else: + if label.shape[-1] == x.shape[-1]: + label = F.softmax(label, axis=-1) + soft_label = True + else: + soft_label = False + loss = F.cross_entropy(x, label=label, soft_label=soft_label) + return loss + + +class KLJSLoss(object): + def __init__(self, mode="kl"): + assert mode in [ + "kl", + "js", + "KL", + "JS", + ], "mode can only be one of ['kl', 'KL', 'js', 'JS']" + self.mode = mode + + def __call__(self, p1, p2, reduction="mean", eps=1e-5): + if self.mode.lower() == "kl": + loss = paddle.multiply(p2, paddle.log((p2 + eps) / (p1 + eps) + eps)) + loss += paddle.multiply(p1, paddle.log((p1 + eps) / (p2 + eps) + eps)) + loss *= 0.5 + elif self.mode.lower() == "js": + loss = paddle.multiply( + p2, paddle.log((2 * p2 + eps) / (p1 + p2 + eps) + eps) + ) + loss += paddle.multiply( + p1, paddle.log((2 * p1 + eps) / (p1 + p2 + eps) + eps) + ) + loss *= 0.5 + else: + raise ValueError( + "The mode.lower() if KLJSLoss should be one of ['kl', 'js']" + ) + + if reduction == "mean": + loss = paddle.mean(loss, axis=[1, 2]) + elif reduction == "none" or reduction is None: + return loss + else: + loss = paddle.sum(loss, axis=[1, 2]) + + return loss + + +class DMLLoss(nn.Layer): + """ + DMLLoss + """ + + def __init__(self, act=None, use_log=False): + super().__init__() + if act is not None: + assert act in ["softmax", "sigmoid"] + if act == "softmax": + self.act = nn.Softmax(axis=-1) + elif act == "sigmoid": + self.act = nn.Sigmoid() + else: + self.act = None + + self.use_log = use_log + self.jskl_loss = KLJSLoss(mode="kl") + + def _kldiv(self, x, target): + eps = 1.0e-10 + loss = target * (paddle.log(target + eps) - x) + # batch mean loss + loss = paddle.sum(loss) / loss.shape[0] + return loss + + def forward(self, out1, out2): + if self.act is not None: + out1 = self.act(out1) + 1e-10 + out2 = self.act(out2) + 1e-10 + if self.use_log: + # for recognition distillation, log is needed for feature map + log_out1 = paddle.log(out1) + log_out2 = paddle.log(out2) + loss = (self._kldiv(log_out1, out2) + self._kldiv(log_out2, out1)) / 2.0 + else: + # for detection distillation log is not needed + loss = self.jskl_loss(out1, out2) + return loss + + +class DistanceLoss(nn.Layer): + """ + DistanceLoss: + mode: loss mode + """ + + def __init__(self, mode="l2", **kargs): + super().__init__() + assert mode in ["l1", "l2", "smooth_l1"] + if mode == "l1": + self.loss_func = nn.L1Loss(**kargs) + elif mode == "l2": + self.loss_func = nn.MSELoss(**kargs) + elif mode == "smooth_l1": + self.loss_func = nn.SmoothL1Loss(**kargs) + + def forward(self, x, y): + return self.loss_func(x, y) + + +class LossFromOutput(nn.Layer): + def __init__(self, key="loss", reduction="none"): + super().__init__() + self.key = key + self.reduction = reduction + + def forward(self, predicts, batch): + loss = predicts + if self.key is not None and isinstance(predicts, dict): + loss = loss[self.key] + if self.reduction == "mean": + loss = paddle.mean(loss) + elif self.reduction == "sum": + loss = paddle.sum(loss) + return {"loss": loss} + + +class KLDivLoss(nn.Layer): + """ + KLDivLoss + """ + + def __init__(self): + super().__init__() + + def _kldiv(self, x, target, mask=None): + eps = 1.0e-10 + loss = target * (paddle.log(target + eps) - x) + if mask is not None: + loss = loss.flatten(0, 1).sum(axis=1) + loss = loss.masked_select(mask).mean() + else: + # batch mean loss + loss = paddle.sum(loss) / loss.shape[0] + return loss + + def forward(self, logits_s, logits_t, mask=None): + log_out_s = F.log_softmax(logits_s, axis=-1) + out_t = F.softmax(logits_t, axis=-1) + loss = self._kldiv(log_out_s, out_t, mask) + return loss + + +class DKDLoss(nn.Layer): + """ + KLDivLoss + """ + + def __init__(self, temperature=1.0, alpha=1.0, beta=1.0): + super().__init__() + self.temperature = temperature + self.alpha = alpha + self.beta = beta + + def _cat_mask(self, t, mask1, mask2): + t1 = (t * mask1).sum(axis=1, keepdim=True) + t2 = (t * mask2).sum(axis=1, keepdim=True) + rt = paddle.concat([t1, t2], axis=1) + return rt + + def _kl_div(self, x, label, mask=None): + y = (label * (paddle.log(label + 1e-10) - x)).sum(axis=1) + if mask is not None: + y = y.masked_select(mask).mean() + else: + y = y.mean() + return y + + def forward(self, logits_student, logits_teacher, target, mask=None): + gt_mask = F.one_hot(target.reshape([-1]), num_classes=logits_student.shape[-1]) + other_mask = 1 - gt_mask + logits_student = logits_student.flatten(0, 1) + logits_teacher = logits_teacher.flatten(0, 1) + pred_student = F.softmax(logits_student / self.temperature, axis=1) + pred_teacher = F.softmax(logits_teacher / self.temperature, axis=1) + pred_student = self._cat_mask(pred_student, gt_mask, other_mask) + pred_teacher = self._cat_mask(pred_teacher, gt_mask, other_mask) + log_pred_student = paddle.log(pred_student) + tckd_loss = self._kl_div(log_pred_student, pred_teacher) * (self.temperature**2) + pred_teacher_part2 = F.softmax( + logits_teacher / self.temperature - 1000.0 * gt_mask, axis=1 + ) + log_pred_student_part2 = F.log_softmax( + logits_student / self.temperature - 1000.0 * gt_mask, axis=1 + ) + nckd_loss = self._kl_div(log_pred_student_part2, pred_teacher_part2) * ( + self.temperature**2 + ) + + loss = self.alpha * tckd_loss + self.beta * nckd_loss + + return loss diff --git a/modules/onnx_ocr_module/src/ppocr/losses/center_loss.py b/modules/onnx_ocr_module/src/ppocr/losses/center_loss.py new file mode 100644 index 0000000..87d4d44 --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/losses/center_loss.py @@ -0,0 +1,89 @@ +# copyright (c) 2021 PaddlePaddle Authors. All Rights Reserve. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# This code is refer from: https://github.com/KaiyangZhou/pytorch-center-loss + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function +import os +import pickle + +import paddle +import paddle.nn as nn +import paddle.nn.functional as F + + +class CenterLoss(nn.Layer): + """ + Reference: Wen et al. A Discriminative Feature Learning Approach for Deep Face Recognition. ECCV 2016. + """ + + def __init__(self, num_classes=6625, feat_dim=96, center_file_path=None): + super().__init__() + self.num_classes = num_classes + self.feat_dim = feat_dim + self.centers = paddle.randn(shape=[self.num_classes, self.feat_dim]).astype( + "float64" + ) + + if center_file_path is not None: + assert os.path.exists( + center_file_path + ), f"center path({center_file_path}) must exist when it is not None." + with open(center_file_path, "rb") as f: + char_dict = pickle.load(f) + for key in char_dict.keys(): + self.centers[key] = paddle.to_tensor(char_dict[key]) + + def __call__(self, predicts, batch): + assert isinstance(predicts, (list, tuple)) + features, predicts = predicts + + feats_reshape = paddle.reshape(features, [-1, features.shape[-1]]).astype( + "float64" + ) + label = paddle.argmax(predicts, axis=2) + label = paddle.reshape(label, [label.shape[0] * label.shape[1]]) + + batch_size = feats_reshape.shape[0] + + # calc l2 distance between feats and centers + square_feat = paddle.sum(paddle.square(feats_reshape), axis=1, keepdim=True) + square_feat = paddle.expand(square_feat, [batch_size, self.num_classes]) + + square_center = paddle.sum(paddle.square(self.centers), axis=1, keepdim=True) + square_center = paddle.expand( + square_center, [self.num_classes, batch_size] + ).astype("float64") + square_center = paddle.transpose(square_center, [1, 0]) + + distmat = paddle.add(square_feat, square_center) + feat_dot_center = paddle.matmul( + feats_reshape, paddle.transpose(self.centers, [1, 0]) + ) + distmat = distmat - 2.0 * feat_dot_center + + # generate the mask + classes = paddle.arange(self.num_classes).astype("int64") + label = paddle.expand( + paddle.unsqueeze(label, 1), (batch_size, self.num_classes) + ) + mask = paddle.equal( + paddle.expand(classes, [batch_size, self.num_classes]), label + ).astype("float64") + dist = paddle.multiply(distmat, mask) + + loss = paddle.sum(paddle.clip(dist, min=1e-12, max=1e12)) / batch_size + return {"loss_center": loss} diff --git a/modules/onnx_ocr_module/src/ppocr/losses/cls_loss.py b/modules/onnx_ocr_module/src/ppocr/losses/cls_loss.py new file mode 100644 index 0000000..eb3b17e --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/losses/cls_loss.py @@ -0,0 +1,30 @@ +# copyright (c) 2019 PaddlePaddle Authors. All Rights Reserve. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +from paddle import nn + + +class ClsLoss(nn.Layer): + def __init__(self, **kwargs): + super(ClsLoss, self).__init__() + self.loss_func = nn.CrossEntropyLoss(reduction="mean") + + def forward(self, predicts, batch): + label = batch[1].astype("int64") + loss = self.loss_func(input=predicts, label=label) + return {"loss": loss} diff --git a/modules/onnx_ocr_module/src/ppocr/losses/combined_loss.py b/modules/onnx_ocr_module/src/ppocr/losses/combined_loss.py new file mode 100644 index 0000000..10cdb01 --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/losses/combined_loss.py @@ -0,0 +1,84 @@ +# Copyright (c) 2020 PaddlePaddle Authors. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import paddle +import paddle.nn as nn + +from .rec_ctc_loss import CTCLoss +from .center_loss import CenterLoss +from .ace_loss import ACELoss +from .rec_sar_loss import SARLoss + +from .distillation_loss import DistillationCTCLoss, DistillCTCLogits +from .distillation_loss import DistillationSARLoss, DistillationNRTRLoss +from .distillation_loss import ( + DistillationDMLLoss, + DistillationKLDivLoss, + DistillationDKDLoss, +) +from .distillation_loss import ( + DistillationDistanceLoss, + DistillationDBLoss, + DistillationDilaDBLoss, +) +from .distillation_loss import ( + DistillationVQASerTokenLayoutLMLoss, + DistillationSERDMLLoss, +) +from .distillation_loss import DistillationLossFromOutput +from .distillation_loss import DistillationVQADistanceLoss + + +class CombinedLoss(nn.Layer): + """ + CombinedLoss: + a combionation of loss function + """ + + def __init__(self, loss_config_list=None): + super().__init__() + self.loss_func = [] + self.loss_weight = [] + assert isinstance(loss_config_list, list), "operator config should be a list" + for config in loss_config_list: + assert isinstance(config, dict) and len(config) == 1, "yaml format error" + name = list(config)[0] + param = config[name] + assert ( + "weight" in param + ), "weight must be in param, but param just contains {}".format( + param.keys() + ) + self.loss_weight.append(param.pop("weight")) + self.loss_func.append(eval(name)(**param)) + + def forward(self, input, batch, **kargs): + loss_dict = {} + loss_all = 0.0 + for idx, loss_func in enumerate(self.loss_func): + loss = loss_func(input, batch, **kargs) + if isinstance(loss, paddle.Tensor): + loss = {"loss_{}_{}".format(str(loss), idx): loss} + + weight = self.loss_weight[idx] + + loss = {key: loss[key] * weight for key in loss} + + if "loss" in loss: + loss_all += loss["loss"] + else: + loss_all += paddle.add_n(list(loss.values())) + loss_dict.update(loss) + loss_dict["loss"] = loss_all + return loss_dict diff --git a/modules/onnx_ocr_module/src/ppocr/losses/det_basic_loss.py b/modules/onnx_ocr_module/src/ppocr/losses/det_basic_loss.py new file mode 100644 index 0000000..5a46e07 --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/losses/det_basic_loss.py @@ -0,0 +1,161 @@ +# copyright (c) 2019 PaddlePaddle Authors. All Rights Reserve. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +""" +This code is refer from: +https://github.com/WenmuZhou/DBNet.pytorch/blob/master/models/losses/basic_loss.py +""" +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import numpy as np + +import paddle +from paddle import nn +import paddle.nn.functional as F + + +class BalanceLoss(nn.Layer): + def __init__( + self, + balance_loss=True, + main_loss_type="DiceLoss", + negative_ratio=3, + return_origin=False, + eps=1e-6, + **kwargs, + ): + """ + The BalanceLoss for Differentiable Binarization text detection + args: + balance_loss (bool): whether balance loss or not, default is True + main_loss_type (str): can only be one of ['CrossEntropy','DiceLoss', + 'Euclidean','BCELoss', 'MaskL1Loss'], default is 'DiceLoss'. + negative_ratio (int|float): float, default is 3. + return_origin (bool): whether return unbalanced loss or not, default is False. + eps (float): default is 1e-6. + """ + super(BalanceLoss, self).__init__() + self.balance_loss = balance_loss + self.main_loss_type = main_loss_type + self.negative_ratio = negative_ratio + self.return_origin = return_origin + self.eps = eps + + if self.main_loss_type == "CrossEntropy": + self.loss = nn.CrossEntropyLoss() + elif self.main_loss_type == "Euclidean": + self.loss = nn.MSELoss() + elif self.main_loss_type == "DiceLoss": + self.loss = DiceLoss(self.eps) + elif self.main_loss_type == "BCELoss": + self.loss = BCELoss(reduction="none") + elif self.main_loss_type == "MaskL1Loss": + self.loss = MaskL1Loss(self.eps) + else: + loss_type = [ + "CrossEntropy", + "DiceLoss", + "Euclidean", + "BCELoss", + "MaskL1Loss", + ] + raise Exception( + "main_loss_type in BalanceLoss() can only be one of {}".format( + loss_type + ) + ) + + def forward(self, pred, gt, mask=None): + """ + The BalanceLoss for Differentiable Binarization text detection + args: + pred (variable): predicted feature maps. + gt (variable): ground truth feature maps. + mask (variable): masked maps. + return: (variable) balanced loss + """ + positive = gt * mask + negative = (1 - gt) * mask + + positive_count = int(positive.sum()) + negative_count = int(min(negative.sum(), positive_count * self.negative_ratio)) + loss = self.loss(pred, gt, mask=mask) + + if not self.balance_loss: + return loss + + positive_loss = positive * loss + negative_loss = negative * loss + negative_loss = paddle.reshape(negative_loss, shape=[-1]) + if negative_count > 0: + sort_loss = negative_loss.sort(descending=True) + negative_loss = sort_loss[:negative_count] + # negative_loss, _ = paddle.topk(negative_loss, k=negative_count_int) + balance_loss = (positive_loss.sum() + negative_loss.sum()) / ( + positive_count + negative_count + self.eps + ) + else: + balance_loss = positive_loss.sum() / (positive_count + self.eps) + if self.return_origin: + return balance_loss, loss + + return balance_loss + + +class DiceLoss(nn.Layer): + def __init__(self, eps=1e-6): + super(DiceLoss, self).__init__() + self.eps = eps + + def forward(self, pred, gt, mask, weights=None): + """ + DiceLoss function. + """ + + assert pred.shape == gt.shape + assert pred.shape == mask.shape + if weights is not None: + assert weights.shape == mask.shape + mask = weights * mask + intersection = paddle.sum(pred * gt * mask) + + union = paddle.sum(pred * mask) + paddle.sum(gt * mask) + self.eps + loss = 1 - 2.0 * intersection / union + assert loss <= 1 + return loss + + +class MaskL1Loss(nn.Layer): + def __init__(self, eps=1e-6): + super(MaskL1Loss, self).__init__() + self.eps = eps + + def forward(self, pred, gt, mask): + """ + Mask L1 Loss + """ + loss = (paddle.abs(pred - gt) * mask).sum() / (mask.sum() + self.eps) + loss = paddle.mean(loss) + return loss + + +class BCELoss(nn.Layer): + def __init__(self, reduction="mean"): + super(BCELoss, self).__init__() + self.reduction = reduction + + def forward(self, input, label, mask=None, weight=None, name=None): + loss = F.binary_cross_entropy(input, label, reduction=self.reduction) + return loss diff --git a/modules/onnx_ocr_module/src/ppocr/losses/det_ct_loss.py b/modules/onnx_ocr_module/src/ppocr/losses/det_ct_loss.py new file mode 100644 index 0000000..4655eff --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/losses/det_ct_loss.py @@ -0,0 +1,302 @@ +# copyright (c) 2021 PaddlePaddle Authors. All Rights Reserve. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +""" +This code is refer from: +https://github.com/shengtao96/CentripetalText/tree/main/models/loss +""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import paddle +from paddle import nn +import paddle.nn.functional as F +import numpy as np + + +def ohem_single(score, gt_text, training_mask): + # online hard example mining + + pos_num = int(paddle.sum(gt_text > 0.5)) - int( + paddle.sum((gt_text > 0.5) & (training_mask <= 0.5)) + ) + + if pos_num == 0: + # selected_mask = gt_text.copy() * 0 # may be not good + selected_mask = training_mask + selected_mask = paddle.cast( + selected_mask.reshape((1, selected_mask.shape[0], selected_mask.shape[1])), + "float32", + ) + return selected_mask + + neg_num = int(paddle.sum((gt_text <= 0.5) & (training_mask > 0.5))) + neg_num = int(min(pos_num * 3, neg_num)) + + if neg_num == 0: + selected_mask = training_mask + selected_mask = paddle.cast( + selected_mask.reshape((1, selected_mask.shape[0], selected_mask.shape[1])), + "float32", + ) + return selected_mask + + # hard example + neg_score = score[(gt_text <= 0.5) & (training_mask > 0.5)] + neg_score_sorted = paddle.sort(-neg_score) + threshold = -neg_score_sorted[neg_num - 1] + + selected_mask = ((score >= threshold) | (gt_text > 0.5)) & (training_mask > 0.5) + selected_mask = paddle.cast( + selected_mask.reshape((1, selected_mask.shape[0], selected_mask.shape[1])), + "float32", + ) + return selected_mask + + +def ohem_batch(scores, gt_texts, training_masks): + selected_masks = [] + for i in range(scores.shape[0]): + selected_masks.append( + ohem_single(scores[i, :, :], gt_texts[i, :, :], training_masks[i, :, :]) + ) + + selected_masks = paddle.cast(paddle.concat(selected_masks, 0), "float32") + return selected_masks + + +def iou_single(a, b, mask, n_class): + EPS = 1e-6 + valid = mask == 1 + a = a[valid] + b = b[valid] + miou = [] + + # iou of each class + for i in range(n_class): + inter = paddle.cast(((a == i) & (b == i)), "float32") + union = paddle.cast(((a == i) | (b == i)), "float32") + + miou.append(paddle.sum(inter) / (paddle.sum(union) + EPS)) + miou = sum(miou) / len(miou) + return miou + + +def iou(a, b, mask, n_class=2, reduce=True): + batch_size = a.shape[0] + + a = a.reshape((batch_size, -1)) + b = b.reshape((batch_size, -1)) + mask = mask.reshape((batch_size, -1)) + + iou = paddle.zeros((batch_size,), dtype="float32") + for i in range(batch_size): + iou[i] = iou_single(a[i], b[i], mask[i], n_class) + + if reduce: + iou = paddle.mean(iou) + return iou + + +class DiceLoss(nn.Layer): + def __init__(self, loss_weight=1.0): + super(DiceLoss, self).__init__() + self.loss_weight = loss_weight + + def forward(self, input, target, mask, reduce=True): + batch_size = input.shape[0] + input = F.sigmoid(input) # scale to 0-1 + + input = input.reshape((batch_size, -1)) + target = paddle.cast(target.reshape((batch_size, -1)), "float32") + mask = paddle.cast(mask.reshape((batch_size, -1)), "float32") + + input = input * mask + target = target * mask + + a = paddle.sum(input * target, axis=1) + b = paddle.sum(input * input, axis=1) + 0.001 + c = paddle.sum(target * target, axis=1) + 0.001 + d = (2 * a) / (b + c) + loss = 1 - d + + loss = self.loss_weight * loss + + if reduce: + loss = paddle.mean(loss) + + return loss + + +class SmoothL1Loss(nn.Layer): + def __init__(self, beta=1.0, loss_weight=1.0): + super(SmoothL1Loss, self).__init__() + self.beta = beta + self.loss_weight = loss_weight + + np_coord = np.zeros(shape=[640, 640, 2], dtype=np.int64) + for i in range(640): + for j in range(640): + np_coord[i, j, 0] = j + np_coord[i, j, 1] = i + np_coord = np_coord.reshape((-1, 2)) + + self.coord = self.create_parameter( + shape=[640 * 640, 2], + dtype="int32", # NOTE: not support "int64" before paddle 2.3.1 + default_initializer=nn.initializer.Assign(value=np_coord), + ) + self.coord.stop_gradient = True + + def forward_single(self, input, target, mask, beta=1.0, eps=1e-6): + batch_size = input.shape[0] + + diff = paddle.abs(input - target) * mask.unsqueeze(1) + loss = paddle.where(diff < beta, 0.5 * diff * diff / beta, diff - 0.5 * beta) + loss = paddle.cast(loss.reshape((batch_size, -1)), "float32") + mask = paddle.cast(mask.reshape((batch_size, -1)), "float32") + loss = paddle.sum(loss, axis=-1) + loss = loss / (mask.sum(axis=-1) + eps) + + return loss + + def select_single(self, distance, gt_instance, gt_kernel_instance, training_mask): + with paddle.no_grad(): + # paddle 2.3.1, paddle.slice not support: + # distance[:, self.coord[:, 1], self.coord[:, 0]] + select_distance_list = [] + for i in range(2): + tmp1 = distance[i, :] + tmp2 = tmp1[self.coord[:, 1], self.coord[:, 0]] + select_distance_list.append(tmp2.unsqueeze(0)) + select_distance = paddle.concat(select_distance_list, axis=0) + + off_points = paddle.cast( + self.coord, "float32" + ) + 10 * select_distance.transpose((1, 0)) + + off_points = paddle.cast(off_points, "int64") + off_points = paddle.clip(off_points, 0, distance.shape[-1] - 1) + + selected_mask = ( + gt_instance[self.coord[:, 1], self.coord[:, 0]] + != gt_kernel_instance[off_points[:, 1], off_points[:, 0]] + ) + selected_mask = paddle.cast( + selected_mask.reshape((1, -1, distance.shape[-1])), "int64" + ) + selected_training_mask = selected_mask * training_mask + + return selected_training_mask + + def forward( + self, + distances, + gt_instances, + gt_kernel_instances, + training_masks, + gt_distances, + reduce=True, + ): + selected_training_masks = [] + for i in range(distances.shape[0]): + selected_training_masks.append( + self.select_single( + distances[i, :, :, :], + gt_instances[i, :, :], + gt_kernel_instances[i, :, :], + training_masks[i, :, :], + ) + ) + selected_training_masks = paddle.cast( + paddle.concat(selected_training_masks, 0), "float32" + ) + + loss = self.forward_single( + distances, gt_distances, selected_training_masks, self.beta + ) + loss = self.loss_weight * loss + + with paddle.no_grad(): + batch_size = distances.shape[0] + false_num = selected_training_masks.reshape((batch_size, -1)) + false_num = false_num.sum(axis=-1) + total_num = paddle.cast(training_masks.reshape((batch_size, -1)), "float32") + total_num = total_num.sum(axis=-1) + iou_text = (total_num - false_num) / (total_num + 1e-6) + + if reduce: + loss = paddle.mean(loss) + + return loss, iou_text + + +class CTLoss(nn.Layer): + def __init__(self): + super(CTLoss, self).__init__() + self.kernel_loss = DiceLoss() + self.loc_loss = SmoothL1Loss(beta=0.1, loss_weight=0.05) + + def forward(self, preds, batch): + imgs = batch[0] + out = preds["maps"] + ( + gt_kernels, + training_masks, + gt_instances, + gt_kernel_instances, + training_mask_distances, + gt_distances, + ) = batch[1:] + + kernels = out[:, 0, :, :] + distances = out[:, 1:, :, :] + + # kernel loss + selected_masks = ohem_batch(kernels, gt_kernels, training_masks) + + loss_kernel = self.kernel_loss( + kernels, gt_kernels, selected_masks, reduce=False + ) + + iou_kernel = iou( + paddle.cast((kernels > 0), "int64"), + gt_kernels, + training_masks, + reduce=False, + ) + losses = dict( + loss_kernels=loss_kernel, + ) + + # loc loss + loss_loc, iou_text = self.loc_loss( + distances, + gt_instances, + gt_kernel_instances, + training_mask_distances, + gt_distances, + reduce=False, + ) + losses.update( + dict( + loss_loc=loss_loc, + ) + ) + + loss_all = loss_kernel + loss_loc + losses = {"loss": loss_all} + + return losses diff --git a/modules/onnx_ocr_module/src/ppocr/losses/det_db_loss.py b/modules/onnx_ocr_module/src/ppocr/losses/det_db_loss.py new file mode 100644 index 0000000..83a363a --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/losses/det_db_loss.py @@ -0,0 +1,99 @@ +# copyright (c) 2019 PaddlePaddle Authors. All Rights Reserve. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +""" +This code is refer from: +https://github.com/WenmuZhou/DBNet.pytorch/blob/master/models/losses/DB_loss.py +""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import paddle +from paddle import nn + +from .det_basic_loss import BalanceLoss, MaskL1Loss, DiceLoss + + +class DBLoss(nn.Layer): + """ + Differentiable Binarization (DB) Loss Function + args: + param (dict): the super parameter for DB Loss + """ + + def __init__( + self, + balance_loss=True, + main_loss_type="DiceLoss", + alpha=5, + beta=10, + ohem_ratio=3, + eps=1e-6, + **kwargs, + ): + super(DBLoss, self).__init__() + self.alpha = alpha + self.beta = beta + self.dice_loss = DiceLoss(eps=eps) + self.l1_loss = MaskL1Loss(eps=eps) + self.bce_loss = BalanceLoss( + balance_loss=balance_loss, + main_loss_type=main_loss_type, + negative_ratio=ohem_ratio, + ) + + def forward(self, predicts, labels): + predict_maps = predicts["maps"] + ( + label_threshold_map, + label_threshold_mask, + label_shrink_map, + label_shrink_mask, + ) = labels[1:] + shrink_maps = predict_maps[:, 0, :, :] + threshold_maps = predict_maps[:, 1, :, :] + binary_maps = predict_maps[:, 2, :, :] + + loss_shrink_maps = self.bce_loss( + shrink_maps, label_shrink_map, label_shrink_mask + ) + loss_threshold_maps = self.l1_loss( + threshold_maps, label_threshold_map, label_threshold_mask + ) + loss_binary_maps = self.dice_loss( + binary_maps, label_shrink_map, label_shrink_mask + ) + loss_shrink_maps = self.alpha * loss_shrink_maps + loss_threshold_maps = self.beta * loss_threshold_maps + # CBN loss + if "distance_maps" in predicts.keys(): + distance_maps = predicts["distance_maps"] + cbn_maps = predicts["cbn_maps"] + cbn_loss = self.bce_loss( + cbn_maps[:, 0, :, :], label_shrink_map, label_shrink_mask + ) + else: + dis_loss = paddle.to_tensor([0.0]) + cbn_loss = paddle.to_tensor([0.0]) + + loss_all = loss_shrink_maps + loss_threshold_maps + loss_binary_maps + losses = { + "loss": loss_all + cbn_loss, + "loss_shrink_maps": loss_shrink_maps, + "loss_threshold_maps": loss_threshold_maps, + "loss_binary_maps": loss_binary_maps, + "loss_cbn": cbn_loss, + } + return losses diff --git a/modules/onnx_ocr_module/src/ppocr/losses/det_drrg_loss.py b/modules/onnx_ocr_module/src/ppocr/losses/det_drrg_loss.py new file mode 100644 index 0000000..6e990ce --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/losses/det_drrg_loss.py @@ -0,0 +1,234 @@ +# copyright (c) 2022 PaddlePaddle Authors. All Rights Reserve. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +""" +This code is refer from: +https://github.com/open-mmlab/mmocr/blob/main/mmocr/models/textdet/losses/drrg_loss.py +""" + +import paddle +import paddle.nn.functional as F +from paddle import nn + + +class DRRGLoss(nn.Layer): + def __init__(self, ohem_ratio=3.0): + super().__init__() + self.ohem_ratio = ohem_ratio + self.downsample_ratio = 1.0 + + def balance_bce_loss(self, pred, gt, mask): + """Balanced Binary-CrossEntropy Loss. + + Args: + pred (Tensor): Shape of :math:`(1, H, W)`. + gt (Tensor): Shape of :math:`(1, H, W)`. + mask (Tensor): Shape of :math:`(1, H, W)`. + + Returns: + Tensor: Balanced bce loss. + """ + assert pred.shape == gt.shape == mask.shape + assert paddle.all(pred >= 0) and paddle.all(pred <= 1) + assert paddle.all(gt >= 0) and paddle.all(gt <= 1) + positive = gt * mask + negative = (1 - gt) * mask + positive_count = int(positive.sum()) + + if positive_count > 0: + loss = F.binary_cross_entropy(pred, gt, reduction="none") + positive_loss = paddle.sum(loss * positive) + negative_loss = loss * negative + negative_count = min( + int(negative.sum()), int(positive_count * self.ohem_ratio) + ) + else: + positive_loss = paddle.to_tensor(0.0) + loss = F.binary_cross_entropy(pred, gt, reduction="none") + negative_loss = loss * negative + negative_count = 100 + negative_loss, _ = paddle.topk(negative_loss.reshape([-1]), negative_count) + + balance_loss = (positive_loss + paddle.sum(negative_loss)) / ( + float(positive_count + negative_count) + 1e-5 + ) + + return balance_loss + + def gcn_loss(self, gcn_data): + """CrossEntropy Loss from gcn module. + + Args: + gcn_data (tuple(Tensor, Tensor)): The first is the + prediction with shape :math:`(N, 2)` and the + second is the gt label with shape :math:`(m, n)` + where :math:`m * n = N`. + + Returns: + Tensor: CrossEntropy loss. + """ + gcn_pred, gt_labels = gcn_data + gt_labels = gt_labels.reshape([-1]) + loss = F.cross_entropy(gcn_pred, gt_labels) + + return loss + + def bitmasks2tensor(self, bitmasks, target_sz): + """Convert Bitmasks to tensor. + + Args: + bitmasks (list[BitmapMasks]): The BitmapMasks list. Each item is + for one img. + target_sz (tuple(int, int)): The target tensor of size + :math:`(H, W)`. + + Returns: + list[Tensor]: The list of kernel tensors. Each element stands for + one kernel level. + """ + batch_size = len(bitmasks) + results = [] + + kernel = [] + for batch_inx in range(batch_size): + mask = bitmasks[batch_inx] + # hxw + mask_sz = mask.shape + # left, right, top, bottom + pad = [0, target_sz[1] - mask_sz[1], 0, target_sz[0] - mask_sz[0]] + mask = F.pad(mask, pad, mode="constant", value=0) + kernel.append(mask) + kernel = paddle.stack(kernel) + results.append(kernel) + + return results + + def forward(self, preds, labels): + """Compute Drrg loss.""" + + assert isinstance(preds, tuple) + ( + gt_text_mask, + gt_center_region_mask, + gt_mask, + gt_top_height_map, + gt_bot_height_map, + gt_sin_map, + gt_cos_map, + ) = labels[1:8] + + downsample_ratio = self.downsample_ratio + + pred_maps, gcn_data = preds + pred_text_region = pred_maps[:, 0, :, :] + pred_center_region = pred_maps[:, 1, :, :] + pred_sin_map = pred_maps[:, 2, :, :] + pred_cos_map = pred_maps[:, 3, :, :] + pred_top_height_map = pred_maps[:, 4, :, :] + pred_bot_height_map = pred_maps[:, 5, :, :] + feature_sz = pred_maps.shape + + # bitmask 2 tensor + mapping = { + "gt_text_mask": paddle.cast(gt_text_mask, "float32"), + "gt_center_region_mask": paddle.cast(gt_center_region_mask, "float32"), + "gt_mask": paddle.cast(gt_mask, "float32"), + "gt_top_height_map": paddle.cast(gt_top_height_map, "float32"), + "gt_bot_height_map": paddle.cast(gt_bot_height_map, "float32"), + "gt_sin_map": paddle.cast(gt_sin_map, "float32"), + "gt_cos_map": paddle.cast(gt_cos_map, "float32"), + } + gt = {} + for key, value in mapping.items(): + gt[key] = value + if abs(downsample_ratio - 1.0) < 1e-2: + gt[key] = self.bitmasks2tensor(gt[key], feature_sz[2:]) + else: + gt[key] = [item.rescale(downsample_ratio) for item in gt[key]] + gt[key] = self.bitmasks2tensor(gt[key], feature_sz[2:]) + if key in ["gt_top_height_map", "gt_bot_height_map"]: + gt[key] = [item * downsample_ratio for item in gt[key]] + gt[key] = [item for item in gt[key]] + + scale = paddle.sqrt(1.0 / (pred_sin_map**2 + pred_cos_map**2 + 1e-8)) + pred_sin_map = pred_sin_map * scale + pred_cos_map = pred_cos_map * scale + + loss_text = self.balance_bce_loss( + F.sigmoid(pred_text_region), gt["gt_text_mask"][0], gt["gt_mask"][0] + ) + + text_mask = gt["gt_text_mask"][0] * gt["gt_mask"][0] + negative_text_mask = (1 - gt["gt_text_mask"][0]) * gt["gt_mask"][0] + loss_center_map = F.binary_cross_entropy( + F.sigmoid(pred_center_region), + gt["gt_center_region_mask"][0], + reduction="none", + ) + if int(text_mask.sum()) > 0: + loss_center_positive = paddle.sum(loss_center_map * text_mask) / paddle.sum( + text_mask + ) + else: + loss_center_positive = paddle.to_tensor(0.0) + loss_center_negative = paddle.sum( + loss_center_map * negative_text_mask + ) / paddle.sum(negative_text_mask) + loss_center = loss_center_positive + 0.5 * loss_center_negative + + center_mask = gt["gt_center_region_mask"][0] * gt["gt_mask"][0] + if int(center_mask.sum()) > 0: + map_sz = pred_top_height_map.shape + ones = paddle.ones(map_sz, dtype="float32") + loss_top = F.smooth_l1_loss( + pred_top_height_map / (gt["gt_top_height_map"][0] + 1e-2), + ones, + reduction="none", + ) + loss_bot = F.smooth_l1_loss( + pred_bot_height_map / (gt["gt_bot_height_map"][0] + 1e-2), + ones, + reduction="none", + ) + gt_height = gt["gt_top_height_map"][0] + gt["gt_bot_height_map"][0] + loss_height = paddle.sum( + (paddle.log(gt_height + 1) * (loss_top + loss_bot)) * center_mask + ) / paddle.sum(center_mask) + + loss_sin = paddle.sum( + F.smooth_l1_loss(pred_sin_map, gt["gt_sin_map"][0], reduction="none") + * center_mask + ) / paddle.sum(center_mask) + loss_cos = paddle.sum( + F.smooth_l1_loss(pred_cos_map, gt["gt_cos_map"][0], reduction="none") + * center_mask + ) / paddle.sum(center_mask) + else: + loss_height = paddle.to_tensor(0.0) + loss_sin = paddle.to_tensor(0.0) + loss_cos = paddle.to_tensor(0.0) + + loss_gcn = self.gcn_loss(gcn_data) + + loss = loss_text + loss_center + loss_height + loss_sin + loss_cos + loss_gcn + results = dict( + loss=loss, + loss_text=loss_text, + loss_center=loss_center, + loss_height=loss_height, + loss_sin=loss_sin, + loss_cos=loss_cos, + loss_gcn=loss_gcn, + ) + + return results diff --git a/modules/onnx_ocr_module/src/ppocr/losses/det_east_loss.py b/modules/onnx_ocr_module/src/ppocr/losses/det_east_loss.py new file mode 100644 index 0000000..e5725d0 --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/losses/det_east_loss.py @@ -0,0 +1,62 @@ +# copyright (c) 2019 PaddlePaddle Authors. All Rights Reserve. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import paddle +from paddle import nn +from .det_basic_loss import DiceLoss + + +class EASTLoss(nn.Layer): + """ """ + + def __init__(self, eps=1e-6, **kwargs): + super(EASTLoss, self).__init__() + self.dice_loss = DiceLoss(eps=eps) + + def forward(self, predicts, labels): + l_score, l_geo, l_mask = labels[1:] + f_score = predicts["f_score"] + f_geo = predicts["f_geo"] + + dice_loss = self.dice_loss(f_score, l_score, l_mask) + + # smoooth_l1_loss + channels = 8 + l_geo_split = paddle.split(l_geo, num_or_sections=channels + 1, axis=1) + f_geo_split = paddle.split(f_geo, num_or_sections=channels, axis=1) + smooth_l1 = 0 + for i in range(0, channels): + geo_diff = l_geo_split[i] - f_geo_split[i] + abs_geo_diff = paddle.abs(geo_diff) + smooth_l1_sign = paddle.less_than(abs_geo_diff, l_score) + smooth_l1_sign = paddle.cast(smooth_l1_sign, dtype="float32") + in_loss = abs_geo_diff * abs_geo_diff * smooth_l1_sign + ( + abs_geo_diff - 0.5 + ) * (1.0 - smooth_l1_sign) + out_loss = l_geo_split[-1] / channels * in_loss * l_score + smooth_l1 += out_loss + smooth_l1_loss = paddle.mean(smooth_l1 * l_score) + + dice_loss = dice_loss * 0.01 + total_loss = dice_loss + smooth_l1_loss + losses = { + "loss": total_loss, + "dice_loss": dice_loss, + "smooth_l1_loss": smooth_l1_loss, + } + return losses diff --git a/modules/onnx_ocr_module/src/ppocr/losses/det_fce_loss.py b/modules/onnx_ocr_module/src/ppocr/losses/det_fce_loss.py new file mode 100644 index 0000000..86b82a8 --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/losses/det_fce_loss.py @@ -0,0 +1,240 @@ +# copyright (c) 2022 PaddlePaddle Authors. All Rights Reserve. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +""" +This code is refer from: +https://github.com/open-mmlab/mmocr/blob/main/mmocr/models/textdet/losses/fce_loss.py +""" + +import numpy as np +from paddle import nn +import paddle +import paddle.nn.functional as F +from functools import partial + + +def multi_apply(func, *args, **kwargs): + pfunc = partial(func, **kwargs) if kwargs else func + map_results = map(pfunc, *args) + return tuple(map(list, zip(*map_results))) + + +class FCELoss(nn.Layer): + """The class for implementing FCENet loss + FCENet(CVPR2021): Fourier Contour Embedding for Arbitrary-shaped + Text Detection + + [https://arxiv.org/abs/2104.10442] + + Args: + fourier_degree (int) : The maximum Fourier transform degree k. + num_sample (int) : The sampling points number of regression + loss. If it is too small, fcenet tends to be overfitting. + ohem_ratio (float): the negative/positive ratio in OHEM. + """ + + def __init__(self, fourier_degree, num_sample, ohem_ratio=3.0): + super().__init__() + self.fourier_degree = fourier_degree + self.num_sample = num_sample + self.ohem_ratio = ohem_ratio + + def forward(self, preds, labels): + assert isinstance(preds, dict) + preds = preds["levels"] + + p3_maps, p4_maps, p5_maps = labels[1:] + assert ( + p3_maps[0].shape[0] == 4 * self.fourier_degree + 5 + ), "fourier degree not equal in FCEhead and FCEtarget" + + # to tensor + gts = [p3_maps, p4_maps, p5_maps] + for idx, maps in enumerate(gts): + gts[idx] = paddle.to_tensor(np.stack(maps)) + + losses = multi_apply(self.forward_single, preds, gts) + + loss_tr = paddle.to_tensor(0.0).astype("float32") + loss_tcl = paddle.to_tensor(0.0).astype("float32") + loss_reg_x = paddle.to_tensor(0.0).astype("float32") + loss_reg_y = paddle.to_tensor(0.0).astype("float32") + loss_all = paddle.to_tensor(0.0).astype("float32") + + for idx, loss in enumerate(losses): + loss_all += sum(loss) + if idx == 0: + loss_tr += sum(loss) + elif idx == 1: + loss_tcl += sum(loss) + elif idx == 2: + loss_reg_x += sum(loss) + else: + loss_reg_y += sum(loss) + + results = dict( + loss=loss_all, + loss_text=loss_tr, + loss_center=loss_tcl, + loss_reg_x=loss_reg_x, + loss_reg_y=loss_reg_y, + ) + return results + + def forward_single(self, pred, gt): + cls_pred = paddle.transpose(pred[0], (0, 2, 3, 1)) + reg_pred = paddle.transpose(pred[1], (0, 2, 3, 1)) + gt = paddle.transpose(gt, (0, 2, 3, 1)) + + k = 2 * self.fourier_degree + 1 + tr_pred = paddle.reshape(cls_pred[:, :, :, :2], (-1, 2)) + tcl_pred = paddle.reshape(cls_pred[:, :, :, 2:], (-1, 2)) + x_pred = paddle.reshape(reg_pred[:, :, :, 0:k], (-1, k)) + y_pred = paddle.reshape(reg_pred[:, :, :, k : 2 * k], (-1, k)) + + tr_mask = gt[:, :, :, :1].reshape([-1]) + tcl_mask = gt[:, :, :, 1:2].reshape([-1]) + train_mask = gt[:, :, :, 2:3].reshape([-1]) + x_map = paddle.reshape(gt[:, :, :, 3 : 3 + k], (-1, k)) + y_map = paddle.reshape(gt[:, :, :, 3 + k :], (-1, k)) + + tr_train_mask = (train_mask * tr_mask).astype("bool") + tr_train_mask2 = paddle.concat( + [tr_train_mask.unsqueeze(1), tr_train_mask.unsqueeze(1)], axis=1 + ) + # tr loss + loss_tr = self.ohem(tr_pred, tr_mask, train_mask) + # tcl loss + loss_tcl = paddle.to_tensor(0.0).astype("float32") + tr_neg_mask = tr_train_mask.logical_not() + tr_neg_mask2 = paddle.concat( + [tr_neg_mask.unsqueeze(1), tr_neg_mask.unsqueeze(1)], axis=1 + ) + if tr_train_mask.sum().item() > 0: + loss_tcl_pos = F.cross_entropy( + tcl_pred.masked_select(tr_train_mask2).reshape([-1, 2]), + tcl_mask.masked_select(tr_train_mask).astype("int64"), + ) + loss_tcl_neg = F.cross_entropy( + tcl_pred.masked_select(tr_neg_mask2).reshape([-1, 2]), + tcl_mask.masked_select(tr_neg_mask).astype("int64"), + ) + loss_tcl = loss_tcl_pos + 0.5 * loss_tcl_neg + + # regression loss + loss_reg_x = paddle.to_tensor(0.0).astype("float32") + loss_reg_y = paddle.to_tensor(0.0).astype("float32") + if tr_train_mask.sum().item() > 0: + weight = ( + tr_mask.masked_select(tr_train_mask.astype("bool")).astype("float32") + + tcl_mask.masked_select(tr_train_mask.astype("bool")).astype("float32") + ) / 2 + weight = weight.reshape([-1, 1]) + + ft_x, ft_y = self.fourier2poly(x_map, y_map) + ft_x_pre, ft_y_pre = self.fourier2poly(x_pred, y_pred) + + dim = ft_x.shape[1] + + tr_train_mask3 = paddle.concat( + [tr_train_mask.unsqueeze(1) for i in range(dim)], axis=1 + ) + + loss_reg_x = paddle.mean( + weight + * F.smooth_l1_loss( + ft_x_pre.masked_select(tr_train_mask3).reshape([-1, dim]), + ft_x.masked_select(tr_train_mask3).reshape([-1, dim]), + reduction="none", + ) + ) + loss_reg_y = paddle.mean( + weight + * F.smooth_l1_loss( + ft_y_pre.masked_select(tr_train_mask3).reshape([-1, dim]), + ft_y.masked_select(tr_train_mask3).reshape([-1, dim]), + reduction="none", + ) + ) + + return loss_tr, loss_tcl, loss_reg_x, loss_reg_y + + def ohem(self, predict, target, train_mask): + pos = (target * train_mask).astype("bool") + neg = ((1 - target) * train_mask).astype("bool") + + pos2 = paddle.concat([pos.unsqueeze(1), pos.unsqueeze(1)], axis=1) + neg2 = paddle.concat([neg.unsqueeze(1), neg.unsqueeze(1)], axis=1) + + n_pos = pos.astype("float32").sum() + + if n_pos.item() > 0: + loss_pos = F.cross_entropy( + predict.masked_select(pos2).reshape([-1, 2]), + target.masked_select(pos).astype("int64"), + reduction="sum", + ) + loss_neg = F.cross_entropy( + predict.masked_select(neg2).reshape([-1, 2]), + target.masked_select(neg).astype("int64"), + reduction="none", + ) + n_neg = min( + int(neg.astype("float32").sum().item()), + int(self.ohem_ratio * n_pos.astype("float32")), + ) + else: + loss_pos = paddle.to_tensor(0.0) + loss_neg = F.cross_entropy( + predict.masked_select(neg2).reshape([-1, 2]), + target.masked_select(neg).astype("int64"), + reduction="none", + ) + n_neg = 100 + if len(loss_neg) > n_neg: + loss_neg, _ = paddle.topk(loss_neg, n_neg) + + return (loss_pos + loss_neg.sum()) / (n_pos + n_neg).astype("float32") + + def fourier2poly(self, real_maps, imag_maps): + """Transform Fourier coefficient maps to polygon maps. + + Args: + real_maps (tensor): A map composed of the real parts of the + Fourier coefficients, whose shape is (-1, 2k+1) + imag_maps (tensor):A map composed of the imag parts of the + Fourier coefficients, whose shape is (-1, 2k+1) + + Returns + x_maps (tensor): A map composed of the x value of the polygon + represented by n sample points (xn, yn), whose shape is (-1, n) + y_maps (tensor): A map composed of the y value of the polygon + represented by n sample points (xn, yn), whose shape is (-1, n) + """ + + k_vect = paddle.arange( + -self.fourier_degree, self.fourier_degree + 1, dtype="float32" + ).reshape([-1, 1]) + i_vect = paddle.arange(0, self.num_sample, dtype="float32").reshape([1, -1]) + + transform_matrix = 2 * np.pi / self.num_sample * paddle.matmul(k_vect, i_vect) + + x1 = paddle.einsum("ak, kn-> an", real_maps, paddle.cos(transform_matrix)) + x2 = paddle.einsum("ak, kn-> an", imag_maps, paddle.sin(transform_matrix)) + y1 = paddle.einsum("ak, kn-> an", real_maps, paddle.sin(transform_matrix)) + y2 = paddle.einsum("ak, kn-> an", imag_maps, paddle.cos(transform_matrix)) + + x_maps = x1 - x2 + y_maps = y1 + y2 + + return x_maps, y_maps diff --git a/modules/onnx_ocr_module/src/ppocr/losses/det_pse_loss.py b/modules/onnx_ocr_module/src/ppocr/losses/det_pse_loss.py new file mode 100644 index 0000000..962339b --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/losses/det_pse_loss.py @@ -0,0 +1,158 @@ +# copyright (c) 2021 PaddlePaddle Authors. All Rights Reserve. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +""" +This code is refer from: +https://github.com/whai362/PSENet/blob/python3/models/head/psenet_head.py +""" + +import paddle +from paddle import nn +from paddle.nn import functional as F +import numpy as np +from ppocr.utils.iou import iou + + +class PSELoss(nn.Layer): + def __init__( + self, + alpha, + ohem_ratio=3, + kernel_sample_mask="pred", + reduction="sum", + eps=1e-6, + **kwargs, + ): + """Implement PSE Loss.""" + super(PSELoss, self).__init__() + assert reduction in ["sum", "mean", "none"] + self.alpha = alpha + self.ohem_ratio = ohem_ratio + self.kernel_sample_mask = kernel_sample_mask + self.reduction = reduction + self.eps = eps + + def forward(self, outputs, labels): + predicts = outputs["maps"] + predicts = F.interpolate(predicts, scale_factor=4) + + texts = predicts[:, 0, :, :] + kernels = predicts[:, 1:, :, :] + gt_texts, gt_kernels, training_masks = labels[1:] + + # text loss + selected_masks = self.ohem_batch(texts, gt_texts, training_masks) + + loss_text = self.dice_loss(texts, gt_texts, selected_masks) + iou_text = iou( + (texts > 0).astype("int64"), gt_texts, training_masks, reduce=False + ) + losses = dict(loss_text=loss_text, iou_text=iou_text) + + # kernel loss + loss_kernels = [] + if self.kernel_sample_mask == "gt": + selected_masks = gt_texts * training_masks + elif self.kernel_sample_mask == "pred": + selected_masks = (F.sigmoid(texts) > 0.5).astype("float32") * training_masks + + for i in range(kernels.shape[1]): + kernel_i = kernels[:, i, :, :] + gt_kernel_i = gt_kernels[:, i, :, :] + loss_kernel_i = self.dice_loss(kernel_i, gt_kernel_i, selected_masks) + loss_kernels.append(loss_kernel_i) + loss_kernels = paddle.mean(paddle.stack(loss_kernels, axis=1), axis=1) + iou_kernel = iou( + (kernels[:, -1, :, :] > 0).astype("int64"), + gt_kernels[:, -1, :, :], + training_masks * gt_texts, + reduce=False, + ) + losses.update(dict(loss_kernels=loss_kernels, iou_kernel=iou_kernel)) + loss = self.alpha * loss_text + (1 - self.alpha) * loss_kernels + losses["loss"] = loss + if self.reduction == "sum": + losses = {x: paddle.sum(v) for x, v in losses.items()} + elif self.reduction == "mean": + losses = {x: paddle.mean(v) for x, v in losses.items()} + return losses + + def dice_loss(self, input, target, mask): + input = F.sigmoid(input) + + input = input.reshape([input.shape[0], -1]) + target = target.reshape([target.shape[0], -1]) + mask = mask.reshape([mask.shape[0], -1]) + + input = input * mask + target = target * mask + + a = paddle.sum(input * target, 1) + b = paddle.sum(input * input, 1) + self.eps + c = paddle.sum(target * target, 1) + self.eps + d = (2 * a) / (b + c) + return 1 - d + + def ohem_single(self, score, gt_text, training_mask, ohem_ratio=3): + pos_num = int(paddle.sum((gt_text > 0.5).astype("float32"))) - int( + paddle.sum( + paddle.logical_and((gt_text > 0.5), (training_mask <= 0.5)).astype( + "float32" + ) + ) + ) + + if pos_num == 0: + selected_mask = training_mask + selected_mask = selected_mask.reshape( + [1, selected_mask.shape[0], selected_mask.shape[1]] + ).astype("float32") + return selected_mask + + neg_num = int(paddle.sum((gt_text <= 0.5).astype("float32"))) + neg_num = int(min(pos_num * ohem_ratio, neg_num)) + + if neg_num == 0: + selected_mask = training_mask + selected_mask = selected_mask.reshape( + [1, selected_mask.shape[0], selected_mask.shape[1]] + ).astype("float32") + return selected_mask + + neg_score = paddle.masked_select(score, gt_text <= 0.5) + neg_score_sorted = paddle.sort(-neg_score) + threshold = -neg_score_sorted[neg_num - 1] + + selected_mask = paddle.logical_and( + paddle.logical_or((score >= threshold), (gt_text > 0.5)), + (training_mask > 0.5), + ) + selected_mask = selected_mask.reshape( + [1, selected_mask.shape[0], selected_mask.shape[1]] + ).astype("float32") + return selected_mask + + def ohem_batch(self, scores, gt_texts, training_masks, ohem_ratio=3): + selected_masks = [] + for i in range(scores.shape[0]): + selected_masks.append( + self.ohem_single( + scores[i, :, :], + gt_texts[i, :, :], + training_masks[i, :, :], + ohem_ratio, + ) + ) + + selected_masks = paddle.concat(selected_masks, 0).astype("float32") + return selected_masks diff --git a/modules/onnx_ocr_module/src/ppocr/losses/det_sast_loss.py b/modules/onnx_ocr_module/src/ppocr/losses/det_sast_loss.py new file mode 100644 index 0000000..160eb06 --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/losses/det_sast_loss.py @@ -0,0 +1,133 @@ +# copyright (c) 2019 PaddlePaddle Authors. All Rights Reserve. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import paddle +from paddle import nn +from .det_basic_loss import DiceLoss +import numpy as np + + +class SASTLoss(nn.Layer): + """ """ + + def __init__(self, eps=1e-6, **kwargs): + super(SASTLoss, self).__init__() + self.dice_loss = DiceLoss(eps=eps) + + def forward(self, predicts, labels): + """ + tcl_pos: N x 128 x 3 + tcl_mask: N x 128 x 1 + tcl_label: N x X list or LoDTensor + """ + + f_score = predicts["f_score"] + f_border = predicts["f_border"] + f_tvo = predicts["f_tvo"] + f_tco = predicts["f_tco"] + + l_score, l_border, l_mask, l_tvo, l_tco = labels[1:] + + # score_loss + intersection = paddle.sum(f_score * l_score * l_mask) + union = paddle.sum(f_score * l_mask) + paddle.sum(l_score * l_mask) + score_loss = 1.0 - 2 * intersection / (union + 1e-5) + + # border loss + l_border_split, l_border_norm = paddle.split( + l_border, num_or_sections=[4, 1], axis=1 + ) + f_border_split = f_border + border_ex_shape = l_border_norm.shape * np.array([1, 4, 1, 1]) + l_border_norm_split = paddle.expand(x=l_border_norm, shape=border_ex_shape) + l_border_score = paddle.expand(x=l_score, shape=border_ex_shape) + l_border_mask = paddle.expand(x=l_mask, shape=border_ex_shape) + + border_diff = l_border_split - f_border_split + abs_border_diff = paddle.abs(border_diff) + border_sign = abs_border_diff < 1.0 + border_sign = paddle.cast(border_sign, dtype="float32") + border_sign.stop_gradient = True + border_in_loss = 0.5 * abs_border_diff * abs_border_diff * border_sign + ( + abs_border_diff - 0.5 + ) * (1.0 - border_sign) + border_out_loss = l_border_norm_split * border_in_loss + border_loss = paddle.sum(border_out_loss * l_border_score * l_border_mask) / ( + paddle.sum(l_border_score * l_border_mask) + 1e-5 + ) + + # tvo_loss + l_tvo_split, l_tvo_norm = paddle.split(l_tvo, num_or_sections=[8, 1], axis=1) + f_tvo_split = f_tvo + tvo_ex_shape = l_tvo_norm.shape * np.array([1, 8, 1, 1]) + l_tvo_norm_split = paddle.expand(x=l_tvo_norm, shape=tvo_ex_shape) + l_tvo_score = paddle.expand(x=l_score, shape=tvo_ex_shape) + l_tvo_mask = paddle.expand(x=l_mask, shape=tvo_ex_shape) + # + tvo_geo_diff = l_tvo_split - f_tvo_split + abs_tvo_geo_diff = paddle.abs(tvo_geo_diff) + tvo_sign = abs_tvo_geo_diff < 1.0 + tvo_sign = paddle.cast(tvo_sign, dtype="float32") + tvo_sign.stop_gradient = True + tvo_in_loss = 0.5 * abs_tvo_geo_diff * abs_tvo_geo_diff * tvo_sign + ( + abs_tvo_geo_diff - 0.5 + ) * (1.0 - tvo_sign) + tvo_out_loss = l_tvo_norm_split * tvo_in_loss + tvo_loss = paddle.sum(tvo_out_loss * l_tvo_score * l_tvo_mask) / ( + paddle.sum(l_tvo_score * l_tvo_mask) + 1e-5 + ) + + # tco_loss + l_tco_split, l_tco_norm = paddle.split(l_tco, num_or_sections=[2, 1], axis=1) + f_tco_split = f_tco + tco_ex_shape = l_tco_norm.shape * np.array([1, 2, 1, 1]) + l_tco_norm_split = paddle.expand(x=l_tco_norm, shape=tco_ex_shape) + l_tco_score = paddle.expand(x=l_score, shape=tco_ex_shape) + l_tco_mask = paddle.expand(x=l_mask, shape=tco_ex_shape) + + tco_geo_diff = l_tco_split - f_tco_split + abs_tco_geo_diff = paddle.abs(tco_geo_diff) + tco_sign = abs_tco_geo_diff < 1.0 + tco_sign = paddle.cast(tco_sign, dtype="float32") + tco_sign.stop_gradient = True + tco_in_loss = 0.5 * abs_tco_geo_diff * abs_tco_geo_diff * tco_sign + ( + abs_tco_geo_diff - 0.5 + ) * (1.0 - tco_sign) + tco_out_loss = l_tco_norm_split * tco_in_loss + tco_loss = paddle.sum(tco_out_loss * l_tco_score * l_tco_mask) / ( + paddle.sum(l_tco_score * l_tco_mask) + 1e-5 + ) + + # total loss + tvo_lw, tco_lw = 1.5, 1.5 + score_lw, border_lw = 1.0, 1.0 + total_loss = ( + score_loss * score_lw + + border_loss * border_lw + + tvo_loss * tvo_lw + + tco_loss * tco_lw + ) + + losses = { + "loss": total_loss, + "score_loss": score_loss, + "border_loss": border_loss, + "tvo_loss": tvo_loss, + "tco_loss": tco_loss, + } + return losses diff --git a/modules/onnx_ocr_module/src/ppocr/losses/distillation_loss.py b/modules/onnx_ocr_module/src/ppocr/losses/distillation_loss.py new file mode 100644 index 0000000..5b69e1d --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/losses/distillation_loss.py @@ -0,0 +1,1192 @@ +# copyright (c) 2021 PaddlePaddle Authors. All Rights Reserve. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import paddle +import paddle.nn as nn +import paddle.nn.functional as F +import numpy as np +import cv2 + +from .rec_ctc_loss import CTCLoss +from .rec_sar_loss import SARLoss +from .rec_ce_loss import CELoss +from .basic_loss import DMLLoss, KLDivLoss, DKDLoss +from .basic_loss import DistanceLoss +from .basic_loss import LossFromOutput +from .det_db_loss import DBLoss +from .det_basic_loss import BalanceLoss, MaskL1Loss, DiceLoss +from .vqa_token_layoutlm_loss import VQASerTokenLayoutLMLoss + + +def _sum_loss(loss_dict): + if "loss" in loss_dict.keys(): + return loss_dict + else: + loss_dict["loss"] = 0.0 + for k, value in loss_dict.items(): + if k == "loss": + continue + else: + loss_dict["loss"] += value + return loss_dict + + +class DistillationDMLLoss(DMLLoss): + """ """ + + def __init__( + self, + model_name_pairs=[], + act=None, + use_log=False, + key=None, + multi_head=False, + dis_head="ctc", + maps_name=None, + name="dml", + ): + super().__init__(act=act, use_log=use_log) + assert isinstance(model_name_pairs, list) + self.key = key + self.multi_head = multi_head + self.dis_head = dis_head + self.model_name_pairs = self._check_model_name_pairs(model_name_pairs) + self.name = name + self.maps_name = self._check_maps_name(maps_name) + + def _check_model_name_pairs(self, model_name_pairs): + if not isinstance(model_name_pairs, list): + return [] + elif isinstance(model_name_pairs[0], list) and isinstance( + model_name_pairs[0][0], str + ): + return model_name_pairs + else: + return [model_name_pairs] + + def _check_maps_name(self, maps_name): + if maps_name is None: + return None + elif isinstance(maps_name, str): + return [maps_name] + elif isinstance(maps_name, list): + return [maps_name] + else: + return None + + def _slice_out(self, outs): + new_outs = {} + for k in self.maps_name: + if k == "thrink_maps": + new_outs[k] = outs[:, 0, :, :] + elif k == "threshold_maps": + new_outs[k] = outs[:, 1, :, :] + elif k == "binary_maps": + new_outs[k] = outs[:, 2, :, :] + else: + continue + return new_outs + + def forward(self, predicts, batch): + loss_dict = dict() + for idx, pair in enumerate(self.model_name_pairs): + out1 = predicts[pair[0]] + out2 = predicts[pair[1]] + if self.key is not None: + out1 = out1[self.key] + out2 = out2[self.key] + if self.maps_name is None: + if self.multi_head: + loss = super().forward(out1[self.dis_head], out2[self.dis_head]) + else: + loss = super().forward(out1, out2) + if isinstance(loss, dict): + for key in loss: + loss_dict["{}_{}_{}_{}".format(key, pair[0], pair[1], idx)] = ( + loss[key] + ) + else: + loss_dict["{}_{}".format(self.name, idx)] = loss + else: + outs1 = self._slice_out(out1) + outs2 = self._slice_out(out2) + for _c, k in enumerate(outs1.keys()): + loss = super().forward(outs1[k], outs2[k]) + if isinstance(loss, dict): + for key in loss: + loss_dict[ + "{}_{}_{}_{}_{}".format( + key, pair[0], pair[1], self.maps_name, idx + ) + ] = loss[key] + else: + loss_dict[ + "{}_{}_{}".format(self.name, self.maps_name[_c], idx) + ] = loss + + loss_dict = _sum_loss(loss_dict) + + return loss_dict + + +class DistillationKLDivLoss(KLDivLoss): + """ """ + + def __init__( + self, + model_name_pairs=[], + key=None, + multi_head=False, + dis_head="ctc", + maps_name=None, + name="kl_div", + ): + super().__init__() + assert isinstance(model_name_pairs, list) + self.key = key + self.multi_head = multi_head + self.dis_head = dis_head + self.model_name_pairs = self._check_model_name_pairs(model_name_pairs) + self.name = name + self.maps_name = self._check_maps_name(maps_name) + + def _check_model_name_pairs(self, model_name_pairs): + if not isinstance(model_name_pairs, list): + return [] + elif isinstance(model_name_pairs[0], list) and isinstance( + model_name_pairs[0][0], str + ): + return model_name_pairs + else: + return [model_name_pairs] + + def _check_maps_name(self, maps_name): + if maps_name is None: + return None + elif isinstance(maps_name, str): + return [maps_name] + elif isinstance(maps_name, list): + return [maps_name] + else: + return None + + def _slice_out(self, outs): + new_outs = {} + for k in self.maps_name: + if k == "thrink_maps": + new_outs[k] = outs[:, 0, :, :] + elif k == "threshold_maps": + new_outs[k] = outs[:, 1, :, :] + elif k == "binary_maps": + new_outs[k] = outs[:, 2, :, :] + else: + continue + return new_outs + + def forward(self, predicts, batch): + loss_dict = dict() + for idx, pair in enumerate(self.model_name_pairs): + out1 = predicts[pair[0]] + out2 = predicts[pair[1]] + if self.key is not None: + out1 = out1[self.key] + out2 = out2[self.key] + if self.maps_name is None: + if self.multi_head: + # for nrtr dml loss + max_len = batch[3].max() + tgt = batch[2][:, 1 : 2 + max_len] + tgt = tgt.reshape([-1]) + non_pad_mask = paddle.not_equal( + tgt, paddle.zeros(tgt.shape, dtype=tgt.dtype) + ) + loss = super().forward( + out1[self.dis_head], out2[self.dis_head], non_pad_mask + ) + else: + loss = super().forward(out1, out2) + if isinstance(loss, dict): + for key in loss: + loss_dict["{}_{}_{}_{}".format(key, pair[0], pair[1], idx)] = ( + loss[key] + ) + else: + loss_dict["{}_{}".format(self.name, idx)] = loss + else: + outs1 = self._slice_out(out1) + outs2 = self._slice_out(out2) + for _c, k in enumerate(outs1.keys()): + loss = super().forward(outs1[k], outs2[k]) + if isinstance(loss, dict): + for key in loss: + loss_dict[ + "{}_{}_{}_{}_{}".format( + key, pair[0], pair[1], self.maps_name, idx + ) + ] = loss[key] + else: + loss_dict[ + "{}_{}_{}".format(self.name, self.maps_name[_c], idx) + ] = loss + + loss_dict = _sum_loss(loss_dict) + + return loss_dict + + +class DistillationDKDLoss(DKDLoss): + """ """ + + def __init__( + self, + model_name_pairs=[], + key=None, + multi_head=False, + dis_head="ctc", + maps_name=None, + name="dkd", + temperature=1.0, + alpha=1.0, + beta=1.0, + ): + super().__init__(temperature, alpha, beta) + assert isinstance(model_name_pairs, list) + self.key = key + self.multi_head = multi_head + self.dis_head = dis_head + self.model_name_pairs = self._check_model_name_pairs(model_name_pairs) + self.name = name + self.maps_name = self._check_maps_name(maps_name) + + def _check_model_name_pairs(self, model_name_pairs): + if not isinstance(model_name_pairs, list): + return [] + elif isinstance(model_name_pairs[0], list) and isinstance( + model_name_pairs[0][0], str + ): + return model_name_pairs + else: + return [model_name_pairs] + + def _check_maps_name(self, maps_name): + if maps_name is None: + return None + elif isinstance(maps_name, str): + return [maps_name] + elif isinstance(maps_name, list): + return [maps_name] + else: + return None + + def _slice_out(self, outs): + new_outs = {} + for k in self.maps_name: + if k == "thrink_maps": + new_outs[k] = outs[:, 0, :, :] + elif k == "threshold_maps": + new_outs[k] = outs[:, 1, :, :] + elif k == "binary_maps": + new_outs[k] = outs[:, 2, :, :] + else: + continue + return new_outs + + def forward(self, predicts, batch): + loss_dict = dict() + + for idx, pair in enumerate(self.model_name_pairs): + out1 = predicts[pair[0]] + out2 = predicts[pair[1]] + if self.key is not None: + out1 = out1[self.key] + out2 = out2[self.key] + if self.maps_name is None: + if self.multi_head: + # for nrtr dml loss + max_len = batch[3].max() + tgt = batch[2][:, 1 : 2 + max_len] # [batch_size, max_len + 1] + + tgt = tgt.reshape([-1]) # batch_size * (max_len + 1) + non_pad_mask = paddle.not_equal( + tgt, paddle.zeros(tgt.shape, dtype=tgt.dtype) + ) # batch_size * (max_len + 1) + + loss = super().forward( + out1[self.dis_head], out2[self.dis_head], tgt, non_pad_mask + ) # [batch_size, max_len + 1, num_char] + else: + loss = super().forward(out1, out2) + if isinstance(loss, dict): + for key in loss: + loss_dict["{}_{}_{}_{}".format(key, pair[0], pair[1], idx)] = ( + loss[key] + ) + else: + loss_dict["{}_{}".format(self.name, idx)] = loss + else: + outs1 = self._slice_out(out1) + outs2 = self._slice_out(out2) + for _c, k in enumerate(outs1.keys()): + loss = super().forward(outs1[k], outs2[k]) + if isinstance(loss, dict): + for key in loss: + loss_dict[ + "{}_{}_{}_{}_{}".format( + key, pair[0], pair[1], self.maps_name, idx + ) + ] = loss[key] + else: + loss_dict[ + "{}_{}_{}".format(self.name, self.maps_name[_c], idx) + ] = loss + + loss_dict = _sum_loss(loss_dict) + + return loss_dict + + +class DistillationNRTRDMLLoss(DistillationDMLLoss): + """ """ + + def forward(self, predicts, batch): + loss_dict = dict() + for idx, pair in enumerate(self.model_name_pairs): + out1 = predicts[pair[0]] + out2 = predicts[pair[1]] + if self.key is not None: + out1 = out1[self.key] + out2 = out2[self.key] + + if self.multi_head: + # for nrtr dml loss + max_len = batch[3].max() + tgt = batch[2][:, 1 : 2 + max_len] + tgt = tgt.reshape([-1]) + non_pad_mask = paddle.not_equal( + tgt, paddle.zeros(tgt.shape, dtype=tgt.dtype) + ) + loss = super().forward( + out1[self.dis_head], out2[self.dis_head], non_pad_mask + ) + else: + loss = super().forward(out1, out2) + if isinstance(loss, dict): + for key in loss: + loss_dict["{}_{}_{}_{}".format(key, pair[0], pair[1], idx)] = loss[ + key + ] + else: + loss_dict["{}_{}".format(self.name, idx)] = loss + + loss_dict = _sum_loss(loss_dict) + + return loss_dict + + +class DistillationKLDivLoss(KLDivLoss): + """ """ + + def __init__( + self, + model_name_pairs=[], + key=None, + multi_head=False, + dis_head="ctc", + maps_name=None, + name="kl_div", + ): + super().__init__() + assert isinstance(model_name_pairs, list) + self.key = key + self.multi_head = multi_head + self.dis_head = dis_head + self.model_name_pairs = self._check_model_name_pairs(model_name_pairs) + self.name = name + self.maps_name = self._check_maps_name(maps_name) + + def _check_model_name_pairs(self, model_name_pairs): + if not isinstance(model_name_pairs, list): + return [] + elif isinstance(model_name_pairs[0], list) and isinstance( + model_name_pairs[0][0], str + ): + return model_name_pairs + else: + return [model_name_pairs] + + def _check_maps_name(self, maps_name): + if maps_name is None: + return None + elif isinstance(maps_name, str): + return [maps_name] + elif isinstance(maps_name, list): + return [maps_name] + else: + return None + + def _slice_out(self, outs): + new_outs = {} + for k in self.maps_name: + if k == "thrink_maps": + new_outs[k] = outs[:, 0, :, :] + elif k == "threshold_maps": + new_outs[k] = outs[:, 1, :, :] + elif k == "binary_maps": + new_outs[k] = outs[:, 2, :, :] + else: + continue + return new_outs + + def forward(self, predicts, batch): + loss_dict = dict() + for idx, pair in enumerate(self.model_name_pairs): + out1 = predicts[pair[0]] + out2 = predicts[pair[1]] + if self.key is not None: + out1 = out1[self.key] + out2 = out2[self.key] + if self.maps_name is None: + if self.multi_head: + # for nrtr dml loss + max_len = batch[3].max() + tgt = batch[2][:, 1 : 2 + max_len] + tgt = tgt.reshape([-1]) + non_pad_mask = paddle.not_equal( + tgt, paddle.zeros(tgt.shape, dtype=tgt.dtype) + ) + loss = super().forward( + out1[self.dis_head], out2[self.dis_head], non_pad_mask + ) + else: + loss = super().forward(out1, out2) + if isinstance(loss, dict): + for key in loss: + loss_dict["{}_{}_{}_{}".format(key, pair[0], pair[1], idx)] = ( + loss[key] + ) + else: + loss_dict["{}_{}".format(self.name, idx)] = loss + else: + outs1 = self._slice_out(out1) + outs2 = self._slice_out(out2) + for _c, k in enumerate(outs1.keys()): + loss = super().forward(outs1[k], outs2[k]) + if isinstance(loss, dict): + for key in loss: + loss_dict[ + "{}_{}_{}_{}_{}".format( + key, pair[0], pair[1], self.maps_name, idx + ) + ] = loss[key] + else: + loss_dict[ + "{}_{}_{}".format(self.name, self.maps_name[_c], idx) + ] = loss + + loss_dict = _sum_loss(loss_dict) + + return loss_dict + + +class DistillationDKDLoss(DKDLoss): + """ """ + + def __init__( + self, + model_name_pairs=[], + key=None, + multi_head=False, + dis_head="ctc", + maps_name=None, + name="dkd", + temperature=1.0, + alpha=1.0, + beta=1.0, + ): + super().__init__(temperature, alpha, beta) + assert isinstance(model_name_pairs, list) + self.key = key + self.multi_head = multi_head + self.dis_head = dis_head + self.model_name_pairs = self._check_model_name_pairs(model_name_pairs) + self.name = name + self.maps_name = self._check_maps_name(maps_name) + + def _check_model_name_pairs(self, model_name_pairs): + if not isinstance(model_name_pairs, list): + return [] + elif isinstance(model_name_pairs[0], list) and isinstance( + model_name_pairs[0][0], str + ): + return model_name_pairs + else: + return [model_name_pairs] + + def _check_maps_name(self, maps_name): + if maps_name is None: + return None + elif isinstance(maps_name, str): + return [maps_name] + elif isinstance(maps_name, list): + return [maps_name] + else: + return None + + def _slice_out(self, outs): + new_outs = {} + for k in self.maps_name: + if k == "thrink_maps": + new_outs[k] = outs[:, 0, :, :] + elif k == "threshold_maps": + new_outs[k] = outs[:, 1, :, :] + elif k == "binary_maps": + new_outs[k] = outs[:, 2, :, :] + else: + continue + return new_outs + + def forward(self, predicts, batch): + loss_dict = dict() + + for idx, pair in enumerate(self.model_name_pairs): + out1 = predicts[pair[0]] + out2 = predicts[pair[1]] + if self.key is not None: + out1 = out1[self.key] + out2 = out2[self.key] + if self.maps_name is None: + if self.multi_head: + # for nrtr dml loss + max_len = batch[3].max() + tgt = batch[2][:, 1 : 2 + max_len] # [batch_size, max_len + 1] + + tgt = tgt.reshape([-1]) # batch_size * (max_len + 1) + non_pad_mask = paddle.not_equal( + tgt, paddle.zeros(tgt.shape, dtype=tgt.dtype) + ) # batch_size * (max_len + 1) + + loss = super().forward( + out1[self.dis_head], out2[self.dis_head], tgt, non_pad_mask + ) # [batch_size, max_len + 1, num_char] + else: + loss = super().forward(out1, out2) + if isinstance(loss, dict): + for key in loss: + loss_dict["{}_{}_{}_{}".format(key, pair[0], pair[1], idx)] = ( + loss[key] + ) + else: + loss_dict["{}_{}".format(self.name, idx)] = loss + else: + outs1 = self._slice_out(out1) + outs2 = self._slice_out(out2) + for _c, k in enumerate(outs1.keys()): + loss = super().forward(outs1[k], outs2[k]) + if isinstance(loss, dict): + for key in loss: + loss_dict[ + "{}_{}_{}_{}_{}".format( + key, pair[0], pair[1], self.maps_name, idx + ) + ] = loss[key] + else: + loss_dict[ + "{}_{}_{}".format(self.name, self.maps_name[_c], idx) + ] = loss + + loss_dict = _sum_loss(loss_dict) + + return loss_dict + + +class DistillationCTCLoss(CTCLoss): + def __init__(self, model_name_list=[], key=None, multi_head=False, name="loss_ctc"): + super().__init__() + self.model_name_list = model_name_list + self.key = key + self.name = name + self.multi_head = multi_head + + def forward(self, predicts, batch): + loss_dict = dict() + for idx, model_name in enumerate(self.model_name_list): + out = predicts[model_name] + if self.key is not None: + out = out[self.key] + if self.multi_head: + assert "ctc" in out, "multi head has multi out" + loss = super().forward(out["ctc"], batch[:2] + batch[3:]) + else: + loss = super().forward(out, batch) + if isinstance(loss, dict): + for key in loss: + loss_dict["{}_{}_{}".format(self.name, model_name, idx)] = loss[key] + else: + loss_dict["{}_{}".format(self.name, model_name)] = loss + return loss_dict + + +class DistillationSARLoss(SARLoss): + def __init__( + self, model_name_list=[], key=None, multi_head=False, name="loss_sar", **kwargs + ): + ignore_index = kwargs.get("ignore_index", 92) + super().__init__(ignore_index=ignore_index) + self.model_name_list = model_name_list + self.key = key + self.name = name + self.multi_head = multi_head + + def forward(self, predicts, batch): + loss_dict = dict() + for idx, model_name in enumerate(self.model_name_list): + out = predicts[model_name] + if self.key is not None: + out = out[self.key] + if self.multi_head: + assert "sar" in out, "multi head has multi out" + loss = super().forward(out["sar"], batch[:1] + batch[2:]) + else: + loss = super().forward(out, batch) + if isinstance(loss, dict): + for key in loss: + loss_dict["{}_{}_{}".format(self.name, model_name, idx)] = loss[key] + else: + loss_dict["{}_{}".format(self.name, model_name)] = loss + return loss_dict + + +class DistillationNRTRLoss(CELoss): + def __init__( + self, + model_name_list=[], + key=None, + multi_head=False, + smoothing=True, + name="loss_nrtr", + **kwargs, + ): + super().__init__(smoothing=smoothing) + self.model_name_list = model_name_list + self.key = key + self.name = name + self.multi_head = multi_head + + def forward(self, predicts, batch): + loss_dict = dict() + for idx, model_name in enumerate(self.model_name_list): + out = predicts[model_name] + if self.key is not None: + out = out[self.key] + if self.multi_head: + assert "gtc" in out, "multi head has multi out" + loss = super().forward(out["gtc"], batch[:1] + batch[2:]) + else: + loss = super().forward(out, batch) + if isinstance(loss, dict): + for key in loss: + loss_dict["{}_{}_{}".format(self.name, model_name, idx)] = loss[key] + else: + loss_dict["{}_{}".format(self.name, model_name)] = loss + return loss_dict + + +class DistillationDBLoss(DBLoss): + def __init__( + self, + model_name_list=[], + balance_loss=True, + main_loss_type="DiceLoss", + alpha=5, + beta=10, + ohem_ratio=3, + eps=1e-6, + name="db", + **kwargs, + ): + super().__init__() + self.model_name_list = model_name_list + self.name = name + self.key = None + + def forward(self, predicts, batch): + loss_dict = {} + for idx, model_name in enumerate(self.model_name_list): + out = predicts[model_name] + if self.key is not None: + out = out[self.key] + loss = super().forward(out, batch) + + if isinstance(loss, dict): + for key in loss.keys(): + if key == "loss": + continue + name = "{}_{}_{}".format(self.name, model_name, key) + loss_dict[name] = loss[key] + else: + loss_dict["{}_{}".format(self.name, model_name)] = loss + + loss_dict = _sum_loss(loss_dict) + return loss_dict + + +class DistillationDilaDBLoss(DBLoss): + def __init__( + self, + model_name_pairs=[], + key=None, + balance_loss=True, + main_loss_type="DiceLoss", + alpha=5, + beta=10, + ohem_ratio=3, + eps=1e-6, + name="dila_dbloss", + ): + super().__init__() + self.model_name_pairs = model_name_pairs + self.name = name + self.key = key + + def forward(self, predicts, batch): + loss_dict = dict() + for idx, pair in enumerate(self.model_name_pairs): + stu_outs = predicts[pair[0]] + tch_outs = predicts[pair[1]] + if self.key is not None: + stu_preds = stu_outs[self.key] + tch_preds = tch_outs[self.key] + + stu_shrink_maps = stu_preds[:, 0, :, :] + stu_binary_maps = stu_preds[:, 2, :, :] + + # dilation to teacher prediction + dilation_w = np.array([[1, 1], [1, 1]]) + th_shrink_maps = tch_preds[:, 0, :, :] + if hasattr(paddle.Tensor, "contiguous"): + th_shrink_maps = th_shrink_maps.contiguous() + th_shrink_maps = th_shrink_maps.numpy() > 0.3 # thresh = 0.3 + dilate_maps = np.zeros_like(th_shrink_maps).astype(np.float32) + for i in range(th_shrink_maps.shape[0]): + dilate_maps[i] = cv2.dilate( + th_shrink_maps[i, :, :].astype(np.uint8), dilation_w + ) + th_shrink_maps = paddle.to_tensor(dilate_maps) + + ( + label_threshold_map, + label_threshold_mask, + label_shrink_map, + label_shrink_mask, + ) = batch[1:] + + # calculate the shrink map loss + bce_loss = self.alpha * self.bce_loss( + stu_shrink_maps, th_shrink_maps, label_shrink_mask + ) + loss_binary_maps = self.dice_loss( + stu_binary_maps, th_shrink_maps, label_shrink_mask + ) + + # k = f"{self.name}_{pair[0]}_{pair[1]}" + k = "{}_{}_{}".format(self.name, pair[0], pair[1]) + loss_dict[k] = bce_loss + loss_binary_maps + + loss_dict = _sum_loss(loss_dict) + return loss_dict + + +class DistillationDistanceLoss(DistanceLoss): + """ """ + + def __init__( + self, mode="l2", model_name_pairs=[], key=None, name="loss_distance", **kargs + ): + super().__init__(mode=mode, **kargs) + assert isinstance(model_name_pairs, list) + self.key = key + self.model_name_pairs = model_name_pairs + self.name = name + "_l2" + + def forward(self, predicts, batch): + loss_dict = dict() + for idx, pair in enumerate(self.model_name_pairs): + out1 = predicts[pair[0]] + out2 = predicts[pair[1]] + if self.key is not None: + out1 = out1[self.key] + out2 = out2[self.key] + loss = super().forward(out1, out2) + if isinstance(loss, dict): + for key in loss: + loss_dict["{}_{}_{}".format(self.name, key, idx)] = loss[key] + else: + loss_dict["{}_{}_{}_{}".format(self.name, pair[0], pair[1], idx)] = loss + return loss_dict + + +class DistillationVQASerTokenLayoutLMLoss(VQASerTokenLayoutLMLoss): + def __init__(self, num_classes, model_name_list=[], key=None, name="loss_ser"): + super().__init__(num_classes=num_classes) + self.model_name_list = model_name_list + self.key = key + self.name = name + + def forward(self, predicts, batch): + loss_dict = dict() + for idx, model_name in enumerate(self.model_name_list): + out = predicts[model_name] + if self.key is not None: + out = out[self.key] + loss = super().forward(out, batch) + loss_dict["{}_{}".format(self.name, model_name)] = loss["loss"] + return loss_dict + + +class DistillationLossFromOutput(LossFromOutput): + def __init__( + self, + reduction="none", + model_name_list=[], + dist_key=None, + key="loss", + name="loss_re", + ): + super().__init__(key=key, reduction=reduction) + self.model_name_list = model_name_list + self.name = name + self.dist_key = dist_key + + def forward(self, predicts, batch): + loss_dict = dict() + for idx, model_name in enumerate(self.model_name_list): + out = predicts[model_name] + if self.dist_key is not None: + out = out[self.dist_key] + loss = super().forward(out, batch) + loss_dict["{}_{}".format(self.name, model_name)] = loss["loss"] + return loss_dict + + +class DistillationSERDMLLoss(DMLLoss): + """ """ + + def __init__( + self, + act="softmax", + use_log=True, + num_classes=7, + model_name_pairs=[], + key=None, + name="loss_dml_ser", + ): + super().__init__(act=act, use_log=use_log) + assert isinstance(model_name_pairs, list) + self.key = key + self.name = name + self.num_classes = num_classes + self.model_name_pairs = model_name_pairs + + def forward(self, predicts, batch): + loss_dict = dict() + for idx, pair in enumerate(self.model_name_pairs): + out1 = predicts[pair[0]] + out2 = predicts[pair[1]] + if self.key is not None: + out1 = out1[self.key] + out2 = out2[self.key] + out1 = out1.reshape([-1, out1.shape[-1]]) + out2 = out2.reshape([-1, out2.shape[-1]]) + + attention_mask = batch[2] + if attention_mask is not None: + active_output = ( + attention_mask.reshape( + [ + -1, + ] + ) + == 1 + ) + out1 = out1[active_output] + out2 = out2[active_output] + + loss_dict["{}_{}".format(self.name, idx)] = super().forward(out1, out2) + + return loss_dict + + +class DistillationVQADistanceLoss(DistanceLoss): + def __init__( + self, + mode="l2", + model_name_pairs=[], + key=None, + index=None, + name="loss_distance", + **kargs, + ): + super().__init__(mode=mode, **kargs) + assert isinstance(model_name_pairs, list) + self.key = key + self.index = index + self.model_name_pairs = model_name_pairs + self.name = name + "_l2" + + def forward(self, predicts, batch): + loss_dict = dict() + for idx, pair in enumerate(self.model_name_pairs): + out1 = predicts[pair[0]] + out2 = predicts[pair[1]] + attention_mask = batch[2] + if self.key is not None: + out1 = out1[self.key] + out2 = out2[self.key] + if self.index is not None: + out1 = out1[:, self.index, :, :] + out2 = out2[:, self.index, :, :] + if attention_mask is not None: + max_len = attention_mask.shape[-1] + out1 = out1[:, :max_len] + out2 = out2[:, :max_len] + out1 = out1.reshape([-1, out1.shape[-1]]) + out2 = out2.reshape([-1, out2.shape[-1]]) + if attention_mask is not None: + active_output = ( + attention_mask.reshape( + [ + -1, + ] + ) + == 1 + ) + out1 = out1[active_output] + out2 = out2[active_output] + + loss = super().forward(out1, out2) + if isinstance(loss, dict): + for key in loss: + loss_dict["{}_{}nohu_{}".format(self.name, key, idx)] = loss[key] + else: + loss_dict["{}_{}_{}_{}".format(self.name, pair[0], pair[1], idx)] = loss + return loss_dict + + +class CTCDKDLoss(nn.Layer): + """ + KLDivLoss + """ + + def __init__(self, temperature=0.5, alpha=1.0, beta=1.0): + super().__init__() + self.temperature = temperature + self.alpha = alpha + self.beta = beta + self.eps = 1e-6 + self.t = temperature + self.act = nn.Softmax(axis=-1) + self.use_log = True + + def kl_loss(self, p1, p2): # predict, label + loss = paddle.multiply( + p2, paddle.log((p2 + self.eps) / (p1 + self.eps) + self.eps) + ) + bs = loss.shape[0] + loss = paddle.sum(loss) / bs + return loss + + def _cat_mask(self, t, mask1, mask2): + t1 = (t * mask1).sum(axis=1, keepdim=True) + t2 = (t * mask2).sum(axis=1, keepdim=True) + rt = paddle.concat([t1, t2], axis=1) + return rt + + def multi_label_mask(self, targets): + targets = targets.astype("int32") + res = F.one_hot(targets, num_classes=11465) + mask = paddle.clip(paddle.sum(res, axis=1), 0, 1) + mask[:, 0] = 0 # ignore ctc blank label + return mask + + def forward(self, logits_student, logits_teacher, targets, mask=None): + gt_mask = self.multi_label_mask(targets) + other_mask = paddle.ones_like(gt_mask) - gt_mask + + pred_student = F.softmax(logits_student / self.temperature, axis=-1) + pred_teacher = F.softmax(logits_teacher / self.temperature, axis=-1) + + # differences with dkd + pred_student = paddle.mean(pred_student, axis=1) + pred_teacher = paddle.mean(pred_teacher, axis=1) + + pred_student = self._cat_mask(pred_student, gt_mask, other_mask) + pred_teacher = self._cat_mask(pred_teacher, gt_mask, other_mask) + + # differences with dkd + tckd_loss = self.kl_loss(pred_student, pred_teacher) + + gt_mask_ex = paddle.expand_as(gt_mask.unsqueeze(axis=1), logits_teacher) + pred_teacher_part2 = F.softmax( + logits_teacher / self.temperature - 1000.0 * gt_mask_ex, axis=-1 + ) + pred_student_part2 = F.softmax( + logits_student / self.temperature - 1000.0 * gt_mask_ex, axis=-1 + ) + # differences with dkd + pred_teacher_part2 = paddle.mean(pred_teacher_part2, axis=1) + pred_student_part2 = paddle.mean(pred_student_part2, axis=1) + + # differences with dkd + nckd_loss = self.kl_loss(pred_student_part2, pred_teacher_part2) + loss = self.alpha * tckd_loss + self.beta * nckd_loss + return loss + + +class KLCTCLogits(nn.Layer): + def __init__(self, weight=1.0, reduction="mean", mode="mean"): + super().__init__() + self.weight = weight + self.reduction = reduction + self.eps = 1e-6 + self.t = 0.5 + self.act = nn.Softmax(axis=-1) + self.use_log = True + self.mode = mode + self.ctc_dkd_loss = CTCDKDLoss() + + def kl_loss(self, p1, p2): # predict, label + loss = paddle.multiply( + p2, paddle.log((p2 + self.eps) / (p1 + self.eps) + self.eps) + ) + bs = loss.shape[0] + loss = paddle.sum(loss) / bs + return loss + + def forward_meanmax(self, stu_out, tea_out): + stu_out = paddle.mean(F.softmax(stu_out / self.t, axis=-1), axis=1) + tea_out = paddle.mean(F.softmax(tea_out / self.t, axis=-1), axis=1) + loss = self.kl_loss(stu_out, tea_out) + + return loss + + def forward_meanlog(self, stu_out, tea_out): + stu_out = paddle.mean(F.softmax(stu_out / self.t, axis=-1), axis=1) + tea_out = paddle.mean(F.softmax(tea_out / self.t, axis=-1), axis=1) + if self.use_log is True: + # for recognition distillation, log is needed for feature map + log_out1 = paddle.log(stu_out) + log_out2 = paddle.log(tea_out) + loss = ( + self._kldiv(log_out1, tea_out) + self._kldiv(log_out2, stu_out) + ) / 2.0 + + return loss + + def forward_sum(self, stu_out, tea_out): + stu_out = paddle.sum(F.softmax(stu_out / self.t, axis=-1), axis=1) + tea_out = paddle.sum(F.softmax(tea_out / self.t, axis=-1), axis=1) + stu_out = paddle.log(stu_out) + bs = stu_out.shape[0] + loss = tea_out * (paddle.log(tea_out + self.eps) - stu_out) + loss = paddle.sum(loss, axis=1) / loss.shape[0] + return loss + + def _kldiv(self, x, target): + eps = 1.0e-10 + loss = target * (paddle.log(target + eps) - x) + loss = paddle.sum(paddle.mean(loss, axis=1)) / loss.shape[0] + return loss + + def forward(self, stu_out, tea_out, targets=None): + if self.mode == "log": + return self.forward_log(stu_out, tea_out) + elif self.mode == "mean": + blank_mask = paddle.ones_like(stu_out) + blank_mask.stop_gradient = True + blank_mask[:, :, 0] = -1 + stu_out *= blank_mask + tea_out *= blank_mask + return self.forward_meanmax(stu_out, tea_out) + elif self.mode == "sum": + return self.forward_sum(stu_out, tea_out) + elif self.mode == "meanlog": + blank_mask = paddle.ones_like(stu_out) + blank_mask.stop_gradient = True + blank_mask[:, :, 0] = -1 + stu_out *= blank_mask + tea_out *= blank_mask + return self.forward_meanlog(stu_out, tea_out) + elif self.mode == "ctcdkd": + # ignore ctc blank logits + blank_mask = paddle.ones_like(stu_out) + blank_mask.stop_gradient = True + blank_mask[:, :, 0] = -1 + stu_out *= blank_mask + tea_out *= blank_mask + return self.ctc_dkd_loss(stu_out, tea_out, targets) + else: + raise ValueError("error!!!!!!") + + def forward_log(self, out1, out2): + if self.act is not None: + out1 = self.act(out1) + 1e-10 + out2 = self.act(out2) + 1e-10 + if self.use_log is True: + # for recognition distillation, log is needed for feature map + log_out1 = paddle.log(out1) + log_out2 = paddle.log(out2) + loss = (self._kldiv(log_out1, out2) + self._kldiv(log_out2, out1)) / 2.0 + + return loss + + +class DistillCTCLogits(KLCTCLogits): + def __init__( + self, model_name_pairs=[], key=None, name="ctc_logits", reduction="mean" + ): + super().__init__(reduction=reduction) + self.model_name_pairs = self._check_model_name_pairs(model_name_pairs) + self.key = key + self.name = name + + def _check_model_name_pairs(self, model_name_pairs): + if not isinstance(model_name_pairs, list): + return [] + elif isinstance(model_name_pairs[0], list) and isinstance( + model_name_pairs[0][0], str + ): + return model_name_pairs + else: + return [model_name_pairs] + + def forward(self, predicts, batch): + loss_dict = dict() + for idx, pair in enumerate(self.model_name_pairs): + out1 = predicts[pair[0]] + out2 = predicts[pair[1]] + + if self.key is not None: + out1 = out1[self.key]["ctc"] + out2 = out2[self.key]["ctc"] + + ctc_label = batch[1] + loss = super().forward(out1, out2, ctc_label) + if isinstance(loss, dict): + for key in loss: + loss_dict[ + "{}_{}_{}".format(self.name, self.model_name_pairs, idx) + ] = loss[key] + else: + loss_dict["{}_{}".format(self.name, idx)] = loss + return loss_dict diff --git a/modules/onnx_ocr_module/src/ppocr/losses/e2e_pg_loss.py b/modules/onnx_ocr_module/src/ppocr/losses/e2e_pg_loss.py new file mode 100644 index 0000000..8b667f0 --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/losses/e2e_pg_loss.py @@ -0,0 +1,165 @@ +# copyright (c) 2021 PaddlePaddle Authors. All Rights Reserve. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +from paddle import nn +import paddle + +from .det_basic_loss import DiceLoss +from ppocr.utils.e2e_utils.extract_batchsize import pre_process + + +class PGLoss(nn.Layer): + def __init__( + self, tcl_bs, max_text_length, max_text_nums, pad_num, eps=1e-6, **kwargs + ): + super(PGLoss, self).__init__() + self.tcl_bs = tcl_bs + self.max_text_nums = max_text_nums + self.max_text_length = max_text_length + self.pad_num = pad_num + self.dice_loss = DiceLoss(eps=eps) + + def border_loss(self, f_border, l_border, l_score, l_mask): + l_border_split, l_border_norm = paddle.tensor.split( + l_border, num_or_sections=[4, 1], axis=1 + ) + f_border_split = f_border + b, c, h, w = l_border_norm.shape + l_border_norm_split = paddle.expand(x=l_border_norm, shape=[b, 4 * c, h, w]) + b, c, h, w = l_score.shape + l_border_score = paddle.expand(x=l_score, shape=[b, 4 * c, h, w]) + b, c, h, w = l_mask.shape + l_border_mask = paddle.expand(x=l_mask, shape=[b, 4 * c, h, w]) + border_diff = l_border_split - f_border_split + abs_border_diff = paddle.abs(border_diff) + border_sign = abs_border_diff < 1.0 + border_sign = paddle.cast(border_sign, dtype="float32") + border_sign.stop_gradient = True + border_in_loss = 0.5 * abs_border_diff * abs_border_diff * border_sign + ( + abs_border_diff - 0.5 + ) * (1.0 - border_sign) + border_out_loss = l_border_norm_split * border_in_loss + border_loss = paddle.sum(border_out_loss * l_border_score * l_border_mask) / ( + paddle.sum(l_border_score * l_border_mask) + 1e-5 + ) + return border_loss + + def direction_loss(self, f_direction, l_direction, l_score, l_mask): + l_direction_split, l_direction_norm = paddle.tensor.split( + l_direction, num_or_sections=[2, 1], axis=1 + ) + f_direction_split = f_direction + b, c, h, w = l_direction_norm.shape + l_direction_norm_split = paddle.expand( + x=l_direction_norm, shape=[b, 2 * c, h, w] + ) + b, c, h, w = l_score.shape + l_direction_score = paddle.expand(x=l_score, shape=[b, 2 * c, h, w]) + b, c, h, w = l_mask.shape + l_direction_mask = paddle.expand(x=l_mask, shape=[b, 2 * c, h, w]) + direction_diff = l_direction_split - f_direction_split + abs_direction_diff = paddle.abs(direction_diff) + direction_sign = abs_direction_diff < 1.0 + direction_sign = paddle.cast(direction_sign, dtype="float32") + direction_sign.stop_gradient = True + direction_in_loss = ( + 0.5 * abs_direction_diff * abs_direction_diff * direction_sign + + (abs_direction_diff - 0.5) * (1.0 - direction_sign) + ) + direction_out_loss = l_direction_norm_split * direction_in_loss + direction_loss = paddle.sum( + direction_out_loss * l_direction_score * l_direction_mask + ) / (paddle.sum(l_direction_score * l_direction_mask) + 1e-5) + return direction_loss + + def ctcloss(self, f_char, tcl_pos, tcl_mask, tcl_label, label_t): + f_char = paddle.transpose(f_char, [0, 2, 3, 1]) + tcl_pos = paddle.reshape(tcl_pos, [-1, 3]) + tcl_pos = paddle.cast(tcl_pos, dtype=int) + f_tcl_char = paddle.gather_nd(f_char, tcl_pos) + f_tcl_char = paddle.reshape( + f_tcl_char, [-1, 64, self.pad_num + 1] + ) # len(Lexicon_Table)+1 + f_tcl_char_fg, f_tcl_char_bg = paddle.split( + f_tcl_char, [self.pad_num, 1], axis=2 + ) + f_tcl_char_bg = f_tcl_char_bg * tcl_mask + (1.0 - tcl_mask) * 20.0 + b, c, l = tcl_mask.shape + tcl_mask_fg = paddle.expand(x=tcl_mask, shape=[b, c, self.pad_num * l]) + tcl_mask_fg.stop_gradient = True + f_tcl_char_fg = f_tcl_char_fg * tcl_mask_fg + (1.0 - tcl_mask_fg) * (-20.0) + f_tcl_char_mask = paddle.concat([f_tcl_char_fg, f_tcl_char_bg], axis=2) + f_tcl_char_ld = paddle.transpose(f_tcl_char_mask, (1, 0, 2)) + N, B, _ = f_tcl_char_ld.shape + input_lengths = paddle.to_tensor([N] * B, dtype="int64") + cost = paddle.nn.functional.ctc_loss( + log_probs=f_tcl_char_ld, + labels=tcl_label, + input_lengths=input_lengths, + label_lengths=label_t, + blank=self.pad_num, + reduction="none", + ) + cost = cost.mean() + return cost + + def forward(self, predicts, labels): + ( + images, + tcl_maps, + tcl_label_maps, + border_maps, + direction_maps, + training_masks, + label_list, + pos_list, + pos_mask, + ) = labels + # for all the batch_size + pos_list, pos_mask, label_list, label_t = pre_process( + label_list, + pos_list, + pos_mask, + self.max_text_length, + self.max_text_nums, + self.pad_num, + self.tcl_bs, + ) + + f_score, f_border, f_direction, f_char = ( + predicts["f_score"], + predicts["f_border"], + predicts["f_direction"], + predicts["f_char"], + ) + score_loss = self.dice_loss(f_score, tcl_maps, training_masks) + border_loss = self.border_loss(f_border, border_maps, tcl_maps, training_masks) + direction_loss = self.direction_loss( + f_direction, direction_maps, tcl_maps, training_masks + ) + ctc_loss = self.ctcloss(f_char, pos_list, pos_mask, label_list, label_t) + loss_all = score_loss + border_loss + direction_loss + 5 * ctc_loss + + losses = { + "loss": loss_all, + "score_loss": score_loss, + "border_loss": border_loss, + "direction_loss": direction_loss, + "ctc_loss": ctc_loss, + } + return losses diff --git a/modules/onnx_ocr_module/src/ppocr/losses/kie_sdmgr_loss.py b/modules/onnx_ocr_module/src/ppocr/losses/kie_sdmgr_loss.py new file mode 100644 index 0000000..685a141 --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/losses/kie_sdmgr_loss.py @@ -0,0 +1,116 @@ +# copyright (c) 2022 PaddlePaddle Authors. All Rights Reserve. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# reference from : https://github.com/open-mmlab/mmocr/blob/main/mmocr/models/kie/losses/sdmgr_loss.py + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +from paddle import nn +import paddle + + +class SDMGRLoss(nn.Layer): + def __init__(self, node_weight=1.0, edge_weight=1.0, ignore=0): + super().__init__() + self.loss_node = nn.CrossEntropyLoss(ignore_index=ignore) + self.loss_edge = nn.CrossEntropyLoss(ignore_index=-1) + self.node_weight = node_weight + self.edge_weight = edge_weight + self.ignore = ignore + + def pre_process(self, gts, tag): + gts, tag = gts.numpy(), tag.numpy().tolist() + temp_gts = [] + batch = len(tag) + for i in range(batch): + num, recoder_len = tag[i][0], tag[i][1] + temp_gts.append(paddle.to_tensor(gts[i, :num, : num + 1], dtype="int64")) + return temp_gts + + def accuracy(self, pred, target, topk=1, thresh=None): + """Calculate accuracy according to the prediction and target. + + Args: + pred (torch.Tensor): The model prediction, shape (N, num_class) + target (torch.Tensor): The target of each prediction, shape (N, ) + topk (int | tuple[int], optional): If the predictions in ``topk`` + matches the target, the predictions will be regarded as + correct ones. Defaults to 1. + thresh (float, optional): If not None, predictions with scores under + this threshold are considered incorrect. Default to None. + + Returns: + float | tuple[float]: If the input ``topk`` is a single integer, + the function will return a single float as accuracy. If + ``topk`` is a tuple containing multiple integers, the + function will return a tuple containing accuracies of + each ``topk`` number. + """ + assert isinstance(topk, (int, tuple)) + if isinstance(topk, int): + topk = (topk,) + return_single = True + else: + return_single = False + + maxk = max(topk) + if pred.shape[0] == 0: + accu = [pred.new_tensor(0.0) for i in range(len(topk))] + return accu[0] if return_single else accu + pred_value, pred_label = paddle.topk(pred, maxk, axis=1) + pred_label = pred_label.transpose([1, 0]) # transpose to shape (maxk, N) + correct = paddle.equal( + pred_label, (target.reshape([1, -1]).expand_as(pred_label)) + ) + res = [] + for k in topk: + correct_k = paddle.sum( + correct[:k].reshape([-1]).astype("float32"), axis=0, keepdim=True + ) + res.append( + paddle.multiply(correct_k, paddle.to_tensor(100.0 / pred.shape[0])) + ) + return res[0] if return_single else res + + def forward(self, pred, batch): + node_preds, edge_preds = pred + gts, tag = batch[4], batch[5] + gts = self.pre_process(gts, tag) + node_gts, edge_gts = [], [] + for gt in gts: + node_gts.append(gt[:, 0]) + edge_gts.append(gt[:, 1:].reshape([-1])) + node_gts = paddle.concat(node_gts) + edge_gts = paddle.concat(edge_gts) + + node_valids = paddle.nonzero(node_gts != self.ignore).reshape([-1]) + edge_valids = paddle.nonzero(edge_gts != -1).reshape([-1]) + loss_node = self.loss_node(node_preds, node_gts) + loss_edge = self.loss_edge(edge_preds, edge_gts) + loss = self.node_weight * loss_node + self.edge_weight * loss_edge + return dict( + loss=loss, + loss_node=loss_node, + loss_edge=loss_edge, + acc_node=self.accuracy( + paddle.gather(node_preds, node_valids), + paddle.gather(node_gts, node_valids), + ), + acc_edge=self.accuracy( + paddle.gather(edge_preds, edge_valids), + paddle.gather(edge_gts, edge_valids), + ), + ) diff --git a/modules/onnx_ocr_module/src/ppocr/losses/rec_aster_loss.py b/modules/onnx_ocr_module/src/ppocr/losses/rec_aster_loss.py new file mode 100644 index 0000000..8a8faa1 --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/losses/rec_aster_loss.py @@ -0,0 +1,103 @@ +# copyright (c) 2021 PaddlePaddle Authors. All Rights Reserve. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import paddle +from paddle import nn + + +class CosineEmbeddingLoss(nn.Layer): + def __init__(self, margin=0.0): + super(CosineEmbeddingLoss, self).__init__() + self.margin = margin + self.epsilon = 1e-12 + + def forward(self, x1, x2, target): + similarity = paddle.sum(x1 * x2, axis=-1) / ( + paddle.norm(x1, axis=-1) * paddle.norm(x2, axis=-1) + self.epsilon + ) + one_list = paddle.full_like(target, fill_value=1) + out = paddle.mean( + paddle.where( + paddle.equal(target, one_list), + 1.0 - similarity, + paddle.maximum(paddle.zeros_like(similarity), similarity - self.margin), + ) + ) + + return out + + +class AsterLoss(nn.Layer): + def __init__( + self, + weight=None, + size_average=True, + ignore_index=-100, + sequence_normalize=False, + sample_normalize=True, + **kwargs, + ): + super(AsterLoss, self).__init__() + self.weight = weight + self.size_average = size_average + self.ignore_index = ignore_index + self.sequence_normalize = sequence_normalize + self.sample_normalize = sample_normalize + self.loss_sem = CosineEmbeddingLoss() + self.is_cosin_loss = True + self.loss_func_rec = nn.CrossEntropyLoss(weight=None, reduction="none") + + def forward(self, predicts, batch): + targets = batch[1].astype("int64") + label_lengths = batch[2].astype("int64") + sem_target = batch[3].astype("float32") + embedding_vectors = predicts["embedding_vectors"] + rec_pred = predicts["rec_pred"] + + if not self.is_cosin_loss: + sem_loss = paddle.sum(self.loss_sem(embedding_vectors, sem_target)) + else: + label_target = paddle.ones([embedding_vectors.shape[0]]) + sem_loss = paddle.sum( + self.loss_sem(embedding_vectors, sem_target, label_target) + ) + + # rec loss + batch_size, def_max_length = targets.shape[0], targets.shape[1] + + mask = paddle.zeros([batch_size, def_max_length]) + for i in range(batch_size): + mask[i, : label_lengths[i]] = 1 + mask = paddle.cast(mask, "float32") + max_length = max(label_lengths) + assert max_length == rec_pred.shape[1] + targets = targets[:, :max_length] + mask = mask[:, :max_length] + rec_pred = paddle.reshape(rec_pred, [-1, rec_pred.shape[2]]) + input = nn.functional.log_softmax(rec_pred, axis=1) + targets = paddle.reshape(targets, [-1, 1]) + mask = paddle.reshape(mask, [-1, 1]) + output = -paddle.index_sample(input, index=targets) * mask + output = paddle.sum(output) + if self.sequence_normalize: + output = output / paddle.sum(mask) + if self.sample_normalize: + output = output / batch_size + + loss = output + sem_loss * 0.1 + return {"loss": loss} diff --git a/modules/onnx_ocr_module/src/ppocr/losses/rec_att_loss.py b/modules/onnx_ocr_module/src/ppocr/losses/rec_att_loss.py new file mode 100644 index 0000000..e0f65d9 --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/losses/rec_att_loss.py @@ -0,0 +1,43 @@ +# copyright (c) 2021 PaddlePaddle Authors. All Rights Reserve. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import paddle +from paddle import nn + + +class AttentionLoss(nn.Layer): + def __init__(self, **kwargs): + super(AttentionLoss, self).__init__() + self.loss_func = nn.CrossEntropyLoss(weight=None, reduction="none") + + def forward(self, predicts, batch): + targets = batch[1].astype("int64") + label_lengths = batch[2].astype("int64") + batch_size, num_steps, num_classes = ( + predicts.shape[0], + predicts.shape[1], + predicts.shape[2], + ) + assert ( + len(targets.shape) == len(list(predicts.shape)) - 1 + ), "The target's shape and inputs's shape is [N, d] and [N, num_steps]" + + inputs = paddle.reshape(predicts, [-1, predicts.shape[-1]]) + targets = paddle.reshape(targets, [-1]) + + return {"loss": paddle.sum(self.loss_func(inputs, targets))} diff --git a/modules/onnx_ocr_module/src/ppocr/losses/rec_can_loss.py b/modules/onnx_ocr_module/src/ppocr/losses/rec_can_loss.py new file mode 100644 index 0000000..1bc58f5 --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/losses/rec_can_loss.py @@ -0,0 +1,88 @@ +# copyright (c) 2021 PaddlePaddle Authors. All Rights Reserve. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +""" +This code is refer from: +https://github.com/LBH1024/CAN/models/can.py +""" + +import paddle +import paddle.nn as nn +import numpy as np + + +class CANLoss(nn.Layer): + """ + CANLoss is consist of two part: + word_average_loss: average accuracy of the symbol + counting_loss: counting loss of every symbol + """ + + def __init__(self): + super(CANLoss, self).__init__() + + self.use_label_mask = False + self.out_channel = 111 + self.cross = ( + nn.CrossEntropyLoss(reduction="none") + if self.use_label_mask + else nn.CrossEntropyLoss() + ) + self.counting_loss = nn.SmoothL1Loss(reduction="mean") + self.ratio = 16 + + def forward(self, preds, batch): + word_probs = preds[0] + counting_preds = preds[1] + counting_preds1 = preds[2] + counting_preds2 = preds[3] + labels = batch[2] + labels_mask = batch[3] + counting_labels = gen_counting_label(labels, self.out_channel, True) + counting_loss = ( + self.counting_loss(counting_preds1, counting_labels) + + self.counting_loss(counting_preds2, counting_labels) + + self.counting_loss(counting_preds, counting_labels) + ) + + word_loss = self.cross( + paddle.reshape(word_probs, [-1, word_probs.shape[-1]]), + paddle.reshape(labels, [-1]), + ) + word_average_loss = ( + paddle.sum(paddle.reshape(word_loss * labels_mask, [-1])) + / (paddle.sum(labels_mask) + 1e-10) + if self.use_label_mask + else word_loss + ) + loss = word_average_loss + counting_loss + return {"loss": loss} + + +def gen_counting_label(labels, channel, tag): + b, t = labels.shape + counting_labels = np.zeros([b, channel]) + + if tag: + ignore = [0, 1, 107, 108, 109, 110] + else: + ignore = [] + for i in range(b): + for j in range(t): + k = labels[i][j] + if k in ignore: + continue + else: + counting_labels[i][k] += 1 + counting_labels = paddle.to_tensor(counting_labels, dtype="float32") + return counting_labels diff --git a/modules/onnx_ocr_module/src/ppocr/losses/rec_ce_loss.py b/modules/onnx_ocr_module/src/ppocr/losses/rec_ce_loss.py new file mode 100644 index 0000000..45906fd --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/losses/rec_ce_loss.py @@ -0,0 +1,61 @@ +import paddle +from paddle import nn +import paddle.nn.functional as F + + +class CELoss(nn.Layer): + def __init__(self, smoothing=False, with_all=False, ignore_index=-1, **kwargs): + super(CELoss, self).__init__() + if ignore_index >= 0: + self.loss_func = nn.CrossEntropyLoss( + reduction="mean", ignore_index=ignore_index + ) + else: + self.loss_func = nn.CrossEntropyLoss(reduction="mean") + self.smoothing = smoothing + self.with_all = with_all + + def forward(self, pred, batch): + if isinstance(pred, dict): # for ABINet + loss = {} + loss_sum = [] + for name, logits in pred.items(): + if isinstance(logits, list): + logit_num = len(logits) + all_tgt = paddle.concat([batch[1]] * logit_num, 0) + all_logits = paddle.concat(logits, 0) + flt_logtis = all_logits.reshape([-1, all_logits.shape[2]]) + flt_tgt = all_tgt.reshape([-1]) + else: + flt_logtis = logits.reshape([-1, logits.shape[2]]) + flt_tgt = batch[1].reshape([-1]) + loss[name + "_loss"] = self.loss_func(flt_logtis, flt_tgt) + loss_sum.append(loss[name + "_loss"]) + loss["loss"] = sum(loss_sum) + return loss + else: + if self.with_all: # for ViTSTR + tgt = batch[1] + pred = pred.reshape([-1, pred.shape[2]]) + tgt = tgt.reshape([-1]) + loss = self.loss_func(pred, tgt) + return {"loss": loss} + else: # for NRTR + max_len = batch[2].max() + tgt = batch[1][:, 1 : 2 + max_len] + pred = pred.reshape([-1, pred.shape[2]]) + tgt = tgt.reshape([-1]) + if self.smoothing: + eps = 0.1 + n_class = pred.shape[1] + one_hot = F.one_hot(tgt, pred.shape[1]) + one_hot = one_hot * (1 - eps) + (1 - one_hot) * eps / (n_class - 1) + log_prb = F.log_softmax(pred, axis=1) + non_pad_mask = paddle.not_equal( + tgt, paddle.zeros(tgt.shape, dtype=tgt.dtype) + ) + loss = -(one_hot * log_prb).sum(axis=1) + loss = loss.masked_select(non_pad_mask).mean() + else: + loss = self.loss_func(pred, tgt) + return {"loss": loss} diff --git a/modules/onnx_ocr_module/src/ppocr/losses/rec_cppd_loss.py b/modules/onnx_ocr_module/src/ppocr/losses/rec_cppd_loss.py new file mode 100644 index 0000000..1e884ce --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/losses/rec_cppd_loss.py @@ -0,0 +1,76 @@ +# copyright (c) 2023 PaddlePaddle Authors. All Rights Reserve. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import paddle +from paddle import nn +import paddle.nn.functional as F + + +class CPPDLoss(nn.Layer): + def __init__( + self, smoothing=False, ignore_index=100, sideloss_weight=1.0, **kwargs + ): + super(CPPDLoss, self).__init__() + self.edge_ce = nn.CrossEntropyLoss(reduction="mean", ignore_index=ignore_index) + self.char_node_ce = nn.CrossEntropyLoss(reduction="mean") + self.pos_node_ce = nn.BCEWithLogitsLoss(reduction="mean") + self.smoothing = smoothing + self.ignore_index = ignore_index + self.sideloss_weight = sideloss_weight + + def label_smoothing_ce(self, preds, targets): + non_pad_mask = paddle.not_equal( + targets, + paddle.zeros(targets.shape, dtype=targets.dtype) + self.ignore_index, + ) + tgts = paddle.where( + targets + == (paddle.zeros(targets.shape, dtype=targets.dtype) + self.ignore_index), + paddle.zeros(targets.shape, dtype=targets.dtype), + targets, + ) + eps = 0.1 + n_class = preds.shape[1] + one_hot = F.one_hot(tgts, preds.shape[1]) + one_hot = one_hot * (1 - eps) + (1 - one_hot) * eps / (n_class - 1) + log_prb = F.log_softmax(preds, axis=1) + loss = -(one_hot * log_prb).sum(axis=1) + loss = loss.masked_select(non_pad_mask).mean() + return loss + + def forward(self, pred, batch): + node_feats, edge_feats = pred + node_tgt = batch[2] + char_tgt = batch[1] + + loss_char_node = self.char_node_ce( + node_feats[0].flatten(0, 1), node_tgt[:, :-26].flatten(0, 1) + ) + loss_pos_node = self.pos_node_ce( + node_feats[1].flatten(0, 1), node_tgt[:, -26:].flatten(0, 1).cast("float32") + ) + loss_node = loss_char_node + loss_pos_node + + edge_feats = edge_feats.flatten(0, 1) + char_tgt = char_tgt.flatten(0, 1) + if self.smoothing: + loss_edge = self.label_smoothing_ce(edge_feats, char_tgt) + else: + loss_edge = self.edge_ce(edge_feats, char_tgt) + + return { + "loss": self.sideloss_weight * loss_node + loss_edge, + "loss_node": self.sideloss_weight * loss_node, + "loss_edge": loss_edge, + } diff --git a/modules/onnx_ocr_module/src/ppocr/losses/rec_ctc_loss.py b/modules/onnx_ocr_module/src/ppocr/losses/rec_ctc_loss.py new file mode 100644 index 0000000..c701fef --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/losses/rec_ctc_loss.py @@ -0,0 +1,46 @@ +# copyright (c) 2019 PaddlePaddle Authors. All Rights Reserve. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import paddle +from paddle import nn + + +class CTCLoss(nn.Layer): + def __init__(self, use_focal_loss=False, **kwargs): + super(CTCLoss, self).__init__() + self.loss_func = nn.CTCLoss(blank=0, reduction="none") + self.use_focal_loss = use_focal_loss + + def forward(self, predicts, batch): + if isinstance(predicts, (list, tuple)): + predicts = predicts[-1] + predicts = predicts.transpose((1, 0, 2)) + N, B, _ = predicts.shape + preds_lengths = paddle.to_tensor( + [N] * B, dtype="int64", place=paddle.CPUPlace() + ) + labels = batch[1].astype("int32") + label_lengths = batch[2].astype("int64") + loss = self.loss_func(predicts, labels, preds_lengths, label_lengths) + if self.use_focal_loss: + weight = paddle.exp(-loss) + weight = paddle.subtract(paddle.to_tensor([1.0]), weight) + weight = paddle.square(weight) + loss = paddle.multiply(loss, weight) + loss = loss.mean() + return {"loss": loss} diff --git a/modules/onnx_ocr_module/src/ppocr/losses/rec_enhanced_ctc_loss.py b/modules/onnx_ocr_module/src/ppocr/losses/rec_enhanced_ctc_loss.py new file mode 100644 index 0000000..e7e0696 --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/losses/rec_enhanced_ctc_loss.py @@ -0,0 +1,76 @@ +# copyright (c) 2021 PaddlePaddle Authors. All Rights Reserve. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import paddle +from paddle import nn +from .ace_loss import ACELoss +from .center_loss import CenterLoss +from .rec_ctc_loss import CTCLoss + + +class EnhancedCTCLoss(nn.Layer): + def __init__( + self, + use_focal_loss=False, + use_ace_loss=False, + ace_loss_weight=0.1, + use_center_loss=False, + center_loss_weight=0.05, + num_classes=6625, + feat_dim=96, + init_center=False, + center_file_path=None, + **kwargs, + ): + super(EnhancedCTCLoss, self).__init__() + self.ctc_loss_func = CTCLoss(use_focal_loss=use_focal_loss) + + self.use_ace_loss = False + if use_ace_loss: + self.use_ace_loss = use_ace_loss + self.ace_loss_func = ACELoss() + self.ace_loss_weight = ace_loss_weight + + self.use_center_loss = False + if use_center_loss: + self.use_center_loss = use_center_loss + self.center_loss_func = CenterLoss( + num_classes=num_classes, + feat_dim=feat_dim, + init_center=init_center, + center_file_path=center_file_path, + ) + self.center_loss_weight = center_loss_weight + + def __call__(self, predicts, batch): + loss = self.ctc_loss_func(predicts, batch)["loss"] + + if self.use_center_loss: + center_loss = ( + self.center_loss_func(predicts, batch)["loss_center"] + * self.center_loss_weight + ) + loss = loss + center_loss + + if self.use_ace_loss: + ace_loss = ( + self.ace_loss_func(predicts, batch)["loss_ace"] * self.ace_loss_weight + ) + loss = loss + ace_loss + + return {"enhanced_ctc_loss": loss} diff --git a/modules/onnx_ocr_module/src/ppocr/losses/rec_latexocr_loss.py b/modules/onnx_ocr_module/src/ppocr/losses/rec_latexocr_loss.py new file mode 100644 index 0000000..d209c04 --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/losses/rec_latexocr_loss.py @@ -0,0 +1,47 @@ +# copyright (c) 2024 PaddlePaddle Authors. All Rights Reserve. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +""" +This code is refer from: +https://github.com/lucidrains/x-transformers/blob/main/x_transformers/autoregressive_wrapper.py +""" + +import paddle +import paddle.nn as nn +import paddle.nn.functional as F +import numpy as np + + +class LaTeXOCRLoss(nn.Layer): + """ + LaTeXOCR adopt CrossEntropyLoss for network training. + """ + + def __init__(self): + super(LaTeXOCRLoss, self).__init__() + self.ignore_index = -100 + self.cross = nn.CrossEntropyLoss( + reduction="mean", ignore_index=self.ignore_index + ) + + def forward(self, preds, batch): + word_probs = preds + labels = batch[1][:, 1:] + word_loss = self.cross( + paddle.reshape(word_probs, [-1, word_probs.shape[-1]]), + paddle.reshape(labels, [-1]), + ) + + loss = word_loss + return {"loss": loss} diff --git a/modules/onnx_ocr_module/src/ppocr/losses/rec_multi_loss.py b/modules/onnx_ocr_module/src/ppocr/losses/rec_multi_loss.py new file mode 100644 index 0000000..74be385 --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/losses/rec_multi_loss.py @@ -0,0 +1,68 @@ +# copyright (c) 2022 PaddlePaddle Authors. All Rights Reserve. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import paddle +from paddle import nn + +from .rec_ctc_loss import CTCLoss +from .rec_sar_loss import SARLoss +from .rec_nrtr_loss import NRTRLoss + + +class MultiLoss(nn.Layer): + def __init__(self, **kwargs): + super().__init__() + self.loss_funcs = {} + self.loss_list = kwargs.pop("loss_config_list") + self.weight_1 = kwargs.get("weight_1", 1.0) + self.weight_2 = kwargs.get("weight_2", 1.0) + for loss_info in self.loss_list: + for name, param in loss_info.items(): + if param is not None: + kwargs.update(param) + loss = eval(name)(**kwargs) + self.loss_funcs[name] = loss + + def forward(self, predicts, batch): + self.total_loss = {} + total_loss = 0.0 + # batch [image, label_ctc, label_sar, length, valid_ratio] + for name, loss_func in self.loss_funcs.items(): + if name == "CTCLoss": + loss = ( + loss_func(predicts["ctc"], batch[:2] + batch[3:])["loss"] + * self.weight_1 + ) + elif name == "SARLoss": + loss = ( + loss_func(predicts["sar"], batch[:1] + batch[2:])["loss"] + * self.weight_2 + ) + elif name == "NRTRLoss": + loss = ( + loss_func(predicts["gtc"], batch[:1] + batch[2:])["loss"] + * self.weight_2 + ) + else: + raise NotImplementedError( + "{} is not supported in MultiLoss yet".format(name) + ) + self.total_loss[name] = loss + total_loss += loss + self.total_loss["loss"] = total_loss + return self.total_loss diff --git a/modules/onnx_ocr_module/src/ppocr/losses/rec_nrtr_loss.py b/modules/onnx_ocr_module/src/ppocr/losses/rec_nrtr_loss.py new file mode 100644 index 0000000..a4f56de --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/losses/rec_nrtr_loss.py @@ -0,0 +1,33 @@ +import paddle +from paddle import nn +import paddle.nn.functional as F + + +class NRTRLoss(nn.Layer): + def __init__(self, smoothing=True, ignore_index=0, **kwargs): + super(NRTRLoss, self).__init__() + if ignore_index >= 0 and not smoothing: + self.loss_func = nn.CrossEntropyLoss( + reduction="mean", ignore_index=ignore_index + ) + self.smoothing = smoothing + + def forward(self, pred, batch): + max_len = batch[2].max() + tgt = batch[1][:, 1 : 2 + max_len] + pred = pred.reshape([-1, pred.shape[2]]) + tgt = tgt.reshape([-1]) + if self.smoothing: + eps = 0.1 + n_class = pred.shape[1] + one_hot = F.one_hot(tgt, pred.shape[1]) + one_hot = one_hot * (1 - eps) + (1 - one_hot) * eps / (n_class - 1) + log_prb = F.log_softmax(pred, axis=1) + non_pad_mask = paddle.not_equal( + tgt, paddle.zeros(tgt.shape, dtype=tgt.dtype) + ) + loss = -(one_hot * log_prb).sum(axis=1) + loss = loss.masked_select(non_pad_mask).mean() + else: + loss = self.loss_func(pred, tgt) + return {"loss": loss} diff --git a/modules/onnx_ocr_module/src/ppocr/losses/rec_parseq_loss.py b/modules/onnx_ocr_module/src/ppocr/losses/rec_parseq_loss.py new file mode 100644 index 0000000..a1731e8 --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/losses/rec_parseq_loss.py @@ -0,0 +1,52 @@ +# copyright (c) 2020 PaddlePaddle Authors. All Rights Reserve. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import paddle +from paddle import nn + + +class ParseQLoss(nn.Layer): + def __init__(self, **kwargs): + super(ParseQLoss, self).__init__() + + def forward(self, predicts, targets): + label = targets[1] # label + label_len = targets[2] + max_step = paddle.max(label_len).cpu().numpy()[0] + 2 + tgt = label[:, :max_step] + + logits_list = predicts["logits_list"] + pad_id = predicts["pad_id"] + eos_id = predicts["eos_id"] + + tgt_out = tgt[:, 1:] + loss = 0 + loss_numel = 0 + n = (tgt_out != pad_id).sum().item() + + for i, logits in enumerate(logits_list): + loss += n * paddle.nn.functional.cross_entropy( + input=logits, label=tgt_out.flatten(), ignore_index=pad_id + ) + loss_numel += n + if i == 1: + tgt_out = paddle.where(condition=tgt_out == eos_id, x=pad_id, y=tgt_out) + n = (tgt_out != pad_id).sum().item() + loss /= loss_numel + + return {"loss": loss} diff --git a/modules/onnx_ocr_module/src/ppocr/losses/rec_ppformulanet_loss.py b/modules/onnx_ocr_module/src/ppocr/losses/rec_ppformulanet_loss.py new file mode 100644 index 0000000..95af575 --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/losses/rec_ppformulanet_loss.py @@ -0,0 +1,74 @@ +# copyright (c) 2024 PaddlePaddle Authors. All Rights Reserve. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +import paddle +import paddle.nn as nn + + +class PPFormulaNet_S_Loss(nn.Layer): + """ + PP=FormulaNet-S adopt CrossEntropyLoss for network training. + """ + + def __init__(self, vocab_size=50000, parallel_step=1): + super(PPFormulaNet_S_Loss, self).__init__() + self.ignore_index = -100 + self.vocab_size = vocab_size + self.parallel_step = int(parallel_step) + self.pad_token_id = 1 + # ignore padding characters during training + self.cross = nn.CrossEntropyLoss( + reduction="mean", ignore_index=self.ignore_index + ) + + def forward(self, preds, batch): + logits, masked_label = preds + + word_loss = self.cross( + paddle.reshape(logits, [-1, logits.shape[-1]]), + paddle.reshape(masked_label[:, self.parallel_step :], [-1]), + ) + loss = word_loss + return { + "loss": loss, + "word_loss": word_loss, + } + + +class PPFormulaNet_L_Loss(nn.Layer): + """ + PPFormulaNet_L adopt CrossEntropyLoss for network training. + """ + + def __init__(self, vocab_size=50000): + super(PPFormulaNet_L_Loss, self).__init__() + self.ignore_index = -100 + self.vocab_size = vocab_size + self.pad_token_id = 1 + # ignore padding characters during training + self.cross = nn.CrossEntropyLoss( + reduction="mean", ignore_index=self.ignore_index + ) + + def forward(self, preds, batch): + logits, masked_label = preds + + word_loss = self.cross( + paddle.reshape(logits, [-1, logits.shape[-1]]), + paddle.reshape(masked_label[:, 1:], [-1]), + ) + loss = word_loss + return { + "loss": loss, + "word_loss": word_loss, + } diff --git a/modules/onnx_ocr_module/src/ppocr/losses/rec_pren_loss.py b/modules/onnx_ocr_module/src/ppocr/losses/rec_pren_loss.py new file mode 100644 index 0000000..d08b27a --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/losses/rec_pren_loss.py @@ -0,0 +1,30 @@ +# copyright (c) 2022 PaddlePaddle Authors. All Rights Reserve. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +from paddle import nn + + +class PRENLoss(nn.Layer): + def __init__(self, **kwargs): + super(PRENLoss, self).__init__() + # note: 0 is padding idx + self.loss_func = nn.CrossEntropyLoss(reduction="mean", ignore_index=0) + + def forward(self, predicts, batch): + loss = self.loss_func(predicts, batch[1].astype("int64")) + return {"loss": loss} diff --git a/modules/onnx_ocr_module/src/ppocr/losses/rec_rfl_loss.py b/modules/onnx_ocr_module/src/ppocr/losses/rec_rfl_loss.py new file mode 100644 index 0000000..b2b1856 --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/losses/rec_rfl_loss.py @@ -0,0 +1,70 @@ +# copyright (c) 2022 PaddlePaddle Authors. All Rights Reserve. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +""" +This code is refer from: +https://github.com/hikopensource/DAVAR-Lab-OCR/blob/main/davarocr/davar_common/models/loss/cross_entropy_loss.py +""" +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import paddle +from paddle import nn + +from .basic_loss import CELoss, DistanceLoss + + +class RFLLoss(nn.Layer): + def __init__(self, ignore_index=-100, **kwargs): + super().__init__() + + self.cnt_loss = nn.MSELoss(**kwargs) + self.seq_loss = nn.CrossEntropyLoss(ignore_index=ignore_index) + + def forward(self, predicts, batch): + self.total_loss = {} + total_loss = 0.0 + if isinstance(predicts, tuple) or isinstance(predicts, list): + cnt_outputs, seq_outputs = predicts + else: + cnt_outputs, seq_outputs = predicts, None + # batch [image, label, length, cnt_label] + if cnt_outputs is not None: + cnt_loss = self.cnt_loss(cnt_outputs, paddle.cast(batch[3], paddle.float32)) + self.total_loss["cnt_loss"] = cnt_loss + total_loss += cnt_loss + + if seq_outputs is not None: + targets = batch[1].astype("int64") + label_lengths = batch[2].astype("int64") + batch_size, num_steps, num_classes = ( + seq_outputs.shape[0], + seq_outputs.shape[1], + seq_outputs.shape[2], + ) + assert ( + len(targets.shape) == len(list(seq_outputs.shape)) - 1 + ), "The target's shape and inputs's shape is [N, d] and [N, num_steps]" + + inputs = seq_outputs[:, :-1, :] + targets = targets[:, 1:] + + inputs = paddle.reshape(inputs, [-1, inputs.shape[-1]]) + targets = paddle.reshape(targets, [-1]) + seq_loss = self.seq_loss(inputs, targets) + self.total_loss["seq_loss"] = seq_loss + total_loss += seq_loss + + self.total_loss["loss"] = total_loss + return self.total_loss diff --git a/modules/onnx_ocr_module/src/ppocr/losses/rec_sar_loss.py b/modules/onnx_ocr_module/src/ppocr/losses/rec_sar_loss.py new file mode 100644 index 0000000..a2cba69 --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/losses/rec_sar_loss.py @@ -0,0 +1,36 @@ +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import paddle +from paddle import nn + + +class SARLoss(nn.Layer): + def __init__(self, **kwargs): + super(SARLoss, self).__init__() + ignore_index = kwargs.get("ignore_index", 92) # 6626 + self.loss_func = paddle.nn.loss.CrossEntropyLoss( + reduction="mean", ignore_index=ignore_index + ) + + def forward(self, predicts, batch): + predict = predicts[ + :, :-1, : + ] # ignore last index of outputs to be in same seq_len with targets + label = batch[1].astype("int64")[ + :, 1: + ] # ignore first index of target in loss calculation + batch_size, num_steps, num_classes = ( + predict.shape[0], + predict.shape[1], + predict.shape[2], + ) + assert ( + len(label.shape) == len(list(predict.shape)) - 1 + ), "The target's shape and inputs's shape is [N, d] and [N, num_steps]" + + inputs = paddle.reshape(predict, [-1, num_classes]) + targets = paddle.reshape(label, [-1]) + loss = self.loss_func(inputs, targets) + return {"loss": loss} diff --git a/modules/onnx_ocr_module/src/ppocr/losses/rec_satrn_loss.py b/modules/onnx_ocr_module/src/ppocr/losses/rec_satrn_loss.py new file mode 100644 index 0000000..16f1b6a --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/losses/rec_satrn_loss.py @@ -0,0 +1,53 @@ +# copyright (c) 2022 PaddlePaddle Authors. All Rights Reserve. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +""" +This code is refer from: +https://github.com/open-mmlab/mmocr/blob/1.x/mmocr/models/textrecog/module_losses/ce_module_loss.py +""" +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import paddle +from paddle import nn + + +class SATRNLoss(nn.Layer): + def __init__(self, **kwargs): + super(SATRNLoss, self).__init__() + ignore_index = kwargs.get("ignore_index", 92) # 6626 + self.loss_func = paddle.nn.loss.CrossEntropyLoss( + reduction="none", ignore_index=ignore_index + ) + + def forward(self, predicts, batch): + predict = predicts[ + :, :-1, : + ] # ignore last index of outputs to be in same seq_len with targets + label = batch[1].astype("int64")[ + :, 1: + ] # ignore first index of target in loss calculation + batch_size, num_steps, num_classes = ( + predict.shape[0], + predict.shape[1], + predict.shape[2], + ) + assert ( + len(label.shape) == len(list(predict.shape)) - 1 + ), "The target's shape and inputs's shape is [N, d] and [N, num_steps]" + + inputs = paddle.reshape(predict, [-1, num_classes]) + targets = paddle.reshape(label, [-1]) + loss = self.loss_func(inputs, targets) + return {"loss": loss.mean()} diff --git a/modules/onnx_ocr_module/src/ppocr/losses/rec_spin_att_loss.py b/modules/onnx_ocr_module/src/ppocr/losses/rec_spin_att_loss.py new file mode 100644 index 0000000..591ae08 --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/losses/rec_spin_att_loss.py @@ -0,0 +1,52 @@ +# copyright (c) 2022 PaddlePaddle Authors. All Rights Reserve. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import paddle +from paddle import nn + +"""This code is refer from: +https://github.com/hikopensource/DAVAR-Lab-OCR +""" + + +class SPINAttentionLoss(nn.Layer): + def __init__(self, reduction="mean", ignore_index=-100, **kwargs): + super(SPINAttentionLoss, self).__init__() + self.loss_func = nn.CrossEntropyLoss( + weight=None, reduction=reduction, ignore_index=ignore_index + ) + + def forward(self, predicts, batch): + targets = batch[1].astype("int64") + targets = targets[:, 1:] # remove [eos] in label + + label_lengths = batch[2].astype("int64") + batch_size, num_steps, num_classes = ( + predicts.shape[0], + predicts.shape[1], + predicts.shape[2], + ) + assert ( + len(targets.shape) == len(list(predicts.shape)) - 1 + ), "The target's shape and inputs's shape is [N, d] and [N, num_steps]" + + inputs = paddle.reshape(predicts, [-1, predicts.shape[-1]]) + targets = paddle.reshape(targets, [-1]) + + return {"loss": self.loss_func(inputs, targets)} diff --git a/modules/onnx_ocr_module/src/ppocr/losses/rec_srn_loss.py b/modules/onnx_ocr_module/src/ppocr/losses/rec_srn_loss.py new file mode 100644 index 0000000..cb034f3 --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/losses/rec_srn_loss.py @@ -0,0 +1,47 @@ +# copyright (c) 2020 PaddlePaddle Authors. All Rights Reserve. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import paddle +from paddle import nn + + +class SRNLoss(nn.Layer): + def __init__(self, **kwargs): + super(SRNLoss, self).__init__() + self.loss_func = paddle.nn.loss.CrossEntropyLoss(reduction="sum") + + def forward(self, predicts, batch): + predict = predicts["predict"] + word_predict = predicts["word_out"] + gsrm_predict = predicts["gsrm_out"] + label = batch[1] + + casted_label = paddle.cast(x=label, dtype="int64") + casted_label = paddle.reshape(x=casted_label, shape=[-1, 1]) + + cost_word = self.loss_func(word_predict, label=casted_label) + cost_gsrm = self.loss_func(gsrm_predict, label=casted_label) + cost_vsfd = self.loss_func(predict, label=casted_label) + + cost_word = paddle.reshape(x=paddle.sum(cost_word), shape=[1]) + cost_gsrm = paddle.reshape(x=paddle.sum(cost_gsrm), shape=[1]) + cost_vsfd = paddle.reshape(x=paddle.sum(cost_vsfd), shape=[1]) + + sum_cost = cost_word * 3.0 + cost_vsfd + cost_gsrm * 0.15 + + return {"loss": sum_cost, "word_loss": cost_word, "img_loss": cost_vsfd} diff --git a/modules/onnx_ocr_module/src/ppocr/losses/rec_unimernet_loss.py b/modules/onnx_ocr_module/src/ppocr/losses/rec_unimernet_loss.py new file mode 100644 index 0000000..2611cf8 --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/losses/rec_unimernet_loss.py @@ -0,0 +1,54 @@ +# copyright (c) 2024 PaddlePaddle Authors. All Rights Reserve. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import paddle +import paddle.nn as nn +import paddle.nn.functional as F +import numpy as np + + +class UniMERNetLoss(nn.Layer): + def __init__(self, length_aware=True, vocab_size=50000): + super(UniMERNetLoss, self).__init__() + self.ignore_index = -100 + self.vocab_size = vocab_size + self.pad_token_id = 1 + self.length_aware = length_aware + self.cross = nn.CrossEntropyLoss( + reduction="mean", ignore_index=self.ignore_index + ) + self.counting_loss_fct = nn.SmoothL1Loss() + + def _get_count_gt(self, labels): + mask = (labels != self.pad_token_id).cast("float32") + one_hot_labels = F.one_hot( + labels, num_classes=self.vocab_size + ) * mask.unsqueeze(-1) + count_gt = paddle.sum(one_hot_labels, axis=1) + return count_gt + + def forward(self, preds, batch): + logits, count_pred, masked_label = preds + labels = batch[1][:, 1:] + word_loss = self.cross( + paddle.reshape(logits, [-1, logits.shape[-1]]), + paddle.reshape(masked_label[:, 1:], [-1]), + ) + loss = word_loss + if self.length_aware: + count_gt = self._get_count_gt(labels) + count_gt = paddle.log(count_gt.cast(paddle.float32) + 1) + count_loss = self.counting_loss_fct(count_pred, count_gt) + loss += 0.5 * count_loss + return {"loss": loss, "word_loss": word_loss, "count_loss": count_loss} diff --git a/modules/onnx_ocr_module/src/ppocr/losses/rec_vl_loss.py b/modules/onnx_ocr_module/src/ppocr/losses/rec_vl_loss.py new file mode 100644 index 0000000..6c41177 --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/losses/rec_vl_loss.py @@ -0,0 +1,70 @@ +# copyright (c) 2022 PaddlePaddle Authors. All Rights Reserve. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +""" +This code is refer from: +https://github.com/wangyuxin87/VisionLAN +""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import paddle +from paddle import nn + + +class VLLoss(nn.Layer): + def __init__(self, mode="LF_1", weight_res=0.5, weight_mas=0.5, **kwargs): + super(VLLoss, self).__init__() + self.loss_func = paddle.nn.loss.CrossEntropyLoss(reduction="mean") + assert mode in ["LF_1", "LF_2", "LA"] + self.mode = mode + self.weight_res = weight_res + self.weight_mas = weight_mas + + def flatten_label(self, target): + label_flatten = [] + label_length = [] + for i in range(0, target.shape[0]): + cur_label = target[i].tolist() + label_flatten += cur_label[: cur_label.index(0) + 1] + label_length.append(cur_label.index(0) + 1) + label_flatten = paddle.to_tensor(label_flatten, dtype="int64") + label_length = paddle.to_tensor(label_length, dtype="int32") + return (label_flatten, label_length) + + def _flatten(self, sources, lengths): + return paddle.concat([t[:l] for t, l in zip(sources, lengths)]) + + def forward(self, predicts, batch): + text_pre = predicts[0] + target = batch[1].astype("int64") + label_flatten, length = self.flatten_label(target) + text_pre = self._flatten(text_pre, length) + if self.mode == "LF_1": + loss = self.loss_func(text_pre, label_flatten) + else: + text_rem = predicts[1] + text_mas = predicts[2] + target_res = batch[2].astype("int64") + target_sub = batch[3].astype("int64") + label_flatten_res, length_res = self.flatten_label(target_res) + label_flatten_sub, length_sub = self.flatten_label(target_sub) + text_rem = self._flatten(text_rem, length_res) + text_mas = self._flatten(text_mas, length_sub) + loss_ori = self.loss_func(text_pre, label_flatten) + loss_res = self.loss_func(text_rem, label_flatten_res) + loss_mas = self.loss_func(text_mas, label_flatten_sub) + loss = loss_ori + loss_res * self.weight_res + loss_mas * self.weight_mas + return {"loss": loss} diff --git a/modules/onnx_ocr_module/src/ppocr/losses/stroke_focus_loss.py b/modules/onnx_ocr_module/src/ppocr/losses/stroke_focus_loss.py new file mode 100644 index 0000000..9b7850c --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/losses/stroke_focus_loss.py @@ -0,0 +1,63 @@ +# copyright (c) 2022 PaddlePaddle Authors. All Rights Reserve. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +""" +This code is refer from: +https://github.com/FudanVI/FudanOCR/blob/main/text-gestalt/loss/stroke_focus_loss.py +""" +import cv2 +import sys +import time +import string +import random +import numpy as np +import paddle.nn as nn +import paddle + + +class StrokeFocusLoss(nn.Layer): + def __init__(self, character_dict_path=None, **kwargs): + super(StrokeFocusLoss, self).__init__(character_dict_path) + self.mse_loss = nn.MSELoss() + self.ce_loss = nn.CrossEntropyLoss() + self.l1_loss = nn.L1Loss() + self.english_stroke_alphabet = "0123456789" + self.english_stroke_dict = {} + for index in range(len(self.english_stroke_alphabet)): + self.english_stroke_dict[self.english_stroke_alphabet[index]] = index + + stroke_decompose_lines = open(character_dict_path, "r").readlines() + self.dic = {} + for line in stroke_decompose_lines: + line = line.strip() + character, sequence = line.split() + self.dic[character] = sequence + + def forward(self, pred, data): + sr_img = pred["sr_img"] + hr_img = pred["hr_img"] + + mse_loss = self.mse_loss(sr_img, hr_img) + word_attention_map_gt = pred["word_attention_map_gt"] + word_attention_map_pred = pred["word_attention_map_pred"] + + hr_pred = pred["hr_pred"] + sr_pred = pred["sr_pred"] + + attention_loss = paddle.nn.functional.l1_loss( + word_attention_map_gt, word_attention_map_pred + ) + + loss = (mse_loss + attention_loss * 50) * 100 + + return {"mse_loss": mse_loss, "attention_loss": attention_loss, "loss": loss} diff --git a/modules/onnx_ocr_module/src/ppocr/losses/table_att_loss.py b/modules/onnx_ocr_module/src/ppocr/losses/table_att_loss.py new file mode 100644 index 0000000..72b737d --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/losses/table_att_loss.py @@ -0,0 +1,100 @@ +# copyright (c) 2021 PaddlePaddle Authors. All Rights Reserve. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import paddle +from paddle import nn +from paddle.nn import functional as F + + +class TableAttentionLoss(nn.Layer): + def __init__(self, structure_weight=1.0, loc_weight=0.0, **kwargs): + super(TableAttentionLoss, self).__init__() + self.loss_func = nn.CrossEntropyLoss(weight=None, reduction="none") + self.structure_weight = structure_weight + self.loc_weight = loc_weight + + def forward(self, predicts, batch): + structure_probs = predicts["structure_probs"] + structure_targets = batch[1].astype("int64") + structure_targets = structure_targets[:, 1:] + structure_probs = paddle.reshape( + structure_probs, [-1, structure_probs.shape[-1]] + ) + structure_targets = paddle.reshape(structure_targets, [-1]) + structure_loss = self.loss_func(structure_probs, structure_targets) + + structure_loss = paddle.mean(structure_loss) * self.structure_weight + + loc_preds = predicts["loc_preds"] + loc_targets = batch[2].astype("float32") + loc_targets_mask = batch[3].astype("float32") + loc_targets = loc_targets[:, 1:, :] + loc_targets_mask = loc_targets_mask[:, 1:, :] + loc_loss = ( + F.mse_loss(loc_preds * loc_targets_mask, loc_targets) * self.loc_weight + ) + + total_loss = structure_loss + loc_loss + return { + "loss": total_loss, + "structure_loss": structure_loss, + "loc_loss": loc_loss, + } + + +class SLALoss(nn.Layer): + def __init__(self, structure_weight=1.0, loc_weight=0.0, loc_loss="mse", **kwargs): + super(SLALoss, self).__init__() + self.loss_func = nn.CrossEntropyLoss(weight=None, reduction="mean") + self.structure_weight = structure_weight + self.loc_weight = loc_weight + self.loc_loss = loc_loss + self.eps = 1e-12 + + def forward(self, predicts, batch): + structure_probs = predicts["structure_probs"] + structure_targets = batch[1].astype("int64") + max_len = batch[-2].max().astype("int32") + structure_targets = structure_targets[:, 1 : max_len + 2] + + structure_loss = self.loss_func(structure_probs, structure_targets) + + structure_loss = paddle.mean(structure_loss) * self.structure_weight + + loc_preds = predicts["loc_preds"] + loc_targets = batch[2].astype("float32") + loc_targets_mask = batch[3].astype("float32") + loc_targets = loc_targets[:, 1 : max_len + 2] + loc_targets_mask = loc_targets_mask[:, 1 : max_len + 2] + + loc_loss = ( + F.smooth_l1_loss( + loc_preds * loc_targets_mask, + loc_targets * loc_targets_mask, + reduction="sum", + ) + * self.loc_weight + ) + + loc_loss = loc_loss / (loc_targets_mask.sum() + self.eps) + total_loss = structure_loss + loc_loss + return { + "loss": total_loss, + "structure_loss": structure_loss, + "loc_loss": loc_loss, + } diff --git a/modules/onnx_ocr_module/src/ppocr/losses/table_master_loss.py b/modules/onnx_ocr_module/src/ppocr/losses/table_master_loss.py new file mode 100644 index 0000000..08b1c29 --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/losses/table_master_loss.py @@ -0,0 +1,74 @@ +# Copyright (c) 2021 PaddlePaddle Authors. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +""" +This code is refer from: +https://github.com/JiaquanYe/TableMASTER-mmocr/tree/master/mmocr/models/textrecog/losses +""" + +import paddle +from paddle import nn + + +class TableMasterLoss(nn.Layer): + def __init__(self, ignore_index=-1): + super(TableMasterLoss, self).__init__() + self.structure_loss = nn.CrossEntropyLoss( + ignore_index=ignore_index, reduction="mean" + ) + self.box_loss = nn.L1Loss(reduction="sum") + self.eps = 1e-12 + + def forward(self, predicts, batch): + # structure_loss + structure_probs = predicts["structure_probs"] + structure_targets = batch[1] + structure_targets = structure_targets[:, 1:] + structure_probs = structure_probs.reshape([-1, structure_probs.shape[-1]]) + structure_targets = structure_targets.reshape([-1]) + + structure_loss = self.structure_loss(structure_probs, structure_targets) + structure_loss = structure_loss.mean() + losses = dict(structure_loss=structure_loss) + + # box loss + bboxes_preds = predicts["loc_preds"] + bboxes_targets = batch[2][:, 1:, :] + bbox_masks = batch[3][:, 1:] + # mask empty-bbox or non-bbox structure token's bbox. + + masked_bboxes_preds = bboxes_preds * bbox_masks + masked_bboxes_targets = bboxes_targets * bbox_masks + + # horizon loss (x and width) + horizon_sum_loss = self.box_loss( + masked_bboxes_preds[:, :, 0::2], masked_bboxes_targets[:, :, 0::2] + ) + horizon_loss = horizon_sum_loss / (bbox_masks.sum() + self.eps) + # vertical loss (y and height) + vertical_sum_loss = self.box_loss( + masked_bboxes_preds[:, :, 1::2], masked_bboxes_targets[:, :, 1::2] + ) + vertical_loss = vertical_sum_loss / (bbox_masks.sum() + self.eps) + + horizon_loss = horizon_loss.mean() + vertical_loss = vertical_loss.mean() + all_loss = structure_loss + horizon_loss + vertical_loss + losses.update( + { + "loss": all_loss, + "horizon_bbox_loss": horizon_loss, + "vertical_bbox_loss": vertical_loss, + } + ) + return losses diff --git a/modules/onnx_ocr_module/src/ppocr/losses/text_focus_loss.py b/modules/onnx_ocr_module/src/ppocr/losses/text_focus_loss.py new file mode 100644 index 0000000..310140b --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/losses/text_focus_loss.py @@ -0,0 +1,91 @@ +# copyright (c) 2022 PaddlePaddle Authors. All Rights Reserve. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +""" +This code is refer from: +https://github.com/FudanVI/FudanOCR/blob/main/scene-text-telescope/loss/text_focus_loss.py +""" + +import paddle.nn as nn +import paddle +import numpy as np +import pickle as pkl + +standard_alphebet = "-0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" +standard_dict = {} +for index in range(len(standard_alphebet)): + standard_dict[standard_alphebet[index]] = index + + +def load_confuse_matrix(confuse_dict_path): + f = open(confuse_dict_path, "rb") + data = pkl.load(f) + f.close() + number = data[:10] + upper = data[10:36] + lower = data[36:] + end = np.ones((1, 62)) + pad = np.ones((63, 1)) + rearrange_data = np.concatenate((end, number, lower, upper), axis=0) + rearrange_data = np.concatenate((pad, rearrange_data), axis=1) + rearrange_data = 1 / rearrange_data + rearrange_data[rearrange_data == np.inf] = 1 + rearrange_data = paddle.to_tensor(rearrange_data) + + lower_alpha = "abcdefghijklmnopqrstuvwxyz" + # upper_alpha = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' + for i in range(63): + for j in range(63): + if i != j and standard_alphebet[j] in lower_alpha: + rearrange_data[i][j] = max( + rearrange_data[i][j], rearrange_data[i][j + 26] + ) + rearrange_data = rearrange_data[:37, :37] + + return rearrange_data + + +def weight_cross_entropy(pred, gt, weight_table): + batch = gt.shape[0] + weight = weight_table[gt] + pred_exp = paddle.exp(pred) + pred_exp_weight = weight * pred_exp + loss = 0 + for i in range(len(gt)): + loss -= paddle.log( + pred_exp_weight[i][gt[i]] / paddle.sum(pred_exp_weight, 1)[i] + ) + return loss / batch + + +class TelescopeLoss(nn.Layer): + def __init__(self, confuse_dict_path): + super(TelescopeLoss, self).__init__() + self.weight_table = load_confuse_matrix(confuse_dict_path) + self.mse_loss = nn.MSELoss() + self.ce_loss = nn.CrossEntropyLoss() + self.l1_loss = nn.L1Loss() + + def forward(self, pred, data): + sr_img = pred["sr_img"] + hr_img = pred["hr_img"] + sr_pred = pred["sr_pred"] + text_gt = pred["text_gt"] + + word_attention_map_gt = pred["word_attention_map_gt"] + word_attention_map_pred = pred["word_attention_map_pred"] + mse_loss = self.mse_loss(sr_img, hr_img) + attention_loss = self.l1_loss(word_attention_map_gt, word_attention_map_pred) + recognition_loss = weight_cross_entropy(sr_pred, text_gt, self.weight_table) + loss = mse_loss + attention_loss * 10 + recognition_loss * 0.0005 + return {"mse_loss": mse_loss, "attention_loss": attention_loss, "loss": loss} diff --git a/modules/onnx_ocr_module/src/ppocr/losses/vqa_token_layoutlm_loss.py b/modules/onnx_ocr_module/src/ppocr/losses/vqa_token_layoutlm_loss.py new file mode 100644 index 0000000..d01c091 --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/losses/vqa_token_layoutlm_loss.py @@ -0,0 +1,61 @@ +# copyright (c) 2021 PaddlePaddle Authors. All Rights Reserve. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +from paddle import nn +from ppocr.losses.basic_loss import DMLLoss + + +class VQASerTokenLayoutLMLoss(nn.Layer): + def __init__(self, num_classes, key=None): + super().__init__() + self.loss_class = nn.CrossEntropyLoss() + self.num_classes = num_classes + self.ignore_index = self.loss_class.ignore_index + self.key = key + + def forward(self, predicts, batch): + if isinstance(predicts, dict) and self.key is not None: + predicts = predicts[self.key] + labels = batch[5] + attention_mask = batch[2] + if attention_mask is not None: + active_loss = ( + attention_mask.reshape( + [ + -1, + ] + ) + == 1 + ) + active_output = predicts.reshape([-1, self.num_classes])[active_loss] + active_label = labels.reshape( + [ + -1, + ] + )[active_loss] + loss = self.loss_class(active_output, active_label) + else: + loss = self.loss_class( + predicts.reshape([-1, self.num_classes]), + labels.reshape( + [ + -1, + ] + ), + ) + return {"loss": loss} diff --git a/modules/onnx_ocr_module/src/ppocr/metrics/__init__.py b/modules/onnx_ocr_module/src/ppocr/metrics/__init__.py new file mode 100644 index 0000000..dd28d73 --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/metrics/__init__.py @@ -0,0 +1,62 @@ +# copyright (c) 2020 PaddlePaddle Authors. All Rights Reserve. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function +from __future__ import unicode_literals + +import copy + +__all__ = ["build_metric"] + +from .det_metric import DetMetric, DetFCEMetric +from .rec_metric import RecMetric, CNTMetric, CANMetric, LaTeXOCRMetric +from .cls_metric import ClsMetric +from .e2e_metric import E2EMetric +from .distillation_metric import DistillationMetric +from .table_metric import TableMetric +from .kie_metric import KIEMetric +from .vqa_token_ser_metric import VQASerTokenMetric +from .vqa_token_re_metric import VQAReTokenMetric +from .sr_metric import SRMetric +from .ct_metric import CTMetric + + +def build_metric(config): + support_dict = [ + "DetMetric", + "DetFCEMetric", + "RecMetric", + "ClsMetric", + "E2EMetric", + "DistillationMetric", + "TableMetric", + "KIEMetric", + "VQASerTokenMetric", + "VQAReTokenMetric", + "SRMetric", + "CTMetric", + "CNTMetric", + "CANMetric", + "LaTeXOCRMetric", + ] + + config = copy.deepcopy(config) + module_name = config.pop("name") + assert module_name in support_dict, Exception( + "metric only support {}".format(support_dict) + ) + module_class = eval(module_name)(**config) + return module_class diff --git a/modules/onnx_ocr_module/src/ppocr/metrics/bleu.py b/modules/onnx_ocr_module/src/ppocr/metrics/bleu.py new file mode 100644 index 0000000..c68e624 --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/metrics/bleu.py @@ -0,0 +1,243 @@ +# copyright (c) 2024 PaddlePaddle Authors. All Rights Reserve. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +""" +This code is refer from: +https://github.com/tensorflow/nmt/blob/master/nmt/scripts/bleu.py +""" + +import re +import math +import collections +from functools import lru_cache + + +def _get_ngrams(segment, max_order): + """Extracts all n-grams upto a given maximum order from an input segment. + + Args: + segment: text segment from which n-grams will be extracted. + max_order: maximum length in tokens of the n-grams returned by this + methods. + + Returns: + The Counter containing all n-grams upto max_order in segment + with a count of how many times each n-gram occurred. + """ + ngram_counts = collections.Counter() + for order in range(1, max_order + 1): + for i in range(0, len(segment) - order + 1): + ngram = tuple(segment[i : i + order]) + ngram_counts[ngram] += 1 + return ngram_counts + + +def compute_bleu(reference_corpus, translation_corpus, max_order=4, smooth=False): + """Computes BLEU score of translated segments against one or more references. + + Args: + reference_corpus: list of lists of references for each translation. Each + reference should be tokenized into a list of tokens. + translation_corpus: list of translations to score. Each translation + should be tokenized into a list of tokens. + max_order: Maximum n-gram order to use when computing BLEU score. + smooth: Whether or not to apply Lin et al. 2004 smoothing. + + Returns: + 3-Tuple with the BLEU score, n-gram precisions, geometric mean of n-gram + precisions and brevity penalty. + """ + matches_by_order = [0] * max_order + possible_matches_by_order = [0] * max_order + reference_length = 0 + translation_length = 0 + for references, translation in zip(reference_corpus, translation_corpus): + reference_length += min(len(r) for r in references) + translation_length += len(translation) + + merged_ref_ngram_counts = collections.Counter() + for reference in references: + merged_ref_ngram_counts |= _get_ngrams(reference, max_order) + translation_ngram_counts = _get_ngrams(translation, max_order) + overlap = translation_ngram_counts & merged_ref_ngram_counts + for ngram in overlap: + matches_by_order[len(ngram) - 1] += overlap[ngram] + for order in range(1, max_order + 1): + possible_matches = len(translation) - order + 1 + if possible_matches > 0: + possible_matches_by_order[order - 1] += possible_matches + + precisions = [0] * max_order + for i in range(0, max_order): + if smooth: + precisions[i] = (matches_by_order[i] + 1.0) / ( + possible_matches_by_order[i] + 1.0 + ) + else: + if possible_matches_by_order[i] > 0: + precisions[i] = ( + float(matches_by_order[i]) / possible_matches_by_order[i] + ) + else: + precisions[i] = 0.0 + + if min(precisions) > 0: + p_log_sum = sum((1.0 / max_order) * math.log(p) for p in precisions) + geo_mean = math.exp(p_log_sum) + else: + geo_mean = 0 + + if float(translation_length) == 0 or float(reference_length) == 0: + ratio = 1e-5 + else: + ratio = float(translation_length) / reference_length + + if ratio > 1.0: + bp = 1.0 + else: + bp = math.exp(1 - 1.0 / ratio) + + bleu = geo_mean * bp + + return (bleu, precisions, bp, ratio, translation_length, reference_length) + + +class BaseTokenizer: + """A base dummy tokenizer to derive from.""" + + def signature(self): + """ + Returns a signature for the tokenizer. + :return: signature string + """ + return "none" + + def __call__(self, line): + """ + Tokenizes an input line with the tokenizer. + :param line: a segment to tokenize + :return: the tokenized line + """ + return line + + +class TokenizerRegexp(BaseTokenizer): + def signature(self): + return "re" + + def __init__(self): + self._re = [ + # language-dependent part (assuming Western languages) + (re.compile(r"([\{-\~\[-\` -\&\(-\+\:-\@\/])"), r" \1 "), + # tokenize period and comma unless preceded by a digit + (re.compile(r"([^0-9])([\.,])"), r"\1 \2 "), + # tokenize period and comma unless followed by a digit + (re.compile(r"([\.,])([^0-9])"), r" \1 \2"), + # tokenize dash when preceded by a digit + (re.compile(r"([0-9])(-)"), r"\1 \2 "), + # one space only between words + # NOTE: Doing this in Python (below) is faster + # (re.compile(r'\s+'), r' '), + ] + + @lru_cache(maxsize=2**16) + def __call__(self, line): + """Common post-processing tokenizer for `13a` and `zh` tokenizers. + :param line: a segment to tokenize + :return: the tokenized line + """ + for _re, repl in self._re: + line = _re.sub(repl, line) + + # no leading or trailing spaces, single space within words + # return ' '.join(line.split()) + # This line is changed with regards to the original tokenizer (seen above) to return individual words + return line.split() + + +class Tokenizer13a(BaseTokenizer): + def signature(self): + return "13a" + + def __init__(self): + self._post_tokenizer = TokenizerRegexp() + + @lru_cache(maxsize=2**16) + def __call__(self, line): + """Tokenizes an input line using a relatively minimal tokenization + that is however equivalent to mteval-v13a, used by WMT. + + :param line: a segment to tokenize + :return: the tokenized line + """ + + # language-independent part: + line = line.replace("", "") + line = line.replace("-\n", "") + line = line.replace("\n", " ") + + if "&" in line: + line = line.replace(""", '"') + line = line.replace("&", "&") + line = line.replace("<", "<") + line = line.replace(">", ">") + + return self._post_tokenizer(f" {line} ") + + +def compute_bleu_score( + predictions, references, tokenizer=Tokenizer13a(), max_order=4, smooth=False +): + # if only one reference is provided make sure we still use list of lists + if isinstance(references[0], str): + references = [[ref] for ref in references] + + references = [[tokenizer(r) for r in ref] for ref in references] + predictions = [tokenizer(p) for p in predictions] + score = compute_bleu( + reference_corpus=references, + translation_corpus=predictions, + max_order=max_order, + smooth=smooth, + ) + (bleu, precisions, bp, ratio, translation_length, reference_length) = score + return bleu + + +def cal_distance(word1, word2): + m = len(word1) + n = len(word2) + if m * n == 0: + return m + n + dp = [[0] * (n + 1) for _ in range(m + 1)] + for i in range(m + 1): + dp[i][0] = i + for j in range(n + 1): + dp[0][j] = j + for i in range(1, m + 1): + for j in range(1, n + 1): + a = dp[i - 1][j] + 1 + b = dp[i][j - 1] + 1 + c = dp[i - 1][j - 1] + if word1[i - 1] != word2[j - 1]: + c += 1 + dp[i][j] = min(a, b, c) + return dp[m][n] + + +def compute_edit_distance(prediction, label): + prediction = prediction.strip().split(" ") + label = label.strip().split(" ") + distance = cal_distance(prediction, label) + return distance diff --git a/modules/onnx_ocr_module/src/ppocr/metrics/cls_metric.py b/modules/onnx_ocr_module/src/ppocr/metrics/cls_metric.py new file mode 100644 index 0000000..820fe75 --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/metrics/cls_metric.py @@ -0,0 +1,48 @@ +# copyright (c) 2020 PaddlePaddle Authors. All Rights Reserve. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +class ClsMetric(object): + def __init__(self, main_indicator="acc", **kwargs): + self.main_indicator = main_indicator + self.eps = 1e-5 + self.reset() + + def __call__(self, pred_label, *args, **kwargs): + preds, labels = pred_label + correct_num = 0 + all_num = 0 + for (pred, pred_conf), (target, _) in zip(preds, labels): + if pred == target: + correct_num += 1 + all_num += 1 + self.correct_num += correct_num + self.all_num += all_num + return { + "acc": correct_num / (all_num + self.eps), + } + + def get_metric(self): + """ + return metrics { + 'acc': 0 + } + """ + acc = self.correct_num / (self.all_num + self.eps) + self.reset() + return {"acc": acc} + + def reset(self): + self.correct_num = 0 + self.all_num = 0 diff --git a/modules/onnx_ocr_module/src/ppocr/metrics/ct_metric.py b/modules/onnx_ocr_module/src/ppocr/metrics/ct_metric.py new file mode 100644 index 0000000..bec3506 --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/metrics/ct_metric.py @@ -0,0 +1,51 @@ +# Copyright (c) 2020 PaddlePaddle Authors. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import os +from scipy import io +import numpy as np + +from ppocr.utils.e2e_metric.Deteval import combine_results, get_score_C + + +class CTMetric(object): + def __init__(self, main_indicator, delimiter="\t", **kwargs): + self.delimiter = delimiter + self.main_indicator = main_indicator + self.reset() + + def reset(self): + self.results = [] # clear results + + def __call__(self, preds, batch, **kwargs): + # NOTE: only support bs=1 now, as the label length of different sample is Unequal + assert len(preds) == 1, "CentripetalText test now only support batch_size=1." + label = batch[2] + text = batch[3] + pred = preds[0]["points"] + result = get_score_C(label, text, pred) + + self.results.append(result) + + def get_metric(self): + """ + Input format: y0,x0, ..... yn,xn. Each detection is separated by the end of line token ('\n')' + """ + metrics = combine_results(self.results, rec_flag=False) + self.reset() + return metrics diff --git a/modules/onnx_ocr_module/src/ppocr/metrics/det_metric.py b/modules/onnx_ocr_module/src/ppocr/metrics/det_metric.py new file mode 100644 index 0000000..be95ec3 --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/metrics/det_metric.py @@ -0,0 +1,153 @@ +# Copyright (c) 2020 PaddlePaddle Authors. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +__all__ = ["DetMetric", "DetFCEMetric"] + +from .eval_det_iou import DetectionIoUEvaluator + + +class DetMetric(object): + def __init__(self, main_indicator="hmean", **kwargs): + self.evaluator = DetectionIoUEvaluator() + self.main_indicator = main_indicator + self.reset() + + def __call__(self, preds, batch, **kwargs): + """ + batch: a list produced by dataloaders. + image: np.ndarray of shape (N, C, H, W). + ratio_list: np.ndarray of shape(N,2) + polygons: np.ndarray of shape (N, K, 4, 2), the polygons of objective regions. + ignore_tags: np.ndarray of shape (N, K), indicates whether a region is ignorable or not. + preds: a list of dict produced by post process + points: np.ndarray of shape (N, K, 4, 2), the polygons of objective regions. + """ + gt_polyons_batch = batch[2] + ignore_tags_batch = batch[3] + for pred, gt_polyons, ignore_tags in zip( + preds, gt_polyons_batch, ignore_tags_batch + ): + # prepare gt + gt_info_list = [ + {"points": gt_polyon, "text": "", "ignore": ignore_tag} + for gt_polyon, ignore_tag in zip(gt_polyons, ignore_tags) + ] + # prepare det + det_info_list = [ + {"points": det_polyon, "text": ""} for det_polyon in pred["points"] + ] + result = self.evaluator.evaluate_image(gt_info_list, det_info_list) + self.results.append(result) + + def get_metric(self): + """ + return metrics { + 'precision': 0, + 'recall': 0, + 'hmean': 0 + } + """ + + metrics = self.evaluator.combine_results(self.results) + self.reset() + return metrics + + def reset(self): + self.results = [] # clear results + + +class DetFCEMetric(object): + def __init__(self, main_indicator="hmean", **kwargs): + self.evaluator = DetectionIoUEvaluator() + self.main_indicator = main_indicator + self.reset() + + def __call__(self, preds, batch, **kwargs): + """ + batch: a list produced by dataloaders. + image: np.ndarray of shape (N, C, H, W). + ratio_list: np.ndarray of shape(N,2) + polygons: np.ndarray of shape (N, K, 4, 2), the polygons of objective regions. + ignore_tags: np.ndarray of shape (N, K), indicates whether a region is ignorable or not. + preds: a list of dict produced by post process + points: np.ndarray of shape (N, K, 4, 2), the polygons of objective regions. + """ + gt_polyons_batch = batch[2] + ignore_tags_batch = batch[3] + + for pred, gt_polyons, ignore_tags in zip( + preds, gt_polyons_batch, ignore_tags_batch + ): + # prepare gt + gt_info_list = [ + {"points": gt_polyon, "text": "", "ignore": ignore_tag} + for gt_polyon, ignore_tag in zip(gt_polyons, ignore_tags) + ] + # prepare det + det_info_list = [ + {"points": det_polyon, "text": "", "score": score} + for det_polyon, score in zip(pred["points"], pred["scores"]) + ] + + for score_thr in self.results.keys(): + det_info_list_thr = [ + det_info + for det_info in det_info_list + if det_info["score"] >= score_thr + ] + result = self.evaluator.evaluate_image(gt_info_list, det_info_list_thr) + self.results[score_thr].append(result) + + def get_metric(self): + """ + return metrics {'heman':0, + 'thr 0.3':'precision: 0 recall: 0 hmean: 0', + 'thr 0.4':'precision: 0 recall: 0 hmean: 0', + 'thr 0.5':'precision: 0 recall: 0 hmean: 0', + 'thr 0.6':'precision: 0 recall: 0 hmean: 0', + 'thr 0.7':'precision: 0 recall: 0 hmean: 0', + 'thr 0.8':'precision: 0 recall: 0 hmean: 0', + 'thr 0.9':'precision: 0 recall: 0 hmean: 0', + } + """ + metrics = {} + hmean = 0 + for score_thr in self.results.keys(): + metric = self.evaluator.combine_results(self.results[score_thr]) + # for key, value in metric.items(): + # metrics['{}_{}'.format(key, score_thr)] = value + metric_str = "precision:{:.5f} recall:{:.5f} hmean:{:.5f}".format( + metric["precision"], metric["recall"], metric["hmean"] + ) + metrics["thr {}".format(score_thr)] = metric_str + hmean = max(hmean, metric["hmean"]) + metrics["hmean"] = hmean + + self.reset() + return metrics + + def reset(self): + self.results = { + 0.3: [], + 0.4: [], + 0.5: [], + 0.6: [], + 0.7: [], + 0.8: [], + 0.9: [], + } # clear results diff --git a/modules/onnx_ocr_module/src/ppocr/metrics/distillation_metric.py b/modules/onnx_ocr_module/src/ppocr/metrics/distillation_metric.py new file mode 100644 index 0000000..8e0bcf1 --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/metrics/distillation_metric.py @@ -0,0 +1,72 @@ +# copyright (c) 2020 PaddlePaddle Authors. All Rights Reserve. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import importlib +import copy + +from .rec_metric import RecMetric +from .det_metric import DetMetric +from .e2e_metric import E2EMetric +from .cls_metric import ClsMetric +from .vqa_token_ser_metric import VQASerTokenMetric +from .vqa_token_re_metric import VQAReTokenMetric + + +class DistillationMetric(object): + def __init__(self, key=None, base_metric_name=None, main_indicator=None, **kwargs): + self.main_indicator = main_indicator + self.key = key + self.main_indicator = main_indicator + self.base_metric_name = base_metric_name + self.kwargs = kwargs + self.metrics = None + + def _init_metrcis(self, preds): + self.metrics = dict() + mod = importlib.import_module(__name__) + for key in preds: + self.metrics[key] = getattr(mod, self.base_metric_name)( + main_indicator=self.main_indicator, **self.kwargs + ) + self.metrics[key].reset() + + def __call__(self, preds, batch, **kwargs): + assert isinstance(preds, dict) + if self.metrics is None: + self._init_metrcis(preds) + output = dict() + for key in preds: + self.metrics[key].__call__(preds[key], batch, **kwargs) + + def get_metric(self): + """ + return metrics { + 'acc': 0, + 'norm_edit_dis': 0, + } + """ + output = dict() + for key in self.metrics: + metric = self.metrics[key].get_metric() + # main indicator + if key == self.key: + output.update(metric) + else: + for sub_key in metric: + output["{}_{}".format(key, sub_key)] = metric[sub_key] + return output + + def reset(self): + for key in self.metrics: + self.metrics[key].reset() diff --git a/modules/onnx_ocr_module/src/ppocr/metrics/e2e_metric.py b/modules/onnx_ocr_module/src/ppocr/metrics/e2e_metric.py new file mode 100644 index 0000000..97796c4 --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/metrics/e2e_metric.py @@ -0,0 +1,88 @@ +# Copyright (c) 2021 PaddlePaddle Authors. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +__all__ = ["E2EMetric"] + +from ppocr.utils.e2e_metric.Deteval import get_socre_A, get_socre_B, combine_results +from ppocr.utils.e2e_utils.extract_textpoint_slow import get_dict + + +class E2EMetric(object): + def __init__( + self, + mode, + gt_mat_dir, + character_dict_path, + main_indicator="f_score_e2e", + **kwargs, + ): + self.mode = mode + self.gt_mat_dir = gt_mat_dir + self.label_list = get_dict(character_dict_path) + self.max_index = len(self.label_list) + self.main_indicator = main_indicator + self.reset() + + def __call__(self, preds, batch, **kwargs): + if self.mode == "A": + gt_polyons_batch = batch[2] + temp_gt_strs_batch = batch[3][0] + ignore_tags_batch = batch[4] + gt_strs_batch = [] + + for temp_list in temp_gt_strs_batch: + t = "" + for index in temp_list: + if index < self.max_index: + t += self.label_list[index] + gt_strs_batch.append(t) + + for pred, gt_polyons, gt_strs, ignore_tags in zip( + [preds], gt_polyons_batch, [gt_strs_batch], ignore_tags_batch + ): + # prepare gt + gt_info_list = [ + {"points": gt_polyon, "text": gt_str, "ignore": ignore_tag} + for gt_polyon, gt_str, ignore_tag in zip( + gt_polyons, gt_strs, ignore_tags + ) + ] + # prepare det + e2e_info_list = [ + {"points": det_polyon, "texts": pred_str} + for det_polyon, pred_str in zip(pred["points"], pred["texts"]) + ] + + result = get_socre_A(gt_info_list, e2e_info_list) + self.results.append(result) + else: + img_id = batch[5][0] + e2e_info_list = [ + {"points": det_polyon, "texts": pred_str} + for det_polyon, pred_str in zip(preds["points"], preds["texts"]) + ] + result = get_socre_B(self.gt_mat_dir, img_id, e2e_info_list) + self.results.append(result) + + def get_metric(self): + metrics = combine_results(self.results) + self.reset() + return metrics + + def reset(self): + self.results = [] # clear results diff --git a/modules/onnx_ocr_module/src/ppocr/metrics/eval_det_iou.py b/modules/onnx_ocr_module/src/ppocr/metrics/eval_det_iou.py new file mode 100644 index 0000000..21d5c18 --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/metrics/eval_det_iou.py @@ -0,0 +1,257 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +from collections import namedtuple +import numpy as np +from shapely.geometry import Polygon + +""" +reference from : +https://github.com/MhLiao/DB/blob/3c32b808d4412680310d3d28eeb6a2d5bf1566c5/concern/icdar2015_eval/detection/iou.py#L8 +""" + + +class DetectionIoUEvaluator(object): + def __init__(self, iou_constraint=0.5, area_precision_constraint=0.5): + self.iou_constraint = iou_constraint + self.area_precision_constraint = area_precision_constraint + + def evaluate_image(self, gt, pred): + def get_union(pD, pG): + return Polygon(pD).union(Polygon(pG)).area + + def get_intersection_over_union(pD, pG): + return get_intersection(pD, pG) / get_union(pD, pG) + + def get_intersection(pD, pG): + return Polygon(pD).intersection(Polygon(pG)).area + + def compute_ap(confList, matchList, numGtCare): + correct = 0 + AP = 0 + if len(confList) > 0: + confList = np.array(confList) + matchList = np.array(matchList) + sorted_ind = np.argsort(-confList) + confList = confList[sorted_ind] + matchList = matchList[sorted_ind] + for n in range(len(confList)): + match = matchList[n] + if match: + correct += 1 + AP += float(correct) / (n + 1) + + if numGtCare > 0: + AP /= numGtCare + + return AP + + perSampleMetrics = {} + + matchedSum = 0 + + Rectangle = namedtuple("Rectangle", "xmin ymin xmax ymax") + + numGlobalCareGt = 0 + numGlobalCareDet = 0 + + arrGlobalConfidences = [] + arrGlobalMatches = [] + + recall = 0 + precision = 0 + hmean = 0 + + detMatched = 0 + + iouMat = np.empty([1, 1]) + + gtPols = [] + detPols = [] + + gtPolPoints = [] + detPolPoints = [] + + # Array of Ground Truth Polygons' keys marked as don't Care + gtDontCarePolsNum = [] + # Array of Detected Polygons' matched with a don't Care GT + detDontCarePolsNum = [] + + pairs = [] + detMatchedNums = [] + + arrSampleConfidences = [] + arrSampleMatch = [] + + evaluationLog = "" + + for n in range(len(gt)): + points = gt[n]["points"] + dontCare = gt[n]["ignore"] + if not Polygon(points).is_valid: + continue + + gtPol = points + gtPols.append(gtPol) + gtPolPoints.append(points) + if dontCare: + gtDontCarePolsNum.append(len(gtPols) - 1) + + evaluationLog += ( + "GT polygons: " + + str(len(gtPols)) + + ( + " (" + str(len(gtDontCarePolsNum)) + " don't care)\n" + if len(gtDontCarePolsNum) > 0 + else "\n" + ) + ) + + for n in range(len(pred)): + points = pred[n]["points"] + if not Polygon(points).is_valid: + continue + + detPol = points + detPols.append(detPol) + detPolPoints.append(points) + if len(gtDontCarePolsNum) > 0: + for dontCarePol in gtDontCarePolsNum: + dontCarePol = gtPols[dontCarePol] + intersected_area = get_intersection(dontCarePol, detPol) + pdDimensions = Polygon(detPol).area + precision = ( + 0 if pdDimensions == 0 else intersected_area / pdDimensions + ) + if precision > self.area_precision_constraint: + detDontCarePolsNum.append(len(detPols) - 1) + break + + evaluationLog += ( + "DET polygons: " + + str(len(detPols)) + + ( + " (" + str(len(detDontCarePolsNum)) + " don't care)\n" + if len(detDontCarePolsNum) > 0 + else "\n" + ) + ) + + if len(gtPols) > 0 and len(detPols) > 0: + # Calculate IoU and precision matrixs + outputShape = [len(gtPols), len(detPols)] + iouMat = np.empty(outputShape) + gtRectMat = np.zeros(len(gtPols), np.int8) + detRectMat = np.zeros(len(detPols), np.int8) + for gtNum in range(len(gtPols)): + for detNum in range(len(detPols)): + pG = gtPols[gtNum] + pD = detPols[detNum] + iouMat[gtNum, detNum] = get_intersection_over_union(pD, pG) + + for gtNum in range(len(gtPols)): + for detNum in range(len(detPols)): + if ( + gtRectMat[gtNum] == 0 + and detRectMat[detNum] == 0 + and gtNum not in gtDontCarePolsNum + and detNum not in detDontCarePolsNum + ): + if iouMat[gtNum, detNum] > self.iou_constraint: + gtRectMat[gtNum] = 1 + detRectMat[detNum] = 1 + detMatched += 1 + pairs.append({"gt": gtNum, "det": detNum}) + detMatchedNums.append(detNum) + evaluationLog += ( + "Match GT #" + + str(gtNum) + + " with Det #" + + str(detNum) + + "\n" + ) + + numGtCare = len(gtPols) - len(gtDontCarePolsNum) + numDetCare = len(detPols) - len(detDontCarePolsNum) + if numGtCare == 0: + recall = float(1) + precision = float(0) if numDetCare > 0 else float(1) + else: + recall = float(detMatched) / numGtCare + precision = 0 if numDetCare == 0 else float(detMatched) / numDetCare + + hmean = ( + 0 + if (precision + recall) == 0 + else 2.0 * precision * recall / (precision + recall) + ) + + matchedSum += detMatched + numGlobalCareGt += numGtCare + numGlobalCareDet += numDetCare + + perSampleMetrics = { + "gtCare": numGtCare, + "detCare": numDetCare, + "detMatched": detMatched, + } + return perSampleMetrics + + def combine_results(self, results): + numGlobalCareGt = 0 + numGlobalCareDet = 0 + matchedSum = 0 + for result in results: + numGlobalCareGt += result["gtCare"] + numGlobalCareDet += result["detCare"] + matchedSum += result["detMatched"] + + methodRecall = ( + 0 if numGlobalCareGt == 0 else float(matchedSum) / numGlobalCareGt + ) + methodPrecision = ( + 0 if numGlobalCareDet == 0 else float(matchedSum) / numGlobalCareDet + ) + methodHmean = ( + 0 + if methodRecall + methodPrecision == 0 + else 2 * methodRecall * methodPrecision / (methodRecall + methodPrecision) + ) + methodMetrics = { + "precision": methodPrecision, + "recall": methodRecall, + "hmean": methodHmean, + } + + return methodMetrics + + +if __name__ == "__main__": + evaluator = DetectionIoUEvaluator() + gts = [ + [ + { + "points": [(0, 0), (1, 0), (1, 1), (0, 1)], + "text": 1234, + "ignore": False, + }, + { + "points": [(2, 2), (3, 2), (3, 3), (2, 3)], + "text": 5678, + "ignore": False, + }, + ] + ] + preds = [ + [ + { + "points": [(0.1, 0.1), (1, 0), (1, 1), (0, 1)], + "text": 123, + "ignore": False, + } + ] + ] + results = [] + for gt, pred in zip(gts, preds): + results.append(evaluator.evaluate_image(gt, pred)) + metrics = evaluator.combine_results(results) + print(metrics) diff --git a/modules/onnx_ocr_module/src/ppocr/metrics/kie_metric.py b/modules/onnx_ocr_module/src/ppocr/metrics/kie_metric.py new file mode 100644 index 0000000..0c83756 --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/metrics/kie_metric.py @@ -0,0 +1,72 @@ +# Copyright (c) 2021 PaddlePaddle Authors. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# The code is refer from: https://github.com/open-mmlab/mmocr/blob/main/mmocr/core/evaluation/kie_metric.py + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import numpy as np +import paddle + +__all__ = ["KIEMetric"] + + +class KIEMetric(object): + def __init__(self, main_indicator="hmean", **kwargs): + self.main_indicator = main_indicator + self.reset() + self.node = [] + self.gt = [] + + def __call__(self, preds, batch, **kwargs): + nodes, _ = preds + gts, tag = batch[4].squeeze(0), batch[5].tolist()[0] + gts = gts[: tag[0], :1].reshape([-1]) + self.node.append(nodes.numpy()) + self.gt.append(gts) + # result = self.compute_f1_score(nodes, gts) + # self.results.append(result) + + def compute_f1_score(self, preds, gts): + ignores = [0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 25] + C = preds.shape[1] + classes = np.array(sorted(set(range(C)) - set(ignores))) + hist = ( + np.bincount((gts * C).astype("int64") + preds.argmax(1), minlength=C**2) + .reshape([C, C]) + .astype("float32") + ) + diag = np.diag(hist) + recalls = diag / hist.sum(1).clip(min=1) + precisions = diag / hist.sum(0).clip(min=1) + f1 = 2 * recalls * precisions / (recalls + precisions).clip(min=1e-8) + return f1[classes] + + def combine_results(self, results): + node = np.concatenate(self.node, 0) + gts = np.concatenate(self.gt, 0) + results = self.compute_f1_score(node, gts) + data = {"hmean": results.mean()} + return data + + def get_metric(self): + metrics = self.combine_results(self.results) + self.reset() + return metrics + + def reset(self): + self.results = [] # clear results + self.node = [] + self.gt = [] diff --git a/modules/onnx_ocr_module/src/ppocr/metrics/rec_metric.py b/modules/onnx_ocr_module/src/ppocr/metrics/rec_metric.py new file mode 100644 index 0000000..48c4caf --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/metrics/rec_metric.py @@ -0,0 +1,299 @@ +# copyright (c) 2020 PaddlePaddle Authors. All Rights Reserve. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from rapidfuzz.distance import Levenshtein +from difflib import SequenceMatcher + +import numpy as np +import string +from .bleu import compute_bleu_score, compute_edit_distance + + +class RecMetric(object): + def __init__( + self, main_indicator="acc", is_filter=False, ignore_space=True, **kwargs + ): + self.main_indicator = main_indicator + self.is_filter = is_filter + self.ignore_space = ignore_space + self.eps = 1e-5 + self.reset() + + def _normalize_text(self, text): + text = "".join( + filter(lambda x: x in (string.digits + string.ascii_letters), text) + ) + return text.lower() + + def __call__(self, pred_label, *args, **kwargs): + preds, labels = pred_label + correct_num = 0 + all_num = 0 + norm_edit_dis = 0.0 + for (pred, pred_conf), (target, _) in zip(preds, labels): + if self.ignore_space: + pred = pred.replace(" ", "") + target = target.replace(" ", "") + if self.is_filter: + pred = self._normalize_text(pred) + target = self._normalize_text(target) + norm_edit_dis += Levenshtein.normalized_distance(pred, target) + if pred == target: + correct_num += 1 + all_num += 1 + self.correct_num += correct_num + self.all_num += all_num + self.norm_edit_dis += norm_edit_dis + return { + "acc": correct_num / (all_num + self.eps), + "norm_edit_dis": 1 - norm_edit_dis / (all_num + self.eps), + } + + def get_metric(self): + """ + return metrics { + 'acc': 0, + 'norm_edit_dis': 0, + } + """ + acc = 1.0 * self.correct_num / (self.all_num + self.eps) + norm_edit_dis = 1 - self.norm_edit_dis / (self.all_num + self.eps) + self.reset() + return {"acc": acc, "norm_edit_dis": norm_edit_dis} + + def reset(self): + self.correct_num = 0 + self.all_num = 0 + self.norm_edit_dis = 0 + + +class CNTMetric(object): + def __init__(self, main_indicator="acc", **kwargs): + self.main_indicator = main_indicator + self.eps = 1e-5 + self.reset() + + def __call__(self, pred_label, *args, **kwargs): + preds, labels = pred_label + correct_num = 0 + all_num = 0 + for pred, target in zip(preds, labels): + if pred == target: + correct_num += 1 + all_num += 1 + self.correct_num += correct_num + self.all_num += all_num + return { + "acc": correct_num / (all_num + self.eps), + } + + def get_metric(self): + """ + return metrics { + 'acc': 0, + } + """ + acc = 1.0 * self.correct_num / (self.all_num + self.eps) + self.reset() + return {"acc": acc} + + def reset(self): + self.correct_num = 0 + self.all_num = 0 + + +class CANMetric(object): + def __init__(self, main_indicator="exp_rate", **kwargs): + self.main_indicator = main_indicator + self.word_right = [] + self.exp_right = [] + self.word_total_length = 0 + self.exp_total_num = 0 + self.word_rate = 0 + self.exp_rate = 0 + self.reset() + self.epoch_reset() + + def __call__(self, preds, batch, **kwargs): + for k, v in kwargs.items(): + epoch_reset = v + if epoch_reset: + self.epoch_reset() + word_probs = preds + word_label, word_label_mask = batch + line_right = 0 + if word_probs is not None: + word_pred = word_probs.argmax(2) + word_pred = word_pred.cpu().detach().numpy() + word_scores = [ + SequenceMatcher( + None, s1[: int(np.sum(s3))], s2[: int(np.sum(s3))], autojunk=False + ).ratio() + * (len(s1[: int(np.sum(s3))]) + len(s2[: int(np.sum(s3))])) + / len(s1[: int(np.sum(s3))]) + / 2 + for s1, s2, s3 in zip(word_label, word_pred, word_label_mask) + ] + batch_size = len(word_scores) + for i in range(batch_size): + if word_scores[i] == 1: + line_right += 1 + self.word_rate = np.mean(word_scores) # float + self.exp_rate = line_right / batch_size # float + exp_length, word_length = word_label.shape[:2] + self.word_right.append(self.word_rate * word_length) + self.exp_right.append(self.exp_rate * exp_length) + self.word_total_length = self.word_total_length + word_length + self.exp_total_num = self.exp_total_num + exp_length + + def get_metric(self): + """ + return { + 'word_rate': 0, + "exp_rate": 0, + } + """ + cur_word_rate = sum(self.word_right) / self.word_total_length + cur_exp_rate = sum(self.exp_right) / self.exp_total_num + self.reset() + return {"word_rate": cur_word_rate, "exp_rate": cur_exp_rate} + + def reset(self): + self.word_rate = 0 + self.exp_rate = 0 + + def epoch_reset(self): + self.word_right = [] + self.exp_right = [] + self.word_total_length = 0 + self.exp_total_num = 0 + + +class LaTeXOCRMetric(object): + def __init__(self, main_indicator="exp_rate", cal_bleu_score=False, **kwargs): + self.main_indicator = main_indicator + self.cal_bleu_score = cal_bleu_score + self.edit_right = [] + self.exp_right = [] + self.bleu_right = [] + self.e1_right = [] + self.e2_right = [] + self.e3_right = [] + self.editdistance_total_length = 0 + self.exp_total_num = 0 + self.edit_dist = 0 + self.exp_rate = 0 + if self.cal_bleu_score: + self.bleu_score = 0 + self.e1 = 0 + self.e2 = 0 + self.e3 = 0 + self.reset() + self.epoch_reset() + + def __call__(self, preds, batch, **kwargs): + for k, v in kwargs.items(): + epoch_reset = v + if epoch_reset: + self.epoch_reset() + word_pred = preds + word_label = batch + line_right, e1, e2, e3 = 0, 0, 0, 0 + lev_dist = [] + for labels, prediction in zip(word_label, word_pred): + if prediction == labels: + line_right += 1 + distance = compute_edit_distance(prediction, labels) + lev_dist.append(Levenshtein.normalized_distance(prediction, labels)) + if distance <= 1: + e1 += 1 + if distance <= 2: + e2 += 1 + if distance <= 3: + e3 += 1 + + batch_size = len(lev_dist) + + self.edit_dist = sum(lev_dist) # float + self.exp_rate = line_right # float + if self.cal_bleu_score: + self.bleu_score = compute_bleu_score(word_pred, word_label) + self.e1 = e1 + self.e2 = e2 + self.e3 = e3 + exp_length = len(word_label) + self.edit_right.append(self.edit_dist) + self.exp_right.append(self.exp_rate) + if self.cal_bleu_score: + self.bleu_right.append(self.bleu_score * batch_size) + self.e1_right.append(self.e1) + self.e2_right.append(self.e2) + self.e3_right.append(self.e3) + self.editdistance_total_length = self.editdistance_total_length + exp_length + self.exp_total_num = self.exp_total_num + exp_length + + def get_metric(self): + """ + return { + 'edit distance': 0, + "bleu_score": 0, + "exp_rate": 0, + } + """ + cur_edit_distance = sum(self.edit_right) / self.exp_total_num + cur_exp_rate = sum(self.exp_right) / self.exp_total_num + if self.cal_bleu_score: + cur_bleu_score = sum(self.bleu_right) / self.editdistance_total_length + cur_exp_1 = sum(self.e1_right) / self.exp_total_num + cur_exp_2 = sum(self.e2_right) / self.exp_total_num + cur_exp_3 = sum(self.e3_right) / self.exp_total_num + self.reset() + if self.cal_bleu_score: + return { + "bleu_score": cur_bleu_score, + "edit distance": cur_edit_distance, + "exp_rate": cur_exp_rate, + "exp_rate<=1 ": cur_exp_1, + "exp_rate<=2 ": cur_exp_2, + "exp_rate<=3 ": cur_exp_3, + } + else: + + return { + "edit distance": cur_edit_distance, + "exp_rate": cur_exp_rate, + "exp_rate<=1 ": cur_exp_1, + "exp_rate<=2 ": cur_exp_2, + "exp_rate<=3 ": cur_exp_3, + } + + def reset(self): + self.edit_dist = 0 + self.exp_rate = 0 + if self.cal_bleu_score: + self.bleu_score = 0 + self.e1 = 0 + self.e2 = 0 + self.e3 = 0 + + def epoch_reset(self): + self.edit_right = [] + self.exp_right = [] + if self.cal_bleu_score: + self.bleu_right = [] + self.e1_right = [] + self.e2_right = [] + self.e3_right = [] + self.editdistance_total_length = 0 + self.exp_total_num = 0 diff --git a/modules/onnx_ocr_module/src/ppocr/metrics/sr_metric.py b/modules/onnx_ocr_module/src/ppocr/metrics/sr_metric.py new file mode 100644 index 0000000..ef9ef96 --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/metrics/sr_metric.py @@ -0,0 +1,161 @@ +# copyright (c) 2022 PaddlePaddle Authors. All Rights Reserve. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +""" +https://github.com/FudanVI/FudanOCR/blob/main/text-gestalt/utils/ssim_psnr.py +""" + +from math import exp + +import paddle +import paddle.nn.functional as F +import paddle.nn as nn +import string + + +class SSIM(nn.Layer): + def __init__(self, window_size=11, size_average=True): + super(SSIM, self).__init__() + self.window_size = window_size + self.size_average = size_average + self.channel = 1 + self.window = self.create_window(window_size, self.channel) + + def gaussian(self, window_size, sigma): + gauss = paddle.to_tensor( + [ + exp(-((x - window_size // 2) ** 2) / float(2 * sigma**2)) + for x in range(window_size) + ] + ) + return gauss / gauss.sum() + + def create_window(self, window_size, channel): + _1D_window = self.gaussian(window_size, 1.5).unsqueeze(1) + _2D_window = _1D_window.mm(_1D_window.t()).unsqueeze(0).unsqueeze(0) + window = _2D_window.expand([channel, 1, window_size, window_size]) + return window + + def _ssim(self, img1, img2, window, window_size, channel, size_average=True): + mu1 = F.conv2d(img1, window, padding=window_size // 2, groups=channel) + mu2 = F.conv2d(img2, window, padding=window_size // 2, groups=channel) + + mu1_sq = mu1.pow(2) + mu2_sq = mu2.pow(2) + mu1_mu2 = mu1 * mu2 + + sigma1_sq = ( + F.conv2d(img1 * img1, window, padding=window_size // 2, groups=channel) + - mu1_sq + ) + sigma2_sq = ( + F.conv2d(img2 * img2, window, padding=window_size // 2, groups=channel) + - mu2_sq + ) + sigma12 = ( + F.conv2d(img1 * img2, window, padding=window_size // 2, groups=channel) + - mu1_mu2 + ) + + C1 = 0.01**2 + C2 = 0.03**2 + + ssim_map = ((2 * mu1_mu2 + C1) * (2 * sigma12 + C2)) / ( + (mu1_sq + mu2_sq + C1) * (sigma1_sq + sigma2_sq + C2) + ) + + if size_average: + return ssim_map.mean() + else: + return ssim_map.mean([1, 2, 3]) + + def ssim(self, img1, img2, window_size=11, size_average=True): + (_, channel, _, _) = img1.shape + window = self.create_window(window_size, channel) + + return self._ssim(img1, img2, window, window_size, channel, size_average) + + def forward(self, img1, img2): + (_, channel, _, _) = img1.shape + + if channel == self.channel and self.window.dtype == img1.dtype: + window = self.window + else: + window = self.create_window(self.window_size, channel) + + self.window = window + self.channel = channel + + return self._ssim( + img1, img2, window, self.window_size, channel, self.size_average + ) + + +class SRMetric(object): + def __init__(self, main_indicator="all", **kwargs): + self.main_indicator = main_indicator + self.eps = 1e-5 + self.psnr_result = [] + self.ssim_result = [] + self.calculate_ssim = SSIM() + self.reset() + + def reset(self): + self.correct_num = 0 + self.all_num = 0 + self.norm_edit_dis = 0 + self.psnr_result = [] + self.ssim_result = [] + + def calculate_psnr(self, img1, img2): + # img1 and img2 have range [0, 1] + mse = ((img1 * 255 - img2 * 255) ** 2).mean() + if mse == 0: + return float("inf") + return 20 * paddle.log10(255.0 / paddle.sqrt(mse)) + + def _normalize_text(self, text): + text = "".join( + filter(lambda x: x in (string.digits + string.ascii_letters), text) + ) + return text.lower() + + def __call__(self, pred_label, *args, **kwargs): + metric = {} + images_sr = pred_label["sr_img"] + images_hr = pred_label["hr_img"] + psnr = self.calculate_psnr(images_sr, images_hr) + ssim = self.calculate_ssim(images_sr, images_hr) + self.psnr_result.append(psnr) + self.ssim_result.append(ssim) + + def get_metric(self): + """ + return metrics { + 'acc': 0, + 'norm_edit_dis': 0, + } + """ + self.psnr_avg = sum(self.psnr_result) / len(self.psnr_result) + self.psnr_avg = round(self.psnr_avg.item(), 6) + self.ssim_avg = sum(self.ssim_result) / len(self.ssim_result) + self.ssim_avg = round(self.ssim_avg.item(), 6) + + self.all_avg = self.psnr_avg + self.ssim_avg + + self.reset() + return { + "psnr_avg": self.psnr_avg, + "ssim_avg": self.ssim_avg, + "all": self.all_avg, + } diff --git a/modules/onnx_ocr_module/src/ppocr/metrics/table_metric.py b/modules/onnx_ocr_module/src/ppocr/metrics/table_metric.py new file mode 100644 index 0000000..98847e1 --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/metrics/table_metric.py @@ -0,0 +1,161 @@ +# copyright (c) 2020 PaddlePaddle Authors. All Rights Reserve. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +import numpy as np +from ppocr.metrics.det_metric import DetMetric + + +class TableStructureMetric(object): + def __init__(self, main_indicator="acc", eps=1e-6, del_thead_tbody=False, **kwargs): + self.main_indicator = main_indicator + self.eps = eps + self.del_thead_tbody = del_thead_tbody + self.reset() + + def __call__(self, pred_label, batch=None, *args, **kwargs): + preds, labels = pred_label + pred_structure_batch_list = preds["structure_batch_list"] + gt_structure_batch_list = labels["structure_batch_list"] + correct_num = 0 + all_num = 0 + for (pred, pred_conf), target in zip( + pred_structure_batch_list, gt_structure_batch_list + ): + pred_str = "".join(pred) + target_str = "".join(target) + if self.del_thead_tbody: + pred_str = ( + pred_str.replace("", "") + .replace("", "") + .replace("", "") + .replace("", "") + ) + target_str = ( + target_str.replace("", "") + .replace("", "") + .replace("", "") + .replace("", "") + ) + if pred_str == target_str: + correct_num += 1 + all_num += 1 + self.correct_num += correct_num + self.all_num += all_num + + def get_metric(self): + """ + return metrics { + 'acc': 0, + } + """ + acc = 1.0 * self.correct_num / (self.all_num + self.eps) + self.reset() + return {"acc": acc} + + def reset(self): + self.correct_num = 0 + self.all_num = 0 + self.len_acc_num = 0 + self.token_nums = 0 + self.anys_dict = dict() + + +class TableMetric(object): + def __init__( + self, + main_indicator="acc", + compute_bbox_metric=False, + box_format="xyxy", + del_thead_tbody=False, + **kwargs, + ): + """ + + @param sub_metrics: configs of sub_metric + @param main_matric: main_matric for save best_model + @param kwargs: + """ + self.structure_metric = TableStructureMetric(del_thead_tbody=del_thead_tbody) + self.bbox_metric = DetMetric() if compute_bbox_metric else None + self.main_indicator = main_indicator + self.box_format = box_format + self.reset() + + def __call__(self, pred_label, batch=None, *args, **kwargs): + self.structure_metric(pred_label) + if self.bbox_metric is not None: + self.bbox_metric(*self.prepare_bbox_metric_input(pred_label)) + + def prepare_bbox_metric_input(self, pred_label): + pred_bbox_batch_list = [] + gt_ignore_tags_batch_list = [] + gt_bbox_batch_list = [] + preds, labels = pred_label + + batch_num = len(preds["bbox_batch_list"]) + for batch_idx in range(batch_num): + # pred + pred_bbox_list = [ + self.format_box(pred_box) + for pred_box in preds["bbox_batch_list"][batch_idx] + ] + pred_bbox_batch_list.append({"points": pred_bbox_list}) + + # gt + gt_bbox_list = [] + gt_ignore_tags_list = [] + for gt_box in labels["bbox_batch_list"][batch_idx]: + gt_bbox_list.append(self.format_box(gt_box)) + gt_ignore_tags_list.append(0) + gt_bbox_batch_list.append(gt_bbox_list) + gt_ignore_tags_batch_list.append(gt_ignore_tags_list) + + return [ + pred_bbox_batch_list, + [0, 0, gt_bbox_batch_list, gt_ignore_tags_batch_list], + ] + + def get_metric(self): + structure_metric = self.structure_metric.get_metric() + if self.bbox_metric is None: + return structure_metric + bbox_metric = self.bbox_metric.get_metric() + if self.main_indicator == self.bbox_metric.main_indicator: + output = bbox_metric + for sub_key in structure_metric: + output["structure_metric_{}".format(sub_key)] = structure_metric[ + sub_key + ] + else: + output = structure_metric + for sub_key in bbox_metric: + output["bbox_metric_{}".format(sub_key)] = bbox_metric[sub_key] + return output + + def reset(self): + self.structure_metric.reset() + if self.bbox_metric is not None: + self.bbox_metric.reset() + + def format_box(self, box): + if self.box_format == "xyxy": + x1, y1, x2, y2 = box + box = [[x1, y1], [x2, y1], [x2, y2], [x1, y2]] + elif self.box_format == "xywh": + x, y, w, h = box + x1, y1, x2, y2 = x - w // 2, y - h // 2, x + w // 2, y + h // 2 + box = [[x1, y1], [x2, y1], [x2, y2], [x1, y2]] + elif self.box_format == "xyxyxyxy": + x1, y1, x2, y2, x3, y3, x4, y4 = box + box = [[x1, y1], [x2, y2], [x3, y3], [x4, y4]] + return box diff --git a/modules/onnx_ocr_module/src/ppocr/metrics/vqa_token_re_metric.py b/modules/onnx_ocr_module/src/ppocr/metrics/vqa_token_re_metric.py new file mode 100644 index 0000000..8c85be5 --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/metrics/vqa_token_re_metric.py @@ -0,0 +1,191 @@ +# Copyright (c) 2021 PaddlePaddle Authors. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import numpy as np +import paddle + +__all__ = ["VQAReTokenMetric"] + + +class VQAReTokenMetric(object): + def __init__(self, main_indicator="hmean", **kwargs): + self.main_indicator = main_indicator + self.reset() + + def __call__(self, preds, batch, **kwargs): + pred_relations, relations, entities = preds + self.pred_relations_list.extend(pred_relations) + self.relations_list.extend(relations) + self.entities_list.extend(entities) + + def get_metric(self): + gt_relations = [] + for b in range(len(self.relations_list)): + rel_sent = [] + relation_list = self.relations_list[b] + entitie_list = self.entities_list[b] + head_len = relation_list[0, 0] + if head_len > 0: + entitie_start_list = entitie_list[1 : entitie_list[0, 0] + 1, 0] + entitie_end_list = entitie_list[1 : entitie_list[0, 1] + 1, 1] + entitie_label_list = entitie_list[1 : entitie_list[0, 2] + 1, 2] + for head, tail in zip( + relation_list[1 : head_len + 1, 0], + relation_list[1 : head_len + 1, 1], + ): + rel = {} + rel["head_id"] = head + rel["head"] = (entitie_start_list[head], entitie_end_list[head]) + rel["head_type"] = entitie_label_list[head] + + rel["tail_id"] = tail + rel["tail"] = (entitie_start_list[tail], entitie_end_list[tail]) + rel["tail_type"] = entitie_label_list[tail] + + rel["type"] = 1 + rel_sent.append(rel) + gt_relations.append(rel_sent) + re_metrics = self.re_score( + self.pred_relations_list, gt_relations, mode="boundaries" + ) + metrics = { + "precision": re_metrics["ALL"]["p"], + "recall": re_metrics["ALL"]["r"], + "hmean": re_metrics["ALL"]["f1"], + } + self.reset() + return metrics + + def reset(self): + self.pred_relations_list = [] + self.relations_list = [] + self.entities_list = [] + + def re_score(self, pred_relations, gt_relations, mode="strict"): + """Evaluate RE predictions + + Args: + pred_relations (list) : list of list of predicted relations (several relations in each sentence) + gt_relations (list) : list of list of ground truth relations + + rel = { "head": (start_idx (inclusive), end_idx (exclusive)), + "tail": (start_idx (inclusive), end_idx (exclusive)), + "head_type": ent_type, + "tail_type": ent_type, + "type": rel_type} + + vocab (Vocab) : dataset vocabulary + mode (str) : in 'strict' or 'boundaries'""" + + assert mode in ["strict", "boundaries"] + + relation_types = [v for v in [0, 1] if not v == 0] + scores = {rel: {"tp": 0, "fp": 0, "fn": 0} for rel in relation_types + ["ALL"]} + + # Count GT relations and Predicted relations + n_sents = len(gt_relations) + n_rels = sum([len([rel for rel in sent]) for sent in gt_relations]) + n_found = sum([len([rel for rel in sent]) for sent in pred_relations]) + + # Count TP, FP and FN per type + for pred_sent, gt_sent in zip(pred_relations, gt_relations): + for rel_type in relation_types: + # strict mode takes argument types into account + if mode == "strict": + pred_rels = { + (rel["head"], rel["head_type"], rel["tail"], rel["tail_type"]) + for rel in pred_sent + if rel["type"] == rel_type + } + gt_rels = { + (rel["head"], rel["head_type"], rel["tail"], rel["tail_type"]) + for rel in gt_sent + if rel["type"] == rel_type + } + + # boundaries mode only takes argument spans into account + elif mode == "boundaries": + pred_rels = { + (rel["head"], rel["tail"]) + for rel in pred_sent + if rel["type"] == rel_type + } + gt_rels = { + (rel["head"], rel["tail"]) + for rel in gt_sent + if rel["type"] == rel_type + } + + scores[rel_type]["tp"] += len(pred_rels & gt_rels) + scores[rel_type]["fp"] += len(pred_rels - gt_rels) + scores[rel_type]["fn"] += len(gt_rels - pred_rels) + + # Compute per entity Precision / Recall / F1 + for rel_type in scores.keys(): + if scores[rel_type]["tp"]: + scores[rel_type]["p"] = scores[rel_type]["tp"] / ( + scores[rel_type]["fp"] + scores[rel_type]["tp"] + ) + scores[rel_type]["r"] = scores[rel_type]["tp"] / ( + scores[rel_type]["fn"] + scores[rel_type]["tp"] + ) + else: + scores[rel_type]["p"], scores[rel_type]["r"] = 0, 0 + + if not scores[rel_type]["p"] + scores[rel_type]["r"] == 0: + scores[rel_type]["f1"] = ( + 2 + * scores[rel_type]["p"] + * scores[rel_type]["r"] + / (scores[rel_type]["p"] + scores[rel_type]["r"]) + ) + else: + scores[rel_type]["f1"] = 0 + + # Compute micro F1 Scores + tp = sum([scores[rel_type]["tp"] for rel_type in relation_types]) + fp = sum([scores[rel_type]["fp"] for rel_type in relation_types]) + fn = sum([scores[rel_type]["fn"] for rel_type in relation_types]) + + if tp: + precision = tp / (tp + fp) + recall = tp / (tp + fn) + f1 = 2 * precision * recall / (precision + recall) + + else: + precision, recall, f1 = 0, 0, 0 + + scores["ALL"]["p"] = precision + scores["ALL"]["r"] = recall + scores["ALL"]["f1"] = f1 + scores["ALL"]["tp"] = tp + scores["ALL"]["fp"] = fp + scores["ALL"]["fn"] = fn + + # Compute Macro F1 Scores + scores["ALL"]["Macro_f1"] = np.mean( + [scores[ent_type]["f1"] for ent_type in relation_types] + ) + scores["ALL"]["Macro_p"] = np.mean( + [scores[ent_type]["p"] for ent_type in relation_types] + ) + scores["ALL"]["Macro_r"] = np.mean( + [scores[ent_type]["r"] for ent_type in relation_types] + ) + + return scores diff --git a/modules/onnx_ocr_module/src/ppocr/metrics/vqa_token_ser_metric.py b/modules/onnx_ocr_module/src/ppocr/metrics/vqa_token_ser_metric.py new file mode 100644 index 0000000..3afcb05 --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/metrics/vqa_token_ser_metric.py @@ -0,0 +1,48 @@ +# Copyright (c) 2021 PaddlePaddle Authors. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import numpy as np +import paddle + +__all__ = ["VQASerTokenMetric"] + + +class VQASerTokenMetric(object): + def __init__(self, main_indicator="hmean", **kwargs): + self.main_indicator = main_indicator + self.reset() + + def __call__(self, preds, batch, **kwargs): + preds, labels = preds + self.pred_list.extend(preds) + self.gt_list.extend(labels) + + def get_metric(self): + from seqeval.metrics import f1_score, precision_score, recall_score + + metrics = { + "precision": precision_score(self.gt_list, self.pred_list), + "recall": recall_score(self.gt_list, self.pred_list), + "hmean": f1_score(self.gt_list, self.pred_list), + } + self.reset() + return metrics + + def reset(self): + self.pred_list = [] + self.gt_list = [] diff --git a/modules/onnx_ocr_module/src/ppocr/modeling/architectures/__init__.py b/modules/onnx_ocr_module/src/ppocr/modeling/architectures/__init__.py new file mode 100644 index 0000000..65b6318 --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/modeling/architectures/__init__.py @@ -0,0 +1,145 @@ +# Copyright (c) 2020 PaddlePaddle Authors. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import copy +import importlib + +from paddle.jit import to_static +from paddle.static import InputSpec + +from .base_model import BaseModel +from .distillation_model import DistillationModel + +__all__ = ["build_model", "apply_to_static"] + + +def build_model(config): + config = copy.deepcopy(config) + if not "name" in config: + arch = BaseModel(config) + else: + name = config.pop("name") + mod = importlib.import_module(__name__) + arch = getattr(mod, name)(config) + return arch + + +def apply_to_static(model, config, logger): + if config["Global"].get("to_static", False) is not True: + return model + assert ( + "d2s_train_image_shape" in config["Global"] + ), "d2s_train_image_shape must be assigned for static training mode..." + supported_list = [ + "DB", + "SVTR_LCNet", + "TableMaster", + "LayoutXLM", + "SLANet", + "SVTR", + "SVTR_HGNet", + "LaTeXOCR", + "UniMERNet", + "PP-FormulaNet-S", + "PP-FormulaNet-L", + ] + if config["Architecture"]["algorithm"] in ["Distillation"]: + algo = list(config["Architecture"]["Models"].values())[0]["algorithm"] + else: + algo = config["Architecture"]["algorithm"] + assert ( + algo in supported_list + ), f"algorithms that supports static training must in in {supported_list} but got {algo}" + + specs = [ + InputSpec([None] + config["Global"]["d2s_train_image_shape"], dtype="float32") + ] + + if algo == "SVTR_LCNet": + specs.append( + [ + InputSpec([None, config["Global"]["max_text_length"]], dtype="int64"), + InputSpec([None, config["Global"]["max_text_length"]], dtype="int64"), + InputSpec([None], dtype="int64"), + InputSpec([None], dtype="float64"), + ] + ) + elif algo == "TableMaster": + specs.append( + [ + InputSpec([None, config["Global"]["max_text_length"]], dtype="int64"), + InputSpec( + [None, config["Global"]["max_text_length"], 4], dtype="float32" + ), + InputSpec( + [None, config["Global"]["max_text_length"], 1], dtype="float32" + ), + InputSpec([None, 6], dtype="float32"), + ] + ) + elif algo == "LayoutXLM": + specs = [ + [ + InputSpec(shape=[None, 512], dtype="int64"), # input_ids + InputSpec(shape=[None, 512, 4], dtype="int64"), # bbox + InputSpec(shape=[None, 512], dtype="int64"), # attention_mask + InputSpec(shape=[None, 512], dtype="int64"), # token_type_ids + InputSpec(shape=[None, 3, 224, 224], dtype="float32"), # image + InputSpec(shape=[None, 512], dtype="int64"), # label + ] + ] + elif algo == "SLANet": + specs.append( + [ + InputSpec( + [None, config["Global"]["max_text_length"] + 2], dtype="int64" + ), + InputSpec( + [None, config["Global"]["max_text_length"] + 2, 4], dtype="float32" + ), + InputSpec( + [None, config["Global"]["max_text_length"] + 2, 1], dtype="float32" + ), + InputSpec([None], dtype="int64"), + InputSpec([None, 6], dtype="float64"), + ] + ) + elif algo == "SVTR": + specs.append( + [ + InputSpec([None, config["Global"]["max_text_length"]], dtype="int64"), + InputSpec([None], dtype="int64"), + ] + ) + elif algo == "LaTeXOCR": + specs = [ + [ + InputSpec(shape=[None, 1, None, None], dtype="float32"), + InputSpec(shape=[None, None], dtype="float32"), + InputSpec(shape=[None, None], dtype="float32"), + ] + ] + elif algo in ["UniMERNet", "PP-FormulaNet-S", "PP-FormulaNet-L"]: + specs = [ + [ + InputSpec( + [None] + config["Global"]["d2s_train_image_shape"], dtype="float32" + ), + InputSpec(shape=[None, None], dtype="float32"), + InputSpec(shape=[None, None], dtype="float32"), + ] + ] + model = to_static(model, input_spec=specs) + logger.info("Successfully to apply @to_static with specs: {}".format(specs)) + return model diff --git a/modules/onnx_ocr_module/src/ppocr/modeling/architectures/base_model.py b/modules/onnx_ocr_module/src/ppocr/modeling/architectures/base_model.py new file mode 100644 index 0000000..036f90c --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/modeling/architectures/base_model.py @@ -0,0 +1,117 @@ +# Copyright (c) 2021 PaddlePaddle Authors. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +from paddle import nn +from ppocr.modeling.transforms import build_transform +from ppocr.modeling.backbones import build_backbone +from ppocr.modeling.necks import build_neck +from ppocr.modeling.heads import build_head + +__all__ = ["BaseModel"] + + +class BaseModel(nn.Layer): + def __init__(self, config): + """ + the module for OCR. + args: + config (dict): the super parameters for module. + """ + super(BaseModel, self).__init__() + in_channels = config.get("in_channels", 3) + model_type = config["model_type"] + # build transform, + # for rec, transform can be TPS,None + # for det and cls, transform should to be None, + # if you make model differently, you can use transform in det and cls + if "Transform" not in config or config["Transform"] is None: + self.use_transform = False + else: + self.use_transform = True + config["Transform"]["in_channels"] = in_channels + self.transform = build_transform(config["Transform"]) + in_channels = self.transform.out_channels + + # build backbone, backbone is need for del, rec and cls + if "Backbone" not in config or config["Backbone"] is None: + self.use_backbone = False + else: + self.use_backbone = True + config["Backbone"]["in_channels"] = in_channels + self.backbone = build_backbone(config["Backbone"], model_type) + in_channels = self.backbone.out_channels + + # build neck + # for rec, neck can be cnn,rnn or reshape(None) + # for det, neck can be FPN, BIFPN and so on. + # for cls, neck should be none + if "Neck" not in config or config["Neck"] is None: + self.use_neck = False + else: + self.use_neck = True + config["Neck"]["in_channels"] = in_channels + self.neck = build_neck(config["Neck"]) + in_channels = self.neck.out_channels + + # # build head, head is need for det, rec and cls + if "Head" not in config or config["Head"] is None: + self.use_head = False + else: + self.use_head = True + config["Head"]["in_channels"] = in_channels + self.head = build_head(config["Head"]) + + self.return_all_feats = config.get("return_all_feats", False) + + def forward(self, x, data=None): + y = dict() + if self.use_transform: + x = self.transform(x) + if self.use_backbone: + x = self.backbone(x) + if isinstance(x, dict): + y.update(x) + else: + y["backbone_out"] = x + final_name = "backbone_out" + if self.use_neck: + x = self.neck(x) + if isinstance(x, dict): + y.update(x) + else: + y["neck_out"] = x + final_name = "neck_out" + if self.use_head: + x = self.head(x, targets=data) + # for multi head, save ctc neck out for udml + if isinstance(x, dict) and "ctc_neck" in x.keys(): + y["neck_out"] = x["ctc_neck"] + y["head_out"] = x + elif isinstance(x, dict): + y.update(x) + else: + y["head_out"] = x + final_name = "head_out" + if self.return_all_feats: + if self.training: + return y + elif isinstance(x, dict): + return x + else: + return {final_name: x} + else: + return x diff --git a/modules/onnx_ocr_module/src/ppocr/modeling/architectures/distillation_model.py b/modules/onnx_ocr_module/src/ppocr/modeling/architectures/distillation_model.py new file mode 100644 index 0000000..2309d2e --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/modeling/architectures/distillation_model.py @@ -0,0 +1,60 @@ +# Copyright (c) 2021 PaddlePaddle Authors. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +from paddle import nn +from ppocr.modeling.transforms import build_transform +from ppocr.modeling.backbones import build_backbone +from ppocr.modeling.necks import build_neck +from ppocr.modeling.heads import build_head +from .base_model import BaseModel +from ppocr.utils.save_load import load_pretrained_params + +__all__ = ["DistillationModel"] + + +class DistillationModel(nn.Layer): + def __init__(self, config): + """ + the module for OCR distillation. + args: + config (dict): the super parameters for module. + """ + super().__init__() + self.model_list = [] + self.model_name_list = [] + for key in config["Models"]: + model_config = config["Models"][key] + freeze_params = False + pretrained = None + if "freeze_params" in model_config: + freeze_params = model_config.pop("freeze_params") + if "pretrained" in model_config: + pretrained = model_config.pop("pretrained") + model = BaseModel(model_config) + if pretrained is not None: + load_pretrained_params(model, pretrained) + if freeze_params: + for param in model.parameters(): + param.trainable = False + self.model_list.append(self.add_sublayer(key, model)) + self.model_name_list.append(key) + + def forward(self, x, data=None): + result_dict = dict() + for idx, model_name in enumerate(self.model_name_list): + result_dict[model_name] = self.model_list[idx](x, data) + return result_dict diff --git a/modules/onnx_ocr_module/src/ppocr/modeling/backbones/__init__.py b/modules/onnx_ocr_module/src/ppocr/modeling/backbones/__init__.py new file mode 100644 index 0000000..efb2016 --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/modeling/backbones/__init__.py @@ -0,0 +1,144 @@ +# Copyright (c) 2020 PaddlePaddle Authors. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +__all__ = ["build_backbone"] + + +def build_backbone(config, model_type): + if model_type == "det" or model_type == "table": + from .det_mobilenet_v3 import MobileNetV3 + from .det_resnet import ResNet + from .det_resnet_vd import ResNet_vd + from .det_resnet_vd_sast import ResNet_SAST + from .det_pp_lcnet import PPLCNet + from .rec_lcnetv3 import PPLCNetV3 + from .rec_hgnet import PPHGNet_small + from .rec_vit import ViT + from .det_pp_lcnet_v2 import PPLCNetV2_base + from .rec_repvit import RepSVTR_det + from .rec_vary_vit import Vary_VIT_B + + support_dict = [ + "MobileNetV3", + "ResNet", + "ResNet_vd", + "ResNet_SAST", + "PPLCNet", + "PPLCNetV3", + "PPHGNet_small", + "PPLCNetV2_base", + "RepSVTR_det", + "Vary_VIT_B", + ] + if model_type == "table": + from .table_master_resnet import TableResNetExtra + + support_dict.append("TableResNetExtra") + elif model_type == "rec" or model_type == "cls": + from .rec_mobilenet_v3 import MobileNetV3 + from .rec_resnet_vd import ResNet + from .rec_resnet_fpn import ResNetFPN + from .rec_mv1_enhance import MobileNetV1Enhance + from .rec_nrtr_mtb import MTB + from .rec_resnet_31 import ResNet31 + from .rec_resnet_32 import ResNet32 + from .rec_resnet_45 import ResNet45 + from .rec_resnet_aster import ResNet_ASTER + from .rec_micronet import MicroNet + from .rec_efficientb3_pren import EfficientNetb3_PREN + from .rec_svtrnet import SVTRNet + from .rec_vitstr import ViTSTR + from .rec_resnet_rfl import ResNetRFL + from .rec_densenet import DenseNet + from .rec_resnetv2 import ResNetV2 + from .rec_hybridvit import HybridTransformer + from .rec_donut_swin import DonutSwinModel + from .rec_shallow_cnn import ShallowCNN + from .rec_lcnetv3 import PPLCNetV3 + from .rec_hgnet import PPHGNet_small + from .rec_vit_parseq import ViTParseQ + from .rec_repvit import RepSVTR + from .rec_svtrv2 import SVTRv2 + from .rec_vary_vit import Vary_VIT_B, Vary_VIT_B_Formula + from .rec_pphgnetv2 import PPHGNetV2_B4 + + support_dict = [ + "MobileNetV1Enhance", + "MobileNetV3", + "ResNet", + "ResNetFPN", + "MTB", + "ResNet31", + "ResNet45", + "ResNet_ASTER", + "MicroNet", + "EfficientNetb3_PREN", + "SVTRNet", + "ViTSTR", + "ResNet32", + "ResNetRFL", + "DenseNet", + "ShallowCNN", + "PPLCNetV3", + "PPHGNet_small", + "ViTParseQ", + "ViT", + "RepSVTR", + "SVTRv2", + "ResNetV2", + "HybridTransformer", + "DonutSwinModel", + "Vary_VIT_B", + "PPHGNetV2_B4", + "Vary_VIT_B_Formula", + ] + elif model_type == "e2e": + from .e2e_resnet_vd_pg import ResNet + + support_dict = ["ResNet"] + elif model_type == "kie": + from .kie_unet_sdmgr import Kie_backbone + from .vqa_layoutlm import ( + LayoutLMForSer, + LayoutLMv2ForSer, + LayoutLMv2ForRe, + LayoutXLMForSer, + LayoutXLMForRe, + ) + + support_dict = [ + "Kie_backbone", + "LayoutLMForSer", + "LayoutLMv2ForSer", + "LayoutLMv2ForRe", + "LayoutXLMForSer", + "LayoutXLMForRe", + ] + elif model_type == "table": + from .table_resnet_vd import ResNet + from .table_mobilenet_v3 import MobileNetV3 + from .rec_vary_vit import Vary_VIT_B + + support_dict = ["ResNet", "MobileNetV3", "Vary_VIT_B"] + else: + raise NotImplementedError + + module_name = config.pop("name") + assert module_name in support_dict, Exception( + "when model typs is {}, backbone only support {}".format( + model_type, support_dict + ) + ) + module_class = eval(module_name)(**config) + return module_class diff --git a/modules/onnx_ocr_module/src/ppocr/modeling/backbones/det_mobilenet_v3.py b/modules/onnx_ocr_module/src/ppocr/modeling/backbones/det_mobilenet_v3.py new file mode 100644 index 0000000..5ff08c6 --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/modeling/backbones/det_mobilenet_v3.py @@ -0,0 +1,289 @@ +# copyright (c) 2020 PaddlePaddle Authors. All Rights Reserve. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import paddle +from paddle import nn +import paddle.nn.functional as F +from paddle import ParamAttr +from ppocr.modeling.backbones.rec_hgnet import MeanPool2D + +__all__ = ["MobileNetV3"] + + +def make_divisible(v, divisor=8, min_value=None): + if min_value is None: + min_value = divisor + new_v = max(min_value, int(v + divisor / 2) // divisor * divisor) + if new_v < 0.9 * v: + new_v += divisor + return new_v + + +class MobileNetV3(nn.Layer): + def __init__( + self, in_channels=3, model_name="large", scale=0.5, disable_se=False, **kwargs + ): + """ + the MobilenetV3 backbone network for detection module. + Args: + params(dict): the super parameters for build network + """ + super(MobileNetV3, self).__init__() + + self.disable_se = disable_se + + if model_name == "large": + cfg = [ + # k, exp, c, se, nl, s, + [3, 16, 16, False, "relu", 1], + [3, 64, 24, False, "relu", 2], + [3, 72, 24, False, "relu", 1], + [5, 72, 40, True, "relu", 2], + [5, 120, 40, True, "relu", 1], + [5, 120, 40, True, "relu", 1], + [3, 240, 80, False, "hardswish", 2], + [3, 200, 80, False, "hardswish", 1], + [3, 184, 80, False, "hardswish", 1], + [3, 184, 80, False, "hardswish", 1], + [3, 480, 112, True, "hardswish", 1], + [3, 672, 112, True, "hardswish", 1], + [5, 672, 160, True, "hardswish", 2], + [5, 960, 160, True, "hardswish", 1], + [5, 960, 160, True, "hardswish", 1], + ] + cls_ch_squeeze = 960 + elif model_name == "small": + cfg = [ + # k, exp, c, se, nl, s, + [3, 16, 16, True, "relu", 2], + [3, 72, 24, False, "relu", 2], + [3, 88, 24, False, "relu", 1], + [5, 96, 40, True, "hardswish", 2], + [5, 240, 40, True, "hardswish", 1], + [5, 240, 40, True, "hardswish", 1], + [5, 120, 48, True, "hardswish", 1], + [5, 144, 48, True, "hardswish", 1], + [5, 288, 96, True, "hardswish", 2], + [5, 576, 96, True, "hardswish", 1], + [5, 576, 96, True, "hardswish", 1], + ] + cls_ch_squeeze = 576 + else: + raise NotImplementedError( + "mode[" + model_name + "_model] is not implemented!" + ) + + supported_scale = [0.35, 0.5, 0.75, 1.0, 1.25] + assert ( + scale in supported_scale + ), "supported scale are {} but input scale is {}".format(supported_scale, scale) + inplanes = 16 + # conv1 + self.conv = ConvBNLayer( + in_channels=in_channels, + out_channels=make_divisible(inplanes * scale), + kernel_size=3, + stride=2, + padding=1, + groups=1, + if_act=True, + act="hardswish", + ) + + self.stages = [] + self.out_channels = [] + block_list = [] + i = 0 + inplanes = make_divisible(inplanes * scale) + for k, exp, c, se, nl, s in cfg: + se = se and not self.disable_se + start_idx = 2 if model_name == "large" else 0 + if s == 2 and i > start_idx: + self.out_channels.append(inplanes) + self.stages.append(nn.Sequential(*block_list)) + block_list = [] + block_list.append( + ResidualUnit( + in_channels=inplanes, + mid_channels=make_divisible(scale * exp), + out_channels=make_divisible(scale * c), + kernel_size=k, + stride=s, + use_se=se, + act=nl, + ) + ) + inplanes = make_divisible(scale * c) + i += 1 + block_list.append( + ConvBNLayer( + in_channels=inplanes, + out_channels=make_divisible(scale * cls_ch_squeeze), + kernel_size=1, + stride=1, + padding=0, + groups=1, + if_act=True, + act="hardswish", + ) + ) + self.stages.append(nn.Sequential(*block_list)) + self.out_channels.append(make_divisible(scale * cls_ch_squeeze)) + for i, stage in enumerate(self.stages): + self.add_sublayer(sublayer=stage, name="stage{}".format(i)) + + def forward(self, x): + x = self.conv(x) + out_list = [] + for stage in self.stages: + x = stage(x) + out_list.append(x) + return out_list + + +class ConvBNLayer(nn.Layer): + def __init__( + self, + in_channels, + out_channels, + kernel_size, + stride, + padding, + groups=1, + if_act=True, + act=None, + ): + super(ConvBNLayer, self).__init__() + self.if_act = if_act + self.act = act + self.conv = nn.Conv2D( + in_channels=in_channels, + out_channels=out_channels, + kernel_size=kernel_size, + stride=stride, + padding=padding, + groups=groups, + bias_attr=False, + ) + + self.bn = nn.BatchNorm(num_channels=out_channels, act=None) + + def forward(self, x): + x = self.conv(x) + x = self.bn(x) + if self.if_act: + if self.act == "relu": + x = F.relu(x) + elif self.act == "hardswish": + x = F.hardswish(x) + else: + print( + "The activation function({}) is selected incorrectly.".format( + self.act + ) + ) + exit() + return x + + +class ResidualUnit(nn.Layer): + def __init__( + self, + in_channels, + mid_channels, + out_channels, + kernel_size, + stride, + use_se, + act=None, + ): + super(ResidualUnit, self).__init__() + self.if_shortcut = stride == 1 and in_channels == out_channels + self.if_se = use_se + + self.expand_conv = ConvBNLayer( + in_channels=in_channels, + out_channels=mid_channels, + kernel_size=1, + stride=1, + padding=0, + if_act=True, + act=act, + ) + self.bottleneck_conv = ConvBNLayer( + in_channels=mid_channels, + out_channels=mid_channels, + kernel_size=kernel_size, + stride=stride, + padding=int((kernel_size - 1) // 2), + groups=mid_channels, + if_act=True, + act=act, + ) + if self.if_se: + self.mid_se = SEModule(mid_channels) + self.linear_conv = ConvBNLayer( + in_channels=mid_channels, + out_channels=out_channels, + kernel_size=1, + stride=1, + padding=0, + if_act=False, + act=None, + ) + + def forward(self, inputs): + x = self.expand_conv(inputs) + x = self.bottleneck_conv(x) + if self.if_se: + x = self.mid_se(x) + x = self.linear_conv(x) + if self.if_shortcut: + x = paddle.add(inputs, x) + return x + + +class SEModule(nn.Layer): + def __init__(self, in_channels, reduction=4): + super(SEModule, self).__init__() + if "npu" in paddle.device.get_device(): + self.avg_pool = MeanPool2D(1, 1) + else: + self.avg_pool = nn.AdaptiveAvgPool2D(1) + self.conv1 = nn.Conv2D( + in_channels=in_channels, + out_channels=in_channels // reduction, + kernel_size=1, + stride=1, + padding=0, + ) + self.conv2 = nn.Conv2D( + in_channels=in_channels // reduction, + out_channels=in_channels, + kernel_size=1, + stride=1, + padding=0, + ) + + def forward(self, inputs): + outputs = self.avg_pool(inputs) + outputs = self.conv1(outputs) + outputs = F.relu(outputs) + outputs = self.conv2(outputs) + outputs = F.hardsigmoid(outputs, slope=0.2, offset=0.5) + return inputs * outputs diff --git a/modules/onnx_ocr_module/src/ppocr/modeling/backbones/det_pp_lcnet.py b/modules/onnx_ocr_module/src/ppocr/modeling/backbones/det_pp_lcnet.py new file mode 100644 index 0000000..bf557a4 --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/modeling/backbones/det_pp_lcnet.py @@ -0,0 +1,274 @@ +# copyright (c) 2021 PaddlePaddle Authors. All Rights Reserve. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from __future__ import absolute_import, division, print_function + +import os +import paddle +import paddle.nn as nn +from paddle import ParamAttr +from paddle.nn import AdaptiveAvgPool2D, BatchNorm, Conv2D, Dropout, Linear +from paddle.regularizer import L2Decay +from paddle.nn.initializer import KaimingNormal +from paddle.utils.download import get_path_from_url + +MODEL_URLS = { + "PPLCNet_x0.25": "https://paddle-imagenet-models-name.bj.bcebos.com/dygraph/legendary_models/PPLCNet_x0_25_pretrained.pdparams", + "PPLCNet_x0.35": "https://paddle-imagenet-models-name.bj.bcebos.com/dygraph/legendary_models/PPLCNet_x0_35_pretrained.pdparams", + "PPLCNet_x0.5": "https://paddle-imagenet-models-name.bj.bcebos.com/dygraph/legendary_models/PPLCNet_x0_5_pretrained.pdparams", + "PPLCNet_x0.75": "https://paddle-imagenet-models-name.bj.bcebos.com/dygraph/legendary_models/PPLCNet_x0_75_pretrained.pdparams", + "PPLCNet_x1.0": "https://paddle-imagenet-models-name.bj.bcebos.com/dygraph/legendary_models/PPLCNet_x1_0_pretrained.pdparams", + "PPLCNet_x1.5": "https://paddle-imagenet-models-name.bj.bcebos.com/dygraph/legendary_models/PPLCNet_x1_5_pretrained.pdparams", + "PPLCNet_x2.0": "https://paddle-imagenet-models-name.bj.bcebos.com/dygraph/legendary_models/PPLCNet_x2_0_pretrained.pdparams", + "PPLCNet_x2.5": "https://paddle-imagenet-models-name.bj.bcebos.com/dygraph/legendary_models/PPLCNet_x2_5_pretrained.pdparams", +} + +MODEL_STAGES_PATTERN = { + "PPLCNet": ["blocks2", "blocks3", "blocks4", "blocks5", "blocks6"] +} + +__all__ = list(MODEL_URLS.keys()) + +# Each element(list) represents a depthwise block, which is composed of k, in_c, out_c, s, use_se. +# k: kernel_size +# in_c: input channel number in depthwise block +# out_c: output channel number in depthwise block +# s: stride in depthwise block +# use_se: whether to use SE block + +NET_CONFIG = { + "blocks2": + # k, in_c, out_c, s, use_se + [[3, 16, 32, 1, False]], + "blocks3": [[3, 32, 64, 2, False], [3, 64, 64, 1, False]], + "blocks4": [[3, 64, 128, 2, False], [3, 128, 128, 1, False]], + "blocks5": [ + [3, 128, 256, 2, False], + [5, 256, 256, 1, False], + [5, 256, 256, 1, False], + [5, 256, 256, 1, False], + [5, 256, 256, 1, False], + [5, 256, 256, 1, False], + ], + "blocks6": [[5, 256, 512, 2, True], [5, 512, 512, 1, True]], +} + + +def make_divisible(v, divisor=8, min_value=None): + if min_value is None: + min_value = divisor + new_v = max(min_value, int(v + divisor / 2) // divisor * divisor) + if new_v < 0.9 * v: + new_v += divisor + return new_v + + +class ConvBNLayer(nn.Layer): + def __init__(self, num_channels, filter_size, num_filters, stride, num_groups=1): + super().__init__() + + self.conv = Conv2D( + in_channels=num_channels, + out_channels=num_filters, + kernel_size=filter_size, + stride=stride, + padding=(filter_size - 1) // 2, + groups=num_groups, + weight_attr=ParamAttr(initializer=KaimingNormal()), + bias_attr=False, + ) + + self.bn = BatchNorm( + num_filters, + param_attr=ParamAttr(regularizer=L2Decay(0.0)), + bias_attr=ParamAttr(regularizer=L2Decay(0.0)), + ) + self.hardswish = nn.Hardswish() + + def forward(self, x): + x = self.conv(x) + x = self.bn(x) + x = self.hardswish(x) + return x + + +class DepthwiseSeparable(nn.Layer): + def __init__(self, num_channels, num_filters, stride, dw_size=3, use_se=False): + super().__init__() + self.use_se = use_se + self.dw_conv = ConvBNLayer( + num_channels=num_channels, + num_filters=num_channels, + filter_size=dw_size, + stride=stride, + num_groups=num_channels, + ) + if use_se: + self.se = SEModule(num_channels) + self.pw_conv = ConvBNLayer( + num_channels=num_channels, filter_size=1, num_filters=num_filters, stride=1 + ) + + def forward(self, x): + x = self.dw_conv(x) + if self.use_se: + x = self.se(x) + x = self.pw_conv(x) + return x + + +class SEModule(nn.Layer): + def __init__(self, channel, reduction=4): + super().__init__() + self.avg_pool = AdaptiveAvgPool2D(1) + self.conv1 = Conv2D( + in_channels=channel, + out_channels=channel // reduction, + kernel_size=1, + stride=1, + padding=0, + ) + self.relu = nn.ReLU() + self.conv2 = Conv2D( + in_channels=channel // reduction, + out_channels=channel, + kernel_size=1, + stride=1, + padding=0, + ) + self.hardsigmoid = nn.Hardsigmoid() + + def forward(self, x): + identity = x + x = self.avg_pool(x) + x = self.conv1(x) + x = self.relu(x) + x = self.conv2(x) + x = self.hardsigmoid(x) + x = paddle.multiply(x=identity, y=x) + return x + + +class PPLCNet(nn.Layer): + def __init__(self, in_channels=3, scale=1.0, pretrained=False, use_ssld=False): + super().__init__() + self.out_channels = [ + int(NET_CONFIG["blocks3"][-1][2] * scale), + int(NET_CONFIG["blocks4"][-1][2] * scale), + int(NET_CONFIG["blocks5"][-1][2] * scale), + int(NET_CONFIG["blocks6"][-1][2] * scale), + ] + self.scale = scale + + self.conv1 = ConvBNLayer( + num_channels=in_channels, + filter_size=3, + num_filters=make_divisible(16 * scale), + stride=2, + ) + + self.blocks2 = nn.Sequential( + *[ + DepthwiseSeparable( + num_channels=make_divisible(in_c * scale), + num_filters=make_divisible(out_c * scale), + dw_size=k, + stride=s, + use_se=se, + ) + for i, (k, in_c, out_c, s, se) in enumerate(NET_CONFIG["blocks2"]) + ] + ) + + self.blocks3 = nn.Sequential( + *[ + DepthwiseSeparable( + num_channels=make_divisible(in_c * scale), + num_filters=make_divisible(out_c * scale), + dw_size=k, + stride=s, + use_se=se, + ) + for i, (k, in_c, out_c, s, se) in enumerate(NET_CONFIG["blocks3"]) + ] + ) + + self.blocks4 = nn.Sequential( + *[ + DepthwiseSeparable( + num_channels=make_divisible(in_c * scale), + num_filters=make_divisible(out_c * scale), + dw_size=k, + stride=s, + use_se=se, + ) + for i, (k, in_c, out_c, s, se) in enumerate(NET_CONFIG["blocks4"]) + ] + ) + + self.blocks5 = nn.Sequential( + *[ + DepthwiseSeparable( + num_channels=make_divisible(in_c * scale), + num_filters=make_divisible(out_c * scale), + dw_size=k, + stride=s, + use_se=se, + ) + for i, (k, in_c, out_c, s, se) in enumerate(NET_CONFIG["blocks5"]) + ] + ) + + self.blocks6 = nn.Sequential( + *[ + DepthwiseSeparable( + num_channels=make_divisible(in_c * scale), + num_filters=make_divisible(out_c * scale), + dw_size=k, + stride=s, + use_se=se, + ) + for i, (k, in_c, out_c, s, se) in enumerate(NET_CONFIG["blocks6"]) + ] + ) + + if pretrained: + self._load_pretrained( + MODEL_URLS["PPLCNet_x{}".format(scale)], use_ssld=use_ssld + ) + + def forward(self, x): + outs = [] + x = self.conv1(x) + x = self.blocks2(x) + x = self.blocks3(x) + outs.append(x) + x = self.blocks4(x) + outs.append(x) + x = self.blocks5(x) + outs.append(x) + x = self.blocks6(x) + outs.append(x) + return outs + + def _load_pretrained(self, pretrained_url, use_ssld=False): + if use_ssld: + pretrained_url = pretrained_url.replace("_pretrained", "_ssld_pretrained") + print(pretrained_url) + local_weight_path = get_path_from_url( + pretrained_url, os.path.expanduser("~/.paddleclas/weights") + ) + param_state_dict = paddle.load(local_weight_path) + self.set_dict(param_state_dict) + return diff --git a/modules/onnx_ocr_module/src/ppocr/modeling/backbones/det_pp_lcnet_v2.py b/modules/onnx_ocr_module/src/ppocr/modeling/backbones/det_pp_lcnet_v2.py new file mode 100644 index 0000000..5b5a568 --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/modeling/backbones/det_pp_lcnet_v2.py @@ -0,0 +1,358 @@ +# copyright (c) 2024 PaddlePaddle Authors. All Rights Reserve. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from __future__ import absolute_import, division, print_function +import os + +import paddle +import paddle.nn as nn +import paddle.nn.functional as F +from paddle import ParamAttr +from paddle.nn import AdaptiveAvgPool2D, BatchNorm2D, Conv2D, Dropout, Linear +from paddle.regularizer import L2Decay +from paddle.nn.initializer import KaimingNormal +from paddle.utils.download import get_path_from_url + +MODEL_URLS = { + "PPLCNetV2_small": "https://paddle-imagenet-models-name.bj.bcebos.com/dygraph/legendary_models/PPLCNetV2_small_ssld_pretrained.pdparams", + "PPLCNetV2_base": "https://paddle-imagenet-models-name.bj.bcebos.com/dygraph/legendary_models/PPLCNetV2_base_ssld_pretrained.pdparams", + "PPLCNetV2_large": "https://paddle-imagenet-models-name.bj.bcebos.com/dygraph/legendary_models/PPLCNetV2_large_ssld_pretrained.pdparams", +} + +__all__ = list(MODEL_URLS.keys()) + +NET_CONFIG = { + # in_channels, kernel_size, split_pw, use_rep, use_se, use_shortcut + "stage1": [64, 3, False, False, False, False], + "stage2": [128, 3, False, False, False, False], + "stage3": [256, 5, True, True, True, False], + "stage4": [512, 5, False, True, False, True], +} + + +def make_divisible(v, divisor=8, min_value=None): + if min_value is None: + min_value = divisor + new_v = max(min_value, int(v + divisor / 2) // divisor * divisor) + if new_v < 0.9 * v: + new_v += divisor + return new_v + + +class ConvBNLayer(nn.Layer): + def __init__( + self, in_channels, out_channels, kernel_size, stride, groups=1, use_act=True + ): + super().__init__() + self.use_act = use_act + self.conv = Conv2D( + in_channels=in_channels, + out_channels=out_channels, + kernel_size=kernel_size, + stride=stride, + padding=(kernel_size - 1) // 2, + groups=groups, + weight_attr=ParamAttr(initializer=KaimingNormal()), + bias_attr=False, + ) + + self.bn = BatchNorm2D( + out_channels, + weight_attr=ParamAttr(regularizer=L2Decay(0.0)), + bias_attr=ParamAttr(regularizer=L2Decay(0.0)), + ) + if self.use_act: + self.act = nn.ReLU() + + def forward(self, x): + x = self.conv(x) + x = self.bn(x) + if self.use_act: + x = self.act(x) + return x + + +class SEModule(nn.Layer): + def __init__(self, channel, reduction=4): + super().__init__() + self.avg_pool = AdaptiveAvgPool2D(1) + self.conv1 = Conv2D( + in_channels=channel, + out_channels=channel // reduction, + kernel_size=1, + stride=1, + padding=0, + ) + self.relu = nn.ReLU() + self.conv2 = Conv2D( + in_channels=channel // reduction, + out_channels=channel, + kernel_size=1, + stride=1, + padding=0, + ) + self.hardsigmoid = nn.Sigmoid() + + def forward(self, x): + identity = x + x = self.avg_pool(x) + x = self.conv1(x) + x = self.relu(x) + x = self.conv2(x) + x = self.hardsigmoid(x) + x = paddle.multiply(x=identity, y=x) + return x + + +class RepDepthwiseSeparable(nn.Layer): + def __init__( + self, + in_channels, + out_channels, + stride, + dw_size=3, + split_pw=False, + use_rep=False, + use_se=False, + use_shortcut=False, + ): + super().__init__() + self.in_channels = in_channels + self.out_channels = out_channels + self.is_repped = False + + self.dw_size = dw_size + self.split_pw = split_pw + self.use_rep = use_rep + self.use_se = use_se + self.use_shortcut = ( + True + if use_shortcut and stride == 1 and in_channels == out_channels + else False + ) + + if self.use_rep: + self.dw_conv_list = nn.LayerList() + for kernel_size in range(self.dw_size, 0, -2): + if kernel_size == 1 and stride != 1: + continue + dw_conv = ConvBNLayer( + in_channels=in_channels, + out_channels=in_channels, + kernel_size=kernel_size, + stride=stride, + groups=in_channels, + use_act=False, + ) + self.dw_conv_list.append(dw_conv) + self.dw_conv = nn.Conv2D( + in_channels=in_channels, + out_channels=in_channels, + kernel_size=dw_size, + stride=stride, + padding=(dw_size - 1) // 2, + groups=in_channels, + ) + else: + self.dw_conv = ConvBNLayer( + in_channels=in_channels, + out_channels=in_channels, + kernel_size=dw_size, + stride=stride, + groups=in_channels, + ) + + self.act = nn.ReLU() + + if use_se: + self.se = SEModule(in_channels) + + if self.split_pw: + pw_ratio = 0.5 + self.pw_conv_1 = ConvBNLayer( + in_channels=in_channels, + kernel_size=1, + out_channels=int(out_channels * pw_ratio), + stride=1, + ) + self.pw_conv_2 = ConvBNLayer( + in_channels=int(out_channels * pw_ratio), + kernel_size=1, + out_channels=out_channels, + stride=1, + ) + else: + self.pw_conv = ConvBNLayer( + in_channels=in_channels, + kernel_size=1, + out_channels=out_channels, + stride=1, + ) + + def forward(self, x): + if self.use_rep: + input_x = x + if self.is_repped: + x = self.act(self.dw_conv(x)) + else: + y = self.dw_conv_list[0](x) + for dw_conv in self.dw_conv_list[1:]: + y += dw_conv(x) + x = self.act(y) + else: + x = self.dw_conv(x) + + if self.use_se: + x = self.se(x) + if self.split_pw: + x = self.pw_conv_1(x) + x = self.pw_conv_2(x) + else: + x = self.pw_conv(x) + if self.use_shortcut: + x = x + input_x + return x + + def re_parameterize(self): + if self.use_rep: + self.is_repped = True + kernel, bias = self._get_equivalent_kernel_bias() + self.dw_conv.weight.set_value(kernel) + self.dw_conv.bias.set_value(bias) + + def _get_equivalent_kernel_bias(self): + kernel_sum = 0 + bias_sum = 0 + for dw_conv in self.dw_conv_list: + kernel, bias = self._fuse_bn_tensor(dw_conv) + kernel = self._pad_tensor(kernel, to_size=self.dw_size) + kernel_sum += kernel + bias_sum += bias + return kernel_sum, bias_sum + + def _fuse_bn_tensor(self, branch): + kernel = branch.conv.weight + running_mean = branch.bn._mean + running_var = branch.bn._variance + gamma = branch.bn.weight + beta = branch.bn.bias + eps = branch.bn._epsilon + std = (running_var + eps).sqrt() + t = (gamma / std).reshape((-1, 1, 1, 1)) + return kernel * t, beta - running_mean * gamma / std + + def _pad_tensor(self, tensor, to_size): + from_size = tensor.shape[-1] + if from_size == to_size: + return tensor + pad = (to_size - from_size) // 2 + return F.pad(tensor, [pad, pad, pad, pad]) + + +class PPLCNetV2(nn.Layer): + def __init__(self, scale, depths, out_indx=[1, 2, 3, 4], **kwargs): + super().__init__(**kwargs) + self.scale = scale + self.out_channels = [ + # int(NET_CONFIG["blocks3"][-1][2] * scale), + int(NET_CONFIG["stage1"][0] * scale * 2), + int(NET_CONFIG["stage2"][0] * scale * 2), + int(NET_CONFIG["stage3"][0] * scale * 2), + int(NET_CONFIG["stage4"][0] * scale * 2), + ] + self.stem = nn.Sequential( + *[ + ConvBNLayer( + in_channels=3, + kernel_size=3, + out_channels=make_divisible(32 * scale), + stride=2, + ), + RepDepthwiseSeparable( + in_channels=make_divisible(32 * scale), + out_channels=make_divisible(64 * scale), + stride=1, + dw_size=3, + ), + ] + ) + self.out_indx = out_indx + # stages + self.stages = nn.LayerList() + for depth_idx, k in enumerate(NET_CONFIG): + ( + in_channels, + kernel_size, + split_pw, + use_rep, + use_se, + use_shortcut, + ) = NET_CONFIG[k] + self.stages.append( + nn.Sequential( + *[ + RepDepthwiseSeparable( + in_channels=make_divisible( + (in_channels if i == 0 else in_channels * 2) * scale + ), + out_channels=make_divisible(in_channels * 2 * scale), + stride=2 if i == 0 else 1, + dw_size=kernel_size, + split_pw=split_pw, + use_rep=use_rep, + use_se=use_se, + use_shortcut=use_shortcut, + ) + for i in range(depths[depth_idx]) + ] + ) + ) + + # if pretrained: + self._load_pretrained(MODEL_URLS["PPLCNetV2_base"], use_ssld=True) + + def forward(self, x): + x = self.stem(x) + i = 1 + outs = [] + for stage in self.stages: + x = stage(x) + if i in self.out_indx: + outs.append(x) + i += 1 + return outs + + def _load_pretrained(self, pretrained_url, use_ssld=False): + print(pretrained_url) + local_weight_path = get_path_from_url( + pretrained_url, os.path.expanduser("~/.paddleclas/weights") + ) + param_state_dict = paddle.load(local_weight_path) + self.set_dict(param_state_dict) + print("load pretrain ssd success!") + return + + +def PPLCNetV2_base(in_channels=3, **kwargs): + """ + PPLCNetV2_base + Args: + pretrained: bool=False or str. If `True` load pretrained parameters, `False` otherwise. + If str, means the path of the pretrained model. + use_ssld: bool=False. Whether using distillation pretrained model when pretrained=True. + Returns: + model: nn.Layer. Specific `PPLCNetV2_base` model depends on args. + """ + model = PPLCNetV2(scale=1.0, depths=[2, 2, 6, 2], **kwargs) + return model diff --git a/modules/onnx_ocr_module/src/ppocr/modeling/backbones/det_resnet.py b/modules/onnx_ocr_module/src/ppocr/modeling/backbones/det_resnet.py new file mode 100644 index 0000000..3842829 --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/modeling/backbones/det_resnet.py @@ -0,0 +1,235 @@ +# copyright (c) 2022 PaddlePaddle Authors. All Rights Reserve. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import numpy as np +import paddle +from paddle import ParamAttr +import paddle.nn as nn +import paddle.nn.functional as F +from paddle.nn import Conv2D, BatchNorm, Linear, Dropout +from paddle.nn import AdaptiveAvgPool2D, MaxPool2D, AvgPool2D +from paddle.nn.initializer import Uniform + +import math + +from paddle.vision.ops import DeformConv2D +from paddle.regularizer import L2Decay +from paddle.nn.initializer import Normal, Constant, XavierUniform +from .det_resnet_vd import DeformableConvV2, ConvBNLayer + + +class BottleneckBlock(nn.Layer): + def __init__(self, num_channels, num_filters, stride, shortcut=True, is_dcn=False): + super(BottleneckBlock, self).__init__() + + self.conv0 = ConvBNLayer( + in_channels=num_channels, + out_channels=num_filters, + kernel_size=1, + act="relu", + ) + self.conv1 = ConvBNLayer( + in_channels=num_filters, + out_channels=num_filters, + kernel_size=3, + stride=stride, + act="relu", + is_dcn=is_dcn, + dcn_groups=1, + ) + self.conv2 = ConvBNLayer( + in_channels=num_filters, + out_channels=num_filters * 4, + kernel_size=1, + act=None, + ) + + if not shortcut: + self.short = ConvBNLayer( + in_channels=num_channels, + out_channels=num_filters * 4, + kernel_size=1, + stride=stride, + ) + + self.shortcut = shortcut + + self._num_channels_out = num_filters * 4 + + def forward(self, inputs): + y = self.conv0(inputs) + conv1 = self.conv1(y) + conv2 = self.conv2(conv1) + + if self.shortcut: + short = inputs + else: + short = self.short(inputs) + + y = paddle.add(x=short, y=conv2) + y = F.relu(y) + return y + + +class BasicBlock(nn.Layer): + def __init__(self, num_channels, num_filters, stride, shortcut=True, name=None): + super(BasicBlock, self).__init__() + self.stride = stride + self.conv0 = ConvBNLayer( + in_channels=num_channels, + out_channels=num_filters, + kernel_size=3, + stride=stride, + act="relu", + ) + self.conv1 = ConvBNLayer( + in_channels=num_filters, out_channels=num_filters, kernel_size=3, act=None + ) + + if not shortcut: + self.short = ConvBNLayer( + in_channels=num_channels, + out_channels=num_filters, + kernel_size=1, + stride=stride, + ) + + self.shortcut = shortcut + + def forward(self, inputs): + y = self.conv0(inputs) + conv1 = self.conv1(y) + + if self.shortcut: + short = inputs + else: + short = self.short(inputs) + y = paddle.add(x=short, y=conv1) + y = F.relu(y) + return y + + +class ResNet(nn.Layer): + def __init__(self, in_channels=3, layers=50, out_indices=None, dcn_stage=None): + super(ResNet, self).__init__() + + self.layers = layers + self.input_image_channel = in_channels + + supported_layers = [18, 34, 50, 101, 152] + assert ( + layers in supported_layers + ), "supported layers are {} but input layer is {}".format( + supported_layers, layers + ) + + if layers == 18: + depth = [2, 2, 2, 2] + elif layers == 34 or layers == 50: + depth = [3, 4, 6, 3] + elif layers == 101: + depth = [3, 4, 23, 3] + elif layers == 152: + depth = [3, 8, 36, 3] + num_channels = [64, 256, 512, 1024] if layers >= 50 else [64, 64, 128, 256] + num_filters = [64, 128, 256, 512] + + self.dcn_stage = ( + dcn_stage if dcn_stage is not None else [False, False, False, False] + ) + self.out_indices = out_indices if out_indices is not None else [0, 1, 2, 3] + + self.conv = ConvBNLayer( + in_channels=self.input_image_channel, + out_channels=64, + kernel_size=7, + stride=2, + act="relu", + ) + self.pool2d_max = MaxPool2D( + kernel_size=3, + stride=2, + padding=1, + ) + + self.stages = [] + self.out_channels = [] + if layers >= 50: + for block in range(len(depth)): + shortcut = False + block_list = [] + is_dcn = self.dcn_stage[block] + for i in range(depth[block]): + if layers in [101, 152] and block == 2: + if i == 0: + conv_name = "res" + str(block + 2) + "a" + else: + conv_name = "res" + str(block + 2) + "b" + str(i) + else: + conv_name = "res" + str(block + 2) + chr(97 + i) + bottleneck_block = self.add_sublayer( + conv_name, + BottleneckBlock( + num_channels=( + num_channels[block] + if i == 0 + else num_filters[block] * 4 + ), + num_filters=num_filters[block], + stride=2 if i == 0 and block != 0 else 1, + shortcut=shortcut, + is_dcn=is_dcn, + ), + ) + block_list.append(bottleneck_block) + shortcut = True + if block in self.out_indices: + self.out_channels.append(num_filters[block] * 4) + self.stages.append(nn.Sequential(*block_list)) + else: + for block in range(len(depth)): + shortcut = False + block_list = [] + for i in range(depth[block]): + conv_name = "res" + str(block + 2) + chr(97 + i) + basic_block = self.add_sublayer( + conv_name, + BasicBlock( + num_channels=( + num_channels[block] if i == 0 else num_filters[block] + ), + num_filters=num_filters[block], + stride=2 if i == 0 and block != 0 else 1, + shortcut=shortcut, + ), + ) + block_list.append(basic_block) + shortcut = True + if block in self.out_indices: + self.out_channels.append(num_filters[block]) + self.stages.append(nn.Sequential(*block_list)) + + def forward(self, inputs): + y = self.conv(inputs) + y = self.pool2d_max(y) + out = [] + for i, block in enumerate(self.stages): + y = block(y) + if i in self.out_indices: + out.append(y) + return out diff --git a/modules/onnx_ocr_module/src/ppocr/modeling/backbones/det_resnet_vd.py b/modules/onnx_ocr_module/src/ppocr/modeling/backbones/det_resnet_vd.py new file mode 100644 index 0000000..070ba3c --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/modeling/backbones/det_resnet_vd.py @@ -0,0 +1,369 @@ +# copyright (c) 2020 PaddlePaddle Authors. All Rights Reserve. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import paddle +from paddle import ParamAttr +import paddle.nn as nn +import paddle.nn.functional as F + +from paddle.vision.ops import DeformConv2D +from paddle.regularizer import L2Decay +from paddle.nn.initializer import Normal, Constant, XavierUniform + +__all__ = ["ResNet_vd", "ConvBNLayer", "DeformableConvV2"] + + +class DeformableConvV2(nn.Layer): + def __init__( + self, + in_channels, + out_channels, + kernel_size, + stride=1, + padding=0, + dilation=1, + groups=1, + weight_attr=None, + bias_attr=None, + lr_scale=1, + regularizer=None, + skip_quant=False, + dcn_bias_regularizer=L2Decay(0.0), + dcn_bias_lr_scale=2.0, + ): + super(DeformableConvV2, self).__init__() + self.offset_channel = 2 * kernel_size**2 * groups + self.mask_channel = kernel_size**2 * groups + + if bias_attr: + # in FCOS-DCN head, specifically need learning_rate and regularizer + dcn_bias_attr = ParamAttr( + initializer=Constant(value=0), + regularizer=dcn_bias_regularizer, + learning_rate=dcn_bias_lr_scale, + ) + else: + # in ResNet backbone, do not need bias + dcn_bias_attr = False + self.conv_dcn = DeformConv2D( + in_channels, + out_channels, + kernel_size, + stride=stride, + padding=(kernel_size - 1) // 2 * dilation, + dilation=dilation, + deformable_groups=groups, + weight_attr=weight_attr, + bias_attr=dcn_bias_attr, + ) + + if lr_scale == 1 and regularizer is None: + offset_bias_attr = ParamAttr(initializer=Constant(0.0)) + else: + offset_bias_attr = ParamAttr( + initializer=Constant(0.0), + learning_rate=lr_scale, + regularizer=regularizer, + ) + self.conv_offset = nn.Conv2D( + in_channels, + groups * 3 * kernel_size**2, + kernel_size, + stride=stride, + padding=(kernel_size - 1) // 2, + weight_attr=ParamAttr(initializer=Constant(0.0)), + bias_attr=offset_bias_attr, + ) + if skip_quant: + self.conv_offset.skip_quant = True + + def forward(self, x): + offset_mask = self.conv_offset(x) + offset, mask = paddle.split( + offset_mask, + num_or_sections=[self.offset_channel, self.mask_channel], + axis=1, + ) + mask = F.sigmoid(mask) + y = self.conv_dcn(x, offset, mask=mask) + return y + + +class ConvBNLayer(nn.Layer): + def __init__( + self, + in_channels, + out_channels, + kernel_size, + stride=1, + groups=1, + dcn_groups=1, + is_vd_mode=False, + act=None, + is_dcn=False, + ): + super(ConvBNLayer, self).__init__() + + self.is_vd_mode = is_vd_mode + self._pool2d_avg = nn.AvgPool2D( + kernel_size=2, stride=2, padding=0, ceil_mode=True + ) + if not is_dcn: + self._conv = nn.Conv2D( + in_channels=in_channels, + out_channels=out_channels, + kernel_size=kernel_size, + stride=stride, + padding=(kernel_size - 1) // 2, + groups=groups, + bias_attr=False, + ) + else: + self._conv = DeformableConvV2( + in_channels=in_channels, + out_channels=out_channels, + kernel_size=kernel_size, + stride=stride, + padding=(kernel_size - 1) // 2, + groups=dcn_groups, # groups, + bias_attr=False, + ) + self._batch_norm = nn.BatchNorm(out_channels, act=act) + + def forward(self, inputs): + if self.is_vd_mode: + inputs = self._pool2d_avg(inputs) + y = self._conv(inputs) + y = self._batch_norm(y) + return y + + +class BottleneckBlock(nn.Layer): + def __init__( + self, + in_channels, + out_channels, + stride, + shortcut=True, + if_first=False, + is_dcn=False, + ): + super(BottleneckBlock, self).__init__() + + self.conv0 = ConvBNLayer( + in_channels=in_channels, + out_channels=out_channels, + kernel_size=1, + act="relu", + ) + self.conv1 = ConvBNLayer( + in_channels=out_channels, + out_channels=out_channels, + kernel_size=3, + stride=stride, + act="relu", + is_dcn=is_dcn, + dcn_groups=2, + ) + self.conv2 = ConvBNLayer( + in_channels=out_channels, + out_channels=out_channels * 4, + kernel_size=1, + act=None, + ) + + if not shortcut: + self.short = ConvBNLayer( + in_channels=in_channels, + out_channels=out_channels * 4, + kernel_size=1, + stride=1, + is_vd_mode=False if if_first else True, + ) + + self.shortcut = shortcut + + def forward(self, inputs): + y = self.conv0(inputs) + conv1 = self.conv1(y) + conv2 = self.conv2(conv1) + + if self.shortcut: + short = inputs + else: + short = self.short(inputs) + y = paddle.add(x=short, y=conv2) + y = F.relu(y) + return y + + +class BasicBlock(nn.Layer): + def __init__( + self, + in_channels, + out_channels, + stride, + shortcut=True, + if_first=False, + ): + super(BasicBlock, self).__init__() + self.stride = stride + self.conv0 = ConvBNLayer( + in_channels=in_channels, + out_channels=out_channels, + kernel_size=3, + stride=stride, + act="relu", + ) + self.conv1 = ConvBNLayer( + in_channels=out_channels, out_channels=out_channels, kernel_size=3, act=None + ) + + if not shortcut: + self.short = ConvBNLayer( + in_channels=in_channels, + out_channels=out_channels, + kernel_size=1, + stride=1, + is_vd_mode=False if if_first else True, + ) + + self.shortcut = shortcut + + def forward(self, inputs): + y = self.conv0(inputs) + conv1 = self.conv1(y) + + if self.shortcut: + short = inputs + else: + short = self.short(inputs) + y = paddle.add(x=short, y=conv1) + y = F.relu(y) + return y + + +class ResNet_vd(nn.Layer): + def __init__( + self, in_channels=3, layers=50, dcn_stage=None, out_indices=None, **kwargs + ): + super(ResNet_vd, self).__init__() + + self.layers = layers + supported_layers = [18, 34, 50, 101, 152, 200] + assert ( + layers in supported_layers + ), "supported layers are {} but input layer is {}".format( + supported_layers, layers + ) + + if layers == 18: + depth = [2, 2, 2, 2] + elif layers == 34 or layers == 50: + depth = [3, 4, 6, 3] + elif layers == 101: + depth = [3, 4, 23, 3] + elif layers == 152: + depth = [3, 8, 36, 3] + elif layers == 200: + depth = [3, 12, 48, 3] + num_channels = [64, 256, 512, 1024] if layers >= 50 else [64, 64, 128, 256] + num_filters = [64, 128, 256, 512] + + self.dcn_stage = ( + dcn_stage if dcn_stage is not None else [False, False, False, False] + ) + self.out_indices = out_indices if out_indices is not None else [0, 1, 2, 3] + + self.conv1_1 = ConvBNLayer( + in_channels=in_channels, + out_channels=32, + kernel_size=3, + stride=2, + act="relu", + ) + self.conv1_2 = ConvBNLayer( + in_channels=32, out_channels=32, kernel_size=3, stride=1, act="relu" + ) + self.conv1_3 = ConvBNLayer( + in_channels=32, out_channels=64, kernel_size=3, stride=1, act="relu" + ) + self.pool2d_max = nn.MaxPool2D(kernel_size=3, stride=2, padding=1) + + self.stages = [] + self.out_channels = [] + if layers >= 50: + for block in range(len(depth)): + block_list = [] + shortcut = False + is_dcn = self.dcn_stage[block] + for i in range(depth[block]): + bottleneck_block = self.add_sublayer( + "bb_%d_%d" % (block, i), + BottleneckBlock( + in_channels=( + num_channels[block] + if i == 0 + else num_filters[block] * 4 + ), + out_channels=num_filters[block], + stride=2 if i == 0 and block != 0 else 1, + shortcut=shortcut, + if_first=block == i == 0, + is_dcn=is_dcn, + ), + ) + shortcut = True + block_list.append(bottleneck_block) + if block in self.out_indices: + self.out_channels.append(num_filters[block] * 4) + self.stages.append(nn.Sequential(*block_list)) + else: + for block in range(len(depth)): + block_list = [] + shortcut = False + for i in range(depth[block]): + basic_block = self.add_sublayer( + "bb_%d_%d" % (block, i), + BasicBlock( + in_channels=( + num_channels[block] if i == 0 else num_filters[block] + ), + out_channels=num_filters[block], + stride=2 if i == 0 and block != 0 else 1, + shortcut=shortcut, + if_first=block == i == 0, + ), + ) + shortcut = True + block_list.append(basic_block) + if block in self.out_indices: + self.out_channels.append(num_filters[block]) + self.stages.append(nn.Sequential(*block_list)) + + def forward(self, inputs): + y = self.conv1_1(inputs) + y = self.conv1_2(y) + y = self.conv1_3(y) + y = self.pool2d_max(y) + out = [] + for i, block in enumerate(self.stages): + y = block(y) + if i in self.out_indices: + out.append(y) + return out diff --git a/modules/onnx_ocr_module/src/ppocr/modeling/backbones/det_resnet_vd_sast.py b/modules/onnx_ocr_module/src/ppocr/modeling/backbones/det_resnet_vd_sast.py new file mode 100644 index 0000000..6769dd9 --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/modeling/backbones/det_resnet_vd_sast.py @@ -0,0 +1,314 @@ +# copyright (c) 2020 PaddlePaddle Authors. All Rights Reserve. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import paddle +from paddle import ParamAttr +import paddle.nn as nn +import paddle.nn.functional as F + +__all__ = ["ResNet_SAST"] + + +class ConvBNLayer(nn.Layer): + def __init__( + self, + in_channels, + out_channels, + kernel_size, + stride=1, + groups=1, + is_vd_mode=False, + act=None, + name=None, + ): + super(ConvBNLayer, self).__init__() + + self.is_vd_mode = is_vd_mode + self._pool2d_avg = nn.AvgPool2D( + kernel_size=2, stride=2, padding=0, ceil_mode=True + ) + self._conv = nn.Conv2D( + in_channels=in_channels, + out_channels=out_channels, + kernel_size=kernel_size, + stride=stride, + padding=(kernel_size - 1) // 2, + groups=groups, + weight_attr=ParamAttr(name=name + "_weights"), + bias_attr=False, + ) + if name == "conv1": + bn_name = "bn_" + name + else: + bn_name = "bn" + name[3:] + self._batch_norm = nn.BatchNorm( + out_channels, + act=act, + param_attr=ParamAttr(name=bn_name + "_scale"), + bias_attr=ParamAttr(bn_name + "_offset"), + moving_mean_name=bn_name + "_mean", + moving_variance_name=bn_name + "_variance", + ) + + def forward(self, inputs): + if self.is_vd_mode: + inputs = self._pool2d_avg(inputs) + y = self._conv(inputs) + y = self._batch_norm(y) + return y + + +class BottleneckBlock(nn.Layer): + def __init__( + self, + in_channels, + out_channels, + stride, + shortcut=True, + if_first=False, + name=None, + ): + super(BottleneckBlock, self).__init__() + + self.conv0 = ConvBNLayer( + in_channels=in_channels, + out_channels=out_channels, + kernel_size=1, + act="relu", + name=name + "_branch2a", + ) + self.conv1 = ConvBNLayer( + in_channels=out_channels, + out_channels=out_channels, + kernel_size=3, + stride=stride, + act="relu", + name=name + "_branch2b", + ) + self.conv2 = ConvBNLayer( + in_channels=out_channels, + out_channels=out_channels * 4, + kernel_size=1, + act=None, + name=name + "_branch2c", + ) + + if not shortcut: + self.short = ConvBNLayer( + in_channels=in_channels, + out_channels=out_channels * 4, + kernel_size=1, + stride=1, + is_vd_mode=False if if_first else True, + name=name + "_branch1", + ) + + self.shortcut = shortcut + + def forward(self, inputs): + y = self.conv0(inputs) + conv1 = self.conv1(y) + conv2 = self.conv2(conv1) + + if self.shortcut: + short = inputs + else: + short = self.short(inputs) + y = paddle.add(x=short, y=conv2) + y = F.relu(y) + return y + + +class BasicBlock(nn.Layer): + def __init__( + self, + in_channels, + out_channels, + stride, + shortcut=True, + if_first=False, + name=None, + ): + super(BasicBlock, self).__init__() + self.stride = stride + self.conv0 = ConvBNLayer( + in_channels=in_channels, + out_channels=out_channels, + kernel_size=3, + stride=stride, + act="relu", + name=name + "_branch2a", + ) + self.conv1 = ConvBNLayer( + in_channels=out_channels, + out_channels=out_channels, + kernel_size=3, + act=None, + name=name + "_branch2b", + ) + + if not shortcut: + self.short = ConvBNLayer( + in_channels=in_channels, + out_channels=out_channels, + kernel_size=1, + stride=1, + is_vd_mode=False if if_first else True, + name=name + "_branch1", + ) + + self.shortcut = shortcut + + def forward(self, inputs): + y = self.conv0(inputs) + conv1 = self.conv1(y) + + if self.shortcut: + short = inputs + else: + short = self.short(inputs) + y = paddle.add(x=short, y=conv1) + y = F.relu(y) + return y + + +class ResNet_SAST(nn.Layer): + def __init__(self, in_channels=3, layers=50, **kwargs): + super(ResNet_SAST, self).__init__() + + self.layers = layers + supported_layers = [18, 34, 50, 101, 152, 200] + assert ( + layers in supported_layers + ), "supported layers are {} but input layer is {}".format( + supported_layers, layers + ) + + if layers == 18: + depth = [2, 2, 2, 2] + elif layers == 34 or layers == 50: + # depth = [3, 4, 6, 3] + depth = [3, 4, 6, 3, 3] + elif layers == 101: + depth = [3, 4, 23, 3] + elif layers == 152: + depth = [3, 8, 36, 3] + elif layers == 200: + depth = [3, 12, 48, 3] + # num_channels = [64, 256, 512, + # 1024] if layers >= 50 else [64, 64, 128, 256] + # num_filters = [64, 128, 256, 512] + num_channels = ( + [64, 256, 512, 1024, 2048] if layers >= 50 else [64, 64, 128, 256] + ) + num_filters = [64, 128, 256, 512, 512] + + self.conv1_1 = ConvBNLayer( + in_channels=in_channels, + out_channels=32, + kernel_size=3, + stride=2, + act="relu", + name="conv1_1", + ) + self.conv1_2 = ConvBNLayer( + in_channels=32, + out_channels=32, + kernel_size=3, + stride=1, + act="relu", + name="conv1_2", + ) + self.conv1_3 = ConvBNLayer( + in_channels=32, + out_channels=64, + kernel_size=3, + stride=1, + act="relu", + name="conv1_3", + ) + self.pool2d_max = nn.MaxPool2D(kernel_size=3, stride=2, padding=1) + + self.stages = [] + self.out_channels = [3, 64] + if layers >= 50: + for block in range(len(depth)): + block_list = [] + shortcut = False + for i in range(depth[block]): + if layers in [101, 152] and block == 2: + if i == 0: + conv_name = "res" + str(block + 2) + "a" + else: + conv_name = "res" + str(block + 2) + "b" + str(i) + else: + conv_name = "res" + str(block + 2) + chr(97 + i) + bottleneck_block = self.add_sublayer( + "bb_%d_%d" % (block, i), + BottleneckBlock( + in_channels=( + num_channels[block] + if i == 0 + else num_filters[block] * 4 + ), + out_channels=num_filters[block], + stride=2 if i == 0 and block != 0 else 1, + shortcut=shortcut, + if_first=block == i == 0, + name=conv_name, + ), + ) + shortcut = True + block_list.append(bottleneck_block) + self.out_channels.append(num_filters[block] * 4) + self.stages.append(nn.Sequential(*block_list)) + else: + for block in range(len(depth)): + block_list = [] + shortcut = False + for i in range(depth[block]): + conv_name = "res" + str(block + 2) + chr(97 + i) + basic_block = self.add_sublayer( + "bb_%d_%d" % (block, i), + BasicBlock( + in_channels=( + num_channels[block] if i == 0 else num_filters[block] + ), + out_channels=num_filters[block], + stride=2 if i == 0 and block != 0 else 1, + shortcut=shortcut, + if_first=block == i == 0, + name=conv_name, + ), + ) + shortcut = True + block_list.append(basic_block) + self.out_channels.append(num_filters[block]) + self.stages.append(nn.Sequential(*block_list)) + + def forward(self, inputs): + out = [inputs] + y = self.conv1_1(inputs) + y = self.conv1_2(y) + y = self.conv1_3(y) + out.append(y) + y = self.pool2d_max(y) + for block in self.stages: + y = block(y) + out.append(y) + return out diff --git a/modules/onnx_ocr_module/src/ppocr/modeling/backbones/e2e_resnet_vd_pg.py b/modules/onnx_ocr_module/src/ppocr/modeling/backbones/e2e_resnet_vd_pg.py new file mode 100644 index 0000000..25738cd --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/modeling/backbones/e2e_resnet_vd_pg.py @@ -0,0 +1,292 @@ +# copyright (c) 2021 PaddlePaddle Authors. All Rights Reserve. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import paddle +from paddle import ParamAttr +import paddle.nn as nn +import paddle.nn.functional as F + +__all__ = ["ResNet"] + + +class ConvBNLayer(nn.Layer): + def __init__( + self, + in_channels, + out_channels, + kernel_size, + stride=1, + groups=1, + is_vd_mode=False, + act=None, + name=None, + ): + super(ConvBNLayer, self).__init__() + + self.is_vd_mode = is_vd_mode + self._pool2d_avg = nn.AvgPool2D( + kernel_size=2, stride=2, padding=0, ceil_mode=True + ) + self._conv = nn.Conv2D( + in_channels=in_channels, + out_channels=out_channels, + kernel_size=kernel_size, + stride=stride, + padding=(kernel_size - 1) // 2, + groups=groups, + weight_attr=ParamAttr(name=name + "_weights"), + bias_attr=False, + ) + if name == "conv1": + bn_name = "bn_" + name + else: + bn_name = "bn" + name[3:] + self._batch_norm = nn.BatchNorm( + out_channels, + act=act, + param_attr=ParamAttr(name=bn_name + "_scale"), + bias_attr=ParamAttr(bn_name + "_offset"), + moving_mean_name=bn_name + "_mean", + moving_variance_name=bn_name + "_variance", + ) + + def forward(self, inputs): + y = self._conv(inputs) + y = self._batch_norm(y) + return y + + +class BottleneckBlock(nn.Layer): + def __init__( + self, + in_channels, + out_channels, + stride, + shortcut=True, + if_first=False, + name=None, + ): + super(BottleneckBlock, self).__init__() + + self.conv0 = ConvBNLayer( + in_channels=in_channels, + out_channels=out_channels, + kernel_size=1, + act="relu", + name=name + "_branch2a", + ) + self.conv1 = ConvBNLayer( + in_channels=out_channels, + out_channels=out_channels, + kernel_size=3, + stride=stride, + act="relu", + name=name + "_branch2b", + ) + self.conv2 = ConvBNLayer( + in_channels=out_channels, + out_channels=out_channels * 4, + kernel_size=1, + act=None, + name=name + "_branch2c", + ) + + if not shortcut: + self.short = ConvBNLayer( + in_channels=in_channels, + out_channels=out_channels * 4, + kernel_size=1, + stride=stride, + is_vd_mode=False if if_first else True, + name=name + "_branch1", + ) + + self.shortcut = shortcut + + def forward(self, inputs): + y = self.conv0(inputs) + conv1 = self.conv1(y) + conv2 = self.conv2(conv1) + + if self.shortcut: + short = inputs + else: + short = self.short(inputs) + y = paddle.add(x=short, y=conv2) + y = F.relu(y) + return y + + +class BasicBlock(nn.Layer): + def __init__( + self, + in_channels, + out_channels, + stride, + shortcut=True, + if_first=False, + name=None, + ): + super(BasicBlock, self).__init__() + self.stride = stride + self.conv0 = ConvBNLayer( + in_channels=in_channels, + out_channels=out_channels, + kernel_size=3, + stride=stride, + act="relu", + name=name + "_branch2a", + ) + self.conv1 = ConvBNLayer( + in_channels=out_channels, + out_channels=out_channels, + kernel_size=3, + act=None, + name=name + "_branch2b", + ) + + if not shortcut: + self.short = ConvBNLayer( + in_channels=in_channels, + out_channels=out_channels, + kernel_size=1, + stride=1, + is_vd_mode=False if if_first else True, + name=name + "_branch1", + ) + + self.shortcut = shortcut + + def forward(self, inputs): + y = self.conv0(inputs) + conv1 = self.conv1(y) + + if self.shortcut: + short = inputs + else: + short = self.short(inputs) + y = paddle.add(x=short, y=conv1) + y = F.relu(y) + return y + + +class ResNet(nn.Layer): + def __init__(self, in_channels=3, layers=50, **kwargs): + super(ResNet, self).__init__() + + self.layers = layers + supported_layers = [18, 34, 50, 101, 152, 200] + assert ( + layers in supported_layers + ), "supported layers are {} but input layer is {}".format( + supported_layers, layers + ) + + if layers == 18: + depth = [2, 2, 2, 2] + elif layers == 34 or layers == 50: + # depth = [3, 4, 6, 3] + depth = [3, 4, 6, 3, 3] + elif layers == 101: + depth = [3, 4, 23, 3] + elif layers == 152: + depth = [3, 8, 36, 3] + elif layers == 200: + depth = [3, 12, 48, 3] + num_channels = ( + [64, 256, 512, 1024, 2048] if layers >= 50 else [64, 64, 128, 256] + ) + num_filters = [64, 128, 256, 512, 512] + + self.conv1_1 = ConvBNLayer( + in_channels=in_channels, + out_channels=64, + kernel_size=7, + stride=2, + act="relu", + name="conv1_1", + ) + self.pool2d_max = nn.MaxPool2D(kernel_size=3, stride=2, padding=1) + + self.stages = [] + self.out_channels = [3, 64] + # num_filters = [64, 128, 256, 512, 512] + if layers >= 50: + for block in range(len(depth)): + block_list = [] + shortcut = False + for i in range(depth[block]): + if layers in [101, 152] and block == 2: + if i == 0: + conv_name = "res" + str(block + 2) + "a" + else: + conv_name = "res" + str(block + 2) + "b" + str(i) + else: + conv_name = "res" + str(block + 2) + chr(97 + i) + bottleneck_block = self.add_sublayer( + "bb_%d_%d" % (block, i), + BottleneckBlock( + in_channels=( + num_channels[block] + if i == 0 + else num_filters[block] * 4 + ), + out_channels=num_filters[block], + stride=2 if i == 0 and block != 0 else 1, + shortcut=shortcut, + if_first=block == i == 0, + name=conv_name, + ), + ) + shortcut = True + block_list.append(bottleneck_block) + self.out_channels.append(num_filters[block] * 4) + self.stages.append(nn.Sequential(*block_list)) + else: + for block in range(len(depth)): + block_list = [] + shortcut = False + for i in range(depth[block]): + conv_name = "res" + str(block + 2) + chr(97 + i) + basic_block = self.add_sublayer( + "bb_%d_%d" % (block, i), + BasicBlock( + in_channels=( + num_channels[block] if i == 0 else num_filters[block] + ), + out_channels=num_filters[block], + stride=2 if i == 0 and block != 0 else 1, + shortcut=shortcut, + if_first=block == i == 0, + name=conv_name, + ), + ) + shortcut = True + block_list.append(basic_block) + self.out_channels.append(num_filters[block]) + self.stages.append(nn.Sequential(*block_list)) + + def forward(self, inputs): + out = [inputs] + y = self.conv1_1(inputs) + out.append(y) + y = self.pool2d_max(y) + for block in self.stages: + y = block(y) + out.append(y) + return out diff --git a/modules/onnx_ocr_module/src/ppocr/modeling/backbones/kie_unet_sdmgr.py b/modules/onnx_ocr_module/src/ppocr/modeling/backbones/kie_unet_sdmgr.py new file mode 100644 index 0000000..7b0c2b5 --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/modeling/backbones/kie_unet_sdmgr.py @@ -0,0 +1,199 @@ +# copyright (c) 2021 PaddlePaddle Authors. All Rights Reserve. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import paddle +from paddle import nn +import numpy as np +import cv2 + +__all__ = ["Kie_backbone"] + + +class Encoder(nn.Layer): + def __init__(self, num_channels, num_filters): + super(Encoder, self).__init__() + self.conv1 = nn.Conv2D( + num_channels, + num_filters, + kernel_size=3, + stride=1, + padding=1, + bias_attr=False, + ) + self.bn1 = nn.BatchNorm(num_filters, act="relu") + + self.conv2 = nn.Conv2D( + num_filters, + num_filters, + kernel_size=3, + stride=1, + padding=1, + bias_attr=False, + ) + self.bn2 = nn.BatchNorm(num_filters, act="relu") + + self.pool = nn.MaxPool2D(kernel_size=3, stride=2, padding=1) + + def forward(self, inputs): + x = self.conv1(inputs) + x = self.bn1(x) + x = self.conv2(x) + x = self.bn2(x) + x_pooled = self.pool(x) + return x, x_pooled + + +class Decoder(nn.Layer): + def __init__(self, num_channels, num_filters): + super(Decoder, self).__init__() + + self.conv1 = nn.Conv2D( + num_channels, + num_filters, + kernel_size=3, + stride=1, + padding=1, + bias_attr=False, + ) + self.bn1 = nn.BatchNorm(num_filters, act="relu") + + self.conv2 = nn.Conv2D( + num_filters, + num_filters, + kernel_size=3, + stride=1, + padding=1, + bias_attr=False, + ) + self.bn2 = nn.BatchNorm(num_filters, act="relu") + + self.conv0 = nn.Conv2D( + num_channels, + num_filters, + kernel_size=1, + stride=1, + padding=0, + bias_attr=False, + ) + self.bn0 = nn.BatchNorm(num_filters, act="relu") + + def forward(self, inputs_prev, inputs): + x = self.conv0(inputs) + x = self.bn0(x) + x = paddle.nn.functional.interpolate( + x, scale_factor=2, mode="bilinear", align_corners=False + ) + x = paddle.concat([inputs_prev, x], axis=1) + x = self.conv1(x) + x = self.bn1(x) + x = self.conv2(x) + x = self.bn2(x) + return x + + +class UNet(nn.Layer): + def __init__(self): + super(UNet, self).__init__() + self.down1 = Encoder(num_channels=3, num_filters=16) + self.down2 = Encoder(num_channels=16, num_filters=32) + self.down3 = Encoder(num_channels=32, num_filters=64) + self.down4 = Encoder(num_channels=64, num_filters=128) + self.down5 = Encoder(num_channels=128, num_filters=256) + + self.up1 = Decoder(32, 16) + self.up2 = Decoder(64, 32) + self.up3 = Decoder(128, 64) + self.up4 = Decoder(256, 128) + self.out_channels = 16 + + def forward(self, inputs): + x1, _ = self.down1(inputs) + _, x2 = self.down2(x1) + _, x3 = self.down3(x2) + _, x4 = self.down4(x3) + _, x5 = self.down5(x4) + + x = self.up4(x4, x5) + x = self.up3(x3, x) + x = self.up2(x2, x) + x = self.up1(x1, x) + return x + + +class Kie_backbone(nn.Layer): + def __init__(self, in_channels, **kwargs): + super(Kie_backbone, self).__init__() + self.out_channels = 16 + self.img_feat = UNet() + self.maxpool = nn.MaxPool2D(kernel_size=7) + + def bbox2roi(self, bbox_list): + rois_list = [] + rois_num = [] + for img_id, bboxes in enumerate(bbox_list): + rois_num.append(bboxes.shape[0]) + rois_list.append(bboxes) + rois = paddle.concat(rois_list, 0) + rois_num = paddle.to_tensor(rois_num, dtype="int32") + return rois, rois_num + + def pre_process(self, img, relations, texts, gt_bboxes, tag, img_size): + img, relations, texts, gt_bboxes, tag, img_size = ( + img.numpy(), + relations.numpy(), + texts.numpy(), + gt_bboxes.numpy(), + tag.numpy().tolist(), + img_size.numpy(), + ) + temp_relations, temp_texts, temp_gt_bboxes = [], [], [] + h, w = int(np.max(img_size[:, 0])), int(np.max(img_size[:, 1])) + img = paddle.to_tensor(img[:, :, :h, :w]) + batch = len(tag) + for i in range(batch): + num, recoder_len = tag[i][0], tag[i][1] + temp_relations.append( + paddle.to_tensor(relations[i, :num, :num, :], dtype="float32") + ) + temp_texts.append( + paddle.to_tensor(texts[i, :num, :recoder_len], dtype="float32") + ) + temp_gt_bboxes.append( + paddle.to_tensor(gt_bboxes[i, :num, ...], dtype="float32") + ) + return img, temp_relations, temp_texts, temp_gt_bboxes + + def forward(self, inputs): + img = inputs[0] + relations, texts, gt_bboxes, tag, img_size = ( + inputs[1], + inputs[2], + inputs[3], + inputs[5], + inputs[-1], + ) + img, relations, texts, gt_bboxes = self.pre_process( + img, relations, texts, gt_bboxes, tag, img_size + ) + x = self.img_feat(img) + boxes, rois_num = self.bbox2roi(gt_bboxes) + feats = paddle.vision.ops.roi_align( + x, boxes, spatial_scale=1.0, output_size=7, boxes_num=rois_num + ) + feats = self.maxpool(feats).squeeze(-1).squeeze(-1) + return [relations, texts, feats] diff --git a/modules/onnx_ocr_module/src/ppocr/modeling/backbones/rec_densenet.py b/modules/onnx_ocr_module/src/ppocr/modeling/backbones/rec_densenet.py new file mode 100644 index 0000000..3e64584 --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/modeling/backbones/rec_densenet.py @@ -0,0 +1,150 @@ +# copyright (c) 2020 PaddlePaddle Authors. All Rights Reserve. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +""" +This code is refer from: +https://github.com/LBH1024/CAN/models/densenet.py + +""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import math +import paddle +import paddle.nn as nn +import paddle.nn.functional as F + + +class Bottleneck(nn.Layer): + def __init__(self, nChannels, growthRate, use_dropout): + super(Bottleneck, self).__init__() + interChannels = 4 * growthRate + self.bn1 = nn.BatchNorm2D(interChannels) + self.conv1 = nn.Conv2D( + nChannels, interChannels, kernel_size=1, bias_attr=None + ) # Xavier initialization + self.bn2 = nn.BatchNorm2D(growthRate) + self.conv2 = nn.Conv2D( + interChannels, growthRate, kernel_size=3, padding=1, bias_attr=None + ) # Xavier initialization + self.use_dropout = use_dropout + self.dropout = nn.Dropout(p=0.2) + + def forward(self, x): + out = F.relu(self.bn1(self.conv1(x))) + if self.use_dropout: + out = self.dropout(out) + out = F.relu(self.bn2(self.conv2(out))) + if self.use_dropout: + out = self.dropout(out) + out = paddle.concat([x, out], 1) + return out + + +class SingleLayer(nn.Layer): + def __init__(self, nChannels, growthRate, use_dropout): + super(SingleLayer, self).__init__() + self.bn1 = nn.BatchNorm2D(nChannels) + self.conv1 = nn.Conv2D( + nChannels, growthRate, kernel_size=3, padding=1, bias_attr=False + ) + + self.use_dropout = use_dropout + self.dropout = nn.Dropout(p=0.2) + + def forward(self, x): + out = self.conv1(F.relu(x)) + if self.use_dropout: + out = self.dropout(out) + + out = paddle.concat([x, out], 1) + return out + + +class Transition(nn.Layer): + def __init__(self, nChannels, out_channels, use_dropout): + super(Transition, self).__init__() + self.bn1 = nn.BatchNorm2D(out_channels) + self.conv1 = nn.Conv2D(nChannels, out_channels, kernel_size=1, bias_attr=False) + self.use_dropout = use_dropout + self.dropout = nn.Dropout(p=0.2) + + def forward(self, x): + out = F.relu(self.bn1(self.conv1(x))) + if self.use_dropout: + out = self.dropout(out) + out = F.avg_pool2d(out, 2, ceil_mode=True, exclusive=False) + return out + + +class DenseNet(nn.Layer): + def __init__( + self, growthRate, reduction, bottleneck, use_dropout, input_channel, **kwargs + ): + super(DenseNet, self).__init__() + + nDenseBlocks = 16 + nChannels = 2 * growthRate + + self.conv1 = nn.Conv2D( + input_channel, + nChannels, + kernel_size=7, + padding=3, + stride=2, + bias_attr=False, + ) + self.dense1 = self._make_dense( + nChannels, growthRate, nDenseBlocks, bottleneck, use_dropout + ) + nChannels += nDenseBlocks * growthRate + out_channels = int(math.floor(nChannels * reduction)) + self.trans1 = Transition(nChannels, out_channels, use_dropout) + + nChannels = out_channels + self.dense2 = self._make_dense( + nChannels, growthRate, nDenseBlocks, bottleneck, use_dropout + ) + nChannels += nDenseBlocks * growthRate + out_channels = int(math.floor(nChannels * reduction)) + self.trans2 = Transition(nChannels, out_channels, use_dropout) + + nChannels = out_channels + self.dense3 = self._make_dense( + nChannels, growthRate, nDenseBlocks, bottleneck, use_dropout + ) + self.out_channels = out_channels + + def _make_dense(self, nChannels, growthRate, nDenseBlocks, bottleneck, use_dropout): + layers = [] + for i in range(int(nDenseBlocks)): + if bottleneck: + layers.append(Bottleneck(nChannels, growthRate, use_dropout)) + else: + layers.append(SingleLayer(nChannels, growthRate, use_dropout)) + nChannels += growthRate + return nn.Sequential(*layers) + + def forward(self, inputs): + x, x_m, y = inputs + out = self.conv1(x) + out = F.relu(out) + out = F.max_pool2d(out, 2, ceil_mode=True) + out = self.dense1(out) + out = self.trans1(out) + out = self.dense2(out) + out = self.trans2(out) + out = self.dense3(out) + return out, x_m, y diff --git a/modules/onnx_ocr_module/src/ppocr/modeling/backbones/rec_donut_swin.py b/modules/onnx_ocr_module/src/ppocr/modeling/backbones/rec_donut_swin.py new file mode 100644 index 0000000..accad00 --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/modeling/backbones/rec_donut_swin.py @@ -0,0 +1,1296 @@ +# copyright (c) 2024 PaddlePaddle Authors. All Rights Reserve. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +""" +This code is refer from: +https://github.com/huggingface/transformers/blob/main/src/transformers/models/donut/modeling_donut_swin.py + +""" + +import collections.abc +from collections import OrderedDict +import math +import numpy as np +from dataclasses import dataclass +from typing import Optional, Tuple, Union + +import paddle +from paddle import nn +import paddle.nn.functional as F + +from paddle.nn.initializer import ( + TruncatedNormal, + Constant, + Normal, + KaimingUniform, + XavierUniform, +) + +zeros_ = Constant(value=0.0) +ones_ = Constant(value=1.0) +kaiming_normal_ = KaimingUniform(nonlinearity="relu") +trunc_normal_ = TruncatedNormal(std=0.02) +xavier_uniform_ = XavierUniform() + +# General docstring +_CONFIG_FOR_DOC = "DonutSwinConfig" + +# Base docstring +_CHECKPOINT_FOR_DOC = "https://huggingface.co/naver-clova-ix/donut-base" +_EXPECTED_OUTPUT_SHAPE = [1, 49, 768] + + +class DonutSwinConfig(object): + model_type = "donut-swin" + + attribute_map = { + "num_attention_heads": "num_heads", + "num_hidden_layers": "num_layers", + } + + def __init__( + self, + image_size=224, + patch_size=4, + num_channels=3, + embed_dim=96, + depths=[2, 2, 6, 2], + num_heads=[3, 6, 12, 24], + window_size=7, + mlp_ratio=4.0, + qkv_bias=True, + hidden_dropout_prob=0.0, + attention_probs_dropout_prob=0.0, + drop_path_rate=0.1, + hidden_act="gelu", + use_absolute_embeddings=False, + initializer_range=0.02, + layer_norm_eps=1e-5, + **kwargs, + ): + super().__init__() + + self.image_size = image_size + self.patch_size = patch_size + self.num_channels = num_channels + self.embed_dim = embed_dim + self.depths = depths + self.num_layers = len(depths) + self.num_heads = num_heads + self.window_size = window_size + self.mlp_ratio = mlp_ratio + self.qkv_bias = qkv_bias + self.hidden_dropout_prob = hidden_dropout_prob + self.attention_probs_dropout_prob = attention_probs_dropout_prob + self.drop_path_rate = drop_path_rate + self.hidden_act = hidden_act + self.use_absolute_embeddings = use_absolute_embeddings + self.layer_norm_eps = layer_norm_eps + self.initializer_range = initializer_range + self.hidden_size = int(embed_dim * 2 ** (len(depths) - 1)) + + for key, value in kwargs.items(): + try: + setattr(self, key, value) + except AttributeError as err: + print(f"Can't set {key} with value {value} for {self}") + raise err + + +@dataclass +# Copied from transformers.models.swin.modeling_swin.SwinEncoderOutput with Swin->DonutSwin +class DonutSwinEncoderOutput(OrderedDict): + last_hidden_state = None + hidden_states = None + attentions = None + reshaped_hidden_states = None + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + + def __getitem__(self, k): + if isinstance(k, str): + inner_dict = dict(self.items()) + return inner_dict[k] + else: + return self.to_tuple()[k] + + def __setattr__(self, name, value): + if name in self.keys() and value is not None: + super().__setitem__(name, value) + super().__setattr__(name, value) + + def __setitem__(self, key, value): + super().__setitem__(key, value) + super().__setattr__(key, value) + + def to_tuple(self): + """ + Convert self to a tuple containing all the attributes/keys that are not `None`. + """ + return tuple(self[k] for k in self.keys()) + + +@dataclass +# Copied from transformers.models.swin.modeling_swin.SwinModelOutput with Swin->DonutSwin +class DonutSwinModelOutput(OrderedDict): + last_hidden_state = None + pooler_output = None + hidden_states = None + attentions = None + reshaped_hidden_states = None + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + + def __getitem__(self, k): + if isinstance(k, str): + inner_dict = dict(self.items()) + return inner_dict[k] + else: + return self.to_tuple()[k] + + def __setattr__(self, name, value): + if name in self.keys() and value is not None: + super().__setitem__(name, value) + super().__setattr__(name, value) + + def __setitem__(self, key, value): + super().__setitem__(key, value) + super().__setattr__(key, value) + + def to_tuple(self): + """ + Convert self to a tuple containing all the attributes/keys that are not `None`. + """ + return tuple(self[k] for k in self.keys()) + + +# Copied from transformers.models.swin.modeling_swin.window_partition +def window_partition(input_feature, window_size): + """ + Partitions the given input into windows. + """ + batch_size, height, width, num_channels = input_feature.shape + input_feature = input_feature.reshape( + [ + batch_size, + height // window_size, + window_size, + width // window_size, + window_size, + num_channels, + ] + ) + windows = input_feature.transpose([0, 1, 3, 2, 4, 5]).reshape( + [-1, window_size, window_size, num_channels] + ) + return windows + + +# Copied from transformers.models.swin.modeling_swin.window_reverse +def window_reverse(windows, window_size, height, width): + """ + Merges windows to produce higher resolution features. + """ + num_channels = windows.shape[-1] + windows = windows.reshape( + [ + -1, + height // window_size, + width // window_size, + window_size, + window_size, + num_channels, + ] + ) + windows = windows.transpose([0, 1, 3, 2, 4, 5]).reshape( + [-1, height, width, num_channels] + ) + return windows + + +# Copied from transformers.models.swin.modeling_swin.SwinEmbeddings with Swin->DonutSwin +class DonutSwinEmbeddings(nn.Layer): + """ + Construct the patch and position embeddings. Optionally, also the mask token. + """ + + def __init__(self, config, use_mask_token=False): + super().__init__() + + self.patch_embeddings = DonutSwinPatchEmbeddings(config) + num_patches = self.patch_embeddings.num_patches + self.patch_grid = self.patch_embeddings.grid_size + if use_mask_token: + self.mask_token = paddle.create_parameter( + [1, 1, config.embed_dim], dtype="float32" + ) + zeros_(self.mask_token) + else: + self.mask_token = None + if config.use_absolute_embeddings: + self.position_embeddings = paddle.create_parameter( + [1, num_patches + 1, config.embed_dim], dtype="float32" + ) + zeros_(self.position_embedding) + else: + self.position_embeddings = None + + self.norm = nn.LayerNorm(config.embed_dim) + self.dropout = nn.Dropout(config.hidden_dropout_prob) + + def forward(self, pixel_values, bool_masked_pos=None): + + embeddings, output_dimensions = self.patch_embeddings(pixel_values) + embeddings = self.norm(embeddings) + + batch_size, seq_len, _ = embeddings.shape + + if bool_masked_pos is not None: + mask_tokens = self.mask_token.expand(batch_size, seq_len, -1) + mask = bool_masked_pos.unsqueeze(-1).type_as(mask_tokens) + embeddings = embeddings * (1.0 - mask) + mask_tokens * mask + + if self.position_embeddings is not None: + embeddings = embeddings + self.position_embeddings + embeddings = self.dropout(embeddings) + return embeddings, output_dimensions + + +class MyConv2d(nn.Conv2D): + def __init__( + self, + in_channel, + out_channels, + kernel_size, + stride=1, + padding="SAME", + dilation=1, + groups=1, + bias_attr=False, + eps=1e-6, + ): + super().__init__( + in_channel, + out_channels, + kernel_size, + stride=stride, + padding=padding, + dilation=dilation, + groups=groups, + bias_attr=bias_attr, + ) + self.weight = paddle.create_parameter( + [out_channels, in_channel, kernel_size[0], kernel_size[1]], dtype="float32" + ) + self.bias = paddle.create_parameter([out_channels], dtype="float32") + ones_(self.weight) + zeros_(self.bias) + + def forward(self, x): + x = F.conv2d( + x, + self.weight, + self.bias, + self._stride, + self._padding, + self._dilation, + self._groups, + ) + return x + + +# Copied from transformers.models.swin.modeling_swin.SwinPatchEmbeddings +class DonutSwinPatchEmbeddings(nn.Layer): + """ + This class turns `pixel_values` of shape `(batch_size, num_channels, height, width)` into the initial + `hidden_states` (patch embeddings) of shape `(batch_size, seq_length, hidden_size)` to be consumed by a + Transformer. + """ + + def __init__(self, config): + super().__init__() + image_size, patch_size = config.image_size, config.patch_size + num_channels, hidden_size = config.num_channels, config.embed_dim + image_size = ( + image_size + if isinstance(image_size, collections.abc.Iterable) + else (image_size, image_size) + ) + patch_size = ( + patch_size + if isinstance(patch_size, collections.abc.Iterable) + else (patch_size, patch_size) + ) + num_patches = (image_size[1] // patch_size[1]) * ( + image_size[0] // patch_size[0] + ) + self.image_size = image_size + self.patch_size = patch_size + self.num_channels = num_channels + self.num_patches = num_patches + self.is_export = config.is_export + self.grid_size = ( + image_size[0] // patch_size[0], + image_size[1] // patch_size[1], + ) + self.projection = nn.Conv2D( + num_channels, hidden_size, kernel_size=patch_size, stride=patch_size + ) + + def maybe_pad(self, pixel_values, height, width): + if width % self.patch_size[1] != 0: + pad_values = (0, self.patch_size[1] - width % self.patch_size[1]) + if self.is_export: + pad_values = paddle.to_tensor(pad_values, dtype="int32") + pixel_values = nn.functional.pad(pixel_values, pad_values) + if height % self.patch_size[0] != 0: + pad_values = (0, 0, 0, self.patch_size[0] - height % self.patch_size[0]) + if self.is_export: + pad_values = paddle.to_tensor(pad_values, dtype="int32") + pixel_values = nn.functional.pad(pixel_values, pad_values) + return pixel_values + + def forward(self, pixel_values) -> Tuple[paddle.Tensor, Tuple[int]]: + _, num_channels, height, width = pixel_values.shape + if num_channels != self.num_channels: + raise ValueError( + "Make sure that the channel dimension of the pixel values match with the one set in the configuration." + ) + pixel_values = self.maybe_pad(pixel_values, height, width) + embeddings = self.projection(pixel_values) + + _, _, height, width = embeddings.shape + output_dimensions = (height, width) + embeddings = embeddings.flatten(2).transpose([0, 2, 1]) + + return embeddings, output_dimensions + + +# Copied from transformers.models.swin.modeling_swin.SwinPatchMerging +class DonutSwinPatchMerging(nn.Layer): + """ + Patch Merging Layer. + + Args: + input_resolution (`Tuple[int]`): + Resolution of input feature. + dim (`int`): + Number of input channels. + norm_layer (`nn.Layer`, *optional*, defaults to `nn.LayerNorm`): + Normalization layer class. + """ + + def __init__( + self, + input_resolution: Tuple[int], + dim: int, + norm_layer: nn.Layer = nn.LayerNorm, + is_export=False, + ): + super().__init__() + self.input_resolution = input_resolution + self.dim = dim + self.reduction = nn.Linear(4 * dim, 2 * dim, bias_attr=False) + self.norm = norm_layer(4 * dim) + self.is_export = is_export + + def maybe_pad(self, input_feature, height, width): + should_pad = (height % 2 == 1) or (width % 2 == 1) + if should_pad: + pad_values = (0, 0, 0, width % 2, 0, height % 2) + if self.is_export: + pad_values = paddle.to_tensor(pad_values, dtype="int32") + input_feature = nn.functional.pad(input_feature, pad_values) + + return input_feature + + def forward( + self, input_feature: paddle.Tensor, input_dimensions: Tuple[int, int] + ) -> paddle.Tensor: + height, width = input_dimensions + batch_size, dim, num_channels = input_feature.shape + + input_feature = input_feature.reshape([batch_size, height, width, num_channels]) + + input_feature = self.maybe_pad(input_feature, height, width) + input_feature_0 = input_feature[:, 0::2, 0::2, :] + input_feature_1 = input_feature[:, 1::2, 0::2, :] + input_feature_2 = input_feature[:, 0::2, 1::2, :] + input_feature_3 = input_feature[:, 1::2, 1::2, :] + input_feature = paddle.concat( + [input_feature_0, input_feature_1, input_feature_2, input_feature_3], -1 + ) + input_feature = input_feature.reshape( + [batch_size, -1, 4 * num_channels] + ) # batch_size height/2*width/2 4*C + + input_feature = self.norm(input_feature) + input_feature = self.reduction(input_feature) + + return input_feature + + +# Copied from transformers.models.beit.modeling_beit.drop_path +def drop_path( + input: paddle.Tensor, drop_prob: float = 0.0, training: bool = False +) -> paddle.Tensor: + if drop_prob == 0.0 or not training: + return input + keep_prob = 1 - drop_prob + shape = (input.shape[0],) + (1,) * ( + input.ndim - 1 + ) # work with diff dim tensors, not just 2D ConvNets + random_tensor = keep_prob + paddle.rand( + shape, + dtype=input.dtype, + ) + random_tensor.floor_() # binarize + output = input / keep_prob * random_tensor + return output + + +# Copied from transformers.models.swin.modeling_swin.SwinDropPath +class DonutSwinDropPath(nn.Layer): + """Drop paths (Stochastic Depth) per sample (when applied in main path of residual blocks).""" + + def __init__(self, drop_prob: Optional[float] = None) -> None: + super().__init__() + self.drop_prob = drop_prob + + def forward(self, hidden_states: paddle.Tensor) -> paddle.Tensor: + return drop_path(hidden_states, self.drop_prob, self.training) + + def extra_repr(self) -> str: + return "p={}".format(self.drop_prob) + + +class DonutSwinSelfAttention(nn.Layer): + def __init__(self, config, dim, num_heads, window_size): + super().__init__() + if dim % num_heads != 0: + raise ValueError( + f"The hidden size ({dim}) is not a multiple of the number of attention heads ({num_heads})" + ) + + self.num_attention_heads = num_heads + self.attention_head_size = int(dim / num_heads) + self.all_head_size = self.num_attention_heads * self.attention_head_size + self.window_size = ( + window_size + if isinstance(window_size, collections.abc.Iterable) + else (window_size, window_size) + ) + self.relative_position_bias_table = paddle.create_parameter( + [(2 * self.window_size[0] - 1) * (2 * self.window_size[1] - 1), num_heads], + dtype="float32", + ) + zeros_(self.relative_position_bias_table) + + # get pair-wise relative position index for each token inside the window + coords_h = paddle.arange(self.window_size[0]) + coords_w = paddle.arange(self.window_size[1]) + coords = paddle.stack(paddle.meshgrid(coords_h, coords_w, indexing="ij")) + coords_flatten = paddle.flatten(coords, 1) + relative_coords = coords_flatten[:, :, None] - coords_flatten[:, None, :] + relative_coords = relative_coords.transpose([1, 2, 0]) + relative_coords[:, :, 0] += self.window_size[0] - 1 + relative_coords[:, :, 1] += self.window_size[1] - 1 + relative_coords[:, :, 0] *= 2 * self.window_size[1] - 1 + relative_position_index = relative_coords.sum(-1) + self.register_buffer("relative_position_index", relative_position_index) + + self.query = nn.Linear( + self.all_head_size, self.all_head_size, bias_attr=config.qkv_bias + ) + self.key = nn.Linear( + self.all_head_size, self.all_head_size, bias_attr=config.qkv_bias + ) + self.value = nn.Linear( + self.all_head_size, self.all_head_size, bias_attr=config.qkv_bias + ) + + self.dropout = nn.Dropout(config.attention_probs_dropout_prob) + + def transpose_for_scores(self, x): + new_x_shape = x.shape[:-1] + [ + self.num_attention_heads, + self.attention_head_size, + ] + x = x.reshape(new_x_shape) + return x.transpose([0, 2, 1, 3]) + + def forward( + self, + hidden_states: paddle.Tensor, + attention_mask=None, + head_mask=None, + output_attentions=False, + ) -> Tuple[paddle.Tensor]: + batch_size, dim, num_channels = hidden_states.shape + mixed_query_layer = self.query(hidden_states) + key_layer = self.transpose_for_scores(self.key(hidden_states)) + value_layer = self.transpose_for_scores(self.value(hidden_states)) + query_layer = self.transpose_for_scores(mixed_query_layer) + + # Take the dot product between "query" and "key" to get the raw attention scores. + attention_scores = paddle.matmul(query_layer, key_layer.transpose([0, 1, 3, 2])) + + attention_scores = attention_scores / math.sqrt(self.attention_head_size) + + relative_position_bias = self.relative_position_bias_table[ + self.relative_position_index.reshape([-1]) + ] + relative_position_bias = relative_position_bias.reshape( + [ + self.window_size[0] * self.window_size[1], + self.window_size[0] * self.window_size[1], + -1, + ] + ) + + relative_position_bias = relative_position_bias.transpose([2, 0, 1]) + attention_scores = attention_scores + relative_position_bias.unsqueeze(0) + + if attention_mask is not None: + # Apply the attention mask is (precomputed for all layers in DonutSwinModel forward() function) + mask_shape = attention_mask.shape[0] + attention_scores = attention_scores.reshape( + [ + batch_size // mask_shape, + mask_shape, + self.num_attention_heads, + dim, + dim, + ] + ) + attention_scores = attention_scores + attention_mask.unsqueeze(1).unsqueeze( + 0 + ) + attention_scores = attention_scores.reshape( + [-1, self.num_attention_heads, dim, dim] + ) + + # Normalize the attention scores to probabilities. + attention_probs = nn.functional.softmax(attention_scores, axis=-1) + + # This is actually dropping out entire tokens to attend to, which might + # seem a bit unusual, but is taken from the original Transformer paper. + attention_probs = self.dropout(attention_probs) + + # Mask heads if we want to + if head_mask is not None: + attention_probs = attention_probs * head_mask + + context_layer = paddle.matmul(attention_probs, value_layer) + context_layer = context_layer.transpose([0, 2, 1, 3]) + new_context_layer_shape = tuple(context_layer.shape[:-2]) + ( + self.all_head_size, + ) + context_layer = context_layer.reshape(new_context_layer_shape) + outputs = ( + (context_layer, attention_probs) if output_attentions else (context_layer,) + ) + return outputs + + +# Copied from transformers.models.swin.modeling_swin.SwinSelfOutput +class DonutSwinSelfOutput(nn.Layer): + def __init__(self, config, dim): + super().__init__() + self.dense = nn.Linear(dim, dim) + self.dropout = nn.Dropout(config.attention_probs_dropout_prob) + + def forward( + self, hidden_states: paddle.Tensor, input_tensor: paddle.Tensor + ) -> paddle.Tensor: + hidden_states = self.dense(hidden_states) + hidden_states = self.dropout(hidden_states) + + return hidden_states + + +# Copied from transformers.models.swin.modeling_swin.SwinAttention with Swin->DonutSwin +class DonutSwinAttention(nn.Layer): + def __init__(self, config, dim, num_heads, window_size): + super().__init__() + self.self = DonutSwinSelfAttention(config, dim, num_heads, window_size) + self.output = DonutSwinSelfOutput(config, dim) + self.pruned_heads = set() + + def forward( + self, + hidden_states: paddle.Tensor, + attention_mask=None, + head_mask=None, + output_attentions=False, + ) -> Tuple[paddle.Tensor]: + self_outputs = self.self( + hidden_states, attention_mask, head_mask, output_attentions + ) + attention_output = self.output(self_outputs[0], hidden_states) + outputs = (attention_output,) + self_outputs[ + 1: + ] # add attentions if we output them + return outputs + + +# Copied from transformers.models.swin.modeling_swin.SwinIntermediate +class DonutSwinIntermediate(nn.Layer): + def __init__(self, config, dim): + super().__init__() + self.dense = nn.Linear(dim, int(config.mlp_ratio * dim)) + self.intermediate_act_fn = F.gelu + + def forward(self, hidden_states: paddle.Tensor) -> paddle.Tensor: + hidden_states = self.dense(hidden_states) + hidden_states = self.intermediate_act_fn(hidden_states) + return hidden_states + + +# Copied from transformers.models.swin.modeling_swin.SwinOutput +class DonutSwinOutput(nn.Layer): + def __init__(self, config, dim): + super().__init__() + self.dense = nn.Linear(int(config.mlp_ratio * dim), dim) + self.dropout = nn.Dropout(config.hidden_dropout_prob) + + def forward(self, hidden_states: paddle.Tensor) -> paddle.Tensor: + hidden_states = self.dense(hidden_states) + hidden_states = self.dropout(hidden_states) + return hidden_states + + +# Copied from transformers.models.swin.modeling_swin.SwinLayer with Swin->DonutSwin +class DonutSwinLayer(nn.Layer): + def __init__(self, config, dim, input_resolution, num_heads, shift_size=0): + super().__init__() + self.chunk_size_feed_forward = config.chunk_size_feed_forward + self.shift_size = shift_size + self.window_size = config.window_size + self.input_resolution = input_resolution + self.layernorm_before = nn.LayerNorm(dim, epsilon=config.layer_norm_eps) + self.attention = DonutSwinAttention( + config, dim, num_heads, window_size=self.window_size + ) + self.drop_path = ( + DonutSwinDropPath(config.drop_path_rate) + if config.drop_path_rate > 0.0 + else nn.Identity() + ) + self.layernorm_after = nn.LayerNorm(dim, epsilon=config.layer_norm_eps) + self.intermediate = DonutSwinIntermediate(config, dim) + self.output = DonutSwinOutput(config, dim) + self.is_export = config.is_export + + def set_shift_and_window_size(self, input_resolution): + if min(input_resolution) <= self.window_size: + # if window size is larger than input resolution, we don't partition windows + self.shift_size = 0 + self.window_size = min(input_resolution) + + def get_attn_mask_export(self, height, width, dtype): + + attn_mask = None + height_slices = ( + slice(0, -self.window_size), + slice(-self.window_size, -self.shift_size), + slice(-self.shift_size, None), + ) + width_slices = ( + slice(0, -self.window_size), + slice(-self.window_size, -self.shift_size), + slice(-self.shift_size, None), + ) + img_mask = paddle.zeros((1, height, width, 1), dtype=dtype) + count = 0 + for height_slice in height_slices: + for width_slice in width_slices: + if self.shift_size > 0: + img_mask[:, height_slice, width_slice, :] = count + count += 1 + if paddle.to_tensor(self.shift_size > 0).cast(paddle.bool): + # calculate attention mask for SW-MSA + mask_windows = window_partition(img_mask, self.window_size) + mask_windows = mask_windows.reshape( + [-1, self.window_size * self.window_size] + ) + attn_mask = mask_windows.unsqueeze(1) - mask_windows.unsqueeze(2) + attn_mask = attn_mask.masked_fill( + attn_mask != 0, float(-100.0) + ).masked_fill(attn_mask == 0, float(0.0)) + + return attn_mask + + def get_attn_mask(self, height, width, dtype): + if self.shift_size > 0: + # calculate attention mask for SW-MSA + img_mask = paddle.zeros((1, height, width, 1), dtype=dtype) + height_slices = ( + slice(0, -self.window_size), + slice(-self.window_size, -self.shift_size), + slice(-self.shift_size, None), + ) + width_slices = ( + slice(0, -self.window_size), + slice(-self.window_size, -self.shift_size), + slice(-self.shift_size, None), + ) + + count = 0 + for height_slice in height_slices: + for width_slice in width_slices: + img_mask[:, height_slice, width_slice, :] = count + count += 1 + + mask_windows = window_partition(img_mask, self.window_size) + mask_windows = mask_windows.reshape( + [-1, self.window_size * self.window_size] + ) + attn_mask = mask_windows.unsqueeze(1) - mask_windows.unsqueeze(2) + attn_mask = attn_mask.masked_fill( + attn_mask != 0, float(-100.0) + ).masked_fill(attn_mask == 0, float(0.0)) + else: + attn_mask = None + return attn_mask + + def maybe_pad(self, hidden_states, height, width): + pad_right = (self.window_size - width % self.window_size) % self.window_size + pad_bottom = (self.window_size - height % self.window_size) % self.window_size + pad_values = (0, 0, 0, pad_bottom, 0, pad_right, 0, 0) + hidden_states = nn.functional.pad(hidden_states, pad_values) + return hidden_states, pad_values + + def forward( + self, + hidden_states: paddle.Tensor, + input_dimensions: Tuple[int, int], + head_mask=None, + output_attentions=False, + always_partition=False, + ) -> Tuple[paddle.Tensor, paddle.Tensor]: + if not always_partition: + self.set_shift_and_window_size(input_dimensions) + else: + pass + height, width = input_dimensions + batch_size, _, channels = hidden_states.shape + shortcut = hidden_states + + hidden_states = self.layernorm_before(hidden_states) + + hidden_states = hidden_states.reshape([batch_size, height, width, channels]) + + # pad hidden_states to multiples of window size + hidden_states, pad_values = self.maybe_pad(hidden_states, height, width) + + _, height_pad, width_pad, _ = hidden_states.shape + + # cyclic shift + if self.shift_size > 0: + shift_value = (-self.shift_size, -self.shift_size) + if self.is_export: + shift_value = paddle.to_tensor(shift_value, dtype="int32") + shifted_hidden_states = paddle.roll( + hidden_states, shifts=shift_value, axis=(1, 2) + ) + else: + shifted_hidden_states = hidden_states + + # partition windows + hidden_states_windows = window_partition( + shifted_hidden_states, self.window_size + ) + hidden_states_windows = hidden_states_windows.reshape( + [-1, self.window_size * self.window_size, channels] + ) + attn_mask = self.get_attn_mask(height_pad, width_pad, dtype=hidden_states.dtype) + + attention_outputs = self.attention( + hidden_states_windows, + attn_mask, + head_mask, + output_attentions=output_attentions, + ) + attention_output = attention_outputs[0] + + attention_windows = attention_output.reshape( + [-1, self.window_size, self.window_size, channels] + ) + shifted_windows = window_reverse( + attention_windows, self.window_size, height_pad, width_pad + ) + # reverse cyclic shift + if self.shift_size > 0: + shift_value = (self.shift_size, self.shift_size) + if self.is_export: + shift_value = paddle.to_tensor(shift_value, dtype="int32") + attention_windows = paddle.roll( + shifted_windows, shifts=shift_value, axis=(1, 2) + ) + else: + attention_windows = shifted_windows + + was_padded = pad_values[3] > 0 or pad_values[5] > 0 + if was_padded: + attention_windows = attention_windows[:, :height, :width, :].contiguous() + + attention_windows = attention_windows.reshape( + [batch_size, height * width, channels] + ) + hidden_states = shortcut + self.drop_path(attention_windows) + layer_output = self.layernorm_after(hidden_states) + layer_output = self.intermediate(layer_output) + layer_output = hidden_states + self.output(layer_output) + layer_outputs = ( + (layer_output, attention_outputs[1]) + if output_attentions + else (layer_output,) + ) + return layer_outputs + + +# Copied from transformers.models.swin.modeling_swin.SwinStage with Swin->DonutSwin +class DonutSwinStage(nn.Layer): + def __init__( + self, config, dim, input_resolution, depth, num_heads, drop_path, downsample + ): + super().__init__() + self.config = config + self.dim = dim + self.blocks = nn.LayerList( + [ + DonutSwinLayer( + config=config, + dim=dim, + input_resolution=input_resolution, + num_heads=num_heads, + shift_size=0 if (i % 2 == 0) else config.window_size // 2, + ) + for i in range(depth) + ] + ) + self.is_export = config.is_export + + # patch merging layer + if downsample is not None: + self.downsample = downsample( + input_resolution, + dim=dim, + norm_layer=nn.LayerNorm, + is_export=self.is_export, + ) + else: + self.downsample = None + + self.pointing = False + + def forward( + self, + hidden_states: paddle.Tensor, + input_dimensions: Tuple[int, int], + head_mask=None, + output_attentions=False, + always_partition=False, + ) -> Tuple[paddle.Tensor]: + height, width = input_dimensions + + for i, layer_module in enumerate(self.blocks): + layer_head_mask = head_mask[i] if head_mask is not None else None + + layer_outputs = layer_module( + hidden_states, + input_dimensions, + layer_head_mask, + output_attentions, + always_partition, + ) + + hidden_states = layer_outputs[0] + + hidden_states_before_downsampling = hidden_states + if self.downsample is not None: + height_downsampled, width_downsampled = (height + 1) // 2, (width + 1) // 2 + output_dimensions = (height, width, height_downsampled, width_downsampled) + hidden_states = self.downsample( + hidden_states_before_downsampling, input_dimensions + ) + else: + output_dimensions = (height, width, height, width) + + stage_outputs = ( + hidden_states, + hidden_states_before_downsampling, + output_dimensions, + ) + + if output_attentions: + stage_outputs += layer_outputs[1:] + return stage_outputs + + +# Copied from transformers.models.swin.modeling_swin.SwinEncoder with Swin->DonutSwin +class DonutSwinEncoder(nn.Layer): + def __init__(self, config, grid_size): + super().__init__() + self.num_layers = len(config.depths) + self.config = config + dpr = [ + x.item() + for x in paddle.linspace(0, config.drop_path_rate, sum(config.depths)) + ] + self.layers = nn.LayerList( + [ + DonutSwinStage( + config=config, + dim=int(config.embed_dim * 2**i_layer), + input_resolution=( + grid_size[0] // (2**i_layer), + grid_size[1] // (2**i_layer), + ), + depth=config.depths[i_layer], + num_heads=config.num_heads[i_layer], + drop_path=dpr[ + sum(config.depths[:i_layer]) : sum(config.depths[: i_layer + 1]) + ], + downsample=( + DonutSwinPatchMerging + if (i_layer < self.num_layers - 1) + else None + ), + ) + for i_layer in range(self.num_layers) + ] + ) + + self.gradient_checkpointing = False + + def forward( + self, + hidden_states: paddle.Tensor, + input_dimensions: Tuple[int, int], + head_mask=None, + output_attentions=False, + output_hidden_states=False, + output_hidden_states_before_downsampling=False, + always_partition=False, + return_dict=True, + ): + all_hidden_states = () if output_hidden_states else None + all_reshaped_hidden_states = () if output_hidden_states else None + all_self_attentions = () if output_attentions else None + + if output_hidden_states: + batch_size, _, hidden_size = hidden_states.shape + reshaped_hidden_state = hidden_states.view( + batch_size, *input_dimensions, hidden_size + ) + reshaped_hidden_state = reshaped_hidden_state.permute(0, 3, 1, 2) + all_hidden_states += (hidden_states,) + all_reshaped_hidden_states += (reshaped_hidden_state,) + + for i, layer_module in enumerate(self.layers): + layer_head_mask = head_mask[i] if head_mask is not None else None + + if self.gradient_checkpointing and self.training: + layer_outputs = self._gradient_checkpointing_func( + layer_module.__call__, + hidden_states, + input_dimensions, + layer_head_mask, + output_attentions, + always_partition, + ) + else: + layer_outputs = layer_module( + hidden_states, + input_dimensions, + layer_head_mask, + output_attentions, + always_partition, + ) + + hidden_states = layer_outputs[0] + + hidden_states_before_downsampling = layer_outputs[1] + output_dimensions = layer_outputs[2] + + input_dimensions = (output_dimensions[-2], output_dimensions[-1]) + + if output_hidden_states and output_hidden_states_before_downsampling: + batch_size, _, hidden_size = hidden_states_before_downsampling.shape + reshaped_hidden_state = hidden_states_before_downsampling.reshape( + [ + batch_size, + *(output_dimensions[0], output_dimensions[1]), + hidden_size, + ] + ) + reshaped_hidden_state = reshaped_hidden_state.transpose([0, 3, 1, 2]) + all_hidden_states += (hidden_states_before_downsampling,) + all_reshaped_hidden_states += (reshaped_hidden_state,) + elif output_hidden_states and not output_hidden_states_before_downsampling: + batch_size, _, hidden_size = hidden_states.shape + reshaped_hidden_state = hidden_states.reshape( + [batch_size, *input_dimensions, hidden_size] + ) + reshaped_hidden_state = reshaped_hidden_state.transpose([0, 3, 1, 2]) + all_hidden_states += (hidden_states,) + all_reshaped_hidden_states += (reshaped_hidden_state,) + + if output_attentions: + all_self_attentions += layer_outputs[3:] + + if not return_dict: + return tuple( + v + for v in [hidden_states, all_hidden_states, all_self_attentions] + if v is not None + ) + + return DonutSwinEncoderOutput( + last_hidden_state=hidden_states, + hidden_states=all_hidden_states, + attentions=all_self_attentions, + reshaped_hidden_states=all_reshaped_hidden_states, + ) + + +class DonutSwinPreTrainedModel(nn.Layer): + """ + An abstract class to handle weights initialization and a simple interface for downloading and loading pretrained + models. + """ + + config_class = DonutSwinConfig + base_model_prefix = "swin" + main_input_name = "pixel_values" + supports_gradient_checkpointing = True + + def _init_weights(self, module): + """Initialize the weights""" + if isinstance(module, (nn.Linear, nn.Conv2D)): + normal_ = Normal(mean=0.0, std=self.config.initializer_range) + normal_(module.weight) + if module.bias is not None: + zeros_(module.bias) + elif isinstance(module, nn.LayerNorm): + zeros_(module.bias) + ones_(module.weight) + + def _initialize_weights(self, module): + """ + Initialize the weights if they are not already initialized. + """ + if getattr(module, "_is_hf_initialized", False): + return + self._init_weights(module) + + def post_init(self): + self.apply(self._initialize_weights) + + def get_head_mask(self, head_mask, num_hidden_layers, is_attention_chunked=False): + if head_mask is not None: + head_mask = self._convert_head_mask_to_5d(head_mask, num_hidden_layers) + if is_attention_chunked is True: + head_mask = head_mask.unsqueeze(-1) + else: + head_mask = [None] * num_hidden_layers + + return head_mask + + +class DonutSwinModel(DonutSwinPreTrainedModel): + def __init__( + self, + in_channels=3, + hidden_size=1024, + num_layers=4, + num_heads=[4, 8, 16, 32], + add_pooling_layer=True, + use_mask_token=False, + is_export=False, + ): + super().__init__() + donut_swin_config = { + "return_dict": True, + "output_hidden_states": False, + "output_attentions": False, + "use_bfloat16": False, + "tf_legacy_loss": False, + "pruned_heads": {}, + "tie_word_embeddings": True, + "chunk_size_feed_forward": 0, + "is_encoder_decoder": False, + "is_decoder": False, + "cross_attention_hidden_size": None, + "add_cross_attention": False, + "tie_encoder_decoder": False, + "max_length": 20, + "min_length": 0, + "do_sample": False, + "early_stopping": False, + "num_beams": 1, + "num_beam_groups": 1, + "diversity_penalty": 0.0, + "temperature": 1.0, + "top_k": 50, + "top_p": 1.0, + "typical_p": 1.0, + "repetition_penalty": 1.0, + "length_penalty": 1.0, + "no_repeat_ngram_size": 0, + "encoder_no_repeat_ngram_size": 0, + "bad_words_ids": None, + "num_return_sequences": 1, + "output_scores": False, + "return_dict_in_generate": False, + "forced_bos_token_id": None, + "forced_eos_token_id": None, + "remove_invalid_values": False, + "exponential_decay_length_penalty": None, + "suppress_tokens": None, + "begin_suppress_tokens": None, + "architectures": None, + "finetuning_task": None, + "id2label": {0: "LABEL_0", 1: "LABEL_1"}, + "label2id": {"LABEL_0": 0, "LABEL_1": 1}, + "tokenizer_class": None, + "prefix": None, + "bos_token_id": None, + "pad_token_id": None, + "eos_token_id": None, + "sep_token_id": None, + "decoder_start_token_id": None, + "task_specific_params": None, + "problem_type": None, + "_name_or_path": "", + "_commit_hash": None, + "_attn_implementation_internal": None, + "transformers_version": None, + "hidden_size": hidden_size, + "num_layers": num_layers, + "path_norm": True, + "use_2d_embeddings": False, + "image_size": [420, 420], + "patch_size": 4, + "num_channels": in_channels, + "embed_dim": 128, + "depths": [2, 2, 14, 2], + "num_heads": num_heads, + "window_size": 5, + "mlp_ratio": 4.0, + "qkv_bias": True, + "hidden_dropout_prob": 0.0, + "attention_probs_dropout_prob": 0.0, + "drop_path_rate": 0.1, + "hidden_act": "gelu", + "use_absolute_embeddings": False, + "layer_norm_eps": 1e-05, + "initializer_range": 0.02, + "is_export": is_export, + } + + config = DonutSwinConfig(**donut_swin_config) + self.config = config + self.num_layers = len(config.depths) + self.num_features = int(config.embed_dim * 2 ** (self.num_layers - 1)) + + self.embeddings = DonutSwinEmbeddings(config, use_mask_token=use_mask_token) + self.encoder = DonutSwinEncoder(config, self.embeddings.patch_grid) + + self.pooler = nn.AdaptiveAvgPool1D(1) if add_pooling_layer else None + self.out_channels = hidden_size + self.post_init() + + def get_input_embeddings(self): + return self.embeddings.patch_embeddings + + def forward( + self, + input_data=None, + bool_masked_pos=None, + head_mask=None, + output_attentions=None, + output_hidden_states=None, + return_dict=None, + ) -> Union[Tuple, DonutSwinModelOutput]: + r""" + bool_masked_pos (`paddle.BoolTensor` of shape `(batch_size, num_patches)`): + Boolean masked positions. Indicates which patches are masked (1) and which aren't (0). + """ + if self.training: + pixel_values, label, attention_mask = input_data + else: + if isinstance(input_data, list): + pixel_values = input_data[0] + else: + pixel_values = input_data + output_attentions = ( + output_attentions + if output_attentions is not None + else self.config.output_attentions + ) + output_hidden_states = ( + output_hidden_states + if output_hidden_states is not None + else self.config.output_hidden_states + ) + return_dict = ( + return_dict if return_dict is not None else self.config.return_dict + ) + + if pixel_values is None: + raise ValueError("You have to specify pixel_values") + num_channels = pixel_values.shape[1] + if num_channels == 1: + pixel_values = paddle.repeat_interleave(pixel_values, repeats=3, axis=1) + + head_mask = self.get_head_mask(head_mask, len(self.config.depths)) + + embedding_output, input_dimensions = self.embeddings( + pixel_values, bool_masked_pos=bool_masked_pos + ) + + encoder_outputs = self.encoder( + embedding_output, + input_dimensions, + head_mask=head_mask, + output_attentions=output_attentions, + output_hidden_states=output_hidden_states, + return_dict=return_dict, + ) + sequence_output = encoder_outputs[0] + + pooled_output = None + if self.pooler is not None: + pooled_output = self.pooler(sequence_output.transpose([0, 2, 1])) + pooled_output = paddle.flatten(pooled_output, 1) + + if not return_dict: + output = (sequence_output, pooled_output) + encoder_outputs[1:] + return output + + donut_swin_output = DonutSwinModelOutput( + last_hidden_state=sequence_output, + pooler_output=pooled_output, + hidden_states=encoder_outputs.hidden_states, + attentions=encoder_outputs.attentions, + reshaped_hidden_states=encoder_outputs.reshaped_hidden_states, + ) + if self.training: + return donut_swin_output, label, attention_mask + else: + return donut_swin_output diff --git a/modules/onnx_ocr_module/src/ppocr/modeling/backbones/rec_efficientb3_pren.py b/modules/onnx_ocr_module/src/ppocr/modeling/backbones/rec_efficientb3_pren.py new file mode 100644 index 0000000..5334cc3 --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/modeling/backbones/rec_efficientb3_pren.py @@ -0,0 +1,305 @@ +# copyright (c) 2022 PaddlePaddle Authors. All Rights Reserve. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +""" +Code is refer from: +https://github.com/RuijieJ/pren/blob/main/Nets/EfficientNet.py +""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import math +import re +import collections +import paddle +import paddle.nn as nn +import paddle.nn.functional as F + +__all__ = ["EfficientNetb3_PREN"] + +GlobalParams = collections.namedtuple( + "GlobalParams", + [ + "batch_norm_momentum", + "batch_norm_epsilon", + "dropout_rate", + "num_classes", + "width_coefficient", + "depth_coefficient", + "depth_divisor", + "min_depth", + "drop_connect_rate", + "image_size", + ], +) + +BlockArgs = collections.namedtuple( + "BlockArgs", + [ + "kernel_size", + "num_repeat", + "input_filters", + "output_filters", + "expand_ratio", + "id_skip", + "stride", + "se_ratio", + ], +) + + +class BlockDecoder: + @staticmethod + def _decode_block_string(block_string): + assert isinstance(block_string, str) + + ops = block_string.split("_") + options = {} + for op in ops: + splits = re.split(r"(\d.*)", op) + if len(splits) >= 2: + key, value = splits[:2] + options[key] = value + + assert ("s" in options and len(options["s"]) == 1) or ( + len(options["s"]) == 2 and options["s"][0] == options["s"][1] + ) + + return BlockArgs( + kernel_size=int(options["k"]), + num_repeat=int(options["r"]), + input_filters=int(options["i"]), + output_filters=int(options["o"]), + expand_ratio=int(options["e"]), + id_skip=("noskip" not in block_string), + se_ratio=float(options["se"]) if "se" in options else None, + stride=[int(options["s"][0])], + ) + + @staticmethod + def decode(string_list): + assert isinstance(string_list, list) + blocks_args = [] + for block_string in string_list: + blocks_args.append(BlockDecoder._decode_block_string(block_string)) + return blocks_args + + +def efficientnet( + width_coefficient=None, + depth_coefficient=None, + dropout_rate=0.2, + drop_connect_rate=0.2, + image_size=None, + num_classes=1000, +): + blocks_args = [ + "r1_k3_s11_e1_i32_o16_se0.25", + "r2_k3_s22_e6_i16_o24_se0.25", + "r2_k5_s22_e6_i24_o40_se0.25", + "r3_k3_s22_e6_i40_o80_se0.25", + "r3_k5_s11_e6_i80_o112_se0.25", + "r4_k5_s22_e6_i112_o192_se0.25", + "r1_k3_s11_e6_i192_o320_se0.25", + ] + blocks_args = BlockDecoder.decode(blocks_args) + + global_params = GlobalParams( + batch_norm_momentum=0.99, + batch_norm_epsilon=1e-3, + dropout_rate=dropout_rate, + drop_connect_rate=drop_connect_rate, + num_classes=num_classes, + width_coefficient=width_coefficient, + depth_coefficient=depth_coefficient, + depth_divisor=8, + min_depth=None, + image_size=image_size, + ) + return blocks_args, global_params + + +class EffUtils: + @staticmethod + def round_filters(filters, global_params): + """Calculate and round number of filters based on depth multiplier.""" + multiplier = global_params.width_coefficient + if not multiplier: + return filters + divisor = global_params.depth_divisor + min_depth = global_params.min_depth + filters *= multiplier + min_depth = min_depth or divisor + new_filters = max(min_depth, int(filters + divisor / 2) // divisor * divisor) + if new_filters < 0.9 * filters: + new_filters += divisor + return int(new_filters) + + @staticmethod + def round_repeats(repeats, global_params): + """Round number of filters based on depth multiplier.""" + multiplier = global_params.depth_coefficient + if not multiplier: + return repeats + return int(math.ceil(multiplier * repeats)) + + +class MbConvBlock(nn.Layer): + def __init__(self, block_args): + super(MbConvBlock, self).__init__() + self._block_args = block_args + self.has_se = (self._block_args.se_ratio is not None) and ( + 0 < self._block_args.se_ratio <= 1 + ) + self.id_skip = block_args.id_skip + + # expansion phase + self.inp = self._block_args.input_filters + oup = self._block_args.input_filters * self._block_args.expand_ratio + if self._block_args.expand_ratio != 1: + self._expand_conv = nn.Conv2D(self.inp, oup, 1, bias_attr=False) + self._bn0 = nn.BatchNorm(oup) + + # depthwise conv phase + k = self._block_args.kernel_size + s = self._block_args.stride + if isinstance(s, list): + s = s[0] + self._depthwise_conv = nn.Conv2D( + oup, + oup, + groups=oup, + kernel_size=k, + stride=s, + padding="same", + bias_attr=False, + ) + self._bn1 = nn.BatchNorm(oup) + + # squeeze and excitation layer, if desired + if self.has_se: + num_squeezed_channels = max( + 1, int(self._block_args.input_filters * self._block_args.se_ratio) + ) + self._se_reduce = nn.Conv2D(oup, num_squeezed_channels, 1) + self._se_expand = nn.Conv2D(num_squeezed_channels, oup, 1) + + # output phase and some util class + self.final_oup = self._block_args.output_filters + self._project_conv = nn.Conv2D(oup, self.final_oup, 1, bias_attr=False) + self._bn2 = nn.BatchNorm(self.final_oup) + self._swish = nn.Swish() + + def _drop_connect(self, inputs, p, training): + if not training: + return inputs + batch_size = inputs.shape[0] + keep_prob = 1 - p + random_tensor = keep_prob + random_tensor += paddle.rand([batch_size, 1, 1, 1], dtype=inputs.dtype) + random_tensor = paddle.to_tensor(random_tensor, place=inputs.place) + binary_tensor = paddle.floor(random_tensor) + output = inputs / keep_prob * binary_tensor + return output + + def forward(self, inputs, drop_connect_rate=None): + # expansion and depthwise conv + x = inputs + if self._block_args.expand_ratio != 1: + x = self._swish(self._bn0(self._expand_conv(inputs))) + x = self._swish(self._bn1(self._depthwise_conv(x))) + + # squeeze and excitation + if self.has_se: + x_squeezed = F.adaptive_avg_pool2d(x, 1) + x_squeezed = self._se_expand(self._swish(self._se_reduce(x_squeezed))) + x = F.sigmoid(x_squeezed) * x + x = self._bn2(self._project_conv(x)) + + # skip connection and drop connect + if self.id_skip and self._block_args.stride == 1 and self.inp == self.final_oup: + if drop_connect_rate: + x = self._drop_connect(x, p=drop_connect_rate, training=self.training) + x = x + inputs + return x + + +class EfficientNetb3_PREN(nn.Layer): + def __init__(self, in_channels): + super(EfficientNetb3_PREN, self).__init__() + """ + the fllowing are efficientnetb3's superparams, + they means efficientnetb3 network's width, depth, resolution and + dropout respectively, to fit for text recognition task, the resolution + here is changed from 300 to 64. + """ + w, d, s, p = 1.2, 1.4, 64, 0.3 + self._blocks_args, self._global_params = efficientnet( + width_coefficient=w, depth_coefficient=d, dropout_rate=p, image_size=s + ) + self.out_channels = [] + # stem + out_channels = EffUtils.round_filters(32, self._global_params) + self._conv_stem = nn.Conv2D( + in_channels, out_channels, 3, 2, padding="same", bias_attr=False + ) + self._bn0 = nn.BatchNorm(out_channels) + + # build blocks + self._blocks = [] + # to extract three feature maps for fpn based on efficientnetb3 backbone + self._concerned_block_idxes = [7, 17, 25] + _concerned_idx = 0 + for i, block_args in enumerate(self._blocks_args): + block_args = block_args._replace( + input_filters=EffUtils.round_filters( + block_args.input_filters, self._global_params + ), + output_filters=EffUtils.round_filters( + block_args.output_filters, self._global_params + ), + num_repeat=EffUtils.round_repeats( + block_args.num_repeat, self._global_params + ), + ) + self._blocks.append(self.add_sublayer(f"{i}-0", MbConvBlock(block_args))) + _concerned_idx += 1 + if _concerned_idx in self._concerned_block_idxes: + self.out_channels.append(block_args.output_filters) + if block_args.num_repeat > 1: + block_args = block_args._replace( + input_filters=block_args.output_filters, stride=1 + ) + for j in range(block_args.num_repeat - 1): + self._blocks.append( + self.add_sublayer(f"{i}-{j+1}", MbConvBlock(block_args)) + ) + _concerned_idx += 1 + if _concerned_idx in self._concerned_block_idxes: + self.out_channels.append(block_args.output_filters) + + self._swish = nn.Swish() + + def forward(self, inputs): + outs = [] + x = self._swish(self._bn0(self._conv_stem(inputs))) + for idx, block in enumerate(self._blocks): + drop_connect_rate = self._global_params.drop_connect_rate + if drop_connect_rate: + drop_connect_rate *= float(idx) / len(self._blocks) + x = block(x, drop_connect_rate=drop_connect_rate) + if idx in self._concerned_block_idxes: + outs.append(x) + return outs diff --git a/modules/onnx_ocr_module/src/ppocr/modeling/backbones/rec_hgnet.py b/modules/onnx_ocr_module/src/ppocr/modeling/backbones/rec_hgnet.py new file mode 100644 index 0000000..d33e1dc --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/modeling/backbones/rec_hgnet.py @@ -0,0 +1,385 @@ +# copyright (c) 2022 PaddlePaddle Authors. All Rights Reserve. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import paddle +import paddle.nn as nn +import paddle.nn.functional as F +from paddle.nn.initializer import KaimingNormal, Constant +from paddle.nn import Conv2D, BatchNorm2D, ReLU, AdaptiveAvgPool2D, MaxPool2D +from paddle.regularizer import L2Decay +from paddle import ParamAttr + +kaiming_normal_ = KaimingNormal() +zeros_ = Constant(value=0.0) +ones_ = Constant(value=1.0) + + +class MeanPool2D(nn.Layer): + def __init__(self, w, h): + super().__init__() + self.w = w + self.h = h + + def forward(self, feat): + batch_size, channels, _, _ = feat.shape + feat_flat = paddle.reshape(feat, [batch_size, channels, -1]) + feat_mean = paddle.mean(feat_flat, axis=2) + feat_mean = paddle.reshape(feat_mean, [batch_size, channels, self.w, self.h]) + return feat_mean + + +class ConvBNAct(nn.Layer): + def __init__( + self, in_channels, out_channels, kernel_size, stride, groups=1, use_act=True + ): + super().__init__() + self.use_act = use_act + self.conv = Conv2D( + in_channels, + out_channels, + kernel_size, + stride, + padding=(kernel_size - 1) // 2, + groups=groups, + bias_attr=False, + ) + self.bn = BatchNorm2D( + out_channels, + weight_attr=ParamAttr(regularizer=L2Decay(0.0)), + bias_attr=ParamAttr(regularizer=L2Decay(0.0)), + ) + if self.use_act: + self.act = ReLU() + + def forward(self, x): + x = self.conv(x) + x = self.bn(x) + if self.use_act: + x = self.act(x) + return x + + +class ESEModule(nn.Layer): + def __init__(self, channels): + super().__init__() + if "npu" in paddle.device.get_device(): + self.avg_pool = MeanPool2D(1, 1) + else: + self.avg_pool = AdaptiveAvgPool2D(1) + self.conv = Conv2D( + in_channels=channels, + out_channels=channels, + kernel_size=1, + stride=1, + padding=0, + ) + self.sigmoid = nn.Sigmoid() + + def forward(self, x): + identity = x + x = self.avg_pool(x) + x = self.conv(x) + x = self.sigmoid(x) + return paddle.multiply(x=identity, y=x) + + +class HG_Block(nn.Layer): + def __init__( + self, + in_channels, + mid_channels, + out_channels, + layer_num, + identity=False, + ): + super().__init__() + self.identity = identity + + self.layers = nn.LayerList() + self.layers.append( + ConvBNAct( + in_channels=in_channels, + out_channels=mid_channels, + kernel_size=3, + stride=1, + ) + ) + for _ in range(layer_num - 1): + self.layers.append( + ConvBNAct( + in_channels=mid_channels, + out_channels=mid_channels, + kernel_size=3, + stride=1, + ) + ) + + # feature aggregation + total_channels = in_channels + layer_num * mid_channels + self.aggregation_conv = ConvBNAct( + in_channels=total_channels, + out_channels=out_channels, + kernel_size=1, + stride=1, + ) + self.att = ESEModule(out_channels) + + def forward(self, x): + identity = x + output = [] + output.append(x) + for layer in self.layers: + x = layer(x) + output.append(x) + x = paddle.concat(output, axis=1) + x = self.aggregation_conv(x) + x = self.att(x) + if self.identity: + x += identity + return x + + +class HG_Stage(nn.Layer): + def __init__( + self, + in_channels, + mid_channels, + out_channels, + block_num, + layer_num, + downsample=True, + stride=[2, 1], + ): + super().__init__() + self.downsample = downsample + if downsample: + self.downsample = ConvBNAct( + in_channels=in_channels, + out_channels=in_channels, + kernel_size=3, + stride=stride, + groups=in_channels, + use_act=False, + ) + + blocks_list = [] + blocks_list.append( + HG_Block(in_channels, mid_channels, out_channels, layer_num, identity=False) + ) + for _ in range(block_num - 1): + blocks_list.append( + HG_Block( + out_channels, mid_channels, out_channels, layer_num, identity=True + ) + ) + self.blocks = nn.Sequential(*blocks_list) + + def forward(self, x): + if self.downsample: + x = self.downsample(x) + x = self.blocks(x) + return x + + +class PPHGNet(nn.Layer): + """ + PPHGNet + Args: + stem_channels: list. Stem channel list of PPHGNet. + stage_config: dict. The configuration of each stage of PPHGNet. such as the number of channels, stride, etc. + layer_num: int. Number of layers of HG_Block. + use_last_conv: boolean. Whether to use a 1x1 convolutional layer before the classification layer. + class_expand: int=2048. Number of channels for the last 1x1 convolutional layer. + dropout_prob: float. Parameters of dropout, 0.0 means dropout is not used. + class_num: int=1000. The number of classes. + Returns: + model: nn.Layer. Specific PPHGNet model depends on args. + """ + + def __init__( + self, + stem_channels, + stage_config, + layer_num, + in_channels=3, + det=False, + out_indices=None, + ): + super().__init__() + self.det = det + self.out_indices = out_indices if out_indices is not None else [0, 1, 2, 3] + + # stem + stem_channels.insert(0, in_channels) + self.stem = nn.Sequential( + *[ + ConvBNAct( + in_channels=stem_channels[i], + out_channels=stem_channels[i + 1], + kernel_size=3, + stride=2 if i == 0 else 1, + ) + for i in range(len(stem_channels) - 1) + ] + ) + + if self.det: + self.pool = nn.MaxPool2D(kernel_size=3, stride=2, padding=1) + # stages + self.stages = nn.LayerList() + self.out_channels = [] + for block_id, k in enumerate(stage_config): + ( + in_channels, + mid_channels, + out_channels, + block_num, + downsample, + stride, + ) = stage_config[k] + self.stages.append( + HG_Stage( + in_channels, + mid_channels, + out_channels, + block_num, + layer_num, + downsample, + stride, + ) + ) + if block_id in self.out_indices: + self.out_channels.append(out_channels) + + if not self.det: + self.out_channels = stage_config["stage4"][2] + + self._init_weights() + + def _init_weights(self): + for m in self.sublayers(): + if isinstance(m, nn.Conv2D): + kaiming_normal_(m.weight) + elif isinstance(m, (nn.BatchNorm2D)): + ones_(m.weight) + zeros_(m.bias) + elif isinstance(m, nn.Linear): + zeros_(m.bias) + + def forward(self, x): + x = self.stem(x) + if self.det: + x = self.pool(x) + + out = [] + for i, stage in enumerate(self.stages): + x = stage(x) + if self.det and i in self.out_indices: + out.append(x) + if self.det: + return out + + if self.training: + x = F.adaptive_avg_pool2d(x, [1, 40]) + else: + x = F.avg_pool2d(x, [3, 2]) + return x + + +def PPHGNet_tiny(pretrained=False, use_ssld=False, **kwargs): + """ + PPHGNet_tiny + Args: + pretrained: bool=False or str. If `True` load pretrained parameters, `False` otherwise. + If str, means the path of the pretrained model. + use_ssld: bool=False. Whether using distillation pretrained model when pretrained=True. + Returns: + model: nn.Layer. Specific `PPHGNet_tiny` model depends on args. + """ + stage_config = { + # in_channels, mid_channels, out_channels, blocks, downsample + "stage1": [96, 96, 224, 1, False, [2, 1]], + "stage2": [224, 128, 448, 1, True, [1, 2]], + "stage3": [448, 160, 512, 2, True, [2, 1]], + "stage4": [512, 192, 768, 1, True, [2, 1]], + } + + model = PPHGNet( + stem_channels=[48, 48, 96], stage_config=stage_config, layer_num=5, **kwargs + ) + return model + + +def PPHGNet_small(pretrained=False, use_ssld=False, det=False, **kwargs): + """ + PPHGNet_small + Args: + pretrained: bool=False or str. If `True` load pretrained parameters, `False` otherwise. + If str, means the path of the pretrained model. + use_ssld: bool=False. Whether using distillation pretrained model when pretrained=True. + Returns: + model: nn.Layer. Specific `PPHGNet_small` model depends on args. + """ + stage_config_det = { + # in_channels, mid_channels, out_channels, blocks, downsample + "stage1": [128, 128, 256, 1, False, 2], + "stage2": [256, 160, 512, 1, True, 2], + "stage3": [512, 192, 768, 2, True, 2], + "stage4": [768, 224, 1024, 1, True, 2], + } + + stage_config_rec = { + # in_channels, mid_channels, out_channels, blocks, downsample + "stage1": [128, 128, 256, 1, True, [2, 1]], + "stage2": [256, 160, 512, 1, True, [1, 2]], + "stage3": [512, 192, 768, 2, True, [2, 1]], + "stage4": [768, 224, 1024, 1, True, [2, 1]], + } + + model = PPHGNet( + stem_channels=[64, 64, 128], + stage_config=stage_config_det if det else stage_config_rec, + layer_num=6, + det=det, + **kwargs, + ) + return model + + +def PPHGNet_base(pretrained=False, use_ssld=True, **kwargs): + """ + PPHGNet_base + Args: + pretrained: bool=False or str. If `True` load pretrained parameters, `False` otherwise. + If str, means the path of the pretrained model. + use_ssld: bool=False. Whether using distillation pretrained model when pretrained=True. + Returns: + model: nn.Layer. Specific `PPHGNet_base` model depends on args. + """ + stage_config = { + # in_channels, mid_channels, out_channels, blocks, downsample + "stage1": [160, 192, 320, 1, False, [2, 1]], + "stage2": [320, 224, 640, 2, True, [1, 2]], + "stage3": [640, 256, 960, 3, True, [2, 1]], + "stage4": [960, 288, 1280, 2, True, [2, 1]], + } + + model = PPHGNet( + stem_channels=[96, 96, 160], + stage_config=stage_config, + layer_num=7, + dropout_prob=0.2, + **kwargs, + ) + return model diff --git a/modules/onnx_ocr_module/src/ppocr/modeling/backbones/rec_hybridvit.py b/modules/onnx_ocr_module/src/ppocr/modeling/backbones/rec_hybridvit.py new file mode 100644 index 0000000..e873a78 --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/modeling/backbones/rec_hybridvit.py @@ -0,0 +1,529 @@ +# copyright (c) 2024 PaddlePaddle Authors. All Rights Reserve. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +""" +This code is refer from: +https://github.com/huggingface/pytorch-image-models/blob/main/timm/models/vision_transformer_hybrid.py +""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +from itertools import repeat +import collections +import math +from functools import partial + +import paddle +import paddle.nn as nn +import paddle.nn.functional as F +from ppocr.modeling.backbones.rec_resnetv2 import ( + ResNetV2, + StdConv2dSame, + DropPath, + get_padding, +) +from paddle.nn.initializer import ( + TruncatedNormal, + Constant, + Normal, + KaimingUniform, + XavierUniform, +) + +normal_ = Normal(mean=0.0, std=1e-6) +zeros_ = Constant(value=0.0) +ones_ = Constant(value=1.0) +kaiming_normal_ = KaimingUniform(nonlinearity="relu") +trunc_normal_ = TruncatedNormal(std=0.02) +xavier_uniform_ = XavierUniform() + + +def _ntuple(n): + def parse(x): + if isinstance(x, collections.abc.Iterable): + return x + return tuple(repeat(x, n)) + + return parse + + +to_1tuple = _ntuple(1) +to_2tuple = _ntuple(2) +to_3tuple = _ntuple(3) +to_4tuple = _ntuple(4) +to_ntuple = _ntuple + + +class Conv2dAlign(nn.Conv2D): + """Conv2d with Weight Standardization. Used for BiT ResNet-V2 models. + + Paper: `Micro-Batch Training with Batch-Channel Normalization and Weight Standardization` - + https://arxiv.org/abs/1903.10520v2 + """ + + def __init__( + self, + in_channel, + out_channels, + kernel_size, + stride=1, + padding=0, + dilation=1, + groups=1, + bias=True, + eps=1e-6, + ): + + super().__init__( + in_channel, + out_channels, + kernel_size, + stride=stride, + padding=padding, + dilation=dilation, + groups=groups, + bias_attr=bias, + weight_attr=True, + ) + self.eps = eps + + def forward(self, x): + x = F.conv2d( + x, + self.weight, + self.bias, + self._stride, + self._padding, + self._dilation, + self._groups, + ) + return x + + +class HybridEmbed(nn.Layer): + """CNN Feature Map Embedding + Extract feature map from CNN, flatten, project to embedding dim. + """ + + def __init__( + self, + backbone, + img_size=224, + patch_size=1, + feature_size=None, + in_chans=3, + embed_dim=768, + ): + super().__init__() + assert isinstance(backbone, nn.Layer) + img_size = to_2tuple(img_size) + patch_size = to_2tuple(patch_size) + self.img_size = img_size + self.patch_size = patch_size + self.backbone = backbone + feature_dim = 1024 + feature_size = (42, 12) + patch_size = (1, 1) + assert ( + feature_size[0] % patch_size[0] == 0 + and feature_size[1] % patch_size[1] == 0 + ) + + self.grid_size = ( + feature_size[0] // patch_size[0], + feature_size[1] // patch_size[1], + ) + self.num_patches = self.grid_size[0] * self.grid_size[1] + self.proj = nn.Conv2D( + feature_dim, + embed_dim, + kernel_size=patch_size, + stride=patch_size, + weight_attr=True, + bias_attr=True, + ) + + def forward(self, x): + + x = self.backbone(x) + if isinstance(x, (list, tuple)): + x = x[-1] # last feature if backbone outputs list/tuple of features + x = self.proj(x).flatten(2).transpose([0, 2, 1]) + + return x + + +class myLinear(nn.Linear): + def __init__(self, in_channel, out_channels, weight_attr=True, bias_attr=True): + super().__init__( + in_channel, out_channels, weight_attr=weight_attr, bias_attr=bias_attr + ) + + def forward(self, x): + return paddle.matmul(x, self.weight, transpose_y=True) + self.bias + + +class Attention(nn.Layer): + def __init__(self, dim, num_heads=8, qkv_bias=False, attn_drop=0.0, proj_drop=0.0): + super().__init__() + self.num_heads = num_heads + head_dim = dim // num_heads + self.scale = head_dim**-0.5 + + self.qkv = nn.Linear(dim, dim * 3, bias_attr=qkv_bias) + self.attn_drop = nn.Dropout(attn_drop) + self.proj = myLinear(dim, dim, weight_attr=True, bias_attr=True) + self.proj_drop = nn.Dropout(proj_drop) + + def forward(self, x): + B, N, C = x.shape + qkv = ( + self.qkv(x) + .reshape([B, N, 3, self.num_heads, C // self.num_heads]) + .transpose([2, 0, 3, 1, 4]) + ) + q, k, v = qkv.unbind(0) # make torchscript happy (cannot use tensor as tuple) + + attn = (q @ k.transpose([0, 1, 3, 2])) * self.scale + + attn = F.softmax(attn, axis=-1) + attn = self.attn_drop(attn) + + x = (attn @ v).transpose([0, 2, 1, 3]).reshape([B, N, C]) + + x = self.proj(x) + x = self.proj_drop(x) + return x + + +class Mlp(nn.Layer): + """MLP as used in Vision Transformer, MLP-Mixer and related networks""" + + def __init__( + self, + in_features, + hidden_features=None, + out_features=None, + act_layer=nn.GELU, + drop=0.0, + ): + super().__init__() + out_features = out_features or in_features + hidden_features = hidden_features or in_features + drop_probs = to_2tuple(drop) + + self.fc1 = nn.Linear(in_features, hidden_features) + self.act = act_layer() + self.drop1 = nn.Dropout(drop_probs[0]) + self.fc2 = nn.Linear(hidden_features, out_features) + self.drop2 = nn.Dropout(drop_probs[1]) + + def forward(self, x): + x = self.fc1(x) + x = self.act(x) + x = self.drop1(x) + x = self.fc2(x) + x = self.drop2(x) + return x + + +class Block(nn.Layer): + + def __init__( + self, + dim, + num_heads, + mlp_ratio=4.0, + qkv_bias=False, + drop=0.0, + attn_drop=0.0, + drop_path=0.0, + act_layer=nn.GELU, + norm_layer=nn.LayerNorm, + ): + super().__init__() + self.norm1 = norm_layer(dim) + self.attn = Attention( + dim, + num_heads=num_heads, + qkv_bias=qkv_bias, + attn_drop=attn_drop, + proj_drop=drop, + ) + # NOTE: drop path for stochastic depth, we shall see if this is better than dropout here + self.drop_path = DropPath(drop_path) if drop_path > 0.0 else nn.Identity() + self.norm2 = norm_layer(dim) + mlp_hidden_dim = int(dim * mlp_ratio) + self.mlp = Mlp( + in_features=dim, + hidden_features=mlp_hidden_dim, + act_layer=act_layer, + drop=drop, + ) + + def forward(self, x): + + x = x + self.drop_path(self.attn(self.norm1(x))) + x = x + self.drop_path(self.mlp(self.norm2(x))) + return x + + +class HybridTransformer(nn.Layer): + """Implementation of HybridTransformer. + + Args: + x: input images with shape [N, 1, H, W] + label: LaTeX-OCR labels with shape [N, L] , L is the max sequence length + attention_mask: LaTeX-OCR attention mask with shape [N, L] , L is the max sequence length + + Returns: + The encoded features with shape [N, 1, H//16, W//16] + """ + + def __init__( + self, + backbone_layers=[2, 3, 7], + input_channel=1, + is_predict=False, + is_export=False, + img_size=(224, 224), + patch_size=16, + num_classes=1000, + embed_dim=768, + depth=12, + num_heads=12, + mlp_ratio=4.0, + qkv_bias=True, + representation_size=None, + distilled=False, + drop_rate=0.0, + attn_drop_rate=0.0, + drop_path_rate=0.0, + embed_layer=None, + norm_layer=None, + act_layer=None, + weight_init="", + **kwargs, + ): + super(HybridTransformer, self).__init__() + self.num_classes = num_classes + self.num_features = self.embed_dim = ( + embed_dim # num_features for consistency with other models + ) + self.num_tokens = 2 if distilled else 1 + norm_layer = norm_layer or partial(nn.LayerNorm, epsilon=1e-6) + act_layer = act_layer or nn.GELU + self.height, self.width = img_size + self.patch_size = patch_size + backbone = ResNetV2( + layers=backbone_layers, + num_classes=0, + global_pool="", + in_chans=input_channel, + preact=False, + stem_type="same", + conv_layer=StdConv2dSame, + is_export=is_export, + ) + min_patch_size = 2 ** (len(backbone_layers) + 1) + self.patch_embed = HybridEmbed( + img_size=img_size, + patch_size=patch_size // min_patch_size, + in_chans=input_channel, + embed_dim=embed_dim, + backbone=backbone, + ) + num_patches = self.patch_embed.num_patches + + self.cls_token = paddle.create_parameter([1, 1, embed_dim], dtype="float32") + self.dist_token = ( + paddle.create_parameter( + [1, 1, embed_dim], + dtype="float32", + ) + if distilled + else None + ) + self.pos_embed = paddle.create_parameter( + [1, num_patches + self.num_tokens, embed_dim], dtype="float32" + ) + self.pos_drop = nn.Dropout(p=drop_rate) + zeros_(self.cls_token) + if self.dist_token is not None: + zeros_(self.dist_token) + zeros_(self.pos_embed) + + dpr = [ + x.item() for x in paddle.linspace(0, drop_path_rate, depth) + ] # stochastic depth decay rule + self.blocks = nn.Sequential( + *[ + Block( + dim=embed_dim, + num_heads=num_heads, + mlp_ratio=mlp_ratio, + qkv_bias=qkv_bias, + drop=drop_rate, + attn_drop=attn_drop_rate, + drop_path=dpr[i], + norm_layer=norm_layer, + act_layer=act_layer, + ) + for i in range(depth) + ] + ) + self.norm = norm_layer(embed_dim) + + # Representation layer + if representation_size and not distilled: + self.num_features = representation_size + self.pre_logits = nn.Sequential( + ("fc", nn.Linear(embed_dim, representation_size)), ("act", nn.Tanh()) + ) + else: + self.pre_logits = nn.Identity() + + # Classifier head(s) + self.head = ( + nn.Linear(self.num_features, num_classes) + if num_classes > 0 + else nn.Identity() + ) + self.head_dist = None + if distilled: + self.head_dist = ( + nn.Linear(self.embed_dim, self.num_classes) + if num_classes > 0 + else nn.Identity() + ) + self.init_weights(weight_init) + self.out_channels = embed_dim + self.is_predict = is_predict + self.is_export = is_export + + def init_weights(self, mode=""): + assert mode in ("jax", "jax_nlhb", "nlhb", "") + head_bias = -math.log(self.num_classes) if "nlhb" in mode else 0.0 + trunc_normal_(self.pos_embed) + trunc_normal_(self.cls_token) + self.apply(_init_vit_weights) + + def _init_weights(self, m): + # this fn left here for compat with downstream users + _init_vit_weights(m) + + def load_pretrained(self, checkpoint_path, prefix=""): + raise NotImplementedError + + def no_weight_decay(self): + return {"pos_embed", "cls_token", "dist_token"} + + def get_classifier(self): + if self.dist_token is None: + return self.head + else: + return self.head, self.head_dist + + def reset_classifier(self, num_classes, global_pool=""): + self.num_classes = num_classes + self.head = ( + nn.Linear(self.embed_dim, num_classes) if num_classes > 0 else nn.Identity() + ) + if self.num_tokens == 2: + self.head_dist = ( + nn.Linear(self.embed_dim, self.num_classes) + if num_classes > 0 + else nn.Identity() + ) + + def forward_features(self, x): + B, c, h, w = x.shape + x = self.patch_embed(x) + cls_tokens = self.cls_token.expand( + [B, -1, -1] + ) # stole cls_tokens impl from Phil Wang, thanks + x = paddle.concat((cls_tokens, x), axis=1) + h, w = h // self.patch_size, w // self.patch_size + repeat_tensor = ( + paddle.arange(h) * (self.width // self.patch_size - w) + ).reshape([-1, 1]) + repeat_tensor = paddle.repeat_interleave( + repeat_tensor, paddle.to_tensor(w), axis=1 + ).reshape([-1]) + pos_emb_ind = repeat_tensor + paddle.arange(h * w) + pos_emb_ind = paddle.concat( + (paddle.zeros([1], dtype="int64"), pos_emb_ind + 1), axis=0 + ).cast(paddle.int64) + x += self.pos_embed[:, pos_emb_ind] + x = self.pos_drop(x) + + for blk in self.blocks: + x = blk(x) + + x = self.norm(x) + return x + + def forward(self, input_data): + + if self.training: + x, label, attention_mask = input_data + else: + if isinstance(input_data, list): + x = input_data[0] + else: + x = input_data + x = self.forward_features(x) + x = self.head(x) + if self.training: + return x, label, attention_mask + else: + return x + + +def _init_vit_weights( + module: nn.Layer, name: str = "", head_bias: float = 0.0, jax_impl: bool = False +): + """ViT weight initialization + * When called without n, head_bias, jax_impl args it will behave exactly the same + as my original init for compatibility with prev hparam / downstream use cases (ie DeiT). + * When called w/ valid n (module name) and jax_impl=True, will (hopefully) match JAX impl + """ + if isinstance(module, nn.Linear): + if name.startswith("head"): + zeros_(module.weight) + constant_ = Constant(value=head_bias) + constant_(module.bias, head_bias) + elif name.startswith("pre_logits"): + zeros_(module.bias) + else: + if jax_impl: + xavier_uniform_(module.weight) + if module.bias is not None: + if "mlp" in name: + normal_(module.bias) + else: + zeros_(module.bias) + else: + trunc_normal_(module.weight) + if module.bias is not None: + zeros_(module.bias) + elif jax_impl and isinstance(module, nn.Conv2D): + # NOTE conv was left to pytorch default in my original init + if module.bias is not None: + zeros_(module.bias) + elif isinstance(module, (nn.LayerNorm, nn.GroupNorm, nn.BatchNorm2D)): + zeros_(module.bias) + ones_(module.weight) diff --git a/modules/onnx_ocr_module/src/ppocr/modeling/backbones/rec_lcnetv3.py b/modules/onnx_ocr_module/src/ppocr/modeling/backbones/rec_lcnetv3.py new file mode 100644 index 0000000..e21dd4a --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/modeling/backbones/rec_lcnetv3.py @@ -0,0 +1,558 @@ +# copyright (c) 2021 PaddlePaddle Authors. All Rights Reserve. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import paddle +import paddle.nn as nn +import paddle.nn.functional as F +from paddle import ParamAttr +from paddle.nn.initializer import Constant, KaimingNormal +from paddle.nn import ( + AdaptiveAvgPool2D, + BatchNorm2D, + Conv2D, + Dropout, + Hardsigmoid, + Hardswish, + Identity, + Linear, + ReLU, +) +from paddle.regularizer import L2Decay +from ppocr.modeling.backbones.rec_hgnet import MeanPool2D + +NET_CONFIG_det = { + "blocks2": + # k, in_c, out_c, s, use_se + [[3, 16, 32, 1, False]], + "blocks3": [[3, 32, 64, 2, False], [3, 64, 64, 1, False]], + "blocks4": [[3, 64, 128, 2, False], [3, 128, 128, 1, False]], + "blocks5": [ + [3, 128, 256, 2, False], + [5, 256, 256, 1, False], + [5, 256, 256, 1, False], + [5, 256, 256, 1, False], + [5, 256, 256, 1, False], + ], + "blocks6": [ + [5, 256, 512, 2, True], + [5, 512, 512, 1, True], + [5, 512, 512, 1, False], + [5, 512, 512, 1, False], + ], +} + +NET_CONFIG_rec = { + "blocks2": + # k, in_c, out_c, s, use_se + [[3, 16, 32, 1, False]], + "blocks3": [[3, 32, 64, 1, False], [3, 64, 64, 1, False]], + "blocks4": [[3, 64, 128, (2, 1), False], [3, 128, 128, 1, False]], + "blocks5": [ + [3, 128, 256, (1, 2), False], + [5, 256, 256, 1, False], + [5, 256, 256, 1, False], + [5, 256, 256, 1, False], + [5, 256, 256, 1, False], + ], + "blocks6": [ + [5, 256, 512, (2, 1), True], + [5, 512, 512, 1, True], + [5, 512, 512, (2, 1), False], + [5, 512, 512, 1, False], + ], +} + + +def make_divisible(v, divisor=16, min_value=None): + if min_value is None: + min_value = divisor + new_v = max(min_value, int(v + divisor / 2) // divisor * divisor) + if new_v < 0.9 * v: + new_v += divisor + return new_v + + +class LearnableAffineBlock(nn.Layer): + def __init__(self, scale_value=1.0, bias_value=0.0, lr_mult=1.0, lab_lr=0.1): + super().__init__() + self.scale = self.create_parameter( + shape=[ + 1, + ], + default_initializer=Constant(value=scale_value), + attr=ParamAttr(learning_rate=lr_mult * lab_lr), + ) + self.add_parameter("scale", self.scale) + self.bias = self.create_parameter( + shape=[ + 1, + ], + default_initializer=Constant(value=bias_value), + attr=ParamAttr(learning_rate=lr_mult * lab_lr), + ) + self.add_parameter("bias", self.bias) + + def forward(self, x): + return self.scale * x + self.bias + + +class ConvBNLayer(nn.Layer): + def __init__( + self, in_channels, out_channels, kernel_size, stride, groups=1, lr_mult=1.0 + ): + super().__init__() + self.conv = Conv2D( + in_channels=in_channels, + out_channels=out_channels, + kernel_size=kernel_size, + stride=stride, + padding=(kernel_size - 1) // 2, + groups=groups, + weight_attr=ParamAttr(initializer=KaimingNormal(), learning_rate=lr_mult), + bias_attr=False, + ) + + self.bn = BatchNorm2D( + out_channels, + weight_attr=ParamAttr(regularizer=L2Decay(0.0), learning_rate=lr_mult), + bias_attr=ParamAttr(regularizer=L2Decay(0.0), learning_rate=lr_mult), + ) + + def forward(self, x): + x = self.conv(x) + x = self.bn(x) + return x + + +class Act(nn.Layer): + def __init__(self, act="hswish", lr_mult=1.0, lab_lr=0.1): + super().__init__() + if act == "hswish": + self.act = Hardswish() + else: + assert act == "relu" + self.act = ReLU() + self.lab = LearnableAffineBlock(lr_mult=lr_mult, lab_lr=lab_lr) + + def forward(self, x): + return self.lab(self.act(x)) + + +class LearnableRepLayer(nn.Layer): + def __init__( + self, + in_channels, + out_channels, + kernel_size, + stride=1, + groups=1, + num_conv_branches=1, + lr_mult=1.0, + lab_lr=0.1, + ): + super().__init__() + self.is_repped = False + self.groups = groups + self.stride = stride + self.kernel_size = kernel_size + self.in_channels = in_channels + self.out_channels = out_channels + self.num_conv_branches = num_conv_branches + self.padding = (kernel_size - 1) // 2 + + self.identity = ( + BatchNorm2D( + num_features=in_channels, + weight_attr=ParamAttr(learning_rate=lr_mult), + bias_attr=ParamAttr(learning_rate=lr_mult), + ) + if out_channels == in_channels and stride == 1 + else None + ) + + self.conv_kxk = nn.LayerList( + [ + ConvBNLayer( + in_channels, + out_channels, + kernel_size, + stride, + groups=groups, + lr_mult=lr_mult, + ) + for _ in range(self.num_conv_branches) + ] + ) + + self.conv_1x1 = ( + ConvBNLayer( + in_channels, out_channels, 1, stride, groups=groups, lr_mult=lr_mult + ) + if kernel_size > 1 + else None + ) + + self.lab = LearnableAffineBlock(lr_mult=lr_mult, lab_lr=lab_lr) + self.act = Act(lr_mult=lr_mult, lab_lr=lab_lr) + + def forward(self, x): + # for export + if self.is_repped: + out = self.lab(self.reparam_conv(x)) + if self.stride != 2: + out = self.act(out) + return out + + out = 0 + if self.identity is not None: + out += self.identity(x) + + if self.conv_1x1 is not None: + out += self.conv_1x1(x) + + for conv in self.conv_kxk: + out += conv(x) + + out = self.lab(out) + if self.stride != 2: + out = self.act(out) + return out + + def rep(self): + if self.is_repped: + return + kernel, bias = self._get_kernel_bias() + self.reparam_conv = Conv2D( + in_channels=self.in_channels, + out_channels=self.out_channels, + kernel_size=self.kernel_size, + stride=self.stride, + padding=self.padding, + groups=self.groups, + ) + self.reparam_conv.weight.set_value(kernel) + self.reparam_conv.bias.set_value(bias) + self.is_repped = True + + def _pad_kernel_1x1_to_kxk(self, kernel1x1, pad): + if not isinstance(kernel1x1, paddle.Tensor): + return 0 + else: + return nn.functional.pad(kernel1x1, [pad, pad, pad, pad]) + + def _get_kernel_bias(self): + kernel_conv_1x1, bias_conv_1x1 = self._fuse_bn_tensor(self.conv_1x1) + kernel_conv_1x1 = self._pad_kernel_1x1_to_kxk( + kernel_conv_1x1, self.kernel_size // 2 + ) + + kernel_identity, bias_identity = self._fuse_bn_tensor(self.identity) + + kernel_conv_kxk = 0 + bias_conv_kxk = 0 + for conv in self.conv_kxk: + kernel, bias = self._fuse_bn_tensor(conv) + kernel_conv_kxk += kernel + bias_conv_kxk += bias + + kernel_reparam = kernel_conv_kxk + kernel_conv_1x1 + kernel_identity + bias_reparam = bias_conv_kxk + bias_conv_1x1 + bias_identity + return kernel_reparam, bias_reparam + + def _fuse_bn_tensor(self, branch): + if not branch: + return 0, 0 + elif isinstance(branch, ConvBNLayer): + kernel = branch.conv.weight + running_mean = branch.bn._mean + running_var = branch.bn._variance + gamma = branch.bn.weight + beta = branch.bn.bias + eps = branch.bn._epsilon + else: + assert isinstance(branch, BatchNorm2D) + if not hasattr(self, "id_tensor"): + input_dim = self.in_channels // self.groups + kernel_value = paddle.zeros( + (self.in_channels, input_dim, self.kernel_size, self.kernel_size), + dtype=branch.weight.dtype, + ) + for i in range(self.in_channels): + kernel_value[ + i, i % input_dim, self.kernel_size // 2, self.kernel_size // 2 + ] = 1 + self.id_tensor = kernel_value + kernel = self.id_tensor + running_mean = branch._mean + running_var = branch._variance + gamma = branch.weight + beta = branch.bias + eps = branch._epsilon + std = (running_var + eps).sqrt() + t = (gamma / std).reshape((-1, 1, 1, 1)) + return kernel * t, beta - running_mean * gamma / std + + +class SELayer(nn.Layer): + def __init__(self, channel, reduction=4, lr_mult=1.0): + super().__init__() + if "npu" in paddle.device.get_device(): + self.avg_pool = MeanPool2D(1, 1) + else: + self.avg_pool = AdaptiveAvgPool2D(1) + self.conv1 = Conv2D( + in_channels=channel, + out_channels=channel // reduction, + kernel_size=1, + stride=1, + padding=0, + weight_attr=ParamAttr(learning_rate=lr_mult), + bias_attr=ParamAttr(learning_rate=lr_mult), + ) + self.relu = ReLU() + self.conv2 = Conv2D( + in_channels=channel // reduction, + out_channels=channel, + kernel_size=1, + stride=1, + padding=0, + weight_attr=ParamAttr(learning_rate=lr_mult), + bias_attr=ParamAttr(learning_rate=lr_mult), + ) + self.hardsigmoid = Hardsigmoid() + + def forward(self, x): + identity = x + x = self.avg_pool(x) + x = self.conv1(x) + x = self.relu(x) + x = self.conv2(x) + x = self.hardsigmoid(x) + x = paddle.multiply(x=identity, y=x) + return x + + +class LCNetV3Block(nn.Layer): + def __init__( + self, + in_channels, + out_channels, + stride, + dw_size, + use_se=False, + conv_kxk_num=4, + lr_mult=1.0, + lab_lr=0.1, + ): + super().__init__() + self.use_se = use_se + self.dw_conv = LearnableRepLayer( + in_channels=in_channels, + out_channels=in_channels, + kernel_size=dw_size, + stride=stride, + groups=in_channels, + num_conv_branches=conv_kxk_num, + lr_mult=lr_mult, + lab_lr=lab_lr, + ) + if use_se: + self.se = SELayer(in_channels, lr_mult=lr_mult) + self.pw_conv = LearnableRepLayer( + in_channels=in_channels, + out_channels=out_channels, + kernel_size=1, + stride=1, + num_conv_branches=conv_kxk_num, + lr_mult=lr_mult, + lab_lr=lab_lr, + ) + + def forward(self, x): + x = self.dw_conv(x) + if self.use_se: + x = self.se(x) + x = self.pw_conv(x) + return x + + +class PPLCNetV3(nn.Layer): + def __init__( + self, + scale=1.0, + conv_kxk_num=4, + lr_mult_list=[1.0, 1.0, 1.0, 1.0, 1.0, 1.0], + lab_lr=0.1, + det=False, + **kwargs, + ): + super().__init__() + self.scale = scale + self.lr_mult_list = lr_mult_list + self.det = det + + self.net_config = NET_CONFIG_det if self.det else NET_CONFIG_rec + + assert isinstance( + self.lr_mult_list, (list, tuple) + ), "lr_mult_list should be in (list, tuple) but got {}".format( + type(self.lr_mult_list) + ) + assert ( + len(self.lr_mult_list) == 6 + ), "lr_mult_list length should be 6 but got {}".format(len(self.lr_mult_list)) + + self.conv1 = ConvBNLayer( + in_channels=3, + out_channels=make_divisible(16 * scale), + kernel_size=3, + stride=2, + lr_mult=self.lr_mult_list[0], + ) + + self.blocks2 = nn.Sequential( + *[ + LCNetV3Block( + in_channels=make_divisible(in_c * scale), + out_channels=make_divisible(out_c * scale), + dw_size=k, + stride=s, + use_se=se, + conv_kxk_num=conv_kxk_num, + lr_mult=self.lr_mult_list[1], + lab_lr=lab_lr, + ) + for i, (k, in_c, out_c, s, se) in enumerate(self.net_config["blocks2"]) + ] + ) + + self.blocks3 = nn.Sequential( + *[ + LCNetV3Block( + in_channels=make_divisible(in_c * scale), + out_channels=make_divisible(out_c * scale), + dw_size=k, + stride=s, + use_se=se, + conv_kxk_num=conv_kxk_num, + lr_mult=self.lr_mult_list[2], + lab_lr=lab_lr, + ) + for i, (k, in_c, out_c, s, se) in enumerate(self.net_config["blocks3"]) + ] + ) + + self.blocks4 = nn.Sequential( + *[ + LCNetV3Block( + in_channels=make_divisible(in_c * scale), + out_channels=make_divisible(out_c * scale), + dw_size=k, + stride=s, + use_se=se, + conv_kxk_num=conv_kxk_num, + lr_mult=self.lr_mult_list[3], + lab_lr=lab_lr, + ) + for i, (k, in_c, out_c, s, se) in enumerate(self.net_config["blocks4"]) + ] + ) + + self.blocks5 = nn.Sequential( + *[ + LCNetV3Block( + in_channels=make_divisible(in_c * scale), + out_channels=make_divisible(out_c * scale), + dw_size=k, + stride=s, + use_se=se, + conv_kxk_num=conv_kxk_num, + lr_mult=self.lr_mult_list[4], + lab_lr=lab_lr, + ) + for i, (k, in_c, out_c, s, se) in enumerate(self.net_config["blocks5"]) + ] + ) + + self.blocks6 = nn.Sequential( + *[ + LCNetV3Block( + in_channels=make_divisible(in_c * scale), + out_channels=make_divisible(out_c * scale), + dw_size=k, + stride=s, + use_se=se, + conv_kxk_num=conv_kxk_num, + lr_mult=self.lr_mult_list[5], + lab_lr=lab_lr, + ) + for i, (k, in_c, out_c, s, se) in enumerate(self.net_config["blocks6"]) + ] + ) + self.out_channels = make_divisible(512 * scale) + + if self.det: + mv_c = [16, 24, 56, 480] + self.out_channels = [ + make_divisible(self.net_config["blocks3"][-1][2] * scale), + make_divisible(self.net_config["blocks4"][-1][2] * scale), + make_divisible(self.net_config["blocks5"][-1][2] * scale), + make_divisible(self.net_config["blocks6"][-1][2] * scale), + ] + + self.layer_list = nn.LayerList( + [ + nn.Conv2D(self.out_channels[0], int(mv_c[0] * scale), 1, 1, 0), + nn.Conv2D(self.out_channels[1], int(mv_c[1] * scale), 1, 1, 0), + nn.Conv2D(self.out_channels[2], int(mv_c[2] * scale), 1, 1, 0), + nn.Conv2D(self.out_channels[3], int(mv_c[3] * scale), 1, 1, 0), + ] + ) + self.out_channels = [ + int(mv_c[0] * scale), + int(mv_c[1] * scale), + int(mv_c[2] * scale), + int(mv_c[3] * scale), + ] + + def forward(self, x): + out_list = [] + x = self.conv1(x) + + x = self.blocks2(x) + x = self.blocks3(x) + out_list.append(x) + x = self.blocks4(x) + out_list.append(x) + x = self.blocks5(x) + out_list.append(x) + x = self.blocks6(x) + out_list.append(x) + + if self.det: + out_list[0] = self.layer_list[0](out_list[0]) + out_list[1] = self.layer_list[1](out_list[1]) + out_list[2] = self.layer_list[2](out_list[2]) + out_list[3] = self.layer_list[3](out_list[3]) + return out_list + + if self.training: + x = F.adaptive_avg_pool2d(x, [1, 40]) + else: + x = F.avg_pool2d(x, [3, 2]) + return x diff --git a/modules/onnx_ocr_module/src/ppocr/modeling/backbones/rec_micronet.py b/modules/onnx_ocr_module/src/ppocr/modeling/backbones/rec_micronet.py new file mode 100644 index 0000000..f554a7d --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/modeling/backbones/rec_micronet.py @@ -0,0 +1,605 @@ +# copyright (c) 2021 PaddlePaddle Authors. All Rights Reserve. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +""" +This code is refer from: +https://github.com/liyunsheng13/micronet/blob/main/backbone/micronet.py +https://github.com/liyunsheng13/micronet/blob/main/backbone/activation.py +""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import paddle +import paddle.nn as nn + +from ppocr.modeling.backbones.det_mobilenet_v3 import make_divisible + +M0_cfgs = [ + # s, n, c, ks, c1, c2, g1, g2, c3, g3, g4, y1, y2, y3, r + [2, 1, 8, 3, 2, 2, 0, 4, 8, 2, 2, 2, 0, 1, 1], + [2, 1, 12, 3, 2, 2, 0, 8, 12, 4, 4, 2, 2, 1, 1], + [2, 1, 16, 5, 2, 2, 0, 12, 16, 4, 4, 2, 2, 1, 1], + [1, 1, 32, 5, 1, 4, 4, 4, 32, 4, 4, 2, 2, 1, 1], + [2, 1, 64, 5, 1, 4, 8, 8, 64, 8, 8, 2, 2, 1, 1], + [1, 1, 96, 3, 1, 4, 8, 8, 96, 8, 8, 2, 2, 1, 2], + [1, 1, 384, 3, 1, 4, 12, 12, 0, 0, 0, 2, 2, 1, 2], +] +M1_cfgs = [ + # s, n, c, ks, c1, c2, g1, g2, c3, g3, g4 + [2, 1, 8, 3, 2, 2, 0, 6, 8, 2, 2, 2, 0, 1, 1], + [2, 1, 16, 3, 2, 2, 0, 8, 16, 4, 4, 2, 2, 1, 1], + [2, 1, 16, 5, 2, 2, 0, 16, 16, 4, 4, 2, 2, 1, 1], + [1, 1, 32, 5, 1, 6, 4, 4, 32, 4, 4, 2, 2, 1, 1], + [2, 1, 64, 5, 1, 6, 8, 8, 64, 8, 8, 2, 2, 1, 1], + [1, 1, 96, 3, 1, 6, 8, 8, 96, 8, 8, 2, 2, 1, 2], + [1, 1, 576, 3, 1, 6, 12, 12, 0, 0, 0, 2, 2, 1, 2], +] +M2_cfgs = [ + # s, n, c, ks, c1, c2, g1, g2, c3, g3, g4 + [2, 1, 12, 3, 2, 2, 0, 8, 12, 4, 4, 2, 0, 1, 1], + [2, 1, 16, 3, 2, 2, 0, 12, 16, 4, 4, 2, 2, 1, 1], + [1, 1, 24, 3, 2, 2, 0, 16, 24, 4, 4, 2, 2, 1, 1], + [2, 1, 32, 5, 1, 6, 6, 6, 32, 4, 4, 2, 2, 1, 1], + [1, 1, 32, 5, 1, 6, 8, 8, 32, 4, 4, 2, 2, 1, 2], + [1, 1, 64, 5, 1, 6, 8, 8, 64, 8, 8, 2, 2, 1, 2], + [2, 1, 96, 5, 1, 6, 8, 8, 96, 8, 8, 2, 2, 1, 2], + [1, 1, 128, 3, 1, 6, 12, 12, 128, 8, 8, 2, 2, 1, 2], + [1, 1, 768, 3, 1, 6, 16, 16, 0, 0, 0, 2, 2, 1, 2], +] +M3_cfgs = [ + # s, n, c, ks, c1, c2, g1, g2, c3, g3, g4 + [2, 1, 16, 3, 2, 2, 0, 12, 16, 4, 4, 0, 2, 0, 1], + [2, 1, 24, 3, 2, 2, 0, 16, 24, 4, 4, 0, 2, 0, 1], + [1, 1, 24, 3, 2, 2, 0, 24, 24, 4, 4, 0, 2, 0, 1], + [2, 1, 32, 5, 1, 6, 6, 6, 32, 4, 4, 0, 2, 0, 1], + [1, 1, 32, 5, 1, 6, 8, 8, 32, 4, 4, 0, 2, 0, 2], + [1, 1, 64, 5, 1, 6, 8, 8, 48, 8, 8, 0, 2, 0, 2], + [1, 1, 80, 5, 1, 6, 8, 8, 80, 8, 8, 0, 2, 0, 2], + [1, 1, 80, 5, 1, 6, 10, 10, 80, 8, 8, 0, 2, 0, 2], + [1, 1, 120, 5, 1, 6, 10, 10, 120, 10, 10, 0, 2, 0, 2], + [1, 1, 120, 5, 1, 6, 12, 12, 120, 10, 10, 0, 2, 0, 2], + [1, 1, 144, 3, 1, 6, 12, 12, 144, 12, 12, 0, 2, 0, 2], + [1, 1, 432, 3, 1, 3, 12, 12, 0, 0, 0, 0, 2, 0, 2], +] + + +def get_micronet_config(mode): + return eval(mode + "_cfgs") + + +class MaxGroupPooling(nn.Layer): + def __init__(self, channel_per_group=2): + super(MaxGroupPooling, self).__init__() + self.channel_per_group = channel_per_group + + def forward(self, x): + if self.channel_per_group == 1: + return x + # max op + b, c, h, w = x.shape + + # reshape + y = paddle.reshape(x, [b, c // self.channel_per_group, -1, h, w]) + out = paddle.max(y, axis=2) + return out + + +class SpatialSepConvSF(nn.Layer): + def __init__(self, inp, oups, kernel_size, stride): + super(SpatialSepConvSF, self).__init__() + + oup1, oup2 = oups + self.conv = nn.Sequential( + nn.Conv2D( + inp, + oup1, + (kernel_size, 1), + (stride, 1), + (kernel_size // 2, 0), + bias_attr=False, + groups=1, + ), + nn.BatchNorm2D(oup1), + nn.Conv2D( + oup1, + oup1 * oup2, + (1, kernel_size), + (1, stride), + (0, kernel_size // 2), + bias_attr=False, + groups=oup1, + ), + nn.BatchNorm2D(oup1 * oup2), + ChannelShuffle(oup1), + ) + + def forward(self, x): + out = self.conv(x) + return out + + +class ChannelShuffle(nn.Layer): + def __init__(self, groups): + super(ChannelShuffle, self).__init__() + self.groups = groups + + def forward(self, x): + b, c, h, w = x.shape + + channels_per_group = c // self.groups + + # reshape + x = paddle.reshape(x, [b, self.groups, channels_per_group, h, w]) + + x = paddle.transpose(x, (0, 2, 1, 3, 4)) + out = paddle.reshape(x, [b, -1, h, w]) + + return out + + +class StemLayer(nn.Layer): + def __init__(self, inp, oup, stride, groups=(4, 4)): + super(StemLayer, self).__init__() + + g1, g2 = groups + self.stem = nn.Sequential( + SpatialSepConvSF(inp, groups, 3, stride), + MaxGroupPooling(2) if g1 * g2 == 2 * oup else nn.ReLU6(), + ) + + def forward(self, x): + out = self.stem(x) + return out + + +class DepthSpatialSepConv(nn.Layer): + def __init__(self, inp, expand, kernel_size, stride): + super(DepthSpatialSepConv, self).__init__() + + exp1, exp2 = expand + + hidden_dim = inp * exp1 + oup = inp * exp1 * exp2 + + self.conv = nn.Sequential( + nn.Conv2D( + inp, + inp * exp1, + (kernel_size, 1), + (stride, 1), + (kernel_size // 2, 0), + bias_attr=False, + groups=inp, + ), + nn.BatchNorm2D(inp * exp1), + nn.Conv2D( + hidden_dim, + oup, + (1, kernel_size), + 1, + (0, kernel_size // 2), + bias_attr=False, + groups=hidden_dim, + ), + nn.BatchNorm2D(oup), + ) + + def forward(self, x): + x = self.conv(x) + return x + + +class GroupConv(nn.Layer): + def __init__(self, inp, oup, groups=2): + super(GroupConv, self).__init__() + self.inp = inp + self.oup = oup + self.groups = groups + self.conv = nn.Sequential( + nn.Conv2D(inp, oup, 1, 1, 0, bias_attr=False, groups=self.groups[0]), + nn.BatchNorm2D(oup), + ) + + def forward(self, x): + x = self.conv(x) + return x + + +class DepthConv(nn.Layer): + def __init__(self, inp, oup, kernel_size, stride): + super(DepthConv, self).__init__() + self.conv = nn.Sequential( + nn.Conv2D( + inp, + oup, + kernel_size, + stride, + kernel_size // 2, + bias_attr=False, + groups=inp, + ), + nn.BatchNorm2D(oup), + ) + + def forward(self, x): + out = self.conv(x) + return out + + +class DYShiftMax(nn.Layer): + def __init__( + self, + inp, + oup, + reduction=4, + act_max=1.0, + act_relu=True, + init_a=[0.0, 0.0], + init_b=[0.0, 0.0], + relu_before_pool=False, + g=None, + expansion=False, + ): + super(DYShiftMax, self).__init__() + self.oup = oup + self.act_max = act_max * 2 + self.act_relu = act_relu + self.avg_pool = nn.Sequential( + nn.ReLU() if relu_before_pool == True else nn.Sequential(), + nn.AdaptiveAvgPool2D(1), + ) + + self.exp = 4 if act_relu else 2 + self.init_a = init_a + self.init_b = init_b + + # determine squeeze + squeeze = make_divisible(inp // reduction, 4) + if squeeze < 4: + squeeze = 4 + + self.fc = nn.Sequential( + nn.Linear(inp, squeeze), + nn.ReLU(), + nn.Linear(squeeze, oup * self.exp), + nn.Hardsigmoid(), + ) + + if g is None: + g = 1 + self.g = g[1] + if self.g != 1 and expansion: + self.g = inp // self.g + + self.gc = inp // self.g + index = paddle.to_tensor([range(inp)]) + index = paddle.reshape(index, [1, inp, 1, 1]) + index = paddle.reshape(index, [1, self.g, self.gc, 1, 1]) + indexgs = paddle.split(index, [1, self.g - 1], axis=1) + indexgs = paddle.concat((indexgs[1], indexgs[0]), axis=1) + indexes = paddle.split(indexgs, [1, self.gc - 1], axis=2) + indexes = paddle.concat((indexes[1], indexes[0]), axis=2) + self.index = paddle.reshape(indexes, [inp]) + self.expansion = expansion + + def forward(self, x): + x_in = x + x_out = x + + b, c, _, _ = x_in.shape + y = self.avg_pool(x_in) + y = paddle.reshape(y, [b, c]) + y = self.fc(y) + y = paddle.reshape(y, [b, self.oup * self.exp, 1, 1]) + y = (y - 0.5) * self.act_max + + n2, c2, h2, w2 = x_out.shape + x2 = paddle.to_tensor(x_out.numpy()[:, self.index.numpy(), :, :]) + + if self.exp == 4: + temp = y.shape + a1, b1, a2, b2 = paddle.split(y, temp[1] // self.oup, axis=1) + + a1 = a1 + self.init_a[0] + a2 = a2 + self.init_a[1] + + b1 = b1 + self.init_b[0] + b2 = b2 + self.init_b[1] + + z1 = x_out * a1 + x2 * b1 + z2 = x_out * a2 + x2 * b2 + + out = paddle.maximum(z1, z2) + + elif self.exp == 2: + temp = y.shape + a1, b1 = paddle.split(y, temp[1] // self.oup, axis=1) + a1 = a1 + self.init_a[0] + b1 = b1 + self.init_b[0] + out = x_out * a1 + x2 * b1 + + return out + + +class DYMicroBlock(nn.Layer): + def __init__( + self, + inp, + oup, + kernel_size=3, + stride=1, + ch_exp=(2, 2), + ch_per_group=4, + groups_1x1=(1, 1), + depthsep=True, + shuffle=False, + activation_cfg=None, + ): + super(DYMicroBlock, self).__init__() + + self.identity = stride == 1 and inp == oup + + y1, y2, y3 = activation_cfg["dy"] + act_reduction = 8 * activation_cfg["ratio"] + init_a = activation_cfg["init_a"] + init_b = activation_cfg["init_b"] + + t1 = ch_exp + gs1 = ch_per_group + hidden_fft, g1, g2 = groups_1x1 + hidden_dim2 = inp * t1[0] * t1[1] + + if gs1[0] == 0: + self.layers = nn.Sequential( + DepthSpatialSepConv(inp, t1, kernel_size, stride), + ( + DYShiftMax( + hidden_dim2, + hidden_dim2, + act_max=2.0, + act_relu=True if y2 == 2 else False, + init_a=init_a, + reduction=act_reduction, + init_b=init_b, + g=gs1, + expansion=False, + ) + if y2 > 0 + else nn.ReLU6() + ), + ChannelShuffle(gs1[1]) if shuffle else nn.Sequential(), + ( + ChannelShuffle(hidden_dim2 // 2) + if shuffle and y2 != 0 + else nn.Sequential() + ), + GroupConv(hidden_dim2, oup, (g1, g2)), + ( + DYShiftMax( + oup, + oup, + act_max=2.0, + act_relu=False, + init_a=[1.0, 0.0], + reduction=act_reduction // 2, + init_b=[0.0, 0.0], + g=(g1, g2), + expansion=False, + ) + if y3 > 0 + else nn.Sequential() + ), + ChannelShuffle(g2) if shuffle else nn.Sequential(), + ( + ChannelShuffle(oup // 2) + if shuffle and oup % 2 == 0 and y3 != 0 + else nn.Sequential() + ), + ) + elif g2 == 0: + self.layers = nn.Sequential( + GroupConv(inp, hidden_dim2, gs1), + ( + DYShiftMax( + hidden_dim2, + hidden_dim2, + act_max=2.0, + act_relu=False, + init_a=[1.0, 0.0], + reduction=act_reduction, + init_b=[0.0, 0.0], + g=gs1, + expansion=False, + ) + if y3 > 0 + else nn.Sequential() + ), + ) + else: + self.layers = nn.Sequential( + GroupConv(inp, hidden_dim2, gs1), + ( + DYShiftMax( + hidden_dim2, + hidden_dim2, + act_max=2.0, + act_relu=True if y1 == 2 else False, + init_a=init_a, + reduction=act_reduction, + init_b=init_b, + g=gs1, + expansion=False, + ) + if y1 > 0 + else nn.ReLU6() + ), + ChannelShuffle(gs1[1]) if shuffle else nn.Sequential(), + ( + DepthSpatialSepConv(hidden_dim2, (1, 1), kernel_size, stride) + if depthsep + else DepthConv(hidden_dim2, hidden_dim2, kernel_size, stride) + ), + nn.Sequential(), + ( + DYShiftMax( + hidden_dim2, + hidden_dim2, + act_max=2.0, + act_relu=True if y2 == 2 else False, + init_a=init_a, + reduction=act_reduction, + init_b=init_b, + g=gs1, + expansion=True, + ) + if y2 > 0 + else nn.ReLU6() + ), + ( + ChannelShuffle(hidden_dim2 // 4) + if shuffle and y1 != 0 and y2 != 0 + else ( + nn.Sequential() + if y1 == 0 and y2 == 0 + else ChannelShuffle(hidden_dim2 // 2) + ) + ), + GroupConv(hidden_dim2, oup, (g1, g2)), + ( + DYShiftMax( + oup, + oup, + act_max=2.0, + act_relu=False, + init_a=[1.0, 0.0], + reduction=( + act_reduction // 2 if oup < hidden_dim2 else act_reduction + ), + init_b=[0.0, 0.0], + g=(g1, g2), + expansion=False, + ) + if y3 > 0 + else nn.Sequential() + ), + ChannelShuffle(g2) if shuffle else nn.Sequential(), + ChannelShuffle(oup // 2) if shuffle and y3 != 0 else nn.Sequential(), + ) + + def forward(self, x): + identity = x + out = self.layers(x) + + if self.identity: + out = out + identity + + return out + + +class MicroNet(nn.Layer): + """ + the MicroNet backbone network for recognition module. + Args: + mode(str): {'M0', 'M1', 'M2', 'M3'} + Four models are proposed based on four different computational costs (4M, 6M, 12M, 21M MAdds) + Default: 'M3'. + """ + + def __init__(self, mode="M3", **kwargs): + super(MicroNet, self).__init__() + + self.cfgs = get_micronet_config(mode) + + activation_cfg = {} + if mode == "M0": + input_channel = 4 + stem_groups = 2, 2 + out_ch = 384 + activation_cfg["init_a"] = 1.0, 1.0 + activation_cfg["init_b"] = 0.0, 0.0 + elif mode == "M1": + input_channel = 6 + stem_groups = 3, 2 + out_ch = 576 + activation_cfg["init_a"] = 1.0, 1.0 + activation_cfg["init_b"] = 0.0, 0.0 + elif mode == "M2": + input_channel = 8 + stem_groups = 4, 2 + out_ch = 768 + activation_cfg["init_a"] = 1.0, 1.0 + activation_cfg["init_b"] = 0.0, 0.0 + elif mode == "M3": + input_channel = 12 + stem_groups = 4, 3 + out_ch = 432 + activation_cfg["init_a"] = 1.0, 0.5 + activation_cfg["init_b"] = 0.0, 0.5 + else: + raise NotImplementedError("mode[" + mode + "_model] is not implemented!") + + layers = [StemLayer(3, input_channel, stride=2, groups=stem_groups)] + + for idx, val in enumerate(self.cfgs): + s, n, c, ks, c1, c2, g1, g2, c3, g3, g4, y1, y2, y3, r = val + + t1 = (c1, c2) + gs1 = (g1, g2) + gs2 = (c3, g3, g4) + activation_cfg["dy"] = [y1, y2, y3] + activation_cfg["ratio"] = r + + output_channel = c + layers.append( + DYMicroBlock( + input_channel, + output_channel, + kernel_size=ks, + stride=s, + ch_exp=t1, + ch_per_group=gs1, + groups_1x1=gs2, + depthsep=True, + shuffle=True, + activation_cfg=activation_cfg, + ) + ) + input_channel = output_channel + for i in range(1, n): + layers.append( + DYMicroBlock( + input_channel, + output_channel, + kernel_size=ks, + stride=1, + ch_exp=t1, + ch_per_group=gs1, + groups_1x1=gs2, + depthsep=True, + shuffle=True, + activation_cfg=activation_cfg, + ) + ) + input_channel = output_channel + self.features = nn.Sequential(*layers) + + self.pool = nn.MaxPool2D(kernel_size=2, stride=2, padding=0) + + self.out_channels = make_divisible(out_ch) + + def forward(self, x): + x = self.features(x) + x = self.pool(x) + return x diff --git a/modules/onnx_ocr_module/src/ppocr/modeling/backbones/rec_mobilenet_v3.py b/modules/onnx_ocr_module/src/ppocr/modeling/backbones/rec_mobilenet_v3.py new file mode 100644 index 0000000..00ee5a3 --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/modeling/backbones/rec_mobilenet_v3.py @@ -0,0 +1,156 @@ +# copyright (c) 2020 PaddlePaddle Authors. All Rights Reserve. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from paddle import nn + +from ppocr.modeling.backbones.det_mobilenet_v3 import ( + ResidualUnit, + ConvBNLayer, + make_divisible, +) + +__all__ = ["MobileNetV3"] + + +class MobileNetV3(nn.Layer): + def __init__( + self, + in_channels=3, + model_name="small", + scale=0.5, + large_stride=None, + small_stride=None, + disable_se=False, + **kwargs, + ): + super(MobileNetV3, self).__init__() + self.disable_se = disable_se + if small_stride is None: + small_stride = [2, 2, 2, 2] + if large_stride is None: + large_stride = [1, 2, 2, 2] + + assert isinstance( + large_stride, list + ), "large_stride type must " "be list but got {}".format(type(large_stride)) + assert isinstance( + small_stride, list + ), "small_stride type must " "be list but got {}".format(type(small_stride)) + assert ( + len(large_stride) == 4 + ), "large_stride length must be " "4 but got {}".format(len(large_stride)) + assert ( + len(small_stride) == 4 + ), "small_stride length must be " "4 but got {}".format(len(small_stride)) + + if model_name == "large": + cfg = [ + # k, exp, c, se, nl, s, + [3, 16, 16, False, "relu", large_stride[0]], + [3, 64, 24, False, "relu", (large_stride[1], 1)], + [3, 72, 24, False, "relu", 1], + [5, 72, 40, True, "relu", (large_stride[2], 1)], + [5, 120, 40, True, "relu", 1], + [5, 120, 40, True, "relu", 1], + [3, 240, 80, False, "hardswish", 1], + [3, 200, 80, False, "hardswish", 1], + [3, 184, 80, False, "hardswish", 1], + [3, 184, 80, False, "hardswish", 1], + [3, 480, 112, True, "hardswish", 1], + [3, 672, 112, True, "hardswish", 1], + [5, 672, 160, True, "hardswish", (large_stride[3], 1)], + [5, 960, 160, True, "hardswish", 1], + [5, 960, 160, True, "hardswish", 1], + ] + cls_ch_squeeze = 960 + elif model_name == "small": + cfg = [ + # k, exp, c, se, nl, s, + [3, 16, 16, True, "relu", (small_stride[0], 1)], + [3, 72, 24, False, "relu", (small_stride[1], 1)], + [3, 88, 24, False, "relu", 1], + [5, 96, 40, True, "hardswish", (small_stride[2], 1)], + [5, 240, 40, True, "hardswish", 1], + [5, 240, 40, True, "hardswish", 1], + [5, 120, 48, True, "hardswish", 1], + [5, 144, 48, True, "hardswish", 1], + [5, 288, 96, True, "hardswish", (small_stride[3], 1)], + [5, 576, 96, True, "hardswish", 1], + [5, 576, 96, True, "hardswish", 1], + ] + cls_ch_squeeze = 576 + else: + raise NotImplementedError( + "mode[" + model_name + "_model] is not implemented!" + ) + + supported_scale = [0.35, 0.5, 0.75, 1.0, 1.25] + assert ( + scale in supported_scale + ), "supported scales are {} but input scale is {}".format( + supported_scale, scale + ) + + inplanes = 16 + # conv1 + self.conv1 = ConvBNLayer( + in_channels=in_channels, + out_channels=make_divisible(inplanes * scale), + kernel_size=3, + stride=2, + padding=1, + groups=1, + if_act=True, + act="hardswish", + ) + i = 0 + block_list = [] + inplanes = make_divisible(inplanes * scale) + for k, exp, c, se, nl, s in cfg: + se = se and not self.disable_se + block_list.append( + ResidualUnit( + in_channels=inplanes, + mid_channels=make_divisible(scale * exp), + out_channels=make_divisible(scale * c), + kernel_size=k, + stride=s, + use_se=se, + act=nl, + ) + ) + inplanes = make_divisible(scale * c) + i += 1 + self.blocks = nn.Sequential(*block_list) + + self.conv2 = ConvBNLayer( + in_channels=inplanes, + out_channels=make_divisible(scale * cls_ch_squeeze), + kernel_size=1, + stride=1, + padding=0, + groups=1, + if_act=True, + act="hardswish", + ) + + self.pool = nn.MaxPool2D(kernel_size=2, stride=2, padding=0) + self.out_channels = make_divisible(scale * cls_ch_squeeze) + + def forward(self, x): + x = self.conv1(x) + x = self.blocks(x) + x = self.conv2(x) + x = self.pool(x) + return x diff --git a/modules/onnx_ocr_module/src/ppocr/modeling/backbones/rec_mv1_enhance.py b/modules/onnx_ocr_module/src/ppocr/modeling/backbones/rec_mv1_enhance.py new file mode 100644 index 0000000..f20fa4c --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/modeling/backbones/rec_mv1_enhance.py @@ -0,0 +1,283 @@ +# copyright (c) 2021 PaddlePaddle Authors. All Rights Reserve. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# This code is refer from: https://github.com/PaddlePaddle/PaddleClas/blob/develop/ppcls/arch/backbone/legendary_models/pp_lcnet.py + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import math +import numpy as np +import paddle +from paddle import ParamAttr, reshape, transpose +import paddle.nn as nn +import paddle.nn.functional as F +from paddle.nn import Conv2D, BatchNorm, Linear, Dropout +from paddle.nn import AdaptiveAvgPool2D, MaxPool2D, AvgPool2D +from paddle.nn.initializer import KaimingNormal +from paddle.regularizer import L2Decay +from paddle.nn.functional import hardswish, hardsigmoid + + +class ConvBNLayer(nn.Layer): + def __init__( + self, + num_channels, + filter_size, + num_filters, + stride, + padding, + channels=None, + num_groups=1, + act="hard_swish", + ): + super(ConvBNLayer, self).__init__() + + self._conv = Conv2D( + in_channels=num_channels, + out_channels=num_filters, + kernel_size=filter_size, + stride=stride, + padding=padding, + groups=num_groups, + weight_attr=ParamAttr(initializer=KaimingNormal()), + bias_attr=False, + ) + + self._batch_norm = BatchNorm( + num_filters, + act=act, + param_attr=ParamAttr(regularizer=L2Decay(0.0)), + bias_attr=ParamAttr(regularizer=L2Decay(0.0)), + ) + + def forward(self, inputs): + y = self._conv(inputs) + y = self._batch_norm(y) + return y + + +class DepthwiseSeparable(nn.Layer): + def __init__( + self, + num_channels, + num_filters1, + num_filters2, + num_groups, + stride, + scale, + dw_size=3, + padding=1, + use_se=False, + ): + super(DepthwiseSeparable, self).__init__() + self.use_se = use_se + self._depthwise_conv = ConvBNLayer( + num_channels=num_channels, + num_filters=int(num_filters1 * scale), + filter_size=dw_size, + stride=stride, + padding=padding, + num_groups=int(num_groups * scale), + ) + if use_se: + self._se = SEModule(int(num_filters1 * scale)) + self._pointwise_conv = ConvBNLayer( + num_channels=int(num_filters1 * scale), + filter_size=1, + num_filters=int(num_filters2 * scale), + stride=1, + padding=0, + ) + + def forward(self, inputs): + y = self._depthwise_conv(inputs) + if self.use_se: + y = self._se(y) + y = self._pointwise_conv(y) + return y + + +class MobileNetV1Enhance(nn.Layer): + def __init__( + self, + in_channels=3, + scale=0.5, + last_conv_stride=1, + last_pool_type="max", + last_pool_kernel_size=[3, 2], + **kwargs, + ): + super().__init__() + self.scale = scale + self.block_list = [] + + self.conv1 = ConvBNLayer( + num_channels=3, + filter_size=3, + channels=3, + num_filters=int(32 * scale), + stride=2, + padding=1, + ) + + conv2_1 = DepthwiseSeparable( + num_channels=int(32 * scale), + num_filters1=32, + num_filters2=64, + num_groups=32, + stride=1, + scale=scale, + ) + self.block_list.append(conv2_1) + + conv2_2 = DepthwiseSeparable( + num_channels=int(64 * scale), + num_filters1=64, + num_filters2=128, + num_groups=64, + stride=1, + scale=scale, + ) + self.block_list.append(conv2_2) + + conv3_1 = DepthwiseSeparable( + num_channels=int(128 * scale), + num_filters1=128, + num_filters2=128, + num_groups=128, + stride=1, + scale=scale, + ) + self.block_list.append(conv3_1) + + conv3_2 = DepthwiseSeparable( + num_channels=int(128 * scale), + num_filters1=128, + num_filters2=256, + num_groups=128, + stride=(2, 1), + scale=scale, + ) + self.block_list.append(conv3_2) + + conv4_1 = DepthwiseSeparable( + num_channels=int(256 * scale), + num_filters1=256, + num_filters2=256, + num_groups=256, + stride=1, + scale=scale, + ) + self.block_list.append(conv4_1) + + conv4_2 = DepthwiseSeparable( + num_channels=int(256 * scale), + num_filters1=256, + num_filters2=512, + num_groups=256, + stride=(2, 1), + scale=scale, + ) + self.block_list.append(conv4_2) + + for _ in range(5): + conv5 = DepthwiseSeparable( + num_channels=int(512 * scale), + num_filters1=512, + num_filters2=512, + num_groups=512, + stride=1, + dw_size=5, + padding=2, + scale=scale, + use_se=False, + ) + self.block_list.append(conv5) + + conv5_6 = DepthwiseSeparable( + num_channels=int(512 * scale), + num_filters1=512, + num_filters2=1024, + num_groups=512, + stride=(2, 1), + dw_size=5, + padding=2, + scale=scale, + use_se=True, + ) + self.block_list.append(conv5_6) + + conv6 = DepthwiseSeparable( + num_channels=int(1024 * scale), + num_filters1=1024, + num_filters2=1024, + num_groups=1024, + stride=last_conv_stride, + dw_size=5, + padding=2, + use_se=True, + scale=scale, + ) + self.block_list.append(conv6) + + self.block_list = nn.Sequential(*self.block_list) + if last_pool_type == "avg": + self.pool = nn.AvgPool2D( + kernel_size=last_pool_kernel_size, + stride=last_pool_kernel_size, + padding=0, + ) + else: + self.pool = nn.MaxPool2D(kernel_size=2, stride=2, padding=0) + self.out_channels = int(1024 * scale) + + def forward(self, inputs): + y = self.conv1(inputs) + y = self.block_list(y) + y = self.pool(y) + return y + + +class SEModule(nn.Layer): + def __init__(self, channel, reduction=4): + super(SEModule, self).__init__() + self.avg_pool = AdaptiveAvgPool2D(1) + self.conv1 = Conv2D( + in_channels=channel, + out_channels=channel // reduction, + kernel_size=1, + stride=1, + padding=0, + weight_attr=ParamAttr(), + bias_attr=ParamAttr(), + ) + self.conv2 = Conv2D( + in_channels=channel // reduction, + out_channels=channel, + kernel_size=1, + stride=1, + padding=0, + weight_attr=ParamAttr(), + bias_attr=ParamAttr(), + ) + + def forward(self, inputs): + outputs = self.avg_pool(inputs) + outputs = self.conv1(outputs) + outputs = F.relu(outputs) + outputs = self.conv2(outputs) + outputs = hardsigmoid(outputs) + return paddle.multiply(x=inputs, y=outputs) diff --git a/modules/onnx_ocr_module/src/ppocr/modeling/backbones/rec_nrtr_mtb.py b/modules/onnx_ocr_module/src/ppocr/modeling/backbones/rec_nrtr_mtb.py new file mode 100644 index 0000000..608a8a7 --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/modeling/backbones/rec_nrtr_mtb.py @@ -0,0 +1,47 @@ +# copyright (c) 2020 PaddlePaddle Authors. All Rights Reserve. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from paddle import nn +import paddle + + +class MTB(nn.Layer): + def __init__(self, cnn_num, in_channels): + super(MTB, self).__init__() + self.block = nn.Sequential() + self.out_channels = in_channels + self.cnn_num = cnn_num + if self.cnn_num == 2: + for i in range(self.cnn_num): + self.block.add_sublayer( + "conv_{}".format(i), + nn.Conv2D( + in_channels=in_channels if i == 0 else 32 * (2 ** (i - 1)), + out_channels=32 * (2**i), + kernel_size=3, + stride=2, + padding=1, + ), + ) + self.block.add_sublayer("relu_{}".format(i), nn.ReLU()) + self.block.add_sublayer("bn_{}".format(i), nn.BatchNorm2D(32 * (2**i))) + + def forward(self, images): + x = self.block(images) + if self.cnn_num == 2: + # (b, w, h, c) + x = paddle.transpose(x, [0, 3, 2, 1]) + x_shape = x.shape + x = paddle.reshape(x, [x_shape[0], x_shape[1], x_shape[2] * x_shape[3]]) + return x diff --git a/modules/onnx_ocr_module/src/ppocr/modeling/backbones/rec_pphgnetv2.py b/modules/onnx_ocr_module/src/ppocr/modeling/backbones/rec_pphgnetv2.py new file mode 100644 index 0000000..806e469 --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/modeling/backbones/rec_pphgnetv2.py @@ -0,0 +1,1585 @@ +# copyright (c) 2024 PaddlePaddle Authors. All Rights Reserve. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +""" +This code is refer from: +https://github.com/PaddlePaddle/PaddleClas/blob/2f36cab604e439b59d1a854df34ece3b10d888e3/ppcls/arch/backbone/legendary_models/pp_hgnet_v2.py +""" + +from __future__ import absolute_import, division, print_function + +import math +import numpy as np +import paddle +import paddle.nn as nn +import paddle.nn.functional as F +from paddle import ParamAttr +from paddle.nn import Conv2D, BatchNorm, Linear, BatchNorm2D, MaxPool2D, AvgPool2D +from paddle.nn.initializer import Uniform +from paddle.regularizer import L2Decay + +from typing import Tuple, List, Dict, Union, Callable, Any +from ppocr.modeling.backbones.rec_donut_swin import DonutSwinModelOutput + + +class IdentityBasedConv1x1(nn.Conv2D): + def __init__(self, channels, groups=1): + super(IdentityBasedConv1x1, self).__init__( + in_channels=channels, + out_channels=channels, + kernel_size=1, + stride=1, + padding=0, + groups=groups, + bias_attr=False, + ) + + assert channels % groups == 0 + input_dim = channels // groups + id_value = np.zeros((channels, input_dim, 1, 1)) + for i in range(channels): + id_value[i, i % input_dim, 0, 0] = 1 + self.id_tensor = paddle.to_tensor(id_value) + self.weight.set_value(paddle.zeros_like(self.weight)) + + def forward(self, input): + kernel = self.weight + self.id_tensor + result = F.conv2d( + input, + kernel, + None, + stride=1, + padding=0, + dilation=self._dilation, + groups=self._groups, + ) + return result + + def get_actual_kernel(self): + return self.weight + self.id_tensor + + +class BNAndPad(nn.Layer): + def __init__( + self, + pad_pixels, + num_features, + epsilon=1e-5, + momentum=0.1, + last_conv_bias=None, + bn=nn.BatchNorm2D, + ): + super().__init__() + self.bn = bn(num_features, momentum=momentum, epsilon=epsilon) + self.pad_pixels = pad_pixels + self.last_conv_bias = last_conv_bias + + def forward(self, input): + output = self.bn(input) + if self.pad_pixels > 0: + bias = -self.bn._mean + if self.last_conv_bias is not None: + bias += self.last_conv_bias + pad_values = self.bn.bias + self.bn.weight * ( + bias / paddle.sqrt(self.bn._variance + self.bn._epsilon) + ) + """ pad """ + # TODO: n,h,w,c format is not supported yet + n, c, h, w = output.shape + values = pad_values.reshape([1, -1, 1, 1]) + w_values = values.expand([n, -1, self.pad_pixels, w]) + x = paddle.concat([w_values, output, w_values], axis=2) + h = h + self.pad_pixels * 2 + h_values = values.expand([n, -1, h, self.pad_pixels]) + x = paddle.concat([h_values, x, h_values], axis=3) + output = x + return output + + @property + def weight(self): + return self.bn.weight + + @property + def bias(self): + return self.bn.bias + + @property + def _mean(self): + return self.bn._mean + + @property + def _variance(self): + return self.bn._variance + + @property + def _epsilon(self): + return self.bn._epsilon + + +def conv_bn( + in_channels, + out_channels, + kernel_size, + stride=1, + padding=0, + dilation=1, + groups=1, + padding_mode="zeros", +): + conv_layer = nn.Conv2D( + in_channels=in_channels, + out_channels=out_channels, + kernel_size=kernel_size, + stride=stride, + padding=padding, + dilation=dilation, + groups=groups, + bias_attr=False, + padding_mode=padding_mode, + ) + bn_layer = nn.BatchNorm2D(num_features=out_channels) + se = nn.Sequential() + se.add_sublayer("conv", conv_layer) + se.add_sublayer("bn", bn_layer) + return se + + +def transI_fusebn(kernel, bn): + gamma = bn.weight + std = (bn._variance + bn._epsilon).sqrt() + return ( + kernel * ((gamma / std).reshape([-1, 1, 1, 1])), + bn.bias - bn._mean * gamma / std, + ) + + +def transII_addbranch(kernels, biases): + return sum(kernels), sum(biases) + + +def transIII_1x1_kxk(k1, b1, k2, b2, groups): + if groups == 1: + k = F.conv2d(k2, k1.transpose([1, 0, 2, 3])) + b_hat = (k2 * b1.reshape([1, -1, 1, 1])).sum((1, 2, 3)) + else: + k_slices = [] + b_slices = [] + k1_T = k1.transpose([1, 0, 2, 3]) + k1_group_width = k1.shape[0] // groups + k2_group_width = k2.shape[0] // groups + for g in range(groups): + k1_T_slice = k1_T[:, g * k1_group_width : (g + 1) * k1_group_width, :, :] + k2_slice = k2[g * k2_group_width : (g + 1) * k2_group_width, :, :, :] + k_slices.append(F.conv2d(k2_slice, k1_T_slice)) + b_slices.append( + ( + k2_slice + * b1[g * k1_group_width : (g + 1) * k1_group_width].reshape( + [1, -1, 1, 1] + ) + ).sum((1, 2, 3)) + ) + k, b_hat = transIV_depthconcat(k_slices, b_slices) + return k, b_hat + b2 + + +def transIV_depthconcat(kernels, biases): + return paddle.cat(kernels, axis=0), paddle.cat(biases) + + +def transV_avg(channels, kernel_size, groups): + input_dim = channels // groups + k = paddle.zeros((channels, input_dim, kernel_size, kernel_size)) + k[np.arange(channels), np.tile(np.arange(input_dim), groups), :, :] = ( + 1.0 / kernel_size**2 + ) + return k + + +def transVI_multiscale(kernel, target_kernel_size): + H_pixels_to_pad = (target_kernel_size - kernel.shape[2]) // 2 + W_pixels_to_pad = (target_kernel_size - kernel.shape[3]) // 2 + return F.pad( + kernel, [H_pixels_to_pad, H_pixels_to_pad, W_pixels_to_pad, W_pixels_to_pad] + ) + + +class DiverseBranchBlock(nn.Layer): + def __init__( + self, + num_channels, + num_filters, + filter_size, + stride=1, + groups=1, + act=None, + is_repped=False, + single_init=False, + **kwargs, + ): + super().__init__() + + padding = (filter_size - 1) // 2 + dilation = 1 + + in_channels = num_channels + out_channels = num_filters + kernel_size = filter_size + internal_channels_1x1_3x3 = None + nonlinear = act + + self.is_repped = is_repped + + if nonlinear is None: + self.nonlinear = nn.Identity() + else: + self.nonlinear = nn.ReLU() + + self.kernel_size = kernel_size + self.out_channels = out_channels + self.groups = groups + assert padding == kernel_size // 2 + + if is_repped: + self.dbb_reparam = nn.Conv2D( + in_channels=in_channels, + out_channels=out_channels, + kernel_size=kernel_size, + stride=stride, + padding=padding, + dilation=dilation, + groups=groups, + bias_attr=True, + ) + else: + self.dbb_origin = conv_bn( + in_channels=in_channels, + out_channels=out_channels, + kernel_size=kernel_size, + stride=stride, + padding=padding, + dilation=dilation, + groups=groups, + ) + + self.dbb_avg = nn.Sequential() + if groups < out_channels: + self.dbb_avg.add_sublayer( + "conv", + nn.Conv2D( + in_channels=in_channels, + out_channels=out_channels, + kernel_size=1, + stride=1, + padding=0, + groups=groups, + bias_attr=False, + ), + ) + self.dbb_avg.add_sublayer( + "bn", BNAndPad(pad_pixels=padding, num_features=out_channels) + ) + self.dbb_avg.add_sublayer( + "avg", + nn.AvgPool2D(kernel_size=kernel_size, stride=stride, padding=0), + ) + self.dbb_1x1 = conv_bn( + in_channels=in_channels, + out_channels=out_channels, + kernel_size=1, + stride=stride, + padding=0, + groups=groups, + ) + else: + self.dbb_avg.add_sublayer( + "avg", + nn.AvgPool2D( + kernel_size=kernel_size, stride=stride, padding=padding + ), + ) + + self.dbb_avg.add_sublayer("avgbn", nn.BatchNorm2D(out_channels)) + + if internal_channels_1x1_3x3 is None: + internal_channels_1x1_3x3 = ( + in_channels if groups < out_channels else 2 * in_channels + ) # For mobilenet, it is better to have 2X internal channels + + self.dbb_1x1_kxk = nn.Sequential() + if internal_channels_1x1_3x3 == in_channels: + self.dbb_1x1_kxk.add_sublayer( + "idconv1", IdentityBasedConv1x1(channels=in_channels, groups=groups) + ) + else: + self.dbb_1x1_kxk.add_sublayer( + "conv1", + nn.Conv2D( + in_channels=in_channels, + out_channels=internal_channels_1x1_3x3, + kernel_size=1, + stride=1, + padding=0, + groups=groups, + bias_attr=False, + ), + ) + self.dbb_1x1_kxk.add_sublayer( + "bn1", + BNAndPad(pad_pixels=padding, num_features=internal_channels_1x1_3x3), + ) + self.dbb_1x1_kxk.add_sublayer( + "conv2", + nn.Conv2D( + in_channels=internal_channels_1x1_3x3, + out_channels=out_channels, + kernel_size=kernel_size, + stride=stride, + padding=0, + groups=groups, + bias_attr=False, + ), + ) + self.dbb_1x1_kxk.add_sublayer("bn2", nn.BatchNorm2D(out_channels)) + + # The experiments reported in the paper used the default initialization of bn.weight (all as 1). But changing the initialization may be useful in some cases. + if single_init: + # Initialize the bn.weight of dbb_origin as 1 and others as 0. This is not the default setting. + self.single_init() + + def forward(self, inputs): + if self.is_repped: + return self.nonlinear(self.dbb_reparam(inputs)) + + out = self.dbb_origin(inputs) + if hasattr(self, "dbb_1x1"): + out += self.dbb_1x1(inputs) + out += self.dbb_avg(inputs) + out += self.dbb_1x1_kxk(inputs) + return self.nonlinear(out) + + def init_gamma(self, gamma_value): + if hasattr(self, "dbb_origin"): + paddle.nn.init.constant_(self.dbb_origin.bn.weight, gamma_value) + if hasattr(self, "dbb_1x1"): + paddle.nn.init.constant_(self.dbb_1x1.bn.weight, gamma_value) + if hasattr(self, "dbb_avg"): + paddle.nn.init.constant_(self.dbb_avg.avgbn.weight, gamma_value) + if hasattr(self, "dbb_1x1_kxk"): + paddle.nn.init.constant_(self.dbb_1x1_kxk.bn2.weight, gamma_value) + + def single_init(self): + self.init_gamma(0.0) + if hasattr(self, "dbb_origin"): + paddle.nn.init.constant_(self.dbb_origin.bn.weight, 1.0) + + def get_equivalent_kernel_bias(self): + k_origin, b_origin = transI_fusebn( + self.dbb_origin.conv.weight, self.dbb_origin.bn + ) + + if hasattr(self, "dbb_1x1"): + k_1x1, b_1x1 = transI_fusebn(self.dbb_1x1.conv.weight, self.dbb_1x1.bn) + k_1x1 = transVI_multiscale(k_1x1, self.kernel_size) + else: + k_1x1, b_1x1 = 0, 0 + + if hasattr(self.dbb_1x1_kxk, "idconv1"): + k_1x1_kxk_first = self.dbb_1x1_kxk.idconv1.get_actual_kernel() + else: + k_1x1_kxk_first = self.dbb_1x1_kxk.conv1.weight + k_1x1_kxk_first, b_1x1_kxk_first = transI_fusebn( + k_1x1_kxk_first, self.dbb_1x1_kxk.bn1 + ) + k_1x1_kxk_second, b_1x1_kxk_second = transI_fusebn( + self.dbb_1x1_kxk.conv2.weight, self.dbb_1x1_kxk.bn2 + ) + k_1x1_kxk_merged, b_1x1_kxk_merged = transIII_1x1_kxk( + k_1x1_kxk_first, + b_1x1_kxk_first, + k_1x1_kxk_second, + b_1x1_kxk_second, + groups=self.groups, + ) + + k_avg = transV_avg(self.out_channels, self.kernel_size, self.groups) + k_1x1_avg_second, b_1x1_avg_second = transI_fusebn(k_avg, self.dbb_avg.avgbn) + if hasattr(self.dbb_avg, "conv"): + k_1x1_avg_first, b_1x1_avg_first = transI_fusebn( + self.dbb_avg.conv.weight, self.dbb_avg.bn + ) + k_1x1_avg_merged, b_1x1_avg_merged = transIII_1x1_kxk( + k_1x1_avg_first, + b_1x1_avg_first, + k_1x1_avg_second, + b_1x1_avg_second, + groups=self.groups, + ) + else: + k_1x1_avg_merged, b_1x1_avg_merged = k_1x1_avg_second, b_1x1_avg_second + + return transII_addbranch( + (k_origin, k_1x1, k_1x1_kxk_merged, k_1x1_avg_merged), + (b_origin, b_1x1, b_1x1_kxk_merged, b_1x1_avg_merged), + ) + + def re_parameterize(self): + if self.is_repped: + return + + kernel, bias = self.get_equivalent_kernel_bias() + self.dbb_reparam = nn.Conv2D( + in_channels=self.dbb_origin.conv._in_channels, + out_channels=self.dbb_origin.conv._out_channels, + kernel_size=self.dbb_origin.conv._kernel_size, + stride=self.dbb_origin.conv._stride, + padding=self.dbb_origin.conv._padding, + dilation=self.dbb_origin.conv._dilation, + groups=self.dbb_origin.conv._groups, + bias_attr=True, + ) + + self.dbb_reparam.weight.set_value(kernel) + self.dbb_reparam.bias.set_value(bias) + + self.__delattr__("dbb_origin") + self.__delattr__("dbb_avg") + if hasattr(self, "dbb_1x1"): + self.__delattr__("dbb_1x1") + self.__delattr__("dbb_1x1_kxk") + self.is_repped = True + + +class Identity(nn.Layer): + def __init__(self): + super(Identity, self).__init__() + + def forward(self, inputs): + return inputs + + +class TheseusLayer(nn.Layer): + def __init__(self, *args, **kwargs): + super().__init__() + self.res_dict = {} + self.res_name = self.full_name() + self.pruner = None + self.quanter = None + + self.init_net(*args, **kwargs) + + def _return_dict_hook(self, layer, input, output): + res_dict = {"logits": output} + # 'list' is needed to avoid error raised by popping self.res_dict + for res_key in list(self.res_dict): + # clear the res_dict because the forward process may change according to input + res_dict[res_key] = self.res_dict.pop(res_key) + return res_dict + + def init_net( + self, + stages_pattern=None, + return_patterns=None, + return_stages=None, + freeze_befor=None, + stop_after=None, + *args, + **kwargs, + ): + # init the output of net + if return_patterns or return_stages: + if return_patterns and return_stages: + msg = f"The 'return_patterns' would be ignored when 'return_stages' is set." + + return_stages = None + + if return_stages is True: + return_patterns = stages_pattern + + # return_stages is int or bool + if type(return_stages) is int: + return_stages = [return_stages] + if isinstance(return_stages, list): + if max(return_stages) > len(stages_pattern) or min(return_stages) < 0: + msg = f"The 'return_stages' set error. Illegal value(s) have been ignored. The stages' pattern list is {stages_pattern}." + + return_stages = [ + val + for val in return_stages + if val >= 0 and val < len(stages_pattern) + ] + return_patterns = [stages_pattern[i] for i in return_stages] + + if return_patterns: + # call update_res function after the __init__ of the object has completed execution, that is, the constructing of layer or model has been completed. + def update_res_hook(layer, input): + self.update_res(return_patterns) + + self.register_forward_pre_hook(update_res_hook) + + # freeze subnet + if freeze_befor is not None: + self.freeze_befor(freeze_befor) + + # set subnet to Identity + if stop_after is not None: + self.stop_after(stop_after) + + def init_res(self, stages_pattern, return_patterns=None, return_stages=None): + + if return_patterns and return_stages: + return_stages = None + + if return_stages is True: + return_patterns = stages_pattern + # return_stages is int or bool + if type(return_stages) is int: + return_stages = [return_stages] + if isinstance(return_stages, list): + if max(return_stages) > len(stages_pattern) or min(return_stages) < 0: + return_stages = [ + val + for val in return_stages + if val >= 0 and val < len(stages_pattern) + ] + return_patterns = [stages_pattern[i] for i in return_stages] + + if return_patterns: + self.update_res(return_patterns) + + def replace_sub(self, *args, **kwargs) -> None: + msg = "The function 'replace_sub()' is deprecated, please use 'upgrade_sublayer()' instead." + raise DeprecationWarning(msg) + + def upgrade_sublayer( + self, + layer_name_pattern: Union[str, List[str]], + handle_func: Callable[[nn.Layer, str], nn.Layer], + ) -> Dict[str, nn.Layer]: + """use 'handle_func' to modify the sub-layer(s) specified by 'layer_name_pattern'. + + Args: + layer_name_pattern (Union[str, List[str]]): The name of layer to be modified by 'handle_func'. + handle_func (Callable[[nn.Layer, str], nn.Layer]): The function to modify target layer specified by 'layer_name_pattern'. The formal params are the layer(nn.Layer) and pattern(str) that is (a member of) layer_name_pattern (when layer_name_pattern is List type). And the return is the layer processed. + + Returns: + Dict[str, nn.Layer]: The key is the pattern and corresponding value is the result returned by 'handle_func()'. + + Examples: + + from paddle import nn + import paddleclas + + def rep_func(layer: nn.Layer, pattern: str): + new_layer = nn.Conv2D( + in_channels=layer._in_channels, + out_channels=layer._out_channels, + kernel_size=5, + padding=2 + ) + return new_layer + + net = paddleclas.MobileNetV1() + res = net.upgrade_sublayer(layer_name_pattern=["blocks[11].depthwise_conv.conv", "blocks[12].depthwise_conv.conv"], handle_func=rep_func) + print(res) + # {'blocks[11].depthwise_conv.conv': the corresponding new_layer, 'blocks[12].depthwise_conv.conv': the corresponding new_layer} + """ + + if not isinstance(layer_name_pattern, list): + layer_name_pattern = [layer_name_pattern] + + hit_layer_pattern_list = [] + for pattern in layer_name_pattern: + # parse pattern to find target layer and its parent + layer_list = parse_pattern_str(pattern=pattern, parent_layer=self) + if not layer_list: + continue + + sub_layer_parent = layer_list[-2]["layer"] if len(layer_list) > 1 else self + sub_layer = layer_list[-1]["layer"] + sub_layer_name = layer_list[-1]["name"] + sub_layer_index_list = layer_list[-1]["index_list"] + + new_sub_layer = handle_func(sub_layer, pattern) + + if sub_layer_index_list: + if len(sub_layer_index_list) > 1: + sub_layer_parent = getattr(sub_layer_parent, sub_layer_name)[ + sub_layer_index_list[0] + ] + for sub_layer_index in sub_layer_index_list[1:-1]: + sub_layer_parent = sub_layer_parent[sub_layer_index] + sub_layer_parent[sub_layer_index_list[-1]] = new_sub_layer + else: + getattr(sub_layer_parent, sub_layer_name)[ + sub_layer_index_list[0] + ] = new_sub_layer + else: + setattr(sub_layer_parent, sub_layer_name, new_sub_layer) + + hit_layer_pattern_list.append(pattern) + return hit_layer_pattern_list + + def stop_after(self, stop_layer_name: str) -> bool: + """stop forward and backward after 'stop_layer_name'. + + Args: + stop_layer_name (str): The name of layer that stop forward and backward after this layer. + + Returns: + bool: 'True' if successful, 'False' otherwise. + """ + + layer_list = parse_pattern_str(stop_layer_name, self) + if not layer_list: + return False + + parent_layer = self + for layer_dict in layer_list: + name, index_list = layer_dict["name"], layer_dict["index_list"] + if not set_identity(parent_layer, name, index_list): + msg = f"Failed to set the layers that after stop_layer_name('{stop_layer_name}') to IdentityLayer. The error layer's name is '{name}'." + return False + parent_layer = layer_dict["layer"] + + return True + + def freeze_befor(self, layer_name: str) -> bool: + """freeze the layer named layer_name and its previous layer. + + Args: + layer_name (str): The name of layer that would be freezed. + + Returns: + bool: 'True' if successful, 'False' otherwise. + """ + + def stop_grad(layer, pattern): + class StopGradLayer(nn.Layer): + def __init__(self): + super().__init__() + self.layer = layer + + def forward(self, x): + x = self.layer(x) + x.stop_gradient = True + return x + + new_layer = StopGradLayer() + return new_layer + + res = self.upgrade_sublayer(layer_name, stop_grad) + if len(res) == 0: + msg = "Failed to stop the gradient before the layer named '{layer_name}'" + return False + return True + + def update_res(self, return_patterns: Union[str, List[str]]) -> Dict[str, nn.Layer]: + """update the result(s) to be returned. + + Args: + return_patterns (Union[str, List[str]]): The name of layer to return output. + + Returns: + Dict[str, nn.Layer]: The pattern(str) and corresponding layer(nn.Layer) that have been set successfully. + """ + + # clear res_dict that could have been set + self.res_dict = {} + + class Handler(object): + def __init__(self, res_dict): + # res_dict is a reference + self.res_dict = res_dict + + def __call__(self, layer, pattern): + layer.res_dict = self.res_dict + layer.res_name = pattern + if hasattr(layer, "hook_remove_helper"): + layer.hook_remove_helper.remove() + layer.hook_remove_helper = layer.register_forward_post_hook( + save_sub_res_hook + ) + return layer + + handle_func = Handler(self.res_dict) + + hit_layer_pattern_list = self.upgrade_sublayer( + return_patterns, handle_func=handle_func + ) + + if hasattr(self, "hook_remove_helper"): + self.hook_remove_helper.remove() + self.hook_remove_helper = self.register_forward_post_hook( + self._return_dict_hook + ) + + return hit_layer_pattern_list + + +def save_sub_res_hook(layer, input, output): + layer.res_dict[layer.res_name] = output + + +def set_identity( + parent_layer: nn.Layer, layer_name: str, layer_index_list: str = None +) -> bool: + """set the layer specified by layer_name and layer_index_list to Identity. + + Args: + parent_layer (nn.Layer): The parent layer of target layer specified by layer_name and layer_index_list. + layer_name (str): The name of target layer to be set to Identity. + layer_index_list (str, optional): The index of target layer to be set to Identity in parent_layer. Defaults to None. + + Returns: + bool: True if successfully, False otherwise. + """ + + stop_after = False + for sub_layer_name in parent_layer._sub_layers: + if stop_after: + parent_layer._sub_layers[sub_layer_name] = Identity() + continue + if sub_layer_name == layer_name: + stop_after = True + + if layer_index_list and stop_after: + layer_container = parent_layer._sub_layers[layer_name] + for num, layer_index in enumerate(layer_index_list): + stop_after = False + for i in range(num): + layer_container = layer_container[layer_index_list[i]] + for sub_layer_index in layer_container._sub_layers: + if stop_after: + parent_layer._sub_layers[layer_name][sub_layer_index] = Identity() + continue + if layer_index == sub_layer_index: + stop_after = True + + return stop_after + + +def parse_pattern_str( + pattern: str, parent_layer: nn.Layer +) -> Union[None, List[Dict[str, Union[nn.Layer, str, None]]]]: + """parse the string type pattern. + + Args: + pattern (str): The pattern to describe layer. + parent_layer (nn.Layer): The root layer relative to the pattern. + + Returns: + Union[None, List[Dict[str, Union[nn.Layer, str, None]]]]: None if failed. If successfully, the members are layers parsed in order: + [ + {"layer": first layer, "name": first layer's name parsed, "index": first layer's index parsed if exist}, + {"layer": second layer, "name": second layer's name parsed, "index": second layer's index parsed if exist}, + ... + ] + """ + + pattern_list = pattern.split(".") + if not pattern_list: + msg = f"The pattern('{pattern}') is illegal. Please check and retry." + return None + + layer_list = [] + while len(pattern_list) > 0: + if "[" in pattern_list[0]: + target_layer_name = pattern_list[0].split("[")[0] + target_layer_index_list = list( + index.split("]")[0] for index in pattern_list[0].split("[")[1:] + ) + else: + target_layer_name = pattern_list[0] + target_layer_index_list = None + + target_layer = getattr(parent_layer, target_layer_name, None) + + if target_layer is None: + msg = f"Not found layer named('{target_layer_name}') specified in pattern('{pattern}')." + return None + + if target_layer_index_list: + for target_layer_index in target_layer_index_list: + if int(target_layer_index) < 0 or int(target_layer_index) >= len( + target_layer + ): + msg = f"Not found layer by index('{target_layer_index}') specified in pattern('{pattern}'). The index should < {len(target_layer)} and > 0." + return None + target_layer = target_layer[target_layer_index] + + layer_list.append( + { + "layer": target_layer, + "name": target_layer_name, + "index_list": target_layer_index_list, + } + ) + + pattern_list = pattern_list[1:] + parent_layer = target_layer + + return layer_list + + +class AdaptiveAvgPool2D(nn.AdaptiveAvgPool2D): + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + + if paddle.device.get_device().startswith("npu"): + self.device = "npu" + else: + self.device = None + + if isinstance(self._output_size, int) and self._output_size == 1: + self._gap = True + elif ( + isinstance(self._output_size, tuple) + and self._output_size[0] == 1 + and self._output_size[1] == 1 + ): + self._gap = True + else: + self._gap = False + + def forward(self, x): + if self.device == "npu" and self._gap: + # Global Average Pooling + N, C, _, _ = x.shape + x_mean = paddle.mean(x, axis=[2, 3]) + x_mean = paddle.reshape(x_mean, [N, C, 1, 1]) + return x_mean + else: + return F.adaptive_avg_pool2d( + x, + output_size=self._output_size, + data_format=self._data_format, + name=self._name, + ) + + +# copyright (c) 2023 PaddlePaddle Authors. All Rights Reserve. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import paddle +import paddle.nn as nn +import paddle.nn.functional as F +from paddle.nn.initializer import KaimingNormal, Constant +from paddle.nn import Conv2D, BatchNorm2D, ReLU, AdaptiveAvgPool2D, MaxPool2D +from paddle.regularizer import L2Decay +from paddle import ParamAttr + +MODEL_URLS = { + "PPHGNetV2_B0": "https://paddle-imagenet-models-name.bj.bcebos.com/dygraph/legendary_models/PPHGNetV2_B0_ssld_pretrained.pdparams", + "PPHGNetV2_B1": "https://paddle-imagenet-models-name.bj.bcebos.com/dygraph/legendary_models/PPHGNetV2_B1_ssld_pretrained.pdparams", + "PPHGNetV2_B2": "https://paddle-imagenet-models-name.bj.bcebos.com/dygraph/legendary_models/PPHGNetV2_B2_ssld_pretrained.pdparams", + "PPHGNetV2_B3": "https://paddle-imagenet-models-name.bj.bcebos.com/dygraph/legendary_models/PPHGNetV2_B3_ssld_pretrained.pdparams", + "PPHGNetV2_B4": "https://paddle-imagenet-models-name.bj.bcebos.com/dygraph/legendary_models/PPHGNetV2_B4_ssld_pretrained.pdparams", + "PPHGNetV2_B5": "https://paddle-imagenet-models-name.bj.bcebos.com/dygraph/legendary_models/PPHGNetV2_B5_ssld_pretrained.pdparams", + "PPHGNetV2_B6": "https://paddle-imagenet-models-name.bj.bcebos.com/dygraph/legendary_models/PPHGNetV2_B6_ssld_pretrained.pdparams", +} + +__all__ = list(MODEL_URLS.keys()) + +kaiming_normal_ = KaimingNormal() +zeros_ = Constant(value=0.0) +ones_ = Constant(value=1.0) + + +class LearnableAffineBlock(TheseusLayer): + """ + Create a learnable affine block module. This module can significantly improve accuracy on smaller models. + + Args: + scale_value (float): The initial value of the scale parameter, default is 1.0. + bias_value (float): The initial value of the bias parameter, default is 0.0. + lr_mult (float): The learning rate multiplier, default is 1.0. + lab_lr (float): The learning rate, default is 0.01. + """ + + def __init__(self, scale_value=1.0, bias_value=0.0, lr_mult=1.0, lab_lr=0.01): + super().__init__() + self.scale = self.create_parameter( + shape=[ + 1, + ], + default_initializer=Constant(value=scale_value), + attr=ParamAttr(learning_rate=lr_mult * lab_lr), + ) + self.add_parameter("scale", self.scale) + self.bias = self.create_parameter( + shape=[ + 1, + ], + default_initializer=Constant(value=bias_value), + attr=ParamAttr(learning_rate=lr_mult * lab_lr), + ) + self.add_parameter("bias", self.bias) + + def forward(self, x): + return self.scale * x + self.bias + + +class ConvBNAct(TheseusLayer): + """ + ConvBNAct is a combination of convolution and batchnorm layers. + + Args: + in_channels (int): Number of input channels. + out_channels (int): Number of output channels. + kernel_size (int): Size of the convolution kernel. Defaults to 3. + stride (int): Stride of the convolution. Defaults to 1. + padding (int/str): Padding or padding type for the convolution. Defaults to 1. + groups (int): Number of groups for the convolution. Defaults to 1. + use_act: (bool): Whether to use activation function. Defaults to True. + use_lab (bool): Whether to use the LAB operation. Defaults to False. + lr_mult (float): Learning rate multiplier for the layer. Defaults to 1.0. + """ + + def __init__( + self, + in_channels, + out_channels, + kernel_size=3, + stride=1, + padding=1, + groups=1, + use_act=True, + use_lab=False, + lr_mult=1.0, + ): + super().__init__() + self.use_act = use_act + self.use_lab = use_lab + self.conv = Conv2D( + in_channels, + out_channels, + kernel_size, + stride, + padding=padding if isinstance(padding, str) else (kernel_size - 1) // 2, + groups=groups, + weight_attr=ParamAttr(learning_rate=lr_mult), + bias_attr=False, + ) + self.bn = BatchNorm2D( + out_channels, + weight_attr=ParamAttr(regularizer=L2Decay(0.0), learning_rate=lr_mult), + bias_attr=ParamAttr(regularizer=L2Decay(0.0), learning_rate=lr_mult), + ) + if self.use_act: + self.act = ReLU() + if self.use_lab: + self.lab = LearnableAffineBlock(lr_mult=lr_mult) + + def forward(self, x): + x = self.conv(x) + x = self.bn(x) + if self.use_act: + x = self.act(x) + if self.use_lab: + x = self.lab(x) + return x + + +class LightConvBNAct(TheseusLayer): + """ + LightConvBNAct is a combination of pw and dw layers. + + Args: + in_channels (int): Number of input channels. + out_channels (int): Number of output channels. + kernel_size (int): Size of the depth-wise convolution kernel. + use_lab (bool): Whether to use the LAB operation. Defaults to False. + lr_mult (float): Learning rate multiplier for the layer. Defaults to 1.0. + """ + + def __init__( + self, + in_channels, + out_channels, + kernel_size, + use_lab=False, + lr_mult=1.0, + **kwargs, + ): + super().__init__() + self.conv1 = ConvBNAct( + in_channels=in_channels, + out_channels=out_channels, + kernel_size=1, + use_act=False, + use_lab=use_lab, + lr_mult=lr_mult, + ) + self.conv2 = ConvBNAct( + in_channels=out_channels, + out_channels=out_channels, + kernel_size=kernel_size, + groups=out_channels, + use_act=True, + use_lab=use_lab, + lr_mult=lr_mult, + ) + + def forward(self, x): + x = self.conv1(x) + x = self.conv2(x) + return x + + +class StemBlock(TheseusLayer): + """ + StemBlock for PP-HGNetV2. + + Args: + in_channels (int): Number of input channels. + mid_channels (int): Number of middle channels. + out_channels (int): Number of output channels. + use_lab (bool): Whether to use the LAB operation. Defaults to False. + lr_mult (float): Learning rate multiplier for the layer. Defaults to 1.0. + """ + + def __init__( + self, in_channels, mid_channels, out_channels, use_lab=False, lr_mult=1.0 + ): + super().__init__() + self.stem1 = ConvBNAct( + in_channels=in_channels, + out_channels=mid_channels, + kernel_size=3, + stride=2, + use_lab=use_lab, + lr_mult=lr_mult, + ) + self.stem2a = ConvBNAct( + in_channels=mid_channels, + out_channels=mid_channels // 2, + kernel_size=2, + stride=1, + padding="SAME", + use_lab=use_lab, + lr_mult=lr_mult, + ) + self.stem2b = ConvBNAct( + in_channels=mid_channels // 2, + out_channels=mid_channels, + kernel_size=2, + stride=1, + padding="SAME", + use_lab=use_lab, + lr_mult=lr_mult, + ) + self.stem3 = ConvBNAct( + in_channels=mid_channels * 2, + out_channels=mid_channels, + kernel_size=3, + stride=2, + use_lab=use_lab, + lr_mult=lr_mult, + ) + self.stem4 = ConvBNAct( + in_channels=mid_channels, + out_channels=out_channels, + kernel_size=1, + stride=1, + use_lab=use_lab, + lr_mult=lr_mult, + ) + self.pool = nn.MaxPool2D( + kernel_size=2, stride=1, ceil_mode=True, padding="SAME" + ) + + def forward(self, x): + x = self.stem1(x) + x2 = self.stem2a(x) + x2 = self.stem2b(x2) + x1 = self.pool(x) + x = paddle.concat([x1, x2], 1) + x = self.stem3(x) + x = self.stem4(x) + + return x + + +class HGV2_Block(TheseusLayer): + """ + HGV2_Block, the basic unit that constitutes the HGV2_Stage. + + Args: + in_channels (int): Number of input channels. + mid_channels (int): Number of middle channels. + out_channels (int): Number of output channels. + kernel_size (int): Size of the convolution kernel. Defaults to 3. + layer_num (int): Number of layers in the HGV2 block. Defaults to 6. + stride (int): Stride of the convolution. Defaults to 1. + padding (int/str): Padding or padding type for the convolution. Defaults to 1. + groups (int): Number of groups for the convolution. Defaults to 1. + use_act (bool): Whether to use activation function. Defaults to True. + use_lab (bool): Whether to use the LAB operation. Defaults to False. + lr_mult (float): Learning rate multiplier for the layer. Defaults to 1.0. + """ + + def __init__( + self, + in_channels, + mid_channels, + out_channels, + kernel_size=3, + layer_num=6, + identity=False, + light_block=True, + use_lab=False, + lr_mult=1.0, + ): + super().__init__() + self.identity = identity + + self.layers = nn.LayerList() + block_type = "LightConvBNAct" if light_block else "ConvBNAct" + for i in range(layer_num): + self.layers.append( + eval(block_type)( + in_channels=in_channels if i == 0 else mid_channels, + out_channels=mid_channels, + stride=1, + kernel_size=kernel_size, + use_lab=use_lab, + lr_mult=lr_mult, + ) + ) + # feature aggregation + total_channels = in_channels + layer_num * mid_channels + self.aggregation_squeeze_conv = ConvBNAct( + in_channels=total_channels, + out_channels=out_channels // 2, + kernel_size=1, + stride=1, + use_lab=use_lab, + lr_mult=lr_mult, + ) + self.aggregation_excitation_conv = ConvBNAct( + in_channels=out_channels // 2, + out_channels=out_channels, + kernel_size=1, + stride=1, + use_lab=use_lab, + lr_mult=lr_mult, + ) + + def forward(self, x): + identity = x + output = [] + output.append(x) + for layer in self.layers: + x = layer(x) + output.append(x) + x = paddle.concat(output, axis=1) + x = self.aggregation_squeeze_conv(x) + x = self.aggregation_excitation_conv(x) + if self.identity: + x += identity + return x + + +class HGV2_Stage(TheseusLayer): + """ + HGV2_Stage, the basic unit that constitutes the PPHGNetV2. + + Args: + in_channels (int): Number of input channels. + mid_channels (int): Number of middle channels. + out_channels (int): Number of output channels. + block_num (int): Number of blocks in the HGV2 stage. + layer_num (int): Number of layers in the HGV2 block. Defaults to 6. + is_downsample (bool): Whether to use downsampling operation. Defaults to False. + light_block (bool): Whether to use light block. Defaults to True. + kernel_size (int): Size of the convolution kernel. Defaults to 3. + use_lab (bool, optional): Whether to use the LAB operation. Defaults to False. + lr_mult (float, optional): Learning rate multiplier for the layer. Defaults to 1.0. + """ + + def __init__( + self, + in_channels, + mid_channels, + out_channels, + block_num, + layer_num=6, + is_downsample=True, + light_block=True, + kernel_size=3, + use_lab=False, + lr_mult=1.0, + ): + + super().__init__() + self.is_downsample = is_downsample + if self.is_downsample: + self.downsample = ConvBNAct( + in_channels=in_channels, + out_channels=in_channels, + kernel_size=3, + stride=2, + groups=in_channels, + use_act=False, + use_lab=use_lab, + lr_mult=lr_mult, + ) + + blocks_list = [] + for i in range(block_num): + blocks_list.append( + HGV2_Block( + in_channels=in_channels if i == 0 else out_channels, + mid_channels=mid_channels, + out_channels=out_channels, + kernel_size=kernel_size, + layer_num=layer_num, + identity=False if i == 0 else True, + light_block=light_block, + use_lab=use_lab, + lr_mult=lr_mult, + ) + ) + self.blocks = nn.Sequential(*blocks_list) + + def forward(self, x): + if self.is_downsample: + x = self.downsample(x) + x = self.blocks(x) + return x + + +class PPHGNetV2(TheseusLayer): + """ + PPHGNetV2 + + Args: + stage_config (dict): Config for PPHGNetV2 stages. such as the number of channels, stride, etc. + stem_channels: (list): Number of channels of the stem of the PPHGNetV2. + use_lab (bool): Whether to use the LAB operation. Defaults to False. + use_last_conv (bool): Whether to use the last conv layer as the output channel. Defaults to True. + class_expand (int): Number of channels for the last 1x1 convolutional layer. + drop_prob (float): Dropout probability for the last 1x1 convolutional layer. Defaults to 0.0. + class_num (int): The number of classes for the classification layer. Defaults to 1000. + lr_mult_list (list): Learning rate multiplier for the stages. Defaults to [1.0, 1.0, 1.0, 1.0, 1.0]. + Returns: + model: nn.Layer. Specific PPHGNetV2 model depends on args. + """ + + def __init__( + self, + stage_config, + stem_channels=[3, 32, 64], + use_lab=False, + use_last_conv=True, + class_expand=2048, + dropout_prob=0.0, + class_num=1000, + lr_mult_list=[1.0, 1.0, 1.0, 1.0, 1.0], + **kwargs, + ): + super().__init__() + self.use_lab = use_lab + self.use_last_conv = use_last_conv + self.class_expand = class_expand + self.class_num = class_num + + # stem + self.stem = StemBlock( + in_channels=stem_channels[0], + mid_channels=stem_channels[1], + out_channels=stem_channels[2], + use_lab=use_lab, + lr_mult=lr_mult_list[0], + ) + + # stages + self.stages = nn.LayerList() + for i, k in enumerate(stage_config): + ( + in_channels, + mid_channels, + out_channels, + block_num, + is_downsample, + light_block, + kernel_size, + layer_num, + ) = stage_config[k] + self.stages.append( + HGV2_Stage( + in_channels, + mid_channels, + out_channels, + block_num, + layer_num, + is_downsample, + light_block, + kernel_size, + use_lab, + lr_mult=lr_mult_list[i + 1], + ) + ) + + self.avg_pool = AdaptiveAvgPool2D(1) + + if self.use_last_conv: + self.last_conv = Conv2D( + in_channels=out_channels, + out_channels=self.class_expand, + kernel_size=1, + stride=1, + padding=0, + bias_attr=False, + ) + self.act = ReLU() + if self.use_lab: + self.lab = LearnableAffineBlock() + self.dropout = nn.Dropout(p=dropout_prob, mode="downscale_in_infer") + + self.flatten = nn.Flatten(start_axis=1, stop_axis=-1) + self.fc = nn.Linear( + self.class_expand if self.use_last_conv else out_channels, self.class_num + ) + + self._init_weights() + + def _init_weights(self): + for m in self.sublayers(): + if isinstance(m, nn.Conv2D): + kaiming_normal_(m.weight) + elif isinstance(m, (nn.BatchNorm2D)): + ones_(m.weight) + zeros_(m.bias) + elif isinstance(m, nn.Linear): + zeros_(m.bias) + + def forward(self, x): + x = self.stem(x) + for stage in self.stages: + x = stage(x) + return x + + +def PPHGNetV2_B0(pretrained=False, use_ssld=False, **kwargs): + """ + PPHGNetV2_B0 + Args: + pretrained (bool/str): If `True` load pretrained parameters, `False` otherwise. + If str, means the path of the pretrained model. + use_ssld (bool) Whether using ssld pretrained model when pretrained is True. + Returns: + model: nn.Layer. Specific `PPHGNetV2_B0` model depends on args. + """ + stage_config = { + # in_channels, mid_channels, out_channels, num_blocks, is_downsample, light_block, kernel_size, layer_num + "stage1": [16, 16, 64, 1, False, False, 3, 3], + "stage2": [64, 32, 256, 1, True, False, 3, 3], + "stage3": [256, 64, 512, 2, True, True, 5, 3], + "stage4": [512, 128, 1024, 1, True, True, 5, 3], + } + + model = PPHGNetV2( + stem_channels=[3, 16, 16], stage_config=stage_config, use_lab=True, **kwargs + ) + return model + + +def PPHGNetV2_B1(pretrained=False, use_ssld=False, **kwargs): + """ + PPHGNetV2_B1 + Args: + pretrained (bool/str): If `True` load pretrained parameters, `False` otherwise. + If str, means the path of the pretrained model. + use_ssld (bool) Whether using ssld pretrained model when pretrained is True. + Returns: + model: nn.Layer. Specific `PPHGNetV2_B1` model depends on args. + """ + stage_config = { + # in_channels, mid_channels, out_channels, num_blocks, is_downsample, light_block, kernel_size, layer_num + "stage1": [32, 32, 64, 1, False, False, 3, 3], + "stage2": [64, 48, 256, 1, True, False, 3, 3], + "stage3": [256, 96, 512, 2, True, True, 5, 3], + "stage4": [512, 192, 1024, 1, True, True, 5, 3], + } + + model = PPHGNetV2( + stem_channels=[3, 24, 32], stage_config=stage_config, use_lab=True, **kwargs + ) + return model + + +def PPHGNetV2_B2(pretrained=False, use_ssld=False, **kwargs): + """ + PPHGNetV2_B2 + Args: + pretrained (bool/str): If `True` load pretrained parameters, `False` otherwise. + If str, means the path of the pretrained model. + use_ssld (bool) Whether using ssld pretrained model when pretrained is True. + Returns: + model: nn.Layer. Specific `PPHGNetV2_B2` model depends on args. + """ + stage_config = { + # in_channels, mid_channels, out_channels, num_blocks, is_downsample, light_block, kernel_size, layer_num + "stage1": [32, 32, 96, 1, False, False, 3, 4], + "stage2": [96, 64, 384, 1, True, False, 3, 4], + "stage3": [384, 128, 768, 3, True, True, 5, 4], + "stage4": [768, 256, 1536, 1, True, True, 5, 4], + } + + model = PPHGNetV2( + stem_channels=[3, 24, 32], stage_config=stage_config, use_lab=True, **kwargs + ) + return model + + +def PPHGNetV2_B3(pretrained=False, use_ssld=False, **kwargs): + """ + PPHGNetV2_B3 + Args: + pretrained (bool/str): If `True` load pretrained parameters, `False` otherwise. + If str, means the path of the pretrained model. + use_ssld (bool) Whether using ssld pretrained model when pretrained is True. + Returns: + model: nn.Layer. Specific `PPHGNetV2_B3` model depends on args. + """ + stage_config = { + # in_channels, mid_channels, out_channels, num_blocks, is_downsample, light_block, kernel_size, layer_num + "stage1": [32, 32, 128, 1, False, False, 3, 5], + "stage2": [128, 64, 512, 1, True, False, 3, 5], + "stage3": [512, 128, 1024, 3, True, True, 5, 5], + "stage4": [1024, 256, 2048, 1, True, True, 5, 5], + } + + model = PPHGNetV2( + stem_channels=[3, 24, 32], stage_config=stage_config, use_lab=True, **kwargs + ) + return model + + +def PPHGNetV2_B5(pretrained=False, use_ssld=False, **kwargs): + """ + PPHGNetV2_B5 + Args: + pretrained (bool/str): If `True` load pretrained parameters, `False` otherwise. + If str, means the path of the pretrained model. + use_ssld (bool) Whether using ssld pretrained model when pretrained is True. + Returns: + model: nn.Layer. Specific `PPHGNetV2_B5` model depends on args. + """ + stage_config = { + # in_channels, mid_channels, out_channels, num_blocks, is_downsample, light_block, kernel_size, layer_num + "stage1": [64, 64, 128, 1, False, False, 3, 6], + "stage2": [128, 128, 512, 2, True, False, 3, 6], + "stage3": [512, 256, 1024, 5, True, True, 5, 6], + "stage4": [1024, 512, 2048, 2, True, True, 5, 6], + } + + model = PPHGNetV2( + stem_channels=[3, 32, 64], stage_config=stage_config, use_lab=False, **kwargs + ) + return model + + +def PPHGNetV2_B6(pretrained=False, use_ssld=False, **kwargs): + """ + PPHGNetV2_B6 + Args: + pretrained (bool/str): If `True` load pretrained parameters, `False` otherwise. + If str, means the path of the pretrained model. + use_ssld (bool) Whether using ssld pretrained model when pretrained is True. + Returns: + model: nn.Layer. Specific `PPHGNetV2_B6` model depends on args. + """ + stage_config = { + # in_channels, mid_channels, out_channels, num_blocks, is_downsample, light_block, kernel_size, layer_num + "stage1": [96, 96, 192, 2, False, False, 3, 6], + "stage2": [192, 192, 512, 3, True, False, 3, 6], + "stage3": [512, 384, 1024, 6, True, True, 5, 6], + "stage4": [1024, 768, 2048, 3, True, True, 5, 6], + } + + model = PPHGNetV2( + stem_channels=[3, 48, 96], stage_config=stage_config, use_lab=False, **kwargs + ) + return model + + +class PPHGNetV2_B4(nn.Layer): + """ + PPHGNetV2_B4 + Args: + in_channels (int): Number of input channels. Default is 3 (for RGB images). + class_num (int): Number of classes for classification. Default is 1000. + Returns: + model: nn.Layer. Specific `PPHGNetV2_B4` model with defined architecture. + """ + + def __init__(self, in_channels=3, class_num=1000): + super().__init__() + self.in_channels = in_channels + self.out_channels = 2048 + stage_config = { + # in_channels, mid_channels, out_channels, num_blocks, is_downsample, light_block, kernel_size, layer_num + "stage1": [48, 48, 128, 1, False, False, 3, 6], + "stage2": [128, 96, 512, 1, True, False, 3, 6], + "stage3": [512, 192, 1024, 3, True, True, 5, 6], + "stage4": [1024, 384, 2048, 1, True, True, 5, 6], + } + + self.pphgnet_b4 = PPHGNetV2( + stem_channels=[3, 32, 48], + stage_config=stage_config, + class_num=class_num, + use_lab=False, + ) + + def forward(self, input_data): + if self.training: + pixel_values, label, attention_mask = input_data + else: + if isinstance(input_data, list): + pixel_values = input_data[0] + else: + pixel_values = input_data + num_channels = pixel_values.shape[1] + if num_channels == 1: + pixel_values = paddle.repeat_interleave(pixel_values, repeats=3, axis=1) + pphgnet_b4_output = self.pphgnet_b4(pixel_values) + b, c, h, w = pphgnet_b4_output.shape + pphgnet_b4_output = pphgnet_b4_output.reshape([b, c, h * w]).transpose( + [0, 2, 1] + ) + pphgnet_b4_output = DonutSwinModelOutput( + last_hidden_state=pphgnet_b4_output, + pooler_output=None, + hidden_states=None, + attentions=False, + reshaped_hidden_states=None, + ) + if self.training: + return pphgnet_b4_output, label, attention_mask + else: + return pphgnet_b4_output diff --git a/modules/onnx_ocr_module/src/ppocr/modeling/backbones/rec_repvit.py b/modules/onnx_ocr_module/src/ppocr/modeling/backbones/rec_repvit.py new file mode 100644 index 0000000..2b3d333 --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/modeling/backbones/rec_repvit.py @@ -0,0 +1,363 @@ +# copyright (c) 2024 PaddlePaddle Authors. All Rights Reserve. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +""" +This code is refer from: +https://github.com/THU-MIG/RepViT +""" + +import paddle.nn as nn +import paddle +from paddle.nn.initializer import TruncatedNormal, Constant, Normal + +trunc_normal_ = TruncatedNormal(std=0.02) +normal_ = Normal +zeros_ = Constant(value=0.0) +ones_ = Constant(value=1.0) + + +def _make_divisible(v, divisor, min_value=None): + """ + This function is taken from the original tf repo. + It ensures that all layers have a channel number that is divisible by 8 + It can be seen here: + https://github.com/tensorflow/models/blob/master/research/slim/nets/mobilenet/mobilenet.py + :param v: + :param divisor: + :param min_value: + :return: + """ + if min_value is None: + min_value = divisor + new_v = max(min_value, int(v + divisor / 2) // divisor * divisor) + # Make sure that round down does not go down by more than 10%. + if new_v < 0.9 * v: + new_v += divisor + return new_v + + +# from timm.models.layers import SqueezeExcite + + +def make_divisible(v, divisor=8, min_value=None, round_limit=0.9): + min_value = min_value or divisor + new_v = max(min_value, int(v + divisor / 2) // divisor * divisor) + # Make sure that round down does not go down by more than 10%. + if new_v < round_limit * v: + new_v += divisor + return new_v + + +class SEModule(nn.Layer): + """SE Module as defined in original SE-Nets with a few additions + Additions include: + * divisor can be specified to keep channels % div == 0 (default: 8) + * reduction channels can be specified directly by arg (if rd_channels is set) + * reduction channels can be specified by float rd_ratio (default: 1/16) + * global max pooling can be added to the squeeze aggregation + * customizable activation, normalization, and gate layer + """ + + def __init__( + self, + channels, + rd_ratio=1.0 / 16, + rd_channels=None, + rd_divisor=8, + act_layer=nn.ReLU, + ): + super(SEModule, self).__init__() + if not rd_channels: + rd_channels = make_divisible( + channels * rd_ratio, rd_divisor, round_limit=0.0 + ) + self.fc1 = nn.Conv2D(channels, rd_channels, kernel_size=1, bias_attr=True) + self.act = act_layer() + self.fc2 = nn.Conv2D(rd_channels, channels, kernel_size=1, bias_attr=True) + + def forward(self, x): + x_se = x.mean((2, 3), keepdim=True) + x_se = self.fc1(x_se) + x_se = self.act(x_se) + x_se = self.fc2(x_se) + return x * nn.functional.sigmoid(x_se) + + +class Conv2D_BN(nn.Sequential): + def __init__( + self, + a, + b, + ks=1, + stride=1, + pad=0, + dilation=1, + groups=1, + bn_weight_init=1, + resolution=-10000, + ): + super().__init__() + self.add_sublayer( + "c", nn.Conv2D(a, b, ks, stride, pad, dilation, groups, bias_attr=False) + ) + self.add_sublayer("bn", nn.BatchNorm2D(b)) + if bn_weight_init == 1: + ones_(self.bn.weight) + else: + zeros_(self.bn.weight) + zeros_(self.bn.bias) + + @paddle.no_grad() + def fuse(self): + c, bn = self.c, self.bn + w = bn.weight / (bn._variance + bn._epsilon) ** 0.5 + w = c.weight * w[:, None, None, None] + b = bn.bias - bn._mean * bn.weight / (bn._variance + bn._epsilon) ** 0.5 + m = nn.Conv2D( + w.shape[1] * self.c._groups, + w.shape[0], + w.shape[2:], + stride=self.c._stride, + padding=self.c._padding, + dilation=self.c._dilation, + groups=self.c._groups, + ) + m.weight.set_value(w) + m.bias.set_value(b) + return m + + +class Residual(nn.Layer): + def __init__(self, m, drop=0.0): + super().__init__() + self.m = m + self.drop = drop + + def forward(self, x): + if self.training and self.drop > 0: + return ( + x + + self.m(x) + * paddle.rand(x.size(0), 1, 1, 1) + .ge_(self.drop) + .div(1 - self.drop) + .detach() + ) + else: + return x + self.m(x) + + @paddle.no_grad() + def fuse(self): + if isinstance(self.m, Conv2D_BN): + m = self.m.fuse() + assert m._groups == m.in_channels + identity = paddle.ones([m.weight.shape[0], m.weight.shape[1], 1, 1]) + identity = nn.functional.pad(identity, [1, 1, 1, 1]) + m.weight += identity + return m + elif isinstance(self.m, nn.Conv2D): + m = self.m + assert m._groups != m.in_channels + identity = paddle.ones([m.weight.shape[0], m.weight.shape[1], 1, 1]) + identity = nn.functional.pad(identity, [1, 1, 1, 1]) + m.weight += identity + return m + else: + return self + + +class RepVGGDW(nn.Layer): + def __init__(self, ed) -> None: + super().__init__() + self.conv = Conv2D_BN(ed, ed, 3, 1, 1, groups=ed) + self.conv1 = nn.Conv2D(ed, ed, 1, 1, 0, groups=ed) + self.dim = ed + self.bn = nn.BatchNorm2D(ed) + + def forward(self, x): + return self.bn((self.conv(x) + self.conv1(x)) + x) + + @paddle.no_grad() + def fuse(self): + conv = self.conv.fuse() + conv1 = self.conv1 + + conv_w = conv.weight + conv_b = conv.bias + conv1_w = conv1.weight + conv1_b = conv1.bias + + conv1_w = nn.functional.pad(conv1_w, [1, 1, 1, 1]) + + identity = nn.functional.pad( + paddle.ones([conv1_w.shape[0], conv1_w.shape[1], 1, 1]), [1, 1, 1, 1] + ) + + final_conv_w = conv_w + conv1_w + identity + final_conv_b = conv_b + conv1_b + + conv.weight.set_value(final_conv_w) + conv.bias.set_value(final_conv_b) + + bn = self.bn + w = bn.weight / (bn._variance + bn._epsilon) ** 0.5 + w = conv.weight * w[:, None, None, None] + b = ( + bn.bias + + (conv.bias - bn._mean) * bn.weight / (bn._variance + bn._epsilon) ** 0.5 + ) + conv.weight.set_value(w) + conv.bias.set_value(b) + return conv + + +class RepViTBlock(nn.Layer): + def __init__(self, inp, hidden_dim, oup, kernel_size, stride, use_se, use_hs): + super(RepViTBlock, self).__init__() + + self.identity = stride == 1 and inp == oup + assert hidden_dim == 2 * inp + + if stride != 1: + self.token_mixer = nn.Sequential( + Conv2D_BN( + inp, inp, kernel_size, stride, (kernel_size - 1) // 2, groups=inp + ), + SEModule(inp, 0.25) if use_se else nn.Identity(), + Conv2D_BN(inp, oup, ks=1, stride=1, pad=0), + ) + self.channel_mixer = Residual( + nn.Sequential( + # pw + Conv2D_BN(oup, 2 * oup, 1, 1, 0), + nn.GELU() if use_hs else nn.GELU(), + # pw-linear + Conv2D_BN(2 * oup, oup, 1, 1, 0, bn_weight_init=0), + ) + ) + else: + assert self.identity + self.token_mixer = nn.Sequential( + RepVGGDW(inp), + SEModule(inp, 0.25) if use_se else nn.Identity(), + ) + self.channel_mixer = Residual( + nn.Sequential( + # pw + Conv2D_BN(inp, hidden_dim, 1, 1, 0), + nn.GELU() if use_hs else nn.GELU(), + # pw-linear + Conv2D_BN(hidden_dim, oup, 1, 1, 0, bn_weight_init=0), + ) + ) + + def forward(self, x): + return self.channel_mixer(self.token_mixer(x)) + + +class RepViT(nn.Layer): + def __init__(self, cfgs, in_channels=3, out_indices=None): + super(RepViT, self).__init__() + # setting of inverted residual blocks + self.cfgs = cfgs + + # building first layer + input_channel = self.cfgs[0][2] + patch_embed = nn.Sequential( + Conv2D_BN(in_channels, input_channel // 2, 3, 2, 1), + nn.GELU(), + Conv2D_BN(input_channel // 2, input_channel, 3, 2, 1), + ) + layers = [patch_embed] + # building inverted residual blocks + block = RepViTBlock + for k, t, c, use_se, use_hs, s in self.cfgs: + output_channel = _make_divisible(c, 8) + exp_size = _make_divisible(input_channel * t, 8) + layers.append( + block(input_channel, exp_size, output_channel, k, s, use_se, use_hs) + ) + input_channel = output_channel + self.features = nn.LayerList(layers) + self.out_indices = out_indices + if out_indices is not None: + self.out_channels = [self.cfgs[ids - 1][2] for ids in out_indices] + else: + self.out_channels = self.cfgs[-1][2] + + def forward(self, x): + if self.out_indices is not None: + return self.forward_det(x) + return self.forward_rec(x) + + def forward_det(self, x): + outs = [] + for i, f in enumerate(self.features): + x = f(x) + if i in self.out_indices: + outs.append(x) + return outs + + def forward_rec(self, x): + for f in self.features: + x = f(x) + h = x.shape[2] + x = nn.functional.avg_pool2d(x, [h, 2]) + return x + + +def RepSVTR(in_channels=3): + """ + Constructs a MobileNetV3-Large model + """ + # k, t, c, SE, HS, s + cfgs = [ + [3, 2, 96, 1, 0, 1], + [3, 2, 96, 0, 0, 1], + [3, 2, 96, 0, 0, 1], + [3, 2, 192, 0, 1, (2, 1)], + [3, 2, 192, 1, 1, 1], + [3, 2, 192, 0, 1, 1], + [3, 2, 192, 1, 1, 1], + [3, 2, 192, 0, 1, 1], + [3, 2, 192, 1, 1, 1], + [3, 2, 192, 0, 1, 1], + [3, 2, 384, 0, 1, (2, 1)], + [3, 2, 384, 1, 1, 1], + [3, 2, 384, 0, 1, 1], + ] + return RepViT(cfgs, in_channels=in_channels) + + +def RepSVTR_det(in_channels=3, out_indices=[2, 5, 10, 13]): + """ + Constructs a MobileNetV3-Large model + """ + # k, t, c, SE, HS, s + cfgs = [ + [3, 2, 48, 1, 0, 1], + [3, 2, 48, 0, 0, 1], + [3, 2, 96, 0, 0, 2], + [3, 2, 96, 1, 0, 1], + [3, 2, 96, 0, 0, 1], + [3, 2, 192, 0, 1, 2], + [3, 2, 192, 1, 1, 1], + [3, 2, 192, 0, 1, 1], + [3, 2, 192, 1, 1, 1], + [3, 2, 192, 0, 1, 1], + [3, 2, 384, 0, 1, 2], + [3, 2, 384, 1, 1, 1], + [3, 2, 384, 0, 1, 1], + ] + return RepViT(cfgs, in_channels=in_channels, out_indices=out_indices) diff --git a/modules/onnx_ocr_module/src/ppocr/modeling/backbones/rec_resnet_31.py b/modules/onnx_ocr_module/src/ppocr/modeling/backbones/rec_resnet_31.py new file mode 100644 index 0000000..c4aa4fc --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/modeling/backbones/rec_resnet_31.py @@ -0,0 +1,318 @@ +# copyright (c) 2021 PaddlePaddle Authors. All Rights Reserve. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +""" +This code is refer from: +https://github.com/open-mmlab/mmocr/blob/main/mmocr/models/textrecog/layers/conv_layer.py +https://github.com/open-mmlab/mmocr/blob/main/mmocr/models/textrecog/backbones/resnet31_ocr.py +""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import paddle +from paddle import ParamAttr +import paddle.nn as nn +import paddle.nn.functional as F +import numpy as np + +__all__ = ["ResNet31"] + + +def conv3x3(in_channel, out_channel, stride=1, conv_weight_attr=None): + return nn.Conv2D( + in_channel, + out_channel, + kernel_size=3, + stride=stride, + padding=1, + weight_attr=conv_weight_attr, + bias_attr=False, + ) + + +class BasicBlock(nn.Layer): + expansion = 1 + + def __init__( + self, + in_channels, + channels, + stride=1, + downsample=False, + conv_weight_attr=None, + bn_weight_attr=None, + ): + super().__init__() + self.conv1 = conv3x3( + in_channels, channels, stride, conv_weight_attr=conv_weight_attr + ) + self.bn1 = nn.BatchNorm2D(channels, weight_attr=bn_weight_attr) + self.relu = nn.ReLU() + self.conv2 = conv3x3(channels, channels, conv_weight_attr=conv_weight_attr) + self.bn2 = nn.BatchNorm2D(channels, weight_attr=bn_weight_attr) + self.downsample = downsample + if downsample: + self.downsample = nn.Sequential( + nn.Conv2D( + in_channels, + channels * self.expansion, + 1, + stride, + weight_attr=conv_weight_attr, + bias_attr=False, + ), + nn.BatchNorm2D(channels * self.expansion, weight_attr=bn_weight_attr), + ) + else: + self.downsample = nn.Sequential() + self.stride = stride + + def forward(self, x): + residual = x + + out = self.conv1(x) + out = self.bn1(out) + out = self.relu(out) + + out = self.conv2(out) + out = self.bn2(out) + + if self.downsample: + residual = self.downsample(x) + + out += residual + out = self.relu(out) + + return out + + +class ResNet31(nn.Layer): + """ + Args: + in_channels (int): Number of channels of input image tensor. + layers (list[int]): List of BasicBlock number for each stage. + channels (list[int]): List of out_channels of Conv2d layer. + out_indices (None | Sequence[int]): Indices of output stages. + last_stage_pool (bool): If True, add `MaxPool2d` layer to last stage. + init_type (None | str): the config to control the initialization. + """ + + def __init__( + self, + in_channels=3, + layers=[1, 2, 5, 3], + channels=[64, 128, 256, 256, 512, 512, 512], + out_indices=None, + last_stage_pool=False, + init_type=None, + ): + super(ResNet31, self).__init__() + assert isinstance(in_channels, int) + assert isinstance(last_stage_pool, bool) + + self.out_indices = out_indices + self.last_stage_pool = last_stage_pool + + conv_weight_attr = None + bn_weight_attr = None + + if init_type is not None: + support_dict = ["KaimingNormal"] + assert init_type in support_dict, Exception( + "resnet31 only support {}".format(support_dict) + ) + conv_weight_attr = nn.initializer.KaimingNormal() + bn_weight_attr = ParamAttr( + initializer=nn.initializer.Uniform(), learning_rate=1 + ) + + # conv 1 (Conv Conv) + self.conv1_1 = nn.Conv2D( + in_channels, + channels[0], + kernel_size=3, + stride=1, + padding=1, + weight_attr=conv_weight_attr, + ) + self.bn1_1 = nn.BatchNorm2D(channels[0], weight_attr=bn_weight_attr) + self.relu1_1 = nn.ReLU() + + self.conv1_2 = nn.Conv2D( + channels[0], + channels[1], + kernel_size=3, + stride=1, + padding=1, + weight_attr=conv_weight_attr, + ) + self.bn1_2 = nn.BatchNorm2D(channels[1], weight_attr=bn_weight_attr) + self.relu1_2 = nn.ReLU() + + # conv 2 (Max-pooling, Residual block, Conv) + self.pool2 = nn.MaxPool2D(kernel_size=2, stride=2, padding=0, ceil_mode=True) + self.block2 = self._make_layer( + channels[1], + channels[2], + layers[0], + conv_weight_attr=conv_weight_attr, + bn_weight_attr=bn_weight_attr, + ) + self.conv2 = nn.Conv2D( + channels[2], + channels[2], + kernel_size=3, + stride=1, + padding=1, + weight_attr=conv_weight_attr, + ) + self.bn2 = nn.BatchNorm2D(channels[2], weight_attr=bn_weight_attr) + self.relu2 = nn.ReLU() + + # conv 3 (Max-pooling, Residual block, Conv) + self.pool3 = nn.MaxPool2D(kernel_size=2, stride=2, padding=0, ceil_mode=True) + self.block3 = self._make_layer( + channels[2], + channels[3], + layers[1], + conv_weight_attr=conv_weight_attr, + bn_weight_attr=bn_weight_attr, + ) + self.conv3 = nn.Conv2D( + channels[3], + channels[3], + kernel_size=3, + stride=1, + padding=1, + weight_attr=conv_weight_attr, + ) + self.bn3 = nn.BatchNorm2D(channels[3], weight_attr=bn_weight_attr) + self.relu3 = nn.ReLU() + + # conv 4 (Max-pooling, Residual block, Conv) + self.pool4 = nn.MaxPool2D( + kernel_size=(2, 1), stride=(2, 1), padding=0, ceil_mode=True + ) + self.block4 = self._make_layer( + channels[3], + channels[4], + layers[2], + conv_weight_attr=conv_weight_attr, + bn_weight_attr=bn_weight_attr, + ) + self.conv4 = nn.Conv2D( + channels[4], + channels[4], + kernel_size=3, + stride=1, + padding=1, + weight_attr=conv_weight_attr, + ) + self.bn4 = nn.BatchNorm2D(channels[4], weight_attr=bn_weight_attr) + self.relu4 = nn.ReLU() + + # conv 5 ((Max-pooling), Residual block, Conv) + self.pool5 = None + if self.last_stage_pool: + self.pool5 = nn.MaxPool2D( + kernel_size=2, stride=2, padding=0, ceil_mode=True + ) + self.block5 = self._make_layer( + channels[4], + channels[5], + layers[3], + conv_weight_attr=conv_weight_attr, + bn_weight_attr=bn_weight_attr, + ) + self.conv5 = nn.Conv2D( + channels[5], + channels[5], + kernel_size=3, + stride=1, + padding=1, + weight_attr=conv_weight_attr, + ) + self.bn5 = nn.BatchNorm2D(channels[5], weight_attr=bn_weight_attr) + self.relu5 = nn.ReLU() + + self.out_channels = channels[-1] + + def _make_layer( + self, + input_channels, + output_channels, + blocks, + conv_weight_attr=None, + bn_weight_attr=None, + ): + layers = [] + for _ in range(blocks): + downsample = None + if input_channels != output_channels: + downsample = nn.Sequential( + nn.Conv2D( + input_channels, + output_channels, + kernel_size=1, + stride=1, + weight_attr=conv_weight_attr, + bias_attr=False, + ), + nn.BatchNorm2D(output_channels, weight_attr=bn_weight_attr), + ) + + layers.append( + BasicBlock( + input_channels, + output_channels, + downsample=downsample, + conv_weight_attr=conv_weight_attr, + bn_weight_attr=bn_weight_attr, + ) + ) + input_channels = output_channels + return nn.Sequential(*layers) + + def forward(self, x): + x = self.conv1_1(x) + x = self.bn1_1(x) + x = self.relu1_1(x) + + x = self.conv1_2(x) + x = self.bn1_2(x) + x = self.relu1_2(x) + + outs = [] + for i in range(4): + layer_index = i + 2 + pool_layer = getattr(self, f"pool{layer_index}") + block_layer = getattr(self, f"block{layer_index}") + conv_layer = getattr(self, f"conv{layer_index}") + bn_layer = getattr(self, f"bn{layer_index}") + relu_layer = getattr(self, f"relu{layer_index}") + + if pool_layer is not None: + x = pool_layer(x) + x = block_layer(x) + x = conv_layer(x) + x = bn_layer(x) + x = relu_layer(x) + + outs.append(x) + + if self.out_indices is not None: + return tuple([outs[i] for i in self.out_indices]) + + return x diff --git a/modules/onnx_ocr_module/src/ppocr/modeling/backbones/rec_resnet_32.py b/modules/onnx_ocr_module/src/ppocr/modeling/backbones/rec_resnet_32.py new file mode 100644 index 0000000..63d78d3 --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/modeling/backbones/rec_resnet_32.py @@ -0,0 +1,305 @@ +# copyright (c) 2022 PaddlePaddle Authors. All Rights Reserve. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +""" +This code is refer from: +https://github.com/hikopensource/DAVAR-Lab-OCR/davarocr/davar_rcg/models/backbones/ResNet32.py +""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import paddle.nn as nn + +__all__ = ["ResNet32"] + +conv_weight_attr = nn.initializer.KaimingNormal() + + +class ResNet32(nn.Layer): + """ + Feature Extractor is proposed in FAN Ref [1] + + Ref [1]: Focusing Attention: Towards Accurate Text Recognition in Neural Images ICCV-2017 + """ + + def __init__(self, in_channels, out_channels=512): + """ + + Args: + in_channels (int): input channel + output_channel (int): output channel + """ + super(ResNet32, self).__init__() + self.out_channels = out_channels + self.ConvNet = ResNet(in_channels, out_channels, BasicBlock, [1, 2, 5, 3]) + + def forward(self, inputs): + """ + Args: + inputs: input feature + + Returns: + output feature + + """ + return self.ConvNet(inputs) + + +class BasicBlock(nn.Layer): + """Res-net Basic Block""" + + expansion = 1 + + def __init__( + self, inplanes, planes, stride=1, downsample=None, norm_type="BN", **kwargs + ): + """ + Args: + inplanes (int): input channel + planes (int): channels of the middle feature + stride (int): stride of the convolution + downsample (int): type of the down_sample + norm_type (str): type of the normalization + **kwargs (None): backup parameter + """ + super(BasicBlock, self).__init__() + self.conv1 = self._conv3x3(inplanes, planes) + self.bn1 = nn.BatchNorm2D(planes) + self.conv2 = self._conv3x3(planes, planes) + self.bn2 = nn.BatchNorm2D(planes) + self.relu = nn.ReLU() + self.downsample = downsample + self.stride = stride + + def _conv3x3(self, in_planes, out_planes, stride=1): + """ + + Args: + in_planes (int): input channel + out_planes (int): channels of the middle feature + stride (int): stride of the convolution + Returns: + nn.Layer: Conv2D with kernel = 3 + + """ + + return nn.Conv2D( + in_planes, + out_planes, + kernel_size=3, + stride=stride, + padding=1, + weight_attr=conv_weight_attr, + bias_attr=False, + ) + + def forward(self, x): + residual = x + + out = self.conv1(x) + out = self.bn1(out) + out = self.relu(out) + + out = self.conv2(out) + out = self.bn2(out) + + if self.downsample is not None: + residual = self.downsample(x) + out += residual + out = self.relu(out) + + return out + + +class ResNet(nn.Layer): + """Res-Net network structure""" + + def __init__(self, input_channel, output_channel, block, layers): + """ + + Args: + input_channel (int): input channel + output_channel (int): output channel + block (BasicBlock): convolution block + layers (list): layers of the block + """ + super(ResNet, self).__init__() + + self.output_channel_block = [ + int(output_channel / 4), + int(output_channel / 2), + output_channel, + output_channel, + ] + + self.inplanes = int(output_channel / 8) + self.conv0_1 = nn.Conv2D( + input_channel, + int(output_channel / 16), + kernel_size=3, + stride=1, + padding=1, + weight_attr=conv_weight_attr, + bias_attr=False, + ) + self.bn0_1 = nn.BatchNorm2D(int(output_channel / 16)) + self.conv0_2 = nn.Conv2D( + int(output_channel / 16), + self.inplanes, + kernel_size=3, + stride=1, + padding=1, + weight_attr=conv_weight_attr, + bias_attr=False, + ) + self.bn0_2 = nn.BatchNorm2D(self.inplanes) + self.relu = nn.ReLU() + + self.maxpool1 = nn.MaxPool2D(kernel_size=2, stride=2, padding=0) + self.layer1 = self._make_layer(block, self.output_channel_block[0], layers[0]) + self.conv1 = nn.Conv2D( + self.output_channel_block[0], + self.output_channel_block[0], + kernel_size=3, + stride=1, + padding=1, + weight_attr=conv_weight_attr, + bias_attr=False, + ) + self.bn1 = nn.BatchNorm2D(self.output_channel_block[0]) + + self.maxpool2 = nn.MaxPool2D(kernel_size=2, stride=2, padding=0) + self.layer2 = self._make_layer( + block, self.output_channel_block[1], layers[1], stride=1 + ) + self.conv2 = nn.Conv2D( + self.output_channel_block[1], + self.output_channel_block[1], + kernel_size=3, + stride=1, + padding=1, + weight_attr=conv_weight_attr, + bias_attr=False, + ) + self.bn2 = nn.BatchNorm2D(self.output_channel_block[1]) + + self.maxpool3 = nn.MaxPool2D(kernel_size=2, stride=(2, 1), padding=(0, 1)) + self.layer3 = self._make_layer( + block, self.output_channel_block[2], layers[2], stride=1 + ) + self.conv3 = nn.Conv2D( + self.output_channel_block[2], + self.output_channel_block[2], + kernel_size=3, + stride=1, + padding=1, + weight_attr=conv_weight_attr, + bias_attr=False, + ) + self.bn3 = nn.BatchNorm2D(self.output_channel_block[2]) + + self.layer4 = self._make_layer( + block, self.output_channel_block[3], layers[3], stride=1 + ) + self.conv4_1 = nn.Conv2D( + self.output_channel_block[3], + self.output_channel_block[3], + kernel_size=2, + stride=(2, 1), + padding=(0, 1), + weight_attr=conv_weight_attr, + bias_attr=False, + ) + self.bn4_1 = nn.BatchNorm2D(self.output_channel_block[3]) + self.conv4_2 = nn.Conv2D( + self.output_channel_block[3], + self.output_channel_block[3], + kernel_size=2, + stride=1, + padding=0, + weight_attr=conv_weight_attr, + bias_attr=False, + ) + self.bn4_2 = nn.BatchNorm2D(self.output_channel_block[3]) + + def _make_layer(self, block, planes, blocks, stride=1): + """ + + Args: + block (block): convolution block + planes (int): input channels + blocks (list): layers of the block + stride (int): stride of the convolution + + Returns: + nn.Sequential: the combination of the convolution block + + """ + downsample = None + if stride != 1 or self.inplanes != planes * block.expansion: + downsample = nn.Sequential( + nn.Conv2D( + self.inplanes, + planes * block.expansion, + kernel_size=1, + stride=stride, + weight_attr=conv_weight_attr, + bias_attr=False, + ), + nn.BatchNorm2D(planes * block.expansion), + ) + + layers = list() + layers.append(block(self.inplanes, planes, stride, downsample)) + self.inplanes = planes * block.expansion + for _ in range(1, blocks): + layers.append(block(self.inplanes, planes)) + + return nn.Sequential(*layers) + + def forward(self, x): + x = self.conv0_1(x) + x = self.bn0_1(x) + x = self.relu(x) + x = self.conv0_2(x) + x = self.bn0_2(x) + x = self.relu(x) + + x = self.maxpool1(x) + x = self.layer1(x) + x = self.conv1(x) + x = self.bn1(x) + x = self.relu(x) + + x = self.maxpool2(x) + x = self.layer2(x) + x = self.conv2(x) + x = self.bn2(x) + x = self.relu(x) + + x = self.maxpool3(x) + x = self.layer3(x) + x = self.conv3(x) + x = self.bn3(x) + x = self.relu(x) + + x = self.layer4(x) + x = self.conv4_1(x) + x = self.bn4_1(x) + x = self.relu(x) + x = self.conv4_2(x) + x = self.bn4_2(x) + x = self.relu(x) + return x diff --git a/modules/onnx_ocr_module/src/ppocr/modeling/backbones/rec_resnet_45.py b/modules/onnx_ocr_module/src/ppocr/modeling/backbones/rec_resnet_45.py new file mode 100644 index 0000000..914d972 --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/modeling/backbones/rec_resnet_45.py @@ -0,0 +1,150 @@ +# copyright (c) 2021 PaddlePaddle Authors. All Rights Reserve. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +""" +This code is refer from: +https://github.com/FangShancheng/ABINet/tree/main/modules +""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import paddle +from paddle import ParamAttr +from paddle.nn.initializer import KaimingNormal +import paddle.nn as nn +import paddle.nn.functional as F +import numpy as np +import math + +__all__ = ["ResNet45"] + + +def conv1x1(in_planes, out_planes, stride=1): + return nn.Conv2D( + in_planes, + out_planes, + kernel_size=1, + stride=1, + weight_attr=ParamAttr(initializer=KaimingNormal()), + bias_attr=False, + ) + + +def conv3x3(in_channel, out_channel, stride=1): + return nn.Conv2D( + in_channel, + out_channel, + kernel_size=3, + stride=stride, + padding=1, + weight_attr=ParamAttr(initializer=KaimingNormal()), + bias_attr=False, + ) + + +class BasicBlock(nn.Layer): + expansion = 1 + + def __init__(self, in_channels, channels, stride=1, downsample=None): + super().__init__() + self.conv1 = conv1x1(in_channels, channels) + self.bn1 = nn.BatchNorm2D(channels) + self.relu = nn.ReLU() + self.conv2 = conv3x3(channels, channels, stride) + self.bn2 = nn.BatchNorm2D(channels) + self.downsample = downsample + self.stride = stride + + def forward(self, x): + residual = x + + out = self.conv1(x) + out = self.bn1(out) + out = self.relu(out) + + out = self.conv2(out) + out = self.bn2(out) + + if self.downsample is not None: + residual = self.downsample(x) + out += residual + out = self.relu(out) + + return out + + +class ResNet45(nn.Layer): + def __init__( + self, + in_channels=3, + block=BasicBlock, + layers=[3, 4, 6, 6, 3], + strides=[2, 1, 2, 1, 1], + ): + self.inplanes = 32 + super(ResNet45, self).__init__() + self.conv1 = nn.Conv2D( + in_channels, + 32, + kernel_size=3, + stride=1, + padding=1, + weight_attr=ParamAttr(initializer=KaimingNormal()), + bias_attr=False, + ) + self.bn1 = nn.BatchNorm2D(32) + self.relu = nn.ReLU() + + self.layer1 = self._make_layer(block, 32, layers[0], stride=strides[0]) + self.layer2 = self._make_layer(block, 64, layers[1], stride=strides[1]) + self.layer3 = self._make_layer(block, 128, layers[2], stride=strides[2]) + self.layer4 = self._make_layer(block, 256, layers[3], stride=strides[3]) + self.layer5 = self._make_layer(block, 512, layers[4], stride=strides[4]) + self.out_channels = 512 + + def _make_layer(self, block, planes, blocks, stride=1): + downsample = None + if stride != 1 or self.inplanes != planes * block.expansion: + # downsample = True + downsample = nn.Sequential( + nn.Conv2D( + self.inplanes, + planes * block.expansion, + kernel_size=1, + stride=stride, + weight_attr=ParamAttr(initializer=KaimingNormal()), + bias_attr=False, + ), + nn.BatchNorm2D(planes * block.expansion), + ) + + layers = [] + layers.append(block(self.inplanes, planes, stride, downsample)) + self.inplanes = planes * block.expansion + for i in range(1, blocks): + layers.append(block(self.inplanes, planes)) + + return nn.Sequential(*layers) + + def forward(self, x): + x = self.conv1(x) + x = self.bn1(x) + x = self.relu(x) + x = self.layer1(x) + x = self.layer2(x) + x = self.layer3(x) + x = self.layer4(x) + x = self.layer5(x) + return x diff --git a/modules/onnx_ocr_module/src/ppocr/modeling/backbones/rec_resnet_aster.py b/modules/onnx_ocr_module/src/ppocr/modeling/backbones/rec_resnet_aster.py new file mode 100644 index 0000000..9b5a15e --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/modeling/backbones/rec_resnet_aster.py @@ -0,0 +1,141 @@ +# copyright (c) 2020 PaddlePaddle Authors. All Rights Reserve. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +""" +This code is refer from: +https://github.com/ayumiymk/aster.pytorch/blob/master/lib/models/resnet_aster.py +""" +import paddle +import paddle.nn as nn + +import sys +import math + + +def conv3x3(in_planes, out_planes, stride=1): + """3x3 convolution with padding""" + return nn.Conv2D( + in_planes, out_planes, kernel_size=3, stride=stride, padding=1, bias_attr=False + ) + + +def conv1x1(in_planes, out_planes, stride=1): + """1x1 convolution""" + return nn.Conv2D( + in_planes, out_planes, kernel_size=1, stride=stride, bias_attr=False + ) + + +def get_sinusoid_encoding(n_position, feat_dim, wave_length=10000): + # [n_position] + positions = paddle.arange(0, n_position) + # [feat_dim] + dim_range = paddle.arange(0, feat_dim) + dim_range = paddle.pow(wave_length, 2 * (dim_range // 2) / feat_dim) + # [n_position, feat_dim] + angles = paddle.unsqueeze(positions, axis=1) / paddle.unsqueeze(dim_range, axis=0) + angles = paddle.cast(angles, "float32") + angles[:, 0::2] = paddle.sin(angles[:, 0::2]) + angles[:, 1::2] = paddle.cos(angles[:, 1::2]) + return angles + + +class AsterBlock(nn.Layer): + def __init__(self, inplanes, planes, stride=1, downsample=None): + super(AsterBlock, self).__init__() + self.conv1 = conv1x1(inplanes, planes, stride) + self.bn1 = nn.BatchNorm2D(planes) + self.relu = nn.ReLU() + self.conv2 = conv3x3(planes, planes) + self.bn2 = nn.BatchNorm2D(planes) + self.downsample = downsample + self.stride = stride + + def forward(self, x): + residual = x + out = self.conv1(x) + out = self.bn1(out) + out = self.relu(out) + out = self.conv2(out) + out = self.bn2(out) + + if self.downsample is not None: + residual = self.downsample(x) + out += residual + out = self.relu(out) + return out + + +class ResNet_ASTER(nn.Layer): + """For aster or crnn""" + + def __init__(self, with_lstm=True, n_group=1, in_channels=3): + super(ResNet_ASTER, self).__init__() + self.with_lstm = with_lstm + self.n_group = n_group + + self.layer0 = nn.Sequential( + nn.Conv2D( + in_channels, + 32, + kernel_size=(3, 3), + stride=1, + padding=1, + bias_attr=False, + ), + nn.BatchNorm2D(32), + nn.ReLU(), + ) + + self.inplanes = 32 + self.layer1 = self._make_layer(32, 3, [2, 2]) # [16, 50] + self.layer2 = self._make_layer(64, 4, [2, 2]) # [8, 25] + self.layer3 = self._make_layer(128, 6, [2, 1]) # [4, 25] + self.layer4 = self._make_layer(256, 6, [2, 1]) # [2, 25] + self.layer5 = self._make_layer(512, 3, [2, 1]) # [1, 25] + + if with_lstm: + self.rnn = nn.LSTM(512, 256, direction="bidirect", num_layers=2) + self.out_channels = 2 * 256 + else: + self.out_channels = 512 + + def _make_layer(self, planes, blocks, stride): + downsample = None + if stride != [1, 1] or self.inplanes != planes: + downsample = nn.Sequential( + conv1x1(self.inplanes, planes, stride), nn.BatchNorm2D(planes) + ) + + layers = [] + layers.append(AsterBlock(self.inplanes, planes, stride, downsample)) + self.inplanes = planes + for _ in range(1, blocks): + layers.append(AsterBlock(self.inplanes, planes)) + return nn.Sequential(*layers) + + def forward(self, x): + x0 = self.layer0(x) + x1 = self.layer1(x0) + x2 = self.layer2(x1) + x3 = self.layer3(x2) + x4 = self.layer4(x3) + x5 = self.layer5(x4) + + cnn_feat = x5.squeeze(2) # [N, c, w] + cnn_feat = paddle.transpose(cnn_feat, perm=[0, 2, 1]) + if self.with_lstm: + rnn_feat, _ = self.rnn(cnn_feat) + return rnn_feat + else: + return cnn_feat diff --git a/modules/onnx_ocr_module/src/ppocr/modeling/backbones/rec_resnet_fpn.py b/modules/onnx_ocr_module/src/ppocr/modeling/backbones/rec_resnet_fpn.py new file mode 100644 index 0000000..d259f1d --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/modeling/backbones/rec_resnet_fpn.py @@ -0,0 +1,317 @@ +# copyright (c) 2020 PaddlePaddle Authors. All Rights Reserve. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +from paddle import nn, ParamAttr +from paddle.nn import functional as F +import paddle +import numpy as np + +__all__ = ["ResNetFPN"] + + +class ResNetFPN(nn.Layer): + def __init__(self, in_channels=1, layers=50, **kwargs): + super(ResNetFPN, self).__init__() + supported_layers = { + 18: {"depth": [2, 2, 2, 2], "block_class": BasicBlock}, + 34: {"depth": [3, 4, 6, 3], "block_class": BasicBlock}, + 50: {"depth": [3, 4, 6, 3], "block_class": BottleneckBlock}, + 101: {"depth": [3, 4, 23, 3], "block_class": BottleneckBlock}, + 152: {"depth": [3, 8, 36, 3], "block_class": BottleneckBlock}, + } + stride_list = [(2, 2), (2, 2), (1, 1), (1, 1)] + num_filters = [64, 128, 256, 512] + self.depth = supported_layers[layers]["depth"] + self.F = [] + self.conv = ConvBNLayer( + in_channels=in_channels, + out_channels=64, + kernel_size=7, + stride=2, + act="relu", + name="conv1", + ) + self.block_list = [] + in_ch = 64 + if layers >= 50: + for block in range(len(self.depth)): + for i in range(self.depth[block]): + if layers in [101, 152] and block == 2: + if i == 0: + conv_name = "res" + str(block + 2) + "a" + else: + conv_name = "res" + str(block + 2) + "b" + str(i) + else: + conv_name = "res" + str(block + 2) + chr(97 + i) + block_list = self.add_sublayer( + "bottleneckBlock_{}_{}".format(block, i), + BottleneckBlock( + in_channels=in_ch, + out_channels=num_filters[block], + stride=stride_list[block] if i == 0 else 1, + name=conv_name, + ), + ) + in_ch = num_filters[block] * 4 + self.block_list.append(block_list) + self.F.append(block_list) + else: + for block in range(len(self.depth)): + for i in range(self.depth[block]): + conv_name = "res" + str(block + 2) + chr(97 + i) + if i == 0 and block != 0: + stride = (2, 1) + else: + stride = (1, 1) + basic_block = self.add_sublayer( + conv_name, + BasicBlock( + in_channels=in_ch, + out_channels=num_filters[block], + stride=stride_list[block] if i == 0 else 1, + is_first=block == i == 0, + name=conv_name, + ), + ) + in_ch = basic_block.out_channels + self.block_list.append(basic_block) + out_ch_list = [in_ch // 4, in_ch // 2, in_ch] + self.base_block = [] + self.conv_trans = [] + self.bn_block = [] + for i in [-2, -3]: + in_channels = out_ch_list[i + 1] + out_ch_list[i] + + self.base_block.append( + self.add_sublayer( + "F_{}_base_block_0".format(i), + nn.Conv2D( + in_channels=in_channels, + out_channels=out_ch_list[i], + kernel_size=1, + weight_attr=ParamAttr(trainable=True), + bias_attr=ParamAttr(trainable=True), + ), + ) + ) + self.base_block.append( + self.add_sublayer( + "F_{}_base_block_1".format(i), + nn.Conv2D( + in_channels=out_ch_list[i], + out_channels=out_ch_list[i], + kernel_size=3, + padding=1, + weight_attr=ParamAttr(trainable=True), + bias_attr=ParamAttr(trainable=True), + ), + ) + ) + self.base_block.append( + self.add_sublayer( + "F_{}_base_block_2".format(i), + nn.BatchNorm( + num_channels=out_ch_list[i], + act="relu", + param_attr=ParamAttr(trainable=True), + bias_attr=ParamAttr(trainable=True), + ), + ) + ) + self.base_block.append( + self.add_sublayer( + "F_{}_base_block_3".format(i), + nn.Conv2D( + in_channels=out_ch_list[i], + out_channels=512, + kernel_size=1, + bias_attr=ParamAttr(trainable=True), + weight_attr=ParamAttr(trainable=True), + ), + ) + ) + self.out_channels = 512 + + def __call__(self, x): + x = self.conv(x) + fpn_list = [] + F = [] + for i in range(len(self.depth)): + fpn_list.append(np.sum(self.depth[: i + 1])) + + for i, block in enumerate(self.block_list): + x = block(x) + for number in fpn_list: + if i + 1 == number: + F.append(x) + base = F[-1] + + j = 0 + for i, block in enumerate(self.base_block): + if i % 3 == 0 and i < 6: + j = j + 1 + b, c, w, h = F[-j - 1].shape + if [w, h] == list(base.shape[2:]): + base = base + else: + base = self.conv_trans[j - 1](base) + base = self.bn_block[j - 1](base) + base = paddle.concat([base, F[-j - 1]], axis=1) + base = block(base) + return base + + +class ConvBNLayer(nn.Layer): + def __init__( + self, + in_channels, + out_channels, + kernel_size, + stride=1, + groups=1, + act=None, + name=None, + ): + super(ConvBNLayer, self).__init__() + self.conv = nn.Conv2D( + in_channels=in_channels, + out_channels=out_channels, + kernel_size=2 if stride == (1, 1) else kernel_size, + dilation=2 if stride == (1, 1) else 1, + stride=stride, + padding=(kernel_size - 1) // 2, + groups=groups, + weight_attr=ParamAttr(name=name + ".conv2d.output.1.w_0"), + bias_attr=False, + ) + + if name == "conv1": + bn_name = "bn_" + name + else: + bn_name = "bn" + name[3:] + self.bn = nn.BatchNorm( + num_channels=out_channels, + act=act, + param_attr=ParamAttr(name=name + ".output.1.w_0"), + bias_attr=ParamAttr(name=name + ".output.1.b_0"), + moving_mean_name=bn_name + "_mean", + moving_variance_name=bn_name + "_variance", + ) + + def __call__(self, x): + x = self.conv(x) + x = self.bn(x) + return x + + +class ShortCut(nn.Layer): + def __init__(self, in_channels, out_channels, stride, name, is_first=False): + super(ShortCut, self).__init__() + self.use_conv = True + + if in_channels != out_channels or stride != 1 or is_first == True: + if stride == (1, 1): + self.conv = ConvBNLayer(in_channels, out_channels, 1, 1, name=name) + else: # stride==(2,2) + self.conv = ConvBNLayer(in_channels, out_channels, 1, stride, name=name) + else: + self.use_conv = False + + def forward(self, x): + if self.use_conv: + x = self.conv(x) + return x + + +class BottleneckBlock(nn.Layer): + def __init__(self, in_channels, out_channels, stride, name): + super(BottleneckBlock, self).__init__() + self.conv0 = ConvBNLayer( + in_channels=in_channels, + out_channels=out_channels, + kernel_size=1, + act="relu", + name=name + "_branch2a", + ) + self.conv1 = ConvBNLayer( + in_channels=out_channels, + out_channels=out_channels, + kernel_size=3, + stride=stride, + act="relu", + name=name + "_branch2b", + ) + + self.conv2 = ConvBNLayer( + in_channels=out_channels, + out_channels=out_channels * 4, + kernel_size=1, + act=None, + name=name + "_branch2c", + ) + + self.short = ShortCut( + in_channels=in_channels, + out_channels=out_channels * 4, + stride=stride, + is_first=False, + name=name + "_branch1", + ) + self.out_channels = out_channels * 4 + + def forward(self, x): + y = self.conv0(x) + y = self.conv1(y) + y = self.conv2(y) + y = y + self.short(x) + y = F.relu(y) + return y + + +class BasicBlock(nn.Layer): + def __init__(self, in_channels, out_channels, stride, name, is_first): + super(BasicBlock, self).__init__() + self.conv0 = ConvBNLayer( + in_channels=in_channels, + out_channels=out_channels, + kernel_size=3, + act="relu", + stride=stride, + name=name + "_branch2a", + ) + self.conv1 = ConvBNLayer( + in_channels=out_channels, + out_channels=out_channels, + kernel_size=3, + act=None, + name=name + "_branch2b", + ) + self.short = ShortCut( + in_channels=in_channels, + out_channels=out_channels, + stride=stride, + is_first=is_first, + name=name + "_branch1", + ) + self.out_channels = out_channels + + def forward(self, x): + y = self.conv0(x) + y = self.conv1(y) + y = y + self.short(x) + return F.relu(y) diff --git a/modules/onnx_ocr_module/src/ppocr/modeling/backbones/rec_resnet_rfl.py b/modules/onnx_ocr_module/src/ppocr/modeling/backbones/rec_resnet_rfl.py new file mode 100644 index 0000000..4742ef8 --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/modeling/backbones/rec_resnet_rfl.py @@ -0,0 +1,359 @@ +# copyright (c) 2022 PaddlePaddle Authors. All Rights Reserve. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +""" +This code is refer from: +https://github.com/hikopensource/DAVAR-Lab-OCR/blob/main/davarocr/davar_rcg/models/backbones/ResNetRFL.py +""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import paddle +import paddle.nn as nn + +from paddle.nn.initializer import TruncatedNormal, Constant, Normal, KaimingNormal + +kaiming_init_ = KaimingNormal() +zeros_ = Constant(value=0.0) +ones_ = Constant(value=1.0) + + +class BasicBlock(nn.Layer): + """Res-net Basic Block""" + + expansion = 1 + + def __init__( + self, inplanes, planes, stride=1, downsample=None, norm_type="BN", **kwargs + ): + """ + Args: + inplanes (int): input channel + planes (int): channels of the middle feature + stride (int): stride of the convolution + downsample (int): type of the down_sample + norm_type (str): type of the normalization + **kwargs (None): backup parameter + """ + super(BasicBlock, self).__init__() + self.conv1 = self._conv3x3(inplanes, planes) + self.bn1 = nn.BatchNorm(planes) + self.conv2 = self._conv3x3(planes, planes) + self.bn2 = nn.BatchNorm(planes) + self.relu = nn.ReLU() + self.downsample = downsample + self.stride = stride + + def _conv3x3(self, in_planes, out_planes, stride=1): + return nn.Conv2D( + in_planes, + out_planes, + kernel_size=3, + stride=stride, + padding=1, + bias_attr=False, + ) + + def forward(self, x): + residual = x + + out = self.conv1(x) + out = self.bn1(out) + out = self.relu(out) + + out = self.conv2(out) + out = self.bn2(out) + + if self.downsample is not None: + residual = self.downsample(x) + out += residual + out = self.relu(out) + + return out + + +class ResNetRFL(nn.Layer): + def __init__(self, in_channels, out_channels=512, use_cnt=True, use_seq=True): + """ + + Args: + in_channels (int): input channel + out_channels (int): output channel + """ + super(ResNetRFL, self).__init__() + assert use_cnt or use_seq + self.use_cnt, self.use_seq = use_cnt, use_seq + self.backbone = RFLBase(in_channels) + + self.out_channels = out_channels + self.out_channels_block = [ + int(self.out_channels / 4), + int(self.out_channels / 2), + self.out_channels, + self.out_channels, + ] + block = BasicBlock + layers = [1, 2, 5, 3] + self.inplanes = int(self.out_channels // 2) + + self.relu = nn.ReLU() + if self.use_seq: + self.maxpool3 = nn.MaxPool2D(kernel_size=2, stride=(2, 1), padding=(0, 1)) + self.layer3 = self._make_layer( + block, self.out_channels_block[2], layers[2], stride=1 + ) + self.conv3 = nn.Conv2D( + self.out_channels_block[2], + self.out_channels_block[2], + kernel_size=3, + stride=1, + padding=1, + bias_attr=False, + ) + self.bn3 = nn.BatchNorm(self.out_channels_block[2]) + + self.layer4 = self._make_layer( + block, self.out_channels_block[3], layers[3], stride=1 + ) + self.conv4_1 = nn.Conv2D( + self.out_channels_block[3], + self.out_channels_block[3], + kernel_size=2, + stride=(2, 1), + padding=(0, 1), + bias_attr=False, + ) + self.bn4_1 = nn.BatchNorm(self.out_channels_block[3]) + self.conv4_2 = nn.Conv2D( + self.out_channels_block[3], + self.out_channels_block[3], + kernel_size=2, + stride=1, + padding=0, + bias_attr=False, + ) + self.bn4_2 = nn.BatchNorm(self.out_channels_block[3]) + + if self.use_cnt: + self.inplanes = int(self.out_channels // 2) + self.v_maxpool3 = nn.MaxPool2D(kernel_size=2, stride=(2, 1), padding=(0, 1)) + self.v_layer3 = self._make_layer( + block, self.out_channels_block[2], layers[2], stride=1 + ) + self.v_conv3 = nn.Conv2D( + self.out_channels_block[2], + self.out_channels_block[2], + kernel_size=3, + stride=1, + padding=1, + bias_attr=False, + ) + self.v_bn3 = nn.BatchNorm(self.out_channels_block[2]) + + self.v_layer4 = self._make_layer( + block, self.out_channels_block[3], layers[3], stride=1 + ) + self.v_conv4_1 = nn.Conv2D( + self.out_channels_block[3], + self.out_channels_block[3], + kernel_size=2, + stride=(2, 1), + padding=(0, 1), + bias_attr=False, + ) + self.v_bn4_1 = nn.BatchNorm(self.out_channels_block[3]) + self.v_conv4_2 = nn.Conv2D( + self.out_channels_block[3], + self.out_channels_block[3], + kernel_size=2, + stride=1, + padding=0, + bias_attr=False, + ) + self.v_bn4_2 = nn.BatchNorm(self.out_channels_block[3]) + + def _make_layer(self, block, planes, blocks, stride=1): + downsample = None + if stride != 1 or self.inplanes != planes * block.expansion: + downsample = nn.Sequential( + nn.Conv2D( + self.inplanes, + planes * block.expansion, + kernel_size=1, + stride=stride, + bias_attr=False, + ), + nn.BatchNorm(planes * block.expansion), + ) + + layers = list() + layers.append(block(self.inplanes, planes, stride, downsample)) + self.inplanes = planes * block.expansion + for _ in range(1, blocks): + layers.append(block(self.inplanes, planes)) + + return nn.Sequential(*layers) + + def forward(self, inputs): + x_1 = self.backbone(inputs) + + if self.use_cnt: + v_x = self.v_maxpool3(x_1) + v_x = self.v_layer3(v_x) + v_x = self.v_conv3(v_x) + v_x = self.v_bn3(v_x) + visual_feature_2 = self.relu(v_x) + + v_x = self.v_layer4(visual_feature_2) + v_x = self.v_conv4_1(v_x) + v_x = self.v_bn4_1(v_x) + v_x = self.relu(v_x) + v_x = self.v_conv4_2(v_x) + v_x = self.v_bn4_2(v_x) + visual_feature_3 = self.relu(v_x) + else: + visual_feature_3 = None + if self.use_seq: + x = self.maxpool3(x_1) + x = self.layer3(x) + x = self.conv3(x) + x = self.bn3(x) + x_2 = self.relu(x) + + x = self.layer4(x_2) + x = self.conv4_1(x) + x = self.bn4_1(x) + x = self.relu(x) + x = self.conv4_2(x) + x = self.bn4_2(x) + x_3 = self.relu(x) + else: + x_3 = None + + return [visual_feature_3, x_3] + + +class ResNetBase(nn.Layer): + def __init__(self, in_channels, out_channels, block, layers): + super(ResNetBase, self).__init__() + + self.out_channels_block = [ + int(out_channels / 4), + int(out_channels / 2), + out_channels, + out_channels, + ] + + self.inplanes = int(out_channels / 8) + self.conv0_1 = nn.Conv2D( + in_channels, + int(out_channels / 16), + kernel_size=3, + stride=1, + padding=1, + bias_attr=False, + ) + self.bn0_1 = nn.BatchNorm(int(out_channels / 16)) + self.conv0_2 = nn.Conv2D( + int(out_channels / 16), + self.inplanes, + kernel_size=3, + stride=1, + padding=1, + bias_attr=False, + ) + self.bn0_2 = nn.BatchNorm(self.inplanes) + self.relu = nn.ReLU() + + self.maxpool1 = nn.MaxPool2D(kernel_size=2, stride=2, padding=0) + self.layer1 = self._make_layer(block, self.out_channels_block[0], layers[0]) + self.conv1 = nn.Conv2D( + self.out_channels_block[0], + self.out_channels_block[0], + kernel_size=3, + stride=1, + padding=1, + bias_attr=False, + ) + self.bn1 = nn.BatchNorm(self.out_channels_block[0]) + + self.maxpool2 = nn.MaxPool2D(kernel_size=2, stride=2, padding=0) + self.layer2 = self._make_layer( + block, self.out_channels_block[1], layers[1], stride=1 + ) + self.conv2 = nn.Conv2D( + self.out_channels_block[1], + self.out_channels_block[1], + kernel_size=3, + stride=1, + padding=1, + bias_attr=False, + ) + self.bn2 = nn.BatchNorm(self.out_channels_block[1]) + + def _make_layer(self, block, planes, blocks, stride=1): + downsample = None + if stride != 1 or self.inplanes != planes * block.expansion: + downsample = nn.Sequential( + nn.Conv2D( + self.inplanes, + planes * block.expansion, + kernel_size=1, + stride=stride, + bias_attr=False, + ), + nn.BatchNorm(planes * block.expansion), + ) + + layers = list() + layers.append(block(self.inplanes, planes, stride, downsample)) + self.inplanes = planes * block.expansion + for _ in range(1, blocks): + layers.append(block(self.inplanes, planes)) + + return nn.Sequential(*layers) + + def forward(self, x): + x = self.conv0_1(x) + x = self.bn0_1(x) + x = self.relu(x) + x = self.conv0_2(x) + x = self.bn0_2(x) + x = self.relu(x) + + x = self.maxpool1(x) + x = self.layer1(x) + x = self.conv1(x) + x = self.bn1(x) + x = self.relu(x) + + x = self.maxpool2(x) + x = self.layer2(x) + x = self.conv2(x) + x = self.bn2(x) + x = self.relu(x) + + return x + + +class RFLBase(nn.Layer): + """Reciprocal feature learning share backbone network""" + + def __init__(self, in_channels, out_channels=512): + super(RFLBase, self).__init__() + self.ConvNet = ResNetBase(in_channels, out_channels, BasicBlock, [1, 2, 5, 3]) + + def forward(self, inputs): + return self.ConvNet(inputs) diff --git a/modules/onnx_ocr_module/src/ppocr/modeling/backbones/rec_resnet_vd.py b/modules/onnx_ocr_module/src/ppocr/modeling/backbones/rec_resnet_vd.py new file mode 100644 index 0000000..343d5ed --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/modeling/backbones/rec_resnet_vd.py @@ -0,0 +1,313 @@ +# copyright (c) 2020 PaddlePaddle Authors. All Rights Reserve. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import paddle +from paddle import ParamAttr +import paddle.nn as nn +import paddle.nn.functional as F + +__all__ = ["ResNet"] + + +class ConvBNLayer(nn.Layer): + def __init__( + self, + in_channels, + out_channels, + kernel_size, + stride=1, + groups=1, + is_vd_mode=False, + act=None, + name=None, + ): + super(ConvBNLayer, self).__init__() + + self.is_vd_mode = is_vd_mode + self._pool2d_avg = nn.AvgPool2D( + kernel_size=stride, stride=stride, padding=0, ceil_mode=True + ) + self._conv = nn.Conv2D( + in_channels=in_channels, + out_channels=out_channels, + kernel_size=kernel_size, + stride=1 if is_vd_mode else stride, + padding=(kernel_size - 1) // 2, + groups=groups, + weight_attr=ParamAttr(name=name + "_weights"), + bias_attr=False, + ) + if name == "conv1": + bn_name = "bn_" + name + else: + bn_name = "bn" + name[3:] + self._batch_norm = nn.BatchNorm( + out_channels, + act=act, + param_attr=ParamAttr(name=bn_name + "_scale"), + bias_attr=ParamAttr(bn_name + "_offset"), + moving_mean_name=bn_name + "_mean", + moving_variance_name=bn_name + "_variance", + ) + + def forward(self, inputs): + if self.is_vd_mode: + inputs = self._pool2d_avg(inputs) + y = self._conv(inputs) + y = self._batch_norm(y) + return y + + +class BottleneckBlock(nn.Layer): + def __init__( + self, + in_channels, + out_channels, + stride, + shortcut=True, + if_first=False, + name=None, + ): + super(BottleneckBlock, self).__init__() + + self.conv0 = ConvBNLayer( + in_channels=in_channels, + out_channels=out_channels, + kernel_size=1, + act="relu", + name=name + "_branch2a", + ) + self.conv1 = ConvBNLayer( + in_channels=out_channels, + out_channels=out_channels, + kernel_size=3, + stride=stride, + act="relu", + name=name + "_branch2b", + ) + self.conv2 = ConvBNLayer( + in_channels=out_channels, + out_channels=out_channels * 4, + kernel_size=1, + act=None, + name=name + "_branch2c", + ) + + if not shortcut: + self.short = ConvBNLayer( + in_channels=in_channels, + out_channels=out_channels * 4, + kernel_size=1, + stride=stride, + is_vd_mode=not if_first and stride[0] != 1, + name=name + "_branch1", + ) + + self.shortcut = shortcut + + def forward(self, inputs): + y = self.conv0(inputs) + + conv1 = self.conv1(y) + conv2 = self.conv2(conv1) + + if self.shortcut: + short = inputs + else: + short = self.short(inputs) + y = paddle.add(x=short, y=conv2) + y = F.relu(y) + return y + + +class BasicBlock(nn.Layer): + def __init__( + self, + in_channels, + out_channels, + stride, + shortcut=True, + if_first=False, + name=None, + ): + super(BasicBlock, self).__init__() + self.stride = stride + self.conv0 = ConvBNLayer( + in_channels=in_channels, + out_channels=out_channels, + kernel_size=3, + stride=stride, + act="relu", + name=name + "_branch2a", + ) + self.conv1 = ConvBNLayer( + in_channels=out_channels, + out_channels=out_channels, + kernel_size=3, + act=None, + name=name + "_branch2b", + ) + + if not shortcut: + self.short = ConvBNLayer( + in_channels=in_channels, + out_channels=out_channels, + kernel_size=1, + stride=stride, + is_vd_mode=not if_first and stride[0] != 1, + name=name + "_branch1", + ) + + self.shortcut = shortcut + + def forward(self, inputs): + y = self.conv0(inputs) + conv1 = self.conv1(y) + + if self.shortcut: + short = inputs + else: + short = self.short(inputs) + y = paddle.add(x=short, y=conv1) + y = F.relu(y) + return y + + +class ResNet(nn.Layer): + def __init__(self, in_channels=3, layers=50, **kwargs): + super(ResNet, self).__init__() + + self.layers = layers + supported_layers = [18, 34, 50, 101, 152, 200] + assert ( + layers in supported_layers + ), "supported layers are {} but input layer is {}".format( + supported_layers, layers + ) + + if layers == 18: + depth = [2, 2, 2, 2] + elif layers == 34 or layers == 50: + depth = [3, 4, 6, 3] + elif layers == 101: + depth = [3, 4, 23, 3] + elif layers == 152: + depth = [3, 8, 36, 3] + elif layers == 200: + depth = [3, 12, 48, 3] + num_channels = [64, 256, 512, 1024] if layers >= 50 else [64, 64, 128, 256] + num_filters = [64, 128, 256, 512] + + self.conv1_1 = ConvBNLayer( + in_channels=in_channels, + out_channels=32, + kernel_size=3, + stride=1, + act="relu", + name="conv1_1", + ) + self.conv1_2 = ConvBNLayer( + in_channels=32, + out_channels=32, + kernel_size=3, + stride=1, + act="relu", + name="conv1_2", + ) + self.conv1_3 = ConvBNLayer( + in_channels=32, + out_channels=64, + kernel_size=3, + stride=1, + act="relu", + name="conv1_3", + ) + self.pool2d_max = nn.MaxPool2D(kernel_size=3, stride=2, padding=1) + + self.block_list = [] + if layers >= 50: + for block in range(len(depth)): + shortcut = False + for i in range(depth[block]): + if layers in [101, 152, 200] and block == 2: + if i == 0: + conv_name = "res" + str(block + 2) + "a" + else: + conv_name = "res" + str(block + 2) + "b" + str(i) + else: + conv_name = "res" + str(block + 2) + chr(97 + i) + + if i == 0 and block != 0: + stride = (2, 1) + else: + stride = (1, 1) + bottleneck_block = self.add_sublayer( + "bb_%d_%d" % (block, i), + BottleneckBlock( + in_channels=( + num_channels[block] + if i == 0 + else num_filters[block] * 4 + ), + out_channels=num_filters[block], + stride=stride, + shortcut=shortcut, + if_first=block == i == 0, + name=conv_name, + ), + ) + shortcut = True + self.block_list.append(bottleneck_block) + self.out_channels = num_filters[block] * 4 + else: + for block in range(len(depth)): + shortcut = False + for i in range(depth[block]): + conv_name = "res" + str(block + 2) + chr(97 + i) + if i == 0 and block != 0: + stride = (2, 1) + else: + stride = (1, 1) + + basic_block = self.add_sublayer( + "bb_%d_%d" % (block, i), + BasicBlock( + in_channels=( + num_channels[block] if i == 0 else num_filters[block] + ), + out_channels=num_filters[block], + stride=stride, + shortcut=shortcut, + if_first=block == i == 0, + name=conv_name, + ), + ) + shortcut = True + self.block_list.append(basic_block) + self.out_channels = num_filters[block] + self.out_pool = nn.MaxPool2D(kernel_size=2, stride=2, padding=0) + + def forward(self, inputs): + y = self.conv1_1(inputs) + y = self.conv1_2(y) + y = self.conv1_3(y) + y = self.pool2d_max(y) + for block in self.block_list: + y = block(y) + y = self.out_pool(y) + return y diff --git a/modules/onnx_ocr_module/src/ppocr/modeling/backbones/rec_resnetv2.py b/modules/onnx_ocr_module/src/ppocr/modeling/backbones/rec_resnetv2.py new file mode 100644 index 0000000..c121b6f --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/modeling/backbones/rec_resnetv2.py @@ -0,0 +1,1227 @@ +# copyright (c) 2024 PaddlePaddle Authors. All Rights Reserve. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +""" +This code is refer from: +https://github.com/huggingface/pytorch-image-models/blob/main/timm/models/resnetv2.py +""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import math +import collections.abc +from itertools import repeat +from collections import OrderedDict # pylint: disable=g-importing-member + +import paddle +import paddle.nn as nn +import paddle.nn.functional as F +from paddle.nn.initializer import TruncatedNormal, Constant, Normal, KaimingUniform +from functools import partial +from typing import Union, Callable, Type, List, Tuple + +IMAGENET_INCEPTION_MEAN = (0.5, 0.5, 0.5) +IMAGENET_INCEPTION_STD = (0.5, 0.5, 0.5) +normal_ = Normal(mean=0.0, std=0.01) +zeros_ = Constant(value=0.0) +ones_ = Constant(value=1.0) +kaiming_normal_ = KaimingUniform(nonlinearity="relu") + + +def _ntuple(n): + def parse(x): + if isinstance(x, collections.abc.Iterable): + return x + return tuple(repeat(x, n)) + + return parse + + +to_1tuple = _ntuple(1) +to_2tuple = _ntuple(2) +to_3tuple = _ntuple(3) +to_4tuple = _ntuple(4) +to_ntuple = _ntuple + + +class StdConv2dSame(nn.Conv2D): + def __init__( + self, + in_channel, + out_channels, + kernel_size, + stride=1, + padding="SAME", + dilation=1, + groups=1, + bias_attr=False, + eps=1e-6, + is_export=False, + ): + padding, is_dynamic = get_padding_value( + padding, kernel_size, stride=stride, dilation=dilation + ) + super().__init__( + in_channel, + out_channels, + kernel_size, + stride=stride, + padding=padding, + dilation=dilation, + groups=groups, + bias_attr=bias_attr, + ) + self.same_pad = is_dynamic + self.export = is_export + self.eps = eps + + self.running_mean = paddle.zeros([self._out_channels], dtype="float32") + self.running_variance = paddle.ones([self._out_channels], dtype="float32") + self.batch_norm = paddle.nn.BatchNorm1D( + self._out_channels, use_global_stats=False + ) + + def forward(self, x): + if not self.training: + self.export = True + if self.same_pad: + if self.export: + x = pad_same_export(x, self._kernel_size, self._stride, self._dilation) + else: + x = pad_same(x, self._kernel_size, self._stride, self._dilation) + if self.export: + weight = paddle.reshape( + self.batch_norm( + self.weight.reshape([1, self._out_channels, -1]).cast( + paddle.float32 + ), + ), + self.weight.shape, + ) + else: + weight = paddle.reshape( + F.batch_norm( + self.weight.reshape([1, self._out_channels, -1]), + self.running_mean, + self.running_variance, + training=True, + momentum=0.0, + epsilon=self.eps, + ), + self.weight.shape, + ) + x = F.conv2d( + x, + weight, + self.bias, + self._stride, + self._padding, + self._dilation, + self._groups, + ) + return x + + +class StdConv2d(nn.Conv2D): + """Conv2d with Weight Standardization. Used for BiT ResNet-V2 models. + + Paper: `Micro-Batch Training with Batch-Channel Normalization and Weight Standardization` - + https://arxiv.org/abs/1903.10520v2 + """ + + def __init__( + self, + in_channel, + out_channels, + kernel_size, + stride=1, + padding=None, + dilation=1, + groups=1, + bias=False, + eps=1e-6, + ): + if padding is None: + padding = get_padding(kernel_size, stride, dilation) + super().__init__( + in_channel, + out_channels, + kernel_size, + stride=stride, + padding=padding, + dilation=dilation, + groups=groups, + bias_attr=bias, + ) + self.eps = eps + + def forward(self, x): + weight = F.batch_norm( + self.weight.reshape(1, self.out_channels, -1), + None, + None, + training=True, + momentum=0.0, + epsilon=self.eps, + ).reshape_as(self.weight) + x = F.conv2d( + x, weight, self.bias, self.stride, self.padding, self.dilation, self.groups + ) + return x + + +class MaxPool2dSame(nn.MaxPool2D): + """Tensorflow like 'SAME' wrapper for 2D max pooling""" + + def __init__( + self, + kernel_size: int, + stride=None, + padding=0, + dilation=1, + ceil_mode=False, + is_export=False, + ): + kernel_size = to_2tuple(kernel_size) + stride = to_2tuple(stride) + dilation = to_2tuple(dilation) + self.export = is_export + super(MaxPool2dSame, self).__init__( + kernel_size, stride, (0, 0), dilation, ceil_mode + ) + + def forward(self, x): + if not self.training: + self.export = True + if self.export: + x = pad_same_export(x, self.ksize, self.stride, value=-float("inf")) + else: + x = pad_same(x, self.ksize, self.stride, value=-float("inf")) + return F.max_pool2d(x, self.ksize, self.stride, (0, 0), self.ceil_mode) + + +def get_padding(kernel_size: int, stride: int = 1, dilation: int = 1, **_) -> int: + padding = ((stride - 1) + dilation * (kernel_size - 1)) // 2 + return padding + + +def is_static_pad(kernel_size: int, stride: int = 1, dilation: int = 1, **_): + return stride == 1 and (dilation * (kernel_size - 1)) % 2 == 0 + + +def get_padding_value(padding, kernel_size, **kwargs) -> Tuple[Tuple, bool]: + dynamic = False + if isinstance(padding, str): + # for any string padding, the padding will be calculated for you, one of three ways + padding = padding.lower() + if padding == "same": + # TF compatible 'SAME' padding, has a performance and GPU memory allocation impact + if is_static_pad(kernel_size, **kwargs): + # static case, no extra overhead + padding = get_padding(kernel_size, **kwargs) + else: + # dynamic 'SAME' padding, has runtime/GPU memory overhead + padding = 0 + dynamic = True + elif padding == "valid": + # 'VALID' padding, same as padding=0 + padding = 0 + else: + # Default to PyTorch style 'same'-ish symmetric padding + padding = get_padding(kernel_size, **kwargs) + return padding, dynamic + + +def create_pool2d(pool_type, kernel_size, stride=None, is_export=False, **kwargs): + stride = stride or kernel_size + padding = kwargs.pop("padding", "") + padding, is_dynamic = get_padding_value( + padding, kernel_size, stride=stride, **kwargs + ) + if is_dynamic: + if pool_type == "avg": + return AvgPool2dSame( + kernel_size, stride=stride, is_export=is_export, **kwargs + ) + elif pool_type == "max": + return MaxPool2dSame( + kernel_size, stride=stride, is_export=is_export, **kwargs + ) + else: + assert False, f"Unsupported pool type {pool_type}" + + +def get_same_padding(x, k, s, d): + return max((math.ceil(x / s) - 1) * s + (k - 1) * d + 1 - x, 0) + + +def get_same_padding_export(x, k, s, d): + x = paddle.to_tensor(x) + k = paddle.to_tensor(k) + s = paddle.to_tensor(s) + d = paddle.to_tensor(d) + return paddle.max((paddle.ceil(x / s) - 1) * s + (k - 1) * d + 1 - x, 0) + + +def pad_same_export(x, k, s, d=(1, 1), value=0): + ih, iw = x.shape[-2:] + pad_h, pad_w = get_same_padding_export( + ih, k[0], s[0], d[0] + ), get_same_padding_export(iw, k[1], s[1], d[1]) + pad_h = pad_h.cast(paddle.int32) + pad_w = pad_w.cast(paddle.int32) + pad_list = paddle.to_tensor( + [ + (pad_w // 2), + (pad_w - pad_w // 2).cast(paddle.int32), + (pad_h // 2).cast(paddle.int32), + (pad_h - pad_h // 2).cast(paddle.int32), + ] + ) + + if pad_h > 0 or pad_w > 0: + if len(pad_list.shape) == 2: + pad_list = pad_list.squeeze(1) + x = F.pad(x, pad_list.cast(paddle.int32), value=value) + return x + + +def pad_same(x, k, s, d=(1, 1), value=0, pad_h=None, pad_w=None): + ih, iw = x.shape[-2:] + + pad_h, pad_w = get_same_padding(ih, k[0], s[0], d[0]), get_same_padding( + iw, k[1], s[1], d[1] + ) + if pad_h > 0 or pad_w > 0: + x = F.pad( + x, + [pad_w // 2, pad_w - pad_w // 2, pad_h // 2, pad_h - pad_h // 2], + value=value, + ) + return x + + +class AvgPool2dSame(nn.AvgPool2D): + """Tensorflow like 'SAME' wrapper for 2D average pooling""" + + def __init__( + self, + kernel_size: int, + stride=None, + padding=0, + ceil_mode=False, + count_include_pad=True, + ): + kernel_size = to_2tuple(kernel_size) + stride = to_2tuple(stride) + super(AvgPool2dSame, self).__init__( + kernel_size, stride, (0, 0), ceil_mode, count_include_pad + ) + + def forward(self, x): + x = pad_same(x, self.kernel_size, self.stride) + return F.avg_pool2d( + x, + self.kernel_size, + self.stride, + self.padding, + self.ceil_mode, + self.count_include_pad, + ) + + +def drop_path( + x, drop_prob: float = 0.0, training: bool = False, scale_by_keep: bool = True +): + if drop_prob == 0.0 or not training: + return x + keep_prob = 1 - drop_prob + shape = (x.shape[0],) + (1,) * ( + x.ndim - 1 + ) # work with diff dim tensors, not just 2D ConvNets + random_tensor = x.new_empty(shape).bernoulli_(keep_prob) + if keep_prob > 0.0 and scale_by_keep: + random_tensor.div_(keep_prob) + return x * random_tensor + + +class DropPath(nn.Layer): + """Drop paths (Stochastic Depth) per sample (when applied in main path of residual blocks).""" + + def __init__(self, drop_prob=None, scale_by_keep=True): + super(DropPath, self).__init__() + self.drop_prob = drop_prob + self.scale_by_keep = scale_by_keep + + def forward(self, x): + return drop_path(x, self.drop_prob, self.training, self.scale_by_keep) + + +def adaptive_pool_feat_mult(pool_type="avg"): + if pool_type == "catavgmax": + return 2 + else: + return 1 + + +class SelectAdaptivePool2d(nn.Layer): + """Selectable global pooling layer with dynamic input kernel size""" + + def __init__(self, output_size=1, pool_type="fast", flatten=False): + super(SelectAdaptivePool2d, self).__init__() + self.pool_type = ( + pool_type or "" + ) # convert other falsy values to empty string for consistent TS typing + self.flatten = nn.Flatten(1) if flatten else nn.Identity() + if pool_type == "": + self.pool = nn.Identity() # pass through + + def is_identity(self): + return not self.pool_type + + def forward(self, x): + x = self.pool(x) + x = self.flatten(x) + return x + + def feat_mult(self): + return adaptive_pool_feat_mult(self.pool_type) + + def __repr__(self): + return ( + self.__class__.__name__ + + " (" + + "pool_type=" + + self.pool_type + + ", flatten=" + + str(self.flatten) + + ")" + ) + + +def _create_pool(num_features, num_classes, pool_type="avg", use_conv=False): + flatten_in_pool = not use_conv # flatten when we use a Linear layer after pooling + if not pool_type: + assert ( + num_classes == 0 or use_conv + ), "Pooling can only be disabled if classifier is also removed or conv classifier is used" + flatten_in_pool = ( + False # disable flattening if pooling is pass-through (no pooling) + ) + global_pool = SelectAdaptivePool2d(pool_type=pool_type, flatten=flatten_in_pool) + num_pooled_features = num_features * global_pool.feat_mult() + return global_pool, num_pooled_features + + +def _create_fc(num_features, num_classes, use_conv=False): + if num_classes <= 0: + fc = nn.Identity() # pass-through (no classifier) + elif use_conv: + fc = nn.Conv2D(num_features, num_classes, 1, bias_attr=True) + else: + fc = nn.Linear(num_features, num_classes, bias_attr=True) + return fc + + +class ClassifierHead(nn.Layer): + """Classifier head w/ configurable global pooling and dropout.""" + + def __init__( + self, in_chs, num_classes, pool_type="avg", drop_rate=0.0, use_conv=False + ): + super(ClassifierHead, self).__init__() + self.drop_rate = drop_rate + self.global_pool, num_pooled_features = _create_pool( + in_chs, num_classes, pool_type, use_conv=use_conv + ) + self.fc = _create_fc(num_pooled_features, num_classes, use_conv=use_conv) + self.flatten = nn.Flatten(1) if use_conv and pool_type else nn.Identity() + + def forward(self, x): + x = self.global_pool(x) + if self.drop_rate: + x = F.dropout(x, p=float(self.drop_rate), training=self.training) + x = self.fc(x) + x = self.flatten(x) + return x + + +class EvoNormBatch2d(nn.Layer): + def __init__( + self, num_features, apply_act=True, momentum=0.1, eps=1e-5, drop_block=None + ): + super(EvoNormBatch2d, self).__init__() + self.apply_act = apply_act # apply activation (non-linearity) + self.momentum = momentum + self.eps = eps + self.weight = paddle.create_parameter( + paddle.ones(num_features), dtype="float32" + ) + self.bias = paddle.create_parameter(paddle.zeros(num_features), dtype="float32") + self.v = ( + paddle.create_parameter(paddle.ones(num_features), dtype="float32") + if apply_act + else None + ) + self.register_buffer("running_var", paddle.ones([num_features])) + self.reset_parameters() + + def reset_parameters(self): + ones_(self.weight) + zeros_(self.bias) + if self.apply_act: + ones_(self.v) + + def forward(self, x): + x_type = x.dtype + if self.v is not None: + running_var = self.running_var.view(1, -1, 1, 1) + if self.training: + var = x.var(dim=(0, 2, 3), unbiased=False, keepdim=True) + n = x.numel() / x.shape[1] + running_var = var.detach() * self.momentum * ( + n / (n - 1) + ) + running_var * (1 - self.momentum) + self.running_var.copy_(running_var.view(self.running_var.shape)) + else: + var = running_var + v = self.v.to(dtype=x_type).reshape(1, -1, 1, 1) + d = x * v + ( + x.var(dim=(2, 3), unbiased=False, keepdim=True) + self.eps + ).sqrt().to(dtype=x_type) + d = d.max((var + self.eps).sqrt().to(dtype=x_type)) + x = x / d + return x * self.weight.view(1, -1, 1, 1) + self.bias.view(1, -1, 1, 1) + + +class EvoNormSample2d(nn.Layer): + def __init__( + self, num_features, apply_act=True, groups=32, eps=1e-5, drop_block=None + ): + super(EvoNormSample2d, self).__init__() + self.apply_act = apply_act + self.groups = groups + self.eps = eps + self.weight = paddle.create_parameter( + paddle.ones(num_features), dtype="float32" + ) + self.bias = paddle.create_parameter(paddle.zeros(num_features), dtype="float32") + self.v = ( + paddle.create_parameter(paddle.ones(num_features), dtype="float32") + if apply_act + else None + ) + self.reset_parameters() + + def reset_parameters(self): + ones_(self.weight) + zeros_(self.bias) + if self.apply_act: + ones_(self.v) + + def forward(self, x): + B, C, H, W = x.shape + if self.v is not None: + n = x * (x * self.v.view(1, -1, 1, 1)).sigmoid() + x = x.reshape(B, self.groups, -1) + x = ( + n.reshape(B, self.groups, -1) + / (x.var(dim=-1, unbiased=False, keepdim=True) + self.eps).sqrt() + ) + x = x.reshape(B, C, H, W) + return x * self.weight.reshape([1, -1, 1, 1]) + self.bias.reshape([1, -1, 1, 1]) + + +class GroupNormAct(nn.GroupNorm): + # NOTE num_channel and num_groups order flipped for easier layer swaps / binding of fixed args + def __init__( + self, + num_channels, + num_groups=32, + eps=1e-5, + affine=True, + apply_act=True, + act_layer=nn.ReLU, + drop_block=None, + ): + super(GroupNormAct, self).__init__(num_groups, num_channels, epsilon=eps) + if affine: + self.weight = paddle.create_parameter([num_channels], dtype="float32") + self.bias = paddle.create_parameter([num_channels], dtype="float32") + ones_(self.weight) + zeros_(self.bias) + if act_layer is not None and apply_act: + act_args = {} + self.act = act_layer(**act_args) + else: + self.act = nn.Identity() + + def forward(self, x): + x = F.group_norm( + x, + num_groups=self._num_groups, + epsilon=self._epsilon, + weight=self.weight, + bias=self.bias, + ) + x = self.act(x) + return x + + +class BatchNormAct2d(nn.BatchNorm2D): + def __init__( + self, + num_features, + eps=1e-5, + momentum=0.1, + affine=True, + track_running_stats=True, + apply_act=True, + act_layer=nn.ReLU, + drop_block=None, + ): + super(BatchNormAct2d, self).__init__( + num_features, + epsilon=eps, + momentum=momentum, + use_global_stats=track_running_stats, + ) + if act_layer is not None and apply_act: + act_args = dict() + self.act = act_layer(**act_args) + else: + self.act = nn.Identity() + + def _forward_python(self, x): + return super(BatchNormAct2d, self).forward(x) + + def forward(self, x): + x = self._forward_python(x) + x = self.act(x) + return x + + +def adapt_input_conv(in_chans, conv_weight): + conv_type = conv_weight.dtype + conv_weight = ( + conv_weight.float() + ) # Some weights are in torch.half, ensure it's float for sum on CPU + O, I, J, K = conv_weight.shape + if in_chans == 1: + if I > 3: + assert conv_weight.shape[1] % 3 == 0 + # For models with space2depth stems + conv_weight = conv_weight.reshape(O, I // 3, 3, J, K) + conv_weight = conv_weight.sum(dim=2, keepdim=False) + else: + conv_weight = conv_weight.sum(dim=1, keepdim=True) + elif in_chans != 3: + if I != 3: + raise NotImplementedError("Weight format not supported by conversion.") + else: + # NOTE this strategy should be better than random init, but there could be other combinations of + # the original RGB input layer weights that'd work better for specific cases. + repeat = int(math.ceil(in_chans / 3)) + conv_weight = conv_weight.repeat(1, repeat, 1, 1)[:, :in_chans, :, :] + conv_weight *= 3 / float(in_chans) + conv_weight = conv_weight.to(conv_type) + return conv_weight + + +def named_apply( + fn: Callable, module: nn.Layer, name="", depth_first=True, include_root=False +) -> nn.Layer: + if not depth_first and include_root: + fn(module=module, name=name) + for child_name, child_module in module.named_children(): + child_name = ".".join((name, child_name)) if name else child_name + named_apply( + fn=fn, + module=child_module, + name=child_name, + depth_first=depth_first, + include_root=True, + ) + if depth_first and include_root: + fn(module=module, name=name) + return module + + +def _cfg(url="", **kwargs): + return { + "url": url, + "num_classes": 1000, + "input_size": (3, 224, 224), + "pool_size": (7, 7), + "crop_pct": 0.875, + "interpolation": "bilinear", + "mean": IMAGENET_INCEPTION_MEAN, + "std": IMAGENET_INCEPTION_STD, + "first_conv": "stem.conv", + "classifier": "head.fc", + **kwargs, + } + + +def make_div(v, divisor=8): + min_value = divisor + new_v = max(min_value, int(v + divisor / 2) // divisor * divisor) + if new_v < 0.9 * v: + new_v += divisor + return new_v + + +class PreActBottleneck(nn.Layer): + """Pre-activation (v2) bottleneck block. + + Follows the implementation of "Identity Mappings in Deep Residual Networks": + https://github.com/KaimingHe/resnet-1k-layers/blob/master/resnet-pre-act.lua + + Except it puts the stride on 3x3 conv when available. + """ + + def __init__( + self, + in_chs, + out_chs=None, + bottle_ratio=0.25, + stride=1, + dilation=1, + first_dilation=None, + groups=1, + act_layer=None, + conv_layer=None, + norm_layer=None, + proj_layer=None, + drop_path_rate=0.0, + is_export=False, + ): + super().__init__() + first_dilation = first_dilation or dilation + conv_layer = conv_layer or StdConv2d + norm_layer = norm_layer or partial(GroupNormAct, num_groups=32) + out_chs = out_chs or in_chs + mid_chs = make_div(out_chs * bottle_ratio) + + if proj_layer is not None: + self.downsample = proj_layer( + in_chs, + out_chs, + stride=stride, + dilation=dilation, + first_dilation=first_dilation, + preact=True, + conv_layer=conv_layer, + norm_layer=norm_layer, + ) + else: + self.downsample = None + + self.norm1 = norm_layer(in_chs) + self.conv1 = conv_layer(in_chs, mid_chs, 1, is_export=is_export) + self.norm2 = norm_layer(mid_chs) + self.conv2 = conv_layer( + mid_chs, + mid_chs, + 3, + stride=stride, + dilation=first_dilation, + groups=groups, + is_export=is_export, + ) + self.norm3 = norm_layer(mid_chs) + self.conv3 = conv_layer(mid_chs, out_chs, 1, is_export=is_export) + self.drop_path = ( + DropPath(drop_path_rate) if drop_path_rate > 0 else nn.Identity() + ) + + def zero_init_last(self): + zeros_(self.conv3.weight) + + def forward(self, x): + x_preact = self.norm1(x) + + # shortcut branch + shortcut = x + if self.downsample is not None: + shortcut = self.downsample(x_preact) + + # residual branch + x = self.conv1(x_preact) + x = self.conv2(self.norm2(x)) + x = self.conv3(self.norm3(x)) + x = self.drop_path(x) + return x + shortcut + + +class Bottleneck(nn.Layer): + """Non Pre-activation bottleneck block, equiv to V1.5/V1b Bottleneck. Used for ViT.""" + + def __init__( + self, + in_chs, + out_chs=None, + bottle_ratio=0.25, + stride=1, + dilation=1, + first_dilation=None, + groups=1, + act_layer=None, + conv_layer=None, + norm_layer=None, + proj_layer=None, + drop_path_rate=0.0, + is_export=False, + ): + super().__init__() + first_dilation = first_dilation or dilation + act_layer = act_layer or nn.ReLU + conv_layer = conv_layer or StdConv2d + norm_layer = norm_layer or partial(GroupNormAct, num_groups=32) + out_chs = out_chs or in_chs + mid_chs = make_div(out_chs * bottle_ratio) + + if proj_layer is not None: + self.downsample = proj_layer( + in_chs, + out_chs, + stride=stride, + dilation=dilation, + preact=False, + conv_layer=conv_layer, + norm_layer=norm_layer, + is_export=is_export, + ) + else: + self.downsample = None + + self.conv1 = conv_layer(in_chs, mid_chs, 1, is_export=is_export) + self.norm1 = norm_layer(mid_chs) + self.conv2 = conv_layer( + mid_chs, + mid_chs, + 3, + stride=stride, + dilation=first_dilation, + groups=groups, + is_export=is_export, + ) + self.norm2 = norm_layer(mid_chs) + self.conv3 = conv_layer(mid_chs, out_chs, 1, is_export=is_export) + self.norm3 = norm_layer(out_chs, apply_act=False) + self.drop_path = ( + DropPath(drop_path_rate) if drop_path_rate > 0 else nn.Identity() + ) + self.act3 = act_layer() + + def zero_init_last(self): + zeros_(self.norm3.weight) + + def forward(self, x): + # shortcut branch + shortcut = x + if self.downsample is not None: + shortcut = self.downsample(x) + + # residual + x = self.conv1(x) + x = self.norm1(x) + x = self.conv2(x) + x = self.norm2(x) + x = self.conv3(x) + x = self.norm3(x) + x = self.drop_path(x) + x = self.act3(x + shortcut) + return x + + +class DownsampleConv(nn.Layer): + def __init__( + self, + in_chs, + out_chs, + stride=1, + dilation=1, + first_dilation=None, + preact=True, + conv_layer=None, + norm_layer=None, + is_export=False, + ): + super(DownsampleConv, self).__init__() + self.conv = conv_layer(in_chs, out_chs, 1, stride=stride, is_export=is_export) + self.norm = nn.Identity() if preact else norm_layer(out_chs, apply_act=False) + + def forward(self, x): + return self.norm(self.conv(x)) + + +class DownsampleAvg(nn.Layer): + def __init__( + self, + in_chs, + out_chs, + stride=1, + dilation=1, + first_dilation=None, + preact=True, + conv_layer=None, + norm_layer=None, + is_export=False, + ): + """AvgPool Downsampling as in 'D' ResNet variants. This is not in RegNet space but I might experiment.""" + super(DownsampleAvg, self).__init__() + avg_stride = stride if dilation == 1 else 1 + if stride > 1 or dilation > 1: + avg_pool_fn = ( + AvgPool2dSame if avg_stride == 1 and dilation > 1 else nn.AvgPool2D + ) + self.pool = avg_pool_fn(2, avg_stride, ceil_mode=True, exclusive=False) + else: + self.pool = nn.Identity() + self.conv = conv_layer(in_chs, out_chs, 1, stride=1, is_export=is_export) + self.norm = nn.Identity() if preact else norm_layer(out_chs, apply_act=False) + + def forward(self, x): + return self.norm(self.conv(self.pool(x))) + + +class ResNetStage(nn.Layer): + """ResNet Stage.""" + + def __init__( + self, + in_chs, + out_chs, + stride, + dilation, + depth, + bottle_ratio=0.25, + groups=1, + avg_down=False, + block_dpr=None, + block_fn=PreActBottleneck, + is_export=False, + act_layer=None, + conv_layer=None, + norm_layer=None, + **block_kwargs, + ): + super(ResNetStage, self).__init__() + first_dilation = 1 if dilation in (1, 2) else 2 + layer_kwargs = dict( + act_layer=act_layer, conv_layer=conv_layer, norm_layer=norm_layer + ) + proj_layer = DownsampleAvg if avg_down else DownsampleConv + prev_chs = in_chs + self.blocks = nn.Sequential() + for block_idx in range(depth): + drop_path_rate = block_dpr[block_idx] if block_dpr else 0.0 + stride = stride if block_idx == 0 else 1 + self.blocks.add_sublayer( + str(block_idx), + block_fn( + prev_chs, + out_chs, + stride=stride, + dilation=dilation, + bottle_ratio=bottle_ratio, + groups=groups, + first_dilation=first_dilation, + proj_layer=proj_layer, + drop_path_rate=drop_path_rate, + is_export=is_export, + **layer_kwargs, + **block_kwargs, + ), + ) + prev_chs = out_chs + first_dilation = dilation + proj_layer = None + + def forward(self, x): + x = self.blocks(x) + return x + + +def is_stem_deep(stem_type): + return any([s in stem_type for s in ("deep", "tiered")]) + + +def create_resnetv2_stem( + in_chs, + out_chs=64, + stem_type="", + preact=True, + conv_layer=StdConv2d, + norm_layer=partial(GroupNormAct, num_groups=32), + is_export=False, +): + stem = OrderedDict() + assert stem_type in ( + "", + "fixed", + "same", + "deep", + "deep_fixed", + "deep_same", + "tiered", + ) + + # NOTE conv padding mode can be changed by overriding the conv_layer def + if is_stem_deep(stem_type): + # A 3 deep 3x3 conv stack as in ResNet V1D models + if "tiered" in stem_type: + stem_chs = (3 * out_chs // 8, out_chs // 2) # 'T' resnets in resnet.py + else: + stem_chs = (out_chs // 2, out_chs // 2) # 'D' ResNets + stem["conv1"] = conv_layer( + in_chs, stem_chs[0], kernel_size=3, stride=2, is_export=is_export + ) + stem["norm1"] = norm_layer(stem_chs[0]) + stem["conv2"] = conv_layer( + stem_chs[0], stem_chs[1], kernel_size=3, stride=1, is_export=is_export + ) + stem["norm2"] = norm_layer(stem_chs[1]) + stem["conv3"] = conv_layer( + stem_chs[1], out_chs, kernel_size=3, stride=1, is_export=is_export + ) + if not preact: + stem["norm3"] = norm_layer(out_chs) + else: + # The usual 7x7 stem conv + stem["conv"] = conv_layer( + in_chs, out_chs, kernel_size=7, stride=2, is_export=is_export + ) + if not preact: + stem["norm"] = norm_layer(out_chs) + + if "fixed" in stem_type: + # 'fixed' SAME padding approximation that is used in BiT models + stem["pad"] = paddle.nn.Pad2D( + 1, mode="constant", value=0.0, data_format="NCHW", name=None + ) + stem["pool"] = nn.MaxPool2D(kernel_size=3, stride=2, padding=0) + elif "same" in stem_type: + # full, input size based 'SAME' padding, used in ViT Hybrid model + stem["pool"] = create_pool2d( + "max", kernel_size=3, stride=2, padding="same", is_export=is_export + ) + else: + # the usual Pypaddle symmetric padding + stem["pool"] = nn.MaxPool2D(kernel_size=3, stride=2, padding=1) + stem_seq = nn.Sequential() + for key, value in stem.items(): + stem_seq.add_sublayer(key, value) + + return stem_seq + + +class ResNetV2(nn.Layer): + """Implementation of Pre-activation (v2) ResNet mode. + + Args: + x: input images with shape [N, 1, H, W] + + Returns: + The extracted features [N, 1, H//16, W//16] + """ + + def __init__( + self, + layers, + channels=(256, 512, 1024, 2048), + num_classes=1000, + in_chans=3, + global_pool="avg", + output_stride=32, + width_factor=1, + stem_chs=64, + stem_type="", + avg_down=False, + preact=True, + act_layer=nn.ReLU, + conv_layer=StdConv2d, + norm_layer=partial(GroupNormAct, num_groups=32), + drop_rate=0.0, + drop_path_rate=0.0, + zero_init_last=False, + is_export=False, + ): + super().__init__() + self.num_classes = num_classes + self.drop_rate = drop_rate + self.is_export = is_export + wf = width_factor + self.feature_info = [] + stem_chs = make_div(stem_chs * wf) + self.stem = create_resnetv2_stem( + in_chans, + stem_chs, + stem_type, + preact, + conv_layer=conv_layer, + norm_layer=norm_layer, + is_export=is_export, + ) + stem_feat = ( + ("stem.conv3" if is_stem_deep(stem_type) else "stem.conv") + if preact + else "stem.norm" + ) + self.feature_info.append(dict(num_chs=stem_chs, reduction=2, module=stem_feat)) + + prev_chs = stem_chs + curr_stride = 4 + dilation = 1 + block_dprs = [ + x.tolist() + for x in paddle.linspace(0, drop_path_rate, sum(layers)).split(layers) + ] + block_fn = PreActBottleneck if preact else Bottleneck + self.stages = nn.Sequential() + for stage_idx, (d, c, bdpr) in enumerate(zip(layers, channels, block_dprs)): + out_chs = make_div(c * wf) + stride = 1 if stage_idx == 0 else 2 + if curr_stride >= output_stride: + dilation *= stride + stride = 1 + stage = ResNetStage( + prev_chs, + out_chs, + stride=stride, + dilation=dilation, + depth=d, + avg_down=avg_down, + act_layer=act_layer, + conv_layer=conv_layer, + norm_layer=norm_layer, + block_dpr=bdpr, + block_fn=block_fn, + is_export=is_export, + ) + prev_chs = out_chs + curr_stride *= stride + self.feature_info += [ + dict( + num_chs=prev_chs, + reduction=curr_stride, + module=f"stages.{stage_idx}", + ) + ] + self.stages.add_sublayer(str(stage_idx), stage) + + self.num_features = prev_chs + self.norm = norm_layer(self.num_features) if preact else nn.Identity() + self.head = ClassifierHead( + self.num_features, + num_classes, + pool_type=global_pool, + drop_rate=self.drop_rate, + use_conv=True, + ) + + self.init_weights(zero_init_last=zero_init_last) + + def init_weights(self, zero_init_last=True): + named_apply(partial(_init_weights, zero_init_last=zero_init_last), self) + + def load_pretrained(self, checkpoint_path, prefix="resnet/"): + _load_weights(self, checkpoint_path, prefix) + + def get_classifier(self): + return self.head.fc + + def reset_classifier(self, num_classes, global_pool="avg"): + self.num_classes = num_classes + self.head = ClassifierHead( + self.num_features, + num_classes, + pool_type=global_pool, + drop_rate=self.drop_rate, + use_conv=True, + ) + + def forward_features(self, x): + x = self.stem(x) + x = self.stages(x) + x = self.norm(x) + return x + + def forward(self, x): + x = self.forward_features(x) + x = self.head(x) + return x + + +def _init_weights(module: nn.Layer, name: str = "", zero_init_last=True): + if isinstance(module, nn.Linear) or ( + "head.fc" in name and isinstance(module, nn.Conv2D) + ): + normal_(module.weight) + zeros_(module.bias) + elif isinstance(module, nn.Conv2D): + kaiming_normal_(module.weight) + if module.bias is not None: + zeros_(module.bias) + elif isinstance(module, (nn.BatchNorm2D, nn.LayerNorm, nn.GroupNorm)): + ones_(module.weight) + zeros_(module.bias) + elif zero_init_last and hasattr(module, "zero_init_last"): + module.zero_init_last() + + +@paddle.no_grad() +def _load_weights(model: nn.Layer, checkpoint_path: str, prefix: str = "resnet/"): + import numpy as np + + def t2p(conv_weights): + """Possibly convert HWIO to OIHW.""" + if conv_weights.ndim == 4: + conv_weights = conv_weights.transpose([3, 2, 0, 1]) + return paddle.to_tensor(conv_weights) + + weights = np.load(checkpoint_path) + stem_conv_w = adapt_input_conv( + model.stem.conv.weight.shape[1], + t2p(weights[f"{prefix}root_block/standardized_conv2d/kernel"]), + ) + model.stem.conv.weight.copy_(stem_conv_w) + model.norm.weight.copy_(t2p(weights[f"{prefix}group_norm/gamma"])) + model.norm.bias.copy_(t2p(weights[f"{prefix}group_norm/beta"])) + if ( + isinstance(getattr(model.head, "fc", None), nn.Conv2D) + and model.head.fc.weight.shape[0] + == weights[f"{prefix}head/conv2d/kernel"].shape[-1] + ): + model.head.fc.weight.copy_(t2p(weights[f"{prefix}head/conv2d/kernel"])) + model.head.fc.bias.copy_(t2p(weights[f"{prefix}head/conv2d/bias"])) + for i, (sname, stage) in enumerate(model.stages.named_children()): + for j, (bname, block) in enumerate(stage.blocks.named_children()): + cname = "standardized_conv2d" + block_prefix = f"{prefix}block{i + 1}/unit{j + 1:02d}/" + block.conv1.weight.copy_(t2p(weights[f"{block_prefix}a/{cname}/kernel"])) + block.conv2.weight.copy_(t2p(weights[f"{block_prefix}b/{cname}/kernel"])) + block.conv3.weight.copy_(t2p(weights[f"{block_prefix}c/{cname}/kernel"])) + block.norm1.weight.copy_(t2p(weights[f"{block_prefix}a/group_norm/gamma"])) + block.norm2.weight.copy_(t2p(weights[f"{block_prefix}b/group_norm/gamma"])) + block.norm3.weight.copy_(t2p(weights[f"{block_prefix}c/group_norm/gamma"])) + block.norm1.bias.copy_(t2p(weights[f"{block_prefix}a/group_norm/beta"])) + block.norm2.bias.copy_(t2p(weights[f"{block_prefix}b/group_norm/beta"])) + block.norm3.bias.copy_(t2p(weights[f"{block_prefix}c/group_norm/beta"])) + if block.downsample is not None: + w = weights[f"{block_prefix}a/proj/{cname}/kernel"] + block.downsample.conv.weight.copy_(t2p(w)) diff --git a/modules/onnx_ocr_module/src/ppocr/modeling/backbones/rec_shallow_cnn.py b/modules/onnx_ocr_module/src/ppocr/modeling/backbones/rec_shallow_cnn.py new file mode 100644 index 0000000..e5a8b65 --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/modeling/backbones/rec_shallow_cnn.py @@ -0,0 +1,82 @@ +# copyright (c) 2022 PaddlePaddle Authors. All Rights Reserve. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +""" +This code is refer from: +https://github.com/open-mmlab/mmocr/blob/1.x/mmocr/models/textrecog/backbones/shallow_cnn.py +""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import math +import numpy as np +import paddle +from paddle import ParamAttr +import paddle.nn as nn +import paddle.nn.functional as F +from paddle.nn import MaxPool2D +from paddle.nn.initializer import KaimingNormal, Uniform, Constant + + +class ConvBNLayer(nn.Layer): + def __init__( + self, num_channels, filter_size, num_filters, stride, padding, num_groups=1 + ): + super(ConvBNLayer, self).__init__() + + self.conv = nn.Conv2D( + in_channels=num_channels, + out_channels=num_filters, + kernel_size=filter_size, + stride=stride, + padding=padding, + groups=num_groups, + weight_attr=ParamAttr(initializer=KaimingNormal()), + bias_attr=False, + ) + + self.bn = nn.BatchNorm2D( + num_filters, + weight_attr=ParamAttr(initializer=Uniform(0, 1)), + bias_attr=ParamAttr(initializer=Constant(0)), + ) + self.relu = nn.ReLU() + + def forward(self, inputs): + y = self.conv(inputs) + y = self.bn(y) + y = self.relu(y) + return y + + +class ShallowCNN(nn.Layer): + def __init__(self, in_channels=1, hidden_dim=512): + super().__init__() + assert isinstance(in_channels, int) + assert isinstance(hidden_dim, int) + + self.conv1 = ConvBNLayer(in_channels, 3, hidden_dim // 2, stride=1, padding=1) + self.conv2 = ConvBNLayer(hidden_dim // 2, 3, hidden_dim, stride=1, padding=1) + self.pool = nn.MaxPool2D(kernel_size=2, stride=2, padding=0) + self.out_channels = hidden_dim + + def forward(self, x): + x = self.conv1(x) + x = self.pool(x) + + x = self.conv2(x) + x = self.pool(x) + + return x diff --git a/modules/onnx_ocr_module/src/ppocr/modeling/backbones/rec_svtrnet.py b/modules/onnx_ocr_module/src/ppocr/modeling/backbones/rec_svtrnet.py new file mode 100644 index 0000000..427c87b --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/modeling/backbones/rec_svtrnet.py @@ -0,0 +1,642 @@ +# copyright (c) 2022 PaddlePaddle Authors. All Rights Reserve. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from paddle import ParamAttr +from paddle.nn.initializer import KaimingNormal +import numpy as np +import paddle +import paddle.nn as nn +from paddle.nn.initializer import TruncatedNormal, Constant, Normal + +trunc_normal_ = TruncatedNormal(std=0.02) +normal_ = Normal +zeros_ = Constant(value=0.0) +ones_ = Constant(value=1.0) + + +def drop_path(x, drop_prob=0.0, training=False): + """Drop paths (Stochastic Depth) per sample (when applied in main path of residual blocks). + the original name is misleading as 'Drop Connect' is a different form of dropout in a separate paper... + See discussion: https://github.com/tensorflow/tpu/issues/494#issuecomment-532968956 ... + """ + if drop_prob == 0.0 or not training: + return x + keep_prob = paddle.to_tensor(1 - drop_prob, dtype=x.dtype) + shape = (x.shape[0],) + (1,) * (x.ndim - 1) + random_tensor = keep_prob + paddle.rand(shape, dtype=x.dtype) + random_tensor = paddle.floor(random_tensor) # binarize + output = x.divide(keep_prob) * random_tensor + return output + + +class ConvBNLayer(nn.Layer): + def __init__( + self, + in_channels, + out_channels, + kernel_size=3, + stride=1, + padding=0, + bias_attr=False, + groups=1, + act=nn.GELU, + ): + super().__init__() + self.conv = nn.Conv2D( + in_channels=in_channels, + out_channels=out_channels, + kernel_size=kernel_size, + stride=stride, + padding=padding, + groups=groups, + weight_attr=paddle.ParamAttr(initializer=nn.initializer.KaimingUniform()), + bias_attr=bias_attr, + ) + self.norm = nn.BatchNorm2D(out_channels) + self.act = act() + + def forward(self, inputs): + out = self.conv(inputs) + out = self.norm(out) + out = self.act(out) + return out + + +class DropPath(nn.Layer): + """Drop paths (Stochastic Depth) per sample (when applied in main path of residual blocks).""" + + def __init__(self, drop_prob=None): + super(DropPath, self).__init__() + self.drop_prob = drop_prob + + def forward(self, x): + return drop_path(x, self.drop_prob, self.training) + + +class Identity(nn.Layer): + def __init__(self): + super(Identity, self).__init__() + + def forward(self, input): + return input + + +class Mlp(nn.Layer): + def __init__( + self, + in_features, + hidden_features=None, + out_features=None, + act_layer=nn.GELU, + drop=0.0, + ): + super().__init__() + out_features = out_features or in_features + hidden_features = hidden_features or in_features + self.fc1 = nn.Linear(in_features, hidden_features) + self.act = act_layer() + self.fc2 = nn.Linear(hidden_features, out_features) + self.drop = nn.Dropout(drop) + + def forward(self, x): + x = self.fc1(x) + x = self.act(x) + x = self.drop(x) + x = self.fc2(x) + x = self.drop(x) + return x + + +class ConvMixer(nn.Layer): + def __init__( + self, + dim, + num_heads=8, + HW=[8, 25], + local_k=[3, 3], + ): + super().__init__() + self.HW = HW + self.dim = dim + self.local_mixer = nn.Conv2D( + dim, + dim, + local_k, + 1, + [local_k[0] // 2, local_k[1] // 2], + groups=num_heads, + weight_attr=ParamAttr(initializer=KaimingNormal()), + ) + + def forward(self, x): + h = self.HW[0] + w = self.HW[1] + x = x.transpose([0, 2, 1]).reshape([0, self.dim, h, w]) + x = self.local_mixer(x) + x = x.flatten(2).transpose([0, 2, 1]) + return x + + +class Attention(nn.Layer): + def __init__( + self, + dim, + num_heads=8, + mixer="Global", + HW=None, + local_k=[7, 11], + qkv_bias=False, + qk_scale=None, + attn_drop=0.0, + proj_drop=0.0, + ): + super().__init__() + self.num_heads = num_heads + self.dim = dim + self.head_dim = dim // num_heads + self.scale = qk_scale or self.head_dim**-0.5 + + self.qkv = nn.Linear(dim, dim * 3, bias_attr=qkv_bias) + self.attn_drop = nn.Dropout(attn_drop) + self.proj = nn.Linear(dim, dim) + self.proj_drop = nn.Dropout(proj_drop) + self.HW = HW + if HW is not None: + H = HW[0] + W = HW[1] + self.N = H * W + self.C = dim + if mixer == "Local" and HW is not None: + hk = local_k[0] + wk = local_k[1] + mask = paddle.ones([H * W, H + hk - 1, W + wk - 1], dtype="float32") + for h in range(0, H): + for w in range(0, W): + mask[h * W + w, h : h + hk, w : w + wk] = 0.0 + mask_paddle = mask[:, hk // 2 : H + hk // 2, wk // 2 : W + wk // 2].flatten( + 1 + ) + mask_inf = paddle.full([H * W, H * W], "-inf", dtype="float32") + mask = paddle.where(mask_paddle < 1, mask_paddle, mask_inf) + self.mask = mask.unsqueeze([0, 1]) + self.mixer = mixer + + def forward(self, x): + qkv = ( + self.qkv(x) + .reshape((0, -1, 3, self.num_heads, self.head_dim)) + .transpose((2, 0, 3, 1, 4)) + ) + q, k, v = qkv[0] * self.scale, qkv[1], qkv[2] + + attn = q.matmul(k.transpose((0, 1, 3, 2))) + if self.mixer == "Local": + attn += self.mask + attn = nn.functional.softmax(attn, axis=-1) + attn = self.attn_drop(attn) + + x = (attn.matmul(v)).transpose((0, 2, 1, 3)).reshape((0, -1, self.dim)) + x = self.proj(x) + x = self.proj_drop(x) + return x + + +class Block(nn.Layer): + def __init__( + self, + dim, + num_heads, + mixer="Global", + local_mixer=[7, 11], + HW=None, + mlp_ratio=4.0, + qkv_bias=False, + qk_scale=None, + drop=0.0, + attn_drop=0.0, + drop_path=0.0, + act_layer=nn.GELU, + norm_layer="nn.LayerNorm", + epsilon=1e-6, + prenorm=True, + ): + super().__init__() + if isinstance(norm_layer, str): + self.norm1 = eval(norm_layer)(dim, epsilon=epsilon) + else: + self.norm1 = norm_layer(dim) + if mixer == "Global" or mixer == "Local": + self.mixer = Attention( + dim, + num_heads=num_heads, + mixer=mixer, + HW=HW, + local_k=local_mixer, + qkv_bias=qkv_bias, + qk_scale=qk_scale, + attn_drop=attn_drop, + proj_drop=drop, + ) + elif mixer == "Conv": + self.mixer = ConvMixer(dim, num_heads=num_heads, HW=HW, local_k=local_mixer) + else: + raise TypeError("The mixer must be one of [Global, Local, Conv]") + + self.drop_path = DropPath(drop_path) if drop_path > 0.0 else Identity() + if isinstance(norm_layer, str): + self.norm2 = eval(norm_layer)(dim, epsilon=epsilon) + else: + self.norm2 = norm_layer(dim) + mlp_hidden_dim = int(dim * mlp_ratio) + self.mlp_ratio = mlp_ratio + self.mlp = Mlp( + in_features=dim, + hidden_features=mlp_hidden_dim, + act_layer=act_layer, + drop=drop, + ) + self.prenorm = prenorm + + def forward(self, x): + if self.prenorm: + x = self.norm1(x + self.drop_path(self.mixer(x))) + x = self.norm2(x + self.drop_path(self.mlp(x))) + else: + x = x + self.drop_path(self.mixer(self.norm1(x))) + x = x + self.drop_path(self.mlp(self.norm2(x))) + return x + + +class PatchEmbed(nn.Layer): + """Image to Patch Embedding""" + + def __init__( + self, + img_size=[32, 100], + in_channels=3, + embed_dim=768, + sub_num=2, + patch_size=[4, 4], + mode="pope", + ): + super().__init__() + num_patches = (img_size[1] // (2**sub_num)) * (img_size[0] // (2**sub_num)) + self.img_size = img_size + self.num_patches = num_patches + self.embed_dim = embed_dim + self.norm = None + if mode == "pope": + if sub_num == 2: + self.proj = nn.Sequential( + ConvBNLayer( + in_channels=in_channels, + out_channels=embed_dim // 2, + kernel_size=3, + stride=2, + padding=1, + act=nn.GELU, + bias_attr=None, + ), + ConvBNLayer( + in_channels=embed_dim // 2, + out_channels=embed_dim, + kernel_size=3, + stride=2, + padding=1, + act=nn.GELU, + bias_attr=None, + ), + ) + if sub_num == 3: + self.proj = nn.Sequential( + ConvBNLayer( + in_channels=in_channels, + out_channels=embed_dim // 4, + kernel_size=3, + stride=2, + padding=1, + act=nn.GELU, + bias_attr=None, + ), + ConvBNLayer( + in_channels=embed_dim // 4, + out_channels=embed_dim // 2, + kernel_size=3, + stride=2, + padding=1, + act=nn.GELU, + bias_attr=None, + ), + ConvBNLayer( + in_channels=embed_dim // 2, + out_channels=embed_dim, + kernel_size=3, + stride=2, + padding=1, + act=nn.GELU, + bias_attr=None, + ), + ) + elif mode == "linear": + self.proj = nn.Conv2D( + 1, embed_dim, kernel_size=patch_size, stride=patch_size + ) + self.num_patches = ( + img_size[0] // patch_size[0] * img_size[1] // patch_size[1] + ) + + def forward(self, x): + B, C, H, W = x.shape + assert ( + H == self.img_size[0] and W == self.img_size[1] + ), f"Input image size ({H}*{W}) doesn't match model ({self.img_size[0]}*{self.img_size[1]})." + x = self.proj(x).flatten(2).transpose((0, 2, 1)) + return x + + +class SubSample(nn.Layer): + def __init__( + self, + in_channels, + out_channels, + types="Pool", + stride=[2, 1], + sub_norm="nn.LayerNorm", + act=None, + ): + super().__init__() + self.types = types + if types == "Pool": + self.avgpool = nn.AvgPool2D( + kernel_size=[3, 5], stride=stride, padding=[1, 2] + ) + self.maxpool = nn.MaxPool2D( + kernel_size=[3, 5], stride=stride, padding=[1, 2] + ) + self.proj = nn.Linear(in_channels, out_channels) + else: + self.conv = nn.Conv2D( + in_channels, + out_channels, + kernel_size=3, + stride=stride, + padding=1, + weight_attr=ParamAttr(initializer=KaimingNormal()), + ) + self.norm = eval(sub_norm)(out_channels) + if act is not None: + self.act = act() + else: + self.act = None + + def forward(self, x): + if self.types == "Pool": + x1 = self.avgpool(x) + x2 = self.maxpool(x) + x = (x1 + x2) * 0.5 + out = self.proj(x.flatten(2).transpose((0, 2, 1))) + else: + x = self.conv(x) + out = x.flatten(2).transpose((0, 2, 1)) + out = self.norm(out) + if self.act is not None: + out = self.act(out) + + return out + + +class SVTRNet(nn.Layer): + def __init__( + self, + img_size=[32, 100], + in_channels=3, + embed_dim=[64, 128, 256], + depth=[3, 6, 3], + num_heads=[2, 4, 8], + mixer=["Local"] * 6 + ["Global"] * 6, # Local atten, Global atten, Conv + local_mixer=[[7, 11], [7, 11], [7, 11]], + patch_merging="Conv", # Conv, Pool, None + mlp_ratio=4, + qkv_bias=True, + qk_scale=None, + drop_rate=0.0, + last_drop=0.1, + attn_drop_rate=0.0, + drop_path_rate=0.1, + norm_layer="nn.LayerNorm", + sub_norm="nn.LayerNorm", + epsilon=1e-6, + out_channels=192, + out_char_num=25, + block_unit="Block", + act="nn.GELU", + last_stage=True, + sub_num=2, + prenorm=True, + use_lenhead=False, + **kwargs, + ): + super().__init__() + self.img_size = img_size + self.embed_dim = embed_dim + self.out_channels = out_channels + self.prenorm = prenorm + patch_merging = ( + None + if patch_merging != "Conv" and patch_merging != "Pool" + else patch_merging + ) + self.patch_embed = PatchEmbed( + img_size=img_size, + in_channels=in_channels, + embed_dim=embed_dim[0], + sub_num=sub_num, + ) + num_patches = self.patch_embed.num_patches + self.HW = [img_size[0] // (2**sub_num), img_size[1] // (2**sub_num)] + self.pos_embed = self.create_parameter( + shape=[1, num_patches, embed_dim[0]], default_initializer=zeros_ + ) + self.add_parameter("pos_embed", self.pos_embed) + self.pos_drop = nn.Dropout(p=drop_rate) + Block_unit = eval(block_unit) + + dpr = np.linspace(0, drop_path_rate, sum(depth)) + self.blocks1 = nn.LayerList( + [ + Block_unit( + dim=embed_dim[0], + num_heads=num_heads[0], + mixer=mixer[0 : depth[0]][i], + HW=self.HW, + local_mixer=local_mixer[0], + mlp_ratio=mlp_ratio, + qkv_bias=qkv_bias, + qk_scale=qk_scale, + drop=drop_rate, + act_layer=eval(act), + attn_drop=attn_drop_rate, + drop_path=dpr[0 : depth[0]][i], + norm_layer=norm_layer, + epsilon=epsilon, + prenorm=prenorm, + ) + for i in range(depth[0]) + ] + ) + if patch_merging is not None: + self.sub_sample1 = SubSample( + embed_dim[0], + embed_dim[1], + sub_norm=sub_norm, + stride=[2, 1], + types=patch_merging, + ) + HW = [self.HW[0] // 2, self.HW[1]] + else: + HW = self.HW + self.patch_merging = patch_merging + self.blocks2 = nn.LayerList( + [ + Block_unit( + dim=embed_dim[1], + num_heads=num_heads[1], + mixer=mixer[depth[0] : depth[0] + depth[1]][i], + HW=HW, + local_mixer=local_mixer[1], + mlp_ratio=mlp_ratio, + qkv_bias=qkv_bias, + qk_scale=qk_scale, + drop=drop_rate, + act_layer=eval(act), + attn_drop=attn_drop_rate, + drop_path=dpr[depth[0] : depth[0] + depth[1]][i], + norm_layer=norm_layer, + epsilon=epsilon, + prenorm=prenorm, + ) + for i in range(depth[1]) + ] + ) + if patch_merging is not None: + self.sub_sample2 = SubSample( + embed_dim[1], + embed_dim[2], + sub_norm=sub_norm, + stride=[2, 1], + types=patch_merging, + ) + HW = [self.HW[0] // 4, self.HW[1]] + else: + HW = self.HW + self.blocks3 = nn.LayerList( + [ + Block_unit( + dim=embed_dim[2], + num_heads=num_heads[2], + mixer=mixer[depth[0] + depth[1] :][i], + HW=HW, + local_mixer=local_mixer[2], + mlp_ratio=mlp_ratio, + qkv_bias=qkv_bias, + qk_scale=qk_scale, + drop=drop_rate, + act_layer=eval(act), + attn_drop=attn_drop_rate, + drop_path=dpr[depth[0] + depth[1] :][i], + norm_layer=norm_layer, + epsilon=epsilon, + prenorm=prenorm, + ) + for i in range(depth[2]) + ] + ) + self.last_stage = last_stage + if last_stage: + self.avg_pool = nn.AdaptiveAvgPool2D([1, out_char_num]) + self.last_conv = nn.Conv2D( + in_channels=embed_dim[2], + out_channels=self.out_channels, + kernel_size=1, + stride=1, + padding=0, + bias_attr=False, + ) + self.hardswish = nn.Hardswish() + self.dropout = nn.Dropout(p=last_drop, mode="downscale_in_infer") + if not prenorm: + self.norm = eval(norm_layer)(embed_dim[-1], epsilon=epsilon) + self.use_lenhead = use_lenhead + if use_lenhead: + self.len_conv = nn.Linear(embed_dim[2], self.out_channels) + self.hardswish_len = nn.Hardswish() + self.dropout_len = nn.Dropout(p=last_drop, mode="downscale_in_infer") + + trunc_normal_(self.pos_embed) + self.apply(self._init_weights) + + def _init_weights(self, m): + if isinstance(m, nn.Linear): + trunc_normal_(m.weight) + if isinstance(m, nn.Linear) and m.bias is not None: + zeros_(m.bias) + elif isinstance(m, nn.LayerNorm): + zeros_(m.bias) + ones_(m.weight) + + def forward_features(self, x): + x = self.patch_embed(x) + x = x + self.pos_embed + x = self.pos_drop(x) + for blk in self.blocks1: + x = blk(x) + if self.patch_merging is not None: + x = self.sub_sample1( + x.transpose([0, 2, 1]).reshape( + [0, self.embed_dim[0], self.HW[0], self.HW[1]] + ) + ) + for blk in self.blocks2: + x = blk(x) + if self.patch_merging is not None: + x = self.sub_sample2( + x.transpose([0, 2, 1]).reshape( + [0, self.embed_dim[1], self.HW[0] // 2, self.HW[1]] + ) + ) + for blk in self.blocks3: + x = blk(x) + if not self.prenorm: + x = self.norm(x) + return x + + def forward(self, x): + x = self.forward_features(x) + if self.use_lenhead: + len_x = self.len_conv(x.mean(1)) + len_x = self.dropout_len(self.hardswish_len(len_x)) + if self.last_stage: + if self.patch_merging is not None: + h = self.HW[0] // 4 + else: + h = self.HW[0] + x = self.avg_pool( + x.transpose([0, 2, 1]).reshape([0, self.embed_dim[2], h, self.HW[1]]) + ) + x = self.last_conv(x) + x = self.hardswish(x) + x = self.dropout(x) + if self.use_lenhead: + return x, len_x + return x diff --git a/modules/onnx_ocr_module/src/ppocr/modeling/backbones/rec_svtrv2.py b/modules/onnx_ocr_module/src/ppocr/modeling/backbones/rec_svtrv2.py new file mode 100644 index 0000000..03281b3 --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/modeling/backbones/rec_svtrv2.py @@ -0,0 +1,575 @@ +# copyright (c) 2024 PaddlePaddle Authors. All Rights Reserve. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from paddle import ParamAttr +from paddle.nn.initializer import KaimingNormal +import numpy as np +import paddle +import paddle.nn as nn +from paddle.nn.initializer import TruncatedNormal, Constant, Normal + +trunc_normal_ = TruncatedNormal(std=0.02) +normal_ = Normal +zeros_ = Constant(value=0.0) +ones_ = Constant(value=1.0) + + +def drop_path(x, drop_prob=0.0, training=False): + """Drop paths (Stochastic Depth) per sample (when applied in main path of residual blocks). + the original name is misleading as 'Drop Connect' is a different form of dropout in a separate paper... + See discussion: https://github.com/tensorflow/tpu/issues/494#issuecomment-532968956 ... + """ + if drop_prob == 0.0 or not training: + return x + keep_prob = paddle.to_tensor(1 - drop_prob, dtype=x.dtype) + shape = (paddle.shape(x)[0],) + (1,) * (x.ndim - 1) + random_tensor = keep_prob + paddle.rand(shape, dtype=x.dtype) + random_tensor = paddle.floor(random_tensor) # binarize + output = x.divide(keep_prob) * random_tensor + return output + + +class DropPath(nn.Layer): + """Drop paths (Stochastic Depth) per sample (when applied in main path of residual blocks).""" + + def __init__(self, drop_prob=None): + super(DropPath, self).__init__() + self.drop_prob = drop_prob + + def forward(self, x): + return drop_path(x, self.drop_prob, self.training) + + +class Identity(nn.Layer): + def __init__(self): + super(Identity, self).__init__() + + def forward(self, input): + return input + + +class Mlp(nn.Layer): + def __init__( + self, + in_features, + hidden_features=None, + out_features=None, + act_layer=nn.GELU, + drop=0.0, + ): + super().__init__() + out_features = out_features or in_features + hidden_features = hidden_features or in_features + self.fc1 = nn.Linear(in_features, hidden_features) + self.act = act_layer() + self.fc2 = nn.Linear(hidden_features, out_features) + self.drop = nn.Dropout(drop) + + def forward(self, x): + x = self.fc1(x) + x = self.act(x) + x = self.drop(x) + x = self.fc2(x) + x = self.drop(x) + return x + + +class ConvBNLayer(nn.Layer): + def __init__( + self, + in_channels, + out_channels, + kernel_size=3, + stride=1, + padding=0, + bias_attr=False, + groups=1, + act=nn.GELU, + ): + super().__init__() + self.conv = nn.Conv2D( + in_channels=in_channels, + out_channels=out_channels, + kernel_size=kernel_size, + stride=stride, + padding=padding, + groups=groups, + weight_attr=paddle.ParamAttr(initializer=nn.initializer.KaimingUniform()), + bias_attr=bias_attr, + ) + self.norm = nn.BatchNorm2D(out_channels) + self.act = act() + + def forward(self, inputs): + out = self.conv(inputs) + out = self.norm(out) + out = self.act(out) + return out + + +class Attention(nn.Layer): + def __init__( + self, + dim, + num_heads=8, + qkv_bias=False, + qk_scale=None, + attn_drop=0.0, + proj_drop=0.0, + ): + super().__init__() + self.num_heads = num_heads + self.dim = dim + self.head_dim = dim // num_heads + self.scale = qk_scale or self.head_dim**-0.5 + + self.qkv = nn.Linear(dim, dim * 3, bias_attr=qkv_bias) + self.attn_drop = nn.Dropout(attn_drop) + self.proj = nn.Linear(dim, dim) + self.proj_drop = nn.Dropout(proj_drop) + + def forward(self, x): + qkv = ( + self.qkv(x) + .reshape((0, -1, 3, self.num_heads, self.head_dim)) + .transpose((2, 0, 3, 1, 4)) + ) + q, k, v = qkv[0], qkv[1], qkv[2] + + attn = (q.matmul(k.transpose((0, 1, 3, 2)))) * self.scale + attn = nn.functional.softmax(attn, axis=-1) + attn = self.attn_drop(attn) + x = (attn.matmul(v)).transpose((0, 2, 1, 3)).reshape((0, -1, self.dim)) + x = self.proj(x) + x = self.proj_drop(x) + return x + + +class Block(nn.Layer): + def __init__( + self, + dim, + num_heads, + mlp_ratio=4.0, + qkv_bias=False, + qk_scale=None, + drop=0.0, + attn_drop=0.0, + drop_path=0.0, + act_layer=nn.GELU, + norm_layer=nn.LayerNorm, + epsilon=1e-6, + ): + super().__init__() + self.norm1 = norm_layer(dim, epsilon=epsilon) + self.mixer = Attention( + dim, + num_heads=num_heads, + qkv_bias=qkv_bias, + qk_scale=qk_scale, + attn_drop=attn_drop, + proj_drop=drop, + ) + self.drop_path = DropPath(drop_path) if drop_path > 0.0 else Identity() + self.norm2 = norm_layer(dim, epsilon=epsilon) + mlp_hidden_dim = int(dim * mlp_ratio) + self.mlp_ratio = mlp_ratio + self.mlp = Mlp( + in_features=dim, + hidden_features=mlp_hidden_dim, + act_layer=act_layer, + drop=drop, + ) + + def forward(self, x): + x = self.norm1(x + self.drop_path(self.mixer(x))) + x = self.norm2(x + self.drop_path(self.mlp(x))) + return x + + +class ConvBlock(nn.Layer): + def __init__( + self, + dim, + num_heads, + mlp_ratio=4.0, + drop=0.0, + drop_path=0.0, + act_layer=nn.GELU, + norm_layer=nn.LayerNorm, + epsilon=1e-6, + ): + super().__init__() + mlp_hidden_dim = int(dim * mlp_ratio) + self.norm1 = norm_layer(dim, epsilon=epsilon) + self.mixer = nn.Conv2D( + dim, + dim, + 5, + 1, + 2, + groups=num_heads, + weight_attr=ParamAttr(initializer=KaimingNormal()), + ) + self.drop_path = DropPath(drop_path) if drop_path > 0.0 else Identity() + self.norm2 = norm_layer(dim, epsilon=epsilon) + self.mlp = Mlp( + in_features=dim, + hidden_features=mlp_hidden_dim, + act_layer=act_layer, + drop=drop, + ) + + def forward(self, x): + C, H, W = x.shape[1:] + x = x + self.drop_path(self.mixer(x)) + x = self.norm1(x.flatten(2).transpose([0, 2, 1])) + x = self.norm2(x + self.drop_path(self.mlp(x))) + x = x.transpose([0, 2, 1]).reshape([0, C, H, W]) + return x + + +class FlattenTranspose(nn.Layer): + def forward(self, x): + return x.flatten(2).transpose([0, 2, 1]) + + +class SubSample2D(nn.Layer): + def __init__( + self, + in_channels, + out_channels, + stride=[2, 1], + ): + super().__init__() + self.conv = nn.Conv2D( + in_channels, + out_channels, + kernel_size=3, + stride=stride, + padding=1, + weight_attr=ParamAttr(initializer=KaimingNormal()), + ) + self.norm = nn.LayerNorm(out_channels) + + def forward(self, x, sz): + # print(x.shape) + x = self.conv(x) + C, H, W = x.shape[1:] + x = self.norm(x.flatten(2).transpose([0, 2, 1])) + x = x.transpose([0, 2, 1]).reshape([0, C, H, W]) + return x, [H, W] + + +class SubSample1D(nn.Layer): + def __init__( + self, + in_channels, + out_channels, + stride=[2, 1], + ): + super().__init__() + self.conv = nn.Conv2D( + in_channels, + out_channels, + kernel_size=3, + stride=stride, + padding=1, + weight_attr=ParamAttr(initializer=KaimingNormal()), + ) + self.norm = nn.LayerNorm(out_channels) + + def forward(self, x, sz): + C = x.shape[-1] + x = x.transpose([0, 2, 1]).reshape([0, C, sz[0], sz[1]]) + x = self.conv(x) + C, H, W = x.shape[1:] + x = self.norm(x.flatten(2).transpose([0, 2, 1])) + return x, [H, W] + + +class IdentitySize(nn.Layer): + def forward(self, x, sz): + return x, sz + + +class SVTRStage(nn.Layer): + def __init__( + self, + dim=64, + out_dim=256, + depth=3, + mixer=["Local"] * 3, + sub_k=[2, 1], + num_heads=2, + mlp_ratio=4, + qkv_bias=True, + qk_scale=None, + drop_rate=0.0, + attn_drop_rate=0.0, + drop_path=[0.1] * 3, + norm_layer=nn.LayerNorm, + act=nn.GELU, + eps=1e-6, + downsample=None, + **kwargs, + ): + super().__init__() + self.dim = dim + + conv_block_num = sum([1 if mix == "Conv" else 0 for mix in mixer]) + blocks = [] + for i in range(depth): + if mixer[i] == "Conv": + blocks.append( + ConvBlock( + dim=dim, + num_heads=num_heads, + mlp_ratio=mlp_ratio, + drop=drop_rate, + act_layer=act, + drop_path=drop_path[i], + norm_layer=norm_layer, + epsilon=eps, + ) + ) + else: + blocks.append( + Block( + dim=dim, + num_heads=num_heads, + mlp_ratio=mlp_ratio, + qkv_bias=qkv_bias, + qk_scale=qk_scale, + drop=drop_rate, + act_layer=act, + attn_drop=attn_drop_rate, + drop_path=drop_path[i], + norm_layer=norm_layer, + epsilon=eps, + ) + ) + if i == conv_block_num - 1 and mixer[-1] != "Conv": + blocks.append(FlattenTranspose()) + self.blocks = nn.Sequential(*blocks) + if downsample: + if mixer[-1] == "Conv": + self.downsample = SubSample2D(dim, out_dim, stride=sub_k) + elif mixer[-1] == "Global": + self.downsample = SubSample1D(dim, out_dim, stride=sub_k) + else: + self.downsample = IdentitySize() + + def forward(self, x, sz): + x = self.blocks(x) + x, sz = self.downsample(x, sz) + return x, sz + + +class ADDPosEmbed(nn.Layer): + def __init__(self, feat_max_size=[8, 32], embed_dim=768): + super().__init__() + pos_embed = paddle.zeros( + [1, feat_max_size[0] * feat_max_size[1], embed_dim], dtype=paddle.float32 + ) + trunc_normal_(pos_embed) + pos_embed = pos_embed.transpose([0, 2, 1]).reshape( + [1, embed_dim, feat_max_size[0], feat_max_size[1]] + ) + self.pos_embed = self.create_parameter( + [1, embed_dim, feat_max_size[0], feat_max_size[1]] + ) + self.add_parameter("pos_embed", self.pos_embed) + self.pos_embed.set_value(pos_embed) + + def forward(self, x): + sz = x.shape[2:] + x = x + self.pos_embed[:, :, : sz[0], : sz[1]] + return x + + +class POPatchEmbed(nn.Layer): + """Image to Patch Embedding""" + + def __init__( + self, + in_channels=3, + feat_max_size=[8, 32], + embed_dim=768, + use_pos_embed=False, + flatten=False, + ): + super().__init__() + patch_embed = [ + ConvBNLayer( + in_channels=in_channels, + out_channels=embed_dim // 2, + kernel_size=3, + stride=2, + padding=1, + act=nn.GELU, + bias_attr=None, + ), + ConvBNLayer( + in_channels=embed_dim // 2, + out_channels=embed_dim, + kernel_size=3, + stride=2, + padding=1, + act=nn.GELU, + bias_attr=None, + ), + ] + if use_pos_embed: + patch_embed.append(ADDPosEmbed(feat_max_size, embed_dim)) + if flatten: + patch_embed.append(FlattenTranspose()) + self.patch_embed = nn.Sequential(*patch_embed) + + def forward(self, x): + sz = x.shape[2:] + x = self.patch_embed(x) + return x, [sz[0] // 4, sz[1] // 4] + + +class LastStage(nn.Layer): + def __init__(self, in_channels, out_channels, last_drop, out_char_num): + super().__init__() + self.last_conv = nn.Linear(in_channels, out_channels, bias_attr=False) + self.hardswish = nn.Hardswish() + self.dropout = nn.Dropout(p=last_drop, mode="downscale_in_infer") + + def forward(self, x, sz): + x = x.reshape([0, sz[0], sz[1], x.shape[-1]]) + x = x.mean(1) + x = self.last_conv(x) + x = self.hardswish(x) + x = self.dropout(x) + return x, [1, sz[1]] + + +class OutPool(nn.Layer): + def __init__(self): + super().__init__() + + def forward(self, x, sz): + C = x.shape[-1] + x = x.transpose([0, 2, 1]).reshape([0, C, sz[0], sz[1]]) + x = nn.functional.avg_pool2d(x, [sz[0], 2]) + return x, [1, sz[1] // 2] + + +class Feat2D(nn.Layer): + def __init__(self): + super().__init__() + + def forward(self, x, sz): + C = x.shape[-1] + x = x.transpose([0, 2, 1]).reshape([0, C, sz[0], sz[1]]) + return x, sz + + +class SVTRv2(nn.Layer): + def __init__( + self, + max_sz=[32, 128], + in_channels=3, + out_channels=192, + out_char_num=25, + depths=[3, 6, 3], + dims=[64, 128, 256], + mixer=[["Conv"] * 3, ["Conv"] * 3 + ["Global"] * 3, ["Global"] * 3], + use_pos_embed=False, + sub_k=[[1, 1], [2, 1], [1, 1]], + num_heads=[2, 4, 8], + mlp_ratio=4, + qkv_bias=True, + qk_scale=None, + drop_rate=0.0, + last_drop=0.1, + attn_drop_rate=0.0, + drop_path_rate=0.1, + norm_layer=nn.LayerNorm, + act=nn.GELU, + last_stage=False, + eps=1e-6, + use_pool=False, + feat2d=False, + **kwargs, + ): + super().__init__() + num_stages = len(depths) + self.num_features = dims[-1] + + feat_max_size = [max_sz[0] // 4, max_sz[1] // 4] + self.pope = POPatchEmbed( + in_channels=in_channels, + feat_max_size=feat_max_size, + embed_dim=dims[0], + use_pos_embed=use_pos_embed, + flatten=mixer[0][0] != "Conv", + ) + + dpr = np.linspace(0, drop_path_rate, sum(depths)) # stochastic depth decay rule + + self.stages = nn.LayerList() + for i_stage in range(num_stages): + stage = SVTRStage( + dim=dims[i_stage], + out_dim=dims[i_stage + 1] if i_stage < num_stages - 1 else 0, + depth=depths[i_stage], + mixer=mixer[i_stage], + sub_k=sub_k[i_stage], + num_heads=num_heads[i_stage], + mlp_ratio=mlp_ratio, + qkv_bias=qkv_bias, + qk_scale=qk_scale, + drop=drop_rate, + attn_drop=attn_drop_rate, + drop_path=dpr[sum(depths[:i_stage]) : sum(depths[: i_stage + 1])], + norm_layer=norm_layer, + act=act, + downsample=False if i_stage == num_stages - 1 else True, + eps=eps, + ) + self.stages.append(stage) + + self.out_channels = self.num_features + self.last_stage = last_stage + if last_stage: + self.out_channels = out_channels + self.stages.append( + LastStage(self.num_features, out_channels, last_drop, out_char_num) + ) + if use_pool: + self.stages.append(OutPool()) + + if feat2d: + self.stages.append(Feat2D()) + self.apply(self._init_weights) + + def _init_weights(self, m): + if isinstance(m, nn.Linear): + trunc_normal_(m.weight) + if isinstance(m, nn.Linear) and m.bias is not None: + zeros_(m.bias) + elif isinstance(m, nn.LayerNorm): + zeros_(m.bias) + ones_(m.weight) + + def forward(self, x): + x, sz = self.pope(x) + for stage in self.stages: + x, sz = stage(x, sz) + return x diff --git a/modules/onnx_ocr_module/src/ppocr/modeling/backbones/rec_vary_vit.py b/modules/onnx_ocr_module/src/ppocr/modeling/backbones/rec_vary_vit.py new file mode 100644 index 0000000..d912ecf --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/modeling/backbones/rec_vary_vit.py @@ -0,0 +1,616 @@ +# copyright (c) 2024 PaddlePaddle Authors. All Rights Reserve. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import math +from functools import partial +from typing import Optional, Tuple, Type + +import numpy as np +import paddle +import paddle.nn as nn +import paddle.nn.functional as F +from paddle.nn.initializer import ( + Constant, + KaimingUniform, + Normal, + TruncatedNormal, + XavierUniform, +) +from ppocr.modeling.backbones.rec_donut_swin import DonutSwinModelOutput + +zeros_ = Constant(value=0.0) +ones_ = Constant(value=1.0) +kaiming_normal_ = KaimingUniform(nonlinearity="relu") +trunc_normal_ = TruncatedNormal(std=0.02) +xavier_uniform_ = XavierUniform() + + +class MLPBlock(nn.Layer): + def __init__( + self, + embedding_dim: int, + mlp_dim: int, + act: Type[nn.Layer] = nn.GELU, + ) -> None: + super().__init__() + self.lin1 = nn.Linear(embedding_dim, mlp_dim) + self.lin2 = nn.Linear(mlp_dim, embedding_dim) + self.act = act() + + def forward(self, x): + return self.lin2(self.act(self.lin1(x))) + + +# From https://github.com/facebookresearch/detectron2/blob/main/detectron2/layers/batch_norm.py # noqa +# Itself from https://github.com/facebookresearch/ConvNeXt/blob/d1fa8f6fef0a165b27399986cc2bdacc92777e40/models/convnext.py#L119 # noqa +class LayerNorm2d(nn.Layer): + def __init__(self, num_channels: int, epsilon: float = 1e-6) -> None: + super().__init__() + self.weight = paddle.create_parameter([num_channels], dtype="float32") + ones_(self.weight) + self.bias = paddle.create_parameter([num_channels], dtype="float32") + zeros_(self.bias) + self.epsilon = epsilon + + def forward(self, x): + u = x.mean(1, keepdim=True) + s = (x - u).pow(2).mean(1, keepdim=True) + x = (x - u) / paddle.sqrt(s + self.epsilon) + x = self.weight[:, None, None] * x + self.bias[:, None, None] + return x + + +# This class and its supporting functions below lightly adapted from the ViTDet backbone available at: https://github.com/facebookresearch/detectron2/blob/main/detectron2/modeling/backbone/vit.py # noqa +class ImageEncoderViT(nn.Layer): + def __init__( + self, + img_size: int = 1024, + patch_size: int = 16, + in_chans: int = 3, + embed_dim: int = 768, + depth: int = 12, + num_heads: int = 12, + mlp_ratio: float = 4.0, + out_chans: int = 256, + qkv_bias: bool = True, + norm_layer: Type[nn.Layer] = nn.LayerNorm, + act_layer: Type[nn.Layer] = nn.GELU, + use_abs_pos: bool = True, + use_rel_pos: bool = False, + rel_pos_zero_init: bool = True, + window_size: int = 0, + global_attn_indexes: Tuple[int, ...] = (), + is_formula: bool = False, + ) -> None: + """ + Args: + img_size (int): Input image size. + patch_size (int): Patch size. + in_chans (int): Number of input image channels. + embed_dim (int): Patch embedding dimension. + depth (int): Depth of ViT. + num_heads (int): Number of attention heads in each ViT block. + mlp_ratio (float): Ratio of mlp hidden dim to embedding dim. + qkv_bias (bool): If True, add a learnable bias to query, key, value. + norm_layer (nn.Layer): Normalization layer. + act_layer (nn.Layer): Activation layer. + use_abs_pos (bool): If True, use absolute positional embeddings. + use_rel_pos (bool): If True, add relative positional embeddings to the attention map. + rel_pos_zero_init (bool): If True, zero initialize relative positional parameters. + window_size (int): Window size for window attention blocks. + global_attn_indexes (list): Indexes for blocks using global attention. + """ + super().__init__() + self.img_size = img_size + + self.patch_embed = PatchEmbed( + kernel_size=(patch_size, patch_size), + stride=(patch_size, patch_size), + in_chans=in_chans, + embed_dim=embed_dim, + ) + + self.pos_embed = None + if use_abs_pos: + # Initialize absolute positional embedding with pretrain image size. + self.pos_embed = paddle.create_parameter( + shape=(1, img_size // patch_size, img_size // patch_size, embed_dim), + dtype="float32", + ) + zeros_(self.pos_embed) + + self.blocks = nn.LayerList() + for i in range(depth): + block = Block( + dim=embed_dim, + num_heads=num_heads, + mlp_ratio=mlp_ratio, + qkv_bias=qkv_bias, + norm_layer=norm_layer, + act_layer=act_layer, + use_rel_pos=use_rel_pos, + rel_pos_zero_init=rel_pos_zero_init, + window_size=window_size if i not in global_attn_indexes else 0, + input_size=(img_size // patch_size, img_size // patch_size), + ) + self.blocks.append(block) + + self.neck = nn.Sequential( + nn.Conv2D( + embed_dim, + out_chans, + kernel_size=1, + bias_attr=False, + ), + LayerNorm2d(out_chans), + nn.Conv2D( + out_chans, + out_chans, + kernel_size=3, + padding=1, + bias_attr=False, + ), + LayerNorm2d(out_chans), + ) + + self.net_2 = nn.Conv2D( + 256, 512, kernel_size=3, stride=2, padding=1, bias_attr=False + ) + self.net_3 = nn.Conv2D( + 512, 1024, kernel_size=3, stride=2, padding=1, bias_attr=False + ) + self.is_formula = is_formula + + def forward(self, x): + x = self.patch_embed(x) + if self.pos_embed is not None: + x = x + self.pos_embed + for blk in self.blocks: + x = blk(x) + x = self.neck(x.transpose([0, 3, 1, 2])) + x = self.net_2(x) + if self.is_formula: + x = self.net_3(x) + return x + + +class Block(nn.Layer): + """Transformer blocks with support of window attention and residual propagation blocks""" + + def __init__( + self, + dim: int, + num_heads: int, + mlp_ratio: float = 4.0, + qkv_bias: bool = True, + norm_layer: Type[nn.Layer] = nn.LayerNorm, + act_layer: Type[nn.Layer] = nn.GELU, + use_rel_pos: bool = False, + rel_pos_zero_init: bool = True, + window_size: int = 0, + input_size: Optional[Tuple[int, int]] = None, + ) -> None: + """ + Args: + dim (int): Number of input channels. + num_heads (int): Number of attention heads in each ViT block. + mlp_ratio (float): Ratio of mlp hidden dim to embedding dim. + qkv_bias (bool): If True, add a learnable bias to query, key, value. + norm_layer (nn.Layer): Normalization layer. + act_layer (nn.Layer): Activation layer. + use_rel_pos (bool): If True, add relative positional embeddings to the attention map. + rel_pos_zero_init (bool): If True, zero initialize relative positional parameters. + window_size (int): Window size for window attention blocks. If it equals 0, then + use global attention. + input_size (tuple(int, int) or None): Input resolution for calculating the relative + positional parameter size. + """ + super().__init__() + self.norm1 = norm_layer(dim) + self.attn = Attention( + dim, + num_heads=num_heads, + qkv_bias=qkv_bias, + use_rel_pos=use_rel_pos, + rel_pos_zero_init=rel_pos_zero_init, + input_size=input_size if window_size == 0 else (window_size, window_size), + ) + + self.norm2 = norm_layer(dim) + self.mlp = MLPBlock( + embedding_dim=dim, mlp_dim=int(dim * mlp_ratio), act=act_layer + ) + + self.window_size = window_size + + def forward(self, x): + shortcut = x + + x = self.norm1(x) + # Window partition + if self.window_size > 0: + H, W = x.shape[1], x.shape[2] + x, pad_hw = window_partition(x, self.window_size) + x = self.attn(x) + # Reverse window partition + if self.window_size > 0: + x = window_unpartition(x, self.window_size, pad_hw, (H, W)) + x = shortcut + x + x = x + self.mlp(self.norm2(x)) + + return x + + +class Attention(nn.Layer): + """Multi-head Attention block with relative position embeddings.""" + + def __init__( + self, + dim: int, + num_heads: int = 8, + qkv_bias: bool = True, + use_rel_pos: bool = False, + rel_pos_zero_init: bool = True, + input_size: Optional[Tuple[int, int]] = None, + ) -> None: + """ + Args: + dim (int): Number of input channels. + num_heads (int): Number of attention heads. + qkv_bias (bool): If True, add a learnable bias to query, key, value. + rel_pos (bool): If True, add relative positional embeddings to the attention map. + rel_pos_zero_init (bool): If True, zero initialize relative positional parameters. + input_size (tuple(int, int) or None): Input resolution for calculating the relative + positional parameter size. + """ + super().__init__() + self.num_heads = num_heads + head_dim = dim // num_heads + self.scale = head_dim**-0.5 + + self.qkv = nn.Linear(dim, dim * 3, bias_attr=qkv_bias) + self.proj = nn.Linear(dim, dim) + + self.use_rel_pos = use_rel_pos + if self.use_rel_pos: + assert ( + input_size is not None + ), "Input size must be provided if using relative positional encoding." + # initialize relative positional embeddings + self.rel_pos_h = paddle.create_parameter( + [2 * input_size[0] - 1, head_dim], dtype="float32" + ) + zeros_(self.rel_pos_h) + self.rel_pos_w = paddle.create_parameter( + [2 * input_size[1] - 1, head_dim], dtype="float32" + ) + zeros_(self.rel_pos_w) + + def forward(self, x): + + B, H, W, _ = x.shape + qkv = ( + self.qkv(x) + .reshape([B, H * W, 3, self.num_heads, -1]) + .transpose([2, 0, 3, 1, 4]) + ) + q, k, v = qkv.reshape([3, B * self.num_heads, H * W, -1]).unbind(0) + attn = (q * self.scale) @ k.transpose([0, 2, 1]) + + if self.use_rel_pos: + attn = add_decomposed_rel_pos( + attn, q, self.rel_pos_h, self.rel_pos_w, (H, W), (H, W) + ) + attn = F.softmax(attn, axis=-1) + x = ( + (attn @ v) + .reshape([B, self.num_heads, H, W, -1]) + .transpose([0, 2, 3, 1, 4]) + .reshape([B, H, W, -1]) + ) + x = self.proj(x) + + return x + + +def window_partition(x, window_size: int): + """ + Partition into non-overlapping windows with padding if needed. + Args: + x (tensor): input tokens with [B, H, W, C]. + window_size (int): window size. + + Returns: + windows: windows after partition with [B * num_windows, window_size, window_size, C]. + (Hp, Wp): padded height and width before partition + """ + B, H, W, C = x.shape + + pad_h = (window_size - H % window_size) % window_size + pad_w = (window_size - W % window_size) % window_size + if pad_h > 0 or pad_w > 0: + x = F.pad(x, (0, 0, 0, pad_w, 0, pad_h, 0, 0)) + Hp, Wp = H + pad_h, W + pad_w + + x = x.reshape( + [B, Hp // window_size, window_size, Wp // window_size, window_size, C] + ) + windows = x.transpose([0, 1, 3, 2, 4, 5]).reshape([-1, window_size, window_size, C]) + return windows, (Hp, Wp) + + +def window_unpartition( + windows, window_size: int, pad_hw: Tuple[int, int], hw: Tuple[int, int] +): + """ + Window unpartition into original sequences and removing padding. + Args: + windows (tensor): input tokens with [B * num_windows, window_size, window_size, C]. + window_size (int): window size. + pad_hw (Tuple): padded height and width (Hp, Wp). + hw (Tuple): original height and width (H, W) before padding. + + Returns: + x: unpartitioned sequences with [B, H, W, C]. + """ + Hp, Wp = pad_hw + H, W = hw + B = windows.shape[0] // (Hp * Wp // window_size // window_size) + x = windows.reshape( + [B, Hp // window_size, Wp // window_size, window_size, window_size, -1] + ) + x = x.transpose([0, 1, 3, 2, 4, 5]).contiguous().reshape([B, Hp, Wp, -1]) + + if Hp > H or Wp > W: + x = x[:, :H, :W, :].contiguous() + return x + + +def get_rel_pos(q_size: int, k_size: int, rel_pos): + """ + Get relative positional embeddings according to the relative positions of + query and key sizes. + Args: + q_size (int): size of query q. + k_size (int): size of key k. + rel_pos (Tensor): relative position embeddings (L, C). + + Returns: + Extracted positional embeddings according to relative positions. + """ + max_rel_dist = int(2 * max(q_size, k_size) - 1) + # Interpolate rel pos if needed. + if rel_pos.shape[0] != max_rel_dist: + # Interpolate rel pos. + rel_pos_resized = F.interpolate( + rel_pos.reshape(1, rel_pos.shape[0], -1).transpose(0, 2, 1), + size=max_rel_dist, + mode="linear", + ) + rel_pos_resized = rel_pos_resized.reshape(-1, max_rel_dist).transpose(1, 0) + else: + rel_pos_resized = rel_pos + + # Scale the coords with short length if shapes for q and k are different. + q_coords = paddle.arange(q_size)[:, None] * max(k_size / q_size, 1.0) + k_coords = paddle.arange(k_size)[None, :] * max(q_size / k_size, 1.0) + relative_coords = (q_coords - k_coords) + (k_size - 1) * max(q_size / k_size, 1.0) + + return rel_pos_resized[relative_coords.cast(paddle.int64)] + + +def add_decomposed_rel_pos( + attn, + q, + rel_pos_h, + rel_pos_w, + q_size: Tuple[int, int], + k_size: Tuple[int, int], +): + """ + Calculate decomposed Relative Positional Embeddings from :paper:`mvitv2`. + https://github.com/facebookresearch/mvit/blob/19786631e330df9f3622e5402b4a419a263a2c80/mvit/models/attention.py # noqa B950 + Args: + attn (Tensor): attention map. + q (Tensor): query q in the attention layer with shape (B, q_h * q_w, C). + rel_pos_h (Tensor): relative position embeddings (Lh, C) for height axis. + rel_pos_w (Tensor): relative position embeddings (Lw, C) for width axis. + q_size (Tuple): spatial sequence size of query q with (q_h, q_w). + k_size (Tuple): spatial sequence size of key k with (k_h, k_w). + + Returns: + attn (Tensor): attention map with added relative positional embeddings. + """ + q_h, q_w = q_size + k_h, k_w = k_size + Rh = get_rel_pos(q_h, k_h, rel_pos_h) + Rw = get_rel_pos(q_w, k_w, rel_pos_w) + + B, _, dim = q.shape + r_q = q.reshape([B, q_h, q_w, dim]) + rel_h = paddle.einsum("bhwc,hkc->bhwk", r_q, Rh) + rel_w = paddle.einsum("bhwc,wkc->bhwk", r_q, Rw) + + attn = ( + attn.reshape([B, q_h, q_w, k_h, k_w]) + + rel_h[:, :, :, :, None] + + rel_w[:, :, :, None, :] + ).reshape([B, q_h * q_w, k_h * k_w]) + + return attn + + +class PatchEmbed(nn.Layer): + """ + Image to Patch Embedding. + """ + + def __init__( + self, + kernel_size: Tuple[int, int] = (16, 16), + stride: Tuple[int, int] = (16, 16), + padding: Tuple[int, int] = (0, 0), + in_chans: int = 3, + embed_dim: int = 768, + ) -> None: + """ + Args: + kernel_size (Tuple): kernel size of the projection layer. + stride (Tuple): stride of the projection layer. + padding (Tuple): padding size of the projection layer. + in_chans (int): Number of input image channels. + embed_dim (int): Patch embedding dimension. + """ + super().__init__() + + self.proj = nn.Conv2D( + in_chans, + embed_dim, + kernel_size=kernel_size, + stride=stride, + padding=padding, + weight_attr=True, + bias_attr=True, + ) + + def forward(self, x): + x = self.proj(x) + # B C H W -> B H W C + x = x.transpose([0, 2, 3, 1]) + return x + + +def _build_vary( + encoder_embed_dim, + encoder_depth, + encoder_num_heads, + encoder_global_attn_indexes, + image_size, + is_formula=False, +): + prompt_embed_dim = 256 + vit_patch_size = 16 + image_embedding_size = image_size // vit_patch_size + image_encoder = ImageEncoderViT( + depth=encoder_depth, + embed_dim=encoder_embed_dim, + img_size=image_size, + mlp_ratio=4, + norm_layer=partial(paddle.nn.LayerNorm, epsilon=1e-6), + num_heads=encoder_num_heads, + patch_size=vit_patch_size, + qkv_bias=True, + use_rel_pos=True, + global_attn_indexes=encoder_global_attn_indexes, + window_size=14, + out_chans=prompt_embed_dim, + is_formula=is_formula, + ) + return image_encoder + + +class Vary_VIT_B(nn.Layer): + def __init__( + self, + in_channels=3, + image_size=768, + encoder_embed_dim=768, + encoder_depth=12, + encoder_num_heads=12, + encoder_global_attn_indexes=[2, 5, 8, 11], + ): + super().__init__() + + self.vision_tower_high = _build_vary( + encoder_embed_dim=768, + encoder_depth=12, + encoder_num_heads=12, + encoder_global_attn_indexes=[2, 5, 8, 11], + image_size=image_size, + ) + + self.out_channels = 1024 + + def forward(self, input_data): + pixel_values = input_data + num_channels = pixel_values.shape[1] + if num_channels == 1: + pixel_values = paddle.repeat_interleave(pixel_values, repeats=3, axis=1) + cnn_feature = self.vision_tower_high(pixel_values) + cnn_feature = cnn_feature.flatten(2).transpose([0, 2, 1]) + return cnn_feature + + +class Vary_VIT_B_Formula(nn.Layer): + def __init__( + self, + in_channels=3, + image_size=768, + encoder_embed_dim=768, + encoder_depth=12, + encoder_num_heads=12, + encoder_global_attn_indexes=[2, 5, 8, 11], + ): + """ + Vary_VIT_B_Formula + Args: + in_channels (int): Number of input channels. Default is 3 (for RGB images). + image_size (int): Size of the input image. Default is 768. + encoder_embed_dim (int): Dimension of the encoder's embedding. Default is 768. + encoder_depth (int): Number of layers (depth) in the encoder. Default is 12. + encoder_num_heads (int): Number of attention heads in the encoder. Default is 12. + encoder_global_attn_indexes (list): List of indices specifying which encoder layers use global attention. Default is [2, 5, 8, 11]. + Returns: + model: nn.Layer. Specific `Vary_VIT_B_Formula` model with defined architecture. + """ + super(Vary_VIT_B_Formula, self).__init__() + + self.vision_tower_high = _build_vary( + encoder_embed_dim=encoder_embed_dim, + encoder_depth=encoder_depth, + encoder_num_heads=encoder_num_heads, + encoder_global_attn_indexes=[2, 5, 8, 11], + image_size=image_size, + is_formula=True, + ) + self.mm_projector_vary = nn.Linear(1024, 1024) + self.out_channels = 1024 + + def forward(self, input_data): + if self.training: + pixel_values, label, attention_mask = input_data + else: + if isinstance(input_data, list): + pixel_values = input_data[0] + else: + pixel_values = input_data + num_channels = pixel_values.shape[1] + if num_channels == 1: + pixel_values = paddle.repeat_interleave(pixel_values, repeats=3, axis=1) + + cnn_feature = self.vision_tower_high(pixel_values) + cnn_feature = cnn_feature.flatten(2).transpose([0, 2, 1]) + + cnn_feature = self.mm_projector_vary(cnn_feature) + donut_swin_output = DonutSwinModelOutput( + last_hidden_state=cnn_feature, + pooler_output=None, + hidden_states=None, + attentions=None, + reshaped_hidden_states=None, + ) + if self.training: + return donut_swin_output, label, attention_mask + else: + return donut_swin_output diff --git a/modules/onnx_ocr_module/src/ppocr/modeling/backbones/rec_vit.py b/modules/onnx_ocr_module/src/ppocr/modeling/backbones/rec_vit.py new file mode 100644 index 0000000..165b868 --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/modeling/backbones/rec_vit.py @@ -0,0 +1,273 @@ +# copyright (c) 2023 PaddlePaddle Authors. All Rights Reserve. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from paddle import ParamAttr +from paddle.nn.initializer import KaimingNormal +import numpy as np +import paddle +import paddle.nn as nn +from paddle.nn.initializer import TruncatedNormal, Constant, Normal + +trunc_normal_ = TruncatedNormal(std=0.02) +normal_ = Normal +zeros_ = Constant(value=0.0) +ones_ = Constant(value=1.0) + + +def drop_path(x, drop_prob=0.0, training=False): + """Drop paths (Stochastic Depth) per sample (when applied in main path of residual blocks). + the original name is misleading as 'Drop Connect' is a different form of dropout in a separate paper... + See discussion: https://github.com/tensorflow/tpu/issues/494#issuecomment-532968956 ... + """ + if drop_prob == 0.0 or not training: + return x + keep_prob = paddle.to_tensor(1 - drop_prob) + shape = (x.shape[0],) + (1,) * (x.ndim - 1) + random_tensor = keep_prob + paddle.rand(shape, dtype=x.dtype) + random_tensor = paddle.floor(random_tensor) # binarize + output = x.divide(keep_prob) * random_tensor + return output + + +class DropPath(nn.Layer): + """Drop paths (Stochastic Depth) per sample (when applied in main path of residual blocks).""" + + def __init__(self, drop_prob=None): + super(DropPath, self).__init__() + self.drop_prob = drop_prob + + def forward(self, x): + return drop_path(x, self.drop_prob, self.training) + + +class Identity(nn.Layer): + def __init__(self): + super(Identity, self).__init__() + + def forward(self, input): + return input + + +class Mlp(nn.Layer): + def __init__( + self, + in_features, + hidden_features=None, + out_features=None, + act_layer=nn.GELU, + drop=0.0, + ): + super().__init__() + out_features = out_features or in_features + hidden_features = hidden_features or in_features + self.fc1 = nn.Linear(in_features, hidden_features) + self.act = act_layer() + self.fc2 = nn.Linear(hidden_features, out_features) + self.drop = nn.Dropout(drop) + + def forward(self, x): + x = self.fc1(x) + x = self.act(x) + x = self.drop(x) + x = self.fc2(x) + x = self.drop(x) + return x + + +class Attention(nn.Layer): + def __init__( + self, + dim, + num_heads=8, + qkv_bias=False, + qk_scale=None, + attn_drop=0.0, + proj_drop=0.0, + ): + super().__init__() + self.num_heads = num_heads + self.dim = dim + head_dim = dim // num_heads + self.scale = qk_scale or head_dim**-0.5 + + self.qkv = nn.Linear(dim, dim * 3, bias_attr=qkv_bias) + self.attn_drop = nn.Dropout(attn_drop) + self.proj = nn.Linear(dim, dim) + self.proj_drop = nn.Dropout(proj_drop) + + def forward(self, x): + qkv = paddle.reshape( + self.qkv(x), (0, -1, 3, self.num_heads, self.dim // self.num_heads) + ).transpose((2, 0, 3, 1, 4)) + q, k, v = qkv[0] * self.scale, qkv[1], qkv[2] + + attn = q.matmul(k.transpose((0, 1, 3, 2))) + attn = nn.functional.softmax(attn, axis=-1) + attn = self.attn_drop(attn) + + x = (attn.matmul(v)).transpose((0, 2, 1, 3)).reshape((0, -1, self.dim)) + x = self.proj(x) + x = self.proj_drop(x) + return x + + +class Block(nn.Layer): + def __init__( + self, + dim, + num_heads, + mlp_ratio=4.0, + qkv_bias=False, + qk_scale=None, + drop=0.0, + attn_drop=0.0, + drop_path=0.0, + act_layer=nn.GELU, + norm_layer="nn.LayerNorm", + epsilon=1e-6, + prenorm=True, + ): + super().__init__() + if isinstance(norm_layer, str): + self.norm1 = eval(norm_layer)(dim, epsilon=epsilon) + else: + self.norm1 = norm_layer(dim) + self.mixer = Attention( + dim, + num_heads=num_heads, + qkv_bias=qkv_bias, + qk_scale=qk_scale, + attn_drop=attn_drop, + proj_drop=drop, + ) + + self.drop_path = DropPath(drop_path) if drop_path > 0.0 else Identity() + if isinstance(norm_layer, str): + self.norm2 = eval(norm_layer)(dim, epsilon=epsilon) + else: + self.norm2 = norm_layer(dim) + mlp_hidden_dim = int(dim * mlp_ratio) + self.mlp_ratio = mlp_ratio + self.mlp = Mlp( + in_features=dim, + hidden_features=mlp_hidden_dim, + act_layer=act_layer, + drop=drop, + ) + self.prenorm = prenorm + + def forward(self, x): + if self.prenorm: + x = self.norm1(x + self.drop_path(self.mixer(x))) + x = self.norm2(x + self.drop_path(self.mlp(x))) + else: + x = x + self.drop_path(self.mixer(self.norm1(x))) + x = x + self.drop_path(self.mlp(self.norm2(x))) + return x + + +class ViT(nn.Layer): + def __init__( + self, + img_size=[32, 128], + patch_size=[4, 4], + in_channels=3, + embed_dim=384, + depth=12, + num_heads=6, + mlp_ratio=4, + qkv_bias=False, + qk_scale=None, + drop_rate=0.0, + attn_drop_rate=0.0, + drop_path_rate=0.1, + norm_layer="nn.LayerNorm", + epsilon=1e-6, + act="nn.GELU", + prenorm=False, + **kwargs, + ): + super().__init__() + self.embed_dim = embed_dim + self.out_channels = embed_dim + self.prenorm = prenorm + self.patch_embed = nn.Conv2D( + in_channels, embed_dim, patch_size, patch_size, padding=(0, 0) + ) + self.pos_embed = self.create_parameter( + shape=[1, 257, embed_dim], default_initializer=zeros_ + ) + self.add_parameter("pos_embed", self.pos_embed) + self.pos_drop = nn.Dropout(p=drop_rate) + dpr = np.linspace(0, drop_path_rate, depth) + self.blocks1 = nn.LayerList( + [ + Block( + dim=embed_dim, + num_heads=num_heads, + mlp_ratio=mlp_ratio, + qkv_bias=qkv_bias, + qk_scale=qk_scale, + drop=drop_rate, + act_layer=eval(act), + attn_drop=attn_drop_rate, + drop_path=dpr[i], + norm_layer=norm_layer, + epsilon=epsilon, + prenorm=prenorm, + ) + for i in range(depth) + ] + ) + if not prenorm: + self.norm = eval(norm_layer)(embed_dim, epsilon=epsilon) + + self.avg_pool = nn.AdaptiveAvgPool2D([1, 25]) + self.last_conv = nn.Conv2D( + in_channels=embed_dim, + out_channels=self.out_channels, + kernel_size=1, + stride=1, + padding=0, + bias_attr=False, + ) + self.hardswish = nn.Hardswish() + self.dropout = nn.Dropout(p=0.1, mode="downscale_in_infer") + + trunc_normal_(self.pos_embed) + self.apply(self._init_weights) + + def _init_weights(self, m): + if isinstance(m, nn.Linear): + trunc_normal_(m.weight) + if isinstance(m, nn.Linear) and m.bias is not None: + zeros_(m.bias) + elif isinstance(m, nn.LayerNorm): + zeros_(m.bias) + ones_(m.weight) + + def forward(self, x): + x = self.patch_embed(x).flatten(2).transpose((0, 2, 1)) + x = x + self.pos_embed[:, 1:, :] # [:, :x.shape[1], :] + x = self.pos_drop(x) + for blk in self.blocks1: + x = blk(x) + if not self.prenorm: + x = self.norm(x) + + x = self.avg_pool(x.transpose([0, 2, 1]).reshape([0, self.embed_dim, -1, 25])) + x = self.last_conv(x) + x = self.hardswish(x) + x = self.dropout(x) + return x diff --git a/modules/onnx_ocr_module/src/ppocr/modeling/backbones/rec_vit_parseq.py b/modules/onnx_ocr_module/src/ppocr/modeling/backbones/rec_vit_parseq.py new file mode 100644 index 0000000..049733f --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/modeling/backbones/rec_vit_parseq.py @@ -0,0 +1,348 @@ +# copyright (c) 2021 PaddlePaddle Authors. All Rights Reserve. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +""" +This code is refer from: +https://github.com/PaddlePaddle/PaddleClas/blob/release%2F2.5/ppcls/arch/backbone/model_zoo/vision_transformer.py +""" + +from collections.abc import Callable + +import numpy as np +import paddle +import paddle.nn as nn +from paddle.nn.initializer import TruncatedNormal, Constant, Normal + + +trunc_normal_ = TruncatedNormal(std=0.02) +normal_ = Normal +zeros_ = Constant(value=0.0) +ones_ = Constant(value=1.0) + + +def to_2tuple(x): + return tuple([x] * 2) + + +def drop_path(x, drop_prob=0.0, training=False): + """Drop paths (Stochastic Depth) per sample (when applied in main path of residual blocks). + the original name is misleading as 'Drop Connect' is a different form of dropout in a separate paper... + See discussion: https://github.com/tensorflow/tpu/issues/494#issuecomment-532968956 ... + """ + if drop_prob == 0.0 or not training: + return x + keep_prob = paddle.to_tensor(1 - drop_prob, dtype=x.dtype) + shape = (x.shape[0],) + (1,) * (x.ndim - 1) + random_tensor = keep_prob + paddle.rand(shape).astype(x.dtype) + random_tensor = paddle.floor(random_tensor) # binarize + output = x.divide(keep_prob) * random_tensor + return output + + +class DropPath(nn.Layer): + """Drop paths (Stochastic Depth) per sample (when applied in main path of residual blocks).""" + + def __init__(self, drop_prob=None): + super(DropPath, self).__init__() + self.drop_prob = drop_prob + + def forward(self, x): + return drop_path(x, self.drop_prob, self.training) + + +class Identity(nn.Layer): + def __init__(self): + super(Identity, self).__init__() + + def forward(self, input): + return input + + +class Mlp(nn.Layer): + def __init__( + self, + in_features, + hidden_features=None, + out_features=None, + act_layer=nn.GELU, + drop=0.0, + ): + super().__init__() + out_features = out_features or in_features + hidden_features = hidden_features or in_features + self.fc1 = nn.Linear(in_features, hidden_features) + self.act = act_layer() + self.fc2 = nn.Linear(hidden_features, out_features) + self.drop = nn.Dropout(drop) + + def forward(self, x): + x = self.fc1(x) + x = self.act(x) + x = self.drop(x) + x = self.fc2(x) + x = self.drop(x) + return x + + +class Attention(nn.Layer): + def __init__( + self, + dim, + num_heads=8, + qkv_bias=False, + qk_scale=None, + attn_drop=0.0, + proj_drop=0.0, + ): + super().__init__() + self.num_heads = num_heads + head_dim = dim // num_heads + self.scale = qk_scale or head_dim**-0.5 + + self.qkv = nn.Linear(dim, dim * 3, bias_attr=qkv_bias) + self.attn_drop = nn.Dropout(attn_drop) + self.proj = nn.Linear(dim, dim) + self.proj_drop = nn.Dropout(proj_drop) + + def forward(self, x): + # B= x.shape[0] + N, C = x.shape[1:] + qkv = ( + self.qkv(x) + .reshape((-1, N, 3, self.num_heads, C // self.num_heads)) + .transpose((2, 0, 3, 1, 4)) + ) + q, k, v = qkv[0], qkv[1], qkv[2] + + attn = (q.matmul(k.transpose((0, 1, 3, 2)))) * self.scale + attn = nn.functional.softmax(attn, axis=-1) + attn = self.attn_drop(attn) + + x = (attn.matmul(v)).transpose((0, 2, 1, 3)).reshape((-1, N, C)) + x = self.proj(x) + x = self.proj_drop(x) + return x + + +class Block(nn.Layer): + def __init__( + self, + dim, + num_heads, + mlp_ratio=4.0, + qkv_bias=False, + qk_scale=None, + drop=0.0, + attn_drop=0.0, + drop_path=0.0, + act_layer=nn.GELU, + norm_layer="nn.LayerNorm", + epsilon=1e-5, + ): + super().__init__() + if isinstance(norm_layer, str): + self.norm1 = eval(norm_layer)(dim, epsilon=epsilon) + elif isinstance(norm_layer, Callable): + self.norm1 = norm_layer(dim) + else: + raise TypeError("The norm_layer must be str or paddle.nn.layer.Layer class") + self.attn = Attention( + dim, + num_heads=num_heads, + qkv_bias=qkv_bias, + qk_scale=qk_scale, + attn_drop=attn_drop, + proj_drop=drop, + ) + # NOTE: drop path for stochastic depth, we shall see if this is better than dropout here + self.drop_path = DropPath(drop_path) if drop_path > 0.0 else Identity() + if isinstance(norm_layer, str): + self.norm2 = eval(norm_layer)(dim, epsilon=epsilon) + elif isinstance(norm_layer, Callable): + self.norm2 = norm_layer(dim) + else: + raise TypeError("The norm_layer must be str or paddle.nn.layer.Layer class") + mlp_hidden_dim = int(dim * mlp_ratio) + self.mlp = Mlp( + in_features=dim, + hidden_features=mlp_hidden_dim, + act_layer=act_layer, + drop=drop, + ) + + def forward(self, x): + x = x + self.drop_path(self.attn(self.norm1(x))) + x = x + self.drop_path(self.mlp(self.norm2(x))) + return x + + +class PatchEmbed(nn.Layer): + """Image to Patch Embedding""" + + def __init__(self, img_size=224, patch_size=16, in_chans=3, embed_dim=768): + super().__init__() + if isinstance(img_size, int): + img_size = to_2tuple(img_size) + if isinstance(patch_size, int): + patch_size = to_2tuple(patch_size) + num_patches = (img_size[1] // patch_size[1]) * (img_size[0] // patch_size[0]) + self.img_size = img_size + self.patch_size = patch_size + self.num_patches = num_patches + + self.proj = nn.Conv2D( + in_chans, embed_dim, kernel_size=patch_size, stride=patch_size + ) + + def forward(self, x): + B, C, H, W = x.shape + assert ( + H == self.img_size[0] and W == self.img_size[1] + ), f"Input image size ({H}*{W}) doesn't match model ({self.img_size[0]}*{self.img_size[1]})." + + x = self.proj(x).flatten(2).transpose((0, 2, 1)) + return x + + +class VisionTransformer(nn.Layer): + """Vision Transformer with support for patch input""" + + def __init__( + self, + img_size=224, + patch_size=16, + in_channels=3, + class_num=1000, + embed_dim=768, + depth=12, + num_heads=12, + mlp_ratio=4, + qkv_bias=False, + qk_scale=None, + drop_rate=0.0, + attn_drop_rate=0.0, + drop_path_rate=0.0, + norm_layer="nn.LayerNorm", + epsilon=1e-5, + **kwargs, + ): + super().__init__() + self.class_num = class_num + + self.num_features = self.embed_dim = embed_dim + + self.patch_embed = PatchEmbed( + img_size=img_size, + patch_size=patch_size, + in_chans=in_channels, + embed_dim=embed_dim, + ) + num_patches = self.patch_embed.num_patches + + self.pos_embed = self.create_parameter( + shape=(1, num_patches, embed_dim), default_initializer=zeros_ + ) + self.add_parameter("pos_embed", self.pos_embed) + self.cls_token = self.create_parameter( + shape=(1, 1, embed_dim), default_initializer=zeros_ + ) + self.add_parameter("cls_token", self.cls_token) + self.pos_drop = nn.Dropout(p=drop_rate) + + dpr = np.linspace(0, drop_path_rate, depth) + + self.blocks = nn.LayerList( + [ + Block( + dim=embed_dim, + num_heads=num_heads, + mlp_ratio=mlp_ratio, + qkv_bias=qkv_bias, + qk_scale=qk_scale, + drop=drop_rate, + attn_drop=attn_drop_rate, + drop_path=dpr[i], + norm_layer=norm_layer, + epsilon=epsilon, + ) + for i in range(depth) + ] + ) + + self.norm = eval(norm_layer)(embed_dim, epsilon=epsilon) + + # Classifier head + self.head = nn.Linear(embed_dim, class_num) if class_num > 0 else Identity() + + trunc_normal_(self.pos_embed) + self.out_channels = embed_dim + self.apply(self._init_weights) + + def _init_weights(self, m): + if isinstance(m, nn.Linear): + trunc_normal_(m.weight) + if isinstance(m, nn.Linear) and m.bias is not None: + zeros_(m.bias) + elif isinstance(m, nn.LayerNorm): + zeros_(m.bias) + ones_(m.weight) + + def forward_features(self, x): + B = x.shape[0] + x = self.patch_embed(x) + x = x + self.pos_embed + x = self.pos_drop(x) + for blk in self.blocks: + x = blk(x) + x = self.norm(x) + return x + + def forward(self, x): + x = self.forward_features(x) + x = self.head(x) + return x + + +class ViTParseQ(VisionTransformer): + def __init__( + self, + img_size=[224, 224], + patch_size=[16, 16], + in_channels=3, + embed_dim=768, + depth=12, + num_heads=12, + mlp_ratio=4.0, + qkv_bias=True, + drop_rate=0.0, + attn_drop_rate=0.0, + drop_path_rate=0.0, + ): + super().__init__( + img_size, + patch_size, + in_channels, + embed_dim=embed_dim, + depth=depth, + num_heads=num_heads, + mlp_ratio=mlp_ratio, + qkv_bias=qkv_bias, + drop_rate=drop_rate, + attn_drop_rate=attn_drop_rate, + drop_path_rate=drop_path_rate, + class_num=0, + ) + + def forward(self, x): + return self.forward_features(x) diff --git a/modules/onnx_ocr_module/src/ppocr/modeling/backbones/rec_vitstr.py b/modules/onnx_ocr_module/src/ppocr/modeling/backbones/rec_vitstr.py new file mode 100644 index 0000000..9a273da --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/modeling/backbones/rec_vitstr.py @@ -0,0 +1,133 @@ +# copyright (c) 2021 PaddlePaddle Authors. All Rights Reserve. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +""" +This code is refer from: +https://github.com/roatienza/deep-text-recognition-benchmark/blob/master/modules/vitstr.py +""" + +import numpy as np +import paddle +import paddle.nn as nn +from ppocr.modeling.backbones.rec_svtrnet import ( + Block, + PatchEmbed, + zeros_, + trunc_normal_, + ones_, +) + +scale_dim_heads = {"tiny": [192, 3], "small": [384, 6], "base": [768, 12]} + + +class ViTSTR(nn.Layer): + def __init__( + self, + img_size=[224, 224], + in_channels=1, + scale="tiny", + seqlen=27, + patch_size=[16, 16], + embed_dim=None, + depth=12, + num_heads=None, + mlp_ratio=4, + qkv_bias=True, + qk_scale=None, + drop_path_rate=0.0, + drop_rate=0.0, + attn_drop_rate=0.0, + norm_layer="nn.LayerNorm", + act_layer="nn.GELU", + epsilon=1e-6, + out_channels=None, + **kwargs, + ): + super().__init__() + self.seqlen = seqlen + embed_dim = embed_dim if embed_dim is not None else scale_dim_heads[scale][0] + num_heads = num_heads if num_heads is not None else scale_dim_heads[scale][1] + out_channels = out_channels if out_channels is not None else embed_dim + self.patch_embed = PatchEmbed( + img_size=img_size, + in_channels=in_channels, + embed_dim=embed_dim, + patch_size=patch_size, + mode="linear", + ) + num_patches = self.patch_embed.num_patches + + self.pos_embed = self.create_parameter( + shape=[1, num_patches + 1, embed_dim], default_initializer=zeros_ + ) + self.add_parameter("pos_embed", self.pos_embed) + self.cls_token = self.create_parameter( + shape=[1, 1, embed_dim], default_initializer=zeros_ + ) + self.add_parameter("cls_token", self.cls_token) + + self.pos_drop = nn.Dropout(p=drop_rate) + + dpr = np.linspace(0, drop_path_rate, depth) + self.blocks = nn.LayerList( + [ + Block( + dim=embed_dim, + num_heads=num_heads, + mlp_ratio=mlp_ratio, + qkv_bias=qkv_bias, + qk_scale=qk_scale, + drop=drop_rate, + attn_drop=attn_drop_rate, + drop_path=dpr[i], + norm_layer=norm_layer, + act_layer=eval(act_layer), + epsilon=epsilon, + prenorm=False, + ) + for i in range(depth) + ] + ) + self.norm = eval(norm_layer)(embed_dim, epsilon=epsilon) + + self.out_channels = out_channels + + trunc_normal_(self.pos_embed) + trunc_normal_(self.cls_token) + self.apply(self._init_weights) + + def _init_weights(self, m): + if isinstance(m, nn.Linear): + trunc_normal_(m.weight) + if isinstance(m, nn.Linear) and m.bias is not None: + zeros_(m.bias) + elif isinstance(m, nn.LayerNorm): + zeros_(m.bias) + ones_(m.weight) + + def forward_features(self, x): + B = x.shape[0] + x = self.patch_embed(x) + cls_tokens = paddle.tile(self.cls_token, repeat_times=[B, 1, 1]) + x = paddle.concat((cls_tokens, x), axis=1) + x = x + self.pos_embed + x = self.pos_drop(x) + for blk in self.blocks: + x = blk(x) + x = self.norm(x) + return x + + def forward(self, x): + x = self.forward_features(x) + x = x[:, : self.seqlen] + return x.transpose([0, 2, 1]).unsqueeze(2) diff --git a/modules/onnx_ocr_module/src/ppocr/modeling/backbones/table_master_resnet.py b/modules/onnx_ocr_module/src/ppocr/modeling/backbones/table_master_resnet.py new file mode 100644 index 0000000..6880ae3 --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/modeling/backbones/table_master_resnet.py @@ -0,0 +1,365 @@ +# Copyright (c) 2022 PaddlePaddle Authors. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +""" +This code is refer from: +https://github.com/JiaquanYe/TableMASTER-mmocr/blob/master/mmocr/models/textrecog/backbones/table_resnet_extra.py +""" + +import paddle +import paddle.nn as nn +import paddle.nn.functional as F + + +class BasicBlock(nn.Layer): + expansion = 1 + + def __init__(self, inplanes, planes, stride=1, downsample=None, gcb_config=None): + super(BasicBlock, self).__init__() + self.conv1 = nn.Conv2D( + inplanes, planes, kernel_size=3, stride=stride, padding=1, bias_attr=False + ) + self.bn1 = nn.BatchNorm2D(planes, momentum=0.9) + self.relu = nn.ReLU() + self.conv2 = nn.Conv2D( + planes, planes, kernel_size=3, stride=1, padding=1, bias_attr=False + ) + self.bn2 = nn.BatchNorm2D(planes, momentum=0.9) + self.downsample = downsample + self.stride = stride + self.gcb_config = gcb_config + + if self.gcb_config is not None: + gcb_ratio = gcb_config["ratio"] + gcb_headers = gcb_config["headers"] + att_scale = gcb_config["att_scale"] + fusion_type = gcb_config["fusion_type"] + self.context_block = MultiAspectGCAttention( + inplanes=planes, + ratio=gcb_ratio, + headers=gcb_headers, + att_scale=att_scale, + fusion_type=fusion_type, + ) + + def forward(self, x): + residual = x + + out = self.conv1(x) + out = self.bn1(out) + out = self.relu(out) + + out = self.conv2(out) + out = self.bn2(out) + + if self.gcb_config is not None: + out = self.context_block(out) + + if self.downsample is not None: + residual = self.downsample(x) + + out += residual + out = self.relu(out) + + return out + + +def get_gcb_config(gcb_config, layer): + if gcb_config is None or not gcb_config["layers"][layer]: + return None + else: + return gcb_config + + +class TableResNetExtra(nn.Layer): + def __init__(self, layers, in_channels=3, gcb_config=None): + assert len(layers) >= 4 + + super(TableResNetExtra, self).__init__() + self.inplanes = 128 + self.conv1 = nn.Conv2D( + in_channels, 64, kernel_size=3, stride=1, padding=1, bias_attr=False + ) + self.bn1 = nn.BatchNorm2D(64) + self.relu1 = nn.ReLU() + + self.conv2 = nn.Conv2D( + 64, 128, kernel_size=3, stride=1, padding=1, bias_attr=False + ) + self.bn2 = nn.BatchNorm2D(128) + self.relu2 = nn.ReLU() + + self.maxpool1 = nn.MaxPool2D(kernel_size=2, stride=2) + + self.layer1 = self._make_layer( + BasicBlock, + 256, + layers[0], + stride=1, + gcb_config=get_gcb_config(gcb_config, 0), + ) + + self.conv3 = nn.Conv2D( + 256, 256, kernel_size=3, stride=1, padding=1, bias_attr=False + ) + self.bn3 = nn.BatchNorm2D(256) + self.relu3 = nn.ReLU() + + self.maxpool2 = nn.MaxPool2D(kernel_size=2, stride=2) + + self.layer2 = self._make_layer( + BasicBlock, + 256, + layers[1], + stride=1, + gcb_config=get_gcb_config(gcb_config, 1), + ) + + self.conv4 = nn.Conv2D( + 256, 256, kernel_size=3, stride=1, padding=1, bias_attr=False + ) + self.bn4 = nn.BatchNorm2D(256) + self.relu4 = nn.ReLU() + + self.maxpool3 = nn.MaxPool2D(kernel_size=2, stride=2) + + self.layer3 = self._make_layer( + BasicBlock, + 512, + layers[2], + stride=1, + gcb_config=get_gcb_config(gcb_config, 2), + ) + + self.conv5 = nn.Conv2D( + 512, 512, kernel_size=3, stride=1, padding=1, bias_attr=False + ) + self.bn5 = nn.BatchNorm2D(512) + self.relu5 = nn.ReLU() + + self.layer4 = self._make_layer( + BasicBlock, + 512, + layers[3], + stride=1, + gcb_config=get_gcb_config(gcb_config, 3), + ) + + self.conv6 = nn.Conv2D( + 512, 512, kernel_size=3, stride=1, padding=1, bias_attr=False + ) + self.bn6 = nn.BatchNorm2D(512) + self.relu6 = nn.ReLU() + + self.out_channels = [256, 256, 512] + + def _make_layer(self, block, planes, blocks, stride=1, gcb_config=None): + downsample = None + if stride != 1 or self.inplanes != planes * block.expansion: + downsample = nn.Sequential( + nn.Conv2D( + self.inplanes, + planes * block.expansion, + kernel_size=1, + stride=stride, + bias_attr=False, + ), + nn.BatchNorm2D(planes * block.expansion), + ) + + layers = [] + layers.append( + block(self.inplanes, planes, stride, downsample, gcb_config=gcb_config) + ) + self.inplanes = planes * block.expansion + for _ in range(1, blocks): + layers.append(block(self.inplanes, planes)) + + return nn.Sequential(*layers) + + def forward(self, x): + f = [] + x = self.conv1(x) + + x = self.bn1(x) + x = self.relu1(x) + + x = self.conv2(x) + x = self.bn2(x) + x = self.relu2(x) + + x = self.maxpool1(x) + x = self.layer1(x) + + x = self.conv3(x) + x = self.bn3(x) + x = self.relu3(x) + f.append(x) + + x = self.maxpool2(x) + x = self.layer2(x) + + x = self.conv4(x) + x = self.bn4(x) + x = self.relu4(x) + f.append(x) + + x = self.maxpool3(x) + + x = self.layer3(x) + x = self.conv5(x) + x = self.bn5(x) + x = self.relu5(x) + + x = self.layer4(x) + x = self.conv6(x) + x = self.bn6(x) + x = self.relu6(x) + f.append(x) + return f + + +class MultiAspectGCAttention(nn.Layer): + def __init__( + self, + inplanes, + ratio, + headers, + pooling_type="att", + att_scale=False, + fusion_type="channel_add", + ): + super(MultiAspectGCAttention, self).__init__() + assert pooling_type in ["avg", "att"] + + assert fusion_type in ["channel_add", "channel_mul", "channel_concat"] + assert ( + inplanes % headers == 0 and inplanes >= 8 + ) # inplanes must be divided by headers evenly + + self.headers = headers + self.inplanes = inplanes + self.ratio = ratio + self.planes = int(inplanes * ratio) + self.pooling_type = pooling_type + self.fusion_type = fusion_type + self.att_scale = False + + self.single_header_inplanes = int(inplanes / headers) + + if pooling_type == "att": + self.conv_mask = nn.Conv2D(self.single_header_inplanes, 1, kernel_size=1) + self.softmax = nn.Softmax(axis=2) + else: + self.avg_pool = nn.AdaptiveAvgPool2D(1) + + if fusion_type == "channel_add": + self.channel_add_conv = nn.Sequential( + nn.Conv2D(self.inplanes, self.planes, kernel_size=1), + nn.LayerNorm([self.planes, 1, 1]), + nn.ReLU(), + nn.Conv2D(self.planes, self.inplanes, kernel_size=1), + ) + elif fusion_type == "channel_concat": + self.channel_concat_conv = nn.Sequential( + nn.Conv2D(self.inplanes, self.planes, kernel_size=1), + nn.LayerNorm([self.planes, 1, 1]), + nn.ReLU(), + nn.Conv2D(self.planes, self.inplanes, kernel_size=1), + ) + # for concat + self.cat_conv = nn.Conv2D(2 * self.inplanes, self.inplanes, kernel_size=1) + elif fusion_type == "channel_mul": + self.channel_mul_conv = nn.Sequential( + nn.Conv2D(self.inplanes, self.planes, kernel_size=1), + nn.LayerNorm([self.planes, 1, 1]), + nn.ReLU(), + nn.Conv2D(self.planes, self.inplanes, kernel_size=1), + ) + + def spatial_pool(self, x): + batch, channel, height, width = x.shape + if self.pooling_type == "att": + # [N*headers, C', H , W] C = headers * C' + x = x.reshape( + [batch * self.headers, self.single_header_inplanes, height, width] + ) + input_x = x + + # [N*headers, C', H * W] C = headers * C' + # input_x = input_x.view(batch, channel, height * width) + input_x = input_x.reshape( + [batch * self.headers, self.single_header_inplanes, height * width] + ) + + # [N*headers, 1, C', H * W] + input_x = input_x.unsqueeze(1) + # [N*headers, 1, H, W] + context_mask = self.conv_mask(x) + # [N*headers, 1, H * W] + context_mask = context_mask.reshape( + [batch * self.headers, 1, height * width] + ) + + # scale variance + if self.att_scale and self.headers > 1: + context_mask = context_mask / paddle.sqrt(self.single_header_inplanes) + + # [N*headers, 1, H * W] + context_mask = self.softmax(context_mask) + + # [N*headers, 1, H * W, 1] + context_mask = context_mask.unsqueeze(-1) + # [N*headers, 1, C', 1] = [N*headers, 1, C', H * W] * [N*headers, 1, H * W, 1] + context = paddle.matmul(input_x, context_mask) + + # [N, headers * C', 1, 1] + context = context.reshape( + [batch, self.headers * self.single_header_inplanes, 1, 1] + ) + else: + # [N, C, 1, 1] + context = self.avg_pool(x) + + return context + + def forward(self, x): + # [N, C, 1, 1] + context = self.spatial_pool(x) + + out = x + + if self.fusion_type == "channel_mul": + # [N, C, 1, 1] + channel_mul_term = F.sigmoid(self.channel_mul_conv(context)) + out = out * channel_mul_term + elif self.fusion_type == "channel_add": + # [N, C, 1, 1] + channel_add_term = self.channel_add_conv(context) + out = out + channel_add_term + else: + # [N, C, 1, 1] + channel_concat_term = self.channel_concat_conv(context) + + # use concat + _, C1, _, _ = channel_concat_term.shape + N, C2, H, W = out.shape + + out = paddle.concat( + [out, channel_concat_term.expand([-1, -1, H, W])], axis=1 + ) + out = self.cat_conv(out) + out = F.layer_norm(out, [self.inplanes, H, W]) + out = F.relu(out) + + return out diff --git a/modules/onnx_ocr_module/src/ppocr/modeling/backbones/vqa_layoutlm.py b/modules/onnx_ocr_module/src/ppocr/modeling/backbones/vqa_layoutlm.py new file mode 100644 index 0000000..c80cb14 --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/modeling/backbones/vqa_layoutlm.py @@ -0,0 +1,260 @@ +# copyright (c) 2021 PaddlePaddle Authors. All Rights Reserve. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import os +from paddle import nn + +from paddlenlp.transformers import ( + LayoutXLMModel, + LayoutXLMForTokenClassification, + LayoutXLMForRelationExtraction, +) +from paddlenlp.transformers import LayoutLMModel, LayoutLMForTokenClassification +from paddlenlp.transformers import ( + LayoutLMv2Model, + LayoutLMv2ForTokenClassification, + LayoutLMv2ForRelationExtraction, +) +from paddlenlp.transformers import AutoModel + +__all__ = ["LayoutXLMForSer", "LayoutLMForSer"] + +pretrained_model_dict = { + LayoutXLMModel: { + "base": "layoutxlm-base-uncased", + "vi": "vi-layoutxlm-base-uncased", + }, + LayoutLMModel: { + "base": "layoutlm-base-uncased", + }, + LayoutLMv2Model: { + "base": "layoutlmv2-base-uncased", + "vi": "vi-layoutlmv2-base-uncased", + }, +} + + +class NLPBaseModel(nn.Layer): + def __init__( + self, + base_model_class, + model_class, + mode="base", + type="ser", + pretrained=True, + checkpoints=None, + **kwargs, + ): + super(NLPBaseModel, self).__init__() + if checkpoints is not None: # load the trained model + self.model = model_class.from_pretrained(checkpoints) + else: # load the pretrained-model + pretrained_model_name = pretrained_model_dict[base_model_class][mode] + if type == "ser": + self.model = model_class.from_pretrained( + pretrained_model_name, num_classes=kwargs["num_classes"], dropout=0 + ) + else: + self.model = model_class.from_pretrained( + pretrained_model_name, dropout=0 + ) + self.out_channels = 1 + self.use_visual_backbone = True + + +class LayoutLMForSer(NLPBaseModel): + def __init__( + self, num_classes, pretrained=True, checkpoints=None, mode="base", **kwargs + ): + super(LayoutLMForSer, self).__init__( + LayoutLMModel, + LayoutLMForTokenClassification, + mode, + "ser", + pretrained, + checkpoints, + num_classes=num_classes, + ) + self.use_visual_backbone = False + + def forward(self, x): + x = self.model( + input_ids=x[0], + bbox=x[1], + attention_mask=x[2], + token_type_ids=x[3], + position_ids=None, + output_hidden_states=False, + ) + return x + + +class LayoutLMv2ForSer(NLPBaseModel): + def __init__( + self, num_classes, pretrained=True, checkpoints=None, mode="base", **kwargs + ): + super(LayoutLMv2ForSer, self).__init__( + LayoutLMv2Model, + LayoutLMv2ForTokenClassification, + mode, + "ser", + pretrained, + checkpoints, + num_classes=num_classes, + ) + if ( + hasattr(self.model.layoutlmv2, "use_visual_backbone") + and self.model.layoutlmv2.use_visual_backbone is False + ): + self.use_visual_backbone = False + + def forward(self, x): + if self.use_visual_backbone is True: + image = x[4] + else: + image = None + x = self.model( + input_ids=x[0], + bbox=x[1], + attention_mask=x[2], + token_type_ids=x[3], + image=image, + position_ids=None, + head_mask=None, + labels=None, + ) + if self.training: + res = {"backbone_out": x[0]} + res.update(x[1]) + return res + else: + return x + + +class LayoutXLMForSer(NLPBaseModel): + def __init__( + self, num_classes, pretrained=True, checkpoints=None, mode="base", **kwargs + ): + super(LayoutXLMForSer, self).__init__( + LayoutXLMModel, + LayoutXLMForTokenClassification, + mode, + "ser", + pretrained, + checkpoints, + num_classes=num_classes, + ) + if ( + hasattr(self.model.layoutxlm, "use_visual_backbone") + and self.model.layoutxlm.use_visual_backbone is False + ): + self.use_visual_backbone = False + + def forward(self, x): + if self.use_visual_backbone is True: + image = x[4] + else: + image = None + x = self.model( + input_ids=x[0], + bbox=x[1], + attention_mask=x[2], + token_type_ids=x[3], + image=image, + position_ids=None, + head_mask=None, + labels=None, + ) + if self.training: + res = {"backbone_out": x[0]} + res.update(x[1]) + return res + else: + return x + + +class LayoutLMv2ForRe(NLPBaseModel): + def __init__(self, pretrained=True, checkpoints=None, mode="base", **kwargs): + super(LayoutLMv2ForRe, self).__init__( + LayoutLMv2Model, + LayoutLMv2ForRelationExtraction, + mode, + "re", + pretrained, + checkpoints, + ) + if ( + hasattr(self.model.layoutlmv2, "use_visual_backbone") + and self.model.layoutlmv2.use_visual_backbone is False + ): + self.use_visual_backbone = False + + def forward(self, x): + x = self.model( + input_ids=x[0], + bbox=x[1], + attention_mask=x[2], + token_type_ids=x[3], + image=x[4], + position_ids=None, + head_mask=None, + labels=None, + entities=x[5], + relations=x[6], + ) + return x + + +class LayoutXLMForRe(NLPBaseModel): + def __init__(self, pretrained=True, checkpoints=None, mode="base", **kwargs): + super(LayoutXLMForRe, self).__init__( + LayoutXLMModel, + LayoutXLMForRelationExtraction, + mode, + "re", + pretrained, + checkpoints, + ) + if ( + hasattr(self.model.layoutxlm, "use_visual_backbone") + and self.model.layoutxlm.use_visual_backbone is False + ): + self.use_visual_backbone = False + + def forward(self, x): + if self.use_visual_backbone is True: + image = x[4] + entities = x[5] + relations = x[6] + else: + image = None + entities = x[4] + relations = x[5] + x = self.model( + input_ids=x[0], + bbox=x[1], + attention_mask=x[2], + token_type_ids=x[3], + image=image, + position_ids=None, + head_mask=None, + labels=None, + entities=entities, + relations=relations, + ) + return x diff --git a/modules/onnx_ocr_module/src/ppocr/modeling/heads/__init__.py b/modules/onnx_ocr_module/src/ppocr/modeling/heads/__init__.py new file mode 100644 index 0000000..c410ccc --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/modeling/heads/__init__.py @@ -0,0 +1,108 @@ +# Copyright (c) 2020 PaddlePaddle Authors. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +__all__ = ["build_head"] + + +def build_head(config): + # det head + from .det_db_head import DBHead, PFHeadLocal + from .det_east_head import EASTHead + from .det_sast_head import SASTHead + from .det_pse_head import PSEHead + from .det_fce_head import FCEHead + from .e2e_pg_head import PGHead + from .det_ct_head import CT_Head + + # rec head + from .rec_ctc_head import CTCHead + from .rec_att_head import AttentionHead + from .rec_srn_head import SRNHead + from .rec_nrtr_head import Transformer + from .rec_sar_head import SARHead + from .rec_aster_head import AsterHead + from .rec_pren_head import PRENHead + from .rec_multi_head import MultiHead + from .rec_spin_att_head import SPINAttentionHead + from .rec_abinet_head import ABINetHead + from .rec_robustscanner_head import RobustScannerHead + from .rec_visionlan_head import VLHead + from .rec_rfl_head import RFLHead + from .rec_can_head import CANHead + from .rec_latexocr_head import LaTeXOCRHead + from .rec_satrn_head import SATRNHead + from .rec_parseq_head import ParseQHead + from .rec_cppd_head import CPPDHead + from .rec_unimernet_head import UniMERNetHead + from .rec_ppformulanet_head import PPFormulaNet_Head + + # cls head + from .cls_head import ClsHead + + # kie head + from .kie_sdmgr_head import SDMGRHead + + from .table_att_head import TableAttentionHead, SLAHead + from .table_master_head import TableMasterHead + + support_dict = [ + "DBHead", + "PSEHead", + "FCEHead", + "EASTHead", + "SASTHead", + "CTCHead", + "ClsHead", + "AttentionHead", + "SRNHead", + "PGHead", + "Transformer", + "TableAttentionHead", + "SARHead", + "AsterHead", + "SDMGRHead", + "PRENHead", + "MultiHead", + "ABINetHead", + "TableMasterHead", + "SPINAttentionHead", + "VLHead", + "SLAHead", + "RobustScannerHead", + "CT_Head", + "RFLHead", + "DRRGHead", + "CANHead", + "LaTeXOCRHead", + "SATRNHead", + "PFHeadLocal", + "ParseQHead", + "CPPDHead", + "UniMERNetHead", + "PPFormulaNet_Head", + ] + + if config["name"] == "DRRGHead": + from .det_drrg_head import DRRGHead + + support_dict.append("DRRGHead") + + # table head + + module_name = config.pop("name") + assert module_name in support_dict, Exception( + "head only support {}".format(support_dict) + ) + module_class = eval(module_name)(**config) + return module_class diff --git a/modules/onnx_ocr_module/src/ppocr/modeling/heads/cls_head.py b/modules/onnx_ocr_module/src/ppocr/modeling/heads/cls_head.py new file mode 100644 index 0000000..867e960 --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/modeling/heads/cls_head.py @@ -0,0 +1,53 @@ +# copyright (c) 2020 PaddlePaddle Authors. All Rights Reserve. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import math +import paddle +from paddle import nn, ParamAttr +import paddle.nn.functional as F + + +class ClsHead(nn.Layer): + """ + Class orientation + + Args: + + params(dict): super parameters for build Class network + """ + + def __init__(self, in_channels, class_dim, **kwargs): + super(ClsHead, self).__init__() + self.pool = nn.AdaptiveAvgPool2D(1) + stdv = 1.0 / math.sqrt(in_channels * 1.0) + self.fc = nn.Linear( + in_channels, + class_dim, + weight_attr=ParamAttr( + name="fc_0.w_0", initializer=nn.initializer.Uniform(-stdv, stdv) + ), + bias_attr=ParamAttr(name="fc_0.b_0"), + ) + + def forward(self, x, targets=None): + x = self.pool(x) + x = paddle.reshape(x, shape=[x.shape[0], x.shape[1]]) + x = self.fc(x) + if not self.training: + x = F.softmax(x, axis=1) + return x diff --git a/modules/onnx_ocr_module/src/ppocr/modeling/heads/det_ct_head.py b/modules/onnx_ocr_module/src/ppocr/modeling/heads/det_ct_head.py new file mode 100644 index 0000000..cd050fc --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/modeling/heads/det_ct_head.py @@ -0,0 +1,69 @@ +# copyright (c) 2019 PaddlePaddle Authors. All Rights Reserve. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import math +import paddle +from paddle import nn +import paddle.nn.functional as F +from paddle import ParamAttr + +import math +from paddle.nn.initializer import TruncatedNormal, Constant, Normal + +ones_ = Constant(value=1.0) +zeros_ = Constant(value=0.0) + + +class CT_Head(nn.Layer): + def __init__( + self, in_channels, hidden_dim, num_classes, loss_kernel=None, loss_loc=None + ): + super(CT_Head, self).__init__() + self.conv1 = nn.Conv2D( + in_channels, hidden_dim, kernel_size=3, stride=1, padding=1 + ) + self.bn1 = nn.BatchNorm2D(hidden_dim) + self.relu1 = nn.ReLU() + + self.conv2 = nn.Conv2D( + hidden_dim, num_classes, kernel_size=1, stride=1, padding=0 + ) + + for m in self.sublayers(): + if isinstance(m, nn.Conv2D): + n = m._kernel_size[0] * m._kernel_size[1] * m._out_channels + normal_ = Normal(mean=0.0, std=math.sqrt(2.0 / n)) + normal_(m.weight) + elif isinstance(m, nn.BatchNorm2D): + zeros_(m.bias) + ones_(m.weight) + + def _upsample(self, x, scale=1): + return F.upsample(x, scale_factor=scale, mode="bilinear") + + def forward(self, f, targets=None): + out = self.conv1(f) + out = self.relu1(self.bn1(out)) + out = self.conv2(out) + + if self.training: + out = self._upsample(out, scale=4) + return {"maps": out} + else: + score = F.sigmoid(out[:, 0, :, :]) + return {"maps": out, "score": score} diff --git a/modules/onnx_ocr_module/src/ppocr/modeling/heads/det_db_head.py b/modules/onnx_ocr_module/src/ppocr/modeling/heads/det_db_head.py new file mode 100644 index 0000000..9f057b6 --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/modeling/heads/det_db_head.py @@ -0,0 +1,159 @@ +# copyright (c) 2019 PaddlePaddle Authors. All Rights Reserve. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import math +import paddle +from paddle import nn +import paddle.nn.functional as F +from paddle import ParamAttr +from ppocr.modeling.backbones.det_mobilenet_v3 import ConvBNLayer + + +def get_bias_attr(k): + stdv = 1.0 / math.sqrt(k * 1.0) + initializer = paddle.nn.initializer.Uniform(-stdv, stdv) + bias_attr = ParamAttr(initializer=initializer) + return bias_attr + + +class Head(nn.Layer): + def __init__(self, in_channels, kernel_list=[3, 2, 2], fix_nan=False, **kwargs): + super(Head, self).__init__() + + self.conv1 = nn.Conv2D( + in_channels=in_channels, + out_channels=in_channels // 4, + kernel_size=kernel_list[0], + padding=int(kernel_list[0] // 2), + weight_attr=ParamAttr(), + bias_attr=False, + ) + self.conv_bn1 = nn.BatchNorm( + num_channels=in_channels // 4, + param_attr=ParamAttr(initializer=paddle.nn.initializer.Constant(value=1.0)), + bias_attr=ParamAttr(initializer=paddle.nn.initializer.Constant(value=1e-4)), + act="relu", + ) + + self.conv2 = nn.Conv2DTranspose( + in_channels=in_channels // 4, + out_channels=in_channels // 4, + kernel_size=kernel_list[1], + stride=2, + weight_attr=ParamAttr(initializer=paddle.nn.initializer.KaimingUniform()), + bias_attr=get_bias_attr(in_channels // 4), + ) + self.conv_bn2 = nn.BatchNorm( + num_channels=in_channels // 4, + param_attr=ParamAttr(initializer=paddle.nn.initializer.Constant(value=1.0)), + bias_attr=ParamAttr(initializer=paddle.nn.initializer.Constant(value=1e-4)), + act="relu", + ) + self.conv3 = nn.Conv2DTranspose( + in_channels=in_channels // 4, + out_channels=1, + kernel_size=kernel_list[2], + stride=2, + weight_attr=ParamAttr(initializer=paddle.nn.initializer.KaimingUniform()), + bias_attr=get_bias_attr(in_channels // 4), + ) + + self.fix_nan = fix_nan + + def forward(self, x, return_f=False): + x = self.conv1(x) + x = self.conv_bn1(x) + if self.fix_nan and self.training: + x = paddle.where(paddle.isnan(x), paddle.zeros_like(x), x) + x = self.conv2(x) + x = self.conv_bn2(x) + if self.fix_nan and self.training: + x = paddle.where(paddle.isnan(x), paddle.zeros_like(x), x) + if return_f is True: + f = x + x = self.conv3(x) + x = F.sigmoid(x) + if return_f is True: + return x, f + return x + + +class DBHead(nn.Layer): + """ + Differentiable Binarization (DB) for text detection: + see https://arxiv.org/abs/1911.08947 + args: + params(dict): super parameters for build DB network + """ + + def __init__(self, in_channels, k=50, **kwargs): + super(DBHead, self).__init__() + self.k = k + self.binarize = Head(in_channels, **kwargs) + self.thresh = Head(in_channels, **kwargs) + + def step_function(self, x, y): + return paddle.reciprocal(1 + paddle.exp(-self.k * (x - y))) + + def forward(self, x, targets=None): + shrink_maps = self.binarize(x) + if not self.training: + return {"maps": shrink_maps} + + threshold_maps = self.thresh(x) + binary_maps = self.step_function(shrink_maps, threshold_maps) + y = paddle.concat([shrink_maps, threshold_maps, binary_maps], axis=1) + return {"maps": y} + + +class LocalModule(nn.Layer): + def __init__(self, in_c, mid_c, use_distance=True): + super(self.__class__, self).__init__() + self.last_3 = ConvBNLayer(in_c + 1, mid_c, 3, 1, 1, act="relu") + self.last_1 = nn.Conv2D(mid_c, 1, 1, 1, 0) + + def forward(self, x, init_map, distance_map): + outf = paddle.concat([init_map, x], axis=1) + # last Conv + out = self.last_1(self.last_3(outf)) + return out + + +class PFHeadLocal(DBHead): + def __init__(self, in_channels, k=50, mode="small", **kwargs): + super(PFHeadLocal, self).__init__(in_channels, k, **kwargs) + self.mode = mode + + self.up_conv = nn.Upsample(scale_factor=2, mode="nearest", align_mode=1) + if self.mode == "large": + self.cbn_layer = LocalModule(in_channels // 4, in_channels // 4) + elif self.mode == "small": + self.cbn_layer = LocalModule(in_channels // 4, in_channels // 8) + + def forward(self, x, targets=None): + shrink_maps, f = self.binarize(x, return_f=True) + base_maps = shrink_maps + cbn_maps = self.cbn_layer(self.up_conv(f), shrink_maps, None) + cbn_maps = F.sigmoid(cbn_maps) + if not self.training: + return {"maps": 0.5 * (base_maps + cbn_maps)} + + threshold_maps = self.thresh(x) + binary_maps = self.step_function(shrink_maps, threshold_maps) + y = paddle.concat([cbn_maps, threshold_maps, binary_maps], axis=1) + return {"maps": y, "distance_maps": cbn_maps, "cbn_maps": binary_maps} diff --git a/modules/onnx_ocr_module/src/ppocr/modeling/heads/det_drrg_head.py b/modules/onnx_ocr_module/src/ppocr/modeling/heads/det_drrg_head.py new file mode 100644 index 0000000..4d6f875 --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/modeling/heads/det_drrg_head.py @@ -0,0 +1,216 @@ +# copyright (c) 2022 PaddlePaddle Authors. All Rights Reserve. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +""" +This code is refer from: +https://github.com/open-mmlab/mmocr/blob/main/mmocr/models/textdet/dense_heads/drrg_head.py +""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import warnings +import cv2 +import numpy as np +import paddle +import paddle.nn as nn +import paddle.nn.functional as F +from .gcn import GCN +from .local_graph import LocalGraphs +from .proposal_local_graph import ProposalLocalGraphs + + +class DRRGHead(nn.Layer): + def __init__( + self, + in_channels, + k_at_hops=(8, 4), + num_adjacent_linkages=3, + node_geo_feat_len=120, + pooling_scale=1.0, + pooling_output_size=(4, 3), + nms_thr=0.3, + min_width=8.0, + max_width=24.0, + comp_shrink_ratio=1.03, + comp_ratio=0.4, + comp_score_thr=0.3, + text_region_thr=0.2, + center_region_thr=0.2, + center_region_area_thr=50, + local_graph_thr=0.7, + **kwargs, + ): + super().__init__() + + assert isinstance(in_channels, int) + assert isinstance(k_at_hops, tuple) + assert isinstance(num_adjacent_linkages, int) + assert isinstance(node_geo_feat_len, int) + assert isinstance(pooling_scale, float) + assert isinstance(pooling_output_size, tuple) + assert isinstance(comp_shrink_ratio, float) + assert isinstance(nms_thr, float) + assert isinstance(min_width, float) + assert isinstance(max_width, float) + assert isinstance(comp_ratio, float) + assert isinstance(comp_score_thr, float) + assert isinstance(text_region_thr, float) + assert isinstance(center_region_thr, float) + assert isinstance(center_region_area_thr, int) + assert isinstance(local_graph_thr, float) + + self.in_channels = in_channels + self.out_channels = 6 + self.downsample_ratio = 1.0 + self.k_at_hops = k_at_hops + self.num_adjacent_linkages = num_adjacent_linkages + self.node_geo_feat_len = node_geo_feat_len + self.pooling_scale = pooling_scale + self.pooling_output_size = pooling_output_size + self.comp_shrink_ratio = comp_shrink_ratio + self.nms_thr = nms_thr + self.min_width = min_width + self.max_width = max_width + self.comp_ratio = comp_ratio + self.comp_score_thr = comp_score_thr + self.text_region_thr = text_region_thr + self.center_region_thr = center_region_thr + self.center_region_area_thr = center_region_area_thr + self.local_graph_thr = local_graph_thr + + self.out_conv = nn.Conv2D( + in_channels=self.in_channels, + out_channels=self.out_channels, + kernel_size=1, + stride=1, + padding=0, + ) + + self.graph_train = LocalGraphs( + self.k_at_hops, + self.num_adjacent_linkages, + self.node_geo_feat_len, + self.pooling_scale, + self.pooling_output_size, + self.local_graph_thr, + ) + + self.graph_test = ProposalLocalGraphs( + self.k_at_hops, + self.num_adjacent_linkages, + self.node_geo_feat_len, + self.pooling_scale, + self.pooling_output_size, + self.nms_thr, + self.min_width, + self.max_width, + self.comp_shrink_ratio, + self.comp_ratio, + self.comp_score_thr, + self.text_region_thr, + self.center_region_thr, + self.center_region_area_thr, + ) + + pool_w, pool_h = self.pooling_output_size + node_feat_len = (pool_w * pool_h) * ( + self.in_channels + self.out_channels + ) + self.node_geo_feat_len + self.gcn = GCN(node_feat_len) + + def forward(self, inputs, targets=None): + """ + Args: + inputs (Tensor): Shape of :math:`(N, C, H, W)`. + gt_comp_attribs (list[ndarray]): The padded text component + attributes. Shape: (num_component, 8). + + Returns: + tuple: Returns (pred_maps, (gcn_pred, gt_labels)). + + - | pred_maps (Tensor): Prediction map with shape + :math:`(N, C_{out}, H, W)`. + - | gcn_pred (Tensor): Prediction from GCN module, with + shape :math:`(N, 2)`. + - | gt_labels (Tensor): Ground-truth label with shape + :math:`(N, 8)`. + """ + if self.training: + assert targets is not None + gt_comp_attribs = targets[7] + pred_maps = self.out_conv(inputs) + feat_maps = paddle.concat([inputs, pred_maps], axis=1) + node_feats, adjacent_matrices, knn_inds, gt_labels = self.graph_train( + feat_maps, np.stack(gt_comp_attribs) + ) + + gcn_pred = self.gcn(node_feats, adjacent_matrices, knn_inds) + + return pred_maps, (gcn_pred, gt_labels) + else: + return self.single_test(inputs) + + def single_test(self, feat_maps): + r""" + Args: + feat_maps (Tensor): Shape of :math:`(N, C, H, W)`. + + Returns: + tuple: Returns (edge, score, text_comps). + + - | edge (ndarray): The edge array of shape :math:`(N, 2)` + where each row is a pair of text component indices + that makes up an edge in graph. + - | score (ndarray): The score array of shape :math:`(N,)`, + corresponding to the edge above. + - | text_comps (ndarray): The text components of shape + :math:`(N, 9)` where each row corresponds to one box and + its score: (x1, y1, x2, y2, x3, y3, x4, y4, score). + """ + pred_maps = self.out_conv(feat_maps) + feat_maps = paddle.concat([feat_maps, pred_maps], axis=1) + + none_flag, graph_data = self.graph_test(pred_maps, feat_maps) + + ( + local_graphs_node_feat, + adjacent_matrices, + pivots_knn_inds, + pivot_local_graphs, + text_comps, + ) = graph_data + + if none_flag: + return None, None, None + gcn_pred = self.gcn(local_graphs_node_feat, adjacent_matrices, pivots_knn_inds) + pred_labels = F.softmax(gcn_pred, axis=1) + + edges = [] + scores = [] + pivot_local_graphs = pivot_local_graphs.squeeze().numpy() + + for pivot_ind, pivot_local_graph in enumerate(pivot_local_graphs): + pivot = pivot_local_graph[0] + for k_ind, neighbor_ind in enumerate(pivots_knn_inds[pivot_ind]): + neighbor = pivot_local_graph[neighbor_ind.item()] + edges.append([pivot, neighbor]) + scores.append( + pred_labels[pivot_ind * pivots_knn_inds.shape[1] + k_ind, 1].item() + ) + + edges = np.asarray(edges) + scores = np.asarray(scores) + + return edges, scores, text_comps diff --git a/modules/onnx_ocr_module/src/ppocr/modeling/heads/det_east_head.py b/modules/onnx_ocr_module/src/ppocr/modeling/heads/det_east_head.py new file mode 100644 index 0000000..c0ad6e8 --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/modeling/heads/det_east_head.py @@ -0,0 +1,129 @@ +# copyright (c) 2019 PaddlePaddle Authors. All Rights Reserve. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import math +import paddle +from paddle import nn +import paddle.nn.functional as F +from paddle import ParamAttr + + +class ConvBNLayer(nn.Layer): + def __init__( + self, + in_channels, + out_channels, + kernel_size, + stride, + padding, + groups=1, + if_act=True, + act=None, + name=None, + ): + super(ConvBNLayer, self).__init__() + self.if_act = if_act + self.act = act + self.conv = nn.Conv2D( + in_channels=in_channels, + out_channels=out_channels, + kernel_size=kernel_size, + stride=stride, + padding=padding, + groups=groups, + weight_attr=ParamAttr(name=name + "_weights"), + bias_attr=False, + ) + + self.bn = nn.BatchNorm( + num_channels=out_channels, + act=act, + param_attr=ParamAttr(name="bn_" + name + "_scale"), + bias_attr=ParamAttr(name="bn_" + name + "_offset"), + moving_mean_name="bn_" + name + "_mean", + moving_variance_name="bn_" + name + "_variance", + ) + + def forward(self, x): + x = self.conv(x) + x = self.bn(x) + return x + + +class EASTHead(nn.Layer): + """ """ + + def __init__(self, in_channels, model_name, **kwargs): + super(EASTHead, self).__init__() + self.model_name = model_name + if self.model_name == "large": + num_outputs = [128, 64, 1, 8] + else: + num_outputs = [64, 32, 1, 8] + + self.det_conv1 = ConvBNLayer( + in_channels=in_channels, + out_channels=num_outputs[0], + kernel_size=3, + stride=1, + padding=1, + if_act=True, + act="relu", + name="det_head1", + ) + self.det_conv2 = ConvBNLayer( + in_channels=num_outputs[0], + out_channels=num_outputs[1], + kernel_size=3, + stride=1, + padding=1, + if_act=True, + act="relu", + name="det_head2", + ) + self.score_conv = ConvBNLayer( + in_channels=num_outputs[1], + out_channels=num_outputs[2], + kernel_size=1, + stride=1, + padding=0, + if_act=False, + act=None, + name="f_score", + ) + self.geo_conv = ConvBNLayer( + in_channels=num_outputs[1], + out_channels=num_outputs[3], + kernel_size=1, + stride=1, + padding=0, + if_act=False, + act=None, + name="f_geo", + ) + + def forward(self, x, targets=None): + f_det = self.det_conv1(x) + f_det = self.det_conv2(f_det) + f_score = self.score_conv(f_det) + f_score = F.sigmoid(f_score) + f_geo = self.geo_conv(f_det) + f_geo = (F.sigmoid(f_geo) - 0.5) * 2 * 800 + + pred = {"f_score": f_score, "f_geo": f_geo} + return pred diff --git a/modules/onnx_ocr_module/src/ppocr/modeling/heads/det_fce_head.py b/modules/onnx_ocr_module/src/ppocr/modeling/heads/det_fce_head.py new file mode 100644 index 0000000..1a90a9a --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/modeling/heads/det_fce_head.py @@ -0,0 +1,100 @@ +# copyright (c) 2022 PaddlePaddle Authors. All Rights Reserve. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +""" +This code is refer from: +https://github.com/open-mmlab/mmocr/blob/main/mmocr/models/textdet/dense_heads/fce_head.py +""" + +from paddle import nn +from paddle import ParamAttr +import paddle.nn.functional as F +from paddle.nn.initializer import Normal +import paddle +from functools import partial + + +def multi_apply(func, *args, **kwargs): + pfunc = partial(func, **kwargs) if kwargs else func + map_results = map(pfunc, *args) + return tuple(map(list, zip(*map_results))) + + +class FCEHead(nn.Layer): + """The class for implementing FCENet head. + FCENet(CVPR2021): Fourier Contour Embedding for Arbitrary-shaped Text + Detection. + + [https://arxiv.org/abs/2104.10442] + + Args: + in_channels (int): The number of input channels. + scales (list[int]) : The scale of each layer. + fourier_degree (int) : The maximum Fourier transform degree k. + """ + + def __init__(self, in_channels, fourier_degree=5): + super().__init__() + assert isinstance(in_channels, int) + + self.downsample_ratio = 1.0 + self.in_channels = in_channels + self.fourier_degree = fourier_degree + self.out_channels_cls = 4 + self.out_channels_reg = (2 * self.fourier_degree + 1) * 2 + + self.out_conv_cls = nn.Conv2D( + in_channels=self.in_channels, + out_channels=self.out_channels_cls, + kernel_size=3, + stride=1, + padding=1, + groups=1, + weight_attr=ParamAttr( + name="cls_weights", initializer=Normal(mean=0.0, std=0.01) + ), + bias_attr=True, + ) + self.out_conv_reg = nn.Conv2D( + in_channels=self.in_channels, + out_channels=self.out_channels_reg, + kernel_size=3, + stride=1, + padding=1, + groups=1, + weight_attr=ParamAttr( + name="reg_weights", initializer=Normal(mean=0.0, std=0.01) + ), + bias_attr=True, + ) + + def forward(self, feats, targets=None): + cls_res, reg_res = multi_apply(self.forward_single, feats) + level_num = len(cls_res) + outs = {} + if not self.training: + for i in range(level_num): + tr_pred = F.softmax(cls_res[i][:, 0:2, :, :], axis=1) + tcl_pred = F.softmax(cls_res[i][:, 2:, :, :], axis=1) + outs["level_{}".format(i)] = paddle.concat( + [tr_pred, tcl_pred, reg_res[i]], axis=1 + ) + else: + preds = [[cls_res[i], reg_res[i]] for i in range(level_num)] + outs["levels"] = preds + return outs + + def forward_single(self, x): + cls_predict = self.out_conv_cls(x) + reg_predict = self.out_conv_reg(x) + return cls_predict, reg_predict diff --git a/modules/onnx_ocr_module/src/ppocr/modeling/heads/det_pse_head.py b/modules/onnx_ocr_module/src/ppocr/modeling/heads/det_pse_head.py new file mode 100644 index 0000000..2f51621 --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/modeling/heads/det_pse_head.py @@ -0,0 +1,39 @@ +# copyright (c) 2021 PaddlePaddle Authors. All Rights Reserve. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +""" +This code is refer from: +https://github.com/whai362/PSENet/blob/python3/models/head/psenet_head.py +""" + +from paddle import nn + + +class PSEHead(nn.Layer): + def __init__(self, in_channels, hidden_dim=256, out_channels=7, **kwargs): + super(PSEHead, self).__init__() + self.conv1 = nn.Conv2D( + in_channels, hidden_dim, kernel_size=3, stride=1, padding=1 + ) + self.bn1 = nn.BatchNorm2D(hidden_dim) + self.relu1 = nn.ReLU() + + self.conv2 = nn.Conv2D( + hidden_dim, out_channels, kernel_size=1, stride=1, padding=0 + ) + + def forward(self, x, **kwargs): + out = self.conv1(x) + out = self.relu1(self.bn1(out)) + out = self.conv2(out) + return {"maps": out} diff --git a/modules/onnx_ocr_module/src/ppocr/modeling/heads/det_sast_head.py b/modules/onnx_ocr_module/src/ppocr/modeling/heads/det_sast_head.py new file mode 100644 index 0000000..9246355 --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/modeling/heads/det_sast_head.py @@ -0,0 +1,152 @@ +# copyright (c) 2019 PaddlePaddle Authors. All Rights Reserve. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import math +import paddle +from paddle import nn +import paddle.nn.functional as F +from paddle import ParamAttr + + +class ConvBNLayer(nn.Layer): + def __init__( + self, + in_channels, + out_channels, + kernel_size, + stride, + groups=1, + if_act=True, + act=None, + name=None, + ): + super(ConvBNLayer, self).__init__() + self.if_act = if_act + self.act = act + self.conv = nn.Conv2D( + in_channels=in_channels, + out_channels=out_channels, + kernel_size=kernel_size, + stride=stride, + padding=(kernel_size - 1) // 2, + groups=groups, + weight_attr=ParamAttr(name=name + "_weights"), + bias_attr=False, + ) + + self.bn = nn.BatchNorm( + num_channels=out_channels, + act=act, + param_attr=ParamAttr(name="bn_" + name + "_scale"), + bias_attr=ParamAttr(name="bn_" + name + "_offset"), + moving_mean_name="bn_" + name + "_mean", + moving_variance_name="bn_" + name + "_variance", + ) + + def forward(self, x): + x = self.conv(x) + x = self.bn(x) + return x + + +class SAST_Header1(nn.Layer): + def __init__(self, in_channels, **kwargs): + super(SAST_Header1, self).__init__() + out_channels = [64, 64, 128] + self.score_conv = nn.Sequential( + ConvBNLayer( + in_channels, out_channels[0], 1, 1, act="relu", name="f_score1" + ), + ConvBNLayer( + out_channels[0], out_channels[1], 3, 1, act="relu", name="f_score2" + ), + ConvBNLayer( + out_channels[1], out_channels[2], 1, 1, act="relu", name="f_score3" + ), + ConvBNLayer(out_channels[2], 1, 3, 1, act=None, name="f_score4"), + ) + self.border_conv = nn.Sequential( + ConvBNLayer( + in_channels, out_channels[0], 1, 1, act="relu", name="f_border1" + ), + ConvBNLayer( + out_channels[0], out_channels[1], 3, 1, act="relu", name="f_border2" + ), + ConvBNLayer( + out_channels[1], out_channels[2], 1, 1, act="relu", name="f_border3" + ), + ConvBNLayer(out_channels[2], 4, 3, 1, act=None, name="f_border4"), + ) + + def forward(self, x): + f_score = self.score_conv(x) + f_score = F.sigmoid(f_score) + f_border = self.border_conv(x) + return f_score, f_border + + +class SAST_Header2(nn.Layer): + def __init__(self, in_channels, **kwargs): + super(SAST_Header2, self).__init__() + out_channels = [64, 64, 128] + self.tvo_conv = nn.Sequential( + ConvBNLayer(in_channels, out_channels[0], 1, 1, act="relu", name="f_tvo1"), + ConvBNLayer( + out_channels[0], out_channels[1], 3, 1, act="relu", name="f_tvo2" + ), + ConvBNLayer( + out_channels[1], out_channels[2], 1, 1, act="relu", name="f_tvo3" + ), + ConvBNLayer(out_channels[2], 8, 3, 1, act=None, name="f_tvo4"), + ) + self.tco_conv = nn.Sequential( + ConvBNLayer(in_channels, out_channels[0], 1, 1, act="relu", name="f_tco1"), + ConvBNLayer( + out_channels[0], out_channels[1], 3, 1, act="relu", name="f_tco2" + ), + ConvBNLayer( + out_channels[1], out_channels[2], 1, 1, act="relu", name="f_tco3" + ), + ConvBNLayer(out_channels[2], 2, 3, 1, act=None, name="f_tco4"), + ) + + def forward(self, x): + f_tvo = self.tvo_conv(x) + f_tco = self.tco_conv(x) + return f_tvo, f_tco + + +class SASTHead(nn.Layer): + """ """ + + def __init__(self, in_channels, **kwargs): + super(SASTHead, self).__init__() + + self.head1 = SAST_Header1(in_channels) + self.head2 = SAST_Header2(in_channels) + + def forward(self, x, targets=None): + f_score, f_border = self.head1(x) + f_tvo, f_tco = self.head2(x) + + predicts = {} + predicts["f_score"] = f_score + predicts["f_border"] = f_border + predicts["f_tvo"] = f_tvo + predicts["f_tco"] = f_tco + return predicts diff --git a/modules/onnx_ocr_module/src/ppocr/modeling/heads/e2e_pg_head.py b/modules/onnx_ocr_module/src/ppocr/modeling/heads/e2e_pg_head.py new file mode 100644 index 0000000..27b0472 --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/modeling/heads/e2e_pg_head.py @@ -0,0 +1,282 @@ +# copyright (c) 2021 PaddlePaddle Authors. All Rights Reserve. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import math +import paddle +from paddle import nn +import paddle.nn.functional as F +from paddle import ParamAttr + + +class ConvBNLayer(nn.Layer): + def __init__( + self, + in_channels, + out_channels, + kernel_size, + stride, + padding, + groups=1, + if_act=True, + act=None, + name=None, + ): + super(ConvBNLayer, self).__init__() + self.if_act = if_act + self.act = act + self.conv = nn.Conv2D( + in_channels=in_channels, + out_channels=out_channels, + kernel_size=kernel_size, + stride=stride, + padding=padding, + groups=groups, + weight_attr=ParamAttr(name=name + "_weights"), + bias_attr=False, + ) + + self.bn = nn.BatchNorm( + num_channels=out_channels, + act=act, + param_attr=ParamAttr(name="bn_" + name + "_scale"), + bias_attr=ParamAttr(name="bn_" + name + "_offset"), + moving_mean_name="bn_" + name + "_mean", + moving_variance_name="bn_" + name + "_variance", + use_global_stats=False, + ) + + def forward(self, x): + x = self.conv(x) + x = self.bn(x) + return x + + +class PGHead(nn.Layer): + """ """ + + def __init__( + self, in_channels, character_dict_path="ppocr/utils/ic15_dict.txt", **kwargs + ): + super(PGHead, self).__init__() + + # get character_length + with open(character_dict_path, "rb") as fin: + lines = fin.readlines() + character_length = len(lines) + 1 + + self.conv_f_score1 = ConvBNLayer( + in_channels=in_channels, + out_channels=64, + kernel_size=1, + stride=1, + padding=0, + act="relu", + name="conv_f_score{}".format(1), + ) + self.conv_f_score2 = ConvBNLayer( + in_channels=64, + out_channels=64, + kernel_size=3, + stride=1, + padding=1, + act="relu", + name="conv_f_score{}".format(2), + ) + self.conv_f_score3 = ConvBNLayer( + in_channels=64, + out_channels=128, + kernel_size=1, + stride=1, + padding=0, + act="relu", + name="conv_f_score{}".format(3), + ) + + self.conv1 = nn.Conv2D( + in_channels=128, + out_channels=1, + kernel_size=3, + stride=1, + padding=1, + groups=1, + weight_attr=ParamAttr(name="conv_f_score{}".format(4)), + bias_attr=False, + ) + + self.conv_f_boder1 = ConvBNLayer( + in_channels=in_channels, + out_channels=64, + kernel_size=1, + stride=1, + padding=0, + act="relu", + name="conv_f_boder{}".format(1), + ) + self.conv_f_boder2 = ConvBNLayer( + in_channels=64, + out_channels=64, + kernel_size=3, + stride=1, + padding=1, + act="relu", + name="conv_f_boder{}".format(2), + ) + self.conv_f_boder3 = ConvBNLayer( + in_channels=64, + out_channels=128, + kernel_size=1, + stride=1, + padding=0, + act="relu", + name="conv_f_boder{}".format(3), + ) + self.conv2 = nn.Conv2D( + in_channels=128, + out_channels=4, + kernel_size=3, + stride=1, + padding=1, + groups=1, + weight_attr=ParamAttr(name="conv_f_boder{}".format(4)), + bias_attr=False, + ) + self.conv_f_char1 = ConvBNLayer( + in_channels=in_channels, + out_channels=128, + kernel_size=1, + stride=1, + padding=0, + act="relu", + name="conv_f_char{}".format(1), + ) + self.conv_f_char2 = ConvBNLayer( + in_channels=128, + out_channels=128, + kernel_size=3, + stride=1, + padding=1, + act="relu", + name="conv_f_char{}".format(2), + ) + self.conv_f_char3 = ConvBNLayer( + in_channels=128, + out_channels=256, + kernel_size=1, + stride=1, + padding=0, + act="relu", + name="conv_f_char{}".format(3), + ) + self.conv_f_char4 = ConvBNLayer( + in_channels=256, + out_channels=256, + kernel_size=3, + stride=1, + padding=1, + act="relu", + name="conv_f_char{}".format(4), + ) + self.conv_f_char5 = ConvBNLayer( + in_channels=256, + out_channels=256, + kernel_size=1, + stride=1, + padding=0, + act="relu", + name="conv_f_char{}".format(5), + ) + self.conv3 = nn.Conv2D( + in_channels=256, + out_channels=character_length, + kernel_size=3, + stride=1, + padding=1, + groups=1, + weight_attr=ParamAttr(name="conv_f_char{}".format(6)), + bias_attr=False, + ) + + self.conv_f_direc1 = ConvBNLayer( + in_channels=in_channels, + out_channels=64, + kernel_size=1, + stride=1, + padding=0, + act="relu", + name="conv_f_direc{}".format(1), + ) + self.conv_f_direc2 = ConvBNLayer( + in_channels=64, + out_channels=64, + kernel_size=3, + stride=1, + padding=1, + act="relu", + name="conv_f_direc{}".format(2), + ) + self.conv_f_direc3 = ConvBNLayer( + in_channels=64, + out_channels=128, + kernel_size=1, + stride=1, + padding=0, + act="relu", + name="conv_f_direc{}".format(3), + ) + self.conv4 = nn.Conv2D( + in_channels=128, + out_channels=2, + kernel_size=3, + stride=1, + padding=1, + groups=1, + weight_attr=ParamAttr(name="conv_f_direc{}".format(4)), + bias_attr=False, + ) + + def forward(self, x, targets=None): + f_score = self.conv_f_score1(x) + f_score = self.conv_f_score2(f_score) + f_score = self.conv_f_score3(f_score) + f_score = self.conv1(f_score) + f_score = F.sigmoid(f_score) + + # f_border + f_border = self.conv_f_boder1(x) + f_border = self.conv_f_boder2(f_border) + f_border = self.conv_f_boder3(f_border) + f_border = self.conv2(f_border) + + f_char = self.conv_f_char1(x) + f_char = self.conv_f_char2(f_char) + f_char = self.conv_f_char3(f_char) + f_char = self.conv_f_char4(f_char) + f_char = self.conv_f_char5(f_char) + f_char = self.conv3(f_char) + + f_direction = self.conv_f_direc1(x) + f_direction = self.conv_f_direc2(f_direction) + f_direction = self.conv_f_direc3(f_direction) + f_direction = self.conv4(f_direction) + + predicts = {} + predicts["f_score"] = f_score + predicts["f_border"] = f_border + predicts["f_char"] = f_char + predicts["f_direction"] = f_direction + return predicts diff --git a/modules/onnx_ocr_module/src/ppocr/modeling/heads/gcn.py b/modules/onnx_ocr_module/src/ppocr/modeling/heads/gcn.py new file mode 100644 index 0000000..6e6e2ea --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/modeling/heads/gcn.py @@ -0,0 +1,118 @@ +# copyright (c) 2022 PaddlePaddle Authors. All Rights Reserve. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +""" +This code is refer from: +https://github.com/open-mmlab/mmocr/blob/main/mmocr/models/textdet/modules/gcn.py +""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import paddle +import paddle.nn as nn +import paddle.nn.functional as F + + +class BatchNorm1D(nn.BatchNorm1D): + def __init__( + self, + num_features, + eps=1e-05, + momentum=0.1, + affine=True, + track_running_stats=True, + ): + momentum = 1 - momentum + weight_attr = None + bias_attr = None + if not affine: + weight_attr = paddle.ParamAttr(learning_rate=0.0) + bias_attr = paddle.ParamAttr(learning_rate=0.0) + super().__init__( + num_features, + momentum=momentum, + epsilon=eps, + weight_attr=weight_attr, + bias_attr=bias_attr, + use_global_stats=track_running_stats, + ) + + +class MeanAggregator(nn.Layer): + def forward(self, features, A): + x = paddle.bmm(A, features) + return x + + +class GraphConv(nn.Layer): + def __init__(self, in_dim, out_dim): + super().__init__() + self.in_dim = in_dim + self.out_dim = out_dim + self.weight = self.create_parameter( + [in_dim * 2, out_dim], default_initializer=nn.initializer.XavierUniform() + ) + self.bias = self.create_parameter( + [out_dim], + is_bias=True, + default_initializer=nn.initializer.Assign([0] * out_dim), + ) + + self.aggregator = MeanAggregator() + + def forward(self, features, A): + b, n, d = features.shape + assert d == self.in_dim + agg_feats = self.aggregator(features, A) + cat_feats = paddle.concat([features, agg_feats], axis=2) + out = paddle.einsum("bnd,df->bnf", cat_feats, self.weight) + out = F.relu(out + self.bias) + return out + + +class GCN(nn.Layer): + def __init__(self, feat_len): + super(GCN, self).__init__() + self.bn0 = BatchNorm1D(feat_len, affine=False) + self.conv1 = GraphConv(feat_len, 512) + self.conv2 = GraphConv(512, 256) + self.conv3 = GraphConv(256, 128) + self.conv4 = GraphConv(128, 64) + self.classifier = nn.Sequential( + nn.Linear(64, 32), nn.PReLU(32), nn.Linear(32, 2) + ) + + def forward(self, x, A, knn_inds): + num_local_graphs, num_max_nodes, feat_len = x.shape + + x = x.reshape([-1, feat_len]) + x = self.bn0(x) + x = x.reshape([num_local_graphs, num_max_nodes, feat_len]) + + x = self.conv1(x, A) + x = self.conv2(x, A) + x = self.conv3(x, A) + x = self.conv4(x, A) + k = knn_inds.shape[-1] + mid_feat_len = x.shape[-1] + edge_feat = paddle.zeros([num_local_graphs, k, mid_feat_len]) + for graph_ind in range(num_local_graphs): + edge_feat[graph_ind, :, :] = x[graph_ind][ + paddle.to_tensor(knn_inds[graph_ind]) + ] + edge_feat = edge_feat.reshape([-1, mid_feat_len]) + pred = self.classifier(edge_feat) + + return pred diff --git a/modules/onnx_ocr_module/src/ppocr/modeling/heads/kie_sdmgr_head.py b/modules/onnx_ocr_module/src/ppocr/modeling/heads/kie_sdmgr_head.py new file mode 100644 index 0000000..eca8efd --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/modeling/heads/kie_sdmgr_head.py @@ -0,0 +1,223 @@ +# copyright (c) 2022 PaddlePaddle Authors. All Rights Reserve. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# reference from : https://github.com/open-mmlab/mmocr/blob/main/mmocr/models/kie/heads/sdmgr_head.py + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import math +import paddle +from paddle import nn +import paddle.nn.functional as F +from paddle import ParamAttr + + +class SDMGRHead(nn.Layer): + def __init__( + self, + in_channels, + num_chars=92, + visual_dim=16, + fusion_dim=1024, + node_input=32, + node_embed=256, + edge_input=5, + edge_embed=256, + num_gnn=2, + num_classes=26, + bidirectional=False, + ): + super().__init__() + + self.fusion = Block([visual_dim, node_embed], node_embed, fusion_dim) + self.node_embed = nn.Embedding(num_chars, node_input, 0) + hidden = node_embed // 2 if bidirectional else node_embed + self.rnn = nn.LSTM(input_size=node_input, hidden_size=hidden, num_layers=1) + self.edge_embed = nn.Linear(edge_input, edge_embed) + self.gnn_layers = nn.LayerList( + [GNNLayer(node_embed, edge_embed) for _ in range(num_gnn)] + ) + self.node_cls = nn.Linear(node_embed, num_classes) + self.edge_cls = nn.Linear(edge_embed, 2) + + def forward(self, input, targets): + relations, texts, x = input + node_nums, char_nums = [], [] + for text in texts: + node_nums.append(text.shape[0]) + char_nums.append(paddle.sum((text > -1).astype(int), axis=-1)) + + max_num = max([char_num.max() for char_num in char_nums]) + all_nodes = paddle.concat( + [ + paddle.concat( + [text, paddle.zeros((text.shape[0], max_num - text.shape[1]))], -1 + ) + for text in texts + ] + ) + temp = paddle.clip(all_nodes, min=0).astype(int) + embed_nodes = self.node_embed(temp) + rnn_nodes, _ = self.rnn(embed_nodes) + + b, h, w = rnn_nodes.shape + nodes = paddle.zeros([b, w]) + all_nums = paddle.concat(char_nums) + valid = paddle.nonzero((all_nums > 0).astype(int)) + temp_all_nums = (paddle.gather(all_nums, valid) - 1).unsqueeze(-1).unsqueeze(-1) + temp_all_nums = paddle.expand( + temp_all_nums, + [temp_all_nums.shape[0], temp_all_nums.shape[1], rnn_nodes.shape[-1]], + ) + temp_all_nodes = paddle.gather(rnn_nodes, valid) + N, C, A = temp_all_nodes.shape + one_hot = F.one_hot(temp_all_nums[:, 0, :], num_classes=C).transpose([0, 2, 1]) + one_hot = paddle.multiply(temp_all_nodes, one_hot.astype("float32")).sum( + axis=1, keepdim=True + ) + t = one_hot.expand([N, 1, A]).squeeze(1) + nodes = paddle.scatter(nodes, valid.squeeze(1), t) + + if x is not None: + nodes = self.fusion([x, nodes]) + + all_edges = paddle.concat( + [rel.reshape([-1, rel.shape[-1]]) for rel in relations] + ) + embed_edges = self.edge_embed(all_edges.astype("float32")) + embed_edges = F.normalize(embed_edges) + + for gnn_layer in self.gnn_layers: + nodes, cat_nodes = gnn_layer(nodes, embed_edges, node_nums) + + node_cls, edge_cls = self.node_cls(nodes), self.edge_cls(cat_nodes) + return node_cls, edge_cls + + +class GNNLayer(nn.Layer): + def __init__(self, node_dim=256, edge_dim=256): + super().__init__() + self.in_fc = nn.Linear(node_dim * 2 + edge_dim, node_dim) + self.coef_fc = nn.Linear(node_dim, 1) + self.out_fc = nn.Linear(node_dim, node_dim) + self.relu = nn.ReLU() + + def forward(self, nodes, edges, nums): + start, cat_nodes = 0, [] + for num in nums: + sample_nodes = nodes[start : start + num] + cat_nodes.append( + paddle.concat( + [ + paddle.expand(sample_nodes.unsqueeze(1), [-1, num, -1]), + paddle.expand(sample_nodes.unsqueeze(0), [num, -1, -1]), + ], + -1, + ).reshape([num**2, -1]) + ) + start += num + cat_nodes = paddle.concat([paddle.concat(cat_nodes), edges], -1) + cat_nodes = self.relu(self.in_fc(cat_nodes)) + coefs = self.coef_fc(cat_nodes) + + start, residuals = 0, [] + for num in nums: + residual = F.softmax( + -paddle.eye(num).unsqueeze(-1) * 1e9 + + coefs[start : start + num**2].reshape([num, num, -1]), + 1, + ) + residuals.append( + ( + residual * cat_nodes[start : start + num**2].reshape([num, num, -1]) + ).sum(1) + ) + start += num**2 + + nodes += self.relu(self.out_fc(paddle.concat(residuals))) + return [nodes, cat_nodes] + + +class Block(nn.Layer): + def __init__( + self, + input_dims, + output_dim, + mm_dim=1600, + chunks=20, + rank=15, + shared=False, + dropout_input=0.0, + dropout_pre_lin=0.0, + dropout_output=0.0, + pos_norm="before_cat", + ): + super().__init__() + self.rank = rank + self.dropout_input = dropout_input + self.dropout_pre_lin = dropout_pre_lin + self.dropout_output = dropout_output + assert pos_norm in ["before_cat", "after_cat"] + self.pos_norm = pos_norm + # Modules + self.linear0 = nn.Linear(input_dims[0], mm_dim) + self.linear1 = self.linear0 if shared else nn.Linear(input_dims[1], mm_dim) + self.merge_linears0 = nn.LayerList() + self.merge_linears1 = nn.LayerList() + self.chunks = self.chunk_sizes(mm_dim, chunks) + for size in self.chunks: + ml0 = nn.Linear(size, size * rank) + self.merge_linears0.append(ml0) + ml1 = ml0 if shared else nn.Linear(size, size * rank) + self.merge_linears1.append(ml1) + self.linear_out = nn.Linear(mm_dim, output_dim) + + def forward(self, x): + x0 = self.linear0(x[0]) + x1 = self.linear1(x[1]) + bs = x1.shape[0] + if self.dropout_input > 0: + x0 = F.dropout(x0, p=self.dropout_input, training=self.training) + x1 = F.dropout(x1, p=self.dropout_input, training=self.training) + x0_chunks = paddle.split(x0, self.chunks, -1) + x1_chunks = paddle.split(x1, self.chunks, -1) + zs = [] + for x0_c, x1_c, m0, m1 in zip( + x0_chunks, x1_chunks, self.merge_linears0, self.merge_linears1 + ): + m = m0(x0_c) * m1(x1_c) # bs x split_size*rank + m = m.reshape([bs, self.rank, -1]) + z = paddle.sum(m, 1) + if self.pos_norm == "before_cat": + z = paddle.sqrt(F.relu(z)) - paddle.sqrt(F.relu(-z)) + z = F.normalize(z) + zs.append(z) + z = paddle.concat(zs, 1) + if self.pos_norm == "after_cat": + z = paddle.sqrt(F.relu(z)) - paddle.sqrt(F.relu(-z)) + z = F.normalize(z) + + if self.dropout_pre_lin > 0: + z = F.dropout(z, p=self.dropout_pre_lin, training=self.training) + z = self.linear_out(z) + if self.dropout_output > 0: + z = F.dropout(z, p=self.dropout_output, training=self.training) + return z + + def chunk_sizes(self, dim, chunks): + split_size = (dim + chunks - 1) // chunks + sizes_list = [split_size] * chunks + sizes_list[-1] = sizes_list[-1] - (sum(sizes_list) - dim) + return sizes_list diff --git a/modules/onnx_ocr_module/src/ppocr/modeling/heads/local_graph.py b/modules/onnx_ocr_module/src/ppocr/modeling/heads/local_graph.py new file mode 100644 index 0000000..f65c9aa --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/modeling/heads/local_graph.py @@ -0,0 +1,425 @@ +# copyright (c) 2022 PaddlePaddle Authors. All Rights Reserve. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +""" +This code is refer from: +https://github.com/open-mmlab/mmocr/blob/main/mmocr/models/textdet/modules/local_graph.py +""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import numpy as np +import paddle +import paddle.nn as nn +from ppocr.ext_op import RoIAlignRotated + + +def normalize_adjacent_matrix(A): + assert A.ndim == 2 + assert A.shape[0] == A.shape[1] + + A = A + np.eye(A.shape[0]) + d = np.sum(A, axis=0) + d = np.clip(d, 0, None) + d_inv = np.power(d, -0.5).flatten() + d_inv[np.isinf(d_inv)] = 0.0 + d_inv = np.diag(d_inv) + G = A.dot(d_inv).transpose().dot(d_inv) + return G + + +def euclidean_distance_matrix(A, B): + """Calculate the Euclidean distance matrix. + + Args: + A (ndarray): The point sequence. + B (ndarray): The point sequence with the same dimensions as A. + + returns: + D (ndarray): The Euclidean distance matrix. + """ + assert A.ndim == 2 + assert B.ndim == 2 + assert A.shape[1] == B.shape[1] + + m = A.shape[0] + n = B.shape[0] + + A_dots = (A * A).sum(axis=1).reshape((m, 1)) * np.ones(shape=(1, n)) + B_dots = (B * B).sum(axis=1) * np.ones(shape=(m, 1)) + D_squared = A_dots + B_dots - 2 * A.dot(B.T) + + zero_mask = np.less(D_squared, 0.0) + D_squared[zero_mask] = 0.0 + D = np.sqrt(D_squared) + return D + + +def feature_embedding(input_feats, out_feat_len): + """Embed features. This code was partially adapted from + https://github.com/GXYM/DRRG licensed under the MIT license. + + Args: + input_feats (ndarray): The input features of shape (N, d), where N is + the number of nodes in graph, d is the input feature vector length. + out_feat_len (int): The length of output feature vector. + + Returns: + embedded_feats (ndarray): The embedded features. + """ + assert input_feats.ndim == 2 + assert isinstance(out_feat_len, int) + assert out_feat_len >= input_feats.shape[1] + + num_nodes = input_feats.shape[0] + feat_dim = input_feats.shape[1] + feat_repeat_times = out_feat_len // feat_dim + residue_dim = out_feat_len % feat_dim + + if residue_dim > 0: + embed_wave = np.array( + [ + np.power(1000, 2.0 * (j // 2) / feat_repeat_times + 1) + for j in range(feat_repeat_times + 1) + ] + ).reshape((feat_repeat_times + 1, 1, 1)) + repeat_feats = np.repeat( + np.expand_dims(input_feats, axis=0), feat_repeat_times, axis=0 + ) + residue_feats = np.hstack( + [ + input_feats[:, 0:residue_dim], + np.zeros((num_nodes, feat_dim - residue_dim)), + ] + ) + residue_feats = np.expand_dims(residue_feats, axis=0) + repeat_feats = np.concatenate([repeat_feats, residue_feats], axis=0) + embedded_feats = repeat_feats / embed_wave + embedded_feats[:, 0::2] = np.sin(embedded_feats[:, 0::2]) + embedded_feats[:, 1::2] = np.cos(embedded_feats[:, 1::2]) + embedded_feats = np.transpose(embedded_feats, (1, 0, 2)).reshape( + (num_nodes, -1) + )[:, 0:out_feat_len] + else: + embed_wave = np.array( + [ + np.power(1000, 2.0 * (j // 2) / feat_repeat_times) + for j in range(feat_repeat_times) + ] + ).reshape((feat_repeat_times, 1, 1)) + repeat_feats = np.repeat( + np.expand_dims(input_feats, axis=0), feat_repeat_times, axis=0 + ) + embedded_feats = repeat_feats / embed_wave + embedded_feats[:, 0::2] = np.sin(embedded_feats[:, 0::2]) + embedded_feats[:, 1::2] = np.cos(embedded_feats[:, 1::2]) + embedded_feats = ( + np.transpose(embedded_feats, (1, 0, 2)) + .reshape((num_nodes, -1)) + .astype(np.float32) + ) + + return embedded_feats + + +class LocalGraphs: + def __init__( + self, + k_at_hops, + num_adjacent_linkages, + node_geo_feat_len, + pooling_scale, + pooling_output_size, + local_graph_thr, + ): + assert len(k_at_hops) == 2 + assert all(isinstance(n, int) for n in k_at_hops) + assert isinstance(num_adjacent_linkages, int) + assert isinstance(node_geo_feat_len, int) + assert isinstance(pooling_scale, float) + assert all(isinstance(n, int) for n in pooling_output_size) + assert isinstance(local_graph_thr, float) + + self.k_at_hops = k_at_hops + self.num_adjacent_linkages = num_adjacent_linkages + self.node_geo_feat_dim = node_geo_feat_len + self.pooling = RoIAlignRotated(pooling_output_size, pooling_scale) + self.local_graph_thr = local_graph_thr + + def generate_local_graphs(self, sorted_dist_inds, gt_comp_labels): + """Generate local graphs for GCN to predict which instance a text + component belongs to. + + Args: + sorted_dist_inds (ndarray): The complete graph node indices, which + is sorted according to the Euclidean distance. + gt_comp_labels(ndarray): The ground truth labels define the + instance to which the text components (nodes in graphs) belong. + + Returns: + pivot_local_graphs(list[list[int]]): The list of local graph + neighbor indices of pivots. + pivot_knns(list[list[int]]): The list of k-nearest neighbor indices + of pivots. + """ + + assert sorted_dist_inds.ndim == 2 + assert ( + sorted_dist_inds.shape[0] + == sorted_dist_inds.shape[1] + == gt_comp_labels.shape[0] + ) + + knn_graph = sorted_dist_inds[:, 1 : self.k_at_hops[0] + 1] + pivot_local_graphs = [] + pivot_knns = [] + for pivot_ind, knn in enumerate(knn_graph): + local_graph_neighbors = set(knn) + + for neighbor_ind in knn: + local_graph_neighbors.update( + set(sorted_dist_inds[neighbor_ind, 1 : self.k_at_hops[1] + 1]) + ) + + local_graph_neighbors.discard(pivot_ind) + pivot_local_graph = list(local_graph_neighbors) + pivot_local_graph.insert(0, pivot_ind) + pivot_knn = [pivot_ind] + list(knn) + + if pivot_ind < 1: + pivot_local_graphs.append(pivot_local_graph) + pivot_knns.append(pivot_knn) + else: + add_flag = True + for graph_ind, added_knn in enumerate(pivot_knns): + added_pivot_ind = added_knn[0] + added_local_graph = pivot_local_graphs[graph_ind] + + union = len( + set(pivot_local_graph[1:]).union(set(added_local_graph[1:])) + ) + intersect = len( + set(pivot_local_graph[1:]).intersection( + set(added_local_graph[1:]) + ) + ) + local_graph_iou = intersect / (union + 1e-8) + + if ( + local_graph_iou > self.local_graph_thr + and pivot_ind in added_knn + and gt_comp_labels[added_pivot_ind] == gt_comp_labels[pivot_ind] + and gt_comp_labels[pivot_ind] != 0 + ): + add_flag = False + break + if add_flag: + pivot_local_graphs.append(pivot_local_graph) + pivot_knns.append(pivot_knn) + + return pivot_local_graphs, pivot_knns + + def generate_gcn_input( + self, + node_feat_batch, + node_label_batch, + local_graph_batch, + knn_batch, + sorted_dist_ind_batch, + ): + """Generate graph convolution network input data. + + Args: + node_feat_batch (List[Tensor]): The batched graph node features. + node_label_batch (List[ndarray]): The batched text component + labels. + local_graph_batch (List[List[list[int]]]): The local graph node + indices of image batch. + knn_batch (List[List[list[int]]]): The knn graph node indices of + image batch. + sorted_dist_ind_batch (list[ndarray]): The node indices sorted + according to the Euclidean distance. + + Returns: + local_graphs_node_feat (Tensor): The node features of graph. + adjacent_matrices (Tensor): The adjacent matrices of local graphs. + pivots_knn_inds (Tensor): The k-nearest neighbor indices in + local graph. + gt_linkage (Tensor): The surpervision signal of GCN for linkage + prediction. + """ + assert isinstance(node_feat_batch, list) + assert isinstance(node_label_batch, list) + assert isinstance(local_graph_batch, list) + assert isinstance(knn_batch, list) + assert isinstance(sorted_dist_ind_batch, list) + + num_max_nodes = max( + [ + len(pivot_local_graph) + for pivot_local_graphs in local_graph_batch + for pivot_local_graph in pivot_local_graphs + ] + ) + + local_graphs_node_feat = [] + adjacent_matrices = [] + pivots_knn_inds = [] + pivots_gt_linkage = [] + + for batch_ind, sorted_dist_inds in enumerate(sorted_dist_ind_batch): + node_feats = node_feat_batch[batch_ind] + pivot_local_graphs = local_graph_batch[batch_ind] + pivot_knns = knn_batch[batch_ind] + node_labels = node_label_batch[batch_ind] + + for graph_ind, pivot_knn in enumerate(pivot_knns): + pivot_local_graph = pivot_local_graphs[graph_ind] + num_nodes = len(pivot_local_graph) + pivot_ind = pivot_local_graph[0] + node2ind_map = {j: i for i, j in enumerate(pivot_local_graph)} + + knn_inds = paddle.to_tensor([node2ind_map[i] for i in pivot_knn[1:]]) + pivot_feats = node_feats[pivot_ind] + normalized_feats = ( + node_feats[paddle.to_tensor(pivot_local_graph)] - pivot_feats + ) + + adjacent_matrix = np.zeros((num_nodes, num_nodes), dtype=np.float32) + for node in pivot_local_graph: + neighbors = sorted_dist_inds[ + node, 1 : self.num_adjacent_linkages + 1 + ] + for neighbor in neighbors: + if neighbor in pivot_local_graph: + adjacent_matrix[ + node2ind_map[node], node2ind_map[neighbor] + ] = 1 + adjacent_matrix[ + node2ind_map[neighbor], node2ind_map[node] + ] = 1 + + adjacent_matrix = normalize_adjacent_matrix(adjacent_matrix) + pad_adjacent_matrix = paddle.zeros((num_max_nodes, num_max_nodes)) + pad_adjacent_matrix[:num_nodes, :num_nodes] = paddle.cast( + paddle.to_tensor(adjacent_matrix), "float32" + ) + + pad_normalized_feats = paddle.concat( + [ + normalized_feats, + paddle.zeros( + (num_max_nodes - num_nodes, normalized_feats.shape[1]) + ), + ], + axis=0, + ) + local_graph_labels = node_labels[pivot_local_graph] + knn_labels = local_graph_labels[knn_inds.numpy()] + link_labels = ( + (node_labels[pivot_ind] == knn_labels) + & (node_labels[pivot_ind] > 0) + ).astype(np.int64) + link_labels = paddle.to_tensor(link_labels) + + local_graphs_node_feat.append(pad_normalized_feats) + adjacent_matrices.append(pad_adjacent_matrix) + pivots_knn_inds.append(knn_inds) + pivots_gt_linkage.append(link_labels) + + local_graphs_node_feat = paddle.stack(local_graphs_node_feat, 0) + adjacent_matrices = paddle.stack(adjacent_matrices, 0) + pivots_knn_inds = paddle.stack(pivots_knn_inds, 0) + pivots_gt_linkage = paddle.stack(pivots_gt_linkage, 0) + + return ( + local_graphs_node_feat, + adjacent_matrices, + pivots_knn_inds, + pivots_gt_linkage, + ) + + def __call__(self, feat_maps, comp_attribs): + """Generate local graphs as GCN input. + + Args: + feat_maps (Tensor): The feature maps to extract the content + features of text components. + comp_attribs (ndarray): The text component attributes. + + Returns: + local_graphs_node_feat (Tensor): The node features of graph. + adjacent_matrices (Tensor): The adjacent matrices of local graphs. + pivots_knn_inds (Tensor): The k-nearest neighbor indices in local + graph. + gt_linkage (Tensor): The surpervision signal of GCN for linkage + prediction. + """ + + assert isinstance(feat_maps, paddle.Tensor) + assert comp_attribs.ndim == 3 + assert comp_attribs.shape[2] == 8 + + sorted_dist_inds_batch = [] + local_graph_batch = [] + knn_batch = [] + node_feat_batch = [] + node_label_batch = [] + + for batch_ind in range(comp_attribs.shape[0]): + num_comps = int(comp_attribs[batch_ind, 0, 0]) + comp_geo_attribs = comp_attribs[batch_ind, :num_comps, 1:7] + node_labels = comp_attribs[batch_ind, :num_comps, 7].astype(np.int32) + + comp_centers = comp_geo_attribs[:, 0:2] + distance_matrix = euclidean_distance_matrix(comp_centers, comp_centers) + + batch_id = ( + np.zeros((comp_geo_attribs.shape[0], 1), dtype=np.float32) * batch_ind + ) + comp_geo_attribs[:, -2] = np.clip(comp_geo_attribs[:, -2], -1, 1) + angle = np.arccos(comp_geo_attribs[:, -2]) * np.sign( + comp_geo_attribs[:, -1] + ) + angle = angle.reshape((-1, 1)) + rotated_rois = np.hstack([batch_id, comp_geo_attribs[:, :-2], angle]) + rois = paddle.to_tensor(rotated_rois) + content_feats = self.pooling(feat_maps[batch_ind].unsqueeze(0), rois) + + content_feats = content_feats.reshape([content_feats.shape[0], -1]) + geo_feats = feature_embedding(comp_geo_attribs, self.node_geo_feat_dim) + geo_feats = paddle.to_tensor(geo_feats) + node_feats = paddle.concat([content_feats, geo_feats], axis=-1) + + sorted_dist_inds = np.argsort(distance_matrix, axis=1) + pivot_local_graphs, pivot_knns = self.generate_local_graphs( + sorted_dist_inds, node_labels + ) + + node_feat_batch.append(node_feats) + node_label_batch.append(node_labels) + local_graph_batch.append(pivot_local_graphs) + knn_batch.append(pivot_knns) + sorted_dist_inds_batch.append(sorted_dist_inds) + + (node_feats, adjacent_matrices, knn_inds, gt_linkage) = self.generate_gcn_input( + node_feat_batch, + node_label_batch, + local_graph_batch, + knn_batch, + sorted_dist_inds_batch, + ) + + return node_feats, adjacent_matrices, knn_inds, gt_linkage diff --git a/modules/onnx_ocr_module/src/ppocr/modeling/heads/proposal_local_graph.py b/modules/onnx_ocr_module/src/ppocr/modeling/heads/proposal_local_graph.py new file mode 100644 index 0000000..7bf0765 --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/modeling/heads/proposal_local_graph.py @@ -0,0 +1,476 @@ +# copyright (c) 2022 PaddlePaddle Authors. All Rights Reserve. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +""" +This code is refer from: +https://github.com/open-mmlab/mmocr/blob/main/mmocr/models/textdet/modules/proposal_local_graph.py +""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import cv2 +import numpy as np +import paddle +import paddle.nn as nn +import paddle.nn.functional as F +from lanms import merge_quadrangle_n9 as la_nms + +from ppocr.ext_op import RoIAlignRotated +from .local_graph import ( + euclidean_distance_matrix, + feature_embedding, + normalize_adjacent_matrix, +) + + +def fill_hole(input_mask): + h, w = input_mask.shape + canvas = np.zeros((h + 2, w + 2), np.uint8) + canvas[1 : h + 1, 1 : w + 1] = input_mask.copy() + + mask = np.zeros((h + 4, w + 4), np.uint8) + + cv2.floodFill(canvas, mask, (0, 0), 1) + canvas = canvas[1 : h + 1, 1 : w + 1].astype(np.bool_) + + return ~canvas | input_mask + + +class ProposalLocalGraphs: + def __init__( + self, + k_at_hops, + num_adjacent_linkages, + node_geo_feat_len, + pooling_scale, + pooling_output_size, + nms_thr, + min_width, + max_width, + comp_shrink_ratio, + comp_w_h_ratio, + comp_score_thr, + text_region_thr, + center_region_thr, + center_region_area_thr, + ): + assert len(k_at_hops) == 2 + assert isinstance(k_at_hops, tuple) + assert isinstance(num_adjacent_linkages, int) + assert isinstance(node_geo_feat_len, int) + assert isinstance(pooling_scale, float) + assert isinstance(pooling_output_size, tuple) + assert isinstance(nms_thr, float) + assert isinstance(min_width, float) + assert isinstance(max_width, float) + assert isinstance(comp_shrink_ratio, float) + assert isinstance(comp_w_h_ratio, float) + assert isinstance(comp_score_thr, float) + assert isinstance(text_region_thr, float) + assert isinstance(center_region_thr, float) + assert isinstance(center_region_area_thr, int) + + self.k_at_hops = k_at_hops + self.active_connection = num_adjacent_linkages + self.local_graph_depth = len(self.k_at_hops) + self.node_geo_feat_dim = node_geo_feat_len + self.pooling = RoIAlignRotated(pooling_output_size, pooling_scale) + self.nms_thr = nms_thr + self.min_width = min_width + self.max_width = max_width + self.comp_shrink_ratio = comp_shrink_ratio + self.comp_w_h_ratio = comp_w_h_ratio + self.comp_score_thr = comp_score_thr + self.text_region_thr = text_region_thr + self.center_region_thr = center_region_thr + self.center_region_area_thr = center_region_area_thr + + def propose_comps( + self, + score_map, + top_height_map, + bot_height_map, + sin_map, + cos_map, + comp_score_thr, + min_width, + max_width, + comp_shrink_ratio, + comp_w_h_ratio, + ): + """Propose text components. + + Args: + score_map (ndarray): The score map for NMS. + top_height_map (ndarray): The predicted text height map from each + pixel in text center region to top sideline. + bot_height_map (ndarray): The predicted text height map from each + pixel in text center region to bottom sideline. + sin_map (ndarray): The predicted sin(theta) map. + cos_map (ndarray): The predicted cos(theta) map. + comp_score_thr (float): The score threshold of text component. + min_width (float): The minimum width of text components. + max_width (float): The maximum width of text components. + comp_shrink_ratio (float): The shrink ratio of text components. + comp_w_h_ratio (float): The width to height ratio of text + components. + + Returns: + text_comps (ndarray): The text components. + """ + + comp_centers = np.argwhere(score_map > comp_score_thr) + comp_centers = comp_centers[np.argsort(comp_centers[:, 0])] + y = comp_centers[:, 0] + x = comp_centers[:, 1] + + top_height = top_height_map[y, x].reshape((-1, 1)) * comp_shrink_ratio + bot_height = bot_height_map[y, x].reshape((-1, 1)) * comp_shrink_ratio + sin = sin_map[y, x].reshape((-1, 1)) + cos = cos_map[y, x].reshape((-1, 1)) + + top_mid_pts = comp_centers + np.hstack([top_height * sin, top_height * cos]) + bot_mid_pts = comp_centers - np.hstack([bot_height * sin, bot_height * cos]) + + width = (top_height + bot_height) * comp_w_h_ratio + width = np.clip(width, min_width, max_width) + r = width / 2 + + tl = top_mid_pts[:, ::-1] - np.hstack([-r * sin, r * cos]) + tr = top_mid_pts[:, ::-1] + np.hstack([-r * sin, r * cos]) + br = bot_mid_pts[:, ::-1] + np.hstack([-r * sin, r * cos]) + bl = bot_mid_pts[:, ::-1] - np.hstack([-r * sin, r * cos]) + text_comps = np.hstack([tl, tr, br, bl]).astype(np.float32) + + score = score_map[y, x].reshape((-1, 1)) + text_comps = np.hstack([text_comps, score]) + + return text_comps + + def propose_comps_and_attribs( + self, + text_region_map, + center_region_map, + top_height_map, + bot_height_map, + sin_map, + cos_map, + ): + """Generate text components and attributes. + + Args: + text_region_map (ndarray): The predicted text region probability + map. + center_region_map (ndarray): The predicted text center region + probability map. + top_height_map (ndarray): The predicted text height map from each + pixel in text center region to top sideline. + bot_height_map (ndarray): The predicted text height map from each + pixel in text center region to bottom sideline. + sin_map (ndarray): The predicted sin(theta) map. + cos_map (ndarray): The predicted cos(theta) map. + + Returns: + comp_attribs (ndarray): The text component attributes. + text_comps (ndarray): The text components. + """ + + assert ( + text_region_map.shape + == center_region_map.shape + == top_height_map.shape + == bot_height_map.shape + == sin_map.shape + == cos_map.shape + ) + text_mask = text_region_map > self.text_region_thr + center_region_mask = (center_region_map > self.center_region_thr) * text_mask + + scale = np.sqrt(1.0 / (sin_map**2 + cos_map**2 + 1e-8)) + sin_map, cos_map = sin_map * scale, cos_map * scale + + center_region_mask = fill_hole(center_region_mask) + center_region_contours, _ = cv2.findContours( + center_region_mask.astype(np.uint8), cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE + ) + + mask_sz = center_region_map.shape + comp_list = [] + for contour in center_region_contours: + current_center_mask = np.zeros(mask_sz) + cv2.drawContours(current_center_mask, [contour], -1, 1, -1) + if current_center_mask.sum() <= self.center_region_area_thr: + continue + score_map = text_region_map * current_center_mask + + text_comps = self.propose_comps( + score_map, + top_height_map, + bot_height_map, + sin_map, + cos_map, + self.comp_score_thr, + self.min_width, + self.max_width, + self.comp_shrink_ratio, + self.comp_w_h_ratio, + ) + + text_comps = la_nms(text_comps, self.nms_thr) + text_comp_mask = np.zeros(mask_sz) + text_comp_boxes = text_comps[:, :8].reshape((-1, 4, 2)).astype(np.int32) + + cv2.drawContours(text_comp_mask, text_comp_boxes, -1, 1, -1) + if (text_comp_mask * text_mask).sum() < text_comp_mask.sum() * 0.5: + continue + if text_comps.shape[-1] > 0: + comp_list.append(text_comps) + + if len(comp_list) <= 0: + return None, None + + text_comps = np.vstack(comp_list) + text_comp_boxes = text_comps[:, :8].reshape((-1, 4, 2)) + centers = np.mean(text_comp_boxes, axis=1).astype(np.int32) + x = centers[:, 0] + y = centers[:, 1] + + scores = [] + for text_comp_box in text_comp_boxes: + text_comp_box[:, 0] = np.clip(text_comp_box[:, 0], 0, mask_sz[1] - 1) + text_comp_box[:, 1] = np.clip(text_comp_box[:, 1], 0, mask_sz[0] - 1) + min_coord = np.min(text_comp_box, axis=0).astype(np.int32) + max_coord = np.max(text_comp_box, axis=0).astype(np.int32) + text_comp_box = text_comp_box - min_coord + box_sz = max_coord - min_coord + 1 + temp_comp_mask = np.zeros((box_sz[1], box_sz[0]), dtype=np.uint8) + cv2.fillPoly(temp_comp_mask, [text_comp_box.astype(np.int32)], 1) + temp_region_patch = text_region_map[ + min_coord[1] : (max_coord[1] + 1), min_coord[0] : (max_coord[0] + 1) + ] + score = cv2.mean(temp_region_patch, temp_comp_mask)[0] + scores.append(score) + scores = np.array(scores).reshape((-1, 1)) + text_comps = np.hstack([text_comps[:, :-1], scores]) + + h = top_height_map[y, x].reshape((-1, 1)) + bot_height_map[y, x].reshape( + (-1, 1) + ) + w = np.clip(h * self.comp_w_h_ratio, self.min_width, self.max_width) + sin = sin_map[y, x].reshape((-1, 1)) + cos = cos_map[y, x].reshape((-1, 1)) + + x = x.reshape((-1, 1)) + y = y.reshape((-1, 1)) + comp_attribs = np.hstack([x, y, h, w, cos, sin]) + + return comp_attribs, text_comps + + def generate_local_graphs(self, sorted_dist_inds, node_feats): + """Generate local graphs and graph convolution network input data. + + Args: + sorted_dist_inds (ndarray): The node indices sorted according to + the Euclidean distance. + node_feats (tensor): The features of nodes in graph. + + Returns: + local_graphs_node_feats (tensor): The features of nodes in local + graphs. + adjacent_matrices (tensor): The adjacent matrices. + pivots_knn_inds (tensor): The k-nearest neighbor indices in + local graphs. + pivots_local_graphs (tensor): The indices of nodes in local + graphs. + """ + + assert sorted_dist_inds.ndim == 2 + assert ( + sorted_dist_inds.shape[0] + == sorted_dist_inds.shape[1] + == node_feats.shape[0] + ) + + knn_graph = sorted_dist_inds[:, 1 : self.k_at_hops[0] + 1] + pivot_local_graphs = [] + pivot_knns = [] + + for pivot_ind, knn in enumerate(knn_graph): + local_graph_neighbors = set(knn) + + for neighbor_ind in knn: + local_graph_neighbors.update( + set(sorted_dist_inds[neighbor_ind, 1 : self.k_at_hops[1] + 1]) + ) + + local_graph_neighbors.discard(pivot_ind) + pivot_local_graph = list(local_graph_neighbors) + pivot_local_graph.insert(0, pivot_ind) + pivot_knn = [pivot_ind] + list(knn) + + pivot_local_graphs.append(pivot_local_graph) + pivot_knns.append(pivot_knn) + + num_max_nodes = max( + [len(pivot_local_graph) for pivot_local_graph in pivot_local_graphs] + ) + + local_graphs_node_feat = [] + adjacent_matrices = [] + pivots_knn_inds = [] + pivots_local_graphs = [] + + for graph_ind, pivot_knn in enumerate(pivot_knns): + pivot_local_graph = pivot_local_graphs[graph_ind] + num_nodes = len(pivot_local_graph) + pivot_ind = pivot_local_graph[0] + node2ind_map = {j: i for i, j in enumerate(pivot_local_graph)} + + knn_inds = paddle.cast( + paddle.to_tensor([node2ind_map[i] for i in pivot_knn[1:]]), "int64" + ) + pivot_feats = node_feats[pivot_ind] + normalized_feats = ( + node_feats[paddle.to_tensor(pivot_local_graph)] - pivot_feats + ) + + adjacent_matrix = np.zeros((num_nodes, num_nodes), dtype=np.float32) + for node in pivot_local_graph: + neighbors = sorted_dist_inds[node, 1 : self.active_connection + 1] + for neighbor in neighbors: + if neighbor in pivot_local_graph: + adjacent_matrix[node2ind_map[node], node2ind_map[neighbor]] = 1 + adjacent_matrix[node2ind_map[neighbor], node2ind_map[node]] = 1 + + adjacent_matrix = normalize_adjacent_matrix(adjacent_matrix) + pad_adjacent_matrix = paddle.zeros( + (num_max_nodes, num_max_nodes), + ) + pad_adjacent_matrix[:num_nodes, :num_nodes] = paddle.cast( + paddle.to_tensor(adjacent_matrix), "float32" + ) + + pad_normalized_feats = paddle.concat( + [ + normalized_feats, + paddle.zeros( + (num_max_nodes - num_nodes, normalized_feats.shape[1]), + ), + ], + axis=0, + ) + + local_graph_nodes = paddle.to_tensor(pivot_local_graph) + local_graph_nodes = paddle.concat( + [ + local_graph_nodes, + paddle.zeros([num_max_nodes - num_nodes], dtype="int64"), + ], + axis=-1, + ) + + local_graphs_node_feat.append(pad_normalized_feats) + adjacent_matrices.append(pad_adjacent_matrix) + pivots_knn_inds.append(knn_inds) + pivots_local_graphs.append(local_graph_nodes) + + local_graphs_node_feat = paddle.stack(local_graphs_node_feat, 0) + adjacent_matrices = paddle.stack(adjacent_matrices, 0) + pivots_knn_inds = paddle.stack(pivots_knn_inds, 0) + pivots_local_graphs = paddle.stack(pivots_local_graphs, 0) + + return ( + local_graphs_node_feat, + adjacent_matrices, + pivots_knn_inds, + pivots_local_graphs, + ) + + def __call__(self, preds, feat_maps): + """Generate local graphs and graph convolutional network input data. + + Args: + preds (tensor): The predicted maps. + feat_maps (tensor): The feature maps to extract content feature of + text components. + + Returns: + none_flag (bool): The flag showing whether the number of proposed + text components is 0. + local_graphs_node_feats (tensor): The features of nodes in local + graphs. + adjacent_matrices (tensor): The adjacent matrices. + pivots_knn_inds (tensor): The k-nearest neighbor indices in + local graphs. + pivots_local_graphs (tensor): The indices of nodes in local + graphs. + text_comps (ndarray): The predicted text components. + """ + if preds.ndim == 4: + assert preds.shape[0] == 1 + preds = paddle.squeeze(preds) + pred_text_region = F.sigmoid(preds[0]).numpy() + pred_center_region = F.sigmoid(preds[1]).numpy() + pred_sin_map = preds[2].numpy() + pred_cos_map = preds[3].numpy() + pred_top_height_map = preds[4].numpy() + pred_bot_height_map = preds[5].numpy() + + comp_attribs, text_comps = self.propose_comps_and_attribs( + pred_text_region, + pred_center_region, + pred_top_height_map, + pred_bot_height_map, + pred_sin_map, + pred_cos_map, + ) + + if comp_attribs is None or len(comp_attribs) < 2: + none_flag = True + return none_flag, (0, 0, 0, 0, 0) + + comp_centers = comp_attribs[:, 0:2] + distance_matrix = euclidean_distance_matrix(comp_centers, comp_centers) + + geo_feats = feature_embedding(comp_attribs, self.node_geo_feat_dim) + geo_feats = paddle.to_tensor(geo_feats) + + batch_id = np.zeros((comp_attribs.shape[0], 1), dtype=np.float32) + comp_attribs = comp_attribs.astype(np.float32) + angle = np.arccos(comp_attribs[:, -2]) * np.sign(comp_attribs[:, -1]) + angle = angle.reshape((-1, 1)) + rotated_rois = np.hstack([batch_id, comp_attribs[:, :-2], angle]) + rois = paddle.to_tensor(rotated_rois) + + content_feats = self.pooling(feat_maps, rois) + content_feats = content_feats.reshape([content_feats.shape[0], -1]) + node_feats = paddle.concat([content_feats, geo_feats], axis=-1) + + sorted_dist_inds = np.argsort(distance_matrix, axis=1) + ( + local_graphs_node_feat, + adjacent_matrices, + pivots_knn_inds, + pivots_local_graphs, + ) = self.generate_local_graphs(sorted_dist_inds, node_feats) + + none_flag = False + return none_flag, ( + local_graphs_node_feat, + adjacent_matrices, + pivots_knn_inds, + pivots_local_graphs, + text_comps, + ) diff --git a/modules/onnx_ocr_module/src/ppocr/modeling/heads/rec_abinet_head.py b/modules/onnx_ocr_module/src/ppocr/modeling/heads/rec_abinet_head.py new file mode 100644 index 0000000..15628c9 --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/modeling/heads/rec_abinet_head.py @@ -0,0 +1,299 @@ +# copyright (c) 2021 PaddlePaddle Authors. All Rights Reserve. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +""" +This code is refer from: +https://github.com/FangShancheng/ABINet/tree/main/modules +""" + +import math +import paddle +from paddle import nn +import paddle.nn.functional as F +from paddle.nn import LayerList +from ppocr.modeling.heads.rec_nrtr_head import TransformerBlock, PositionalEncoding + + +class BCNLanguage(nn.Layer): + def __init__( + self, + d_model=512, + nhead=8, + num_layers=4, + dim_feedforward=2048, + dropout=0.0, + max_length=25, + detach=True, + num_classes=37, + ): + super().__init__() + + self.d_model = d_model + self.detach = detach + self.max_length = max_length + 1 # additional stop token + self.proj = nn.Linear(num_classes, d_model, bias_attr=False) + self.token_encoder = PositionalEncoding( + dropout=0.1, dim=d_model, max_len=self.max_length + ) + self.pos_encoder = PositionalEncoding( + dropout=0, dim=d_model, max_len=self.max_length + ) + + self.decoder = nn.LayerList( + [ + TransformerBlock( + d_model=d_model, + nhead=nhead, + dim_feedforward=dim_feedforward, + attention_dropout_rate=dropout, + residual_dropout_rate=dropout, + with_self_attn=False, + with_cross_attn=True, + ) + for i in range(num_layers) + ] + ) + + self.cls = nn.Linear(d_model, num_classes) + + def forward(self, tokens, lengths): + """ + Args: + tokens: (B, N, C) where N is length, B is batch size and C is classes number + lengths: (B,) + """ + if self.detach: + tokens = tokens.detach() + embed = self.proj(tokens) # (B, N, C) + embed = self.token_encoder(embed) # (B, N, C) + padding_mask = _get_mask(lengths, self.max_length) + zeros = paddle.zeros_like(embed) # (B, N, C) + query = self.pos_encoder(zeros) + for decoder_layer in self.decoder: + query = decoder_layer(query, embed, cross_mask=padding_mask) + output = query # (B, N, C) + + logits = self.cls(output) # (B, N, C) + + return output, logits + + +def encoder_layer(in_c, out_c, k=3, s=2, p=1): + return nn.Sequential( + nn.Conv2D(in_c, out_c, k, s, p), nn.BatchNorm2D(out_c), nn.ReLU() + ) + + +def decoder_layer( + in_c, out_c, k=3, s=1, p=1, mode="nearest", scale_factor=None, size=None +): + align_corners = False if mode == "nearest" else True + return nn.Sequential( + nn.Upsample( + size=size, scale_factor=scale_factor, mode=mode, align_corners=align_corners + ), + nn.Conv2D(in_c, out_c, k, s, p), + nn.BatchNorm2D(out_c), + nn.ReLU(), + ) + + +class PositionAttention(nn.Layer): + def __init__( + self, + max_length, + in_channels=512, + num_channels=64, + h=8, + w=32, + mode="nearest", + **kwargs, + ): + super().__init__() + self.max_length = max_length + self.k_encoder = nn.Sequential( + encoder_layer(in_channels, num_channels, s=(1, 2)), + encoder_layer(num_channels, num_channels, s=(2, 2)), + encoder_layer(num_channels, num_channels, s=(2, 2)), + encoder_layer(num_channels, num_channels, s=(2, 2)), + ) + self.k_decoder = nn.Sequential( + decoder_layer(num_channels, num_channels, scale_factor=2, mode=mode), + decoder_layer(num_channels, num_channels, scale_factor=2, mode=mode), + decoder_layer(num_channels, num_channels, scale_factor=2, mode=mode), + decoder_layer(num_channels, in_channels, size=(h, w), mode=mode), + ) + + self.pos_encoder = PositionalEncoding( + dropout=0, dim=in_channels, max_len=max_length + ) + self.project = nn.Linear(in_channels, in_channels) + + def forward(self, x): + B, C, H, W = x.shape + k, v = x, x + + # calculate key vector + features = [] + for i in range(0, len(self.k_encoder)): + k = self.k_encoder[i](k) + features.append(k) + for i in range(0, len(self.k_decoder) - 1): + k = self.k_decoder[i](k) + # print(k.shape, features[len(self.k_decoder) - 2 - i].shape) + k = k + features[len(self.k_decoder) - 2 - i] + k = self.k_decoder[-1](k) + + # calculate query vector + # TODO q=f(q,k) + zeros = paddle.zeros((B, self.max_length, C), dtype=x.dtype) # (B, N, C) + q = self.pos_encoder(zeros) # (B, N, C) + q = self.project(q) # (B, N, C) + + # calculate attention + attn_scores = q @ k.flatten(2) # (B, N, (H*W)) + attn_scores = attn_scores / (C**0.5) + attn_scores = F.softmax(attn_scores, axis=-1) + + v = v.flatten(2).transpose([0, 2, 1]) # (B, (H*W), C) + attn_vecs = attn_scores @ v # (B, N, C) + + return attn_vecs, attn_scores.reshape([0, self.max_length, H, W]) + + +class ABINetHead(nn.Layer): + def __init__( + self, + in_channels, + out_channels, + d_model=512, + nhead=8, + num_layers=3, + dim_feedforward=2048, + dropout=0.1, + max_length=25, + use_lang=False, + iter_size=1, + image_size=(32, 128), + ): + super().__init__() + self.max_length = max_length + 1 + h, w = image_size[0] // 4, image_size[1] // 4 + self.pos_encoder = PositionalEncoding(dropout=0.1, dim=d_model, max_len=h * w) + self.encoder = nn.LayerList( + [ + TransformerBlock( + d_model=d_model, + nhead=nhead, + dim_feedforward=dim_feedforward, + attention_dropout_rate=dropout, + residual_dropout_rate=dropout, + with_self_attn=True, + with_cross_attn=False, + ) + for i in range(num_layers) + ] + ) + self.decoder = PositionAttention( + max_length=max_length + 1, mode="nearest", h=h, w=w # additional stop token + ) + self.out_channels = out_channels + self.cls = nn.Linear(d_model, self.out_channels) + self.use_lang = use_lang + if use_lang: + self.iter_size = iter_size + self.language = BCNLanguage( + d_model=d_model, + nhead=nhead, + num_layers=4, + dim_feedforward=dim_feedforward, + dropout=dropout, + max_length=max_length, + num_classes=self.out_channels, + ) + # alignment + self.w_att_align = nn.Linear(2 * d_model, d_model) + self.cls_align = nn.Linear(d_model, self.out_channels) + + def forward(self, x, targets=None): + x = x.transpose([0, 2, 3, 1]) + _, H, W, C = x.shape + feature = x.flatten(1, 2) + feature = self.pos_encoder(feature) + for encoder_layer in self.encoder: + feature = encoder_layer(feature) + feature = feature.reshape([0, H, W, C]).transpose([0, 3, 1, 2]) + v_feature, attn_scores = self.decoder(feature) # (B, N, C), (B, C, H, W) + vis_logits = self.cls(v_feature) # (B, N, C) + logits = vis_logits + vis_lengths = _get_length(vis_logits) + if self.use_lang: + align_logits = vis_logits + align_lengths = vis_lengths + all_l_res, all_a_res = [], [] + for i in range(self.iter_size): + tokens = F.softmax(align_logits, axis=-1) + lengths = align_lengths + lengths = paddle.clip( + lengths, 2, self.max_length + ) # TODO:move to language model + l_feature, l_logits = self.language(tokens, lengths) + + # alignment + all_l_res.append(l_logits) + fuse = paddle.concat((l_feature, v_feature), -1) + f_att = F.sigmoid(self.w_att_align(fuse)) + output = f_att * v_feature + (1 - f_att) * l_feature + align_logits = self.cls_align(output) # (B, N, C) + + align_lengths = _get_length(align_logits) + all_a_res.append(align_logits) + if self.training: + return {"align": all_a_res, "lang": all_l_res, "vision": vis_logits} + else: + logits = align_logits + if self.training: + return logits + else: + return F.softmax(logits, -1) + + +def _get_length(logit): + """Greed decoder to obtain length from logit""" + out = logit.argmax(-1) == 0 + abn = out.any(-1) + out_int = out.cast("int32") + out = (out_int.cumsum(-1) == 1) & out + out = out.cast("int32") + out = out.argmax(-1) + out = out + 1 + len_seq = paddle.zeros_like(out) + logit.shape[1] + out = paddle.where(abn, out, len_seq) + return out + + +def _get_mask(length, max_length): + """Generate a square mask for the sequence. The masked positions are filled with float('-inf'). + Unmasked positions are filled with float(0.0). + """ + length = length.unsqueeze(-1) + B = length.shape[0] + grid = paddle.arange(0, max_length).unsqueeze(0).tile([B, 1]) + zero_mask = paddle.zeros([B, max_length], dtype="float32") + inf_mask = paddle.full([B, max_length], "-inf", dtype="float32") + diag_mask = paddle.diag( + paddle.full([max_length], "-inf", dtype=paddle.float32), offset=0, name=None + ) + mask = paddle.where(grid >= length, inf_mask, zero_mask) + mask = mask.unsqueeze(1) + diag_mask + return mask.unsqueeze(1) diff --git a/modules/onnx_ocr_module/src/ppocr/modeling/heads/rec_aster_head.py b/modules/onnx_ocr_module/src/ppocr/modeling/heads/rec_aster_head.py new file mode 100644 index 0000000..60eb995 --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/modeling/heads/rec_aster_head.py @@ -0,0 +1,404 @@ +# copyright (c) 2020 PaddlePaddle Authors. All Rights Reserve. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +""" +This code is refer from: +https://github.com/ayumiymk/aster.pytorch/blob/master/lib/models/attention_recognition_head.py +""" +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import sys + +import paddle +from paddle import nn +from paddle.nn import functional as F + + +class AsterHead(nn.Layer): + def __init__( + self, + in_channels, + out_channels, + sDim, + attDim, + max_len_labels, + time_step=25, + beam_width=5, + **kwargs, + ): + super(AsterHead, self).__init__() + self.num_classes = out_channels + self.in_planes = in_channels + self.sDim = sDim + self.attDim = attDim + self.max_len_labels = max_len_labels + self.decoder = AttentionRecognitionHead( + in_channels, out_channels, sDim, attDim, max_len_labels + ) + self.time_step = time_step + self.embedder = Embedding(self.time_step, in_channels) + self.beam_width = beam_width + self.eos = self.num_classes - 3 + + def forward(self, x, targets=None, embed=None): + return_dict = {} + embedding_vectors = self.embedder(x) + + if self.training: + rec_targets, rec_lengths, _ = targets + rec_pred = self.decoder([x, rec_targets, rec_lengths], embedding_vectors) + return_dict["rec_pred"] = rec_pred + return_dict["embedding_vectors"] = embedding_vectors + else: + rec_pred, rec_pred_scores = self.decoder.beam_search( + x, self.beam_width, self.eos, embedding_vectors + ) + return_dict["rec_pred"] = rec_pred + return_dict["rec_pred_scores"] = rec_pred_scores + return_dict["embedding_vectors"] = embedding_vectors + + return return_dict + + +class Embedding(nn.Layer): + def __init__(self, in_timestep, in_planes, mid_dim=4096, embed_dim=300): + super(Embedding, self).__init__() + self.in_timestep = in_timestep + self.in_planes = in_planes + self.embed_dim = embed_dim + self.mid_dim = mid_dim + self.eEmbed = nn.Linear( + in_timestep * in_planes, self.embed_dim + ) # Embed encoder output to a word-embedding like + + def forward(self, x): + x = paddle.reshape(x, [x.shape[0], -1]) + x = self.eEmbed(x) + return x + + +class AttentionRecognitionHead(nn.Layer): + """ + input: [b x 16 x 64 x in_planes] + output: probability sequence: [b x T x num_classes] + """ + + def __init__(self, in_channels, out_channels, sDim, attDim, max_len_labels): + super(AttentionRecognitionHead, self).__init__() + self.num_classes = ( + out_channels # this is the output classes. So it includes the . + ) + self.in_planes = in_channels + self.sDim = sDim + self.attDim = attDim + self.max_len_labels = max_len_labels + + self.decoder = DecoderUnit( + sDim=sDim, xDim=in_channels, yDim=self.num_classes, attDim=attDim + ) + + def forward(self, x, embed): + x, targets, lengths = x + batch_size = x.shape[0] + # Decoder + state = self.decoder.get_initial_state(embed) + outputs = [] + for i in range(max(lengths)): + if i == 0: + y_prev = paddle.full(shape=[batch_size], fill_value=self.num_classes) + else: + y_prev = targets[:, i - 1] + output, state = self.decoder(x, state, y_prev) + outputs.append(output) + outputs = paddle.concat([_.unsqueeze(1) for _ in outputs], 1) + return outputs + + # inference stage. + def sample(self, x): + x, _, _ = x + batch_size = x.size(0) + # Decoder + state = paddle.zeros([1, batch_size, self.sDim]) + + predicted_ids, predicted_scores, predicted = [], [], None + for i in range(self.max_len_labels): + if i == 0: + y_prev = paddle.full(shape=[batch_size], fill_value=self.num_classes) + else: + y_prev = predicted + + output, state = self.decoder(x, state, y_prev) + output = F.softmax(output, axis=1) + score, predicted = output.max(1) + predicted_ids.append(predicted.unsqueeze(1)) + predicted_scores.append(score.unsqueeze(1)) + predicted_ids = paddle.concat([predicted_ids, 1]) + predicted_scores = paddle.concat([predicted_scores, 1]) + # return predicted_ids.squeeze(), predicted_scores.squeeze() + return predicted_ids, predicted_scores + + def beam_search(self, x, beam_width, eos, embed): + def _inflate(tensor, times, dim): + repeat_dims = [1] * tensor.dim() + repeat_dims[dim] = times + output = paddle.tile(tensor, repeat_dims) + return output + + # https://github.com/IBM/pytorch-seq2seq/blob/fede87655ddce6c94b38886089e05321dc9802af/seq2seq/models/TopKDecoder.py + batch_size, l, d = x.shape + x = paddle.tile( + paddle.transpose(x.unsqueeze(1), perm=[1, 0, 2, 3]), [beam_width, 1, 1, 1] + ) + inflated_encoder_feats = paddle.reshape( + paddle.transpose(x, perm=[1, 0, 2, 3]), [-1, l, d] + ) + + # Initialize the decoder + state = self.decoder.get_initial_state(embed, tile_times=beam_width) + + pos_index = paddle.reshape( + paddle.arange(batch_size) * beam_width, shape=[-1, 1] + ) + + # Initialize the scores + sequence_scores = paddle.full( + shape=[batch_size * beam_width, 1], fill_value=-float("Inf") + ) + index = [i * beam_width for i in range(0, batch_size)] + sequence_scores[index] = 0.0 + + # Initialize the input vector + y_prev = paddle.full( + shape=[batch_size * beam_width], fill_value=self.num_classes + ) + + # Store decisions for backtracking + stored_scores = list() + stored_predecessors = list() + stored_emitted_symbols = list() + + for i in range(self.max_len_labels): + output, state = self.decoder(inflated_encoder_feats, state, y_prev) + state = paddle.unsqueeze(state, axis=0) + log_softmax_output = paddle.nn.functional.log_softmax(output, axis=1) + + sequence_scores = _inflate(sequence_scores, self.num_classes, 1) + sequence_scores += log_softmax_output + scores, candidates = paddle.topk( + paddle.reshape(sequence_scores, [batch_size, -1]), beam_width, axis=1 + ) + + # Reshape input = (bk, 1) and sequence_scores = (bk, 1) + y_prev = paddle.reshape( + candidates % self.num_classes, shape=[batch_size * beam_width] + ) + sequence_scores = paddle.reshape(scores, shape=[batch_size * beam_width, 1]) + + # Update fields for next timestep + pos_index = paddle.expand_as(pos_index, candidates) + predecessors = paddle.cast( + candidates / self.num_classes + pos_index, dtype="int64" + ) + predecessors = paddle.reshape( + predecessors, shape=[batch_size * beam_width, 1] + ) + state = paddle.index_select(state, index=predecessors.squeeze(), axis=1) + + # Update sequence scores and erase scores for symbol so that they aren't expanded + stored_scores.append(sequence_scores.clone()) + y_prev = paddle.reshape(y_prev, shape=[-1, 1]) + eos_prev = paddle.full_like(y_prev, fill_value=eos) + mask = eos_prev == y_prev + mask = paddle.nonzero(mask) + if mask.dim() > 0: + sequence_scores = sequence_scores.numpy() + mask = mask.numpy() + sequence_scores[mask] = -float("inf") + sequence_scores = paddle.to_tensor(sequence_scores) + + # Cache results for backtracking + stored_predecessors.append(predecessors) + y_prev = paddle.squeeze(y_prev) + stored_emitted_symbols.append(y_prev) + + # Do backtracking to return the optimal values + # ====== backtrak ======# + # Initialize return variables given different types + p = list() + l = [ + [self.max_len_labels] * beam_width for _ in range(batch_size) + ] # Placeholder for lengths of top-k sequences + + # the last step output of the beams are not sorted + # thus they are sorted here + sorted_score, sorted_idx = paddle.topk( + paddle.reshape(stored_scores[-1], shape=[batch_size, beam_width]), + beam_width, + ) + + # initialize the sequence scores with the sorted last step beam scores + s = sorted_score.clone() + + batch_eos_found = [0] * batch_size # the number of EOS found + # in the backward loop below for each batch + t = self.max_len_labels - 1 + # initialize the back pointer with the sorted order of the last step beams. + # add pos_index for indexing variable with b*k as the first dimension. + t_predecessors = paddle.reshape( + sorted_idx + pos_index.expand_as(sorted_idx), + shape=[batch_size * beam_width], + ) + while t >= 0: + # Re-order the variables with the back pointer + current_symbol = paddle.index_select( + stored_emitted_symbols[t], index=t_predecessors, axis=0 + ) + t_predecessors = paddle.index_select( + stored_predecessors[t].squeeze(), index=t_predecessors, axis=0 + ) + eos_indices = stored_emitted_symbols[t] == eos + eos_indices = paddle.nonzero(eos_indices) + + if eos_indices.dim() > 0: + for i in range(eos_indices.shape[0] - 1, -1, -1): + # Indices of the EOS symbol for both variables + # with b*k as the first dimension, and b, k for + # the first two dimensions + idx = eos_indices[i] + b_idx = int(idx[0] / beam_width) + # The indices of the replacing position + # according to the replacement strategy noted above + res_k_idx = beam_width - (batch_eos_found[b_idx] % beam_width) - 1 + batch_eos_found[b_idx] += 1 + res_idx = b_idx * beam_width + res_k_idx + + # Replace the old information in return variables + # with the new ended sequence information + t_predecessors[res_idx] = stored_predecessors[t][idx[0]] + current_symbol[res_idx] = stored_emitted_symbols[t][idx[0]] + s[b_idx, res_k_idx] = stored_scores[t][idx[0], 0] + l[b_idx][res_k_idx] = t + 1 + + # record the back tracked results + p.append(current_symbol) + t -= 1 + + # Sort and re-order again as the added ended sequences may change + # the order (very unlikely) + s, re_sorted_idx = s.topk(beam_width) + for b_idx in range(batch_size): + l[b_idx] = [l[b_idx][k_idx.item()] for k_idx in re_sorted_idx[b_idx, :]] + + re_sorted_idx = paddle.reshape( + re_sorted_idx + pos_index.expand_as(re_sorted_idx), + [batch_size * beam_width], + ) + + # Reverse the sequences and re-order at the same time + # It is reversed because the backtracking happens in reverse time order + p = [ + paddle.reshape( + paddle.index_select(step, re_sorted_idx, 0), + shape=[batch_size, beam_width, -1], + ) + for step in reversed(p) + ] + p = paddle.concat(p, -1)[:, 0, :] + return p, paddle.ones_like(p) + + +class AttentionUnit(nn.Layer): + def __init__(self, sDim, xDim, attDim): + super(AttentionUnit, self).__init__() + + self.sDim = sDim + self.xDim = xDim + self.attDim = attDim + + self.sEmbed = nn.Linear(sDim, attDim) + self.xEmbed = nn.Linear(xDim, attDim) + self.wEmbed = nn.Linear(attDim, 1) + + def forward(self, x, sPrev): + batch_size, T, _ = x.shape # [b x T x xDim] + x = paddle.reshape(x, [-1, self.xDim]) # [(b x T) x xDim] + xProj = self.xEmbed(x) # [(b x T) x attDim] + xProj = paddle.reshape(xProj, [batch_size, T, -1]) # [b x T x attDim] + + sPrev = sPrev.squeeze(0) + sProj = self.sEmbed(sPrev) # [b x attDim] + sProj = paddle.unsqueeze(sProj, 1) # [b x 1 x attDim] + sProj = paddle.expand(sProj, [batch_size, T, self.attDim]) # [b x T x attDim] + + sumTanh = paddle.tanh(sProj + xProj) + sumTanh = paddle.reshape(sumTanh, [-1, self.attDim]) + + vProj = self.wEmbed(sumTanh) # [(b x T) x 1] + vProj = paddle.reshape(vProj, [batch_size, T]) + alpha = F.softmax( + vProj, axis=1 + ) # attention weights for each sample in the minibatch + return alpha + + +class DecoderUnit(nn.Layer): + def __init__(self, sDim, xDim, yDim, attDim): + super(DecoderUnit, self).__init__() + self.sDim = sDim + self.xDim = xDim + self.yDim = yDim + self.attDim = attDim + self.emdDim = attDim + + self.attention_unit = AttentionUnit(sDim, xDim, attDim) + self.tgt_embedding = nn.Embedding( + yDim + 1, self.emdDim, weight_attr=nn.initializer.Normal(std=0.01) + ) # the last is used for + self.gru = nn.GRUCell(input_size=xDim + self.emdDim, hidden_size=sDim) + self.fc = nn.Linear( + sDim, + yDim, + weight_attr=nn.initializer.Normal(std=0.01), + bias_attr=nn.initializer.Constant(value=0), + ) + self.embed_fc = nn.Linear(300, self.sDim) + + def get_initial_state(self, embed, tile_times=1): + assert embed.shape[1] == 300 + state = self.embed_fc(embed) # N * sDim + if tile_times != 1: + state = state.unsqueeze(1) + trans_state = paddle.transpose(state, perm=[1, 0, 2]) + state = paddle.tile(trans_state, repeat_times=[tile_times, 1, 1]) + trans_state = paddle.transpose(state, perm=[1, 0, 2]) + state = paddle.reshape(trans_state, shape=[-1, self.sDim]) + state = state.unsqueeze(0) # 1 * N * sDim + return state + + def forward(self, x, sPrev, yPrev): + # x: feature sequence from the image decoder. + batch_size, T, _ = x.shape + alpha = self.attention_unit(x, sPrev) + context = paddle.squeeze(paddle.matmul(alpha.unsqueeze(1), x), axis=1) + yPrev = paddle.cast(yPrev, dtype="int64") + yProj = self.tgt_embedding(yPrev) + + concat_context = paddle.concat([yProj, context], 1) + concat_context = paddle.squeeze(concat_context, 1) + sPrev = paddle.squeeze(sPrev, 0) + output, state = self.gru(concat_context, sPrev) + output = paddle.squeeze(output, axis=1) + output = self.fc(output) + return output, state diff --git a/modules/onnx_ocr_module/src/ppocr/modeling/heads/rec_att_head.py b/modules/onnx_ocr_module/src/ppocr/modeling/heads/rec_att_head.py new file mode 100644 index 0000000..2c952ce --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/modeling/heads/rec_att_head.py @@ -0,0 +1,215 @@ +# copyright (c) 2021 PaddlePaddle Authors. All Rights Reserve. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import paddle +import paddle.nn as nn +import paddle.nn.functional as F +import numpy as np + + +class AttentionHead(nn.Layer): + def __init__(self, in_channels, out_channels, hidden_size, **kwargs): + super(AttentionHead, self).__init__() + self.input_size = in_channels + self.hidden_size = hidden_size + self.num_classes = out_channels + + self.attention_cell = AttentionGRUCell( + in_channels, hidden_size, out_channels, use_gru=False + ) + self.generator = nn.Linear(hidden_size, out_channels) + + def _char_to_onehot(self, input_char, onehot_dim): + input_ont_hot = F.one_hot(input_char, onehot_dim) + return input_ont_hot + + def forward(self, inputs, targets=None, batch_max_length=25): + batch_size = inputs.shape[0] + num_steps = batch_max_length + + hidden = paddle.zeros((batch_size, self.hidden_size)) + output_hiddens = [] + + if targets is not None: + for i in range(num_steps): + char_onehots = self._char_to_onehot( + targets[:, i], onehot_dim=self.num_classes + ) + (outputs, hidden), alpha = self.attention_cell( + hidden, inputs, char_onehots + ) + output_hiddens.append(paddle.unsqueeze(outputs, axis=1)) + output = paddle.concat(output_hiddens, axis=1) + probs = self.generator(output) + else: + targets = paddle.zeros(shape=[batch_size], dtype="int32") + probs = None + char_onehots = None + outputs = None + alpha = None + + for i in range(num_steps): + char_onehots = self._char_to_onehot( + targets, onehot_dim=self.num_classes + ) + (outputs, hidden), alpha = self.attention_cell( + hidden, inputs, char_onehots + ) + probs_step = self.generator(outputs) + if probs is None: + probs = paddle.unsqueeze(probs_step, axis=1) + else: + probs = paddle.concat( + [probs, paddle.unsqueeze(probs_step, axis=1)], axis=1 + ) + next_input = probs_step.argmax(axis=1) + targets = next_input + if not self.training: + probs = paddle.nn.functional.softmax(probs, axis=2) + return probs + + +class AttentionGRUCell(nn.Layer): + def __init__(self, input_size, hidden_size, num_embeddings, use_gru=False): + super(AttentionGRUCell, self).__init__() + self.i2h = nn.Linear(input_size, hidden_size, bias_attr=False) + self.h2h = nn.Linear(hidden_size, hidden_size) + self.score = nn.Linear(hidden_size, 1, bias_attr=False) + + self.rnn = nn.GRUCell( + input_size=input_size + num_embeddings, hidden_size=hidden_size + ) + + self.hidden_size = hidden_size + + def forward(self, prev_hidden, batch_H, char_onehots): + batch_H_proj = self.i2h(batch_H) + prev_hidden_proj = paddle.unsqueeze(self.h2h(prev_hidden), axis=1) + + res = paddle.add(batch_H_proj, prev_hidden_proj) + res = paddle.tanh(res) + e = self.score(res) + + alpha = F.softmax(e, axis=1) + alpha = paddle.transpose(alpha, [0, 2, 1]) + context = paddle.squeeze(paddle.mm(alpha, batch_H), axis=1) + concat_context = paddle.concat([context, char_onehots], 1) + + cur_hidden = self.rnn(concat_context, prev_hidden) + + return cur_hidden, alpha + + +class AttentionLSTM(nn.Layer): + def __init__(self, in_channels, out_channels, hidden_size, **kwargs): + super(AttentionLSTM, self).__init__() + self.input_size = in_channels + self.hidden_size = hidden_size + self.num_classes = out_channels + + self.attention_cell = AttentionLSTMCell( + in_channels, hidden_size, out_channels, use_gru=False + ) + self.generator = nn.Linear(hidden_size, out_channels) + + def _char_to_onehot(self, input_char, onehot_dim): + input_ont_hot = F.one_hot(input_char, onehot_dim) + return input_ont_hot + + def forward(self, inputs, targets=None, batch_max_length=25): + batch_size = inputs.shape[0] + num_steps = batch_max_length + + hidden = ( + paddle.zeros((batch_size, self.hidden_size)), + paddle.zeros((batch_size, self.hidden_size)), + ) + output_hiddens = [] + + if targets is not None: + for i in range(num_steps): + # one-hot vectors for a i-th char + char_onehots = self._char_to_onehot( + targets[:, i], onehot_dim=self.num_classes + ) + hidden, alpha = self.attention_cell(hidden, inputs, char_onehots) + + hidden = (hidden[1][0], hidden[1][1]) + output_hiddens.append(paddle.unsqueeze(hidden[0], axis=1)) + output = paddle.concat(output_hiddens, axis=1) + probs = self.generator(output) + + else: + targets = paddle.zeros(shape=[batch_size], dtype="int32") + probs = None + char_onehots = None + alpha = None + + for i in range(num_steps): + char_onehots = self._char_to_onehot( + targets, onehot_dim=self.num_classes + ) + hidden, alpha = self.attention_cell(hidden, inputs, char_onehots) + probs_step = self.generator(hidden[0]) + hidden = (hidden[1][0], hidden[1][1]) + if probs is None: + probs = paddle.unsqueeze(probs_step, axis=1) + else: + probs = paddle.concat( + [probs, paddle.unsqueeze(probs_step, axis=1)], axis=1 + ) + + next_input = probs_step.argmax(axis=1) + + targets = next_input + if not self.training: + probs = paddle.nn.functional.softmax(probs, axis=2) + return probs + + +class AttentionLSTMCell(nn.Layer): + def __init__(self, input_size, hidden_size, num_embeddings, use_gru=False): + super(AttentionLSTMCell, self).__init__() + self.i2h = nn.Linear(input_size, hidden_size, bias_attr=False) + self.h2h = nn.Linear(hidden_size, hidden_size) + self.score = nn.Linear(hidden_size, 1, bias_attr=False) + if not use_gru: + self.rnn = nn.LSTMCell( + input_size=input_size + num_embeddings, hidden_size=hidden_size + ) + else: + self.rnn = nn.GRUCell( + input_size=input_size + num_embeddings, hidden_size=hidden_size + ) + + self.hidden_size = hidden_size + + def forward(self, prev_hidden, batch_H, char_onehots): + batch_H_proj = self.i2h(batch_H) + prev_hidden_proj = paddle.unsqueeze(self.h2h(prev_hidden[0]), axis=1) + res = paddle.add(batch_H_proj, prev_hidden_proj) + res = paddle.tanh(res) + e = self.score(res) + + alpha = F.softmax(e, axis=1) + alpha = paddle.transpose(alpha, [0, 2, 1]) + context = paddle.squeeze(paddle.mm(alpha, batch_H), axis=1) + concat_context = paddle.concat([context, char_onehots], 1) + cur_hidden = self.rnn(concat_context, prev_hidden) + + return cur_hidden, alpha diff --git a/modules/onnx_ocr_module/src/ppocr/modeling/heads/rec_can_head.py b/modules/onnx_ocr_module/src/ppocr/modeling/heads/rec_can_head.py new file mode 100644 index 0000000..e80951c --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/modeling/heads/rec_can_head.py @@ -0,0 +1,338 @@ +# copyright (c) 2019 PaddlePaddle Authors. All Rights Reserve. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +""" +This code is refer from: +https://github.com/LBH1024/CAN/models/can.py +https://github.com/LBH1024/CAN/models/counting.py +https://github.com/LBH1024/CAN/models/decoder.py +https://github.com/LBH1024/CAN/models/attention.py + +""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import paddle.nn as nn +import paddle +import math + +""" +Counting Module +""" + + +class ChannelAtt(nn.Layer): + def __init__(self, channel, reduction): + super(ChannelAtt, self).__init__() + self.avg_pool = nn.AdaptiveAvgPool2D(1) + + self.fc = nn.Sequential( + nn.Linear(channel, channel // reduction), + nn.ReLU(), + nn.Linear(channel // reduction, channel), + nn.Sigmoid(), + ) + + def forward(self, x): + b, c, _, _ = x.shape + y = paddle.reshape(self.avg_pool(x), [b, c]) + y = paddle.reshape(self.fc(y), [b, c, 1, 1]) + return x * y + + +class CountingDecoder(nn.Layer): + def __init__(self, in_channel, out_channel, kernel_size): + super(CountingDecoder, self).__init__() + self.in_channel = in_channel + self.out_channel = out_channel + + self.trans_layer = nn.Sequential( + nn.Conv2D( + self.in_channel, + 512, + kernel_size=kernel_size, + padding=kernel_size // 2, + bias_attr=False, + ), + nn.BatchNorm2D(512), + ) + + self.channel_att = ChannelAtt(512, 16) + + self.pred_layer = nn.Sequential( + nn.Conv2D(512, self.out_channel, kernel_size=1, bias_attr=False), + nn.Sigmoid(), + ) + + def forward(self, x, mask): + b, _, h, w = x.shape + x = self.trans_layer(x) + x = self.channel_att(x) + x = self.pred_layer(x) + + if mask is not None: + x = x * mask + x = paddle.reshape(x, [b, self.out_channel, -1]) + x1 = paddle.sum(x, axis=-1) + + return x1, paddle.reshape(x, [b, self.out_channel, h, w]) + + +""" +Attention Decoder +""" + + +class PositionEmbeddingSine(nn.Layer): + def __init__( + self, num_pos_feats=64, temperature=10000, normalize=False, scale=None + ): + super().__init__() + self.num_pos_feats = num_pos_feats + self.temperature = temperature + self.normalize = normalize + if scale is not None and normalize is False: + raise ValueError("normalize should be True if scale is passed") + if scale is None: + scale = 2 * math.pi + self.scale = scale + + def forward(self, x, mask): + y_embed = paddle.cumsum(mask, 1, dtype="float32") + x_embed = paddle.cumsum(mask, 2, dtype="float32") + + if self.normalize: + eps = 1e-6 + y_embed = y_embed / (y_embed[:, -1:, :] + eps) * self.scale + x_embed = x_embed / (x_embed[:, :, -1:] + eps) * self.scale + dim_t = paddle.arange(self.num_pos_feats, dtype="float32") + dim_d = paddle.expand(paddle.to_tensor(2), dim_t.shape) + dim_t = self.temperature ** ( + 2 * (dim_t / dim_d).astype("int64") / self.num_pos_feats + ) + + pos_x = paddle.unsqueeze(x_embed, [3]) / dim_t + pos_y = paddle.unsqueeze(y_embed, [3]) / dim_t + + pos_x = paddle.flatten( + paddle.stack( + [paddle.sin(pos_x[:, :, :, 0::2]), paddle.cos(pos_x[:, :, :, 1::2])], + axis=4, + ), + 3, + ) + pos_y = paddle.flatten( + paddle.stack( + [paddle.sin(pos_y[:, :, :, 0::2]), paddle.cos(pos_y[:, :, :, 1::2])], + axis=4, + ), + 3, + ) + + pos = paddle.transpose(paddle.concat([pos_y, pos_x], axis=3), [0, 3, 1, 2]) + + return pos + + +class AttDecoder(nn.Layer): + def __init__( + self, + ratio, + is_train, + input_size, + hidden_size, + encoder_out_channel, + dropout, + dropout_ratio, + word_num, + counting_decoder_out_channel, + attention, + ): + super(AttDecoder, self).__init__() + self.input_size = input_size + self.hidden_size = hidden_size + self.out_channel = encoder_out_channel + self.attention_dim = attention["attention_dim"] + self.dropout_prob = dropout + self.ratio = ratio + self.word_num = word_num + + self.counting_num = counting_decoder_out_channel + self.is_train = is_train + + self.init_weight = nn.Linear(self.out_channel, self.hidden_size) + self.embedding = nn.Embedding(self.word_num, self.input_size) + self.word_input_gru = nn.GRUCell(self.input_size, self.hidden_size) + self.word_attention = Attention(hidden_size, attention["attention_dim"]) + + self.encoder_feature_conv = nn.Conv2D( + self.out_channel, + self.attention_dim, + kernel_size=attention["word_conv_kernel"], + padding=attention["word_conv_kernel"] // 2, + ) + + self.word_state_weight = nn.Linear(self.hidden_size, self.hidden_size) + self.word_embedding_weight = nn.Linear(self.input_size, self.hidden_size) + self.word_context_weight = nn.Linear(self.out_channel, self.hidden_size) + self.counting_context_weight = nn.Linear(self.counting_num, self.hidden_size) + self.word_convert = nn.Linear(self.hidden_size, self.word_num) + + if dropout: + self.dropout = nn.Dropout(dropout_ratio) + + def forward(self, cnn_features, labels, counting_preds, images_mask): + if self.is_train: + _, num_steps = labels.shape + else: + num_steps = 36 + + batch_size, _, height, width = cnn_features.shape + images_mask = images_mask[:, :, :: self.ratio, :: self.ratio] + + word_probs = paddle.zeros((batch_size, num_steps, self.word_num)) + word_alpha_sum = paddle.zeros((batch_size, 1, height, width)) + + hidden = self.init_hidden(cnn_features, images_mask) + counting_context_weighted = self.counting_context_weight(counting_preds) + cnn_features_trans = self.encoder_feature_conv(cnn_features) + + position_embedding = PositionEmbeddingSine(256, normalize=True) + pos = position_embedding(cnn_features_trans, images_mask[:, 0, :, :]) + + cnn_features_trans = cnn_features_trans + pos + + word = paddle.ones([batch_size, 1], dtype="int64") # init word as sos + word = word.squeeze(axis=1) + for i in range(num_steps): + word_embedding = self.embedding(word) + _, hidden = self.word_input_gru(word_embedding, hidden) + word_context_vec, _, word_alpha_sum = self.word_attention( + cnn_features, cnn_features_trans, hidden, word_alpha_sum, images_mask + ) + + current_state = self.word_state_weight(hidden) + word_weighted_embedding = self.word_embedding_weight(word_embedding) + word_context_weighted = self.word_context_weight(word_context_vec) + + if self.dropout_prob: + word_out_state = self.dropout( + current_state + + word_weighted_embedding + + word_context_weighted + + counting_context_weighted + ) + else: + word_out_state = ( + current_state + + word_weighted_embedding + + word_context_weighted + + counting_context_weighted + ) + + word_prob = self.word_convert(word_out_state) + word_probs[:, i] = word_prob + + if self.is_train: + word = labels[:, i] + else: + word = word_prob.argmax(1) + word = paddle.multiply( + word, labels[:, i] + ) # labels are oneslike tensor in infer/predict mode + + return word_probs + + def init_hidden(self, features, feature_mask): + average = paddle.sum( + paddle.sum(features * feature_mask, axis=-1), axis=-1 + ) / paddle.sum((paddle.sum(feature_mask, axis=-1)), axis=-1) + average = self.init_weight(average) + return paddle.tanh(average) + + +""" +Attention Module +""" + + +class Attention(nn.Layer): + def __init__(self, hidden_size, attention_dim): + super(Attention, self).__init__() + self.hidden = hidden_size + self.attention_dim = attention_dim + self.hidden_weight = nn.Linear(self.hidden, self.attention_dim) + self.attention_conv = nn.Conv2D( + 1, 512, kernel_size=11, padding=5, bias_attr=False + ) + self.attention_weight = nn.Linear(512, self.attention_dim, bias_attr=False) + self.alpha_convert = nn.Linear(self.attention_dim, 1) + + def forward( + self, cnn_features, cnn_features_trans, hidden, alpha_sum, image_mask=None + ): + query = self.hidden_weight(hidden) + alpha_sum_trans = self.attention_conv(alpha_sum) + coverage_alpha = self.attention_weight( + paddle.transpose(alpha_sum_trans, [0, 2, 3, 1]) + ) + alpha_score = paddle.tanh( + paddle.unsqueeze(query, [1, 2]) + + coverage_alpha + + paddle.transpose(cnn_features_trans, [0, 2, 3, 1]) + ) + energy = self.alpha_convert(alpha_score) + energy = energy - energy.max() + energy_exp = paddle.exp(paddle.squeeze(energy, -1)) + + if image_mask is not None: + energy_exp = energy_exp * paddle.squeeze(image_mask, 1) + alpha = energy_exp / ( + paddle.unsqueeze(paddle.sum(paddle.sum(energy_exp, -1), -1), [1, 2]) + 1e-10 + ) + alpha_sum = paddle.unsqueeze(alpha, 1) + alpha_sum + context_vector = paddle.sum( + paddle.sum((paddle.unsqueeze(alpha, 1) * cnn_features), -1), -1 + ) + + return context_vector, alpha, alpha_sum + + +class CANHead(nn.Layer): + def __init__(self, in_channel, out_channel, ratio, attdecoder, **kwargs): + super(CANHead, self).__init__() + + self.in_channel = in_channel + self.out_channel = out_channel + + self.counting_decoder1 = CountingDecoder( + self.in_channel, self.out_channel, 3 + ) # mscm + self.counting_decoder2 = CountingDecoder(self.in_channel, self.out_channel, 5) + + self.decoder = AttDecoder(ratio, **attdecoder) + + self.ratio = ratio + + def forward(self, inputs, targets=None): + cnn_features, images_mask, labels = inputs + + counting_mask = images_mask[:, :, :: self.ratio, :: self.ratio] + counting_preds1, _ = self.counting_decoder1(cnn_features, counting_mask) + counting_preds2, _ = self.counting_decoder2(cnn_features, counting_mask) + counting_preds = (counting_preds1 + counting_preds2) / 2 + + word_probs = self.decoder(cnn_features, labels, counting_preds, images_mask) + return word_probs, counting_preds, counting_preds1, counting_preds2 diff --git a/modules/onnx_ocr_module/src/ppocr/modeling/heads/rec_cppd_head.py b/modules/onnx_ocr_module/src/ppocr/modeling/heads/rec_cppd_head.py new file mode 100644 index 0000000..c29946b --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/modeling/heads/rec_cppd_head.py @@ -0,0 +1,387 @@ +# copyright (c) 2023 PaddlePaddle Authors. All Rights Reserve. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +try: + from collections import Callable +except: + from collections.abc import Callable + +import numpy as np +import paddle +from paddle import nn +from paddle.nn import functional as F +from ppocr.modeling.heads.rec_nrtr_head import Embeddings +from ppocr.modeling.backbones.rec_svtrnet import ( + DropPath, + Identity, + trunc_normal_, + zeros_, + ones_, + Mlp, +) + + +class Attention(nn.Layer): + def __init__( + self, + dim, + num_heads=8, + qkv_bias=False, + qk_scale=None, + attn_drop=0.0, + proj_drop=0.0, + ): + super().__init__() + self.num_heads = num_heads + head_dim = dim // num_heads + self.scale = qk_scale or head_dim**-0.5 + + self.q = nn.Linear(dim, dim, bias_attr=qkv_bias) + self.kv = nn.Linear(dim, dim * 2, bias_attr=qkv_bias) + self.attn_drop = nn.Dropout(attn_drop) + self.proj = nn.Linear(dim, dim) + self.proj_drop = nn.Dropout(proj_drop) + + def forward(self, q, kv): + N, C = kv.shape[1:] + QN = q.shape[1] + q = ( + self.q(q) + .reshape([-1, QN, self.num_heads, C // self.num_heads]) + .transpose([0, 2, 1, 3]) + ) + k, v = ( + self.kv(kv) + .reshape([-1, N, 2, self.num_heads, C // self.num_heads]) + .transpose((2, 0, 3, 1, 4)) + ) + attn = q.matmul(k.transpose((0, 1, 3, 2))) * self.scale + attn = F.softmax(attn, axis=-1) + attn = self.attn_drop(attn) + x = (attn.matmul(v)).transpose((0, 2, 1, 3)).reshape((-1, QN, C)) + x = self.proj(x) + x = self.proj_drop(x) + return x + + +class EdgeDecoderLayer(nn.Layer): + def __init__( + self, + dim, + num_heads, + mlp_ratio=4.0, + qkv_bias=False, + qk_scale=None, + drop=0.0, + attn_drop=0.0, + drop_path=[0.0, 0.0], + act_layer=nn.GELU, + norm_layer="nn.LayerNorm", + epsilon=1e-6, + ): + super().__init__() + + self.head_dim = dim // num_heads + self.scale = qk_scale or self.head_dim**-0.5 + + # NOTE: drop path for stochastic depth, we shall see if this is better than dropout here + self.drop_path1 = DropPath(drop_path[0]) if drop_path[0] > 0.0 else Identity() + self.norm1 = eval(norm_layer)(dim, epsilon=epsilon) + self.norm2 = eval(norm_layer)(dim, epsilon=epsilon) + + self.p = nn.Linear(dim, dim) + self.cv = nn.Linear(dim, dim) + self.pv = nn.Linear(dim, dim) + + self.dim = dim + self.num_heads = num_heads + self.p_proj = nn.Linear(dim, dim) + mlp_hidden_dim = int(dim * mlp_ratio) + self.mlp_ratio = mlp_ratio + self.mlp = Mlp( + in_features=dim, + hidden_features=mlp_hidden_dim, + act_layer=act_layer, + drop=drop, + ) + + def forward(self, p, cv, pv): + pN = p.shape[1] + vN = cv.shape[1] + p_shortcut = p + + p1 = ( + self.p(p) + .reshape([-1, pN, self.num_heads, self.dim // self.num_heads]) + .transpose([0, 2, 1, 3]) + ) + cv1 = ( + self.cv(cv) + .reshape([-1, vN, self.num_heads, self.dim // self.num_heads]) + .transpose([0, 2, 1, 3]) + ) + pv1 = ( + self.pv(pv) + .reshape([-1, vN, self.num_heads, self.dim // self.num_heads]) + .transpose([0, 2, 1, 3]) + ) + + edge = F.softmax(p1.matmul(pv1.transpose((0, 1, 3, 2))), -1) # B h N N + p_c = (edge @ cv1).transpose((0, 2, 1, 3)).reshape((-1, pN, self.dim)) + + x1 = self.norm1(p_shortcut + self.drop_path1(self.p_proj(p_c))) + + x = self.norm2(x1 + self.drop_path1(self.mlp(x1))) + return x + + +class DecoderLayer(nn.Layer): + def __init__( + self, + dim, + num_heads, + mlp_ratio=4.0, + qkv_bias=False, + qk_scale=None, + drop=0.0, + attn_drop=0.0, + drop_path=0.0, + act_layer=nn.GELU, + norm_layer="nn.LayerNorm", + epsilon=1e-6, + ): + super().__init__() + if isinstance(norm_layer, str): + self.norm1 = eval(norm_layer)(dim, epsilon=epsilon) + self.normkv = eval(norm_layer)(dim, epsilon=epsilon) + elif isinstance(norm_layer, Callable): + self.norm1 = norm_layer(dim) + self.normkv = norm_layer(dim) + else: + raise TypeError("The norm_layer must be str or paddle.nn.LayerNorm class") + self.mixer = Attention( + dim, + num_heads=num_heads, + qkv_bias=qkv_bias, + qk_scale=qk_scale, + attn_drop=attn_drop, + proj_drop=drop, + ) + + # NOTE: drop path for stochastic depth, we shall see if this is better than dropout here + self.drop_path = DropPath(drop_path) if drop_path > 0.0 else Identity() + if isinstance(norm_layer, str): + self.norm2 = eval(norm_layer)(dim, epsilon=epsilon) + elif isinstance(norm_layer, Callable): + self.norm2 = norm_layer(dim) + else: + raise TypeError("The norm_layer must be str or paddle.nn.layer.Layer class") + mlp_hidden_dim = int(dim * mlp_ratio) + self.mlp_ratio = mlp_ratio + self.mlp = Mlp( + in_features=dim, + hidden_features=mlp_hidden_dim, + act_layer=act_layer, + drop=drop, + ) + + def forward(self, q, kv): + x1 = self.norm1(q + self.drop_path(self.mixer(q, kv))) + x = self.norm2(x1 + self.drop_path(self.mlp(x1))) + return x + + +class CPPDHead(nn.Layer): + def __init__( + self, + in_channels, + dim, + out_channels, + num_layer=2, + drop_path_rate=0.1, + max_len=25, + vis_seq=50, + ch=False, + **kwargs, + ): + super(CPPDHead, self).__init__() + + self.out_channels = out_channels # none + 26 + 10 + self.dim = dim + self.ch = ch + self.max_len = max_len + 1 # max_len + eos + self.char_node_embed = Embeddings( + d_model=dim, vocab=self.out_channels, scale_embedding=True + ) + self.pos_node_embed = Embeddings( + d_model=dim, vocab=self.max_len, scale_embedding=True + ) + dpr = np.linspace(0, drop_path_rate, num_layer + 1) + + self.char_node_decoder = nn.LayerList( + [ + DecoderLayer( + dim=dim, + num_heads=dim // 32, + mlp_ratio=4.0, + qkv_bias=True, + drop_path=dpr[i], + ) + for i in range(num_layer) + ] + ) + self.pos_node_decoder = nn.LayerList( + [ + DecoderLayer( + dim=dim, + num_heads=dim // 32, + mlp_ratio=4.0, + qkv_bias=True, + drop_path=dpr[i], + ) + for i in range(num_layer) + ] + ) + + self.edge_decoder = EdgeDecoderLayer( + dim=dim, + num_heads=dim // 32, + mlp_ratio=4.0, + qkv_bias=True, + drop_path=dpr[num_layer : num_layer + 1], + ) + + self.char_pos_embed = self.create_parameter( + shape=[1, self.max_len, dim], default_initializer=zeros_ + ) + self.add_parameter("char_pos_embed", self.char_pos_embed) + self.vis_pos_embed = self.create_parameter( + shape=[1, vis_seq, dim], default_initializer=zeros_ + ) + self.add_parameter("vis_pos_embed", self.vis_pos_embed) + + self.char_node_fc1 = nn.Linear(dim, max_len) + self.pos_node_fc1 = nn.Linear(dim, self.max_len) + + self.edge_fc = nn.Linear(dim, self.out_channels) + trunc_normal_(self.char_pos_embed) + trunc_normal_(self.vis_pos_embed) + self.apply(self._init_weights) + + def _init_weights(self, m): + if isinstance(m, nn.Linear): + trunc_normal_(m.weight) + if isinstance(m, nn.Linear) and m.bias is not None: + zeros_(m.bias) + elif isinstance(m, nn.LayerNorm): + zeros_(m.bias) + ones_(m.weight) + + def forward(self, x, targets=None, epoch=0): + if self.training: + return self.forward_train(x, targets, epoch) + else: + return self.forward_test(x) + + def forward_test(self, x): + visual_feats = x + self.vis_pos_embed + bs = visual_feats.shape[0] + pos_node_embed = ( + self.pos_node_embed(paddle.arange(self.max_len)).unsqueeze(0) + + self.char_pos_embed + ) + pos_node_embed = paddle.tile(pos_node_embed, [bs, 1, 1]) + char_vis_node_query = visual_feats + pos_vis_node_query = paddle.concat([pos_node_embed, visual_feats], 1) + + for char_decoder_layer, pos_decoder_layer in zip( + self.char_node_decoder, self.pos_node_decoder + ): + char_vis_node_query = char_decoder_layer( + char_vis_node_query, char_vis_node_query + ) + pos_vis_node_query = pos_decoder_layer( + pos_vis_node_query, pos_vis_node_query[:, self.max_len :, :] + ) + pos_node_query = pos_vis_node_query[:, : self.max_len, :] + char_vis_feats = char_vis_node_query + + pos_node_feats = self.edge_decoder( + pos_node_query, char_vis_feats, char_vis_feats + ) # B, 26, dim + edge_feats = self.edge_fc(pos_node_feats) # B, 26, 37 + edge_logits = F.softmax(edge_feats, -1) + + return edge_logits + + def forward_train(self, x, targets=None, epoch=0): + visual_feats = x + self.vis_pos_embed + bs = visual_feats.shape[0] + + if self.ch: + char_node_embed = self.char_node_embed(targets[-2]) + else: + char_node_embed = self.char_node_embed( + paddle.arange(self.out_channels) + ).unsqueeze(0) + char_node_embed = paddle.tile(char_node_embed, [bs, 1, 1]) + counting_char_num = char_node_embed.shape[1] + pos_node_embed = ( + self.pos_node_embed(paddle.arange(self.max_len)).unsqueeze(0) + + self.char_pos_embed + ) + pos_node_embed = paddle.tile(pos_node_embed, [bs, 1, 1]) + + node_feats = [] + + char_vis_node_query = paddle.concat([char_node_embed, visual_feats], 1) + pos_vis_node_query = paddle.concat([pos_node_embed, visual_feats], 1) + + for char_decoder_layer, pos_decoder_layer in zip( + self.char_node_decoder, self.pos_node_decoder + ): + char_vis_node_query = char_decoder_layer( + char_vis_node_query, char_vis_node_query[:, counting_char_num:, :] + ) + pos_vis_node_query = pos_decoder_layer( + pos_vis_node_query, pos_vis_node_query[:, self.max_len :, :] + ) + + char_node_query = char_vis_node_query[:, :counting_char_num, :] + pos_node_query = pos_vis_node_query[:, : self.max_len, :] + + char_vis_feats = char_vis_node_query[:, counting_char_num:, :] + char_node_feats1 = self.char_node_fc1(char_node_query) + + pos_node_feats1 = self.pos_node_fc1(pos_node_query) + diag_mask = ( + paddle.eye(pos_node_feats1.shape[1]) + .unsqueeze(0) + .tile([pos_node_feats1.shape[0], 1, 1]) + ) + pos_node_feats1 = (pos_node_feats1 * diag_mask).sum(-1) + + node_feats.append(char_node_feats1) + node_feats.append(pos_node_feats1) + + pos_node_feats = self.edge_decoder( + pos_node_query, char_vis_feats, char_vis_feats + ) # B, 26, dim + edge_feats = self.edge_fc(pos_node_feats) # B, 26, 37 + + return node_feats, edge_feats diff --git a/modules/onnx_ocr_module/src/ppocr/modeling/heads/rec_ctc_head.py b/modules/onnx_ocr_module/src/ppocr/modeling/heads/rec_ctc_head.py new file mode 100644 index 0000000..5e19a9a --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/modeling/heads/rec_ctc_head.py @@ -0,0 +1,92 @@ +# copyright (c) 2019 PaddlePaddle Authors. All Rights Reserve. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import math + +import paddle +from paddle import ParamAttr, nn +from paddle.nn import functional as F + + +def get_para_bias_attr(l2_decay, k): + regularizer = paddle.regularizer.L2Decay(l2_decay) + stdv = 1.0 / math.sqrt(k * 1.0) + initializer = nn.initializer.Uniform(-stdv, stdv) + weight_attr = ParamAttr(regularizer=regularizer, initializer=initializer) + bias_attr = ParamAttr(regularizer=regularizer, initializer=initializer) + return [weight_attr, bias_attr] + + +class CTCHead(nn.Layer): + def __init__( + self, + in_channels, + out_channels, + fc_decay=0.0004, + mid_channels=None, + return_feats=False, + **kwargs, + ): + super(CTCHead, self).__init__() + if mid_channels is None: + weight_attr, bias_attr = get_para_bias_attr( + l2_decay=fc_decay, k=in_channels + ) + self.fc = nn.Linear( + in_channels, out_channels, weight_attr=weight_attr, bias_attr=bias_attr + ) + else: + weight_attr1, bias_attr1 = get_para_bias_attr( + l2_decay=fc_decay, k=in_channels + ) + self.fc1 = nn.Linear( + in_channels, + mid_channels, + weight_attr=weight_attr1, + bias_attr=bias_attr1, + ) + + weight_attr2, bias_attr2 = get_para_bias_attr( + l2_decay=fc_decay, k=mid_channels + ) + self.fc2 = nn.Linear( + mid_channels, + out_channels, + weight_attr=weight_attr2, + bias_attr=bias_attr2, + ) + self.out_channels = out_channels + self.mid_channels = mid_channels + self.return_feats = return_feats + + def forward(self, x, targets=None): + if self.mid_channels is None: + predicts = self.fc(x) + else: + x = self.fc1(x) + predicts = self.fc2(x) + + if self.return_feats: + result = (x, predicts) + else: + result = predicts + if not self.training: + predicts = F.softmax(predicts, axis=2) + result = predicts + + return result diff --git a/modules/onnx_ocr_module/src/ppocr/modeling/heads/rec_latexocr_head.py b/modules/onnx_ocr_module/src/ppocr/modeling/heads/rec_latexocr_head.py new file mode 100644 index 0000000..1b01d9d --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/modeling/heads/rec_latexocr_head.py @@ -0,0 +1,1030 @@ +# copyright (c) 2024 PaddlePaddle Authors. All Rights Reserve. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +""" +This code is refer from: +https://github.com/lukas-blecher/LaTeX-OCR/blob/main/pix2tex/models/transformer.py +""" + +import math +import paddle +from paddle import nn, einsum +import paddle.nn.functional as F +from functools import partial +from inspect import isfunction +from collections import namedtuple + +from paddle.nn.initializer import ( + TruncatedNormal, + Constant, + Normal, + KaimingUniform, + XavierUniform, +) + +zeros_ = Constant(value=0.0) +ones_ = Constant(value=1.0) +normal_ = Normal(std=0.02) +DEFAULT_DIM_HEAD = 64 + +Intermediates = namedtuple("Intermediates", ["pre_softmax_attn", "post_softmax_attn"]) + +LayerIntermediates = namedtuple("Intermediates", ["hiddens", "attn_intermediates"]) + +# helpers + + +def exists(val): + return val is not None + + +def default(val, d): + if exists(val): + return val + return d() if isfunction(d) else d + + +class always: + def __init__(self, val): + self.val = val + + def __call__(self, *args, **kwargs): + return self.val + + +class not_equals: + def __init__(self, val): + self.val = val + + def __call__(self, x, *args, **kwargs): + return x != self.val + + +class equals: + def __init__(self, val): + self.val = val + + def __call__(self, x, *args, **kwargs): + return x == self.val + + +def max_neg_value(tensor): + return -paddle.finfo(tensor.dtype).max + + +def pick_and_pop(keys, d): + values = list(map(lambda key: d.pop(key), keys)) + return dict(zip(keys, values)) + + +def group_dict_by_key(cond, d): + return_val = [dict(), dict()] + for key in d.keys(): + match = bool(cond(key)) + ind = int(not match) + return_val[ind][key] = d[key] + return (*return_val,) + + +def string_begins_with(prefix, str): + return str.startswith(prefix) + + +def group_by_key_prefix(prefix, d): + return group_dict_by_key(partial(string_begins_with, prefix), d) + + +def groupby_prefix_and_trim(prefix, d): + kwargs_with_prefix, kwargs = group_dict_by_key( + partial(string_begins_with, prefix), d + ) + kwargs_without_prefix = dict( + map(lambda x: (x[0][len(prefix) :], x[1]), tuple(kwargs_with_prefix.items())) + ) + return kwargs_without_prefix, kwargs + + +# positional embeddings + + +class DepthWiseConv1d(nn.Layer): + def __init__( + self, dim_in, dim_out, kernel_size, padding=0, stride=1, bias=True, groups=False + ): + super().__init__() + groups = default(groups, dim_in) + self.net = nn.Sequential( + nn.Conv1D( + dim_in, + dim_in, + kernel_size=kernel_size, + padding=padding, + groups=dim_in, + stride=stride, + bias_attr=bias, + ), + nn.Conv1D(dim_in, dim_out, 1), + ) + + def forward(self, x): + return self.net(x) + + +class AbsolutePositionalEmbedding(nn.Layer): + def __init__(self, dim, max_seq_len): + super().__init__() + self.emb = nn.Embedding(max_seq_len, dim) + self.init_() + + def init_(self): + + normal_(self.emb.weight) + + def forward(self, x): + n = paddle.arange(x.shape[1]) + return self.emb(n)[None, :, :] + + +class FixedPositionalEmbedding(nn.Layer): + def __init__(self, dim): + super().__init__() + inv_freq = 1.0 / (10000 ** (paddle.arange(0, dim, 2).float() / dim)) + self.register_buffer("inv_freq", inv_freq) + + def forward(self, x, seq_dim=1, offset=0): + t = ( + paddle.arange( + x.shape[seq_dim], + ).type_as(self.inv_freq) + + offset + ) + sinusoid_inp = paddle.einsum("i , j -> i j", t, self.inv_freq) + emb = paddle.concat((sinusoid_inp.sin(), sinusoid_inp.cos()), axis=-1) + return emb[None, :, :] + + +class Scale(nn.Layer): + def __init__(self, value, fn): + super().__init__() + self.value = value + self.fn = fn + + def forward(self, x, **kwargs): + x, *rest = self.fn(x, **kwargs) + return (x * self.value, *rest) + + +class Rezero(nn.Layer): + def __init__(self, fn): + super().__init__() + self.fn = fn + self.g = paddle.create_parameter([1], dtype="float32") + zeros_(self.g) + + def forward(self, x, **kwargs): + x, *rest = self.fn(x, **kwargs) + return (x * self.g, *rest) + + +class ScaleNorm(nn.Layer): + def __init__(self, dim, eps=1e-5): + super().__init__() + self.scale = dim**-0.5 + self.eps = eps + self.g = paddle.create_parameter([1], dtype="float32") + ones_(self.g) + + def forward(self, x): + norm = paddle.norm(x, axis=-1, keepdim=True) * self.scale + return x / norm.clamp(min=self.eps) * self.g + + +class RMSNorm(nn.Layer): + def __init__(self, dim, eps=1e-8): + super().__init__() + self.scale = dim**-0.5 + self.eps = eps + self.g = paddle.create_parameter([dim]) + ones_(self.g) + + def forward(self, x): + norm = paddle.norm(x, axis=-1, keepdim=True) * self.scale + return x / norm.clamp(min=self.eps) * self.g + + +class Residual(nn.Layer): + def forward(self, x, residual): + return x + residual + + +class GEGLU(nn.Layer): + def __init__(self, dim_in, dim_out): + super().__init__() + self.proj = nn.Linear(dim_in, dim_out * 2) + + def forward(self, x): + x, gate = self.proj(x).chunk(2, axis=-1) + return x * F.gelu(gate) + + +class FeedForward(nn.Layer): + def __init__(self, dim, dim_out=None, mult=4, glu=False, dropout=0.0): + super().__init__() + inner_dim = int(dim * mult) + dim_out = default(dim_out, dim) + project_in = ( + nn.Sequential(nn.Linear(dim, inner_dim), nn.GELU()) + if not glu + else GEGLU(dim, inner_dim) + ) + + self.net = nn.Sequential( + project_in, nn.Dropout(dropout), nn.Linear(inner_dim, dim_out) + ) + + def forward(self, x): + return self.net(x) + + +class Attention(nn.Layer): + def __init__( + self, + dim, + dim_head=DEFAULT_DIM_HEAD, + heads=8, + causal=False, + mask=None, + talking_heads=False, + collab_heads=False, + collab_compression=0.3, + sparse_topk=None, + use_entmax15=False, + num_mem_kv=0, + dropout=0.0, + on_attn=False, + gate_values=False, + is_export=False, + ): + super().__init__() + self.scale = dim_head**-0.5 + self.heads = heads + self.causal = causal + self.mask = mask + self.is_export = is_export + + qk_dim = v_dim = dim_head * heads + + # collaborative heads + self.collab_heads = collab_heads + if self.collab_heads: + qk_dim = int(collab_compression * qk_dim) + self.collab_mixing = nn.Parameter(paddle.randn(heads, qk_dim)) + + self.to_q = nn.Linear(dim, qk_dim, bias_attr=False) + self.to_k = nn.Linear(dim, qk_dim, bias_attr=False) + self.to_v = nn.Linear(dim, v_dim, bias_attr=False) + + self.dropout = nn.Dropout(dropout) + + # add GLU gating for aggregated values, from alphafold2 + self.to_v_gate = None + if gate_values: + self.to_v_gate = nn.Linear(dim, v_dim) + zeros_(self.to_v_gate.weight) + ones_(self.to_v_gate.bias) + + # talking heads + self.talking_heads = talking_heads + if talking_heads: + self.pre_softmax_proj = nn.Parameter(paddle.randn(heads, heads)) + self.post_softmax_proj = nn.Parameter(paddle.randn(heads, heads)) + + # explicit topk sparse attention + self.sparse_topk = sparse_topk + + self.attn_fn = F.softmax + + # add memory key / values + self.num_mem_kv = num_mem_kv + if num_mem_kv > 0: + self.mem_k = nn.Parameter(paddle.randn(heads, num_mem_kv, dim_head)) + self.mem_v = nn.Parameter(paddle.randn(heads, num_mem_kv, dim_head)) + + # attention on attention + self.attn_on_attn = on_attn + self.to_out = ( + nn.Sequential(nn.Linear(v_dim, dim * 2), nn.GLU()) + if on_attn + else nn.Linear(v_dim, dim) + ) + + def forward( + self, + x, + context=None, + mask=None, + context_mask=None, + rel_pos=None, + sinusoidal_emb=None, + rotary_pos_emb=None, + prev_attn=None, + mem=None, + seq_len=0, + ): + if not self.training: + self.is_export = True + b, n, _, h, talking_heads, collab_heads, has_context = ( + *x.shape, + self.heads, + self.talking_heads, + self.collab_heads, + exists(context), + ) + kv_input = default(context, x) + + q_input = x + k_input = kv_input + v_input = kv_input + + if exists(mem): + k_input = paddle.concat((mem, k_input), axis=-2) + v_input = paddle.concat((mem, v_input), axis=-2) + + if exists(sinusoidal_emb): + # in shortformer, the query would start at a position offset depending on the past cached memory + offset = k_input.shape[-2] - q_input.shape[-2] + q_input = q_input + sinusoidal_emb(q_input, offset=offset) + k_input = k_input + sinusoidal_emb(k_input) + q = self.to_q(q_input) + k = self.to_k(k_input) + v = self.to_v(v_input) + + def rearrange_q_k_v(x, h, is_export): + if is_export: + b, n, h_d = paddle.shape(x) + else: + b, n, h_d = x.shape + d = h_d // h + return x.reshape([b, n, h, d]).transpose([0, 2, 1, 3]) + + q, k, v = map( + lambda t: rearrange_q_k_v(t, h, is_export=self.is_export), (q, k, v) + ) + + input_mask = None + if any(map(exists, (mask, context_mask))): + q_mask = default( + mask, + lambda: paddle.ones( + (b, n), + ).cast(paddle.bool), + ) + k_mask = q_mask if not exists(context) else context_mask + k_mask = default( + k_mask, lambda: paddle.ones((b, k.shape[-2])).cast(paddle.bool) + ) + + q_mask = q_mask.reshape([q_mask.shape[0], 1, q_mask.shape[1], 1]) + k_mask = k_mask.reshape([k_mask.shape[0], 1, 1, k_mask.shape[1]]) + input_mask = q_mask * k_mask + + if collab_heads: + k = k.expand(-1, h, -1, -1) + dots = einsum("b h i d, b h j d -> b h i j", q, k) * self.scale + + mask_value = max_neg_value(dots) + + if exists(prev_attn): + dots = dots + prev_attn + + pre_softmax_attn = dots.clone() + + if talking_heads: + dots = einsum( + "b h i j, h k -> b k i j", dots, self.pre_softmax_proj + ).contiguous() + + if exists(rel_pos): + dots = rel_pos(dots) + + input_mask = input_mask.cast(paddle.bool) + if exists(input_mask): + + dots.masked_fill_(~input_mask, mask_value) + del input_mask + + if self.causal: + i, j = dots.shape[-2:] + r = paddle.arange(i) + r_shape = r.shape[0] + mask = r.reshape([1, 1, r_shape, 1]) < r.reshape([1, 1, 1, r_shape]) + + if self.is_export: + pad_list = [ + paddle.to_tensor(0, dtype="int32"), + paddle.to_tensor(0, dtype="int32"), + paddle.to_tensor(j - i, dtype="int32"), + paddle.to_tensor(0, dtype="int32"), + ] + mask = F.pad( + mask.cast(paddle.int32), + paddle.to_tensor(pad_list).cast(paddle.int32), + value=False, + ).cast(paddle.bool) + dots = dots.masked_fill_(mask, mask_value) + else: + mask = F.pad(mask.cast(paddle.int32), (0, 0, j - i, 0), value=False) + dots.masked_fill_(mask, mask_value) + del mask + if exists(self.sparse_topk) and self.sparse_topk < dots.shape[-1]: + top, _ = dots.topk(self.sparse_topk, dim=-1) + vk = top[..., -1].unsqueeze(-1).expand_as(dots) + mask = dots < vk + dots.masked_fill_(mask, mask_value) + del mask + + attn = self.attn_fn(dots, axis=-1) + post_softmax_attn = attn.clone() + + attn = self.dropout(attn) + + if talking_heads: + attn = einsum( + "b h i j, h k -> b k i j", attn, self.post_softmax_proj + ).contiguous() + out = einsum("b h i j, b h j d -> b h i d", attn, v) + + b, h, n, d = out.shape + out = out.transpose([0, 2, 1, 3]).reshape([b, n, h * d]) + + if exists(self.to_v_gate): + gates = self.gate_v(x) + out = out * gates.sigmoid() + + intermediates = Intermediates( + pre_softmax_attn=pre_softmax_attn, post_softmax_attn=post_softmax_attn + ) + + return self.to_out(out), intermediates + + +class AttentionLayers(nn.Layer): + def __init__( + self, + dim, + depth, + heads=8, + causal=False, + cross_attend=False, + only_cross=False, + use_scalenorm=False, + use_rmsnorm=False, + use_rezero=False, + rel_pos_bias=False, + rel_pos_num_buckets=32, + rel_pos_max_distance=128, + position_infused_attn=False, + rotary_pos_emb=False, + rotary_emb_dim=None, + custom_layers=None, + sandwich_coef=None, + par_ratio=None, + residual_attn=False, + cross_residual_attn=False, + macaron=False, + pre_norm=True, + gate_residual=False, + is_export=False, + **kwargs, + ): + super().__init__() + ff_kwargs, kwargs = groupby_prefix_and_trim("ff_", kwargs) + attn_kwargs, _ = groupby_prefix_and_trim("attn_", kwargs) + + dim_head = attn_kwargs.get("dim_head", DEFAULT_DIM_HEAD) + + self.dim = dim + self.depth = depth + self.layers = nn.LayerList([]) + + self.has_pos_emb = position_infused_attn or rel_pos_bias or rotary_pos_emb + self.pia_pos_emb = ( + FixedPositionalEmbedding(dim) if position_infused_attn else None + ) + + assert ( + rel_pos_num_buckets <= rel_pos_max_distance + ), "number of relative position buckets must be less than the relative position max distance" + + self.pre_norm = pre_norm + + self.residual_attn = residual_attn + self.cross_residual_attn = cross_residual_attn + self.cross_attend = cross_attend + self.rel_pos = None + + norm_class = ScaleNorm if use_scalenorm else nn.LayerNorm + norm_class = RMSNorm if use_rmsnorm else norm_class + norm_fn = partial(norm_class, dim) + + norm_fn = nn.Identity if use_rezero else norm_fn + branch_fn = Rezero if use_rezero else None + + if cross_attend and not only_cross: + default_block = ("a", "c", "f") + elif cross_attend and only_cross: + default_block = ("c", "f") + else: + default_block = ("a", "f") + if macaron: + default_block = ("f",) + default_block + + if exists(custom_layers): + layer_types = custom_layers + elif exists(par_ratio): + par_depth = depth * len(default_block) + assert 1 < par_ratio <= par_depth, "par ratio out of range" + default_block = tuple(filter(not_equals("f"), default_block)) + par_attn = par_depth // par_ratio + depth_cut = ( + par_depth * 2 // 3 + ) # 2 / 3 attention layer cutoff suggested by PAR paper + par_width = (depth_cut + depth_cut // par_attn) // par_attn + assert ( + len(default_block) <= par_width + ), "default block is too large for par_ratio" + par_block = default_block + ("f",) * (par_width - len(default_block)) + par_head = par_block * par_attn + layer_types = par_head + ("f",) * (par_depth - len(par_head)) + elif exists(sandwich_coef): + assert ( + sandwich_coef > 0 and sandwich_coef <= depth + ), "sandwich coefficient should be less than the depth" + layer_types = ( + ("a",) * sandwich_coef + + default_block * (depth - sandwich_coef) + + ("f",) * sandwich_coef + ) + else: + layer_types = default_block * depth + + self.layer_types = layer_types + self.num_attn_layers = len(list(filter(equals("a"), layer_types))) + for layer_type in self.layer_types: + if layer_type == "a": + layer = Attention( + dim, heads=heads, causal=causal, is_export=is_export, **attn_kwargs + ) + elif layer_type == "c": + layer = Attention(dim, heads=heads, is_export=is_export, **attn_kwargs) + elif layer_type == "f": + layer = FeedForward(dim, **ff_kwargs) + layer = layer if not macaron else Scale(0.5, layer) + else: + raise Exception(f"invalid layer type {layer_type}") + if isinstance(layer, Attention) and exists(branch_fn): + layer = branch_fn(layer) + residual_fn = Residual() + self.layers.append(nn.LayerList([norm_fn(), layer, residual_fn])) + + def forward( + self, + x, + context=None, + mask=None, + context_mask=None, + mems=None, + seq_len=0, + return_hiddens=False, + ): + assert not ( + self.cross_attend ^ exists(context) + ), "context must be passed in if cross_attend is set to True" + + hiddens = [] + intermediates = [] + prev_attn = None + prev_cross_attn = None + rotary_pos_emb = None + + mems = mems.copy() if exists(mems) else [None] * self.num_attn_layers + + for ind, (layer_type, (norm, block, residual_fn)) in enumerate( + zip(self.layer_types, self.layers) + ): + is_last = ind == (len(self.layers) - 1) + + if layer_type == "a": + hiddens.append(x) + layer_mem = mems.pop(0) + + residual = x + + if self.pre_norm: + x = norm(x) + + if layer_type == "a": + out, inter = block( + x, + mask=mask, + sinusoidal_emb=self.pia_pos_emb, + rel_pos=self.rel_pos, + rotary_pos_emb=rotary_pos_emb, + prev_attn=prev_attn, + mem=layer_mem, + ) + elif layer_type == "c": + out, inter = block( + x, + context=context, + mask=mask, + context_mask=context_mask, + prev_attn=prev_cross_attn, + ) + elif layer_type == "f": + out = block(x) + + x = residual_fn(out, residual) + + if layer_type in ("a", "c"): + intermediates.append(inter) + + if layer_type == "a" and self.residual_attn: + prev_attn = inter.pre_softmax_attn + elif layer_type == "c" and self.cross_residual_attn: + prev_cross_attn = inter.pre_softmax_attn + + if not self.pre_norm and not is_last: + x = norm(x) + + if return_hiddens: + intermediates = LayerIntermediates( + hiddens=hiddens, attn_intermediates=intermediates + ) + + return x, intermediates + + return x + + +class Encoder(AttentionLayers): + def __init__(self, **kwargs): + assert "causal" not in kwargs, "cannot set causality on encoder" + super().__init__(causal=False, **kwargs) + + +class Decoder(AttentionLayers): + def __init__(self, **kwargs): + assert "causal" not in kwargs, "cannot set causality on decoder" + super().__init__(causal=True, **kwargs) + + +class CrossAttender(AttentionLayers): + def __init__(self, **kwargs): + super().__init__(cross_attend=True, only_cross=True, **kwargs) + + +def create_latex_parameter(shape): + return paddle.create_parameter( + shape=shape, + dtype="float32", + default_initializer=paddle.nn.initializer.Assign(paddle.randn(shape)), + ) + + +class TransformerDecoder(nn.Layer): + def __init__( + self, + *, + num_tokens, + max_seq_len, + attn_layers, + emb_dim=None, + max_mem_len=0.0, + emb_dropout=0.0, + num_memory_tokens=None, + tie_embedding=False, + use_pos_emb=True, + is_export=False, + ): + super().__init__() + assert isinstance( + attn_layers, AttentionLayers + ), "attention layers must be one of Encoder or Decoder" + + dim = attn_layers.dim + emb_dim = default(emb_dim, dim) + + self.max_seq_len = max_seq_len + self.max_mem_len = max_mem_len + + self.token_emb = nn.Embedding(num_tokens, emb_dim) + self.pos_emb = ( + AbsolutePositionalEmbedding(emb_dim, max_seq_len) + if (use_pos_emb and not attn_layers.has_pos_emb) + else always(0) + ) + self.emb_dropout = nn.Dropout(emb_dropout) + + self.project_emb = nn.Linear(emb_dim, dim) if emb_dim != dim else nn.Identity() + self.attn_layers = attn_layers + self.norm = nn.LayerNorm(dim) + self.is_export = is_export + + self.init_() + + self.to_logits = ( + nn.Linear(dim, num_tokens) + if not tie_embedding + else lambda t: t @ self.token_emb.weight.t() + ) + + # memory tokens (like [cls]) from Memory Transformers paper + num_memory_tokens = default(num_memory_tokens, 0) + self.num_memory_tokens = num_memory_tokens + if num_memory_tokens > 0: + self.memory_tokens = create_latex_parameter([num_memory_tokens, dim]) + + # let funnel encoder know number of memory tokens, if specified + # TODO: think of a cleaner solution + if hasattr(attn_layers, "num_memory_tokens"): + attn_layers.num_memory_tokens = num_memory_tokens + + def init_(self): + normal_(self.token_emb.weight) + + def forward( + self, + x, + return_embeddings=False, + mask=None, + return_mems=False, + return_attn=False, + seq_len=0, + mems=None, + **kwargs, + ): + b, n, num_mem = *x.shape, self.num_memory_tokens + x = self.token_emb(x) + x = x + self.pos_emb(x) + + x = self.emb_dropout(x) + x = self.project_emb(x) + + x, intermediates = self.attn_layers( + x, mask=mask, mems=mems, return_hiddens=True, seq_len=seq_len, **kwargs + ) + x = self.norm(x) + if paddle.device.get_device().startswith("npu"): + x = x[:, num_mem:] + else: + mem, x = x[:, :num_mem], x[:, num_mem:] + out = self.to_logits(x) if not return_embeddings else x + if return_mems: + hiddens = intermediates.hiddens + new_mems = ( + list(map(lambda pair: paddle.concat(pair, axis=-2), zip(mems, hiddens))) + if exists(mems) + else hiddens + ) + new_mems = list( + map(lambda t: t[..., -self.max_mem_len :, :].detach(), new_mems) + ) + return out, new_mems + + if return_attn: + attn_maps = list( + map(lambda t: t.post_softmax_attn, intermediates.attn_intermediates) + ) + return out, attn_maps + + return out + + +def top_p(logits, thres=0.9): + sorted_logits, sorted_indices = paddle.sort(logits, descending=True) + cum_probs = paddle.cumsum(F.softmax(sorted_logits, axis=-1), axis=-1) + + sorted_indices_to_remove = cum_probs > (1 - thres) + sorted_indices_to_remove[:, 1:] = sorted_indices_to_remove[:, :-1].clone() + sorted_indices_to_remove[:, 0] = 0 + + sorted_logits[sorted_indices_to_remove] = float("-inf") + return sorted_logits.scatter(1, sorted_indices, sorted_logits) + + +# topk + + +def top_k(logits, thres=0.9): + k = int((1 - thres) * logits.shape[-1]) + val, ind = paddle.topk(logits, k) + probs = paddle.full_like(logits, float("-inf")) + probs = paddle.put_along_axis(probs, ind, val, 1) + return probs + + +class LaTeXOCRHead(nn.Layer): + """Implementation of LaTeX OCR decoder. + + Args: + encoded_feat: The encoded features with shape[N, 1, H//16, W//16] + tgt_seq: LaTeX-OCR labels with shape [N, L] , L is the max sequence length + xi: The first N-1 LaTeX-OCR sequences in tgt_seq with shape [N, L-1] + mask: The first N-1 LaTeX-OCR attention mask with shape [N, L-1] , L is the max sequence length + + Returns: + The predicted LaTeX sequences with shape [N, L-1, C], C is the number of LaTeX classes + """ + + def __init__( + self, + net=None, + in_channels=256, + out_channels=256, + pad_value=0, + decoder_args=None, + is_export=False, + ): + super().__init__() + decoder = Decoder( + dim=256, depth=4, heads=8, is_export=is_export, **decoder_args + ) + transformer_decoder = TransformerDecoder( + num_tokens=8000, + max_seq_len=512, + attn_layers=decoder, + is_export=is_export, + ) + self.temperature = 0.333 + self.bos_token = 1 + self.eos_token = 2 + self.max_length = 512 + self.pad_value = pad_value + + self.net = transformer_decoder + self.max_seq_len = self.net.max_seq_len + self.is_export = is_export + + @paddle.no_grad() + def generate( + self, + start_tokens, + seq_len, + eos_token=None, + temperature=1.0, + filter_logits_fn=top_k, + filter_thres=0.9, + **kwargs, + ): + was_training = self.net.training + num_dims = len(start_tokens.shape) + + if num_dims == 1: + start_tokens = start_tokens[None, :] + + b, t = start_tokens.shape + + self.net.eval() + out = start_tokens + mask = kwargs.pop("mask", None) + + if mask is None: + mask = paddle.full_like(out, True, dtype=paddle.bool) + + for _ in range(seq_len): + x = out[:, -self.max_seq_len :] + mask = mask[:, -self.max_seq_len :] + logits = self.net(x, mask=mask, **kwargs)[:, -1, :] + if filter_logits_fn in {top_k, top_p}: + filtered_logits = filter_logits_fn(logits, thres=filter_thres) + + probs = F.softmax(filtered_logits / temperature, axis=-1) + else: + raise NotImplementedError("The filter_logits_fn is not supported ") + + sample = paddle.multinomial(probs, 1) + out = paddle.concat((out, sample), axis=-1) + pad_mask = paddle.full(shape=[mask.shape[0], 1], fill_value=1, dtype="bool") + mask = paddle.concat((mask, pad_mask), axis=1) + if ( + eos_token is not None + and ( + paddle.cumsum((out == eos_token).cast(paddle.int64), 1)[:, -1] >= 1 + ).all() + ): + break + out = out[:, t:] + if num_dims == 1: + out = out.squeeze(0) + return out + + @paddle.no_grad() + def generate_export( + self, + start_tokens, + seq_len, + eos_token=None, + context=None, + temperature=1.0, + filter_logits_fn=None, + filter_thres=0.9, + **kwargs, + ): + was_training = self.net.training + num_dims = len(start_tokens.shape) + + if num_dims == 1: + start_tokens = start_tokens[None, :] + + b, t = start_tokens.shape + + self.net.eval() + out = start_tokens + mask = kwargs.pop("mask", None) + + if mask is None: + mask = paddle.full_like(out, True, dtype=paddle.bool) + + i_idx = paddle.full([], 0) + while i_idx < paddle.to_tensor(seq_len): + x = out[:, -self.max_seq_len :] + paddle.jit.api.set_dynamic_shape(x, [-1, -1]) + mask = mask[:, -self.max_seq_len :] + paddle.jit.api.set_dynamic_shape(mask, [-1, -1]) + logits = self.net(x, mask=mask, context=context, seq_len=i_idx, **kwargs)[ + :, -1, : + ] + if filter_logits_fn in {top_k, top_p}: + filtered_logits = filter_logits_fn(logits, thres=filter_thres) + + probs = F.softmax(filtered_logits / temperature, axis=-1) + + sample = paddle.multinomial(probs, 1) + out = paddle.concat((out, sample), axis=-1) + + pad_mask = paddle.full(shape=[mask.shape[0], 1], fill_value=1, dtype="bool") + mask = paddle.concat((mask, pad_mask), axis=1) + if ( + eos_token is not None + and ( + paddle.cumsum((out == eos_token).cast(paddle.int64), 1)[:, -1] >= 1 + ).all() + ): + break + i_idx += 1 + out = out[:, t:] + if num_dims == 1: + out = out.squeeze(0) + return out + + # forward for export + def forward(self, inputs, targets=None): + if not self.training: + self.is_export = True + encoded_feat = inputs + batch_num = encoded_feat.shape[0] + bos_tensor = paddle.full([batch_num, 1], self.bos_token, dtype=paddle.int64) + if self.is_export: + word_pred = self.generate_export( + bos_tensor, + self.max_seq_len, + eos_token=self.eos_token, + context=encoded_feat, + temperature=self.temperature, + filter_logits_fn=top_k, + ) + else: + word_pred = self.generate( + bos_tensor, + self.max_seq_len, + eos_token=self.eos_token, + context=encoded_feat, + temperature=self.temperature, + filter_logits_fn=top_k, + ) + return word_pred + + encoded_feat, tgt_seq, mask = inputs + kwargs = {"context": encoded_feat, "mask": mask.cast(paddle.bool)} + x = tgt_seq + xi = x[:, :-1] + + mask = kwargs.get("mask", None) + if mask is not None and mask.shape[1] == x.shape[1]: + mask = mask[:, :-1] + kwargs["mask"] = mask + out = self.net(xi, **kwargs) + + return out diff --git a/modules/onnx_ocr_module/src/ppocr/modeling/heads/rec_multi_head.py b/modules/onnx_ocr_module/src/ppocr/modeling/heads/rec_multi_head.py new file mode 100644 index 0000000..be44615 --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/modeling/heads/rec_multi_head.py @@ -0,0 +1,153 @@ +# copyright (c) 2022 PaddlePaddle Authors. All Rights Reserve. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import math +import paddle +from paddle import ParamAttr +import paddle.nn as nn +import paddle.nn.functional as F + +from ppocr.modeling.necks.rnn import ( + Im2Seq, + EncoderWithRNN, + EncoderWithFC, + SequenceEncoder, + EncoderWithSVTR, + trunc_normal_, + zeros_, +) +from .rec_ctc_head import CTCHead +from .rec_sar_head import SARHead +from .rec_nrtr_head import Transformer + + +class FCTranspose(nn.Layer): + def __init__(self, in_channels, out_channels, only_transpose=False): + super().__init__() + self.only_transpose = only_transpose + if not self.only_transpose: + self.fc = nn.Linear(in_channels, out_channels, bias_attr=False) + + def forward(self, x): + if self.only_transpose: + return x.transpose([0, 2, 1]) + else: + return self.fc(x.transpose([0, 2, 1])) + + +class AddPos(nn.Layer): + def __init__(self, dim, w): + super().__init__() + self.dec_pos_embed = self.create_parameter( + shape=[1, w, dim], default_initializer=zeros_ + ) + self.add_parameter("dec_pos_embed", self.dec_pos_embed) + trunc_normal_(self.dec_pos_embed) + + def forward(self, x): + x = x + self.dec_pos_embed[:, : x.shape[1], :] + return x + + +class MultiHead(nn.Layer): + def __init__(self, in_channels, out_channels_list, **kwargs): + super().__init__() + self.head_list = kwargs.pop("head_list") + self.use_pool = kwargs.get("use_pool", False) + self.use_pos = kwargs.get("use_pos", False) + self.in_channels = in_channels + if self.use_pool: + self.pool = nn.AvgPool2D(kernel_size=[3, 2], stride=[3, 2], padding=0) + self.gtc_head = "sar" + assert len(self.head_list) >= 2 + for idx, head_name in enumerate(self.head_list): + name = list(head_name)[0] + if name == "SARHead": + # sar head + sar_args = self.head_list[idx][name] + self.sar_head = eval(name)( + in_channels=in_channels, + out_channels=out_channels_list["SARLabelDecode"], + **sar_args, + ) + elif name == "NRTRHead": + gtc_args = self.head_list[idx][name] + max_text_length = gtc_args.get("max_text_length", 25) + nrtr_dim = gtc_args.get("nrtr_dim", 256) + num_decoder_layers = gtc_args.get("num_decoder_layers", 4) + if self.use_pos: + self.before_gtc = nn.Sequential( + nn.Flatten(2), + FCTranspose(in_channels, nrtr_dim), + AddPos(nrtr_dim, 80), + ) + else: + self.before_gtc = nn.Sequential( + nn.Flatten(2), FCTranspose(in_channels, nrtr_dim) + ) + + self.gtc_head = Transformer( + d_model=nrtr_dim, + nhead=nrtr_dim // 32, + num_encoder_layers=-1, + beam_size=-1, + num_decoder_layers=num_decoder_layers, + max_len=max_text_length, + dim_feedforward=nrtr_dim * 4, + out_channels=out_channels_list["NRTRLabelDecode"], + ) + elif name == "CTCHead": + # ctc neck + self.encoder_reshape = Im2Seq(in_channels) + neck_args = self.head_list[idx][name]["Neck"] + encoder_type = neck_args.pop("name") + self.ctc_encoder = SequenceEncoder( + in_channels=in_channels, encoder_type=encoder_type, **neck_args + ) + # ctc head + head_args = self.head_list[idx][name]["Head"] + self.ctc_head = eval(name)( + in_channels=self.ctc_encoder.out_channels, + out_channels=out_channels_list["CTCLabelDecode"], + **head_args, + ) + else: + raise NotImplementedError( + "{} is not supported in MultiHead yet".format(name) + ) + + def forward(self, x, targets=None): + if self.use_pool: + x = self.pool( + x.reshape([0, 3, -1, self.in_channels]).transpose([0, 3, 1, 2]) + ) + ctc_encoder = self.ctc_encoder(x) + ctc_out = self.ctc_head(ctc_encoder, targets) + head_out = dict() + head_out["ctc"] = ctc_out + head_out["ctc_neck"] = ctc_encoder + # eval mode + if not self.training: + return ctc_out + if self.gtc_head == "sar": + sar_out = self.sar_head(x, targets[1:]) + head_out["sar"] = sar_out + else: + gtc_out = self.gtc_head(self.before_gtc(x), targets[1:]) + head_out["gtc"] = gtc_out + return head_out diff --git a/modules/onnx_ocr_module/src/ppocr/modeling/heads/rec_nrtr_head.py b/modules/onnx_ocr_module/src/ppocr/modeling/heads/rec_nrtr_head.py new file mode 100644 index 0000000..34f911c --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/modeling/heads/rec_nrtr_head.py @@ -0,0 +1,705 @@ +# copyright (c) 2021 PaddlePaddle Authors. All Rights Reserve. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import math +import paddle +from paddle import nn +import paddle.nn.functional as F +from paddle.nn import Dropout, LayerNorm +import numpy as np +from ppocr.modeling.backbones.rec_svtrnet import Mlp, zeros_ +from paddle.nn.initializer import XavierNormal as xavier_normal_ + + +class Transformer(nn.Layer): + """A transformer model. User is able to modify the attributes as needed. The architecture + is based on the paper "Attention Is All You Need". Ashish Vaswani, Noam Shazeer, + Niki Parmar, Jakob Uszkoreit, Llion Jones, Aidan N Gomez, Lukasz Kaiser, and + Illia Polosukhin. 2017. Attention is all you need. In Advances in Neural Information + Processing Systems, pages 6000-6010. + + Args: + d_model: the number of expected features in the encoder/decoder inputs (default=512). + nhead: the number of heads in the multiheadattention models (default=8). + num_encoder_layers: the number of sub-encoder-layers in the encoder (default=6). + num_decoder_layers: the number of sub-decoder-layers in the decoder (default=6). + dim_feedforward: the dimension of the feedforward network model (default=2048). + dropout: the dropout value (default=0.1). + custom_encoder: custom encoder (default=None). + custom_decoder: custom decoder (default=None). + """ + + def __init__( + self, + d_model=512, + nhead=8, + num_encoder_layers=6, + beam_size=0, + num_decoder_layers=6, + max_len=25, + dim_feedforward=1024, + attention_dropout_rate=0.0, + residual_dropout_rate=0.1, + in_channels=0, + out_channels=0, + scale_embedding=True, + ): + super(Transformer, self).__init__() + self.out_channels = out_channels + 1 + self.max_len = max_len + self.embedding = Embeddings( + d_model=d_model, + vocab=self.out_channels, + padding_idx=0, + scale_embedding=scale_embedding, + ) + self.positional_encoding = PositionalEncoding( + dropout=residual_dropout_rate, dim=d_model + ) + + if num_encoder_layers > 0: + self.encoder = nn.LayerList( + [ + TransformerBlock( + d_model, + nhead, + dim_feedforward, + attention_dropout_rate, + residual_dropout_rate, + with_self_attn=True, + with_cross_attn=False, + ) + for i in range(num_encoder_layers) + ] + ) + else: + self.encoder = None + + self.decoder = nn.LayerList( + [ + TransformerBlock( + d_model, + nhead, + dim_feedforward, + attention_dropout_rate, + residual_dropout_rate, + with_self_attn=True, + with_cross_attn=True, + ) + for i in range(num_decoder_layers) + ] + ) + + self.beam_size = beam_size + self.d_model = d_model + self.nhead = nhead + self.tgt_word_prj = nn.Linear(d_model, self.out_channels, bias_attr=False) + w0 = np.random.normal( + 0.0, d_model**-0.5, (d_model, self.out_channels) + ).astype(np.float32) + self.tgt_word_prj.weight.set_value(w0) + self.apply(self._init_weights) + + def _init_weights(self, m): + if isinstance(m, nn.Linear): + xavier_normal_(m.weight) + if m.bias is not None: + zeros_(m.bias) + + def forward_train(self, src, tgt): + tgt = tgt[:, :-1] + + tgt = self.embedding(tgt) + tgt = self.positional_encoding(tgt) + tgt_mask = self.generate_square_subsequent_mask(tgt.shape[1]) + + if self.encoder is not None: + src = self.positional_encoding(src) + for encoder_layer in self.encoder: + src = encoder_layer(src) + memory = src # B N C + else: + memory = src # B N C + for decoder_layer in self.decoder: + tgt = decoder_layer(tgt, memory, self_mask=tgt_mask) + output = tgt + logit = self.tgt_word_prj(output) + return logit + + def forward(self, src, targets=None): + """Take in and process masked source/target sequences. + Args: + src: the sequence to the encoder (required). + tgt: the sequence to the decoder (required). + Shape: + - src: :math:`(B, sN, C)`. + - tgt: :math:`(B, tN, C)`. + Examples: + >>> output = transformer_model(src, tgt) + """ + + if self.training: + max_len = targets[1].max() + tgt = targets[0][:, : 2 + max_len] + return self.forward_train(src, tgt) + else: + if self.beam_size > 0: + return self.forward_beam(src) + else: + return self.forward_test(src) + + def forward_test(self, src): + bs = src.shape[0] + if self.encoder is not None: + src = self.positional_encoding(src) + for encoder_layer in self.encoder: + src = encoder_layer(src) + memory = src # B N C + else: + memory = src + dec_seq = paddle.full((bs, 1), 2, dtype=paddle.int64) + dec_prob = paddle.full((bs, 1), 1.0, dtype=paddle.float32) + for len_dec_seq in range(1, paddle.to_tensor(self.max_len)): + dec_seq_embed = self.embedding(dec_seq) + dec_seq_embed = self.positional_encoding(dec_seq_embed) + tgt_mask = self.generate_square_subsequent_mask(dec_seq_embed.shape[1]) + tgt = dec_seq_embed + for decoder_layer in self.decoder: + tgt = decoder_layer(tgt, memory, self_mask=tgt_mask) + dec_output = tgt + dec_output = dec_output[:, -1, :] + word_prob = F.softmax(self.tgt_word_prj(dec_output), axis=-1) + preds_idx = paddle.argmax(word_prob, axis=-1) + if paddle.equal_all( + preds_idx, paddle.full(preds_idx.shape, 3, dtype="int64") + ): + break + preds_prob = paddle.max(word_prob, axis=-1) + dec_seq = paddle.concat( + [dec_seq, paddle.reshape(preds_idx, [-1, 1])], axis=1 + ) + dec_prob = paddle.concat( + [dec_prob, paddle.reshape(preds_prob, [-1, 1])], axis=1 + ) + return [dec_seq, dec_prob] + + def forward_beam(self, images): + """Translation work in one batch""" + + def get_inst_idx_to_tensor_position_map(inst_idx_list): + """Indicate the position of an instance in a tensor.""" + return { + inst_idx: tensor_position + for tensor_position, inst_idx in enumerate(inst_idx_list) + } + + def collect_active_part( + beamed_tensor, curr_active_inst_idx, n_prev_active_inst, n_bm + ): + """Collect tensor parts associated to active instances.""" + + beamed_tensor_shape = beamed_tensor.shape + n_curr_active_inst = len(curr_active_inst_idx) + new_shape = ( + n_curr_active_inst * n_bm, + beamed_tensor_shape[1], + beamed_tensor_shape[2], + ) + + beamed_tensor = beamed_tensor.reshape([n_prev_active_inst, -1]) + beamed_tensor = beamed_tensor.index_select(curr_active_inst_idx, axis=0) + beamed_tensor = beamed_tensor.reshape(new_shape) + + return beamed_tensor + + def collate_active_info( + src_enc, inst_idx_to_position_map, active_inst_idx_list + ): + # Sentences which are still active are collected, + # so the decoder will not run on completed sentences. + + n_prev_active_inst = len(inst_idx_to_position_map) + active_inst_idx = [ + inst_idx_to_position_map[k] for k in active_inst_idx_list + ] + active_inst_idx = paddle.to_tensor(active_inst_idx, dtype="int64") + active_src_enc = collect_active_part( + src_enc.transpose([1, 0, 2]), active_inst_idx, n_prev_active_inst, n_bm + ).transpose([1, 0, 2]) + active_inst_idx_to_position_map = get_inst_idx_to_tensor_position_map( + active_inst_idx_list + ) + return active_src_enc, active_inst_idx_to_position_map + + def beam_decode_step( + inst_dec_beams, len_dec_seq, enc_output, inst_idx_to_position_map, n_bm + ): + """Decode and update beam status, and then return active beam idx""" + + def prepare_beam_dec_seq(inst_dec_beams, len_dec_seq): + dec_partial_seq = [ + b.get_current_state() for b in inst_dec_beams if not b.done + ] + dec_partial_seq = paddle.stack(dec_partial_seq) + dec_partial_seq = dec_partial_seq.reshape([-1, len_dec_seq]) + return dec_partial_seq + + def predict_word(dec_seq, enc_output, n_active_inst, n_bm): + dec_seq = self.embedding(dec_seq) + dec_seq = self.positional_encoding(dec_seq) + tgt_mask = self.generate_square_subsequent_mask(dec_seq.shape[1]) + tgt = dec_seq + for decoder_layer in self.decoder: + tgt = decoder_layer(tgt, enc_output, self_mask=tgt_mask) + dec_output = tgt + dec_output = dec_output[:, -1, :] # Pick the last step: (bh * bm) * d_h + word_prob = F.softmax(self.tgt_word_prj(dec_output), axis=1) + word_prob = paddle.reshape(word_prob, [n_active_inst, n_bm, -1]) + return word_prob + + def collect_active_inst_idx_list( + inst_beams, word_prob, inst_idx_to_position_map + ): + active_inst_idx_list = [] + for inst_idx, inst_position in inst_idx_to_position_map.items(): + is_inst_complete = inst_beams[inst_idx].advance( + word_prob[inst_position] + ) + if not is_inst_complete: + active_inst_idx_list += [inst_idx] + + return active_inst_idx_list + + n_active_inst = len(inst_idx_to_position_map) + dec_seq = prepare_beam_dec_seq(inst_dec_beams, len_dec_seq) + word_prob = predict_word(dec_seq, enc_output, n_active_inst, n_bm) + # Update the beam with predicted word prob information and collect incomplete instances + active_inst_idx_list = collect_active_inst_idx_list( + inst_dec_beams, word_prob, inst_idx_to_position_map + ) + return active_inst_idx_list + + def collect_hypothesis_and_scores(inst_dec_beams, n_best): + all_hyp, all_scores = [], [] + for inst_idx in range(len(inst_dec_beams)): + scores, tail_idxs = inst_dec_beams[inst_idx].sort_scores() + all_scores += [scores[:n_best]] + hyps = [ + inst_dec_beams[inst_idx].get_hypothesis(i) + for i in tail_idxs[:n_best] + ] + all_hyp += [hyps] + return all_hyp, all_scores + + with paddle.no_grad(): + # -- Encode + if self.encoder is not None: + src = self.positional_encoding(images) + src_enc = self.encoder(src) + else: + src_enc = images + + n_bm = self.beam_size + src_shape = src_enc.shape + inst_dec_beams = [Beam(n_bm) for _ in range(1)] + active_inst_idx_list = list(range(1)) + # Repeat data for beam search + src_enc = paddle.tile(src_enc, [1, n_bm, 1]) + inst_idx_to_position_map = get_inst_idx_to_tensor_position_map( + active_inst_idx_list + ) + # Decode + for len_dec_seq in range(1, paddle.to_tensor(self.max_len)): + src_enc_copy = src_enc.clone() + active_inst_idx_list = beam_decode_step( + inst_dec_beams, + len_dec_seq, + src_enc_copy, + inst_idx_to_position_map, + n_bm, + ) + if not active_inst_idx_list: + break # all instances have finished their path to + src_enc, inst_idx_to_position_map = collate_active_info( + src_enc_copy, inst_idx_to_position_map, active_inst_idx_list + ) + batch_hyp, batch_scores = collect_hypothesis_and_scores(inst_dec_beams, 1) + result_hyp = [] + hyp_scores = [] + for bs_hyp, score in zip(batch_hyp, batch_scores): + l = len(bs_hyp[0]) + bs_hyp_pad = bs_hyp[0] + [3] * (25 - l) + result_hyp.append(bs_hyp_pad) + score = float(score) / l + hyp_score = [score for _ in range(25)] + hyp_scores.append(hyp_score) + return [ + paddle.to_tensor(np.array(result_hyp), dtype=paddle.int64), + paddle.to_tensor(hyp_scores), + ] + + def generate_square_subsequent_mask(self, sz): + """Generate a square mask for the sequence. The masked positions are filled with float('-inf'). + Unmasked positions are filled with float(0.0). + """ + mask = paddle.zeros([sz, sz], dtype="float32") + mask_inf = paddle.triu( + paddle.full(shape=[sz, sz], dtype="float32", fill_value=float("-inf")), + diagonal=1, + ) + mask = mask + mask_inf + return mask.unsqueeze([0, 1]) + + +class MultiheadAttention(nn.Layer): + """Allows the model to jointly attend to information + from different representation subspaces. + See reference: Attention Is All You Need + + .. math:: + \text{MultiHead}(Q, K, V) = \text{Concat}(head_1,\dots,head_h)W^O + \text{where} head_i = \text{Attention}(QW_i^Q, KW_i^K, VW_i^V) + + Args: + embed_dim: total dimension of the model + num_heads: parallel attention layers, or heads + + """ + + def __init__(self, embed_dim, num_heads, dropout=0.0, self_attn=False): + super(MultiheadAttention, self).__init__() + self.embed_dim = embed_dim + self.num_heads = num_heads + # self.dropout = dropout + self.head_dim = embed_dim // num_heads + assert ( + self.head_dim * num_heads == self.embed_dim + ), "embed_dim must be divisible by num_heads" + self.scale = self.head_dim**-0.5 + self.self_attn = self_attn + if self_attn: + self.qkv = nn.Linear(embed_dim, embed_dim * 3) + else: + self.q = nn.Linear(embed_dim, embed_dim) + self.kv = nn.Linear(embed_dim, embed_dim * 2) + self.attn_drop = nn.Dropout(dropout) + self.out_proj = nn.Linear(embed_dim, embed_dim) + + def forward(self, query, key=None, attn_mask=None): + qN = query.shape[1] + + if self.self_attn: + qkv = ( + self.qkv(query) + .reshape((0, qN, 3, self.num_heads, self.head_dim)) + .transpose((2, 0, 3, 1, 4)) + ) + q, k, v = qkv[0], qkv[1], qkv[2] + else: + kN = key.shape[1] + q = ( + self.q(query) + .reshape([0, qN, self.num_heads, self.head_dim]) + .transpose([0, 2, 1, 3]) + ) + kv = ( + self.kv(key) + .reshape((0, kN, 2, self.num_heads, self.head_dim)) + .transpose((2, 0, 3, 1, 4)) + ) + k, v = kv[0], kv[1] + + attn = (q.matmul(k.transpose((0, 1, 3, 2)))) * self.scale + + if attn_mask is not None: + attn += attn_mask + + attn = F.softmax(attn, axis=-1) + attn = self.attn_drop(attn) + + x = (attn.matmul(v)).transpose((0, 2, 1, 3)).reshape((0, qN, self.embed_dim)) + x = self.out_proj(x) + + return x + + +class TransformerBlock(nn.Layer): + def __init__( + self, + d_model, + nhead, + dim_feedforward=2048, + attention_dropout_rate=0.0, + residual_dropout_rate=0.1, + with_self_attn=True, + with_cross_attn=False, + epsilon=1e-5, + ): + super(TransformerBlock, self).__init__() + self.with_self_attn = with_self_attn + if with_self_attn: + self.self_attn = MultiheadAttention( + d_model, nhead, dropout=attention_dropout_rate, self_attn=with_self_attn + ) + self.norm1 = LayerNorm(d_model, epsilon=epsilon) + self.dropout1 = Dropout(residual_dropout_rate) + self.with_cross_attn = with_cross_attn + if with_cross_attn: + self.cross_attn = ( + MultiheadAttention( # for self_attn of encoder or cross_attn of decoder + d_model, nhead, dropout=attention_dropout_rate + ) + ) + self.norm2 = LayerNorm(d_model, epsilon=epsilon) + self.dropout2 = Dropout(residual_dropout_rate) + + self.mlp = Mlp( + in_features=d_model, + hidden_features=dim_feedforward, + act_layer=nn.ReLU, + drop=residual_dropout_rate, + ) + + self.norm3 = LayerNorm(d_model, epsilon=epsilon) + + self.dropout3 = Dropout(residual_dropout_rate) + + def forward(self, tgt, memory=None, self_mask=None, cross_mask=None): + if self.with_self_attn: + tgt1 = self.self_attn(tgt, attn_mask=self_mask) + tgt = self.norm1(tgt + self.dropout1(tgt1)) + + if self.with_cross_attn: + tgt2 = self.cross_attn(tgt, key=memory, attn_mask=cross_mask) + tgt = self.norm2(tgt + self.dropout2(tgt2)) + tgt = self.norm3(tgt + self.dropout3(self.mlp(tgt))) + return tgt + + +class PositionalEncoding(nn.Layer): + """Inject some information about the relative or absolute position of the tokens + in the sequence. The positional encodings have the same dimension as + the embeddings, so that the two can be summed. Here, we use sine and cosine + functions of different frequencies. + .. math:: + \text{PosEncoder}(pos, 2i) = sin(pos/10000^(2i/d_model)) + \text{PosEncoder}(pos, 2i+1) = cos(pos/10000^(2i/d_model)) + \text{where pos is the word position and i is the embed idx) + Args: + d_model: the embed dim (required). + dropout: the dropout value (default=0.1). + max_len: the max. length of the incoming sequence (default=5000). + Examples: + >>> pos_encoder = PositionalEncoding(d_model) + """ + + def __init__(self, dropout, dim, max_len=5000): + super(PositionalEncoding, self).__init__() + self.dropout = nn.Dropout(p=dropout) + + pe = paddle.zeros([max_len, dim]) + position = paddle.arange(0, max_len, dtype=paddle.float32).unsqueeze(1) + div_term = paddle.exp( + paddle.arange(0, dim, 2).astype("float32") * (-math.log(10000.0) / dim) + ) + pe[:, 0::2] = paddle.sin(position * div_term) + pe[:, 1::2] = paddle.cos(position * div_term) + pe = paddle.unsqueeze(pe, 0) + pe = paddle.transpose(pe, [1, 0, 2]) + self.register_buffer("pe", pe) + + def forward(self, x): + """Inputs of forward function + Args: + x: the sequence fed to the positional encoder model (required). + Shape: + x: [sequence length, batch size, embed dim] + output: [sequence length, batch size, embed dim] + Examples: + >>> output = pos_encoder(x) + """ + x = x.transpose([1, 0, 2]) + x = x + self.pe[: x.shape[0], :] + return self.dropout(x).transpose([1, 0, 2]) + + +class PositionalEncoding_2d(nn.Layer): + """Inject some information about the relative or absolute position of the tokens + in the sequence. The positional encodings have the same dimension as + the embeddings, so that the two can be summed. Here, we use sine and cosine + functions of different frequencies. + .. math:: + \text{PosEncoder}(pos, 2i) = sin(pos/10000^(2i/d_model)) + \text{PosEncoder}(pos, 2i+1) = cos(pos/10000^(2i/d_model)) + \text{where pos is the word position and i is the embed idx) + Args: + d_model: the embed dim (required). + dropout: the dropout value (default=0.1). + max_len: the max. length of the incoming sequence (default=5000). + Examples: + >>> pos_encoder = PositionalEncoding(d_model) + """ + + def __init__(self, dropout, dim, max_len=5000): + super(PositionalEncoding_2d, self).__init__() + self.dropout = nn.Dropout(p=dropout) + + pe = paddle.zeros([max_len, dim]) + position = paddle.arange(0, max_len, dtype=paddle.float32).unsqueeze(1) + div_term = paddle.exp( + paddle.arange(0, dim, 2).astype("float32") * (-math.log(10000.0) / dim) + ) + pe[:, 0::2] = paddle.sin(position * div_term) + pe[:, 1::2] = paddle.cos(position * div_term) + pe = paddle.transpose(paddle.unsqueeze(pe, 0), [1, 0, 2]) + self.register_buffer("pe", pe) + + self.avg_pool_1 = nn.AdaptiveAvgPool2D((1, 1)) + self.linear1 = nn.Linear(dim, dim) + self.linear1.weight.data.fill_(1.0) + self.avg_pool_2 = nn.AdaptiveAvgPool2D((1, 1)) + self.linear2 = nn.Linear(dim, dim) + self.linear2.weight.data.fill_(1.0) + + def forward(self, x): + """Inputs of forward function + Args: + x: the sequence fed to the positional encoder model (required). + Shape: + x: [sequence length, batch size, embed dim] + output: [sequence length, batch size, embed dim] + Examples: + >>> output = pos_encoder(x) + """ + w_pe = self.pe[: x.shape[-1], :] + w1 = self.linear1(self.avg_pool_1(x).squeeze()).unsqueeze(0) + w_pe = w_pe * w1 + w_pe = paddle.transpose(w_pe, [1, 2, 0]) + w_pe = paddle.unsqueeze(w_pe, 2) + + h_pe = self.pe[: x.shape.shape[-2], :] + w2 = self.linear2(self.avg_pool_2(x).squeeze()).unsqueeze(0) + h_pe = h_pe * w2 + h_pe = paddle.transpose(h_pe, [1, 2, 0]) + h_pe = paddle.unsqueeze(h_pe, 3) + + x = x + w_pe + h_pe + x = paddle.transpose( + paddle.reshape(x, [x.shape[0], x.shape[1], x.shape[2] * x.shape[3]]), + [2, 0, 1], + ) + + return self.dropout(x) + + +class Embeddings(nn.Layer): + def __init__(self, d_model, vocab, padding_idx=None, scale_embedding=True): + super(Embeddings, self).__init__() + self.embedding = nn.Embedding(vocab, d_model, padding_idx=padding_idx) + w0 = np.random.normal(0.0, d_model**-0.5, (vocab, d_model)).astype(np.float32) + self.embedding.weight.set_value(w0) + self.d_model = d_model + self.scale_embedding = scale_embedding + + def forward(self, x): + if self.scale_embedding: + x = self.embedding(x) + return x * math.sqrt(self.d_model) + return self.embedding(x) + + +class Beam: + """Beam search""" + + def __init__(self, size, device=False): + self.size = size + self._done = False + # The score for each translation on the beam. + self.scores = paddle.zeros((size,), dtype=paddle.float32) + self.all_scores = [] + # The backpointers at each time-step. + self.prev_ks = [] + # The outputs at each time-step. + self.next_ys = [paddle.full((size,), 0, dtype=paddle.int64)] + self.next_ys[0][0] = 2 + + def get_current_state(self): + "Get the outputs for the current timestep." + return self.get_tentative_hypothesis() + + def get_current_origin(self): + "Get the backpointers for the current timestep." + return self.prev_ks[-1] + + @property + def done(self): + return self._done + + def advance(self, word_prob): + "Update beam status and check if finished or not." + num_words = word_prob.shape[1] + + # Sum the previous scores. + if len(self.prev_ks) > 0: + beam_lk = word_prob + self.scores.unsqueeze(1).expand_as(word_prob) + else: + beam_lk = word_prob[0] + + flat_beam_lk = beam_lk.reshape([-1]) + best_scores, best_scores_id = flat_beam_lk.topk( + self.size, 0, True, True + ) # 1st sort + self.all_scores.append(self.scores) + self.scores = best_scores + # bestScoresId is flattened as a (beam x word) array, + # so we need to calculate which word and beam each score came from + prev_k = best_scores_id // num_words + self.prev_ks.append(prev_k) + self.next_ys.append(best_scores_id - prev_k * num_words) + # End condition is when top-of-beam is EOS. + if self.next_ys[-1][0] == 3: + self._done = True + self.all_scores.append(self.scores) + + return self._done + + def sort_scores(self): + "Sort the scores." + return self.scores, paddle.to_tensor( + [i for i in range(int(self.scores.shape[0]))], dtype="int32" + ) + + def get_the_best_score_and_idx(self): + "Get the score of the best in the beam." + scores, ids = self.sort_scores() + return scores[1], ids[1] + + def get_tentative_hypothesis(self): + "Get the decoded sequence for the current timestep." + if len(self.next_ys) == 1: + dec_seq = self.next_ys[0].unsqueeze(1) + else: + _, keys = self.sort_scores() + hyps = [self.get_hypothesis(k) for k in keys] + hyps = [[2] + h for h in hyps] + dec_seq = paddle.to_tensor(hyps, dtype="int64") + return dec_seq + + def get_hypothesis(self, k): + """Walk back to construct the full hypothesis.""" + hyp = [] + for j in range(len(self.prev_ks) - 1, -1, -1): + hyp.append(self.next_ys[j + 1][k]) + k = self.prev_ks[j][k] + return list(map(lambda x: x.item(), hyp[::-1])) diff --git a/modules/onnx_ocr_module/src/ppocr/modeling/heads/rec_parseq_head.py b/modules/onnx_ocr_module/src/ppocr/modeling/heads/rec_parseq_head.py new file mode 100644 index 0000000..572387e --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/modeling/heads/rec_parseq_head.py @@ -0,0 +1,504 @@ +# copyright (c) 2020 PaddlePaddle Authors. All Rights Reserve. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Code was based on https://github.com/baudm/parseq/blob/main/strhub/models/parseq/system.py +# reference: https://arxiv.org/abs/2207.06966 + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import math +import paddle +from paddle import nn, ParamAttr +from paddle.nn import functional as F +import numpy as np +from .self_attention import WrapEncoderForFeature +from .self_attention import WrapEncoder +from collections import OrderedDict +from typing import Optional +import copy +from itertools import permutations + + +class DecoderLayer(paddle.nn.Layer): + """A Transformer decoder layer supporting two-stream attention (XLNet) + This implements a pre-LN decoder, as opposed to the post-LN default in PyTorch.""" + + def __init__( + self, + d_model, + nhead, + dim_feedforward=2048, + dropout=0.1, + activation="gelu", + layer_norm_eps=1e-05, + ): + super().__init__() + self.self_attn = paddle.nn.MultiHeadAttention( + d_model, nhead, dropout=dropout, need_weights=True + ) # paddle.nn.MultiHeadAttention默认为batch_first模式 + self.cross_attn = paddle.nn.MultiHeadAttention( + d_model, nhead, dropout=dropout, need_weights=True + ) + self.linear1 = paddle.nn.Linear( + in_features=d_model, out_features=dim_feedforward + ) + self.dropout = paddle.nn.Dropout(p=dropout) + self.linear2 = paddle.nn.Linear( + in_features=dim_feedforward, out_features=d_model + ) + self.norm1 = paddle.nn.LayerNorm( + normalized_shape=d_model, epsilon=layer_norm_eps + ) + self.norm2 = paddle.nn.LayerNorm( + normalized_shape=d_model, epsilon=layer_norm_eps + ) + self.norm_q = paddle.nn.LayerNorm( + normalized_shape=d_model, epsilon=layer_norm_eps + ) + self.norm_c = paddle.nn.LayerNorm( + normalized_shape=d_model, epsilon=layer_norm_eps + ) + self.dropout1 = paddle.nn.Dropout(p=dropout) + self.dropout2 = paddle.nn.Dropout(p=dropout) + self.dropout3 = paddle.nn.Dropout(p=dropout) + if activation == "gelu": + self.activation = paddle.nn.GELU() + + def __setstate__(self, state): + if "activation" not in state: + state["activation"] = paddle.nn.functional.gelu + super().__setstate__(state) + + def forward_stream( + self, tgt, tgt_norm, tgt_kv, memory, tgt_mask, tgt_key_padding_mask + ): + """Forward pass for a single stream (i.e. content or query) + tgt_norm is just a LayerNorm'd tgt. Added as a separate parameter for efficiency. + Both tgt_kv and memory are expected to be LayerNorm'd too. + memory is LayerNorm'd by ViT. + """ + if tgt_key_padding_mask is not None: + tgt_mask1 = (tgt_mask != float("-inf"))[None, None, :, :] & ( + tgt_key_padding_mask[:, None, None, :] == False + ) + tgt2, sa_weights = self.self_attn( + tgt_norm, tgt_kv, tgt_kv, attn_mask=tgt_mask1 + ) + else: + tgt2, sa_weights = self.self_attn( + tgt_norm, tgt_kv, tgt_kv, attn_mask=tgt_mask + ) + + tgt = tgt + self.dropout1(tgt2) + tgt2, ca_weights = self.cross_attn(self.norm1(tgt), memory, memory) + tgt = tgt + self.dropout2(tgt2) + tgt2 = self.linear2( + self.dropout(self.activation(self.linear1(self.norm2(tgt)))) + ) + tgt = tgt + self.dropout3(tgt2) + return tgt, sa_weights, ca_weights + + def forward( + self, + query, + content, + memory, + query_mask=None, + content_mask=None, + content_key_padding_mask=None, + update_content=True, + ): + query_norm = self.norm_q(query) + content_norm = self.norm_c(content) + query = self.forward_stream( + query, + query_norm, + content_norm, + memory, + query_mask, + content_key_padding_mask, + )[0] + if update_content: + content = self.forward_stream( + content, + content_norm, + content_norm, + memory, + content_mask, + content_key_padding_mask, + )[0] + return query, content + + +def get_clones(module, N): + return paddle.nn.LayerList([copy.deepcopy(module) for i in range(N)]) + + +class Decoder(paddle.nn.Layer): + __constants__ = ["norm"] + + def __init__(self, decoder_layer, num_layers, norm): + super().__init__() + self.layers = get_clones(decoder_layer, num_layers) + self.num_layers = num_layers + self.norm = norm + + def forward( + self, + query, + content, + memory, + query_mask: Optional[paddle.Tensor] = None, + content_mask: Optional[paddle.Tensor] = None, + content_key_padding_mask: Optional[paddle.Tensor] = None, + ): + for i, mod in enumerate(self.layers): + last = i == len(self.layers) - 1 + query, content = mod( + query, + content, + memory, + query_mask, + content_mask, + content_key_padding_mask, + update_content=not last, + ) + query = self.norm(query) + return query + + +class TokenEmbedding(paddle.nn.Layer): + def __init__(self, charset_size: int, embed_dim: int): + super().__init__() + self.embedding = paddle.nn.Embedding( + num_embeddings=charset_size, embedding_dim=embed_dim + ) + self.embed_dim = embed_dim + + def forward(self, tokens: paddle.Tensor): + return math.sqrt(self.embed_dim) * self.embedding(tokens.astype(paddle.int64)) + + +def trunc_normal_init(param, **kwargs): + initializer = nn.initializer.TruncatedNormal(**kwargs) + initializer(param, param.block) + + +def constant_init(param, **kwargs): + initializer = nn.initializer.Constant(**kwargs) + initializer(param, param.block) + + +def kaiming_normal_init(param, **kwargs): + initializer = nn.initializer.KaimingNormal(**kwargs) + initializer(param, param.block) + + +class ParseQHead(nn.Layer): + def __init__( + self, + out_channels, + max_text_length, + embed_dim, + dec_num_heads, + dec_mlp_ratio, + dec_depth, + perm_num, + perm_forward, + perm_mirrored, + decode_ar, + refine_iters, + dropout, + **kwargs, + ): + super().__init__() + + self.bos_id = out_channels - 2 + self.eos_id = 0 + self.pad_id = out_channels - 1 + + self.max_label_length = max_text_length + self.decode_ar = decode_ar + self.refine_iters = refine_iters + decoder_layer = DecoderLayer( + embed_dim, dec_num_heads, embed_dim * dec_mlp_ratio, dropout + ) + self.decoder = Decoder( + decoder_layer, + num_layers=dec_depth, + norm=paddle.nn.LayerNorm(normalized_shape=embed_dim), + ) + self.rng = np.random.default_rng() + self.max_gen_perms = perm_num // 2 if perm_mirrored else perm_num + self.perm_forward = perm_forward + self.perm_mirrored = perm_mirrored + self.head = paddle.nn.Linear( + in_features=embed_dim, out_features=out_channels - 2 + ) + self.text_embed = TokenEmbedding(out_channels, embed_dim) + self.pos_queries = paddle.create_parameter( + shape=paddle.empty(shape=[1, max_text_length + 1, embed_dim]).shape, + dtype=paddle.empty(shape=[1, max_text_length + 1, embed_dim]).numpy().dtype, + default_initializer=paddle.nn.initializer.Assign( + paddle.empty(shape=[1, max_text_length + 1, embed_dim]) + ), + ) + self.pos_queries.stop_gradient = not True + self.dropout = paddle.nn.Dropout(p=dropout) + self._device = self.parameters()[0].place + trunc_normal_init(self.pos_queries, std=0.02) + self.apply(self._init_weights) + + def _init_weights(self, m): + if isinstance(m, paddle.nn.Linear): + trunc_normal_init(m.weight, std=0.02) + if m.bias is not None: + constant_init(m.bias, value=0.0) + elif isinstance(m, paddle.nn.Embedding): + trunc_normal_init(m.weight, std=0.02) + if m._padding_idx is not None: + m.weight.data[m._padding_idx].zero_() + elif isinstance(m, paddle.nn.Conv2D): + kaiming_normal_init(m.weight, fan_in=None, nonlinearity="relu") + if m.bias is not None: + constant_init(m.bias, value=0.0) + elif isinstance( + m, (paddle.nn.LayerNorm, paddle.nn.BatchNorm2D, paddle.nn.GroupNorm) + ): + constant_init(m.weight, value=1.0) + constant_init(m.bias, value=0.0) + + def no_weight_decay(self): + param_names = {"text_embed.embedding.weight", "pos_queries"} + enc_param_names = {("encoder." + n) for n in self.encoder.no_weight_decay()} + return param_names.union(enc_param_names) + + def encode(self, img): + return self.encoder(img) + + def decode( + self, + tgt, + memory, + tgt_mask=None, + tgt_padding_mask=None, + tgt_query=None, + tgt_query_mask=None, + ): + N, L = tgt.shape + null_ctx = self.text_embed(tgt[:, :1]) + if L != 1: + tgt_emb = self.pos_queries[:, : L - 1] + self.text_embed(tgt[:, 1:]) + tgt_emb = self.dropout(paddle.concat(x=[null_ctx, tgt_emb], axis=1)) + else: + tgt_emb = self.dropout(null_ctx) + if tgt_query is None: + tgt_query = self.pos_queries[:, :L].expand(shape=[N, -1, -1]) + tgt_query = self.dropout(tgt_query) + return self.decoder( + tgt_query, tgt_emb, memory, tgt_query_mask, tgt_mask, tgt_padding_mask + ) + + def forward_test(self, memory, max_length=None): + testing = max_length is None + max_length = ( + self.max_label_length + if max_length is None + else min(max_length, self.max_label_length) + ) + bs = memory.shape[0] + num_steps = max_length + 1 + + pos_queries = self.pos_queries[:, :num_steps].expand(shape=[bs, -1, -1]) + tgt_mask = query_mask = paddle.triu( + x=paddle.full(shape=(num_steps, num_steps), fill_value=float("-inf")), + diagonal=1, + ) + if self.decode_ar: + tgt_in = paddle.full(shape=(bs, num_steps), fill_value=self.pad_id).astype( + "int64" + ) + tgt_in[:, (0)] = self.bos_id + + logits = [] + for i in range(paddle.to_tensor(num_steps)): + j = i + 1 + tgt_out = self.decode( + tgt_in[:, :j], + memory, + tgt_mask[:j, :j], + tgt_query=pos_queries[:, i:j], + tgt_query_mask=query_mask[i:j, :j], + ) + p_i = self.head(tgt_out) + logits.append(p_i) + if j < num_steps: + tgt_in[:, (j)] = p_i.squeeze().argmax(axis=-1) + if ( + testing + and (tgt_in == self.eos_id) + .astype("bool") + .any(axis=-1) + .astype("bool") + .all() + ): + break + logits = paddle.concat(x=logits, axis=1) + else: + tgt_in = paddle.full(shape=(bs, 1), fill_value=self.bos_id).astype("int64") + tgt_out = self.decode(tgt_in, memory, tgt_query=pos_queries) + logits = self.head(tgt_out) + if self.refine_iters: + temp = paddle.triu( + x=paddle.ones(shape=[num_steps, num_steps], dtype="bool"), diagonal=2 + ) + posi = np.where(temp.cpu().numpy() == True) + query_mask[posi] = 0 + bos = paddle.full(shape=(bs, 1), fill_value=self.bos_id).astype("int64") + for i in range(self.refine_iters): + tgt_in = paddle.concat(x=[bos, logits[:, :-1].argmax(axis=-1)], axis=1) + tgt_padding_mask = (tgt_in == self.eos_id).astype(dtype="int32") + tgt_padding_mask = tgt_padding_mask.cpu() + tgt_padding_mask = tgt_padding_mask.cumsum(axis=-1) > 0 + tgt_padding_mask = ( + tgt_padding_mask.cuda().astype(dtype="float32") == 1.0 + ) + tgt_out = self.decode( + tgt_in, + memory, + tgt_mask, + tgt_padding_mask, + tgt_query=pos_queries, + tgt_query_mask=query_mask[:, : tgt_in.shape[1]], + ) + logits = self.head(tgt_out) + + # transfer to probability + logits = F.softmax(logits, axis=-1) + + final_output = {"predict": logits} + + return final_output + + def gen_tgt_perms(self, tgt): + """Generate shared permutations for the whole batch. + This works because the same attention mask can be used for the shorter sequences + because of the padding mask. + """ + max_num_chars = tgt.shape[1] - 2 + if max_num_chars == 1: + return paddle.arange(end=3).unsqueeze(axis=0) + perms = [paddle.arange(end=max_num_chars)] if self.perm_forward else [] + max_perms = math.factorial(max_num_chars) + if self.perm_mirrored: + max_perms //= 2 + num_gen_perms = min(self.max_gen_perms, max_perms) + if max_num_chars < 5: + if max_num_chars == 4 and self.perm_mirrored: + selector = [0, 3, 4, 6, 9, 10, 12, 16, 17, 18, 19, 21] + else: + selector = list(range(max_perms)) + perm_pool = paddle.to_tensor( + data=list(permutations(range(max_num_chars), max_num_chars)), + place=self._device, + )[selector] + if self.perm_forward: + perm_pool = perm_pool[1:] + perms = paddle.stack(x=perms) + if len(perm_pool): + i = self.rng.choice( + len(perm_pool), size=num_gen_perms - len(perms), replace=False + ) + perms = paddle.concat(x=[perms, perm_pool[i]]) + else: + perms.extend( + [ + paddle.randperm(n=max_num_chars) + for _ in range(num_gen_perms - len(perms)) + ] + ) + perms = paddle.stack(x=perms) + if self.perm_mirrored: + comp = perms.flip(axis=-1) + x = paddle.stack(x=[perms, comp]) + perm_2 = list(range(x.ndim)) + perm_2[0] = 1 + perm_2[1] = 0 + perms = x.transpose(perm=perm_2).reshape((-1, max_num_chars)) + bos_idx = paddle.zeros(shape=(len(perms), 1), dtype=perms.dtype) + eos_idx = paddle.full( + shape=(len(perms), 1), fill_value=max_num_chars + 1, dtype=perms.dtype + ) + perms = paddle.concat(x=[bos_idx, perms + 1, eos_idx], axis=1) + if len(perms) > 1: + perms[(1), 1:] = max_num_chars + 1 - paddle.arange(end=max_num_chars + 1) + return perms + + def generate_attn_masks(self, perm): + """Generate attention masks given a sequence permutation (includes pos. for bos and eos tokens) + :param perm: the permutation sequence. i = 0 is always the BOS + :return: lookahead attention masks + """ + sz = perm.shape[0] + mask = paddle.zeros(shape=(sz, sz)) + for i in range(sz): + query_idx = perm[i].cpu().numpy().tolist() + masked_keys = perm[i + 1 :].cpu().numpy().tolist() + if len(masked_keys) == 0: + break + mask[query_idx, masked_keys] = float("-inf") + content_mask = mask[:-1, :-1].clone() + mask[paddle.eye(num_rows=sz).astype("bool")] = float("-inf") + query_mask = mask[1:, :-1] + return content_mask, query_mask + + def forward_train(self, memory, tgt): + tgt_perms = self.gen_tgt_perms(tgt) + tgt_in = tgt[:, :-1] + tgt_padding_mask = (tgt_in == self.pad_id) | (tgt_in == self.eos_id) + logits_list = [] + final_out = {} + for i, perm in enumerate(tgt_perms): + tgt_mask, query_mask = self.generate_attn_masks(perm) + out = self.decode( + tgt_in, memory, tgt_mask, tgt_padding_mask, tgt_query_mask=query_mask + ) + logits = self.head(out) + if i == 0: + final_out["predict"] = logits + logits = logits.flatten(stop_axis=1) + logits_list.append(logits) + + final_out["logits_list"] = logits_list + final_out["pad_id"] = self.pad_id + final_out["eos_id"] = self.eos_id + + return final_out + + def forward(self, feat, targets=None): + # feat : B, N, C + # targets : labels, labels_len + + if self.training: + label = targets[0] # label + label_len = targets[1] + max_step = paddle.max(label_len).cpu().numpy()[0] + 2 + crop_label = label[:, :max_step] + final_out = self.forward_train(feat, crop_label) + else: + final_out = self.forward_test(feat) + + return final_out diff --git a/modules/onnx_ocr_module/src/ppocr/modeling/heads/rec_ppformulanet_head.py b/modules/onnx_ocr_module/src/ppocr/modeling/heads/rec_ppformulanet_head.py new file mode 100644 index 0000000..644dc2a --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/modeling/heads/rec_ppformulanet_head.py @@ -0,0 +1,1391 @@ +# copyright (c) 2024 PaddlePaddle Authors. All Rights Reserve. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import math +import re +import numpy as np +import inspect +import paddle +import paddle.nn as nn +import paddle.nn.functional as F +from paddle.nn import CrossEntropyLoss +from paddle import Tensor +from collections import OrderedDict +from typing import Optional, Tuple, Union, List, Dict, Any +from dataclasses import dataclass, fields, is_dataclass +from ppocr.modeling.backbones.rec_donut_swin import DonutSwinModelOutput +from ppocr.modeling.heads.rec_unimernet_head import ( + MBartForCausalLM, + MBartDecoder, + MBartConfig, + ModelOutput, + BaseModelOutputWithPastAndCrossAttentions, + Seq2SeqLMOutput, + zeros_, + ones_, + kaiming_normal_, + trunc_normal_, + xavier_uniform_, + CausalLMOutputWithCrossAttentions, + LogitsProcessorList, + ForcedEOSTokenLogitsProcessor, + UniMERNetHead, +) + + +@dataclass +class AttentionMaskConverter: + """ + A class to convert attention masks based on specific configurations. + + This class is designed to handle the conversion of attention masks with options for causal masking + and sliding window attention, which are commonly used in transformer models. + + Attributes: + is_causal (bool): Flag indicating whether the attention mask should enforce causal masking, + which ensures each position can only attend to previous positions. + sliding_window (int, optional): Size of the sliding window for local attention. If set, + attention is restricted to a local window of this size. + + """ + + is_causal: bool + sliding_window: int + + def __init__(self, is_causal: bool, sliding_window=None): + self.is_causal = is_causal + self.sliding_window = sliding_window + + if self.sliding_window is not None and self.sliding_window <= 0: + raise ValueError( + f"Make sure that when passing `sliding_window` that its value is a strictly positive integer, not `{self.sliding_window}`" + ) + + @staticmethod + def _make_causal_mask( + input_ids_shape, + dtype, + past_key_values_length=0, + sliding_window=None, + is_export=False, + ): + """ + Make causal mask used for bi-directional self-attention. + """ + bsz, tgt_len = input_ids_shape + if is_export: + mask = paddle.full( + (tgt_len, tgt_len), paddle.finfo(dtype).min, dtype="float64" + ) + mask_cond = paddle.arange(mask.shape[-1]) + mask.masked_fill_( + mask_cond < (mask_cond + 1).reshape([mask.shape[-1], 1]), 0 + ) + else: + mask = paddle.full((tgt_len, tgt_len), paddle.finfo(dtype).min) + mask_cond = paddle.arange(mask.shape[-1]) + mask.masked_fill_( + mask_cond < (mask_cond + 1).reshape([mask.shape[-1], 1]), 0 + ) + mask = mask.cast(dtype) + + if past_key_values_length > 0: + mask = paddle.concat( + [paddle.zeros(tgt_len, past_key_values_length, dtype=dtype), mask], + axis=-1, + ) + + # add lower triangular sliding window mask if necessary + if sliding_window is not None: + diagonal = past_key_values_length - sliding_window - 1 + + context_mask = paddle.tril( + paddle.ones_like(mask, dtype=paddle.bool), diagonal=diagonal + ) + mask.masked_fill_(context_mask, paddle.finfo(dtype).min) + + return mask[None, None, :, :].expand( + [bsz, 1, tgt_len, tgt_len + past_key_values_length] + ) + + @staticmethod + def _make_causal_mask_parallel( + input_ids_shape, + dtype, + past_key_values_length=0, + sliding_window=None, + parallel_step=1, + is_export=False, + ): + """ + Make causal mask used for bi-directional self-attention. + """ + bsz, tgt_len = input_ids_shape + mask = paddle.full((tgt_len, tgt_len), paddle.finfo(dtype).min) + mask_cond = paddle.arange(mask.shape[-1]) + mask_cond_parallel = paddle.arange(mask.shape[-1]) + + mask_parallel = paddle.arange(0, tgt_len, step=parallel_step).reshape([1, -1]) + mask_parallel = paddle.repeat_interleave(mask_parallel, parallel_step, 1)[ + :, :tgt_len + ] + mask.masked_fill_( + mask_cond < (mask_parallel + parallel_step).reshape([mask.shape[-1], 1]), 0 + ) + mask = mask.cast(dtype) + + if past_key_values_length > 0: + mask = paddle.concat( + [paddle.zeros([tgt_len, past_key_values_length], dtype=dtype), mask], + axis=-1, + ) + + # add lower triangular sliding window mask if necessary + if sliding_window is not None: + diagonal = past_key_values_length - sliding_window - 1 + + context_mask = paddle.tril( + paddle.ones_like(mask, dtype=paddle.bool), diagonal=diagonal + ) + mask.masked_fill_(context_mask, paddle.finfo(dtype).min) + + return mask[None, None, :, :].expand( + [bsz, 1, tgt_len, tgt_len + past_key_values_length] + ) + + def to_4d( + self, + attention_mask_2d, + query_length, + dtype, + key_value_length, + use_parallel=False, + parallel_step=3, + is_export=False, + ): + """ + Converts 2D attention mask to 4D attention mask by expanding mask to (bsz, head_dim=1, query_length, + key_value_length) shape and by adding a large negative bias to not-attended positions. If attention_mask is + causal, a causal mask will be added. + """ + input_shape = (attention_mask_2d.shape[0], query_length) + + causal_4d_mask = None + if use_parallel: + step = parallel_step + else: + step = 1 + if ( + input_shape[-1] > step or self.sliding_window is not None + ) and self.is_causal: + + if key_value_length is None: + raise ValueError( + "This attention mask converter is causal. Make sure to pass `key_value_length` to correctly create a causal mask." + ) + + past_key_values_length = key_value_length - query_length + + if use_parallel: + causal_4d_mask = self._make_causal_mask_parallel( + input_shape, + dtype, + past_key_values_length=past_key_values_length, + sliding_window=self.sliding_window, + parallel_step=parallel_step, + is_export=is_export, + ) + else: + causal_4d_mask = self._make_causal_mask( + input_shape, + dtype, + past_key_values_length=past_key_values_length, + sliding_window=self.sliding_window, + is_export=is_export, + ) + + elif self.sliding_window is not None: + raise NotImplementedError( + "Sliding window is currently only implemented for causal masking" + ) + + expanded_attn_mask = self._expand_mask( + attention_mask_2d, dtype, tgt_len=input_shape[-1] + ) + + if causal_4d_mask is not None: + expanded_attn_mask = causal_4d_mask.masked_fill_( + expanded_attn_mask.cast(paddle.bool), paddle.finfo(dtype).min + ) + + expanded_4d_mask = expanded_attn_mask + return expanded_4d_mask + + def to_4d_export( + self, + attention_mask_2d, + query_length, + dtype, + key_value_length, + use_parallel=False, + parallel_step=3, + is_export=False, + ): + input_shape = (attention_mask_2d.shape[0], query_length) + + expanded_attn_mask = self._expand_mask_export( + attention_mask_2d, dtype, tgt_len=input_shape[-1] + ) + expanded_4d_mask = expanded_attn_mask + + return expanded_4d_mask + + def _expand_mask(self, mask, dtype, tgt_len=None): + """ + Expands attention_mask from `[bsz, seq_len]` to `[bsz, 1, tgt_seq_len, src_seq_len]`. + """ + bsz, src_len = mask.shape + tgt_len = tgt_len if tgt_len is not None else src_len + expanded_mask = ( + mask[:, None, None, :].expand([bsz, 1, tgt_len, src_len]).cast(dtype) + ) + + inverted_mask = 1.0 - expanded_mask + + return inverted_mask.masked_fill_( + inverted_mask.cast(paddle.bool), paddle.finfo(dtype).min + ) + + def _expand_mask_export(self, mask, dtype, tgt_len=None): + """ + Expands attention_mask from `[bsz, seq_len]` to `[bsz, 1, tgt_seq_len, src_seq_len]`. + """ + bsz, src_len = paddle.shape(mask) + expanded_mask = ( + mask[:, None, None, :].expand([bsz, 1, tgt_len, src_len]).cast(dtype) + ) + paddle.jit.api.set_dynamic_shape(expanded_mask, [-1, -1, -1, -1]) + inverted_mask = 1.0 - expanded_mask + return inverted_mask.masked_fill_( + inverted_mask.cast(paddle.bool), paddle.finfo(dtype).min + ) + + +def _prepare_4d_attention_mask(mask, dtype, tgt_len=None): + return AttentionMaskConverter._expand_mask(mask=mask, dtype=dtype, tgt_len=tgt_len) + + +def _prepare_4d_causal_attention_mask( + attention_mask, + input_shape, + inputs_embeds, + past_key_values_length, + sliding_window=None, + use_parallel=False, + parallel_step=3, + is_export=False, +): + """ + Creates a causal 4D mask of shape `(batch_size, 1, query_length, key_value_length)` from a 2D mask of shape + `(batch_size, key_value_length)` + + Args: + attention_mask (`paddle.Tensor` or `None`): + A 2D attention mask of shape `(batch_size, key_value_length)` + input_shape (`tuple(int)` or `list(int)` or `paddle.Size`): + The input shape should be a tuple that defines `(batch_size, query_length)`. + inputs_embeds (`paddle.Tensor`): + The embedded inputs as a paddle Tensor. + past_key_values_length (`int`): + The length of the key value cache. + sliding_window (`int`, *optional*): + If the model uses windowed attention, a sliding window should be passed. + """ + attn_mask_converter = AttentionMaskConverter( + is_causal=True, sliding_window=sliding_window + ) + + key_value_length = input_shape[-1] + past_key_values_length + + # 4d mask is passed through the layers + if attention_mask is not None and len(attention_mask.shape) == 2: + attention_mask = attn_mask_converter.to_4d( + attention_mask, + input_shape[-1], + key_value_length=key_value_length, + dtype=inputs_embeds.dtype, + use_parallel=use_parallel, + parallel_step=parallel_step, + is_export=is_export, + ) + elif attention_mask is not None and len(attention_mask.shape) == 4: + expected_shape = (input_shape[0], 1, input_shape[1], key_value_length) + if tuple(attention_mask.shape) != expected_shape: + raise ValueError( + f"Incorrect 4D attention_mask shape: {tuple(attention_mask.shape)}; expected: {expected_shape}." + ) + else: + # if the 4D mask has correct shape - invert it and fill with negative infinity + inverted_mask = 1.0 - attention_mask + attention_mask = inverted_mask.masked_fill_( + inverted_mask.to(paddle.bool), paddle.finfo(inputs_embeds.dtype).min + ) + else: + attention_mask = attn_mask_converter.to_causal_4d( + input_shape[0], + input_shape[-1], + key_value_length, + dtype=inputs_embeds.dtype, + ) + + return attention_mask + + +def _prepare_4d_causal_attention_mask_export( + attention_mask, + input_shape, + inputs_embeds, + past_key_values_length, + sliding_window=None, + use_parallel=False, + parallel_step=3, + is_export=False, +): + """ + Prepare a 4D causal attention mask for export. + + This function prepares a 4-dimensional causal attention mask, which is used to ensure that each position in the + sequence can only attend to previous positions. It is specifically designed to handle scenarios where the model + is being exported, potentially with additional options like sliding window or parallel processing. + + Args: + attention_mask: The initial attention mask, typically used to avoid attending to padding tokens. + input_shape: Shape of the input tensor, usually in the form (batch_size, sequence_length). + inputs_embeds: Embeddings of the input sequence, used to derive certain dimensions if needed. + past_key_values_length: Length of past key values, used in contexts like transformer decoders with caching. + sliding_window: Optional parameter. If provided, specifies the size of a sliding window for local attention. + use_parallel: Flag indicating whether to use parallel processing for attention computation. + parallel_step: Number of steps to use in parallel processing, relevant if `use_parallel` is True. + is_export: Flag indicating whether the attention mask is being prepared for model export. + + Returns: + A 4D causal attention mask suitable for use in transformer models, ensuring correct causal masking. + """ + attn_mask_converter = AttentionMaskConverter( + is_causal=True, sliding_window=sliding_window + ) + key_value_length = input_shape[-1] + past_key_values_length + + shape = attention_mask.shape + len_shape = len(shape) + + attention_mask = attn_mask_converter.to_4d_export( + attention_mask, + input_shape[-1], + key_value_length=key_value_length, + dtype=inputs_embeds.dtype, + use_parallel=use_parallel, + parallel_step=parallel_step, + is_export=is_export, + ) + return attention_mask + + +class CustomMBartDecoder(MBartDecoder): + def __init__(self, config): + super().__init__(config) + hidden_size = config.d_model + self.is_export = config.is_export + self.config_decoder = config + + def forward( + self, + input_ids=None, + attention_mask=None, + encoder_hidden_states=None, + encoder_attention_mask=None, + head_mask=None, + cross_attn_head_mask=None, + past_key_values=None, + inputs_embeds=None, + use_cache=None, + output_attentions=None, + output_hidden_states=None, + return_dict=None, + ): + self.is_export = False if self.training else True + + output_attentions = ( + output_attentions + if output_attentions is not None + else self.config.output_attentions + ) + output_hidden_states = ( + output_hidden_states + if output_hidden_states is not None + else self.config.output_hidden_states + ) + use_cache = use_cache if use_cache is not None else self.config.use_cache + return_dict = ( + return_dict if return_dict is not None else self.config.use_return_dict + ) + + # retrieve input_ids and inputs_embeds + if input_ids is not None and inputs_embeds is not None: + raise ValueError( + "You cannot specify both decoder_input_ids and decoder_inputs_embeds at the same time" + ) + elif input_ids is not None: + input = input_ids + input_shape = input.shape + input_ids = input_ids.reshape([-1, input_shape[-1]]) + elif inputs_embeds is not None: + input_shape = inputs_embeds.shape[:-1] + input = inputs_embeds[:, :, -1] + else: + raise ValueError( + "You have to specify either decoder_input_ids or decoder_inputs_embeds" + ) + + # past_key_values_length + past_key_values_length = ( + past_key_values[0][0].shape[2] if past_key_values is not None else 0 + ) + + if inputs_embeds is None: + inputs_embeds = self.embed_tokens(input_ids) * self.embed_scale + + if self._use_flash_attention_2: + # 2d mask is passed through the layers + attention_mask = ( + attention_mask + if (attention_mask is not None and 0 in attention_mask) + else None + ) + else: + # 4d mask is passed through the layers + if self.is_export: + attention_mask = _prepare_4d_causal_attention_mask_export( + attention_mask, + input_shape, + inputs_embeds, + past_key_values_length, + use_parallel=self.config_decoder.use_parallel, + parallel_step=self.config_decoder.parallel_step, + is_export=self.is_export, + ) + else: + attention_mask = _prepare_4d_causal_attention_mask( + attention_mask, + input_shape, + inputs_embeds, + past_key_values_length, + use_parallel=self.config_decoder.use_parallel, + parallel_step=self.config_decoder.parallel_step, + is_export=self.is_export, + ) + + # expand encoder attention mask + if encoder_hidden_states is not None and encoder_attention_mask is not None: + if self._use_flash_attention_2: + encoder_attention_mask = ( + encoder_attention_mask if 0 in encoder_attention_mask else None + ) + else: + # [bsz, seq_len] -> [bsz, 1, tgt_seq_len, src_seq_len] + encoder_attention_mask = _prepare_4d_attention_mask( + encoder_attention_mask, inputs_embeds.dtype, tgt_len=input_shape[-1] + ) + + # embed positions + positions = self.embed_positions(input, past_key_values_length) + + hidden_states = inputs_embeds + positions + + hidden_states = self.layernorm_embedding(hidden_states) + hidden_states = nn.functional.dropout( + hidden_states, p=self.dropout, training=self.training + ) + if self.gradient_checkpointing and self.training: + if use_cache: + print( + "`use_cache=True` is incompatible with gradient checkpointing`. Setting `use_cache=False`..." + ) + use_cache = False + + # decoder layers + all_hidden_states = () if output_hidden_states else None + all_self_attns = () if output_attentions else None + all_cross_attentions = ( + () if (output_attentions and encoder_hidden_states is not None) else None + ) + next_decoder_cache = () if use_cache else None + + # check if head_mask/cross_attn_head_mask has a correct number of layers specified if desired + for attn_mask, mask_name in zip( + [head_mask, cross_attn_head_mask], ["head_mask", "cross_attn_head_mask"] + ): + if attn_mask is not None: + if attn_mask.size()[0] != len(self.layers): + raise ValueError( + f"The `{mask_name}` should be specified for {len(self.layers)} layers, but it is for" + f" {attn_mask.size()[0]}." + ) + for idx, decoder_layer in enumerate(self.layers): + # add LayerDrop (see https://arxiv.org/abs/1909.11556 for description) + if output_hidden_states: + all_hidden_states += (hidden_states,) + if self.training: + dropout_probability = paddle.rand([]) + if dropout_probability < self.layerdrop: + continue + + past_key_value = ( + past_key_values[idx] if past_key_values is not None else None + ) + if self.gradient_checkpointing and self.training: + layer_outputs = self._gradient_checkpointing_func( + decoder_layer.__call__, + hidden_states, + attention_mask, + encoder_hidden_states, + encoder_attention_mask, + head_mask[idx] if head_mask is not None else None, + ( + cross_attn_head_mask[idx] + if cross_attn_head_mask is not None + else None + ), + None, + output_attentions, + use_cache, + ) + else: + layer_outputs = decoder_layer( + hidden_states, + attention_mask=attention_mask, + encoder_hidden_states=encoder_hidden_states, + encoder_attention_mask=encoder_attention_mask, + layer_head_mask=(head_mask[idx] if head_mask is not None else None), + cross_attn_layer_head_mask=( + cross_attn_head_mask[idx] + if cross_attn_head_mask is not None + else None + ), + past_key_value=past_key_value, + output_attentions=output_attentions, + use_cache=use_cache, + ) + hidden_states = layer_outputs[0] + + if self.is_export: + next_decoder_cache += (layer_outputs[3 if output_attentions else 1],) + else: + if use_cache: + next_decoder_cache += ( + layer_outputs[3 if output_attentions else 1], + ) + + if output_attentions: + all_self_attns += (layer_outputs[1],) + + if encoder_hidden_states is not None: + all_cross_attentions += (layer_outputs[2],) + + hidden_states = self.layer_norm(hidden_states) + + # add hidden states from the last decoder layer + if output_hidden_states: + all_hidden_states += (hidden_states,) + + if self.is_export: + next_cache = next_decoder_cache + else: + next_cache = next_decoder_cache if use_cache else None + if not return_dict: + return tuple( + v + for v in [ + hidden_states, + next_cache, + all_hidden_states, + all_self_attns, + all_cross_attentions, + ] + if v is not None + ) + + return BaseModelOutputWithPastAndCrossAttentions( + last_hidden_state=hidden_states, + past_key_values=next_cache, + hidden_states=all_hidden_states, + attentions=all_self_attns, + cross_attentions=all_cross_attentions, + ) + + +class CustomMBartForCausalLM(MBartForCausalLM): + def __init__(self, config): + super().__init__(config) + # Modify the decoder within MBartDecoderWrapper + self.model.decoder = CustomMBartDecoder(config) + + def forward( + self, + input_ids=None, + attention_mask=None, + encoder_hidden_states=None, + encoder_attention_mask=None, + head_mask=None, + cross_attn_head_mask=None, + past_key_values=None, + inputs_embeds=None, + labels=None, + use_cache=None, + output_attentions=None, + output_hidden_states=None, + return_dict=None, + ): + output_attentions = ( + output_attentions + if output_attentions is not None + else self.config.output_attentions + ) + output_hidden_states = ( + output_hidden_states + if output_hidden_states is not None + else self.config.output_hidden_states + ) + return_dict = ( + return_dict if return_dict is not None else self.config.use_return_dict + ) + + # decoder outputs consists of (dec_features, layer_state, dec_hidden, dec_attn) + outputs = self.model.decoder( + input_ids=input_ids, + attention_mask=attention_mask, + encoder_hidden_states=encoder_hidden_states, + encoder_attention_mask=encoder_attention_mask, + head_mask=head_mask, + cross_attn_head_mask=cross_attn_head_mask, + past_key_values=past_key_values, + inputs_embeds=inputs_embeds, + use_cache=use_cache, + output_attentions=output_attentions, + output_hidden_states=output_hidden_states, + return_dict=return_dict, + ) + logits = self.lm_head(outputs[0]) + + return CausalLMOutputWithCrossAttentions( + logits=logits, + past_key_values=outputs.past_key_values, + hidden_states=outputs.hidden_states, + attentions=outputs.attentions, + cross_attentions=outputs.cross_attentions, + ) + + +class PPFormulaNet_Head(UniMERNetHead): + """ + PPFormulaNet_Head + Args: + max_new_tokens (int): Maximum number of new tokens to generate. Default is 1536. + decoder_start_token_id (int): Start token ID for the decoder. Default is 0. + temperature (float): Temperature parameter for controlling randomness in sampling. Default is 0.2. + do_sample (bool): Flag to determine whether to use sampling for generation. Default is False. + top_p (float): Top-p (nucleus) sampling parameter for controlling diversity. Default is 0.95. + in_channels (int): Number of input channels for the model. Default is 1024. + decoder_layers (int): Number of layers in the decoder. Default is 8. + encoder_hidden_size (int): Size of the hidden layer in the encoder. Default is 1024. + decoder_ffn_dim (int): Dimension of the feed-forward network in the decoder. Default is 4096. + decoder_hidden_size (int): Size of the hidden layer in the decoder. Default is 1024. + is_export (bool): Flag indicating whether the model is to be exported. Default is False. + length_aware (bool): Flag to determine if the model should be aware of input sequence length. Default is True. + use_parallel (bool): Flag to enable or disable parallel processing. Default is False. + parallel_step (int): Number of steps to use in parallel processing. Default is 3. + """ + + def __init__( + self, + max_new_tokens=1536, + decoder_start_token_id=0, + temperature=0.2, + do_sample=False, + top_p=0.95, + in_channels=1024, + decoder_layers=8, + encoder_hidden_size=1024, + decoder_ffn_dim=4096, + decoder_hidden_size=1024, + is_export=False, + length_aware=True, + use_parallel=False, + parallel_step=3, + ): + + super().__init__() + + mbart_config_dict = { + "activation_dropout": 0.0, + "activation_function": "gelu", + "add_cross_attention": True, + "add_final_layer_norm": True, + "attention_dropout": 0.0, + "bos_token_id": 0, + "classifier_dropout": 0.0, + "d_model": decoder_hidden_size, + "decoder_attention_heads": 16, + "decoder_ffn_dim": decoder_ffn_dim, + "decoder_layerdrop": 0.0, + "decoder_layers": decoder_layers, + "dropout": 0.1, + "encoder_attention_heads": 16, + "encoder_ffn_dim": 4096, + "encoder_layerdrop": 0.0, + "encoder_layers": 12, + "eos_token_id": 2, + "forced_eos_token_id": 2, + "init_std": 0.02, + "is_decoder": True, + "is_encoder_decoder": False, + "output_hidden_states": False, + "max_position_embeddings": ( + max_new_tokens + parallel_step if use_parallel else max_new_tokens + ), + "model_type": "mbart", + "num_hidden_layers": 12, + "pad_token_id": 1, + "scale_embedding": True, + "tie_word_embeddings": False, + "transformers_version": "4.40.0", + "use_cache": True, + "use_return_dict": True, + "vocab_size": 50000, + "_attn_implementation": "eager", + "hidden_size": decoder_hidden_size, + "use_parallel": use_parallel, + "parallel_step": int(parallel_step), + "is_export": is_export, + } + self.decoder_start_token_id = decoder_start_token_id + self.temperature = temperature + self.do_sample = do_sample + self.top_p = top_p + self.is_export = is_export + self.max_seq_len = max_new_tokens + self.config_decoder = MBartConfig(**mbart_config_dict) + self.encoder_hidden_size = encoder_hidden_size + self.decoder = CustomMBartForCausalLM(self.config_decoder) + if self.config_decoder.hidden_size != self.encoder_hidden_size: + self.enc_to_dec_proj = nn.Linear( + self.encoder_hidden_size, self.config_decoder.hidden_size + ) + generation_config = { + "max_length": 1537, + "forced_eos_token_id": 2, + } + self.eos_token_id = generation_config["forced_eos_token_id"] + self.pad_token_id = self.config_decoder.pad_token_id + self.logits_processor = LogitsProcessorList() + self.logits_processor.append( + ForcedEOSTokenLogitsProcessor( + generation_config["max_length"], + generation_config["forced_eos_token_id"], + ) + ) + + def prepare_inputs_for_generation( + self, + input_ids, + past_key_values=None, + attention_mask=None, + use_cache=None, + encoder_outputs=None, + **kwargs, + ): + decoder_inputs = self.prepare_inputs_for_generation_mbart( + input_ids, past_key_values=past_key_values + ) + decoder_attention_mask = ( + decoder_inputs["attention_mask"] + if "attention_mask" in decoder_inputs + else None + ) + input_dict = { + "attention_mask": attention_mask, + "decoder_attention_mask": decoder_attention_mask, + "decoder_input_ids": decoder_inputs["input_ids"], + "past_key_values": decoder_inputs["past_key_values"], + "use_cache": use_cache, + } + return input_dict + + def _extract_past_from_model_output( + self, outputs: ModelOutput, standardize_cache_format: bool = False + ): + past_key_values = None + if "past_key_values" in outputs: + past_key_values = outputs.past_key_values + elif "mems" in outputs: + past_key_values = outputs.mems + elif "past_buckets_states" in outputs: + past_key_values = outputs.past_buckets_states + return past_key_values + + def _update_model_kwargs_for_generation( + self, + outputs: ModelOutput, + model_kwargs: Dict[str, Any], + is_encoder_decoder: bool = False, + standardize_cache_format: bool = False, + ) -> Dict[str, Any]: + # update past_key_values + model_kwargs["past_key_values"] = self._extract_past_from_model_output( + outputs, standardize_cache_format=standardize_cache_format + ) + if getattr(outputs, "state", None) is not None: + model_kwargs["state"] = outputs.state + + # update token_type_ids with last value + if "token_type_ids" in model_kwargs: + token_type_ids = model_kwargs["token_type_ids"] + model_kwargs["token_type_ids"] = paddle.concat( + [token_type_ids, token_type_ids[:, -1].unsqueeze(-1)], axis=-1 + ) + + if not is_encoder_decoder: + # update attention mask + if "attention_mask" in model_kwargs: + attention_mask = model_kwargs["attention_mask"] + model_kwargs["attention_mask"] = paddle.concat( + [ + attention_mask, + attention_mask.new_ones((attention_mask.shape[0], 1)), + ], + axis=-1, + ) + else: + # update decoder attention mask + if "decoder_attention_mask" in model_kwargs: + decoder_attention_mask = model_kwargs["decoder_attention_mask"] + model_kwargs["decoder_attention_mask"] = paddle.concat( + [ + decoder_attention_mask, + decoder_attention_mask.new_ones( + (decoder_attention_mask.shape[0], 1) + ), + ], + dim=-1, + ) + + if ( + "cache_position" in model_kwargs + and model_kwargs["cache_position"] is not None + ): + model_kwargs["cache_position"] = model_kwargs["cache_position"][-1:] + 1 + return model_kwargs + + def stopping_criteria(self, input_ids): + if self.is_export: + return input_ids[:, -1] == paddle.to_tensor([self.eos_token_id]) + is_done = paddle.isin(input_ids[:, -1], paddle.to_tensor([self.eos_token_id])) + return is_done + + def stopping_criteria_parallel(self, input_ids): + parallel_step = self.config_decoder.parallel_step + + if self.is_export: + is_done_list = [] + for i in range(parallel_step, 0, -1): + cur_is_done = input_ids[:, -i] == paddle.to_tensor([self.eos_token_id]) + is_done_list.append(cur_is_done) + is_done_list = paddle.to_tensor(is_done_list).transpose([1, 0]) + return is_done_list + else: + is_done = paddle.isin( + input_ids[:, -parallel_step:], + paddle.to_tensor([self.eos_token_id]).reshape([1, 1]), + ) + return paddle.to_tensor(is_done) + + def generate_single_iter( + self, + decoder_input_ids=None, + decoder_attention_mask=None, + encoder_outputs=None, + past_key_values=None, + decoder_inputs_embeds=None, + labels=None, + use_cache=None, + output_attentions=None, + output_hidden_states=None, + return_dict=None, + **kwargs, + ): + + encoder_hidden_states = encoder_outputs[0] + if self.config_decoder.hidden_size != self.encoder_hidden_size: + encoder_hidden_states = self.enc_to_dec_proj(encoder_hidden_states) + kwargs_decoder = {} + decoder_outputs = self.decoder( + input_ids=decoder_input_ids, + attention_mask=decoder_attention_mask, + encoder_hidden_states=encoder_hidden_states, + encoder_attention_mask=None, + inputs_embeds=None, + output_attentions=False, + output_hidden_states=output_hidden_states, + use_cache=use_cache, + past_key_values=past_key_values, + return_dict=return_dict, + **kwargs_decoder, + ) + + return Seq2SeqLMOutput( + loss=None, + logits=decoder_outputs.logits, + past_key_values=decoder_outputs.past_key_values, + decoder_hidden_states=decoder_outputs.hidden_states, + decoder_attentions=decoder_outputs.attentions, + cross_attentions=decoder_outputs.cross_attentions, + encoder_last_hidden_state=encoder_outputs.last_hidden_state, + encoder_hidden_states=encoder_outputs.hidden_states, + encoder_attentions=encoder_outputs.attentions, + ) + + def _prepare_decoder_input_ids_for_generation( + self, + batch_size, + model_kwargs, + decoder_start_token_id=None, + bos_token_id=None, + ): + + # 1. Check whether the user has defined `decoder_input_ids` manually. To facilitate in terms of input naming, + # we also allow the user to pass it under `input_ids`, if the encoder does not use it as the main input. + if model_kwargs is not None and "decoder_input_ids" in model_kwargs: + decoder_input_ids = model_kwargs.pop("decoder_input_ids") + elif "input_ids" in model_kwargs: + decoder_input_ids = model_kwargs.pop("input_ids") + else: + decoder_input_ids = None + + # 2. Encoder-decoder models expect the `decoder_input_ids` to start with a special token. Let's ensure that. + decoder_start_token_id = self._get_decoder_start_token_id( + decoder_start_token_id, bos_token_id + ) + + if isinstance(decoder_start_token_id, list): + if len(decoder_start_token_id) != batch_size: + raise ValueError( + f"`decoder_start_token_id` expected to have length {batch_size} but got {len(decoder_start_token_id)}" + ) + decoder_input_ids_start = paddle.to_tensor( + decoder_start_token_id, + dtype=paddle.int64, + ) + decoder_input_ids_start = decoder_input_ids_start.view(-1, 1) + else: + use_parallel = self.config_decoder.use_parallel + parallel_step = self.config_decoder.parallel_step + + if use_parallel: + decoder_input_ids_start = ( + paddle.ones( + (batch_size, parallel_step), + dtype=paddle.int64, + ) + * decoder_start_token_id + ) + else: + decoder_input_ids_start = ( + paddle.ones( + (batch_size, 1), + dtype=paddle.int64, + ) + * decoder_start_token_id + ) + # no user input -> use decoder_start_token_id as decoder_input_ids + if decoder_input_ids is None: + decoder_input_ids = decoder_input_ids_start + # exception: Donut checkpoints have task-specific decoder starts and don't expect a BOS token + elif ( + self.config.model_type == "vision-encoder-decoder" + and "donut" in self.name_or_path.lower() + ): + pass + elif self.config.model_type in ["whisper"]: + pass + # user input but doesn't start with decoder_start_token_id -> prepend decoder_start_token_id (and adjust + # decoder_attention_mask if provided) + elif ( + isinstance(decoder_start_token_id, int) + and (decoder_input_ids[:, 0] != decoder_start_token_id).all().item() + ) or ( + isinstance(decoder_start_token_id, paddle.Tensor) + and (decoder_input_ids[:, 0] != decoder_start_token_id[:, 0]).all().item() + ): + decoder_input_ids = paddle.concat( + [decoder_input_ids_start, decoder_input_ids], axis=-1 + ) + if "decoder_attention_mask" in model_kwargs: + decoder_attention_mask = model_kwargs["decoder_attention_mask"] + decoder_attention_mask = paddle.cat( + ( + paddle.ones_like(decoder_attention_mask)[:, :1], + decoder_attention_mask, + ), + dim=-1, + ) + model_kwargs["decoder_attention_mask"] = decoder_attention_mask + + return decoder_input_ids, model_kwargs + + @paddle.no_grad() + def generate_export( + self, + encoder_outputs, + model_kwargs, + ): + use_parallel = self.config_decoder.use_parallel + parallel_step = self.config_decoder.parallel_step + batch_size = encoder_outputs["last_hidden_state"].shape[0] + generation_config = { + "decoder_start_token_id": 0, + "bos_token_id": 0, + } + input_ids, model_kwargs = self._prepare_decoder_input_ids_for_generation( + batch_size=batch_size, + model_kwargs=model_kwargs, + decoder_start_token_id=generation_config["decoder_start_token_id"], + bos_token_id=generation_config["bos_token_id"], + ) + if not use_parallel: + input_ids = input_ids.reshape([-1, 1]) + decoder_input_ids = input_ids + model_kwargs["key use_cache"] = True + batch_size, cur_len = input_ids.shape + + if "inputs_embeds" in model_kwargs: + cur_len = model_kwargs["inputs_embeds"].shape[1] + + cache_position = paddle.arange(cur_len) + pad_token_id = self.pad_token_id + eos_token_id = [self.eos_token_id] + eos_token = self.eos_token_id + if use_parallel: + unfinished_sequences = paddle.ones( + [batch_size, parallel_step], dtype=paddle.int64 + ) + parallel_length = math.ceil(self.max_seq_len // parallel_step) + else: + unfinished_sequences = paddle.ones(batch_size, dtype=paddle.int64) + parallel_length = self.max_seq_len + + i_idx = paddle.full([], 0) + past_key_values = [] + decoder_attention_heads = self.config_decoder.decoder_attention_heads + decoder_attention_heads_dim = int( + self.config_decoder.d_model / decoder_attention_heads + ) + for i in range(self.config_decoder.decoder_layers): + init_arr = paddle.zeros( + [batch_size, decoder_attention_heads, 0, decoder_attention_heads_dim] + ) + paddle.jit.api.set_dynamic_shape(init_arr, [-1, -1, -1, -1]) + cache = (init_arr, init_arr, init_arr, init_arr) + past_key_values.append(cache) + + while i_idx < paddle.to_tensor(parallel_length): + + model_inputs = self.prepare_inputs_for_generation_export( + past_key_values=past_key_values, **model_kwargs + ) + decoder_attention_mask = paddle.ones(paddle.shape(input_ids)) + paddle.jit.api.set_dynamic_shape(decoder_input_ids, [-1, -1]) + paddle.jit.api.set_dynamic_shape(decoder_attention_mask, [-1, -1]) + + outputs = self.generate_single_iter( + decoder_input_ids=decoder_input_ids, + decoder_attention_mask=decoder_attention_mask, + encoder_outputs=encoder_outputs, + past_key_values=past_key_values, + return_dict=True, + output_attentions=False, + output_hidden_states=False, + ) + + if use_parallel: + next_token_logits = outputs.logits[:, -parallel_step:, :] + else: + next_token_logits = outputs.logits[:, -1, :] + next_tokens_scores = self.logits_processor(input_ids, next_token_logits) + next_tokens = paddle.argmax(next_tokens_scores, axis=-1) + + if eos_token_id is not None: + # False + if pad_token_id is None: + raise ValueError( + "If `eos_token_id` is defined, make sure that `pad_token_id` is defined." + ) + next_tokens = next_tokens * unfinished_sequences + pad_token_id * ( + 1 - unfinished_sequences + ) + if use_parallel: + input_ids = paddle.concat([input_ids, next_tokens], axis=-1) + decoder_input_ids = next_tokens + else: + input_ids = paddle.concat( + [input_ids, next_tokens.unsqueeze(1)], axis=-1 + ) + decoder_input_ids = next_tokens.unsqueeze(1) + + past_length = past_key_values[0][0].shape[2] + + past_key_values = outputs.past_key_values + cache_position = cache_position[-1:] + 1 + if use_parallel: + unfinished_sequences = ( + unfinished_sequences + & ~self.stopping_criteria_parallel(input_ids).cast(paddle.int64) + ) + else: + unfinished_sequences = unfinished_sequences & ~self.stopping_criteria( + input_ids + ).cast(paddle.int64) + + if ( + eos_token is not None + and ( + paddle.cumsum((input_ids == eos_token).cast(paddle.int64), 1)[:, -1] + >= 1 + ).all() + ): + break + i_idx += 1 + # break + + return input_ids + + @paddle.no_grad() + def generate( + self, + encoder_outputs, + model_kwargs, + ): + """ + Generate sequences from the model without computing gradients. + + This method is used to generate sequences from the model based on the given encoder outputs. + It does not compute gradients, making it suitable for inference. + + Args: + encoder_outputs: The outputs from the encoder, typically including hidden states necessary for generation. + model_kwargs: Additional keyword arguments that may include parameters such as maximum length, + temperature, top-k/top-p sampling parameters, and other generation-specific settings. + + Returns: + Generated sequences based on the encoder outputs and specified generation parameters. + """ + use_parallel = self.config_decoder.use_parallel + parallel_step = self.config_decoder.parallel_step + batch_size = encoder_outputs["last_hidden_state"].shape[0] + generation_config = { + "decoder_start_token_id": 0, + "bos_token_id": 0, + } + + input_ids, model_kwargs = self._prepare_decoder_input_ids_for_generation( + batch_size=batch_size, + model_kwargs=model_kwargs, + decoder_start_token_id=generation_config["decoder_start_token_id"], + bos_token_id=generation_config["bos_token_id"], + ) + + decoder_input_ids = input_ids + model_kwargs["key use_cache"] = True + batch_size, cur_len = input_ids.shape + + if "inputs_embeds" in model_kwargs: + cur_len = model_kwargs["inputs_embeds"].shape[1] + model_kwargs["cache_position"] = paddle.arange(cur_len) + pad_token_id = self.pad_token_id + eos_token_id = [self.eos_token_id] + eos_token = self.eos_token_id + if use_parallel: + unfinished_sequences = paddle.ones( + [batch_size, parallel_step], dtype=paddle.int64 + ) + parallel_length = math.ceil(self.max_seq_len // parallel_step) + else: + unfinished_sequences = paddle.ones(batch_size, dtype=paddle.int64) + parallel_length = self.max_seq_len + past_key_values = [] + + for idx in range(parallel_length): + + model_inputs = self.prepare_inputs_for_generation(input_ids, **model_kwargs) + outputs = self.generate_single_iter( + **model_inputs, + encoder_outputs=encoder_outputs, + return_dict=True, + output_attentions=False, + output_hidden_states=False, + ) + + if use_parallel: + next_token_logits = outputs.logits[:, :, :] + else: + next_token_logits = outputs.logits[:, -1, :] + + next_tokens_scores = self.logits_processor(input_ids, next_token_logits) + next_tokens = paddle.argmax(next_tokens_scores, axis=-1) + if eos_token_id is not None: + # False + if pad_token_id is None: + raise ValueError( + "If `eos_token_id` is defined, make sure that `pad_token_id` is defined." + ) + next_tokens = next_tokens * unfinished_sequences + pad_token_id * ( + 1 - unfinished_sequences + ) + if use_parallel: + input_ids = paddle.concat([input_ids, next_tokens], axis=-1) + else: + input_ids = paddle.concat([input_ids, next_tokens[:, None]], axis=-1) + + model_kwargs = self._update_model_kwargs_for_generation( + outputs, + model_kwargs, + is_encoder_decoder=self.config_decoder.is_encoder_decoder, + ) + if use_parallel: + unfinished_sequences = ( + unfinished_sequences + & ~self.stopping_criteria_parallel(input_ids).cast(paddle.int64) + ) + else: + unfinished_sequences = unfinished_sequences & ~self.stopping_criteria( + input_ids + ).cast(paddle.int64) + + if ( + eos_token is not None + and ( + paddle.cumsum((input_ids == eos_token).cast(paddle.int64), 1)[:, -1] + >= 1 + ).all() + ): + break + return input_ids + + def forwad_train( + self, + encoder_outputs, + decoder_input_ids, + decoder_attention_mask, + past_key_values=None, + decoder_inputs_embeds=None, + labels=None, + use_cache=None, + output_attentions=None, + output_hidden_states=None, + return_dict=None, + **kwargs, + ): + """ + Forward pass for training the model. + + Args: + encoder_outputs: The outputs from the encoder, typically including hidden states. + decoder_input_ids: Input IDs for the decoder. + decoder_attention_mask: Attention mask for the decoder inputs to avoid attending to padding tokens. + past_key_values: Previously computed key and value states for the decoder, used for fast generation. + decoder_inputs_embeds: Optional embeddings for decoder inputs, used instead of decoder_input_ids if provided. + labels: Labels for computing the training loss. + use_cache: Whether to use a cache of past key values for faster generation. + output_attentions: Whether to output attention weights. + output_hidden_states: Whether to output hidden states of all layers. + return_dict: Whether to return the output as a dictionary. + **kwargs: Additional keyword arguments. + + Returns: + Depending on the `return_dict` flag, returns either a dictionary of model outputs or a tuple. + """ + if self.config_decoder.use_parallel: + batch = decoder_input_ids.shape[0] + add_sos_token = self.config_decoder.parallel_step - 1 + start_token = paddle.zeros([batch, add_sos_token]).cast(paddle.int64) + start_mask = paddle.ones([batch, add_sos_token]).cast(paddle.int64) + decoder_input_ids = paddle.concat([start_token, decoder_input_ids], axis=1) + decoder_attention_mask = paddle.concat( + [start_mask, decoder_attention_mask], axis=1 + ) + + labels = decoder_input_ids * 1 + labels = labels.masked_fill_(labels == self.pad_token_id, -100) + if self.config_decoder.use_parallel: + input_decoder_input_ids = decoder_input_ids[ + :, : -self.config_decoder.parallel_step + ] + input_decoder_attention_mask = decoder_attention_mask[ + :, : -self.config_decoder.parallel_step + ] + else: + input_decoder_input_ids = decoder_input_ids[:, :-1] + input_decoder_attention_mask = decoder_attention_mask[:, :-1] + + encoder_hidden_states = encoder_outputs[0] + kwargs_decoder = {} + if self.config_decoder.hidden_size != self.encoder_hidden_size: + encoder_hidden_states = self.enc_to_dec_proj(encoder_hidden_states) + + decoder_outputs = self.decoder( + input_ids=input_decoder_input_ids, + attention_mask=input_decoder_attention_mask, + encoder_hidden_states=encoder_hidden_states, + encoder_attention_mask=None, + inputs_embeds=None, + output_attentions=False, + output_hidden_states=output_hidden_states, + use_cache=use_cache, + past_key_values=past_key_values, + return_dict=return_dict, + **kwargs_decoder, + ) + + logits = decoder_outputs.logits + return logits, labels + + # forward for export + def forward(self, inputs, targets=None): + self.is_export = False if self.training else True + if not self.training: + encoder_outputs = inputs + model_kwargs = { + "output_attentions": False, + "output_hidden_states": False, + "use_cache": True, + } + if self.is_export: + word_pred = self.generate_export(encoder_outputs, model_kwargs) + else: + word_pred = self.generate(encoder_outputs, model_kwargs) + + return word_pred + encoder_outputs, tgt_seq, mask = inputs + logits, masked_labels = self.forwad_train(encoder_outputs, tgt_seq, mask) + + return logits, masked_labels diff --git a/modules/onnx_ocr_module/src/ppocr/modeling/heads/rec_pren_head.py b/modules/onnx_ocr_module/src/ppocr/modeling/heads/rec_pren_head.py new file mode 100644 index 0000000..c9e4b3e --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/modeling/heads/rec_pren_head.py @@ -0,0 +1,34 @@ +# copyright (c) 2022 PaddlePaddle Authors. All Rights Reserve. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +from paddle import nn +from paddle.nn import functional as F + + +class PRENHead(nn.Layer): + def __init__(self, in_channels, out_channels, **kwargs): + super(PRENHead, self).__init__() + self.linear = nn.Linear(in_channels, out_channels) + + def forward(self, x, targets=None): + predicts = self.linear(x) + + if not self.training: + predicts = F.softmax(predicts, axis=2) + + return predicts diff --git a/modules/onnx_ocr_module/src/ppocr/modeling/heads/rec_rfl_head.py b/modules/onnx_ocr_module/src/ppocr/modeling/heads/rec_rfl_head.py new file mode 100644 index 0000000..bd7efd4 --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/modeling/heads/rec_rfl_head.py @@ -0,0 +1,106 @@ +# copyright (c) 2022 PaddlePaddle Authors. All Rights Reserve. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +""" +This code is refer from: +https://github.com/hikopensource/DAVAR-Lab-OCR/blob/main/davarocr/davar_rcg/models/sequence_heads/counting_head.py +""" +import paddle +import paddle.nn as nn +from paddle.nn.initializer import TruncatedNormal, Constant, Normal, KaimingNormal + +from .rec_att_head import AttentionLSTM + +kaiming_init_ = KaimingNormal() +zeros_ = Constant(value=0.0) +ones_ = Constant(value=1.0) + + +class CNTHead(nn.Layer): + def __init__(self, embed_size=512, encode_length=26, out_channels=38, **kwargs): + super(CNTHead, self).__init__() + + self.out_channels = out_channels + + self.Wv_fusion = nn.Linear(embed_size, embed_size, bias_attr=False) + self.Prediction_visual = nn.Linear( + encode_length * embed_size, self.out_channels + ) + + def forward(self, visual_feature): + b, c, h, w = visual_feature.shape + visual_feature = visual_feature.reshape([b, c, h * w]).transpose([0, 2, 1]) + visual_feature_num = self.Wv_fusion(visual_feature) # batch * 26 * 512 + b, n, c = visual_feature_num.shape + # using visual feature directly calculate the text length + visual_feature_num = visual_feature_num.reshape([b, n * c]) + prediction_visual = self.Prediction_visual(visual_feature_num) + + return prediction_visual + + +class RFLHead(nn.Layer): + def __init__( + self, + in_channels=512, + hidden_size=256, + batch_max_legnth=25, + out_channels=38, + use_cnt=True, + use_seq=True, + **kwargs, + ): + super(RFLHead, self).__init__() + assert use_cnt or use_seq + self.use_cnt = use_cnt + self.use_seq = use_seq + if self.use_cnt: + self.cnt_head = CNTHead( + embed_size=in_channels, + encode_length=batch_max_legnth + 1, + out_channels=out_channels, + **kwargs, + ) + if self.use_seq: + self.seq_head = AttentionLSTM( + in_channels=in_channels, + out_channels=out_channels, + hidden_size=hidden_size, + **kwargs, + ) + self.batch_max_legnth = batch_max_legnth + self.num_class = out_channels + self.apply(self.init_weights) + + def init_weights(self, m): + if isinstance(m, nn.Linear): + kaiming_init_(m.weight) + if isinstance(m, nn.Linear) and m.bias is not None: + zeros_(m.bias) + + def forward(self, x, targets=None): + cnt_inputs, seq_inputs = x + if self.use_cnt: + cnt_outputs = self.cnt_head(cnt_inputs) + else: + cnt_outputs = None + if self.use_seq: + if self.training: + seq_outputs = self.seq_head( + seq_inputs, targets[0], self.batch_max_legnth + ) + else: + seq_outputs = self.seq_head(seq_inputs, None, self.batch_max_legnth) + return cnt_outputs, seq_outputs + else: + return cnt_outputs diff --git a/modules/onnx_ocr_module/src/ppocr/modeling/heads/rec_robustscanner_head.py b/modules/onnx_ocr_module/src/ppocr/modeling/heads/rec_robustscanner_head.py new file mode 100644 index 0000000..5510ee8 --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/modeling/heads/rec_robustscanner_head.py @@ -0,0 +1,748 @@ +# copyright (c) 2022 PaddlePaddle Authors. All Rights Reserve. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +""" +This code is refer from: +https://github.com/open-mmlab/mmocr/blob/main/mmocr/models/textrecog/encoders/channel_reduction_encoder.py +https://github.com/open-mmlab/mmocr/blob/main/mmocr/models/textrecog/decoders/robust_scanner_decoder.py +""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import math +import paddle +from paddle import ParamAttr +import paddle.nn as nn +import paddle.nn.functional as F + + +class BaseDecoder(nn.Layer): + def __init__(self, **kwargs): + super().__init__() + + def forward_train(self, feat, out_enc, targets, img_metas): + raise NotImplementedError + + def forward_test(self, feat, out_enc, img_metas): + raise NotImplementedError + + def forward( + self, + feat, + out_enc, + label=None, + valid_ratios=None, + word_positions=None, + train_mode=True, + ): + self.train_mode = train_mode + + if train_mode: + return self.forward_train( + feat, out_enc, label, valid_ratios, word_positions + ) + return self.forward_test(feat, out_enc, valid_ratios, word_positions) + + +class ChannelReductionEncoder(nn.Layer): + """Change the channel number with a one by one convoluational layer. + + Args: + in_channels (int): Number of input channels. + out_channels (int): Number of output channels. + """ + + def __init__(self, in_channels, out_channels, **kwargs): + super(ChannelReductionEncoder, self).__init__() + + self.layer = nn.Conv2D( + in_channels, + out_channels, + kernel_size=1, + stride=1, + padding=0, + weight_attr=nn.initializer.XavierNormal(), + ) + + def forward(self, feat): + """ + Args: + feat (Tensor): Image features with the shape of + :math:`(N, C_{in}, H, W)`. + + Returns: + Tensor: A tensor of shape :math:`(N, C_{out}, H, W)`. + """ + return self.layer(feat) + + +def masked_fill(x, mask, value): + y = paddle.full(x.shape, value, x.dtype) + return paddle.where(mask, y, x) + + +class DotProductAttentionLayer(nn.Layer): + def __init__(self, dim_model=None): + super().__init__() + + self.scale = dim_model**-0.5 if dim_model is not None else 1.0 + + def forward(self, query, key, value, h, w, valid_ratios=None): + query = paddle.transpose(query, (0, 2, 1)) + logits = paddle.matmul(query, key) * self.scale + n, c, t = logits.shape + # reshape to (n, c, h, w) + logits = paddle.reshape(logits, [n, c, h, w]) + if valid_ratios is not None: + # cal mask of attention weight + with paddle.base.framework._stride_in_no_check_dy2st_diff(): + for i, valid_ratio in enumerate(valid_ratios): + valid_width = min(w, int(w * valid_ratio + 0.5)) + if valid_width < w: + logits[i, :, :, valid_width:] = float("-inf") + + # reshape to (n, c, h, w) + logits = paddle.reshape(logits, [n, c, t]) + weights = F.softmax(logits, axis=2) + value = paddle.transpose(value, (0, 2, 1)) + glimpse = paddle.matmul(weights, value) + glimpse = paddle.transpose(glimpse, (0, 2, 1)) + return glimpse + + +class SequenceAttentionDecoder(BaseDecoder): + """Sequence attention decoder for RobustScanner. + + RobustScanner: `RobustScanner: Dynamically Enhancing Positional Clues for + Robust Text Recognition `_ + + Args: + num_classes (int): Number of output classes :math:`C`. + rnn_layers (int): Number of RNN layers. + dim_input (int): Dimension :math:`D_i` of input vector ``feat``. + dim_model (int): Dimension :math:`D_m` of the model. Should also be the + same as encoder output vector ``out_enc``. + max_seq_len (int): Maximum output sequence length :math:`T`. + start_idx (int): The index of ``. + mask (bool): Whether to mask input features according to + ``img_meta['valid_ratio']``. + padding_idx (int): The index of ``. + dropout (float): Dropout rate. + return_feature (bool): Return feature or logits as the result. + encode_value (bool): Whether to use the output of encoder ``out_enc`` + as `value` of attention layer. If False, the original feature + ``feat`` will be used. + + Warning: + This decoder will not predict the final class which is assumed to be + ``. Therefore, its output size is always :math:`C - 1`. `` + is also ignored by loss as specified in + :obj:`mmocr.models.textrecog.recognizer.EncodeDecodeRecognizer`. + """ + + def __init__( + self, + num_classes=None, + rnn_layers=2, + dim_input=512, + dim_model=128, + max_seq_len=40, + start_idx=0, + mask=True, + padding_idx=None, + dropout=0, + return_feature=False, + encode_value=False, + ): + super().__init__() + + self.num_classes = num_classes + self.dim_input = dim_input + self.dim_model = dim_model + self.return_feature = return_feature + self.encode_value = encode_value + self.max_seq_len = max_seq_len + self.start_idx = start_idx + self.mask = mask + + self.embedding = nn.Embedding( + self.num_classes, self.dim_model, padding_idx=padding_idx + ) + + self.sequence_layer = nn.LSTM( + input_size=dim_model, + hidden_size=dim_model, + num_layers=rnn_layers, + time_major=False, + dropout=dropout, + ) + + self.attention_layer = DotProductAttentionLayer() + + self.prediction = None + if not self.return_feature: + pred_num_classes = num_classes - 1 + self.prediction = nn.Linear( + dim_model if encode_value else dim_input, pred_num_classes + ) + + def forward_train(self, feat, out_enc, targets, valid_ratios): + """ + Args: + feat (Tensor): Tensor of shape :math:`(N, D_i, H, W)`. + out_enc (Tensor): Encoder output of shape + :math:`(N, D_m, H, W)`. + targets (Tensor): a tensor of shape :math:`(N, T)`. Each element is the index of a + character. + valid_ratios (Tensor): valid length ratio of img. + Returns: + Tensor: A raw logit tensor of shape :math:`(N, T, C-1)` if + ``return_feature=False``. Otherwise it would be the hidden feature + before the prediction projection layer, whose shape is + :math:`(N, T, D_m)`. + """ + + tgt_embedding = self.embedding(targets) + + n, c_enc, h, w = out_enc.shape + assert c_enc == self.dim_model + _, c_feat, _, _ = feat.shape + assert c_feat == self.dim_input + _, len_q, c_q = tgt_embedding.shape + assert c_q == self.dim_model + assert len_q <= self.max_seq_len + + query, _ = self.sequence_layer(tgt_embedding) + query = paddle.transpose(query, (0, 2, 1)) + key = paddle.reshape(out_enc, [n, c_enc, h * w]) + if self.encode_value: + value = key + else: + value = paddle.reshape(feat, [n, c_feat, h * w]) + + attn_out = self.attention_layer(query, key, value, h, w, valid_ratios) + attn_out = paddle.transpose(attn_out, (0, 2, 1)) + + if self.return_feature: + return attn_out + + out = self.prediction(attn_out) + + return out + + def forward_test(self, feat, out_enc, valid_ratios): + """ + Args: + feat (Tensor): Tensor of shape :math:`(N, D_i, H, W)`. + out_enc (Tensor): Encoder output of shape + :math:`(N, D_m, H, W)`. + valid_ratios (Tensor): valid length ratio of img. + + Returns: + Tensor: The output logit sequence tensor of shape + :math:`(N, T, C-1)`. + """ + seq_len = self.max_seq_len + batch_size = feat.shape[0] + + decode_sequence = ( + paddle.ones((batch_size, seq_len), dtype="int64") * self.start_idx + ) + + outputs = [] + for i in range(seq_len): + step_out = self.forward_test_step( + feat, out_enc, decode_sequence, i, valid_ratios + ) + outputs.append(step_out) + max_idx = paddle.argmax(step_out, axis=1, keepdim=False) + if i < seq_len - 1: + decode_sequence[:, i + 1] = max_idx + + outputs = paddle.stack(outputs, 1) + + return outputs + + def forward_test_step( + self, feat, out_enc, decode_sequence, current_step, valid_ratios + ): + """ + Args: + feat (Tensor): Tensor of shape :math:`(N, D_i, H, W)`. + out_enc (Tensor): Encoder output of shape + :math:`(N, D_m, H, W)`. + decode_sequence (Tensor): Shape :math:`(N, T)`. The tensor that + stores history decoding result. + current_step (int): Current decoding step. + valid_ratios (Tensor): valid length ratio of img + + Returns: + Tensor: Shape :math:`(N, C-1)`. The logit tensor of predicted + tokens at current time step. + """ + + embed = self.embedding(decode_sequence) + + n, c_enc, h, w = out_enc.shape + assert c_enc == self.dim_model + _, c_feat, _, _ = feat.shape + assert c_feat == self.dim_input + _, _, c_q = embed.shape + assert c_q == self.dim_model + + query, _ = self.sequence_layer(embed) + query = paddle.transpose(query, (0, 2, 1)) + key = paddle.reshape(out_enc, [n, c_enc, h * w]) + if self.encode_value: + value = key + else: + value = paddle.reshape(feat, [n, c_feat, h * w]) + + # [n, c, l] + attn_out = self.attention_layer(query, key, value, h, w, valid_ratios) + out = attn_out[:, :, current_step] + + if self.return_feature: + return out + + out = self.prediction(out) + out = F.softmax(out, dim=-1) + + return out + + +class PositionAwareLayer(nn.Layer): + def __init__(self, dim_model, rnn_layers=2): + super().__init__() + + self.dim_model = dim_model + + self.rnn = nn.LSTM( + input_size=dim_model, + hidden_size=dim_model, + num_layers=rnn_layers, + time_major=False, + ) + + self.mixer = nn.Sequential( + nn.Conv2D(dim_model, dim_model, kernel_size=3, stride=1, padding=1), + nn.ReLU(), + nn.Conv2D(dim_model, dim_model, kernel_size=3, stride=1, padding=1), + ) + + def forward(self, img_feature): + n, c, h, w = img_feature.shape + rnn_input = paddle.transpose(img_feature, (0, 2, 3, 1)) + rnn_input = paddle.reshape(rnn_input, (n * h, w, c)) + rnn_output, _ = self.rnn(rnn_input) + rnn_output = paddle.reshape(rnn_output, (n, h, w, c)) + rnn_output = paddle.transpose(rnn_output, (0, 3, 1, 2)) + out = self.mixer(rnn_output) + return out + + +class PositionAttentionDecoder(BaseDecoder): + """Position attention decoder for RobustScanner. + + RobustScanner: `RobustScanner: Dynamically Enhancing Positional Clues for + Robust Text Recognition `_ + + Args: + num_classes (int): Number of output classes :math:`C`. + rnn_layers (int): Number of RNN layers. + dim_input (int): Dimension :math:`D_i` of input vector ``feat``. + dim_model (int): Dimension :math:`D_m` of the model. Should also be the + same as encoder output vector ``out_enc``. + max_seq_len (int): Maximum output sequence length :math:`T`. + mask (bool): Whether to mask input features according to + ``img_meta['valid_ratio']``. + return_feature (bool): Return feature or logits as the result. + encode_value (bool): Whether to use the output of encoder ``out_enc`` + as `value` of attention layer. If False, the original feature + ``feat`` will be used. + + Warning: + This decoder will not predict the final class which is assumed to be + ``. Therefore, its output size is always :math:`C - 1`. `` + is also ignored by loss + + """ + + def __init__( + self, + num_classes=None, + rnn_layers=2, + dim_input=512, + dim_model=128, + max_seq_len=40, + mask=True, + return_feature=False, + encode_value=False, + ): + super().__init__() + + self.num_classes = num_classes + self.dim_input = dim_input + self.dim_model = dim_model + self.max_seq_len = max_seq_len + self.return_feature = return_feature + self.encode_value = encode_value + self.mask = mask + + self.embedding = nn.Embedding(self.max_seq_len + 1, self.dim_model) + + self.position_aware_module = PositionAwareLayer(self.dim_model, rnn_layers) + + self.attention_layer = DotProductAttentionLayer() + + self.prediction = None + if not self.return_feature: + pred_num_classes = num_classes - 1 + self.prediction = nn.Linear( + dim_model if encode_value else dim_input, pred_num_classes + ) + + def _get_position_index(self, length, batch_size): + position_index_list = [] + for i in range(batch_size): + position_index = paddle.arange(0, end=length, step=1, dtype="int64") + position_index_list.append(position_index) + batch_position_index = paddle.stack(position_index_list, axis=0) + return batch_position_index + + def forward_train(self, feat, out_enc, targets, valid_ratios, position_index): + """ + Args: + feat (Tensor): Tensor of shape :math:`(N, D_i, H, W)`. + out_enc (Tensor): Encoder output of shape + :math:`(N, D_m, H, W)`. + targets (dict): A dict with the key ``padded_targets``, a + tensor of shape :math:`(N, T)`. Each element is the index of a + character. + valid_ratios (Tensor): valid length ratio of img. + position_index (Tensor): The position of each word. + + Returns: + Tensor: A raw logit tensor of shape :math:`(N, T, C-1)` if + ``return_feature=False``. Otherwise it will be the hidden feature + before the prediction projection layer, whose shape is + :math:`(N, T, D_m)`. + """ + n, c_enc, h, w = out_enc.shape + assert c_enc == self.dim_model + _, c_feat, _, _ = feat.shape + assert c_feat == self.dim_input + _, len_q = targets.shape + assert len_q <= self.max_seq_len + + position_out_enc = self.position_aware_module(out_enc) + + query = self.embedding(position_index) + query = paddle.transpose(query, (0, 2, 1)) + key = paddle.reshape(position_out_enc, (n, c_enc, h * w)) + if self.encode_value: + value = paddle.reshape(out_enc, (n, c_enc, h * w)) + else: + value = paddle.reshape(feat, (n, c_feat, h * w)) + + attn_out = self.attention_layer(query, key, value, h, w, valid_ratios) + attn_out = paddle.transpose(attn_out, (0, 2, 1)) # [n, len_q, dim_v] + + if self.return_feature: + return attn_out + + return self.prediction(attn_out) + + def forward_test(self, feat, out_enc, valid_ratios, position_index): + """ + Args: + feat (Tensor): Tensor of shape :math:`(N, D_i, H, W)`. + out_enc (Tensor): Encoder output of shape + :math:`(N, D_m, H, W)`. + valid_ratios (Tensor): valid length ratio of img + position_index (Tensor): The position of each word. + + Returns: + Tensor: A raw logit tensor of shape :math:`(N, T, C-1)` if + ``return_feature=False``. Otherwise it would be the hidden feature + before the prediction projection layer, whose shape is + :math:`(N, T, D_m)`. + """ + n, c_enc, h, w = out_enc.shape + assert c_enc == self.dim_model + _, c_feat, _, _ = feat.shape + assert c_feat == self.dim_input + + position_out_enc = self.position_aware_module(out_enc) + + query = self.embedding(position_index) + query = paddle.transpose(query, (0, 2, 1)) + key = paddle.reshape(position_out_enc, (n, c_enc, h * w)) + if self.encode_value: + value = paddle.reshape(out_enc, (n, c_enc, h * w)) + else: + value = paddle.reshape(feat, (n, c_feat, h * w)) + + attn_out = self.attention_layer(query, key, value, h, w, valid_ratios) + attn_out = paddle.transpose(attn_out, (0, 2, 1)) # [n, len_q, dim_v] + + if self.return_feature: + return attn_out + + return self.prediction(attn_out) + + +class RobustScannerFusionLayer(nn.Layer): + def __init__(self, dim_model, dim=-1): + super(RobustScannerFusionLayer, self).__init__() + + self.dim_model = dim_model + self.dim = dim + self.linear_layer = nn.Linear(dim_model * 2, dim_model * 2) + + def forward(self, x0, x1): + assert x0.shape == x1.shape + fusion_input = paddle.concat([x0, x1], self.dim) + output = self.linear_layer(fusion_input) + output = F.glu(output, self.dim) + return output + + +class RobustScannerDecoder(BaseDecoder): + """Decoder for RobustScanner. + + RobustScanner: `RobustScanner: Dynamically Enhancing Positional Clues for + Robust Text Recognition `_ + + Args: + num_classes (int): Number of output classes :math:`C`. + dim_input (int): Dimension :math:`D_i` of input vector ``feat``. + dim_model (int): Dimension :math:`D_m` of the model. Should also be the + same as encoder output vector ``out_enc``. + max_seq_len (int): Maximum output sequence length :math:`T`. + start_idx (int): The index of ``. + mask (bool): Whether to mask input features according to + ``img_meta['valid_ratio']``. + padding_idx (int): The index of ``. + encode_value (bool): Whether to use the output of encoder ``out_enc`` + as `value` of attention layer. If False, the original feature + ``feat`` will be used. + + Warning: + This decoder will not predict the final class which is assumed to be + ``. Therefore, its output size is always :math:`C - 1`. `` + is also ignored by loss as specified in + :obj:`mmocr.models.textrecog.recognizer.EncodeDecodeRecognizer`. + """ + + def __init__( + self, + num_classes=None, + dim_input=512, + dim_model=128, + hybrid_decoder_rnn_layers=2, + hybrid_decoder_dropout=0, + position_decoder_rnn_layers=2, + max_seq_len=40, + start_idx=0, + mask=True, + padding_idx=None, + encode_value=False, + ): + super().__init__() + self.num_classes = num_classes + self.dim_input = dim_input + self.dim_model = dim_model + self.max_seq_len = max_seq_len + self.encode_value = encode_value + self.start_idx = start_idx + self.padding_idx = padding_idx + self.mask = mask + + # init hybrid decoder + self.hybrid_decoder = SequenceAttentionDecoder( + num_classes=num_classes, + rnn_layers=hybrid_decoder_rnn_layers, + dim_input=dim_input, + dim_model=dim_model, + max_seq_len=max_seq_len, + start_idx=start_idx, + mask=mask, + padding_idx=padding_idx, + dropout=hybrid_decoder_dropout, + encode_value=encode_value, + return_feature=True, + ) + + # init position decoder + self.position_decoder = PositionAttentionDecoder( + num_classes=num_classes, + rnn_layers=position_decoder_rnn_layers, + dim_input=dim_input, + dim_model=dim_model, + max_seq_len=max_seq_len, + mask=mask, + encode_value=encode_value, + return_feature=True, + ) + + self.fusion_module = RobustScannerFusionLayer( + self.dim_model if encode_value else dim_input + ) + + pred_num_classes = num_classes - 1 + self.prediction = nn.Linear( + dim_model if encode_value else dim_input, pred_num_classes + ) + + def forward_train(self, feat, out_enc, target, valid_ratios, word_positions): + """ + Args: + feat (Tensor): Tensor of shape :math:`(N, D_i, H, W)`. + out_enc (Tensor): Encoder output of shape + :math:`(N, D_m, H, W)`. + target (dict): A dict with the key ``padded_targets``, a + tensor of shape :math:`(N, T)`. Each element is the index of a + character. + valid_ratios (Tensor): + word_positions (Tensor): The position of each word. + + Returns: + Tensor: A raw logit tensor of shape :math:`(N, T, C-1)`. + """ + hybrid_glimpse = self.hybrid_decoder.forward_train( + feat, out_enc, target, valid_ratios + ) + position_glimpse = self.position_decoder.forward_train( + feat, out_enc, target, valid_ratios, word_positions + ) + + fusion_out = self.fusion_module(hybrid_glimpse, position_glimpse) + + out = self.prediction(fusion_out) + + return out + + def forward_test(self, feat, out_enc, valid_ratios, word_positions): + """ + Args: + feat (Tensor): Tensor of shape :math:`(N, D_i, H, W)`. + out_enc (Tensor): Encoder output of shape + :math:`(N, D_m, H, W)`. + valid_ratios (Tensor): + word_positions (Tensor): The position of each word. + Returns: + Tensor: The output logit sequence tensor of shape + :math:`(N, T, C-1)`. + """ + seq_len = self.max_seq_len + batch_size = feat.shape[0] + + decode_sequence = ( + paddle.ones((batch_size, seq_len), dtype="int64") * self.start_idx + ) + + position_glimpse = self.position_decoder.forward_test( + feat, out_enc, valid_ratios, word_positions + ) + + outputs = [] + for i in range(seq_len): + hybrid_glimpse_step = self.hybrid_decoder.forward_test_step( + feat, out_enc, decode_sequence, i, valid_ratios + ) + + fusion_out = self.fusion_module( + hybrid_glimpse_step, position_glimpse[:, i, :] + ) + + char_out = self.prediction(fusion_out) + char_out = F.softmax(char_out, -1) + outputs.append(char_out) + max_idx = paddle.argmax(char_out, axis=1, keepdim=False) + if i < seq_len - 1: + decode_sequence[:, i + 1] = max_idx + + outputs = paddle.stack(outputs, 1) + + return outputs + + +class RobustScannerHead(nn.Layer): + def __init__( + self, + out_channels, # 90 + unknown + start + padding + in_channels, + enc_outchannles=128, + hybrid_dec_rnn_layers=2, + hybrid_dec_dropout=0, + position_dec_rnn_layers=2, + start_idx=0, + max_text_length=40, + mask=True, + padding_idx=None, + encode_value=False, + **kwargs, + ): + super(RobustScannerHead, self).__init__() + + # encoder module + self.encoder = ChannelReductionEncoder( + in_channels=in_channels, out_channels=enc_outchannles + ) + + # decoder module + self.decoder = RobustScannerDecoder( + num_classes=out_channels, + dim_input=in_channels, + dim_model=enc_outchannles, + hybrid_decoder_rnn_layers=hybrid_dec_rnn_layers, + hybrid_decoder_dropout=hybrid_dec_dropout, + position_decoder_rnn_layers=position_dec_rnn_layers, + max_seq_len=max_text_length, + start_idx=start_idx, + mask=mask, + padding_idx=padding_idx, + encode_value=encode_value, + ) + + def forward(self, inputs, targets=None): + """ + targets: [label, valid_ratio, word_positions] + """ + out_enc = self.encoder(inputs) + valid_ratios = None + word_positions = targets[-1] + + if len(targets) > 1: + valid_ratios = targets[-2] + + if self.training: + label = targets[0] # label + label = paddle.to_tensor(label, dtype="int64") + final_out = self.decoder( + inputs, out_enc, label, valid_ratios, word_positions + ) + if not self.training: + final_out = self.decoder( + inputs, + out_enc, + label=None, + valid_ratios=valid_ratios, + word_positions=word_positions, + train_mode=False, + ) + return final_out diff --git a/modules/onnx_ocr_module/src/ppocr/modeling/heads/rec_sar_head.py b/modules/onnx_ocr_module/src/ppocr/modeling/heads/rec_sar_head.py new file mode 100644 index 0000000..1e01999 --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/modeling/heads/rec_sar_head.py @@ -0,0 +1,407 @@ +# copyright (c) 2021 PaddlePaddle Authors. All Rights Reserve. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +""" +This code is refer from: +https://github.com/open-mmlab/mmocr/blob/main/mmocr/models/textrecog/encoders/sar_encoder.py +https://github.com/open-mmlab/mmocr/blob/main/mmocr/models/textrecog/decoders/sar_decoder.py +""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import math +import paddle +from paddle import ParamAttr +import paddle.nn as nn +import paddle.nn.functional as F + + +class SAREncoder(nn.Layer): + """ + Args: + enc_bi_rnn (bool): If True, use bidirectional RNN in encoder. + enc_drop_rnn (float): Dropout probability of RNN layer in encoder. + enc_gru (bool): If True, use GRU, else LSTM in encoder. + d_model (int): Dim of channels from backbone. + d_enc (int): Dim of encoder RNN layer. + mask (bool): If True, mask padding in RNN sequence. + """ + + def __init__( + self, + enc_bi_rnn=False, + enc_drop_rnn=0.1, + enc_gru=False, + d_model=512, + d_enc=512, + mask=True, + **kwargs, + ): + super().__init__() + assert isinstance(enc_bi_rnn, bool) + assert isinstance(enc_drop_rnn, (int, float)) + assert 0 <= enc_drop_rnn < 1.0 + assert isinstance(enc_gru, bool) + assert isinstance(d_model, int) + assert isinstance(d_enc, int) + assert isinstance(mask, bool) + + self.enc_bi_rnn = enc_bi_rnn + self.enc_drop_rnn = enc_drop_rnn + self.mask = mask + + # LSTM Encoder + if enc_bi_rnn: + direction = "bidirectional" + else: + direction = "forward" + kwargs = dict( + input_size=d_model, + hidden_size=d_enc, + num_layers=2, + time_major=False, + dropout=enc_drop_rnn, + direction=direction, + ) + if enc_gru: + self.rnn_encoder = nn.GRU(**kwargs) + else: + self.rnn_encoder = nn.LSTM(**kwargs) + + # global feature transformation + encoder_rnn_out_size = d_enc * (int(enc_bi_rnn) + 1) + self.linear = nn.Linear(encoder_rnn_out_size, encoder_rnn_out_size) + + def forward(self, feat, img_metas=None): + if img_metas is not None: + assert len(img_metas[0]) == feat.shape[0] + + valid_ratios = None + if img_metas is not None and self.mask: + valid_ratios = img_metas[-1] + + h_feat = feat.shape[2] # bsz c h w + feat_v = F.max_pool2d(feat, kernel_size=(h_feat, 1), stride=1, padding=0) + feat_v = feat_v.squeeze(2) # bsz * C * W + feat_v = paddle.transpose(feat_v, perm=[0, 2, 1]) # bsz * W * C + holistic_feat = self.rnn_encoder(feat_v)[0] # bsz * T * C + + if valid_ratios is not None: + valid_hf = [] + T = paddle.shape(holistic_feat)[1] + for i in range(valid_ratios.shape[0]): + valid_step = ( + paddle.minimum(T, paddle.ceil(valid_ratios[i] * T).astype(T.dtype)) + - 1 + ) + valid_hf.append(holistic_feat[i, valid_step, :]) + valid_hf = paddle.stack(valid_hf, axis=0) + else: + valid_hf = holistic_feat[:, -1, :] # bsz * C + holistic_feat = self.linear(valid_hf) # bsz * C + + return holistic_feat + + +class BaseDecoder(nn.Layer): + def __init__(self, **kwargs): + super().__init__() + + def forward_train(self, feat, out_enc, targets, img_metas): + raise NotImplementedError + + def forward_test(self, feat, out_enc, img_metas): + raise NotImplementedError + + def forward(self, feat, out_enc, label=None, img_metas=None, train_mode=True): + self.train_mode = train_mode + + if train_mode: + return self.forward_train(feat, out_enc, label, img_metas) + return self.forward_test(feat, out_enc, img_metas) + + +class ParallelSARDecoder(BaseDecoder): + """ + Args: + out_channels (int): Output class number. + enc_bi_rnn (bool): If True, use bidirectional RNN in encoder. + dec_bi_rnn (bool): If True, use bidirectional RNN in decoder. + dec_drop_rnn (float): Dropout of RNN layer in decoder. + dec_gru (bool): If True, use GRU, else LSTM in decoder. + d_model (int): Dim of channels from backbone. + d_enc (int): Dim of encoder RNN layer. + d_k (int): Dim of channels of attention module. + pred_dropout (float): Dropout probability of prediction layer. + max_seq_len (int): Maximum sequence length for decoding. + mask (bool): If True, mask padding in feature map. + start_idx (int): Index of start token. + padding_idx (int): Index of padding token. + pred_concat (bool): If True, concat glimpse feature from + attention with holistic feature and hidden state. + """ + + def __init__( + self, + out_channels, # 90 + unknown + start + padding + enc_bi_rnn=False, + dec_bi_rnn=False, + dec_drop_rnn=0.0, + dec_gru=False, + d_model=512, + d_enc=512, + d_k=64, + pred_dropout=0.1, + max_text_length=30, + mask=True, + pred_concat=True, + **kwargs, + ): + super().__init__() + + self.num_classes = out_channels + self.enc_bi_rnn = enc_bi_rnn + self.d_k = d_k + self.start_idx = out_channels - 2 + self.padding_idx = out_channels - 1 + self.max_seq_len = max_text_length + self.mask = mask + self.pred_concat = pred_concat + + encoder_rnn_out_size = d_enc * (int(enc_bi_rnn) + 1) + decoder_rnn_out_size = encoder_rnn_out_size * (int(dec_bi_rnn) + 1) + + # 2D attention layer + self.conv1x1_1 = nn.Linear(decoder_rnn_out_size, d_k) + self.conv3x3_1 = nn.Conv2D(d_model, d_k, kernel_size=3, stride=1, padding=1) + self.conv1x1_2 = nn.Linear(d_k, 1) + + # Decoder RNN layer + if dec_bi_rnn: + direction = "bidirectional" + else: + direction = "forward" + + kwargs = dict( + input_size=encoder_rnn_out_size, + hidden_size=encoder_rnn_out_size, + num_layers=2, + time_major=False, + dropout=dec_drop_rnn, + direction=direction, + ) + if dec_gru: + self.rnn_decoder = nn.GRU(**kwargs) + else: + self.rnn_decoder = nn.LSTM(**kwargs) + + # Decoder input embedding + self.embedding = nn.Embedding( + self.num_classes, encoder_rnn_out_size, padding_idx=self.padding_idx + ) + + # Prediction layer + self.pred_dropout = nn.Dropout(pred_dropout) + pred_num_classes = self.num_classes - 1 + if pred_concat: + fc_in_channel = decoder_rnn_out_size + d_model + encoder_rnn_out_size + else: + fc_in_channel = d_model + self.prediction = nn.Linear(fc_in_channel, pred_num_classes) + + def _2d_attention(self, decoder_input, feat, holistic_feat, valid_ratios=None): + y = self.rnn_decoder(decoder_input)[0] + # y: bsz * (seq_len + 1) * hidden_size + + attn_query = self.conv1x1_1(y) # bsz * (seq_len + 1) * attn_size + bsz, seq_len, attn_size = attn_query.shape + attn_query = paddle.unsqueeze(attn_query, axis=[3, 4]) + # (bsz, seq_len + 1, attn_size, 1, 1) + + attn_key = self.conv3x3_1(feat) + # bsz * attn_size * h * w + attn_key = attn_key.unsqueeze(1) + # bsz * 1 * attn_size * h * w + + attn_weight = paddle.tanh(paddle.add(attn_key, attn_query)) + + # bsz * (seq_len + 1) * attn_size * h * w + attn_weight = paddle.transpose(attn_weight, perm=[0, 1, 3, 4, 2]) + # bsz * (seq_len + 1) * h * w * attn_size + attn_weight = self.conv1x1_2(attn_weight) + # bsz * (seq_len + 1) * h * w * 1 + bsz, T, h, w, c = paddle.shape(attn_weight) + assert c == 1 + + if valid_ratios is not None: + # cal mask of attention weight + for i in range(valid_ratios.shape[0]): + valid_width = paddle.minimum( + w.astype("int64"), paddle.ceil(valid_ratios[i] * w).astype("int64") + ) + if valid_width < w: + attn_weight[i, :, :, valid_width:, :] = float("-inf") + + attn_weight = paddle.reshape(attn_weight, [bsz, T, -1]) + attn_weight = F.softmax(attn_weight, axis=-1) + + attn_weight = paddle.reshape(attn_weight, [bsz, T, h, w, c]) + attn_weight = paddle.transpose(attn_weight, perm=[0, 1, 4, 2, 3]) + # attn_weight: bsz * T * c * h * w + # feat: bsz * c * h * w + attn_feat = paddle.sum( + paddle.multiply(feat.unsqueeze(1), attn_weight), (3, 4), keepdim=False + ) + # bsz * (seq_len + 1) * C + + # Linear transformation + if self.pred_concat: + hf_c = holistic_feat.shape[-1] + holistic_feat = paddle.expand(holistic_feat, shape=[bsz, seq_len, hf_c]) + y = self.prediction( + paddle.concat( + (y, attn_feat.astype(y.dtype), holistic_feat.astype(y.dtype)), 2 + ) + ) + else: + y = self.prediction(attn_feat) + # bsz * (seq_len + 1) * num_classes + if self.train_mode: + y = self.pred_dropout(y) + + return y + + def forward_train(self, feat, out_enc, label, img_metas): + """ + img_metas: [label, valid_ratio] + """ + if img_metas is not None: + assert img_metas[0].shape[0] == feat.shape[0] + + valid_ratios = None + if img_metas is not None and self.mask: + valid_ratios = img_metas[-1] + + lab_embedding = self.embedding(label) + # bsz * seq_len * emb_dim + out_enc = out_enc.unsqueeze(1).astype(lab_embedding.dtype) + # bsz * 1 * emb_dim + in_dec = paddle.concat((out_enc, lab_embedding), axis=1) + # bsz * (seq_len + 1) * C + out_dec = self._2d_attention(in_dec, feat, out_enc, valid_ratios=valid_ratios) + + return out_dec[:, 1:, :] # bsz * seq_len * num_classes + + def forward_test(self, feat, out_enc, img_metas): + if img_metas is not None: + assert len(img_metas[0]) == feat.shape[0] + + valid_ratios = None + if img_metas is not None and self.mask: + valid_ratios = img_metas[-1] + + seq_len = self.max_seq_len + bsz = feat.shape[0] + start_token = paddle.full((bsz,), fill_value=self.start_idx, dtype="int64") + # bsz + start_token = self.embedding(start_token) + # bsz * emb_dim + emb_dim = start_token.shape[1] + start_token = start_token.unsqueeze(1) + start_token = paddle.expand(start_token, shape=[bsz, seq_len, emb_dim]) + # bsz * seq_len * emb_dim + out_enc = out_enc.unsqueeze(1) + # bsz * 1 * emb_dim + decoder_input = paddle.concat((out_enc, start_token), axis=1) + # bsz * (seq_len + 1) * emb_dim + + outputs = [] + for i in range(1, seq_len + 1): + decoder_output = self._2d_attention( + decoder_input, feat, out_enc, valid_ratios=valid_ratios + ) + char_output = decoder_output[:, i, :] # bsz * num_classes + char_output = F.softmax(char_output, -1) + outputs.append(char_output) + max_idx = paddle.argmax(char_output, axis=1, keepdim=False) + char_embedding = self.embedding(max_idx) # bsz * emb_dim + if i < seq_len: + decoder_input[:, i + 1, :] = char_embedding + + outputs = paddle.stack(outputs, 1) # bsz * seq_len * num_classes + + return outputs + + +class SARHead(nn.Layer): + def __init__( + self, + in_channels, + out_channels, + enc_dim=512, + max_text_length=30, + enc_bi_rnn=False, + enc_drop_rnn=0.1, + enc_gru=False, + dec_bi_rnn=False, + dec_drop_rnn=0.0, + dec_gru=False, + d_k=512, + pred_dropout=0.1, + pred_concat=True, + **kwargs, + ): + super(SARHead, self).__init__() + + # encoder module + self.encoder = SAREncoder( + enc_bi_rnn=enc_bi_rnn, + enc_drop_rnn=enc_drop_rnn, + enc_gru=enc_gru, + d_model=in_channels, + d_enc=enc_dim, + ) + + # decoder module + self.decoder = ParallelSARDecoder( + out_channels=out_channels, + enc_bi_rnn=enc_bi_rnn, + dec_bi_rnn=dec_bi_rnn, + dec_drop_rnn=dec_drop_rnn, + dec_gru=dec_gru, + d_model=in_channels, + d_enc=enc_dim, + d_k=d_k, + pred_dropout=pred_dropout, + max_text_length=max_text_length, + pred_concat=pred_concat, + ) + + def forward(self, feat, targets=None): + """ + img_metas: [label, valid_ratio] + """ + holistic_feat = self.encoder(feat, targets) # bsz c + + if self.training: + label = targets[0] # label + final_out = self.decoder(feat, holistic_feat, label, img_metas=targets) + else: + final_out = self.decoder( + feat, holistic_feat, label=None, img_metas=targets, train_mode=False + ) + # (bsz, seq_len, num_classes) + + return final_out diff --git a/modules/onnx_ocr_module/src/ppocr/modeling/heads/rec_satrn_head.py b/modules/onnx_ocr_module/src/ppocr/modeling/heads/rec_satrn_head.py new file mode 100644 index 0000000..8f20474 --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/modeling/heads/rec_satrn_head.py @@ -0,0 +1,592 @@ +# copyright (c) 2022 PaddlePaddle Authors. All Rights Reserve. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +""" +This code is refer from: +https://github.com/open-mmlab/mmocr/blob/1.x/mmocr/models/textrecog/encoders/satrn_encoder.py +https://github.com/open-mmlab/mmocr/blob/1.x/mmocr/models/textrecog/decoders/nrtr_decoder.py +""" + +import math +import numpy as np +import paddle +import paddle.nn as nn +import paddle.nn.functional as F +from paddle import ParamAttr, reshape, transpose +from paddle.nn import Conv2D, BatchNorm, Linear, Dropout +from paddle.nn import AdaptiveAvgPool2D, MaxPool2D, AvgPool2D +from paddle.nn.initializer import KaimingNormal, Uniform, Constant + + +class ConvBNLayer(nn.Layer): + def __init__( + self, num_channels, filter_size, num_filters, stride, padding, num_groups=1 + ): + super(ConvBNLayer, self).__init__() + + self.conv = nn.Conv2D( + in_channels=num_channels, + out_channels=num_filters, + kernel_size=filter_size, + stride=stride, + padding=padding, + groups=num_groups, + bias_attr=False, + ) + + self.bn = nn.BatchNorm2D( + num_filters, + weight_attr=ParamAttr(initializer=Constant(1)), + bias_attr=ParamAttr(initializer=Constant(0)), + ) + self.relu = nn.ReLU() + + def forward(self, inputs): + y = self.conv(inputs) + y = self.bn(y) + y = self.relu(y) + return y + + +class SATRNEncoderLayer(nn.Layer): + def __init__( + self, + d_model=512, + d_inner=512, + n_head=8, + d_k=64, + d_v=64, + dropout=0.1, + qkv_bias=False, + ): + super().__init__() + self.norm1 = nn.LayerNorm(d_model) + self.attn = MultiHeadAttention( + n_head, d_model, d_k, d_v, qkv_bias=qkv_bias, dropout=dropout + ) + self.norm2 = nn.LayerNorm(d_model) + self.feed_forward = LocalityAwareFeedforward(d_model, d_inner, dropout=dropout) + + def forward(self, x, h, w, mask=None): + n, hw, c = x.shape + residual = x + x = self.norm1(x) + x = residual + self.attn(x, x, x, mask) + residual = x + x = self.norm2(x) + x = x.transpose([0, 2, 1]).reshape([n, c, h, w]) + x = self.feed_forward(x) + x = x.reshape([n, c, hw]).transpose([0, 2, 1]) + x = residual + x + return x + + +class LocalityAwareFeedforward(nn.Layer): + def __init__( + self, + d_in, + d_hid, + dropout=0.1, + ): + super().__init__() + self.conv1 = ConvBNLayer(d_in, 1, d_hid, stride=1, padding=0) + + self.depthwise_conv = ConvBNLayer( + d_hid, 3, d_hid, stride=1, padding=1, num_groups=d_hid + ) + + self.conv2 = ConvBNLayer(d_hid, 1, d_in, stride=1, padding=0) + + def forward(self, x): + x = self.conv1(x) + x = self.depthwise_conv(x) + x = self.conv2(x) + + return x + + +class Adaptive2DPositionalEncoding(nn.Layer): + def __init__(self, d_hid=512, n_height=100, n_width=100, dropout=0.1): + super().__init__() + + h_position_encoder = self._get_sinusoid_encoding_table(n_height, d_hid) + h_position_encoder = h_position_encoder.transpose([1, 0]) + h_position_encoder = h_position_encoder.reshape([1, d_hid, n_height, 1]) + + w_position_encoder = self._get_sinusoid_encoding_table(n_width, d_hid) + w_position_encoder = w_position_encoder.transpose([1, 0]) + w_position_encoder = w_position_encoder.reshape([1, d_hid, 1, n_width]) + + self.register_buffer("h_position_encoder", h_position_encoder) + self.register_buffer("w_position_encoder", w_position_encoder) + + self.h_scale = self.scale_factor_generate(d_hid) + self.w_scale = self.scale_factor_generate(d_hid) + self.pool = nn.AdaptiveAvgPool2D(1) + self.dropout = nn.Dropout(p=dropout) + + def _get_sinusoid_encoding_table(self, n_position, d_hid): + """Sinusoid position encoding table.""" + denominator = paddle.to_tensor( + [1.0 / np.power(10000, 2 * (hid_j // 2) / d_hid) for hid_j in range(d_hid)] + ) + denominator = denominator.reshape([1, -1]) + pos_tensor = paddle.cast(paddle.arange(n_position).unsqueeze(-1), "float32") + sinusoid_table = pos_tensor * denominator + sinusoid_table[:, 0::2] = paddle.sin(sinusoid_table[:, 0::2]) + sinusoid_table[:, 1::2] = paddle.cos(sinusoid_table[:, 1::2]) + + return sinusoid_table + + def scale_factor_generate(self, d_hid): + scale_factor = nn.Sequential( + nn.Conv2D(d_hid, d_hid, 1), + nn.ReLU(), + nn.Conv2D(d_hid, d_hid, 1), + nn.Sigmoid(), + ) + + return scale_factor + + def forward(self, x): + b, c, h, w = x.shape + + avg_pool = self.pool(x) + + h_pos_encoding = self.h_scale(avg_pool) * self.h_position_encoder[:, :, :h, :] + w_pos_encoding = self.w_scale(avg_pool) * self.w_position_encoder[:, :, :, :w] + + out = x + h_pos_encoding + w_pos_encoding + + out = self.dropout(out) + + return out + + +class ScaledDotProductAttention(nn.Layer): + def __init__(self, temperature, attn_dropout=0.1): + super().__init__() + self.temperature = temperature + self.dropout = nn.Dropout(attn_dropout) + + def forward(self, q, k, v, mask=None): + def masked_fill(x, mask, value): + y = paddle.full(x.shape, value, x.dtype) + return paddle.where(mask, y, x) + + attn = paddle.matmul(q / self.temperature, k.transpose([0, 1, 3, 2])) + if mask is not None: + attn = masked_fill(attn, mask == 0, -1e9) + # attn = attn.masked_fill(mask == 0, float('-inf')) + # attn += mask + + attn = self.dropout(F.softmax(attn, axis=-1)) + output = paddle.matmul(attn, v) + + return output, attn + + +class MultiHeadAttention(nn.Layer): + def __init__( + self, n_head=8, d_model=512, d_k=64, d_v=64, dropout=0.1, qkv_bias=False + ): + super().__init__() + self.n_head = n_head + self.d_k = d_k + self.d_v = d_v + + self.dim_k = n_head * d_k + self.dim_v = n_head * d_v + + self.linear_q = nn.Linear(self.dim_k, self.dim_k, bias_attr=qkv_bias) + self.linear_k = nn.Linear(self.dim_k, self.dim_k, bias_attr=qkv_bias) + self.linear_v = nn.Linear(self.dim_v, self.dim_v, bias_attr=qkv_bias) + + self.attention = ScaledDotProductAttention(d_k**0.5, dropout) + + self.fc = nn.Linear(self.dim_v, d_model, bias_attr=qkv_bias) + self.proj_drop = nn.Dropout(dropout) + + def forward(self, q, k, v, mask=None): + batch_size, len_q, _ = q.shape + _, len_k, _ = k.shape + + q = self.linear_q(q).reshape([batch_size, len_q, self.n_head, self.d_k]) + k = self.linear_k(k).reshape([batch_size, len_k, self.n_head, self.d_k]) + v = self.linear_v(v).reshape([batch_size, len_k, self.n_head, self.d_v]) + + q, k, v = ( + q.transpose([0, 2, 1, 3]), + k.transpose([0, 2, 1, 3]), + v.transpose([0, 2, 1, 3]), + ) + + if mask is not None: + if mask.dim() == 3: + mask = mask.unsqueeze(1) + elif mask.dim() == 2: + mask = mask.unsqueeze(1).unsqueeze(1) + + attn_out, _ = self.attention(q, k, v, mask=mask) + + attn_out = attn_out.transpose([0, 2, 1, 3]).reshape( + [batch_size, len_q, self.dim_v] + ) + + attn_out = self.fc(attn_out) + attn_out = self.proj_drop(attn_out) + + return attn_out + + +class SATRNEncoder(nn.Layer): + def __init__( + self, + n_layers=12, + n_head=8, + d_k=64, + d_v=64, + d_model=512, + n_position=100, + d_inner=256, + dropout=0.1, + ): + super().__init__() + self.d_model = d_model + self.position_enc = Adaptive2DPositionalEncoding( + d_hid=d_model, n_height=n_position, n_width=n_position, dropout=dropout + ) + self.layer_stack = nn.LayerList( + [ + SATRNEncoderLayer(d_model, d_inner, n_head, d_k, d_v, dropout=dropout) + for _ in range(n_layers) + ] + ) + self.layer_norm = nn.LayerNorm(d_model) + + def forward(self, feat, valid_ratios=None): + """ + Args: + feat (Tensor): Feature tensor of shape :math:`(N, D_m, H, W)`. + img_metas (dict): A dict that contains meta information of input + images. Preferably with the key ``valid_ratio``. + + Returns: + Tensor: A tensor of shape :math:`(N, T, D_m)`. + """ + if valid_ratios is None: + bs = feat.shape[0] + valid_ratios = paddle.full((bs, 1), 1.0, dtype=paddle.float32) + + feat = self.position_enc(feat) + n, c, h, w = feat.shape + + mask = paddle.zeros((n, h, w)) + for i, valid_ratio in enumerate(valid_ratios): + valid_width = int(min(w, paddle.ceil(w * valid_ratio))) + mask[i, :, :valid_width] = 1 + + mask = mask.reshape([n, h * w]) + feat = feat.reshape([n, c, h * w]) + + output = feat.transpose([0, 2, 1]) + for enc_layer in self.layer_stack: + output = enc_layer(output, h, w, mask) + output = self.layer_norm(output) + + return output + + +class PositionwiseFeedForward(nn.Layer): + def __init__(self, d_in, d_hid, dropout=0.1): + super().__init__() + self.w_1 = nn.Linear(d_in, d_hid) + self.w_2 = nn.Linear(d_hid, d_in) + self.act = nn.GELU() + self.dropout = nn.Dropout(dropout) + + def forward(self, x): + x = self.w_1(x) + x = self.act(x) + x = self.w_2(x) + x = self.dropout(x) + + return x + + +class PositionalEncoding(nn.Layer): + def __init__(self, d_hid=512, n_position=200, dropout=0): + super().__init__() + self.dropout = nn.Dropout(p=dropout) + + # Not a parameter + # Position table of shape (1, n_position, d_hid) + self.register_buffer( + "position_table", self._get_sinusoid_encoding_table(n_position, d_hid) + ) + + def _get_sinusoid_encoding_table(self, n_position, d_hid): + """Sinusoid position encoding table.""" + denominator = paddle.to_tensor( + [1.0 / np.power(10000, 2 * (hid_j // 2) / d_hid) for hid_j in range(d_hid)] + ) + denominator = denominator.reshape([1, -1]) + pos_tensor = paddle.cast(paddle.arange(n_position).unsqueeze(-1), "float32") + sinusoid_table = pos_tensor * denominator + sinusoid_table[:, 0::2] = paddle.sin(sinusoid_table[:, 0::2]) + sinusoid_table[:, 1::2] = paddle.cos(sinusoid_table[:, 1::2]) + + return sinusoid_table.unsqueeze(0) + + def forward(self, x): + x = x + self.position_table[:, : x.shape[1]].clone().detach() + return self.dropout(x) + + +class TFDecoderLayer(nn.Layer): + def __init__( + self, + d_model=512, + d_inner=256, + n_head=8, + d_k=64, + d_v=64, + dropout=0.1, + qkv_bias=False, + operation_order=None, + ): + super().__init__() + + self.norm1 = nn.LayerNorm(d_model) + self.norm2 = nn.LayerNorm(d_model) + self.norm3 = nn.LayerNorm(d_model) + + self.self_attn = MultiHeadAttention( + n_head, d_model, d_k, d_v, dropout=dropout, qkv_bias=qkv_bias + ) + + self.enc_attn = MultiHeadAttention( + n_head, d_model, d_k, d_v, dropout=dropout, qkv_bias=qkv_bias + ) + + self.mlp = PositionwiseFeedForward(d_model, d_inner, dropout=dropout) + + self.operation_order = operation_order + if self.operation_order is None: + self.operation_order = ( + "norm", + "self_attn", + "norm", + "enc_dec_attn", + "norm", + "ffn", + ) + assert self.operation_order in [ + ("norm", "self_attn", "norm", "enc_dec_attn", "norm", "ffn"), + ("self_attn", "norm", "enc_dec_attn", "norm", "ffn", "norm"), + ] + + def forward( + self, dec_input, enc_output, self_attn_mask=None, dec_enc_attn_mask=None + ): + if self.operation_order == ( + "self_attn", + "norm", + "enc_dec_attn", + "norm", + "ffn", + "norm", + ): + dec_attn_out = self.self_attn( + dec_input, dec_input, dec_input, self_attn_mask + ) + dec_attn_out += dec_input + dec_attn_out = self.norm1(dec_attn_out) + + enc_dec_attn_out = self.enc_attn( + dec_attn_out, enc_output, enc_output, dec_enc_attn_mask + ) + enc_dec_attn_out += dec_attn_out + enc_dec_attn_out = self.norm2(enc_dec_attn_out) + + mlp_out = self.mlp(enc_dec_attn_out) + mlp_out += enc_dec_attn_out + mlp_out = self.norm3(mlp_out) + elif self.operation_order == ( + "norm", + "self_attn", + "norm", + "enc_dec_attn", + "norm", + "ffn", + ): + dec_input_norm = self.norm1(dec_input) + dec_attn_out = self.self_attn( + dec_input_norm, dec_input_norm, dec_input_norm, self_attn_mask + ) + dec_attn_out += dec_input + + enc_dec_attn_in = self.norm2(dec_attn_out) + enc_dec_attn_out = self.enc_attn( + enc_dec_attn_in, enc_output, enc_output, dec_enc_attn_mask + ) + enc_dec_attn_out += dec_attn_out + + mlp_out = self.mlp(self.norm3(enc_dec_attn_out)) + mlp_out += enc_dec_attn_out + + return mlp_out + + +class SATRNDecoder(nn.Layer): + def __init__( + self, + n_layers=6, + d_embedding=512, + n_head=8, + d_k=64, + d_v=64, + d_model=512, + d_inner=256, + n_position=200, + dropout=0.1, + num_classes=93, + max_seq_len=40, + start_idx=1, + padding_idx=92, + ): + super().__init__() + + self.padding_idx = padding_idx + self.start_idx = start_idx + self.max_seq_len = max_seq_len + + self.trg_word_emb = nn.Embedding( + num_classes, d_embedding, padding_idx=padding_idx + ) + + self.position_enc = PositionalEncoding(d_embedding, n_position=n_position) + self.dropout = nn.Dropout(p=dropout) + + self.layer_stack = nn.LayerList( + [ + TFDecoderLayer(d_model, d_inner, n_head, d_k, d_v, dropout=dropout) + for _ in range(n_layers) + ] + ) + self.layer_norm = nn.LayerNorm(d_model, epsilon=1e-6) + + pred_num_class = num_classes - 1 # ignore padding_idx + self.classifier = nn.Linear(d_model, pred_num_class) + + @staticmethod + def get_pad_mask(seq, pad_idx): + return (seq != pad_idx).unsqueeze(-2) + + @staticmethod + def get_subsequent_mask(seq): + """For masking out the subsequent info.""" + len_s = seq.shape[1] + subsequent_mask = 1 - paddle.triu(paddle.ones((len_s, len_s)), diagonal=1) + subsequent_mask = paddle.cast(subsequent_mask.unsqueeze(0), "bool") + + return subsequent_mask + + def _attention(self, trg_seq, src, src_mask=None): + trg_embedding = self.trg_word_emb(trg_seq) + trg_pos_encoded = self.position_enc(trg_embedding) + tgt = self.dropout(trg_pos_encoded) + + trg_mask = self.get_pad_mask( + trg_seq, pad_idx=self.padding_idx + ) & self.get_subsequent_mask(trg_seq) + output = tgt + for dec_layer in self.layer_stack: + output = dec_layer( + output, src, self_attn_mask=trg_mask, dec_enc_attn_mask=src_mask + ) + output = self.layer_norm(output) + + return output + + def _get_mask(self, logit, valid_ratios): + N, T, _ = logit.shape + mask = None + if valid_ratios is not None: + mask = paddle.zeros((N, T)) + for i, valid_ratio in enumerate(valid_ratios): + valid_width = min(T, math.ceil(T * valid_ratio)) + mask[i, :valid_width] = 1 + + return mask + + def forward_train(self, feat, out_enc, targets, valid_ratio): + src_mask = self._get_mask(out_enc, valid_ratio) + attn_output = self._attention(targets, out_enc, src_mask=src_mask) + outputs = self.classifier(attn_output) + + return outputs + + def forward_test(self, feat, out_enc, valid_ratio): + src_mask = self._get_mask(out_enc, valid_ratio) + N = out_enc.shape[0] + init_target_seq = paddle.full( + (N, self.max_seq_len + 1), self.padding_idx, dtype="int64" + ) + # bsz * seq_len + init_target_seq[:, 0] = self.start_idx + + outputs = [] + for step in range(0, paddle.to_tensor(self.max_seq_len)): + decoder_output = self._attention( + init_target_seq, out_enc, src_mask=src_mask + ) + # bsz * seq_len * C + step_result = F.softmax( + self.classifier(decoder_output[:, step, :]), axis=-1 + ) + # bsz * num_classes + outputs.append(step_result) + step_max_index = paddle.argmax(step_result, axis=-1) + init_target_seq[:, step + 1] = step_max_index + + outputs = paddle.stack(outputs, axis=1) + + return outputs + + def forward(self, feat, out_enc, targets=None, valid_ratio=None): + if self.training: + return self.forward_train(feat, out_enc, targets, valid_ratio) + else: + return self.forward_test(feat, out_enc, valid_ratio) + + +class SATRNHead(nn.Layer): + def __init__(self, enc_cfg, dec_cfg, **kwargs): + super(SATRNHead, self).__init__() + + # encoder module + self.encoder = SATRNEncoder(**enc_cfg) + + # decoder module + self.decoder = SATRNDecoder(**dec_cfg) + + def forward(self, feat, targets=None): + if targets is not None: + targets, valid_ratio = targets + else: + targets, valid_ratio = None, None + holistic_feat = self.encoder(feat, valid_ratio) # bsz c + final_out = self.decoder(feat, holistic_feat, targets, valid_ratio) + + return final_out diff --git a/modules/onnx_ocr_module/src/ppocr/modeling/heads/rec_spin_att_head.py b/modules/onnx_ocr_module/src/ppocr/modeling/heads/rec_spin_att_head.py new file mode 100644 index 0000000..930d970 --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/modeling/heads/rec_spin_att_head.py @@ -0,0 +1,124 @@ +# copyright (c) 2022 PaddlePaddle Authors. All Rights Reserve. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +""" +This code is refer from: +https://github.com/hikopensource/DAVAR-Lab-OCR/davarocr/davar_rcg/models/sequence_heads/att_head.py +""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import paddle +import paddle.nn as nn +import paddle.nn.functional as F + + +class SPINAttentionHead(nn.Layer): + def __init__(self, in_channels, out_channels, hidden_size, **kwargs): + super(SPINAttentionHead, self).__init__() + self.input_size = in_channels + self.hidden_size = hidden_size + self.num_classes = out_channels + + self.attention_cell = AttentionLSTMCell( + in_channels, hidden_size, out_channels, use_gru=False + ) + self.generator = nn.Linear(hidden_size, out_channels) + + def _char_to_onehot(self, input_char, onehot_dim): + input_ont_hot = F.one_hot(input_char, onehot_dim) + return input_ont_hot + + def forward(self, inputs, targets=None, batch_max_length=25): + batch_size = inputs.shape[0] + num_steps = batch_max_length + 1 # +1 for [sos] at end of sentence + + hidden = ( + paddle.zeros((batch_size, self.hidden_size)), + paddle.zeros((batch_size, self.hidden_size)), + ) + output_hiddens = [] + if self.training: # for train + targets = targets[0] + for i in range(num_steps): + char_onehots = self._char_to_onehot( + targets[:, i], onehot_dim=self.num_classes + ) + (outputs, hidden), alpha = self.attention_cell( + hidden, inputs, char_onehots + ) + output_hiddens.append(paddle.unsqueeze(outputs, axis=1)) + output = paddle.concat(output_hiddens, axis=1) + probs = self.generator(output) + else: + targets = paddle.zeros(shape=[batch_size], dtype="int32") + probs = None + char_onehots = None + outputs = None + alpha = None + + for i in range(num_steps): + char_onehots = self._char_to_onehot( + targets, onehot_dim=self.num_classes + ) + (outputs, hidden), alpha = self.attention_cell( + hidden, inputs, char_onehots + ) + probs_step = self.generator(outputs) + if probs is None: + probs = paddle.unsqueeze(probs_step, axis=1) + else: + probs = paddle.concat( + [probs, paddle.unsqueeze(probs_step, axis=1)], axis=1 + ) + next_input = probs_step.argmax(axis=1) + targets = next_input + if not self.training: + probs = paddle.nn.functional.softmax(probs, axis=2) + return probs + + +class AttentionLSTMCell(nn.Layer): + def __init__(self, input_size, hidden_size, num_embeddings, use_gru=False): + super(AttentionLSTMCell, self).__init__() + self.i2h = nn.Linear(input_size, hidden_size, bias_attr=False) + self.h2h = nn.Linear(hidden_size, hidden_size) + self.score = nn.Linear(hidden_size, 1, bias_attr=False) + if not use_gru: + self.rnn = nn.LSTMCell( + input_size=input_size + num_embeddings, hidden_size=hidden_size + ) + else: + self.rnn = nn.GRUCell( + input_size=input_size + num_embeddings, hidden_size=hidden_size + ) + + self.hidden_size = hidden_size + + def forward(self, prev_hidden, batch_H, char_onehots): + batch_H_proj = self.i2h(batch_H) + prev_hidden_proj = paddle.unsqueeze(self.h2h(prev_hidden[0]), axis=1) + res = paddle.add(batch_H_proj, prev_hidden_proj) + res = paddle.tanh(res) + e = self.score(res) + + alpha = F.softmax(e, axis=1) + alpha = paddle.transpose(alpha, [0, 2, 1]) + context = paddle.squeeze(paddle.mm(alpha, batch_H), axis=1) + concat_context = paddle.concat([context, char_onehots], 1) + cur_hidden = self.rnn(concat_context, prev_hidden) + + return cur_hidden, alpha diff --git a/modules/onnx_ocr_module/src/ppocr/modeling/heads/rec_srn_head.py b/modules/onnx_ocr_module/src/ppocr/modeling/heads/rec_srn_head.py new file mode 100644 index 0000000..8f6f139 --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/modeling/heads/rec_srn_head.py @@ -0,0 +1,315 @@ +# copyright (c) 2020 PaddlePaddle Authors. All Rights Reserve. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import math +import paddle +from paddle import nn, ParamAttr +from paddle.nn import functional as F +import numpy as np +from .self_attention import WrapEncoderForFeature +from .self_attention import WrapEncoder +from paddle.static import Program +from ppocr.modeling.backbones.rec_resnet_fpn import ResNetFPN + +from collections import OrderedDict + +gradient_clip = 10 + + +class PVAM(nn.Layer): + def __init__( + self, + in_channels, + char_num, + max_text_length, + num_heads, + num_encoder_tus, + hidden_dims, + ): + super(PVAM, self).__init__() + self.char_num = char_num + self.max_length = max_text_length + self.num_heads = num_heads + self.num_encoder_TUs = num_encoder_tus + self.hidden_dims = hidden_dims + # Transformer encoder + t = 256 + c = 512 + self.wrap_encoder_for_feature = WrapEncoderForFeature( + src_vocab_size=1, + max_length=t, + n_layer=self.num_encoder_TUs, + n_head=self.num_heads, + d_key=int(self.hidden_dims / self.num_heads), + d_value=int(self.hidden_dims / self.num_heads), + d_model=self.hidden_dims, + d_inner_hid=self.hidden_dims, + prepostprocess_dropout=0.1, + attention_dropout=0.1, + relu_dropout=0.1, + preprocess_cmd="n", + postprocess_cmd="da", + weight_sharing=True, + ) + + # PVAM + self.flatten0 = paddle.nn.Flatten(start_axis=0, stop_axis=1) + self.fc0 = paddle.nn.Linear( + in_features=in_channels, + out_features=in_channels, + ) + self.emb = paddle.nn.Embedding( + num_embeddings=self.max_length, embedding_dim=in_channels + ) + self.flatten1 = paddle.nn.Flatten(start_axis=0, stop_axis=2) + self.fc1 = paddle.nn.Linear( + in_features=in_channels, out_features=1, bias_attr=False + ) + + def forward(self, inputs, encoder_word_pos, gsrm_word_pos): + b, c, h, w = inputs.shape + conv_features = paddle.reshape(inputs, shape=[-1, c, h * w]) + conv_features = paddle.transpose(conv_features, perm=[0, 2, 1]) + # transformer encoder + b, t, c = conv_features.shape + + enc_inputs = [conv_features, encoder_word_pos, None] + word_features = self.wrap_encoder_for_feature(enc_inputs) + + # pvam + b, t, c = word_features.shape + word_features = self.fc0(word_features) + word_features_ = paddle.reshape(word_features, [-1, 1, t, c]) + word_features_ = paddle.tile(word_features_, [1, self.max_length, 1, 1]) + word_pos_feature = self.emb(gsrm_word_pos) + word_pos_feature_ = paddle.reshape( + word_pos_feature, [-1, self.max_length, 1, c] + ) + word_pos_feature_ = paddle.tile(word_pos_feature_, [1, 1, t, 1]) + y = word_pos_feature_ + word_features_ + y = F.tanh(y) + attention_weight = self.fc1(y) + attention_weight = paddle.reshape( + attention_weight, shape=[-1, self.max_length, t] + ) + attention_weight = F.softmax(attention_weight, axis=-1) + pvam_features = paddle.matmul( + attention_weight, word_features + ) # [b, max_length, c] + return pvam_features + + +class GSRM(nn.Layer): + def __init__( + self, + in_channels, + char_num, + max_text_length, + num_heads, + num_encoder_tus, + num_decoder_tus, + hidden_dims, + ): + super(GSRM, self).__init__() + self.char_num = char_num + self.max_length = max_text_length + self.num_heads = num_heads + self.num_encoder_TUs = num_encoder_tus + self.num_decoder_TUs = num_decoder_tus + self.hidden_dims = hidden_dims + + self.fc0 = paddle.nn.Linear(in_features=in_channels, out_features=self.char_num) + self.wrap_encoder0 = WrapEncoder( + src_vocab_size=self.char_num + 1, + max_length=self.max_length, + n_layer=self.num_decoder_TUs, + n_head=self.num_heads, + d_key=int(self.hidden_dims / self.num_heads), + d_value=int(self.hidden_dims / self.num_heads), + d_model=self.hidden_dims, + d_inner_hid=self.hidden_dims, + prepostprocess_dropout=0.1, + attention_dropout=0.1, + relu_dropout=0.1, + preprocess_cmd="n", + postprocess_cmd="da", + weight_sharing=True, + ) + + self.wrap_encoder1 = WrapEncoder( + src_vocab_size=self.char_num + 1, + max_length=self.max_length, + n_layer=self.num_decoder_TUs, + n_head=self.num_heads, + d_key=int(self.hidden_dims / self.num_heads), + d_value=int(self.hidden_dims / self.num_heads), + d_model=self.hidden_dims, + d_inner_hid=self.hidden_dims, + prepostprocess_dropout=0.1, + attention_dropout=0.1, + relu_dropout=0.1, + preprocess_cmd="n", + postprocess_cmd="da", + weight_sharing=True, + ) + + self.mul = lambda x: paddle.matmul( + x=x, y=self.wrap_encoder0.prepare_decoder.emb0.weight, transpose_y=True + ) + + def forward(self, inputs, gsrm_word_pos, gsrm_slf_attn_bias1, gsrm_slf_attn_bias2): + # ===== GSRM Visual-to-semantic embedding block ===== + b, t, c = inputs.shape + pvam_features = paddle.reshape(inputs, [-1, c]) + word_out = self.fc0(pvam_features) + word_ids = paddle.argmax(F.softmax(word_out), axis=1) + word_ids = paddle.reshape(x=word_ids, shape=[-1, t, 1]) + + # ===== GSRM Semantic reasoning block ===== + """ + This module is achieved through bi-transformers, + ngram_feature1 is the forward one, ngram_fetaure2 is the backward one + """ + pad_idx = self.char_num + + word1 = paddle.cast(word_ids, "float32") + word1 = F.pad(word1, [1, 0], value=1.0 * pad_idx, data_format="NLC") + word1 = paddle.cast(word1, "int64") + word1 = word1[:, :-1, :] + word2 = word_ids + + enc_inputs_1 = [word1, gsrm_word_pos, gsrm_slf_attn_bias1] + enc_inputs_2 = [word2, gsrm_word_pos, gsrm_slf_attn_bias2] + + gsrm_feature1 = self.wrap_encoder0(enc_inputs_1) + gsrm_feature2 = self.wrap_encoder1(enc_inputs_2) + + gsrm_feature2 = F.pad(gsrm_feature2, [0, 1], value=0.0, data_format="NLC") + gsrm_feature2 = gsrm_feature2[ + :, + 1:, + ] + gsrm_features = gsrm_feature1 + gsrm_feature2 + + gsrm_out = self.mul(gsrm_features) + + b, t, c = gsrm_out.shape + gsrm_out = paddle.reshape(gsrm_out, [-1, c]) + + return gsrm_features, word_out, gsrm_out + + +class VSFD(nn.Layer): + def __init__(self, in_channels=512, pvam_ch=512, char_num=38): + super(VSFD, self).__init__() + self.char_num = char_num + self.fc0 = paddle.nn.Linear(in_features=in_channels * 2, out_features=pvam_ch) + self.fc1 = paddle.nn.Linear(in_features=pvam_ch, out_features=self.char_num) + + def forward(self, pvam_feature, gsrm_feature): + b, t, c1 = pvam_feature.shape + b, t, c2 = gsrm_feature.shape + combine_feature_ = paddle.concat([pvam_feature, gsrm_feature], axis=2) + img_comb_feature_ = paddle.reshape(combine_feature_, shape=[-1, c1 + c2]) + img_comb_feature_map = self.fc0(img_comb_feature_) + img_comb_feature_map = F.sigmoid(img_comb_feature_map) + img_comb_feature_map = paddle.reshape(img_comb_feature_map, shape=[-1, t, c1]) + combine_feature = ( + img_comb_feature_map * pvam_feature + + (1.0 - img_comb_feature_map) * gsrm_feature + ) + img_comb_feature = paddle.reshape(combine_feature, shape=[-1, c1]) + + out = self.fc1(img_comb_feature) + return out + + +class SRNHead(nn.Layer): + def __init__( + self, + in_channels, + out_channels, + max_text_length, + num_heads, + num_encoder_TUs, + num_decoder_TUs, + hidden_dims, + **kwargs, + ): + super(SRNHead, self).__init__() + self.char_num = out_channels + self.max_length = max_text_length + self.num_heads = num_heads + self.num_encoder_TUs = num_encoder_TUs + self.num_decoder_TUs = num_decoder_TUs + self.hidden_dims = hidden_dims + + self.pvam = PVAM( + in_channels=in_channels, + char_num=self.char_num, + max_text_length=self.max_length, + num_heads=self.num_heads, + num_encoder_tus=self.num_encoder_TUs, + hidden_dims=self.hidden_dims, + ) + + self.gsrm = GSRM( + in_channels=in_channels, + char_num=self.char_num, + max_text_length=self.max_length, + num_heads=self.num_heads, + num_encoder_tus=self.num_encoder_TUs, + num_decoder_tus=self.num_decoder_TUs, + hidden_dims=self.hidden_dims, + ) + self.vsfd = VSFD(in_channels=in_channels, char_num=self.char_num) + + self.gsrm.wrap_encoder1.prepare_decoder.emb0 = ( + self.gsrm.wrap_encoder0.prepare_decoder.emb0 + ) + + def forward(self, inputs, targets=None): + others = targets[-4:] + encoder_word_pos = others[0] + gsrm_word_pos = others[1] + gsrm_slf_attn_bias1 = others[2] + gsrm_slf_attn_bias2 = others[3] + + pvam_feature = self.pvam(inputs, encoder_word_pos, gsrm_word_pos) + + gsrm_feature, word_out, gsrm_out = self.gsrm( + pvam_feature, gsrm_word_pos, gsrm_slf_attn_bias1, gsrm_slf_attn_bias2 + ) + + final_out = self.vsfd(pvam_feature, gsrm_feature) + if not self.training: + final_out = F.softmax(final_out, axis=1) + + _, decoded_out = paddle.topk(final_out, k=1) + + predicts = OrderedDict( + [ + ("predict", final_out), + ("pvam_feature", pvam_feature), + ("decoded_out", decoded_out), + ("word_out", word_out), + ("gsrm_out", gsrm_out), + ] + ) + + return predicts diff --git a/modules/onnx_ocr_module/src/ppocr/modeling/heads/rec_unimernet_head.py b/modules/onnx_ocr_module/src/ppocr/modeling/heads/rec_unimernet_head.py new file mode 100644 index 0000000..e45eeb3 --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/modeling/heads/rec_unimernet_head.py @@ -0,0 +1,2673 @@ +# copyright (c) 2024 PaddlePaddle Authors. All Rights Reserve. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +""" +This code is refer from: +https://github.com/opendatalab/UniMERNet/blob/main/unimernet/models/unimernet/configuration_unimernet_decoder.py +""" + +import copy +import math +import re +import numpy as np +import inspect +import warnings +from collections import OrderedDict +from typing import Optional, Tuple, Union, List, Dict, Any +from dataclasses import dataclass, fields, is_dataclass + +import paddle +import paddle.nn as nn +from paddle import Tensor +import paddle.nn.functional as F +from paddle.nn import CrossEntropyLoss +from paddle.nn.initializer import ( + TruncatedNormal, + Constant, + Normal, + KaimingUniform, + XavierUniform, + XavierNormal, +) + +zeros_ = Constant(value=0.0) +ones_ = Constant(value=1.0) +kaiming_normal_ = KaimingUniform(nonlinearity="relu") +trunc_normal_ = TruncatedNormal(std=0.02) +xavier_uniform_ = XavierUniform() +xavier_normal_ = XavierNormal() + + +class ModelOutput(OrderedDict): + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + + def __post_init__(self): + class_fields = fields(self) + + if not len(class_fields): + raise ValueError(f"{self.__class__.__name__} has no fields.") + if not all(field.default is None for field in class_fields[1:]): + raise ValueError( + f"{self.__class__.__name__} should not have more than one required field." + ) + + first_field = getattr(self, class_fields[0].name) + other_fields_are_none = all( + getattr(self, field.name) is None for field in class_fields[1:] + ) + if other_fields_are_none: + if isinstance(first_field, dict): + iterator = first_field.items() + first_field_iterator = True + else: + try: + iterator = iter(first_field) + first_field_iterator = True + except TypeError: + first_field_iterator = False + + if first_field_iterator: + for idx, element in enumerate(iterator): + if ( + not isinstance(element, (list, tuple)) + or not len(element) == 2 + or not isinstance(element[0], str) + ): + if idx == 0: + self[class_fields[0].name] = first_field + else: + raise ValueError( + f"Cannot set key/value for {element}. It needs to be a tuple (key, value)." + ) + break + setattr(self, element[0], element[1]) + if element[1] is not None: + self[element[0]] = element[1] + elif first_field is not None: + self[class_fields[0].name] = first_field + else: + for field in class_fields: + v = getattr(self, field.name) + if v is not None: + self[field.name] = v + + def __delitem__(self, *args, **kwargs): + raise Exception( + f"You cannot use ``__delitem__`` on a {self.__class__.__name__} instance." + ) + + def setdefault(self, *args, **kwargs): + raise Exception( + f"You cannot use ``setdefault`` on a {self.__class__.__name__} instance." + ) + + def pop(self, *args, **kwargs): + raise Exception( + f"You cannot use ``pop`` on a {self.__class__.__name__} instance." + ) + + def update(self, *args, **kwargs): + raise Exception( + f"You cannot use ``update`` on a {self.__class__.__name__} instance." + ) + + def __getitem__(self, k): + if isinstance(k, str): + inner_dict = dict(self.items()) + return inner_dict[k] + else: + return self.to_tuple()[k] + + def __setattr__(self, name, value): + if name in self.keys() and value is not None: + super().__setitem__(name, value) + super().__setattr__(name, value) + + def __setitem__(self, key, value): + super().__setitem__(key, value) + super().__setattr__(key, value) + + def __reduce__(self): + if not is_dataclass(self): + return super().__reduce__() + callable, _args, *remaining = super().__reduce__() + args = tuple(getattr(self, field.name) for field in fields(self)) + return callable, args, *remaining + + def to_tuple(self): + return tuple(self[k] for k in self.keys()) + + +@dataclass +class BaseModelOutputWithPastAndCrossAttentions(ModelOutput): + last_hidden_state = None + past_key_values = None + hidden_states = None + attentions = None + cross_attentions = None + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + + +@dataclass +class Seq2SeqLMOutput(ModelOutput): + loss = None + logits = None + past_key_values = None + decoder_hidden_states = None + decoder_attentions = None + cross_attentions = None + encoder_last_hidden_state = None + encoder_hidden_states = None + encoder_attentions = None + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + + +class MBartConfig(object): + + model_type = "mbart" + keys_to_ignore_at_inference = ["past_key_values"] + attribute_map = { + "num_attention_heads": "encoder_attention_heads", + "hidden_size": "d_model", + } + + def __init__( + self, + vocab_size=50265, + max_position_embeddings=1024, + encoder_layers=12, + encoder_ffn_dim=4096, + encoder_attention_heads=16, + decoder_layers=12, + decoder_ffn_dim=4096, + decoder_attention_heads=16, + encoder_layerdrop=0.0, + decoder_layerdrop=0.0, + use_cache=True, + is_encoder_decoder=True, + activation_function="gelu", + d_model=1024, + dropout=0.1, + output_hidden_states=False, + use_return_dict=True, + attention_dropout=0.0, + activation_dropout=0.0, + init_std=0.02, + classifier_dropout=0.0, + scale_embedding=False, + pad_token_id=1, + bos_token_id=0, + eos_token_id=2, + forced_eos_token_id=2, + _attn_implementation="eager", + hidden_size=1024, + use_parallel=False, + parallel_step=2, + is_export=False, + **kwargs, + ): + self.vocab_size = vocab_size + self.hidden_size = hidden_size + self.max_position_embeddings = max_position_embeddings + self.d_model = d_model + self.encoder_ffn_dim = encoder_ffn_dim + self.encoder_layers = encoder_layers + self.encoder_attention_heads = encoder_attention_heads + self.decoder_ffn_dim = decoder_ffn_dim + self.decoder_layers = decoder_layers + self.decoder_attention_heads = decoder_attention_heads + self.dropout = dropout + self.output_hidden_states = output_hidden_states + self.use_return_dict = use_return_dict + self.attention_dropout = attention_dropout + self.activation_dropout = activation_dropout + self.activation_function = activation_function + self.init_std = init_std + self.encoder_layerdrop = encoder_layerdrop + self.decoder_layerdrop = decoder_layerdrop + self.classifier_dropout = classifier_dropout + self.use_cache = use_cache + self.num_hidden_layers = encoder_layers + self.scale_embedding = ( + scale_embedding # scale factor will be sqrt(d_model) if True + ) + self.pad_token_id = pad_token_id + self.bos_token_id = bos_token_id + self.eos_token_id = eos_token_id + self.is_encoder_decoder = is_encoder_decoder + self.forced_eos_token_id = forced_eos_token_id + self._attn_implementation = _attn_implementation + self.use_parallel = use_parallel + self.parallel_step = parallel_step + self.is_export = is_export + super().__init__() + + +@dataclass +class AttentionMaskConverter: + """ + A utility class for converting attention masks used in transformer models. + + This class handles the conversion of attention masks based on whether the + attention mechanism is causal (i.e., preventing information flow from future + tokens to past tokens) and whether a sliding window approach is used. + + Attributes: + is_causal (bool): Indicates if the attention mechanism is causal. + sliding_window (Optional[int]): Specifies the size of the sliding window + for local attention, if applicable. + + Args: + is_causal (bool): Determines if the attention mask should enforce causality. + sliding_window (Optional[int], optional): The size of the sliding window + for local attention. Default is None. + """ + + is_causal: bool + sliding_window: int + + def __init__(self, is_causal: bool, sliding_window=None): + self.is_causal = is_causal + self.sliding_window = sliding_window + + if self.sliding_window is not None and self.sliding_window <= 0: + raise ValueError( + f"Make sure that when passing `sliding_window` that its value is a strictly positive integer, not `{self.sliding_window}`" + ) + + @staticmethod + def _make_causal_mask( + input_ids_shape, + dtype, + past_key_values_length=0, + sliding_window=None, + is_export=False, + ): + bsz, tgt_len = input_ids_shape + if is_export: + mask = paddle.full( + (tgt_len, tgt_len), paddle.finfo(dtype).min, dtype="float64" + ) + else: + mask = paddle.full((tgt_len, tgt_len), paddle.finfo(dtype).min) + mask_cond = paddle.arange(mask.shape[-1]) + mask = mask.masked_fill_( + mask_cond < (mask_cond + 1).reshape([mask.shape[-1], 1]), 0 + ) + return mask[None, None, :, :].expand( + [bsz, 1, tgt_len, tgt_len + past_key_values_length] + ) + + def to_4d_export( + self, + attention_mask_2d, + query_length, + dtype, + key_value_length, + is_export=False, + ): + input_shape = (attention_mask_2d.shape[0], query_length) + expanded_attn_mask = self._expand_mask( + attention_mask_2d, dtype, tgt_len=input_shape[-1] + ) + expanded_4d_mask = expanded_attn_mask + + return expanded_4d_mask + + def to_4d( + self, + attention_mask_2d, + query_length, + dtype, + key_value_length, + is_export=False, + ): + + input_shape = (attention_mask_2d.shape[0], query_length) + causal_4d_mask = None + if (input_shape[-1] > 1 or self.sliding_window is not None) and self.is_causal: + if key_value_length is None: + raise ValueError( + "This attention mask converter is causal. Make sure to pass `key_value_length` to correctly create a causal mask." + ) + + past_key_values_length = key_value_length - query_length + + causal_4d_mask = self._make_causal_mask( + input_shape, + dtype, + past_key_values_length=past_key_values_length, + sliding_window=self.sliding_window, + is_export=is_export, + ) + elif self.sliding_window is not None: + raise NotImplementedError( + "Sliding window is currently only implemented for causal masking" + ) + + expanded_attn_mask = self._expand_mask( + attention_mask_2d, dtype, tgt_len=input_shape[-1] + ) + + if causal_4d_mask is not None: + if is_export: + expanded_attn_mask = causal_4d_mask + return expanded_attn_mask + else: + expanded_attn_mask = causal_4d_mask.masked_fill_( + expanded_attn_mask.cast(paddle.bool), paddle.finfo(dtype).min + ) + + expanded_4d_mask = expanded_attn_mask + + return expanded_4d_mask + + def _expand_mask(self, mask, dtype, tgt_len=None): + bsz, src_len = mask.shape + tgt_len = tgt_len if tgt_len is not None else src_len + expanded_mask = ( + mask[:, None, None, :].expand([bsz, 1, tgt_len, src_len]).cast(dtype) + ) + inverted_mask = 1.0 - expanded_mask + return inverted_mask.masked_fill_( + inverted_mask.cast(paddle.bool), paddle.finfo(dtype).min + ) + + +def _prepare_4d_attention_mask(mask, dtype, tgt_len=None): + return AttentionMaskConverter._expand_mask(mask=mask, dtype=dtype, tgt_len=tgt_len) + + +def _prepare_4d_causal_attention_mask_export( + attention_mask, + input_shape, + inputs_embeds, + past_key_values_length, + sliding_window=None, + is_export=False, +): + + attn_mask_converter = AttentionMaskConverter( + is_causal=True, sliding_window=sliding_window + ) + key_value_length = input_shape[-1] + past_key_values_length + + shape = attention_mask.shape + len_shape = len(shape) + + attention_mask = attn_mask_converter.to_4d_export( + attention_mask, + input_shape[-1], + key_value_length=key_value_length, + dtype=inputs_embeds.dtype, + is_export=is_export, + ) + return attention_mask + + +def _prepare_4d_causal_attention_mask( + attention_mask, + input_shape, + inputs_embeds, + past_key_values_length, + sliding_window=None, + is_export=False, +): + + attn_mask_converter = AttentionMaskConverter( + is_causal=True, sliding_window=sliding_window + ) + key_value_length = input_shape[-1] + past_key_values_length + + shape = attention_mask.shape + len_shape = len(shape) + if (attention_mask is not None) and (len_shape == 2): + attention_mask = attn_mask_converter.to_4d( + attention_mask, + input_shape[-1], + key_value_length=key_value_length, + dtype=inputs_embeds.dtype, + is_export=is_export, + ) + + return attention_mask + elif attention_mask is not None and len(attention_mask.shape) == 4: + expected_shape = (input_shape[0], 1, input_shape[1], key_value_length) + if tuple(attention_mask.shape) != expected_shape: + raise ValueError( + f"Incorrect 4D attention_mask shape: {tuple(attention_mask.shape)}; expected: {expected_shape}." + ) + else: + inverted_mask = 1.0 - attention_mask + attention_mask = inverted_mask.masked_fill_( + inverted_mask.to(paddle.bool), paddle.finfo(inputs_embeds.dtype).min + ) + else: + attention_mask = attn_mask_converter.to_causal_4d( + input_shape[0], + input_shape[-1], + key_value_length, + dtype=inputs_embeds.dtype, + ) + + return attention_mask + + +class MBartLearnedPositionalEmbedding(nn.Embedding): + """ + This module learns positional embeddings up to a fixed maximum size. + """ + + def __init__(self, num_embeddings, embedding_dim): + self.offset = 2 + super().__init__(num_embeddings + self.offset, embedding_dim) + + def forward(self, input_ids, past_key_values_length=0): + """`input_ids' shape is expected to be [bsz x seqlen].""" + bsz, seq_len = input_ids.shape[:2] + positions = paddle.arange( + past_key_values_length, past_key_values_length + seq_len, dtype=paddle.int64 + ).expand([bsz, -1]) + return nn.Embedding.forward(self, positions + self.offset) + + +class MBartPreTrainedModel(nn.Layer): + base_model_prefix = "model" + supports_gradient_checkpointing = True + _no_split_modules = ["MBartDecoderLayer", "MBartAttention"] + _supports_flash_attn_2 = True + + def __init__(self, config): + super().__init__() + self.config = config + + def _initialize_weights(self, module): + """ + Initialize the weights if they are not already initialized. + """ + if getattr(module, "_is_hf_initialized", False): + return + self._init_weights(module) + + def post_init(self): + self.apply(self._initialize_weights) + + def _init_weights(self, module): + std = self.config.init_std + normal_ = Normal(mean=0.0, std=std) + if isinstance(module, nn.Linear): + normal_(module.weight) + if module.bias is not None: + zeros_(module.bias) + elif isinstance(module, nn.Embedding): + normal_(module.weight) + if module._padding_idx is not None: + zeros_(module.weight[module._padding_idx]) + + @property + def dummy_inputs(self): + pad_token = self.config.pad_token_id + input_ids = paddle.tensor([[0, 6, 10, 4, 2], [0, 8, 12, 2, pad_token]]) + dummy_inputs = { + "attention_mask": input_ids.ne(pad_token), + "input_ids": input_ids, + } + return dummy_inputs + + +class MBartAttention(nn.Layer): + """Multi-headed attention from 'Attention Is All You Need' paper""" + + def __init__( + self, + embed_dim, + num_heads, + dropout: float = 0.0, + is_decoder: bool = False, + bias: bool = True, + is_causal: bool = False, + config=None, + ): + super().__init__() + self.embed_dim = embed_dim + self.num_heads = num_heads + self.dropout = dropout + self.head_dim = embed_dim // num_heads + self.config = config + + if (self.head_dim * num_heads) != self.embed_dim: + raise ValueError( + f"embed_dim must be divisible by num_heads (got `embed_dim`: {self.embed_dim}" + f" and `num_heads`: {num_heads})." + ) + self.scaling = self.head_dim**-0.5 + self.is_decoder = is_decoder + self.is_causal = is_causal + + self.k_proj = nn.Linear(embed_dim, embed_dim, bias_attr=bias) + self.v_proj = nn.Linear(embed_dim, embed_dim, bias_attr=bias) + self.q_proj = nn.Linear(embed_dim, embed_dim, bias_attr=bias) + self.out_proj = nn.Linear(embed_dim, embed_dim, bias_attr=bias) + + def _shape(self, tensor, seq_len, bsz): + return tensor.reshape([bsz, seq_len, self.num_heads, self.head_dim]).transpose( + [0, 2, 1, 3] + ) + + def forward( + self, + hidden_states, + key_value_states=None, + past_key_value=None, + attention_mask=None, + layer_head_mask=None, + output_attentions=False, + ): + + is_cross_attention = key_value_states is not None + + bsz, tgt_len, _ = paddle.shape(hidden_states) + query_states = self.q_proj(hidden_states) * self.scaling + if ( + is_cross_attention + and past_key_value is not None + and past_key_value[0].shape[2] == key_value_states.shape[1] + ): + key_states = past_key_value[0] + value_states = past_key_value[1] + elif is_cross_attention: + key_states = self._shape(self.k_proj(key_value_states), -1, bsz) + value_states = self._shape(self.v_proj(key_value_states), -1, bsz) + elif past_key_value is not None: + key_states = self._shape(self.k_proj(hidden_states), -1, bsz) + value_states = self._shape(self.v_proj(hidden_states), -1, bsz) + key_states = paddle.concat([past_key_value[0], key_states], axis=2) + value_states = paddle.concat([past_key_value[1], value_states], axis=2) + else: + key_states = self._shape(self.k_proj(hidden_states), -1, bsz) + value_states = self._shape(self.v_proj(hidden_states), -1, bsz) + + if self.is_decoder: + past_key_value = (key_states, value_states) + + proj_shape = (bsz * self.num_heads, -1, self.head_dim) + query_states = self._shape(query_states, tgt_len, bsz).reshape(proj_shape) + key_states = key_states.reshape(proj_shape) + value_states = value_states.reshape(proj_shape) + + src_len = key_states.shape[1] + attn_weights = paddle.bmm(query_states, key_states.transpose([0, 2, 1])) + + if attention_mask is not None: + attn_weights = ( + attn_weights.reshape([bsz, self.num_heads, tgt_len, src_len]) + + attention_mask + ) + attn_weights = attn_weights.reshape( + [bsz * self.num_heads, tgt_len, src_len] + ) + + attn_weights = nn.functional.softmax(attn_weights, axis=-1) + if layer_head_mask is not None: + if tuple(layer_head_mask.shape) != (self.num_heads,): + raise ValueError( + f"Head mask for a single layer should be of shape {(self.num_heads,)}, but is" + f" {layer_head_mask.shape}" + ) + attn_weights = layer_head_mask.reshape( + [1, -1, 1, 1] + ) * attn_weights.reshape([bsz, self.num_heads, tgt_len, src_len]) + attn_weights = attn_weights.reshape( + [bsz * self.num_heads, tgt_len, src_len] + ) + + if output_attentions: + attn_weights_reshaped = attn_weights.reshape( + [bsz, self.num_heads, tgt_len, src_len] + ) + attn_weights = attn_weights_reshaped.reshape( + [bsz * self.num_heads, tgt_len, src_len] + ) + else: + attn_weights_reshaped = None + attn_probs = nn.functional.dropout( + attn_weights, p=self.dropout, training=self.training + ) + attn_output = paddle.bmm(attn_probs, value_states) + + attn_output = attn_output.reshape([bsz, self.num_heads, tgt_len, self.head_dim]) + attn_output = attn_output.transpose([0, 2, 1, 3]) + + attn_output = attn_output.reshape([bsz, tgt_len, self.embed_dim]) + attn_output = self.out_proj(attn_output) + return attn_output, attn_weights_reshaped, past_key_value + + +MBART_ATTENTION_CLASSES = { + "eager": MBartAttention, +} + + +class MBartDecoderLayer(nn.Layer): + def __init__(self, config): + super().__init__() + self.embed_dim = config.d_model + self.self_attn = MBART_ATTENTION_CLASSES[config._attn_implementation]( + embed_dim=self.embed_dim, + num_heads=config.decoder_attention_heads, + dropout=config.attention_dropout, + is_decoder=True, + is_causal=True, + config=config, + ) + self.is_export = config.is_export + self.dropout = config.dropout + self.activation_fn = F.gelu + self.activation_dropout = config.activation_dropout + + self.self_attn_layer_norm = nn.LayerNorm(self.embed_dim) + self.encoder_attn = MBART_ATTENTION_CLASSES[config._attn_implementation]( + self.embed_dim, + config.decoder_attention_heads, + dropout=config.attention_dropout, + is_decoder=True, + config=config, + ) + self.encoder_attn_layer_norm = nn.LayerNorm(self.embed_dim) + self.fc1 = nn.Linear(self.embed_dim, config.decoder_ffn_dim) + self.fc2 = nn.Linear(config.decoder_ffn_dim, self.embed_dim) + self.final_layer_norm = nn.LayerNorm(self.embed_dim) + + def forward( + self, + hidden_states, + attention_mask=None, + encoder_hidden_states=None, + encoder_attention_mask=None, + layer_head_mask=None, + cross_attn_layer_head_mask=None, + past_key_value: Optional[Tuple[paddle.Tensor]] = None, + output_attentions: Optional[bool] = False, + use_cache: Optional[bool] = True, + ) -> paddle.Tensor: + + residual = hidden_states + hidden_states = self.self_attn_layer_norm(hidden_states) + self_attn_past_key_value = ( + past_key_value[:2] if past_key_value is not None else None + ) + + hidden_states, self_attn_weights, present_key_value = self.self_attn( + hidden_states=hidden_states, + past_key_value=self_attn_past_key_value, + attention_mask=attention_mask, + layer_head_mask=layer_head_mask, + output_attentions=output_attentions, + ) + hidden_states = nn.functional.dropout( + hidden_states, p=self.dropout, training=self.training + ) + hidden_states = residual + hidden_states + + cross_attn_present_key_value = None + cross_attn_weights = None + if encoder_hidden_states is not None: + residual = hidden_states + hidden_states = self.encoder_attn_layer_norm(hidden_states) + cross_attn_past_key_value = ( + past_key_value[-2:] if past_key_value is not None else None + ) + hidden_states, cross_attn_weights, cross_attn_present_key_value = ( + self.encoder_attn( + hidden_states=hidden_states, + key_value_states=encoder_hidden_states, + attention_mask=encoder_attention_mask, + layer_head_mask=cross_attn_layer_head_mask, + past_key_value=cross_attn_past_key_value, + output_attentions=output_attentions, + ) + ) + hidden_states = nn.functional.dropout( + hidden_states, p=self.dropout, training=self.training + ) + hidden_states = residual + hidden_states + + present_key_value = present_key_value + cross_attn_present_key_value + + residual = hidden_states + hidden_states = self.final_layer_norm(hidden_states) + hidden_states = self.activation_fn(self.fc1(hidden_states)) + hidden_states = nn.functional.dropout( + hidden_states, p=self.activation_dropout, training=self.training + ) + hidden_states = self.fc2(hidden_states) + hidden_states = nn.functional.dropout( + hidden_states, p=self.dropout, training=self.training + ) + hidden_states = residual + hidden_states + outputs = (hidden_states,) + + if output_attentions: + outputs += (self_attn_weights, cross_attn_weights) + + if self.is_export: + outputs += (present_key_value,) + else: + if use_cache: + outputs += (present_key_value,) + return outputs + + +class MBartForCausalLM(MBartPreTrainedModel): + _tied_weights_keys = ["lm_head.weight"] + + def __init__(self, config): + config = copy.deepcopy(config) + config.is_decoder = True + config.is_encoder_decoder = False + super().__init__(config) + self.model = MBartDecoderWrapper(config) + self.lm_head = nn.Linear(config.hidden_size, config.vocab_size, bias_attr=False) + + self.post_init() + + def get_input_embeddings(self): + return self.model.decoder.embed_tokens + + def set_input_embeddings(self, value): + self.model.decoder.embed_tokens = value + + def get_output_embeddings(self): + return self.lm_head + + def set_output_embeddings(self, new_embeddings): + self.lm_head = new_embeddings + + def set_decoder(self, decoder): + self.model.decoder = decoder + + def get_decoder(self): + return self.model.decoder + + def forward( + self, + input_ids=None, + attention_mask=None, + encoder_hidden_states=None, + encoder_attention_mask=None, + head_mask=None, + cross_attn_head_mask=None, + past_key_values=None, + inputs_embeds=None, + labels=None, + use_cache=None, + output_attentions=None, + output_hidden_states=None, + return_dict=None, + ): + + output_attentions = ( + output_attentions + if output_attentions is not None + else self.config.output_attentions + ) + output_hidden_states = ( + output_hidden_states + if output_hidden_states is not None + else self.config.output_hidden_states + ) + return_dict = ( + return_dict if return_dict is not None else self.config.use_return_dict + ) + + outputs = self.model.decoder( + input_ids=input_ids, + attention_mask=attention_mask, + encoder_hidden_states=encoder_hidden_states, + encoder_attention_mask=encoder_attention_mask, + head_mask=head_mask, + cross_attn_head_mask=cross_attn_head_mask, + past_key_values=past_key_values, + inputs_embeds=inputs_embeds, + use_cache=use_cache, + output_attentions=output_attentions, + output_hidden_states=output_hidden_states, + return_dict=return_dict, + ) + + logits = self.lm_head(outputs[0]) + + loss = None + if labels is not None: + labels = labels + loss_fct = CrossEntropyLoss() + loss = loss_fct( + logits.reshape([-1, self.config.vocab_size]), labels.reshape([-1]) + ) + + if not return_dict: + output = (logits,) + outputs[1:] + return (loss,) + output if loss is not None else output + + return CausalLMOutputWithCrossAttentions( + loss=loss, + logits=logits, + past_key_values=outputs.past_key_values, + hidden_states=outputs.hidden_states, + attentions=outputs.attentions, + cross_attentions=outputs.cross_attentions, + ) + + def prepare_inputs_for_generation( + self, + input_ids, + past_key_values=None, + attention_mask=None, + use_cache=None, + **kwargs, + ): + if attention_mask is None: + attention_mask = input_ids.new_ones(input_ids.shape) + + if past_key_values: + past_length = past_key_values[0][0].shape[2] + + if input_ids.shape[1] > past_length: + remove_prefix_length = past_length + else: + remove_prefix_length = input_ids.shape[1] - 1 + + input_ids = input_ids[:, remove_prefix_length:] + return { + "input_ids": input_ids, + "attention_mask": attention_mask, + "past_key_values": past_key_values, + "use_cache": use_cache, + } + + @staticmethod + def _reorder_cache(past_key_values, beam_idx): + reordered_past = () + for layer_past in past_key_values: + reordered_past += ( + tuple( + past_state.index_select(0, beam_idx) for past_state in layer_past + ), + ) + return reordered_past + + +class myLayerNorm(nn.LayerNorm): + """ + Custom implementation of Layer Normalization, with additional options. + + This class extends the standard LayerNorm to include optional features, + such as drop block regularization, which might be used for improving + model generalization. + + Args: + num_channels (int): The number of features or channels in the input. + eps (float, optional): A small value added to the denominator for numerical stability. Default is 1e-5. + affine (bool, optional): If True, this module has learnable affine parameters (gamma and beta). Default is True. + drop_block (optional): Additional regularization technique that might be applied. Default is None. + + """ + + def __init__( + self, + num_channels, + eps=1e-5, + affine=True, + drop_block=None, + ): + super(nn.LayerNorm, self).__init__() + self._epsilon = eps + self.num_channels = num_channels + if affine: + self.weight = paddle.create_parameter([num_channels], dtype="float32") + self.bias = paddle.create_parameter([num_channels], dtype="float32") + ones_(self.weight) + zeros_(self.bias) + + def forward(self, x): + x = F.layer_norm( + x, + self.num_channels, + weight=self.weight, + bias=self.bias, + epsilon=self._epsilon, + ) + return x + + +class MBartDecoder(MBartPreTrainedModel): + """ + Transformer decoder consisting of *config.decoder_layers* layers. Each layer is a [`MBartDecoderLayer`] + + Args: + config + embed_tokens (nn.Embedding): output embedding + """ + + def __init__(self, config, embed_tokens=None): + super().__init__(config) + self.dropout = config.dropout + self.layerdrop = config.decoder_layerdrop + self.padding_idx = config.pad_token_id + self.max_target_positions = config.max_position_embeddings + self.embed_scale = math.sqrt(config.d_model) if config.scale_embedding else 1.0 + + self.embed_tokens = nn.Embedding( + config.vocab_size, config.d_model, self.padding_idx + ) + + if embed_tokens is not None: + self.embed_tokens.weight = embed_tokens.weight + + self.embed_positions = MBartLearnedPositionalEmbedding( + config.max_position_embeddings, + config.d_model, + ) + self.layers = nn.LayerList( + [MBartDecoderLayer(config) for _ in range(config.decoder_layers)] + ) + self._use_flash_attention_2 = config._attn_implementation == "flash_attention_2" + self.layernorm_embedding = myLayerNorm(config.d_model, affine=True) + self.layer_norm = nn.LayerNorm(config.d_model) + + self.gradient_checkpointing = False + # Initialize weights and apply final processing + self.post_init() + self.is_export = config.is_export + + def get_input_embeddings(self): + return self.embed_tokens + + def set_input_embeddings(self, value): + self.embed_tokens = value + + def forward( + self, + input_ids=None, + attention_mask=None, + encoder_hidden_states=None, + encoder_attention_mask=None, + head_mask=None, + cross_attn_head_mask=None, + past_key_values=None, + inputs_embeds=None, + use_cache=None, + output_attentions=None, + output_hidden_states=None, + return_dict=None, + ): + + output_attentions = ( + output_attentions + if output_attentions is not None + else self.config.output_attentions + ) + output_hidden_states = ( + output_hidden_states + if output_hidden_states is not None + else self.config.output_hidden_states + ) + use_cache = use_cache if use_cache is not None else self.config.use_cache + return_dict = ( + return_dict if return_dict is not None else self.config.use_return_dict + ) + + if input_ids is not None and inputs_embeds is not None: + raise ValueError( + "You cannot specify both decoder_input_ids and decoder_inputs_embeds at the same time" + ) + elif input_ids is not None: + input = input_ids + input_shape = input.shape + input_ids = input_ids.reshape([-1, input_shape[-1]]) + elif inputs_embeds is not None: + input_shape = inputs_embeds.shape[:-1] + input = inputs_embeds[:, :, -1] + else: + raise ValueError( + "You have to specify either decoder_input_ids or decoder_inputs_embeds" + ) + + past_key_values_length = ( + past_key_values[0][0].shape[2] if past_key_values is not None else 0 + ) + + if inputs_embeds is None: + inputs_embeds = self.embed_tokens(input_ids) * self.embed_scale + + if self._use_flash_attention_2: + attention_mask = ( + attention_mask + if (attention_mask is not None and 0 in attention_mask) + else None + ) + else: + attention_mask = _prepare_4d_causal_attention_mask( + attention_mask, + input_shape, + inputs_embeds, + past_key_values_length, + is_export=self.is_export, + ) + + if encoder_hidden_states is not None and encoder_attention_mask is not None: + if self._use_flash_attention_2: + encoder_attention_mask = ( + encoder_attention_mask if 0 in encoder_attention_mask else None + ) + else: + encoder_attention_mask = _prepare_4d_attention_mask( + encoder_attention_mask, inputs_embeds.dtype, tgt_len=input_shape[-1] + ) + + # embed positions + positions = self.embed_positions(input, past_key_values_length) + + hidden_states = inputs_embeds + positions + hidden_states = self.layernorm_embedding(hidden_states) + + hidden_states = nn.functional.dropout( + hidden_states, p=self.dropout, training=self.training + ) + + if self.gradient_checkpointing and self.training: + if use_cache: + print( + "`use_cache=True` is incompatible with gradient checkpointing`. Setting `use_cache=False`..." + ) + use_cache = False + + all_hidden_states = () if output_hidden_states else None + all_self_attns = () if output_attentions else None + all_cross_attentions = ( + () if (output_attentions and encoder_hidden_states is not None) else None + ) + next_decoder_cache = () if use_cache else None + + for attn_mask, mask_name in zip( + [head_mask, cross_attn_head_mask], ["head_mask", "cross_attn_head_mask"] + ): + if attn_mask is not None: + if attn_mask.shape[0] != len(self.layers): + raise ValueError( + f"The `{mask_name}` should be specified for {len(self.layers)} layers, but it is for" + f" {attn_mask.shape[0]}." + ) + + for idx, decoder_layer in enumerate(self.layers): + if output_hidden_states: + all_hidden_states += (hidden_states,) + if self.training: + dropout_probability = paddle.rand([]) + if dropout_probability < self.layerdrop: + continue + + past_key_value = ( + past_key_values[idx] if past_key_values is not None else None + ) + + if self.gradient_checkpointing and self.training: + layer_outputs = self._gradient_checkpointing_func( + decoder_layer.__call__, + hidden_states, + attention_mask, + encoder_hidden_states, + encoder_attention_mask, + head_mask[idx] if head_mask is not None else None, + ( + cross_attn_head_mask[idx] + if cross_attn_head_mask is not None + else None + ), + None, + output_attentions, + use_cache, + ) + else: + layer_outputs = decoder_layer( + hidden_states, + attention_mask=attention_mask, + encoder_hidden_states=encoder_hidden_states, + encoder_attention_mask=encoder_attention_mask, + layer_head_mask=(head_mask[idx] if head_mask is not None else None), + cross_attn_layer_head_mask=( + cross_attn_head_mask[idx] + if cross_attn_head_mask is not None + else None + ), + past_key_value=past_key_value, + output_attentions=output_attentions, + use_cache=use_cache, + ) + hidden_states = layer_outputs[0] + + if use_cache: + next_decoder_cache += (layer_outputs[3 if output_attentions else 1],) + + if output_attentions: + all_self_attns += (layer_outputs[1],) + + if encoder_hidden_states is not None: + all_cross_attentions += (layer_outputs[2],) + + hidden_states = self.layer_norm(hidden_states) + + if output_hidden_states: + all_hidden_states += (hidden_states,) + + next_cache = next_decoder_cache if use_cache else None + if not return_dict: + return tuple( + v + for v in [ + hidden_states, + next_cache, + all_hidden_states, + all_self_attns, + all_cross_attentions, + ] + if v is not None + ) + return BaseModelOutputWithPastAndCrossAttentions( + last_hidden_state=hidden_states, + past_key_values=next_cache, + hidden_states=all_hidden_states, + attentions=all_self_attns, + cross_attentions=all_cross_attentions, + ) + + +class MBartDecoderWrapper(MBartPreTrainedModel): + """ + This wrapper class is a helper class to correctly load pretrained checkpoints when the causal language model is + used in combination with the [`EncoderDecoderModel`] framework. + """ + + def __init__(self, config): + super().__init__(config) + self.decoder = MBartDecoder(config) + + def forward(self, *args, **kwargs): + return self.decoder(*args, **kwargs) + + +def _in_projection( + q: paddle.Tensor, + k: paddle.Tensor, + v: paddle.Tensor, + w_q: paddle.Tensor, + w_k: paddle.Tensor, + w_v: paddle.Tensor, + b_q: Optional[paddle.Tensor] = None, + b_k: Optional[paddle.Tensor] = None, + b_v: Optional[paddle.Tensor] = None, +) -> Tuple[paddle.Tensor, paddle.Tensor, paddle.Tensor]: + + Eq, Ek, Ev = q.shape[-1], k.shape[-1], v.shape[-1] + assert w_q.shape == ( + Eq, + Eq, + ), f"expecting query weights shape of {(Eq, Eq)}, but got {w_q.shape}" + assert w_k.shape == ( + Eq, + Ek, + ), f"expecting key weights shape of {(Eq, Ek)}, but got {w_k.shape}" + assert w_v.shape == ( + Eq, + Ev, + ), f"expecting value weights shape of {(Eq, Ev)}, but got {w_v.shape}" + assert b_q is None or b_q.shape == ( + Eq, + ), f"expecting query bias shape of {(Eq,)}, but got {b_q.shape}" + assert b_k is None or b_k.shape == ( + Eq, + ), f"expecting key bias shape of {(Eq,)}, but got {b_k.shape}" + assert b_v is None or b_v.shape == ( + Eq, + ), f"expecting value bias shape of {(Eq,)}, but got {b_v.shape}" + return linear(q, w_q.T, b_q), linear(k, w_k.T, b_k), linear(v, w_v.T, b_v) + + +def _scaled_dot_product_attention( + q: paddle.Tensor, + k: paddle.Tensor, + v: paddle.Tensor, + attn_mask: Optional[paddle.Tensor] = None, + dropout_p: float = 0.0, +) -> Tuple[paddle.Tensor, paddle.Tensor]: + + B, Nt, E = q.shape + q = q / math.sqrt(E) + attn = paddle.bmm(q, k.transpose([0, 2, 1])) + if attn_mask is not None: + attn += attn_mask + attn = F.softmax(attn, axis=-1) + if dropout_p > 0.0: + attn = F.dropout(attn, p=dropout_p) + output = paddle.bmm(attn, v) + return output, attn + + +def linear(x, w, b, is_transpose): + + if b is not None: + return paddle.matmul(x, w, transpose_y=is_transpose) + b + else: + return paddle.matmul(x, w, transpose_y=is_transpose) + + +def _in_projection_packed( + q: Tensor, + k: Tensor, + v: Tensor, + w: Tensor, + b: Optional[Tensor] = None, + is_export=False, +) -> List[Tensor]: + + E = paddle.shape(q)[-1] + if k is v: + if q is k: + proj = linear(q, w, b, is_transpose=True) + if is_export: + B, D, L = paddle.shape(proj) + proj = proj.reshape([B, D, 3, E]) + proj = ( + proj.unsqueeze(0) + .transpose([3, 1, 2, 0, 4]) + .squeeze(-2) + .contiguous() + ) + else: + proj = ( + proj.unflatten(-1, (3, E)) + .unsqueeze(0) + .transpose([3, 1, 2, 0, 4]) + .squeeze(-2) + .contiguous() + ) + return proj[0], proj[1], proj[2] + else: + w_q, w_k, w_v = w.chunk(3) + if b is None: + b_q = b_k = b_v = None + else: + b_q, b_k, b_v = b.chunk(3) + return linear(q, w_q, b_q), linear(k, w_k, b_k), linear(v, w_v, b_v) + + +def multi_head_attention_forward( + query: paddle.Tensor, + key: paddle.Tensor, + value: paddle.Tensor, + embed_dim_to_check: int, + num_heads: int, + in_proj_weight: paddle.Tensor, + in_proj_bias: Optional[paddle.Tensor], + bias_k: Optional[paddle.Tensor], + bias_v: Optional[paddle.Tensor], + add_zero_attn: bool, + dropout_p: float, + out_proj_weight: paddle.Tensor, + out_proj_bias: Optional[paddle.Tensor], + training: bool = True, + key_padding_mask: Optional[paddle.Tensor] = None, + need_weights: bool = True, + attn_mask: Optional[paddle.Tensor] = None, + use_separate_proj_weight: bool = False, + q_proj_weight: Optional[paddle.Tensor] = None, + k_proj_weight: Optional[paddle.Tensor] = None, + v_proj_weight: Optional[paddle.Tensor] = None, + static_k: Optional[paddle.Tensor] = None, + static_v: Optional[paddle.Tensor] = None, + is_export=False, +): + + tgt_len, bsz, embed_dim = query.shape + src_len, _, _ = key.shape + + if isinstance(embed_dim, paddle.Tensor): + head_dim = embed_dim.div(num_heads, rounding_mode="trunc") + else: + head_dim = embed_dim // num_heads + q, k, v = _in_projection_packed( + query, key, value, in_proj_weight, in_proj_bias, is_export + ) + + if key_padding_mask is not None and key_padding_mask.dtype == paddle.uint8: + warnings.warn( + "Byte tensor for key_padding_mask in nn.MultiheadAttention is deprecated. Use bool tensor instead." + ) + key_padding_mask = key_padding_mask.to(paddle.bool) + + if bias_k is not None and bias_v is not None: # False + assert static_k is None, "bias cannot be added to static key." + assert static_v is None, "bias cannot be added to static value." + k = paddle.concat([k, bias_k.repeat(1, bsz, 1)]) + v = paddle.concat([v, bias_v.repeat(1, bsz, 1)]) + else: + assert bias_k is None + assert bias_v is None + + q = q.reshape([tgt_len, bsz * num_heads, head_dim]).transpose([1, 0, 2]) + if static_k is None: # True + k = k.reshape([k.shape[0], bsz * num_heads, head_dim]).transpose([1, 0, 2]) + else: + assert ( + static_k.shape[0] == bsz * num_heads + ), f"expecting static_k.size(0) of {bsz * num_heads}, but got {static_k.shape[0]}" + assert ( + static_k.shape[2] == head_dim + ), f"expecting static_k.size(2) of {head_dim}, but got {static_k.shape[2]}" + k = static_k + if static_v is None: # True + v = v.reshape([v.shape[0], bsz * num_heads, head_dim]).transpose([1, 0, 2]) + else: + assert ( + static_v.shape[0] == bsz * num_heads + ), f"expecting static_v.size(0) of {bsz * num_heads}, but got {static_v.shape[0]}" + assert ( + static_v.shape[2] == head_dim + ), f"expecting static_v.size(2) of {head_dim}, but got {static_v.shape[2]}" + v = static_v + + src_len = k.shape[1] + + if not training: + dropout_p = 0.0 + + attn_output, attn_output_weights = _scaled_dot_product_attention( + q, k, v, attn_mask, dropout_p + ) + + attn_output = attn_output.transpose([1, 0, 2]).reshape([tgt_len, bsz, embed_dim]) + attn_output = linear( + attn_output, out_proj_weight, out_proj_bias, is_transpose=False + ) + + if need_weights: + attn_output_weights = attn_output_weights.reshape( + [bsz, num_heads, tgt_len, src_len] + ) + return attn_output, attn_output_weights.sum(axis=1) / num_heads + else: + return attn_output, None + + +class MyMultiheadAttention(nn.Layer): + """ + Custom implementation of a multi-head attention layer. + + Attributes: + __constants__ (list): List of constant attributes. + bias_k (Optional[paddle.Tensor]): Optional tensor for key bias. + bias_v (Optional[paddle.Tensor]): Optional tensor for value bias. + + Args: + embed_dim (int): Total dimension of the model. This is the size of the input feature vectors. + num_heads (int): Number of parallel attention heads. The input dimension must be divisible by the number of heads. + dropout (float, optional): Dropout probability on the attention weights. Default is 0.0. + bias (bool, optional): If True, adds a learnable bias to the output. Default is True. + add_bias_kv (bool, optional): If True, adds bias to the key and value sequences. Default is False. + add_zero_attn (bool, optional): If True, adds a zero attention head. Default is False. + kdim (int, optional): Total number of features for keys. If None, defaults to embed_dim. + vdim (int, optional): Total number of features for values. If None, defaults to embed_dim. + batch_first (bool, optional): If True, the input and output tensors are provided as (batch, seq, feature). Default is False. + device (optional): The device on which the layer's parameters should be initialized. Default is None. + dtype (optional): The data type for the parameters. Default is None. + is_export (bool, optional): If True, the layer is set up for export, potentially changing behavior for compatibility. Default is False. + """ + + __constants__ = ["batch_first"] + bias_k: Optional[paddle.Tensor] + bias_v: Optional[paddle.Tensor] + + def __init__( + self, + embed_dim, + num_heads, + dropout=0.0, + bias=True, + add_bias_kv=False, + add_zero_attn=False, + kdim=None, + vdim=None, + batch_first=False, + device=None, + dtype=None, + is_export=False, + ) -> None: + super(MyMultiheadAttention, self).__init__() + self.embed_dim = embed_dim + self.kdim = kdim if kdim is not None else embed_dim + self.vdim = vdim if vdim is not None else embed_dim + self._qkv_same_embed_dim = self.kdim == embed_dim and self.vdim == embed_dim + + self.num_heads = num_heads + self.dropout = dropout + self.batch_first = batch_first + self.head_dim = embed_dim // num_heads + self.is_export = is_export + assert ( + self.head_dim * num_heads == self.embed_dim + ), "embed_dim must be divisible by num_heads" + + if self._qkv_same_embed_dim is False: + pass + else: + if dtype is None: + dtype = paddle.float32 + self.in_proj_weight = paddle.create_parameter( + (3 * embed_dim, embed_dim), dtype + ) + self.q_proj_weight = None + self.k_proj_weight = None + self.v_proj_weight = None + + if bias: + self.in_proj_bias = paddle.create_parameter((3 * embed_dim,), dtype) + zeros_(self.in_proj_bias) + else: + self.in_proj_bias = None + self.out_proj = nn.Linear(embed_dim, embed_dim, bias_attr=bias) + + if add_bias_kv: + pass + else: + self.bias_k = self.bias_v = None + + self.add_zero_attn = add_zero_attn + + self._reset_parameters() + + def _reset_parameters(self): + + if self._qkv_same_embed_dim: + xavier_uniform_(self.in_proj_weight) + else: + xavier_uniform_(self.q_proj_weight) + xavier_uniform_(self.k_proj_weight) + xavier_uniform_(self.v_proj_weight) + + if self.in_proj_bias is not None: + zeros_(self.in_proj_bias) + zeros_(self.out_proj.bias) + if self.bias_k is not None: + xavier_normal_(self.bias_k) + if self.bias_v is not None: + xavier_normal_(self.bias_v) + + def forward( + self, + query: paddle.Tensor, + key: paddle.Tensor, + value: paddle.Tensor, + key_padding_mask: Optional[paddle.Tensor] = None, + need_weights: bool = True, + attn_mask: Optional[paddle.Tensor] = None, + ) -> Tuple[paddle.Tensor, Optional[paddle.Tensor]]: + + attn_output, attn_output_weights = multi_head_attention_forward( + query, + key, + value, + self.embed_dim, + self.num_heads, + self.in_proj_weight, + self.in_proj_bias, + self.bias_k, + self.bias_v, + self.add_zero_attn, + self.dropout, + self.out_proj.weight, + self.out_proj.bias, + training=self.training, + key_padding_mask=key_padding_mask, + need_weights=need_weights, + attn_mask=attn_mask, + is_export=self.is_export, + ) + + return attn_output, attn_output_weights + + +class LogitsProcessorList(list): + """ + A list of logits processors that can be applied sequentially. + + Methods: + __call__(input_ids, scores, **kwargs): Apply all processors to the given inputs. + """ + + def __call__(self, input_ids, scores, **kwargs): + for processor in self: + function_args = inspect.signature(processor.__call__).parameters + if len(function_args) > 2: + if not all(arg in kwargs for arg in list(function_args.keys())[2:]): + raise ValueError( + f"Make sure that all the required parameters: {list(function_args.keys())} for " + f"{processor.__class__} are passed to the logits processor." + ) + scores = processor(input_ids, scores, **kwargs) + else: + scores = processor(input_ids, scores) + return scores + + +class ForcedEOSTokenLogitsProcessor(object): + """ + A processor that forces the generation of an end-of-sequence (EOS) token + at a specified position in the sequence. + + This is typically used in language generation tasks to ensure that the + generated sequence ends properly when it reaches a certain length. + + Args: + max_length (int): The maximum length of the sequence. Forces EOS when this length is reached. + eos_token_id (Union[int, List[int]]): The ID(s) of the EOS token(s) to be forced in the sequence. + """ + + def __init__(self, max_length: int, eos_token_id: Union[int, List[int]]): + self.max_length = max_length + if isinstance(eos_token_id, int): + eos_token_id = [eos_token_id] + self.eos_token_id = eos_token_id + + def __call__(self, input_ids, scores): + cur_len = input_ids.shape[-1] + scores_processed = scores + if cur_len == self.max_length - 1: + scores_processed = paddle.full_like(scores, -math.inf) + scores_processed[:, self.eos_token_id] = 0 + return scores_processed + + +@dataclass +class CausalLMOutputWithCrossAttentions(ModelOutput): + + loss = None + logits = None + past_key_values = None + hidden_states = None + attentions = None + cross_attentions = None + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + + +@dataclass +class CausalLMOutputWithCrossAttentionsAndCounting(ModelOutput): + """ + Base class for causal language model (or autoregressive) outputs. + """ + + logits = None + counting = None + past_key_values = None + hidden_states = None + attentions = None + cross_attentions = None + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + + +class CustomMBartDecoder(MBartDecoder): + """ + A custom MBartDecoder that includes additional processing layers. + + This class extends the MBartDecoder by adding a customizable neural network + component called `counting_context_weight`, which applies a series of linear + transformations followed by ReLU activations. This can be used to modify or + enhance the decoder's behavior for specific tasks. + + Args: + config: The configuration object containing model parameters. + """ + + def __init__(self, config): + super().__init__(config) + hidden_size = config.d_model + self.is_export = config.is_export + self.counting_context_weight = nn.Sequential( + nn.Linear(config.vocab_size, hidden_size), + nn.ReLU(), + nn.Linear(hidden_size, hidden_size), + nn.ReLU(), + nn.Linear(hidden_size, config.d_model), + ) + + def forward( + self, + input_ids=None, + attention_mask=None, + count_pred=None, + encoder_hidden_states=None, + encoder_attention_mask=None, + head_mask=None, + cross_attn_head_mask=None, + past_key_values=None, + inputs_embeds=None, + use_cache=None, + output_attentions=None, + output_hidden_states=None, + return_dict=None, + ): + self.is_export = False if self.training else True + output_attentions = ( + output_attentions + if output_attentions is not None + else self.config.output_attentions + ) + output_hidden_states = ( + output_hidden_states + if output_hidden_states is not None + else self.config.output_hidden_states + ) + use_cache = use_cache if use_cache is not None else self.config.use_cache + return_dict = ( + return_dict if return_dict is not None else self.config.use_return_dict + ) + + if input_ids is not None and inputs_embeds is not None: + raise ValueError( + "You cannot specify both decoder_input_ids and decoder_inputs_embeds at the same time" + ) + elif input_ids is not None: + input = input_ids + input_shape = input.shape + input_ids = input_ids.reshape([-1, input_shape[-1]]) + elif inputs_embeds is not None: + input_shape = inputs_embeds.shape[:-1] + input = inputs_embeds[:, :, -1] + else: + raise ValueError( + "You have to specify either decoder_input_ids or decoder_inputs_embeds" + ) + + past_key_values_length = ( + past_key_values[0][0].shape[2] if past_key_values is not None else 0 + ) + + if inputs_embeds is None: + inputs_embeds = self.embed_tokens(input_ids) * self.embed_scale + + if self._use_flash_attention_2: + attention_mask = ( + attention_mask + if (attention_mask is not None and 0 in attention_mask) + else None + ) + else: + if self.is_export: + attention_mask = _prepare_4d_causal_attention_mask_export( + attention_mask, + input_shape, + inputs_embeds, + past_key_values_length, + is_export=self.is_export, + ).cast(paddle.float32) + else: + attention_mask = _prepare_4d_causal_attention_mask( + attention_mask, + input_shape, + inputs_embeds, + past_key_values_length, + is_export=self.is_export, + ) + + if encoder_hidden_states is not None and encoder_attention_mask is not None: + if self._use_flash_attention_2: + encoder_attention_mask = ( + encoder_attention_mask if 0 in encoder_attention_mask else None + ) + else: + encoder_attention_mask = _prepare_4d_attention_mask( + encoder_attention_mask, inputs_embeds.dtype, tgt_len=input_shape[-1] + ) + + # embed positions + positions = self.embed_positions(input, past_key_values_length) + + hidden_states = inputs_embeds + positions + + # TODO: add counting context weight to hidden_states + if count_pred is not None: + count_context_weight = self.counting_context_weight(count_pred) + hidden_states = hidden_states + 0.5 * count_context_weight.unsqueeze(1) + + hidden_states = self.layernorm_embedding(hidden_states) + hidden_states = nn.functional.dropout( + hidden_states, p=self.dropout, training=self.training + ) + + if self.gradient_checkpointing and self.training: + if use_cache: + print( + "`use_cache=True` is incompatible with gradient checkpointing`. Setting `use_cache=False`..." + ) + use_cache = False + + # decoder layers + all_hidden_states = () if output_hidden_states else None + all_self_attns = () if output_attentions else None + all_cross_attentions = ( + () if (output_attentions and encoder_hidden_states is not None) else None + ) + next_decoder_cache = () if use_cache else None + + # check if head_mask/cross_attn_head_mask has a correct number of layers specified if desired + for attn_mask, mask_name in zip( + [head_mask, cross_attn_head_mask], ["head_mask", "cross_attn_head_mask"] + ): + if attn_mask is not None: + if attn_mask.size()[0] != len(self.layers): + raise ValueError( + f"The `{mask_name}` should be specified for {len(self.layers)} layers, but it is for" + f" {attn_mask.size()[0]}." + ) + + for idx, decoder_layer in enumerate(self.layers): + if output_hidden_states: + all_hidden_states += (hidden_states,) + if self.training: + dropout_probability = paddle.rand([]) + if dropout_probability < self.layerdrop: + continue + + past_key_value = ( + past_key_values[idx] if past_key_values is not None else None + ) + + if self.gradient_checkpointing and self.training: + layer_outputs = self._gradient_checkpointing_func( + decoder_layer.__call__, + hidden_states, + attention_mask, + encoder_hidden_states, + encoder_attention_mask, + head_mask[idx] if head_mask is not None else None, + ( + cross_attn_head_mask[idx] + if cross_attn_head_mask is not None + else None + ), + None, + output_attentions, + use_cache, + ) + else: + layer_outputs = decoder_layer( + hidden_states, + attention_mask=attention_mask, + encoder_hidden_states=encoder_hidden_states, + encoder_attention_mask=encoder_attention_mask, + layer_head_mask=(head_mask[idx] if head_mask is not None else None), + cross_attn_layer_head_mask=( + cross_attn_head_mask[idx] + if cross_attn_head_mask is not None + else None + ), + past_key_value=past_key_value, + output_attentions=output_attentions, + use_cache=use_cache, + ) + hidden_states = layer_outputs[0] + if self.is_export: + next_decoder_cache += (layer_outputs[3 if output_attentions else 1],) + else: + if use_cache: + next_decoder_cache += ( + layer_outputs[3 if output_attentions else 1], + ) + + if output_attentions: + all_self_attns += (layer_outputs[1],) + + if encoder_hidden_states is not None: + all_cross_attentions += (layer_outputs[2],) + + hidden_states = self.layer_norm(hidden_states) + + if output_hidden_states: + all_hidden_states += (hidden_states,) + if self.is_export: + next_cache = next_decoder_cache + else: + next_cache = next_decoder_cache if use_cache else None + if not self.is_export: + if not return_dict: + return tuple( + v + for v in [ + hidden_states, + next_cache, + all_hidden_states, + all_self_attns, + all_cross_attentions, + ] + if v is not None + ) + return BaseModelOutputWithPastAndCrossAttentions( + last_hidden_state=hidden_states, + past_key_values=next_cache, + hidden_states=all_hidden_states, + attentions=all_self_attns, + cross_attentions=all_cross_attentions, + ) + + +class SelfAttentionBlock(nn.Layer): + """ + A self-attention block that implements multi-head self-attention + followed by a feed-forward network, typically used in transformer architectures. + + Args: + embed_size (int): The size of the embedding vector. + num_heads (int): The number of attention heads. + is_export (bool): Flag indicating whether to configure the layer for export. + """ + + def __init__(self, embed_size, num_heads, is_export): + super(SelfAttentionBlock, self).__init__() + self.self_attention = MyMultiheadAttention( + embed_dim=embed_size, num_heads=num_heads, is_export=is_export + ) + self.norm = nn.LayerNorm(embed_size) + + def forward(self, x): + attn_output, _ = self.self_attention(x, x, x) + x = self.norm(attn_output + x) + return x + + +class SeqCountingDecoder(nn.Layer): + """ + A custom sequence counting decoder that incorporates multi-head attention layers + and feed-forward networks to process sequences, potentially for latex code counting . + + Args: + in_features (int): The number of input features. + out_features (int): The number of output features. + num_heads (int): The number of attention heads. Defaults to 8. + num_layers (int): The number of attention layers. Defaults to 4. + is_export (bool): Flag indicating whether to configure the layer for export. + """ + + def __init__( + self, in_features, out_features, num_heads=8, num_layers=4, is_export=False + ): + super(SeqCountingDecoder, self).__init__() + + self.attention_blocks = nn.LayerList( + [ + SelfAttentionBlock( + embed_size=in_features, num_heads=num_heads, is_export=is_export + ) + for i in range(num_layers) + ] + ) + self.fc1 = nn.Linear(in_features, in_features // 2) + self.relu = nn.ReLU() + self.global_avg_pool = nn.AdaptiveAvgPool1D(1) + self.fc2 = nn.Linear(in_features // 2, out_features) + + def forward(self, x): + for block in self.attention_blocks: + x = block(x) + x = self.fc1(x) + x = self.relu(x) + x = x.transpose([0, 2, 1]) + x = self.global_avg_pool(x) + x = x.squeeze(-1) + x = self.fc2(x) + return x + + +class CustomMBartForCausalLM(MBartForCausalLM): + """ + Custom MBart model for causal language modeling with a custom decoder. + + This class extends the MBartForCausalLM by replacing its decoder with a + custom decoder, allowing for additional flexibility and features in the + decoding process. + + Args: + config: The configuration object containing model parameters. + length_aware (bool): A flag to enable or configure length-aware mechanisms. + """ + + def __init__(self, config, length_aware=True): + super().__init__(config) + self.model.decoder = CustomMBartDecoder(config) + self.counting_decoder = SeqCountingDecoder( + config.d_model, config.vocab_size, is_export=config.is_export + ) + self.length_aware = length_aware + + def forward( + self, + input_ids=None, + attention_mask=None, + encoder_hidden_states=None, + encoder_attention_mask=None, + head_mask=None, + cross_attn_head_mask=None, + past_key_values=None, + inputs_embeds=None, + labels=None, + use_cache=None, + output_attentions=None, + output_hidden_states=None, + return_dict=None, + count_gt=None, + ): + output_attentions = ( + output_attentions + if output_attentions is not None + else self.config.output_attentions + ) + output_hidden_states = ( + output_hidden_states + if output_hidden_states is not None + else self.config.output_hidden_states + ) + return_dict = ( + return_dict if return_dict is not None else self.config.use_return_dict + ) + + if self.length_aware: + count_pred = self.counting_decoder(encoder_hidden_states) + else: + count_pred = None + + outputs = self.model.decoder( + input_ids=input_ids, + attention_mask=attention_mask, + count_pred=count_pred, + encoder_hidden_states=encoder_hidden_states, + encoder_attention_mask=encoder_attention_mask, + head_mask=head_mask, + cross_attn_head_mask=cross_attn_head_mask, + past_key_values=past_key_values, + inputs_embeds=inputs_embeds, + use_cache=use_cache, + output_attentions=output_attentions, + output_hidden_states=output_hidden_states, + return_dict=return_dict, + ) + logits = self.lm_head(outputs[0]) + + return CausalLMOutputWithCrossAttentionsAndCounting( + logits=logits, + counting=count_pred, + past_key_values=outputs.past_key_values, + hidden_states=outputs.hidden_states, + attentions=outputs.attentions, + cross_attentions=outputs.cross_attentions, + ) + + +class UniMERNetHead(nn.Layer): + """Implementation of UniMERNetHead decoder. + + Args: + max_new_tokens (int): Maximum number of new tokens to generate. + decoder_start_token_id (int): ID of the token that starts the decoding. + temperature (float): Sampling temperature for generation. + do_sample (bool): Whether to use sampling; if False, uses greedy decoding. + top_p (float): Top-p (nucleus) sampling parameter. + in_channels (int): Number of input channels/features. + encoder_hidden_size (int): Hidden size of the encoder. + decoder_hidden_size (int): Hidden size of the decoder. + decoder_ffn_dim (int): Dimension of the decoder's feed-forward network. + decoder_layers (int): Number of layers in the decoder. + is_export (bool): Flag indicating if the model is being prepared for export. + length_aware (bool): Flag to enable length-aware mechanisms. + """ + + def __init__( + self, + max_new_tokens=1536, + decoder_start_token_id=0, + temperature=0.2, + do_sample=False, + top_p=0.95, + in_channels=1024, + encoder_hidden_size=1024, + decoder_hidden_size=1024, + decoder_ffn_dim=4096, + decoder_layers=8, + is_export=False, + length_aware=True, + ): + super().__init__() + mbart_config_dict = { + "activation_dropout": 0.0, + "activation_function": "gelu", + "add_cross_attention": True, + "add_final_layer_norm": True, + "attention_dropout": 0.0, + "bos_token_id": 0, + "classifier_dropout": 0.0, + "d_model": decoder_hidden_size, + "decoder_attention_heads": 16, + "decoder_ffn_dim": decoder_ffn_dim, + "decoder_layerdrop": 0.0, + "decoder_layers": decoder_layers, + "dropout": 0.1, + "encoder_attention_heads": 16, + "encoder_ffn_dim": 4096, + "encoder_layerdrop": 0.0, + "encoder_layers": 12, + "eos_token_id": 2, + "forced_eos_token_id": 2, + "init_std": 0.02, + "is_decoder": True, + "is_encoder_decoder": False, + "output_hidden_states": False, + "max_position_embeddings": max_new_tokens, + "model_type": "mbart", + "num_hidden_layers": 12, + "pad_token_id": 1, + "scale_embedding": True, + "tie_word_embeddings": False, + "transformers_version": "4.40.0", + "use_cache": True, + "use_return_dict": True, + "vocab_size": 50000, + "_attn_implementation": "eager", + "hidden_size": decoder_hidden_size, + "is_export": is_export, + } + + self.max_new_tokens = max_new_tokens + self.decoder_start_token_id = decoder_start_token_id + self.temperature = temperature + self.do_sample = do_sample + self.top_p = top_p + self.max_seq_len = max_new_tokens + self.config_decoder = MBartConfig(**mbart_config_dict) + self.encoder_hidden_size = encoder_hidden_size + self.is_export = self.config_decoder.is_export + self.decoder = CustomMBartForCausalLM( + self.config_decoder, length_aware=length_aware + ) + if self.config_decoder.hidden_size != self.encoder_hidden_size: + self.enc_to_dec_proj = nn.Linear( + self.encoder_hidden_size, self.config_decoder.hidden_size + ) + generation_config = { + "max_length": 1537, + "forced_eos_token_id": 2, + } + self.eos_token_id = generation_config["forced_eos_token_id"] + self.pad_token_id = self.config_decoder.pad_token_id + self.logits_processor = LogitsProcessorList() + self.logits_processor.append( + ForcedEOSTokenLogitsProcessor( + generation_config["max_length"], + generation_config["forced_eos_token_id"], + ) + ) + + def _get_decoder_start_token_id( + self, decoder_start_token_id=None, bos_token_id=None + ) -> int: + decoder_start_token_id = ( + decoder_start_token_id + if decoder_start_token_id is not None + else self.generation_config.decoder_start_token_id + ) + bos_token_id = ( + bos_token_id + if bos_token_id is not None + else self.generation_config.bos_token_id + ) + if decoder_start_token_id is not None: + return decoder_start_token_id + elif bos_token_id is not None: + return bos_token_id + raise ValueError( + "`decoder_start_token_id` or `bos_token_id` has to be defined for encoder-decoder generation." + ) + + def _prepare_decoder_input_ids_for_generation( + self, + batch_size, + model_kwargs, + decoder_start_token_id=None, + bos_token_id=None, + ): + if model_kwargs is not None and "decoder_input_ids" in model_kwargs: + decoder_input_ids = model_kwargs.pop("decoder_input_ids") + elif "input_ids" in model_kwargs: + decoder_input_ids = model_kwargs.pop("input_ids") + else: + decoder_input_ids = None + + decoder_start_token_id = self._get_decoder_start_token_id( + decoder_start_token_id, bos_token_id + ) + + if isinstance(decoder_start_token_id, list): + if len(decoder_start_token_id) != batch_size: + raise ValueError( + f"`decoder_start_token_id` expected to have length {batch_size} but got {len(decoder_start_token_id)}" + ) + decoder_input_ids_start = paddle.to_tensor( + decoder_start_token_id, + dtype=paddle.int64, + ) + decoder_input_ids_start = decoder_input_ids_start.view(-1, 1) + else: + decoder_input_ids_start = ( + paddle.ones( + (batch_size, 1), + dtype=paddle.int64, + ) + * decoder_start_token_id + ) + + if decoder_input_ids is None: + decoder_input_ids = decoder_input_ids_start + elif ( + self.config.model_type == "vision-encoder-decoder" + and "donut" in self.name_or_path.lower() + ): + pass + elif self.config.model_type in ["whisper"]: + pass + elif ( + isinstance(decoder_start_token_id, int) + and (decoder_input_ids[:, 0] != decoder_start_token_id).all().item() + ) or ( + isinstance(decoder_start_token_id, paddle.Tensor) + and (decoder_input_ids[:, 0] != decoder_start_token_id[:, 0]).all().item() + ): + decoder_input_ids = paddle.concat( + [decoder_input_ids_start, decoder_input_ids], axis=-1 + ) + if "decoder_attention_mask" in model_kwargs: + decoder_attention_mask = model_kwargs["decoder_attention_mask"] + decoder_attention_mask = paddle.cat( + ( + paddle.ones_like(decoder_attention_mask)[:, :1], + decoder_attention_mask, + ), + dim=-1, + ) + model_kwargs["decoder_attention_mask"] = decoder_attention_mask + + return decoder_input_ids, model_kwargs + + def prepare_inputs_for_generation_mbart( + self, + input_ids, + past_key_values=None, + attention_mask=None, + use_cache=None, + **kwargs, + ): + + if attention_mask is None: + attention_mask = paddle.ones(input_ids.shape) + + if past_key_values: + past_length = past_key_values[0][0].shape[2] + + if input_ids.shape[1] > past_length: + remove_prefix_length = past_length + else: + remove_prefix_length = input_ids.shape[1] - 1 + + input_ids = input_ids[:, remove_prefix_length:] + return { + "input_ids": input_ids, + "attention_mask": attention_mask, + "past_key_values": past_key_values, + "use_cache": use_cache, + } + + def prepare_inputs_for_generation( + self, + input_ids, + past_key_values=None, + attention_mask=None, + use_cache=None, + encoder_outputs=None, + **kwargs, + ): + decoder_inputs = self.prepare_inputs_for_generation_mbart( + input_ids, past_key_values=past_key_values + ) + decoder_attention_mask = ( + decoder_inputs["attention_mask"] + if "attention_mask" in decoder_inputs + else None + ) + input_dict = { + "attention_mask": attention_mask, + "decoder_attention_mask": decoder_attention_mask, + "decoder_input_ids": decoder_inputs["input_ids"], + "encoder_outputs": encoder_outputs, + "past_key_values": decoder_inputs["past_key_values"], + "use_cache": use_cache, + } + return input_dict + + def prepare_inputs_for_generation_export( + self, + past_key_values=None, + attention_mask=None, + use_cache=None, + encoder_outputs=None, + **kwargs, + ): + + input_dict = { + "decoder_attention_mask": None, + "use_cache": use_cache, + } + return input_dict + + def _extract_past_from_model_output( + self, outputs: ModelOutput, standardize_cache_format: bool = False + ): + past_key_values = None + if "past_key_values" in outputs: + past_key_values = outputs.past_key_values + elif "mems" in outputs: + past_key_values = outputs.mems + elif "past_buckets_states" in outputs: + past_key_values = outputs.past_buckets_states + + return past_key_values + + def _update_model_kwargs_for_generation( + self, + outputs: ModelOutput, + model_kwargs: Dict[str, Any], + is_encoder_decoder: bool = False, + standardize_cache_format: bool = False, + ) -> Dict[str, Any]: + model_kwargs["past_key_values"] = self._extract_past_from_model_output( + outputs, standardize_cache_format=standardize_cache_format + ) + if getattr(outputs, "state", None) is not None: + model_kwargs["state"] = outputs.state + + if "token_type_ids" in model_kwargs: + token_type_ids = model_kwargs["token_type_ids"] + model_kwargs["token_type_ids"] = paddle.concat( + [token_type_ids, token_type_ids[:, -1].unsqueeze(-1)], axis=-1 + ) + + if not is_encoder_decoder: + if "attention_mask" in model_kwargs: + attention_mask = model_kwargs["attention_mask"] + model_kwargs["attention_mask"] = paddle.concat( + [ + attention_mask, + attention_mask.new_ones((attention_mask.shape[0], 1)), + ], + axis=-1, + ) + else: + if "decoder_attention_mask" in model_kwargs: + decoder_attention_mask = model_kwargs["decoder_attention_mask"] + model_kwargs["decoder_attention_mask"] = paddle.concat( + [ + decoder_attention_mask, + decoder_attention_mask.new_ones( + (decoder_attention_mask.shape[0], 1) + ), + ], + axis=-1, + ) + + if ( + "cache_position" in model_kwargs + and model_kwargs["cache_position"] is not None + ): + model_kwargs["cache_position"] = model_kwargs["cache_position"][-1:] + 1 + + return model_kwargs + + def stopping_criteria(self, input_ids): + if self.is_export: + return input_ids[:, -1] == paddle.to_tensor([self.eos_token_id]) + is_done = paddle.isin(input_ids[:, -1], paddle.to_tensor([self.eos_token_id])) + return is_done + + def generate_single_iter( + self, + decoder_input_ids=None, + decoder_attention_mask=None, + encoder_outputs=None, + past_key_values=None, + decoder_inputs_embeds=None, + labels=None, + use_cache=None, + output_attentions=None, + output_hidden_states=None, + return_dict=None, + **kwargs, + ): + encoder_hidden_states = encoder_outputs[0] + if self.config_decoder.hidden_size != self.encoder_hidden_size: + encoder_hidden_states = self.enc_to_dec_proj(encoder_hidden_states) + kwargs_decoder = {} + + decoder_outputs = self.decoder( + input_ids=decoder_input_ids, + attention_mask=decoder_attention_mask, + encoder_hidden_states=encoder_hidden_states, + encoder_attention_mask=None, + inputs_embeds=None, + output_attentions=False, + output_hidden_states=output_hidden_states, + use_cache=use_cache, + past_key_values=past_key_values, + return_dict=return_dict, + **kwargs_decoder, + ) + + return Seq2SeqLMOutput( + loss=None, + logits=decoder_outputs.logits, + past_key_values=decoder_outputs.past_key_values, + decoder_hidden_states=decoder_outputs.hidden_states, + decoder_attentions=decoder_outputs.attentions, + cross_attentions=decoder_outputs.cross_attentions, + encoder_last_hidden_state=encoder_outputs.last_hidden_state, + encoder_hidden_states=encoder_outputs.hidden_states, + encoder_attentions=encoder_outputs.attentions, + ) + + @paddle.no_grad() + def generate( + self, + model_kwargs, + ): + """ + Generate sequences using the UniMERNetHead for inference tasks. + + Args: + model_kwargs (dict): A dictionary of model configurations and inputs, which typically include: + - encoder_outputs: Outputs from the encoder. + - use_cache: Boolean flag to indicate if caching should be used. + - output_attentions: Boolean flag for outputting attention scores. + - output_hidden_states: Boolean flag for outputting hidden states. + + Returns: + A tensor containing the generated sequences. + """ + batch_size = model_kwargs["encoder_outputs"]["last_hidden_state"].shape[0] + generation_config = { + "decoder_start_token_id": 0, + "bos_token_id": 0, + } + input_ids, model_kwargs = self._prepare_decoder_input_ids_for_generation( + batch_size=batch_size, + model_kwargs=model_kwargs, + decoder_start_token_id=generation_config["decoder_start_token_id"], + bos_token_id=generation_config["bos_token_id"], + ) + model_kwargs["key use_cache"] = True + batch_size, cur_len = input_ids.shape + + if "inputs_embeds" in model_kwargs: + cur_len = model_kwargs["inputs_embeds"].shape[1] + model_kwargs["cache_position"] = paddle.arange(cur_len) + pad_token_id = self.pad_token_id + eos_token_id = [self.eos_token_id] + eos_token = self.eos_token_id + unfinished_sequences = paddle.ones(batch_size, dtype=paddle.int64) + for idx in range(self.max_seq_len): + model_inputs = self.prepare_inputs_for_generation(input_ids, **model_kwargs) + outputs = self.generate_single_iter( + **model_inputs, + return_dict=True, + output_attentions=False, + output_hidden_states=False, + ) + next_token_logits = outputs.logits[:, -1, :] + + next_tokens_scores = self.logits_processor(input_ids, next_token_logits) + next_tokens = paddle.argmax(next_tokens_scores, axis=-1) + if eos_token_id is not None: + if pad_token_id is None: + raise ValueError( + "If `eos_token_id` is defined, make sure that `pad_token_id` is defined." + ) + next_tokens = next_tokens * unfinished_sequences + pad_token_id * ( + 1 - unfinished_sequences + ) + input_ids = paddle.concat([input_ids, next_tokens[:, None]], axis=-1) + model_kwargs = self._update_model_kwargs_for_generation( + outputs, + model_kwargs, + is_encoder_decoder=self.config_decoder.is_encoder_decoder, + ) + unfinished_sequences = unfinished_sequences & ~self.stopping_criteria( + input_ids + ).cast(paddle.int64) + + if ( + eos_token is not None + and ( + paddle.cumsum((input_ids == eos_token).cast(paddle.int64), 1)[:, -1] + >= 1 + ).all() + ): + break + + return input_ids + + @paddle.no_grad() + def generate_export( + self, + encoder_outputs, + model_kwargs, + ): + batch_size = encoder_outputs["last_hidden_state"].shape[0] + generation_config = { + "decoder_start_token_id": 0, + "bos_token_id": 0, + } + input_ids, model_kwargs = self._prepare_decoder_input_ids_for_generation( + batch_size=batch_size, + model_kwargs=model_kwargs, + decoder_start_token_id=generation_config["decoder_start_token_id"], + bos_token_id=generation_config["bos_token_id"], + ) + input_ids = input_ids.reshape([-1, 1]) + decoder_input_ids = input_ids + model_kwargs["key use_cache"] = True + batch_size, cur_len = input_ids.shape + + if "inputs_embeds" in model_kwargs: + cur_len = model_kwargs["inputs_embeds"].shape[1] + cache_position = paddle.arange(cur_len) + pad_token_id = self.pad_token_id + eos_token_id = [self.eos_token_id] + eos_token = self.eos_token_id + unfinished_sequences = paddle.ones([batch_size], dtype=paddle.int64) + i_idx = paddle.full([], 0) + past_key_values = [] + for i in range(8): + init_arr = paddle.zeros([batch_size, 16, 0, 64]) + paddle.jit.api.set_dynamic_shape(init_arr, [-1, -1, -1, -1]) + cache = (init_arr, init_arr, init_arr, init_arr) + past_key_values.append(cache) + idx = 0 + while i_idx < paddle.to_tensor(self.max_seq_len): + + model_inputs = self.prepare_inputs_for_generation_export( + past_key_values=past_key_values, **model_kwargs + ) + decoder_attention_mask = model_inputs["decoder_attention_mask"] + decoder_attention_mask = paddle.ones(input_ids.shape) + paddle.jit.api.set_dynamic_shape(decoder_input_ids, [-1, -1]) + paddle.jit.api.set_dynamic_shape(decoder_attention_mask, [-1, -1]) + + outputs = self.generate_single_iter( + decoder_input_ids=decoder_input_ids, + decoder_attention_mask=decoder_attention_mask, + encoder_outputs=encoder_outputs, + past_key_values=past_key_values, + return_dict=True, + output_attentions=False, + output_hidden_states=False, + ) + + next_token_logits = outputs.logits[:, -1, :] + + next_tokens_scores = self.logits_processor(input_ids, next_token_logits) + next_tokens = paddle.argmax(next_tokens_scores, axis=-1) + if eos_token_id is not None: + next_tokens = next_tokens * unfinished_sequences + pad_token_id * ( + 1 - unfinished_sequences + ) + input_ids = paddle.concat([input_ids, next_tokens.unsqueeze(1)], axis=-1) + past_length = past_key_values[0][0].shape[2] + decoder_input_ids = next_tokens.unsqueeze(1) + past_key_values = outputs.past_key_values + cache_position = cache_position[-1:] + 1 + unfinished_sequences = unfinished_sequences & ~self.stopping_criteria( + input_ids + ).cast(paddle.int64) + if ( + eos_token is not None + and ( + paddle.cumsum((input_ids == eos_token).cast(paddle.int64), 1)[:, -1] + >= 1 + ).all() + ): + break + + i_idx += 1 + return input_ids + + def forwad_train( + self, + encoder_outputs, + decoder_input_ids, + decoder_attention_mask, + past_key_values=None, + decoder_inputs_embeds=None, + labels=None, + use_cache=None, + output_attentions=None, + output_hidden_states=None, + return_dict=None, + **kwargs, + ): + """ + Training for the UniMERNetHead. + + Args: + encoder_outputs: Outputs from the encoder, used as input to the decoder. + decoder_input_ids: Input IDs for the decoder. + decoder_attention_mask: Attention mask for the decoder inputs. + past_key_values: Cached key/values for faster decoding. + decoder_inputs_embeds: Optional embeddings for the decoder inputs. + labels: Target labels for calculating loss. + use_cache: Whether to use cache during decoding. + output_attentions: Whether to return attention scores. + output_hidden_states: Whether to return hidden states. + return_dict: Whether to return a dictionary of outputs. + **kwargs: Additional keyword arguments. + + Returns: + logits: The raw, unnormalized predictions from the model. + count_pred: Optional prediction related to sequence length or other counts. + masked_labels: The labels used during training, possibly masked. + """ + labels = decoder_input_ids * 1 + labels = labels.masked_fill_(labels == self.pad_token_id, -100) + input_decoder_input_ids = decoder_input_ids[:, :-1] + input_decoder_attention_mask = decoder_attention_mask[:, :-1] + encoder_hidden_states = encoder_outputs[0] + if self.config_decoder.hidden_size != self.encoder_hidden_size: + encoder_hidden_states = self.enc_to_dec_proj(encoder_hidden_states) + kwargs_decoder = {} + decoder_outputs = self.decoder( + input_ids=input_decoder_input_ids, + attention_mask=input_decoder_attention_mask, + encoder_hidden_states=encoder_hidden_states, + encoder_attention_mask=None, + inputs_embeds=None, + output_attentions=False, + output_hidden_states=output_hidden_states, + use_cache=use_cache, + past_key_values=past_key_values, + return_dict=return_dict, + **kwargs_decoder, + ) + + logits = decoder_outputs.logits + count_pred = decoder_outputs.counting + return logits, count_pred, labels + + def forward(self, inputs, targets=None): + """ + Forward pass for the UniMERNetHead, handling both training and inference. + + Args: + inputs: The input data, which can vary based on training or inference. + targets: The target labels, used only during training. + + Returns: + During inference: Returns predicted latex code. + During training: Returns logits, predicted counts, and masked labels. + """ + self.is_export = False if self.training else True + if not self.training: + encoder_outputs = inputs + if self.is_export: + model_kwargs = { + "output_attentions": False, + "output_hidden_states": False, + "use_cache": True, + } + word_pred = self.generate_export(encoder_outputs, model_kwargs) + else: + model_kwargs = { + "output_attentions": False, + "output_hidden_states": False, + "use_cache": True, + "encoder_outputs": encoder_outputs, + } + word_pred = self.generate(model_kwargs) + + return word_pred + + encoder_outputs, tgt_seq, mask = inputs + logits, count_pred, masked_labels = self.forwad_train( + encoder_outputs, tgt_seq, mask + ) + return logits, count_pred, masked_labels diff --git a/modules/onnx_ocr_module/src/ppocr/modeling/heads/rec_visionlan_head.py b/modules/onnx_ocr_module/src/ppocr/modeling/heads/rec_visionlan_head.py new file mode 100644 index 0000000..bb34dc3 --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/modeling/heads/rec_visionlan_head.py @@ -0,0 +1,474 @@ +# copyright (c) 2022 PaddlePaddle Authors. All Rights Reserve. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +""" +This code is refer from: +https://github.com/wangyuxin87/VisionLAN +""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import paddle +from paddle import ParamAttr +import paddle.nn as nn +import paddle.nn.functional as F +from paddle.nn.initializer import Normal, XavierNormal +import numpy as np + + +class PositionalEncoding(nn.Layer): + def __init__(self, d_hid, n_position=200): + super(PositionalEncoding, self).__init__() + self.register_buffer( + "pos_table", self._get_sinusoid_encoding_table(n_position, d_hid) + ) + + def _get_sinusoid_encoding_table(self, n_position, d_hid): + """Sinusoid position encoding table""" + + def get_position_angle_vec(position): + return [ + position / np.power(10000, 2 * (hid_j // 2) / d_hid) + for hid_j in range(d_hid) + ] + + sinusoid_table = np.array( + [get_position_angle_vec(pos_i) for pos_i in range(n_position)] + ) + sinusoid_table[:, 0::2] = np.sin(sinusoid_table[:, 0::2]) # dim 2i + sinusoid_table[:, 1::2] = np.cos(sinusoid_table[:, 1::2]) # dim 2i+1 + sinusoid_table = paddle.to_tensor(sinusoid_table, dtype="float32") + sinusoid_table = paddle.unsqueeze(sinusoid_table, axis=0) + return sinusoid_table + + def forward(self, x): + return x + self.pos_table[:, : x.shape[1]].clone().detach() + + +class ScaledDotProductAttention(nn.Layer): + "Scaled Dot-Product Attention" + + def __init__(self, temperature, attn_dropout=0.1): + super(ScaledDotProductAttention, self).__init__() + self.temperature = temperature + self.dropout = nn.Dropout(attn_dropout) + self.softmax = nn.Softmax(axis=2) + + def forward(self, q, k, v, mask=None): + k = paddle.transpose(k, perm=[0, 2, 1]) + attn = paddle.bmm(q, k) + attn = attn / self.temperature + if mask is not None: + attn = attn.masked_fill(mask, -1e9) + if mask.dim() == 3: + mask = paddle.unsqueeze(mask, axis=1) + elif mask.dim() == 2: + mask = paddle.unsqueeze(mask, axis=1) + mask = paddle.unsqueeze(mask, axis=1) + repeat_times = [ + attn.shape[1] // mask.shape[1], + attn.shape[2] // mask.shape[2], + ] + mask = paddle.tile(mask, [1, repeat_times[0], repeat_times[1], 1]) + attn[mask == 0] = -1e9 + attn = self.softmax(attn) + attn = self.dropout(attn) + output = paddle.bmm(attn, v) + return output + + +class MultiHeadAttention(nn.Layer): + "Multi-Head Attention module" + + def __init__(self, n_head, d_model, d_k, d_v, dropout=0.1): + super(MultiHeadAttention, self).__init__() + self.n_head = n_head + self.d_k = d_k + self.d_v = d_v + self.w_qs = nn.Linear( + d_model, + n_head * d_k, + weight_attr=ParamAttr( + initializer=Normal(mean=0, std=np.sqrt(2.0 / (d_model + d_k))) + ), + ) + self.w_ks = nn.Linear( + d_model, + n_head * d_k, + weight_attr=ParamAttr( + initializer=Normal(mean=0, std=np.sqrt(2.0 / (d_model + d_k))) + ), + ) + self.w_vs = nn.Linear( + d_model, + n_head * d_v, + weight_attr=ParamAttr( + initializer=Normal(mean=0, std=np.sqrt(2.0 / (d_model + d_v))) + ), + ) + + self.attention = ScaledDotProductAttention(temperature=np.power(d_k, 0.5)) + self.layer_norm = nn.LayerNorm(d_model) + self.fc = nn.Linear( + n_head * d_v, d_model, weight_attr=ParamAttr(initializer=XavierNormal()) + ) + self.dropout = nn.Dropout(dropout) + + def forward(self, q, k, v, mask=None): + d_k, d_v, n_head = self.d_k, self.d_v, self.n_head + sz_b, len_q, _ = q.shape + sz_b, len_k, _ = k.shape + sz_b, len_v, _ = v.shape + residual = q + + q = self.w_qs(q) + q = paddle.reshape(q, shape=[-1, len_q, n_head, d_k]) # 4*21*512 ---- 4*21*8*64 + k = self.w_ks(k) + k = paddle.reshape(k, shape=[-1, len_k, n_head, d_k]) + v = self.w_vs(v) + v = paddle.reshape(v, shape=[-1, len_v, n_head, d_v]) + + q = paddle.transpose(q, perm=[2, 0, 1, 3]) + q = paddle.reshape(q, shape=[-1, len_q, d_k]) # (n*b) x lq x dk + k = paddle.transpose(k, perm=[2, 0, 1, 3]) + k = paddle.reshape(k, shape=[-1, len_k, d_k]) # (n*b) x lk x dk + v = paddle.transpose(v, perm=[2, 0, 1, 3]) + v = paddle.reshape(v, shape=[-1, len_v, d_v]) # (n*b) x lv x dv + + mask = ( + paddle.tile(mask, [n_head, 1, 1]) if mask is not None else None + ) # (n*b) x .. x .. + output = self.attention(q, k, v, mask=mask) + output = paddle.reshape(output, shape=[n_head, -1, len_q, d_v]) + output = paddle.transpose(output, perm=[1, 2, 0, 3]) + output = paddle.reshape( + output, shape=[-1, len_q, n_head * d_v] + ) # b x lq x (n*dv) + output = self.dropout(self.fc(output)) + output = self.layer_norm(output + residual) + return output + + +class PositionwiseFeedForward(nn.Layer): + def __init__(self, d_in, d_hid, dropout=0.1): + super(PositionwiseFeedForward, self).__init__() + self.w_1 = nn.Conv1D(d_in, d_hid, 1) # position-wise + self.w_2 = nn.Conv1D(d_hid, d_in, 1) # position-wise + self.layer_norm = nn.LayerNorm(d_in) + self.dropout = nn.Dropout(dropout) + + def forward(self, x): + residual = x + x = paddle.transpose(x, perm=[0, 2, 1]) + x = self.w_2(F.relu(self.w_1(x))) + x = paddle.transpose(x, perm=[0, 2, 1]) + x = self.dropout(x) + x = self.layer_norm(x + residual) + return x + + +class EncoderLayer(nn.Layer): + """Compose with two layers""" + + def __init__(self, d_model, d_inner, n_head, d_k, d_v, dropout=0.1): + super(EncoderLayer, self).__init__() + self.slf_attn = MultiHeadAttention(n_head, d_model, d_k, d_v, dropout=dropout) + self.pos_ffn = PositionwiseFeedForward(d_model, d_inner, dropout=dropout) + + def forward(self, enc_input, slf_attn_mask=None): + enc_output = self.slf_attn(enc_input, enc_input, enc_input, mask=slf_attn_mask) + enc_output = self.pos_ffn(enc_output) + return enc_output + + +class Transformer_Encoder(nn.Layer): + def __init__( + self, + n_layers=2, + n_head=8, + d_word_vec=512, + d_k=64, + d_v=64, + d_model=512, + d_inner=2048, + dropout=0.1, + n_position=256, + ): + super(Transformer_Encoder, self).__init__() + self.position_enc = PositionalEncoding(d_word_vec, n_position=n_position) + self.dropout = nn.Dropout(p=dropout) + self.layer_stack = nn.LayerList( + [ + EncoderLayer(d_model, d_inner, n_head, d_k, d_v, dropout=dropout) + for _ in range(n_layers) + ] + ) + self.layer_norm = nn.LayerNorm(d_model, epsilon=1e-6) + + def forward(self, enc_output, src_mask, return_attns=False): + enc_output = self.dropout(self.position_enc(enc_output)) # position embedding + for enc_layer in self.layer_stack: + enc_output = enc_layer(enc_output, slf_attn_mask=src_mask) + enc_output = self.layer_norm(enc_output) + return enc_output + + +class PP_layer(nn.Layer): + def __init__(self, n_dim=512, N_max_character=25, n_position=256): + super(PP_layer, self).__init__() + self.character_len = N_max_character + self.f0_embedding = nn.Embedding(N_max_character, n_dim) + self.w0 = nn.Linear(N_max_character, n_position) + self.wv = nn.Linear(n_dim, n_dim) + self.we = nn.Linear(n_dim, N_max_character) + self.active = nn.Tanh() + self.softmax = nn.Softmax(axis=2) + + def forward(self, enc_output): + # enc_output: b,256,512 + reading_order = paddle.arange(self.character_len, dtype="int64") + reading_order = reading_order.unsqueeze(0).expand( + [enc_output.shape[0], self.character_len] + ) # (S,) -> (B, S) + reading_order = self.f0_embedding(reading_order) # b,25,512 + + # calculate attention + reading_order = paddle.transpose(reading_order, perm=[0, 2, 1]) + t = self.w0(reading_order) # b,512,256 + t = self.active( + paddle.transpose(t, perm=[0, 2, 1]) + self.wv(enc_output) + ) # b,256,512 + t = self.we(t) # b,256,25 + t = self.softmax(paddle.transpose(t, perm=[0, 2, 1])) # b,25,256 + g_output = paddle.bmm(t, enc_output) # b,25,512 + return g_output + + +class Prediction(nn.Layer): + def __init__(self, n_dim=512, n_position=256, N_max_character=25, n_class=37): + super(Prediction, self).__init__() + self.pp = PP_layer( + n_dim=n_dim, N_max_character=N_max_character, n_position=n_position + ) + self.pp_share = PP_layer( + n_dim=n_dim, N_max_character=N_max_character, n_position=n_position + ) + self.w_vrm = nn.Linear(n_dim, n_class) # output layer + self.w_share = nn.Linear(n_dim, n_class) # output layer + self.nclass = n_class + + def forward(self, cnn_feature, f_res, f_sub, train_mode=False, use_mlm=True): + if train_mode: + if not use_mlm: + g_output = self.pp(cnn_feature) # b,25,512 + g_output = self.w_vrm(g_output) + f_res = 0 + f_sub = 0 + return g_output, f_res, f_sub + g_output = self.pp(cnn_feature) # b,25,512 + f_res = self.pp_share(f_res) + f_sub = self.pp_share(f_sub) + g_output = self.w_vrm(g_output) + f_res = self.w_share(f_res) + f_sub = self.w_share(f_sub) + return g_output, f_res, f_sub + else: + g_output = self.pp(cnn_feature) # b,25,512 + g_output = self.w_vrm(g_output) + return g_output + + +class MLM(nn.Layer): + "Architecture of MLM" + + def __init__(self, n_dim=512, n_position=256, max_text_length=25): + super(MLM, self).__init__() + self.MLM_SequenceModeling_mask = Transformer_Encoder( + n_layers=2, n_position=n_position + ) + self.MLM_SequenceModeling_WCL = Transformer_Encoder( + n_layers=1, n_position=n_position + ) + self.pos_embedding = nn.Embedding(max_text_length, n_dim) + self.w0_linear = nn.Linear(1, n_position) + self.wv = nn.Linear(n_dim, n_dim) + self.active = nn.Tanh() + self.we = nn.Linear(n_dim, 1) + self.sigmoid = nn.Sigmoid() + + def forward(self, x, label_pos): + # transformer unit for generating mask_c + feature_v_seq = self.MLM_SequenceModeling_mask(x, src_mask=None) + # position embedding layer + label_pos = paddle.to_tensor(label_pos, dtype="int64") + pos_emb = self.pos_embedding(label_pos) + pos_emb = self.w0_linear(paddle.unsqueeze(pos_emb, axis=2)) + pos_emb = paddle.transpose(pos_emb, perm=[0, 2, 1]) + # fusion position embedding with features V & generate mask_c + att_map_sub = self.active(pos_emb + self.wv(feature_v_seq)) + att_map_sub = self.we(att_map_sub) # b,256,1 + att_map_sub = paddle.transpose(att_map_sub, perm=[0, 2, 1]) + att_map_sub = self.sigmoid(att_map_sub) # b,1,256 + # WCL + ## generate inputs for WCL + att_map_sub = paddle.transpose(att_map_sub, perm=[0, 2, 1]) + f_res = x * (1 - att_map_sub) # second path with remaining string + f_sub = x * att_map_sub # first path with occluded character + ## transformer units in WCL + f_res = self.MLM_SequenceModeling_WCL(f_res, src_mask=None) + f_sub = self.MLM_SequenceModeling_WCL(f_sub, src_mask=None) + return f_res, f_sub, att_map_sub + + +def trans_1d_2d(x): + b, w_h, c = x.shape # b, 256, 512 + x = paddle.transpose(x, perm=[0, 2, 1]) + x = paddle.reshape(x, [-1, c, 32, 8]) + x = paddle.transpose(x, perm=[0, 1, 3, 2]) # [b, c, 8, 32] + return x + + +class MLM_VRM(nn.Layer): + """ + MLM+VRM, MLM is only used in training. + ratio controls the occluded number in a batch. + The pipeline of VisionLAN in testing is very concise with only a backbone + sequence modeling(transformer unit) + prediction layer(pp layer). + x: input image + label_pos: character index + training_step: LF or LA process + output + text_pre: prediction of VRM + test_rem: prediction of remaining string in MLM + text_mas: prediction of occluded character in MLM + mask_c_show: visualization of Mask_c + """ + + def __init__( + self, n_layers=3, n_position=256, n_dim=512, max_text_length=25, nclass=37 + ): + super(MLM_VRM, self).__init__() + self.MLM = MLM( + n_dim=n_dim, n_position=n_position, max_text_length=max_text_length + ) + self.SequenceModeling = Transformer_Encoder( + n_layers=n_layers, n_position=n_position + ) + self.Prediction = Prediction( + n_dim=n_dim, + n_position=n_position, + N_max_character=max_text_length + + 1, # N_max_character = 1 eos + 25 characters + n_class=nclass, + ) + self.nclass = nclass + self.max_text_length = max_text_length + + def forward(self, x, label_pos, training_step, train_mode=False): + b, c, h, w = x.shape + nT = self.max_text_length + x = paddle.transpose(x, perm=[0, 1, 3, 2]) + x = paddle.reshape(x, [-1, c, h * w]) + x = paddle.transpose(x, perm=[0, 2, 1]) + if train_mode: + if training_step == "LF_1": + f_res = 0 + f_sub = 0 + x = self.SequenceModeling(x, src_mask=None) + text_pre, test_rem, text_mas = self.Prediction( + x, f_res, f_sub, train_mode=True, use_mlm=False + ) + return text_pre, text_pre, text_pre, text_pre + elif training_step == "LF_2": + # MLM + f_res, f_sub, mask_c = self.MLM(x, label_pos) + x = self.SequenceModeling(x, src_mask=None) + text_pre, test_rem, text_mas = self.Prediction( + x, f_res, f_sub, train_mode=True + ) + mask_c_show = trans_1d_2d(mask_c) + return text_pre, test_rem, text_mas, mask_c_show + elif training_step == "LA": + # MLM + f_res, f_sub, mask_c = self.MLM(x, label_pos) + ## use the mask_c (1 for occluded character and 0 for remaining characters) to occlude input + ## ratio controls the occluded number in a batch + character_mask = paddle.zeros_like(mask_c) + + ratio = b // 2 + if ratio >= 1: + with paddle.no_grad(): + character_mask[0:ratio, :, :] = mask_c[0:ratio, :, :] + else: + character_mask = mask_c + x = x * (1 - character_mask) + # VRM + ## transformer unit for VRM + x = self.SequenceModeling(x, src_mask=None) + ## prediction layer for MLM and VSR + text_pre, test_rem, text_mas = self.Prediction( + x, f_res, f_sub, train_mode=True + ) + mask_c_show = trans_1d_2d(mask_c) + return text_pre, test_rem, text_mas, mask_c_show + else: + raise NotImplementedError + else: # VRM is only used in the testing stage + f_res = 0 + f_sub = 0 + contextual_feature = self.SequenceModeling(x, src_mask=None) + text_pre = self.Prediction( + contextual_feature, f_res, f_sub, train_mode=False, use_mlm=False + ) + text_pre = paddle.transpose(text_pre, perm=[1, 0, 2]) # (26, b, 37)) + return text_pre, x + + +class VLHead(nn.Layer): + """ + Architecture of VisionLAN + """ + + def __init__( + self, + in_channels, + out_channels=36, + n_layers=3, + n_position=256, + n_dim=512, + max_text_length=25, + training_step="LA", + ): + super(VLHead, self).__init__() + self.MLM_VRM = MLM_VRM( + n_layers=n_layers, + n_position=n_position, + n_dim=n_dim, + max_text_length=max_text_length, + nclass=out_channels + 1, + ) + self.training_step = training_step + + def forward(self, feat, targets=None): + if self.training: + label_pos = targets[-2] + text_pre, test_rem, text_mas, mask_map = self.MLM_VRM( + feat, label_pos, self.training_step, train_mode=True + ) + return text_pre, test_rem, text_mas, mask_map + else: + text_pre, x = self.MLM_VRM( + feat, targets, self.training_step, train_mode=False + ) + return text_pre, x diff --git a/modules/onnx_ocr_module/src/ppocr/modeling/heads/self_attention.py b/modules/onnx_ocr_module/src/ppocr/modeling/heads/self_attention.py new file mode 100644 index 0000000..c02b9cd --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/modeling/heads/self_attention.py @@ -0,0 +1,460 @@ +# copyright (c) 2020 PaddlePaddle Authors. All Rights Reserve. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import math + +import paddle +from paddle import ParamAttr, nn +from paddle import nn, ParamAttr +from paddle.nn import functional as F +import numpy as np + +gradient_clip = 10 + + +class WrapEncoderForFeature(nn.Layer): + def __init__( + self, + src_vocab_size, + max_length, + n_layer, + n_head, + d_key, + d_value, + d_model, + d_inner_hid, + prepostprocess_dropout, + attention_dropout, + relu_dropout, + preprocess_cmd, + postprocess_cmd, + weight_sharing, + bos_idx=0, + ): + super(WrapEncoderForFeature, self).__init__() + + self.prepare_encoder = PrepareEncoder( + src_vocab_size, + d_model, + max_length, + prepostprocess_dropout, + bos_idx=bos_idx, + word_emb_param_name="src_word_emb_table", + ) + self.encoder = Encoder( + n_layer, + n_head, + d_key, + d_value, + d_model, + d_inner_hid, + prepostprocess_dropout, + attention_dropout, + relu_dropout, + preprocess_cmd, + postprocess_cmd, + ) + + def forward(self, enc_inputs): + conv_features, src_pos, src_slf_attn_bias = enc_inputs + enc_input = self.prepare_encoder(conv_features, src_pos) + enc_output = self.encoder(enc_input, src_slf_attn_bias) + return enc_output + + +class WrapEncoder(nn.Layer): + """ + embedder + encoder + """ + + def __init__( + self, + src_vocab_size, + max_length, + n_layer, + n_head, + d_key, + d_value, + d_model, + d_inner_hid, + prepostprocess_dropout, + attention_dropout, + relu_dropout, + preprocess_cmd, + postprocess_cmd, + weight_sharing, + bos_idx=0, + ): + super(WrapEncoder, self).__init__() + + self.prepare_decoder = PrepareDecoder( + src_vocab_size, d_model, max_length, prepostprocess_dropout, bos_idx=bos_idx + ) + self.encoder = Encoder( + n_layer, + n_head, + d_key, + d_value, + d_model, + d_inner_hid, + prepostprocess_dropout, + attention_dropout, + relu_dropout, + preprocess_cmd, + postprocess_cmd, + ) + + def forward(self, enc_inputs): + src_word, src_pos, src_slf_attn_bias = enc_inputs + enc_input = self.prepare_decoder(src_word, src_pos) + enc_output = self.encoder(enc_input, src_slf_attn_bias) + return enc_output + + +class Encoder(nn.Layer): + """ + encoder + """ + + def __init__( + self, + n_layer, + n_head, + d_key, + d_value, + d_model, + d_inner_hid, + prepostprocess_dropout, + attention_dropout, + relu_dropout, + preprocess_cmd="n", + postprocess_cmd="da", + ): + super(Encoder, self).__init__() + + self.encoder_layers = list() + for i in range(n_layer): + self.encoder_layers.append( + self.add_sublayer( + "layer_%d" % i, + EncoderLayer( + n_head, + d_key, + d_value, + d_model, + d_inner_hid, + prepostprocess_dropout, + attention_dropout, + relu_dropout, + preprocess_cmd, + postprocess_cmd, + ), + ) + ) + self.processor = PrePostProcessLayer( + preprocess_cmd, d_model, prepostprocess_dropout + ) + + def forward(self, enc_input, attn_bias): + for encoder_layer in self.encoder_layers: + enc_output = encoder_layer(enc_input, attn_bias) + enc_input = enc_output + enc_output = self.processor(enc_output) + return enc_output + + +class EncoderLayer(nn.Layer): + """ + EncoderLayer + """ + + def __init__( + self, + n_head, + d_key, + d_value, + d_model, + d_inner_hid, + prepostprocess_dropout, + attention_dropout, + relu_dropout, + preprocess_cmd="n", + postprocess_cmd="da", + ): + super(EncoderLayer, self).__init__() + self.preprocesser1 = PrePostProcessLayer( + preprocess_cmd, d_model, prepostprocess_dropout + ) + self.self_attn = MultiHeadAttention( + d_key, d_value, d_model, n_head, attention_dropout + ) + self.postprocesser1 = PrePostProcessLayer( + postprocess_cmd, d_model, prepostprocess_dropout + ) + + self.preprocesser2 = PrePostProcessLayer( + preprocess_cmd, d_model, prepostprocess_dropout + ) + self.ffn = FFN(d_inner_hid, d_model, relu_dropout) + self.postprocesser2 = PrePostProcessLayer( + postprocess_cmd, d_model, prepostprocess_dropout + ) + + def forward(self, enc_input, attn_bias): + attn_output = self.self_attn( + self.preprocesser1(enc_input), None, None, attn_bias + ) + attn_output = self.postprocesser1(attn_output, enc_input) + ffn_output = self.ffn(self.preprocesser2(attn_output)) + ffn_output = self.postprocesser2(ffn_output, attn_output) + return ffn_output + + +class MultiHeadAttention(nn.Layer): + """ + Multi-Head Attention + """ + + def __init__(self, d_key, d_value, d_model, n_head=1, dropout_rate=0.0): + super(MultiHeadAttention, self).__init__() + self.n_head = n_head + self.d_key = d_key + self.d_value = d_value + self.d_model = d_model + self.dropout_rate = dropout_rate + self.q_fc = paddle.nn.Linear( + in_features=d_model, out_features=d_key * n_head, bias_attr=False + ) + self.k_fc = paddle.nn.Linear( + in_features=d_model, out_features=d_key * n_head, bias_attr=False + ) + self.v_fc = paddle.nn.Linear( + in_features=d_model, out_features=d_value * n_head, bias_attr=False + ) + self.proj_fc = paddle.nn.Linear( + in_features=d_value * n_head, out_features=d_model, bias_attr=False + ) + + def _prepare_qkv(self, queries, keys, values, cache=None): + if keys is None: # self-attention + keys, values = queries, queries + static_kv = False + else: # cross-attention + static_kv = True + + q = self.q_fc(queries) + q = paddle.reshape(x=q, shape=[0, 0, self.n_head, self.d_key]) + q = paddle.transpose(x=q, perm=[0, 2, 1, 3]) + + if cache is not None and static_kv and "static_k" in cache: + # for encoder-decoder attention in inference and has cached + k = cache["static_k"] + v = cache["static_v"] + else: + k = self.k_fc(keys) + v = self.v_fc(values) + k = paddle.reshape(x=k, shape=[0, 0, self.n_head, self.d_key]) + k = paddle.transpose(x=k, perm=[0, 2, 1, 3]) + v = paddle.reshape(x=v, shape=[0, 0, self.n_head, self.d_value]) + v = paddle.transpose(x=v, perm=[0, 2, 1, 3]) + + if cache is not None: + if static_kv and not "static_k" in cache: + # for encoder-decoder attention in inference and has not cached + cache["static_k"], cache["static_v"] = k, v + elif not static_kv: + # for decoder self-attention in inference + cache_k, cache_v = cache["k"], cache["v"] + k = paddle.concat([cache_k, k], axis=2) + v = paddle.concat([cache_v, v], axis=2) + cache["k"], cache["v"] = k, v + + return q, k, v + + def forward(self, queries, keys, values, attn_bias, cache=None): + # compute q ,k ,v + keys = queries if keys is None else keys + values = keys if values is None else values + q, k, v = self._prepare_qkv(queries, keys, values, cache) + + # scale dot product attention + product = paddle.matmul(x=q, y=k, transpose_y=True) + product = product * self.d_model**-0.5 + if attn_bias is not None: + product += attn_bias.astype(product.dtype) + weights = F.softmax(product) + if self.dropout_rate: + weights = F.dropout(weights, p=self.dropout_rate, mode="downscale_in_infer") + out = paddle.matmul(weights, v) + + # combine heads + out = paddle.transpose(out, perm=[0, 2, 1, 3]) + out = paddle.reshape(x=out, shape=[0, 0, out.shape[2] * out.shape[3]]) + + # project to output + out = self.proj_fc(out) + + return out + + +class PrePostProcessLayer(nn.Layer): + """ + PrePostProcessLayer + """ + + def __init__(self, process_cmd, d_model, dropout_rate): + super(PrePostProcessLayer, self).__init__() + self.process_cmd = process_cmd + self.functors = [] + for cmd in self.process_cmd: + if cmd == "a": # add residual connection + self.functors.append(lambda x, y: x + y if y is not None else x) + elif cmd == "n": # add layer normalization + self.functors.append( + self.add_sublayer( + "layer_norm_%d" % len(self.sublayers()), + paddle.nn.LayerNorm( + normalized_shape=d_model, + weight_attr=paddle.ParamAttr( + initializer=paddle.nn.initializer.Constant(1.0) + ), + bias_attr=paddle.ParamAttr( + initializer=paddle.nn.initializer.Constant(0.0) + ), + ), + ) + ) + elif cmd == "d": # add dropout + self.functors.append( + lambda x: ( + F.dropout(x, p=dropout_rate, mode="downscale_in_infer") + if dropout_rate + else x + ) + ) + + def forward(self, x, residual=None): + for i, cmd in enumerate(self.process_cmd): + if cmd == "a": + x = self.functors[i](x, residual) + else: + x = self.functors[i](x) + return x + + +class PrepareEncoder(nn.Layer): + def __init__( + self, + src_vocab_size, + src_emb_dim, + src_max_len, + dropout_rate=0, + bos_idx=0, + word_emb_param_name=None, + pos_enc_param_name=None, + ): + super(PrepareEncoder, self).__init__() + self.src_emb_dim = src_emb_dim + self.src_max_len = src_max_len + self.emb = paddle.nn.Embedding( + num_embeddings=self.src_max_len, embedding_dim=self.src_emb_dim + ) + self.dropout_rate = dropout_rate + + def forward(self, src_word, src_pos): + src_word_emb = src_word + src_word_emb = paddle.cast(src_word_emb, "float32") + src_word_emb = paddle.scale(x=src_word_emb, scale=self.src_emb_dim**0.5) + src_pos = paddle.squeeze(src_pos, axis=-1) + src_pos_enc = self.emb(src_pos) + src_pos_enc.stop_gradient = True + enc_input = src_word_emb + src_pos_enc + if self.dropout_rate: + out = F.dropout(x=enc_input, p=self.dropout_rate, mode="downscale_in_infer") + else: + out = enc_input + return out + + +class PrepareDecoder(nn.Layer): + def __init__( + self, + src_vocab_size, + src_emb_dim, + src_max_len, + dropout_rate=0, + bos_idx=0, + word_emb_param_name=None, + pos_enc_param_name=None, + ): + super(PrepareDecoder, self).__init__() + self.src_emb_dim = src_emb_dim + """ + self.emb0 = Embedding(num_embeddings=src_vocab_size, + embedding_dim=src_emb_dim) + """ + self.emb0 = paddle.nn.Embedding( + num_embeddings=src_vocab_size, + embedding_dim=self.src_emb_dim, + padding_idx=bos_idx, + weight_attr=paddle.ParamAttr( + name=word_emb_param_name, + initializer=nn.initializer.Normal(0.0, src_emb_dim**-0.5), + ), + ) + self.emb1 = paddle.nn.Embedding( + num_embeddings=src_max_len, + embedding_dim=self.src_emb_dim, + weight_attr=paddle.ParamAttr(name=pos_enc_param_name), + ) + self.dropout_rate = dropout_rate + + def forward(self, src_word, src_pos): + src_word = paddle.cast(src_word, "int64") + src_word = paddle.squeeze(src_word, axis=-1) + src_word_emb = self.emb0(src_word) + src_word_emb = paddle.scale(x=src_word_emb, scale=self.src_emb_dim**0.5) + src_pos = paddle.squeeze(src_pos, axis=-1) + src_pos_enc = self.emb1(src_pos) + src_pos_enc.stop_gradient = True + enc_input = src_word_emb + src_pos_enc + if self.dropout_rate: + out = F.dropout(x=enc_input, p=self.dropout_rate, mode="downscale_in_infer") + else: + out = enc_input + return out + + +class FFN(nn.Layer): + """ + Feed-Forward Network + """ + + def __init__(self, d_inner_hid, d_model, dropout_rate): + super(FFN, self).__init__() + self.dropout_rate = dropout_rate + self.fc1 = paddle.nn.Linear(in_features=d_model, out_features=d_inner_hid) + self.fc2 = paddle.nn.Linear(in_features=d_inner_hid, out_features=d_model) + + def forward(self, x): + hidden = self.fc1(x) + hidden = F.relu(hidden) + if self.dropout_rate: + hidden = F.dropout(hidden, p=self.dropout_rate, mode="downscale_in_infer") + out = self.fc2(hidden) + return out diff --git a/modules/onnx_ocr_module/src/ppocr/modeling/heads/sr_rensnet_transformer.py b/modules/onnx_ocr_module/src/ppocr/modeling/heads/sr_rensnet_transformer.py new file mode 100644 index 0000000..dcb8bfb --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/modeling/heads/sr_rensnet_transformer.py @@ -0,0 +1,427 @@ +# copyright (c) 2022 PaddlePaddle Authors. All Rights Reserve. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +""" +This code is refer from: +https://github.com/FudanVI/FudanOCR/blob/main/text-gestalt/loss/transformer_english_decomposition.py +""" +import copy +import math + +import paddle +import paddle.nn as nn +import paddle.nn.functional as F + + +def subsequent_mask(size): + """Generate a square mask for the sequence. The masked positions are filled with float('-inf'). + Unmasked positions are filled with float(0.0). + """ + mask = paddle.ones([1, size, size], dtype="float32") + mask_inf = paddle.triu( + paddle.full(shape=[1, size, size], dtype="float32", fill_value="-inf"), + diagonal=1, + ) + mask = mask + mask_inf + padding_mask = paddle.equal(mask, paddle.to_tensor(1, dtype=mask.dtype)) + return padding_mask + + +def clones(module, N): + return nn.LayerList([copy.deepcopy(module) for _ in range(N)]) + + +def masked_fill(x, mask, value): + y = paddle.full(x.shape, value, x.dtype) + return paddle.where(mask, y, x) + + +def attention(query, key, value, mask=None, dropout=None, attention_map=None): + d_k = query.shape[-1] + scores = paddle.matmul(query, paddle.transpose(key, [0, 1, 3, 2])) / math.sqrt(d_k) + + if mask is not None: + scores = masked_fill(scores, mask == 0, float("-inf")) + else: + pass + + p_attn = F.softmax(scores, axis=-1) + + if dropout is not None: + p_attn = dropout(p_attn) + return paddle.matmul(p_attn, value), p_attn + + +class MultiHeadedAttention(nn.Layer): + def __init__(self, h, d_model, dropout=0.1, compress_attention=False): + super(MultiHeadedAttention, self).__init__() + assert d_model % h == 0 + self.d_k = d_model // h + self.h = h + self.linears = clones(nn.Linear(d_model, d_model), 4) + self.attn = None + self.dropout = nn.Dropout(p=dropout, mode="downscale_in_infer") + self.compress_attention = compress_attention + self.compress_attention_linear = nn.Linear(h, 1) + + def forward(self, query, key, value, mask=None, attention_map=None): + if mask is not None: + mask = mask.unsqueeze(1) + nbatches = query.shape[0] + + query, key, value = [ + paddle.transpose( + l(x).reshape([nbatches, -1, self.h, self.d_k]), [0, 2, 1, 3] + ) + for l, x in zip(self.linears, (query, key, value)) + ] + + x, attention_map = attention( + query, + key, + value, + mask=mask, + dropout=self.dropout, + attention_map=attention_map, + ) + + x = paddle.reshape( + paddle.transpose(x, [0, 2, 1, 3]), [nbatches, -1, self.h * self.d_k] + ) + + return self.linears[-1](x), attention_map + + +class ResNet(nn.Layer): + def __init__(self, num_in, block, layers): + super(ResNet, self).__init__() + + self.conv1 = nn.Conv2D(num_in, 64, kernel_size=3, stride=1, padding=1) + self.bn1 = nn.BatchNorm2D(64, use_global_stats=True) + self.relu1 = nn.ReLU() + self.pool = nn.MaxPool2D((2, 2), (2, 2)) + + self.conv2 = nn.Conv2D(64, 128, kernel_size=3, stride=1, padding=1) + self.bn2 = nn.BatchNorm2D(128, use_global_stats=True) + self.relu2 = nn.ReLU() + + self.layer1_pool = nn.MaxPool2D((2, 2), (2, 2)) + self.layer1 = self._make_layer(block, 128, 256, layers[0]) + self.layer1_conv = nn.Conv2D(256, 256, 3, 1, 1) + self.layer1_bn = nn.BatchNorm2D(256, use_global_stats=True) + self.layer1_relu = nn.ReLU() + + self.layer2_pool = nn.MaxPool2D((2, 2), (2, 2)) + self.layer2 = self._make_layer(block, 256, 256, layers[1]) + self.layer2_conv = nn.Conv2D(256, 256, 3, 1, 1) + self.layer2_bn = nn.BatchNorm2D(256, use_global_stats=True) + self.layer2_relu = nn.ReLU() + + self.layer3_pool = nn.MaxPool2D((2, 2), (2, 2)) + self.layer3 = self._make_layer(block, 256, 512, layers[2]) + self.layer3_conv = nn.Conv2D(512, 512, 3, 1, 1) + self.layer3_bn = nn.BatchNorm2D(512, use_global_stats=True) + self.layer3_relu = nn.ReLU() + + self.layer4_pool = nn.MaxPool2D((2, 2), (2, 2)) + self.layer4 = self._make_layer(block, 512, 512, layers[3]) + self.layer4_conv2 = nn.Conv2D(512, 1024, 3, 1, 1) + self.layer4_conv2_bn = nn.BatchNorm2D(1024, use_global_stats=True) + self.layer4_conv2_relu = nn.ReLU() + + def _make_layer(self, block, inplanes, planes, blocks): + if inplanes != planes: + downsample = nn.Sequential( + nn.Conv2D(inplanes, planes, 3, 1, 1), + nn.BatchNorm2D(planes, use_global_stats=True), + ) + else: + downsample = None + layers = [] + layers.append(block(inplanes, planes, downsample)) + for i in range(1, blocks): + layers.append(block(planes, planes, downsample=None)) + + return nn.Sequential(*layers) + + def forward(self, x): + x = self.conv1(x) + x = self.bn1(x) + x = self.relu1(x) + x = self.pool(x) + + x = self.conv2(x) + x = self.bn2(x) + x = self.relu2(x) + + x = self.layer1_pool(x) + x = self.layer1(x) + x = self.layer1_conv(x) + x = self.layer1_bn(x) + x = self.layer1_relu(x) + + x = self.layer2(x) + x = self.layer2_conv(x) + x = self.layer2_bn(x) + x = self.layer2_relu(x) + + x = self.layer3(x) + x = self.layer3_conv(x) + x = self.layer3_bn(x) + x = self.layer3_relu(x) + + x = self.layer4(x) + x = self.layer4_conv2(x) + x = self.layer4_conv2_bn(x) + x = self.layer4_conv2_relu(x) + + return x + + +class Bottleneck(nn.Layer): + def __init__(self, input_dim): + super(Bottleneck, self).__init__() + self.conv1 = nn.Conv2D(input_dim, input_dim, 1) + self.bn1 = nn.BatchNorm2D(input_dim, use_global_stats=True) + self.relu = nn.ReLU() + + self.conv2 = nn.Conv2D(input_dim, input_dim, 3, 1, 1) + self.bn2 = nn.BatchNorm2D(input_dim, use_global_stats=True) + + def forward(self, x): + residual = x + + out = self.conv1(x) + out = self.bn1(out) + out = self.relu(out) + + out = self.conv2(out) + out = self.bn2(out) + + out += residual + out = self.relu(out) + + return out + + +class PositionalEncoding(nn.Layer): + "Implement the PE function." + + def __init__(self, dropout, dim, max_len=5000): + super(PositionalEncoding, self).__init__() + self.dropout = nn.Dropout(p=dropout, mode="downscale_in_infer") + + pe = paddle.zeros([max_len, dim]) + position = paddle.arange(0, max_len, dtype=paddle.float32).unsqueeze(1) + div_term = paddle.exp( + paddle.arange(0, dim, 2).astype("float32") * (-math.log(10000.0) / dim) + ) + pe[:, 0::2] = paddle.sin(position * div_term) + pe[:, 1::2] = paddle.cos(position * div_term) + pe = paddle.unsqueeze(pe, 0) + self.register_buffer("pe", pe) + + def forward(self, x): + x = x + self.pe[:, : x.shape[1]] + return self.dropout(x) + + +class PositionwiseFeedForward(nn.Layer): + "Implements FFN equation." + + def __init__(self, d_model, d_ff, dropout=0.1): + super(PositionwiseFeedForward, self).__init__() + self.w_1 = nn.Linear(d_model, d_ff) + self.w_2 = nn.Linear(d_ff, d_model) + self.dropout = nn.Dropout(dropout, mode="downscale_in_infer") + + def forward(self, x): + return self.w_2(self.dropout(F.relu(self.w_1(x)))) + + +class Generator(nn.Layer): + "Define standard linear + softmax generation step." + + def __init__(self, d_model, vocab): + super(Generator, self).__init__() + self.proj = nn.Linear(d_model, vocab) + self.relu = nn.ReLU() + + def forward(self, x): + out = self.proj(x) + return out + + +class Embeddings(nn.Layer): + def __init__(self, d_model, vocab): + super(Embeddings, self).__init__() + self.lut = nn.Embedding(vocab, d_model) + self.d_model = d_model + + def forward(self, x): + embed = self.lut(x) * math.sqrt(self.d_model) + return embed + + +class LayerNorm(nn.Layer): + "Construct a layernorm module (See citation for details)." + + def __init__(self, features, eps=1e-6): + super(LayerNorm, self).__init__() + self.a_2 = self.create_parameter( + shape=[features], default_initializer=paddle.nn.initializer.Constant(1.0) + ) + self.b_2 = self.create_parameter( + shape=[features], default_initializer=paddle.nn.initializer.Constant(0.0) + ) + self.eps = eps + + def forward(self, x): + mean = x.mean(-1, keepdim=True) + std = x.std(-1, keepdim=True) + return self.a_2 * (x - mean) / (std + self.eps) + self.b_2 + + +class Decoder(nn.Layer): + def __init__(self): + super(Decoder, self).__init__() + + self.mask_multihead = MultiHeadedAttention(h=16, d_model=1024, dropout=0.1) + self.mul_layernorm1 = LayerNorm(1024) + + self.multihead = MultiHeadedAttention(h=16, d_model=1024, dropout=0.1) + self.mul_layernorm2 = LayerNorm(1024) + + self.pff = PositionwiseFeedForward(1024, 2048) + self.mul_layernorm3 = LayerNorm(1024) + + def forward(self, text, conv_feature, attention_map=None): + text_max_length = text.shape[1] + mask = subsequent_mask(text_max_length) + result = text + result = self.mul_layernorm1( + result + self.mask_multihead(text, text, text, mask=mask)[0] + ) + b, c, h, w = conv_feature.shape + conv_feature = paddle.transpose(conv_feature.reshape([b, c, h * w]), [0, 2, 1]) + word_image_align, attention_map = self.multihead( + result, conv_feature, conv_feature, mask=None, attention_map=attention_map + ) + result = self.mul_layernorm2(result + word_image_align) + result = self.mul_layernorm3(result + self.pff(result)) + + return result, attention_map + + +class BasicBlock(nn.Layer): + def __init__(self, inplanes, planes, downsample): + super(BasicBlock, self).__init__() + self.conv1 = nn.Conv2D(inplanes, planes, kernel_size=3, stride=1, padding=1) + self.bn1 = nn.BatchNorm2D(planes, use_global_stats=True) + self.relu = nn.ReLU() + self.conv2 = nn.Conv2D(planes, planes, kernel_size=3, stride=1, padding=1) + self.bn2 = nn.BatchNorm2D(planes, use_global_stats=True) + self.downsample = downsample + + def forward(self, x): + residual = x + + out = self.conv1(x) + out = self.bn1(out) + out = self.relu(out) + + out = self.conv2(out) + out = self.bn2(out) + + if self.downsample != None: + residual = self.downsample(residual) + + out += residual + out = self.relu(out) + + return out + + +class Encoder(nn.Layer): + def __init__(self): + super(Encoder, self).__init__() + self.cnn = ResNet(num_in=1, block=BasicBlock, layers=[1, 2, 5, 3]) + + def forward(self, input): + conv_result = self.cnn(input) + return conv_result + + +class Transformer(nn.Layer): + def __init__(self, in_channels=1, alphabet="0123456789"): + super(Transformer, self).__init__() + self.alphabet = alphabet + word_n_class = self.get_alphabet_len() + self.embedding_word_with_upperword = Embeddings(512, word_n_class) + self.pe = PositionalEncoding(dim=512, dropout=0.1, max_len=5000) + + self.encoder = Encoder() + self.decoder = Decoder() + self.generator_word_with_upperword = Generator(1024, word_n_class) + + for p in self.parameters(): + if p.dim() > 1: + nn.initializer.XavierNormal(p) + + def get_alphabet_len(self): + return len(self.alphabet) + + def forward(self, image, text_length, text_input, attention_map=None): + if image.shape[1] == 3: + R = image[:, 0:1, :, :] + G = image[:, 1:2, :, :] + B = image[:, 2:3, :, :] + image = 0.299 * R + 0.587 * G + 0.114 * B + + conv_feature = self.encoder(image) # batch, 1024, 8, 32 + max_length = max(text_length) + text_input = text_input[:, :max_length] + + text_embedding = self.embedding_word_with_upperword( + text_input + ) # batch, text_max_length, 512 + postion_embedding = self.pe( + paddle.zeros(text_embedding.shape) + ) # batch, text_max_length, 512 + text_input_with_pe = paddle.concat( + [text_embedding, postion_embedding], 2 + ) # batch, text_max_length, 1024 + batch, seq_len, _ = text_input_with_pe.shape + + text_input_with_pe, word_attention_map = self.decoder( + text_input_with_pe, conv_feature + ) + + word_decoder_result = self.generator_word_with_upperword(text_input_with_pe) + + if self.training: + total_length = paddle.sum(text_length) + probs_res = paddle.zeros([total_length, self.get_alphabet_len()]) + start = 0 + + for index, length in enumerate(text_length): + length = int(length.numpy()) + probs_res[start : start + length, :] = word_decoder_result[ + index, 0 : 0 + length, : + ] + + start = start + length + + return probs_res, word_attention_map, None + else: + return word_decoder_result diff --git a/modules/onnx_ocr_module/src/ppocr/modeling/heads/table_att_head.py b/modules/onnx_ocr_module/src/ppocr/modeling/heads/table_att_head.py new file mode 100644 index 0000000..1f43af1 --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/modeling/heads/table_att_head.py @@ -0,0 +1,421 @@ +# copyright (c) 2021 PaddlePaddle Authors. All Rights Reserve. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import math +import paddle +import paddle.nn as nn +from paddle import ParamAttr +import paddle.nn.functional as F +import numpy as np + +from .rec_att_head import AttentionGRUCell +from ppocr.modeling.backbones.rec_svtrnet import DropPath, Identity, Mlp + + +def get_para_bias_attr(l2_decay, k): + if l2_decay > 0: + regularizer = paddle.regularizer.L2Decay(l2_decay) + stdv = 1.0 / math.sqrt(k * 1.0) + initializer = nn.initializer.Uniform(-stdv, stdv) + else: + regularizer = None + initializer = None + weight_attr = ParamAttr(regularizer=regularizer, initializer=initializer) + bias_attr = ParamAttr(regularizer=regularizer, initializer=initializer) + return [weight_attr, bias_attr] + + +class TableAttentionHead(nn.Layer): + def __init__( + self, + in_channels, + hidden_size, + in_max_len=488, + max_text_length=800, + out_channels=30, + loc_reg_num=4, + **kwargs, + ): + super(TableAttentionHead, self).__init__() + self.input_size = in_channels[-1] + self.hidden_size = hidden_size + self.out_channels = out_channels + self.max_text_length = max_text_length + + self.structure_attention_cell = AttentionGRUCell( + self.input_size, hidden_size, self.out_channels, use_gru=False + ) + self.structure_generator = nn.Linear(hidden_size, self.out_channels) + self.in_max_len = in_max_len + + if self.in_max_len == 640: + self.loc_fea_trans = nn.Linear(400, self.max_text_length + 1) + elif self.in_max_len == 800: + self.loc_fea_trans = nn.Linear(625, self.max_text_length + 1) + else: + self.loc_fea_trans = nn.Linear(256, self.max_text_length + 1) + self.loc_generator = nn.Linear(self.input_size + hidden_size, loc_reg_num) + + def _char_to_onehot(self, input_char, onehot_dim): + input_ont_hot = F.one_hot(input_char, onehot_dim) + return input_ont_hot + + def forward(self, inputs, targets=None): + # if and else branch are both needed when you want to assign a variable + # if you modify the var in just one branch, then the modification will not work. + fea = inputs[-1] + last_shape = int(np.prod(fea.shape[2:])) # gry added + fea = paddle.reshape(fea, [fea.shape[0], fea.shape[1], last_shape]) + fea = fea.transpose([0, 2, 1]) # (NTC)(batch, width, channels) + batch_size = fea.shape[0] + + hidden = paddle.zeros((batch_size, self.hidden_size)) + output_hiddens = paddle.zeros( + (batch_size, self.max_text_length + 1, self.hidden_size) + ) + if self.training and targets is not None: + structure = targets[0] + for i in range(self.max_text_length + 1): + elem_onehots = self._char_to_onehot( + structure[:, i], onehot_dim=self.out_channels + ) + (outputs, hidden), alpha = self.structure_attention_cell( + hidden, fea, elem_onehots + ) + output_hiddens[:, i, :] = outputs + structure_probs = self.structure_generator(output_hiddens) + loc_fea = fea.transpose([0, 2, 1]) + loc_fea = self.loc_fea_trans(loc_fea) + loc_fea = loc_fea.transpose([0, 2, 1]) + loc_concat = paddle.concat([output_hiddens, loc_fea], axis=2) + loc_preds = self.loc_generator(loc_concat) + loc_preds = F.sigmoid(loc_preds) + else: + temp_elem = paddle.zeros(shape=[batch_size], dtype="int32") + structure_probs = None + loc_preds = None + elem_onehots = None + outputs = None + alpha = None + max_text_length = paddle.to_tensor(self.max_text_length) + for i in range(max_text_length + 1): + elem_onehots = self._char_to_onehot( + temp_elem, onehot_dim=self.out_channels + ) + (outputs, hidden), alpha = self.structure_attention_cell( + hidden, fea, elem_onehots + ) + output_hiddens[:, i, :] = outputs + structure_probs_step = self.structure_generator(outputs) + temp_elem = structure_probs_step.argmax(axis=1, dtype="int32") + + structure_probs = self.structure_generator(output_hiddens) + structure_probs = F.softmax(structure_probs) + loc_fea = fea.transpose([0, 2, 1]) + loc_fea = self.loc_fea_trans(loc_fea) + loc_fea = loc_fea.transpose([0, 2, 1]) + loc_concat = paddle.concat([output_hiddens, loc_fea], axis=2) + loc_preds = self.loc_generator(loc_concat) + loc_preds = F.sigmoid(loc_preds) + return {"structure_probs": structure_probs, "loc_preds": loc_preds} + + +class HWAttention(nn.Layer): + def __init__( + self, + head_dim=32, + qk_scale=None, + attn_drop=0.0, + ): + super().__init__() + self.head_dim = head_dim + self.scale = qk_scale or self.head_dim**-0.5 + self.attn_drop = nn.Dropout(attn_drop) + + def forward(self, x): + B, N, C = x.shape + C = C // 3 + qkv = x.reshape([B, N, 3, C // self.head_dim, self.head_dim]).transpose( + [2, 0, 3, 1, 4] + ) + q, k, v = qkv.unbind(0) + attn = q @ k.transpose([0, 1, 3, 2]) * self.scale + attn = F.softmax(attn, -1) + attn = self.attn_drop(attn) + x = attn @ v + x = x.transpose([0, 2, 1]).reshape([B, N, C]) + return x + + +def img2windows(img, H_sp, W_sp): + """ + img: B C H W + """ + B, H, W, C = img.shape + img_reshape = img.reshape([B, H // H_sp, H_sp, W // W_sp, W_sp, C]) + img_perm = img_reshape.transpose([0, 1, 3, 2, 4, 5]).reshape([-1, H_sp * W_sp, C]) + return img_perm + + +def windows2img(img_splits_hw, H_sp, W_sp, H, W): + """ + img_splits_hw: B' H W C + """ + B = int(img_splits_hw.shape[0] / (H * W / H_sp / W_sp)) + + img = img_splits_hw.reshape([B, H // H_sp, W // W_sp, H_sp, W_sp, -1]) + img = img.transpose([0, 1, 3, 2, 4, 5]).flatten(1, 4) + return img + + +class Block(nn.Layer): + def __init__( + self, + dim, + num_heads, + split_h=4, + split_w=4, + h_num_heads=None, + w_num_heads=None, + mlp_ratio=4.0, + qkv_bias=False, + qk_scale=None, + drop=0.0, + attn_drop=0.0, + drop_path=0.0, + act_layer=nn.GELU, + norm_layer=nn.LayerNorm, + eps=1e-6, + ): + super().__init__() + self.qkv = nn.Linear(dim, dim * 3, bias_attr=qkv_bias) + self.proj = nn.Linear(dim, dim) + self.split_h = split_h + self.split_w = split_w + mlp_hidden_dim = int(dim * mlp_ratio) + self.norm1 = norm_layer(dim, epsilon=eps) + self.h_num_heads = h_num_heads if h_num_heads is not None else num_heads // 2 + self.w_num_heads = w_num_heads if w_num_heads is not None else num_heads // 2 + self.head_dim = dim // num_heads + self.mixer = HWAttention( + head_dim=dim // num_heads, + qk_scale=qk_scale, + attn_drop=attn_drop, + ) + self.drop_path = DropPath(drop_path) if drop_path > 0.0 else Identity() + self.norm2 = norm_layer(dim, epsilon=eps) + self.mlp = Mlp( + in_features=dim, + hidden_features=mlp_hidden_dim, + act_layer=act_layer, + drop=drop, + ) + + def forward(self, x): + B, C, H, W = x.shape + x = x.flatten(2).transpose([0, 2, 1]) + + qkv = self.qkv(x).reshape([B, H, W, 3 * C]) + + x1 = qkv[:, :, :, : 3 * self.h_num_heads * self.head_dim] # b, h, w, 3ch + x2 = qkv[:, :, :, 3 * self.h_num_heads * self.head_dim :] # b, h, w, 3cw + + x1 = self.mixer(img2windows(x1, self.split_h, W)) # b*splith, W, 3ch + x2 = self.mixer(img2windows(x2, H, self.split_w)) # b*splitw, h, 3ch + x1 = windows2img(x1, self.split_h, W, H, W) + x2 = windows2img(x2, H, self.split_w, H, W) + + attened_x = paddle.concat([x1, x2], 2) + attened_x = self.proj(attened_x) + + x = self.norm1(x + self.drop_path(attened_x)) + x = self.norm2(x + self.drop_path(self.mlp(x))) + x = x.transpose([0, 2, 1]).reshape([-1, C, H, W]) + return x + + +class SLAHead(nn.Layer): + def __init__( + self, + in_channels, + hidden_size, + out_channels=30, + max_text_length=500, + loc_reg_num=4, + fc_decay=0.0, + use_attn=False, + **kwargs, + ): + """ + @param in_channels: input shape + @param hidden_size: hidden_size for RNN and Embedding + @param out_channels: num_classes to rec + @param max_text_length: max text pred + """ + super().__init__() + + if isinstance(in_channels, int): + self.is_next = True + in_channels = 512 + else: + self.is_next = False + in_channels = in_channels[-1] + self.hidden_size = hidden_size + self.max_text_length = max_text_length + self.emb = self._char_to_onehot + self.num_embeddings = out_channels + self.loc_reg_num = loc_reg_num + self.eos = self.num_embeddings - 1 + + # structure + self.structure_attention_cell = AttentionGRUCell( + in_channels, hidden_size, self.num_embeddings + ) + weight_attr, bias_attr = get_para_bias_attr(l2_decay=fc_decay, k=hidden_size) + weight_attr1_1, bias_attr1_1 = get_para_bias_attr( + l2_decay=fc_decay, k=hidden_size + ) + weight_attr1_2, bias_attr1_2 = get_para_bias_attr( + l2_decay=fc_decay, k=hidden_size + ) + self.structure_generator = nn.Sequential( + nn.Linear( + self.hidden_size, + self.hidden_size, + weight_attr=weight_attr1_2, + bias_attr=bias_attr1_2, + ), + nn.Linear( + hidden_size, out_channels, weight_attr=weight_attr, bias_attr=bias_attr + ), + ) + dpr = np.linspace(0, 0.1, 2) + + self.use_attn = use_attn + if use_attn: + layer_list = [ + Block( + in_channels, + num_heads=2, + mlp_ratio=4.0, + qkv_bias=True, + drop_path=dpr[i], + ) + for i in range(2) + ] + self.cross_atten = nn.Sequential(*layer_list) + # loc + weight_attr1, bias_attr1 = get_para_bias_attr( + l2_decay=fc_decay, k=self.hidden_size + ) + weight_attr2, bias_attr2 = get_para_bias_attr( + l2_decay=fc_decay, k=self.hidden_size + ) + self.loc_generator = nn.Sequential( + nn.Linear( + self.hidden_size, + self.hidden_size, + weight_attr=weight_attr1, + bias_attr=bias_attr1, + ), + nn.Linear( + self.hidden_size, + loc_reg_num, + weight_attr=weight_attr2, + bias_attr=bias_attr2, + ), + nn.Sigmoid(), + ) + + def forward(self, inputs, targets=None): + if self.is_next == True: + fea = inputs + batch_size = fea.shape[0] + else: + fea = inputs[-1] + batch_size = fea.shape[0] + if self.use_attn: + fea = fea + self.cross_atten(fea) + # reshape + fea = paddle.reshape(fea, [fea.shape[0], fea.shape[1], -1]) + fea = fea.transpose([0, 2, 1]) # (NTC)(batch, width, channels) + + hidden = paddle.zeros((batch_size, self.hidden_size)) + structure_preds = paddle.zeros( + (batch_size, self.max_text_length + 1, self.num_embeddings) + ) + loc_preds = paddle.zeros( + (batch_size, self.max_text_length + 1, self.loc_reg_num) + ) + structure_preds.stop_gradient = True + loc_preds.stop_gradient = True + + if self.training and targets is not None: + structure = targets[0] + max_len = targets[-2].max().astype("int32") + for i in range(max_len + 1): + hidden, structure_step, loc_step = self._decode( + structure[:, i], fea, hidden + ) + structure_preds[:, i, :] = structure_step + loc_preds[:, i, :] = loc_step + structure_preds = structure_preds[:, : max_len + 1] + loc_preds = loc_preds[:, : max_len + 1] + else: + structure_ids = paddle.zeros( + (batch_size, self.max_text_length + 1), dtype="int32" + ) + pre_chars = paddle.zeros(shape=[batch_size], dtype="int32") + max_text_length = paddle.to_tensor(self.max_text_length) + for i in range(max_text_length + 1): + hidden, structure_step, loc_step = self._decode(pre_chars, fea, hidden) + pre_chars = structure_step.argmax(axis=1, dtype="int32") + structure_preds[:, i, :] = structure_step + loc_preds[:, i, :] = loc_step + + structure_ids[:, i] = pre_chars + if (structure_ids == self.eos).any(-1).all(): + break + if not self.training: + structure_preds = F.softmax(structure_preds[:, : i + 1]) + loc_preds = loc_preds[:, : i + 1] + return {"structure_probs": structure_preds, "loc_preds": loc_preds} + + def _decode(self, pre_chars, features, hidden): + """ + Predict table label and coordinates for each step + @param pre_chars: Table label in previous step + @param features: + @param hidden: hidden status in previous step + @return: + """ + emb_feature = self.emb(pre_chars) + # output shape is b * self.hidden_size + (output, hidden), alpha = self.structure_attention_cell( + hidden, features, emb_feature + ) + + # structure + structure_step = self.structure_generator(output) + # loc + loc_step = self.loc_generator(output) + return hidden, structure_step, loc_step + + def _char_to_onehot(self, input_char): + input_ont_hot = F.one_hot(input_char, self.num_embeddings) + return input_ont_hot diff --git a/modules/onnx_ocr_module/src/ppocr/modeling/heads/table_master_head.py b/modules/onnx_ocr_module/src/ppocr/modeling/heads/table_master_head.py new file mode 100644 index 0000000..66ad7a4 --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/modeling/heads/table_master_head.py @@ -0,0 +1,285 @@ +# Copyright (c) 2021 PaddlePaddle Authors. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +""" +This code is refer from: +https://github.com/JiaquanYe/TableMASTER-mmocr/blob/master/mmocr/models/textrecog/decoders/master_decoder.py +""" + +import copy +import math +import paddle +from paddle import nn +from paddle.nn import functional as F + + +class TableMasterHead(nn.Layer): + """ + Split to two transformer header at the last layer. + Cls_layer is used to structure token classification. + Bbox_layer is used to regress bbox coord. + """ + + def __init__( + self, + in_channels, + out_channels=30, + headers=8, + d_ff=2048, + dropout=0, + max_text_length=500, + loc_reg_num=4, + **kwargs, + ): + super(TableMasterHead, self).__init__() + hidden_size = in_channels[-1] + self.layers = clones(DecoderLayer(headers, hidden_size, dropout, d_ff), 2) + self.cls_layer = clones(DecoderLayer(headers, hidden_size, dropout, d_ff), 1) + self.bbox_layer = clones(DecoderLayer(headers, hidden_size, dropout, d_ff), 1) + self.cls_fc = nn.Linear(hidden_size, out_channels) + self.bbox_fc = nn.Sequential( + # nn.Linear(hidden_size, hidden_size), + nn.Linear(hidden_size, loc_reg_num), + nn.Sigmoid(), + ) + self.norm = nn.LayerNorm(hidden_size) + self.embedding = Embeddings(d_model=hidden_size, vocab=out_channels) + self.positional_encoding = PositionalEncoding(d_model=hidden_size) + + self.SOS = out_channels - 3 + self.PAD = out_channels - 1 + self.out_channels = out_channels + self.loc_reg_num = loc_reg_num + self.max_text_length = max_text_length + + def make_mask(self, tgt): + """ + Make mask for self attention. + :param src: [b, c, h, l_src] + :param tgt: [b, l_tgt] + :return: + """ + trg_pad_mask = (tgt != self.PAD).unsqueeze(1).unsqueeze(3) + + tgt_len = tgt.shape[1] + trg_sub_mask = paddle.tril( + paddle.ones(([tgt_len, tgt_len]), dtype=paddle.float32) + ) + + tgt_mask = paddle.logical_and(trg_pad_mask.astype(paddle.float32), trg_sub_mask) + return tgt_mask.astype(paddle.float32) + + def decode(self, input, feature, src_mask, tgt_mask): + # main process of transformer decoder. + x = self.embedding(input) # x: 1*x*512, feature: 1*3600,512 + x = self.positional_encoding(x) + + # origin transformer layers + for i, layer in enumerate(self.layers): + x = layer(x, feature, src_mask, tgt_mask) + + # cls head + cls_x = x + for layer in self.cls_layer: + cls_x = layer(x, feature, src_mask, tgt_mask) + cls_x = self.norm(cls_x) + + # bbox head + bbox_x = x + for layer in self.bbox_layer: + bbox_x = layer(x, feature, src_mask, tgt_mask) + bbox_x = self.norm(bbox_x) + return self.cls_fc(cls_x), self.bbox_fc(bbox_x) + + def greedy_forward(self, SOS, feature): + input = SOS + output = paddle.zeros( + [input.shape[0], self.max_text_length + 1, self.out_channels] + ) + bbox_output = paddle.zeros( + [input.shape[0], self.max_text_length + 1, self.loc_reg_num] + ) + max_text_length = paddle.to_tensor(self.max_text_length) + for i in range(max_text_length + 1): + target_mask = self.make_mask(input) + out_step, bbox_output_step = self.decode(input, feature, None, target_mask) + prob = F.softmax(out_step, axis=-1) + next_word = prob.argmax(axis=2, dtype="int64") + input = paddle.concat([input, next_word[:, -1].unsqueeze(-1)], axis=1) + if i == self.max_text_length: + output = out_step + bbox_output = bbox_output_step + return output, bbox_output + + def forward_train(self, out_enc, targets): + # x is token of label + # feat is feature after backbone before pe. + # out_enc is feature after pe. + padded_targets = targets[0] + src_mask = None + tgt_mask = self.make_mask(padded_targets[:, :-1]) + output, bbox_output = self.decode( + padded_targets[:, :-1], out_enc, src_mask, tgt_mask + ) + return {"structure_probs": output, "loc_preds": bbox_output} + + def forward_test(self, out_enc): + batch_size = out_enc.shape[0] + SOS = paddle.zeros([batch_size, 1], dtype="int64") + self.SOS + output, bbox_output = self.greedy_forward(SOS, out_enc) + output = F.softmax(output) + return {"structure_probs": output, "loc_preds": bbox_output} + + def forward(self, feat, targets=None): + feat = feat[-1] + b, c, h, w = feat.shape + feat = feat.reshape([b, c, h * w]) # flatten 2D feature map + feat = feat.transpose((0, 2, 1)) + out_enc = self.positional_encoding(feat) + if self.training: + return self.forward_train(out_enc, targets) + + return self.forward_test(out_enc) + + +class DecoderLayer(nn.Layer): + """ + Decoder is made of self attention, source attention and feed forward. + """ + + def __init__(self, headers, d_model, dropout, d_ff): + super(DecoderLayer, self).__init__() + self.self_attn = MultiHeadAttention(headers, d_model, dropout) + self.src_attn = MultiHeadAttention(headers, d_model, dropout) + self.feed_forward = FeedForward(d_model, d_ff, dropout) + self.sublayer = clones(SubLayerConnection(d_model, dropout), 3) + + def forward(self, x, feature, src_mask, tgt_mask): + x = self.sublayer[0](x, lambda x: self.self_attn(x, x, x, tgt_mask)) + x = self.sublayer[1](x, lambda x: self.src_attn(x, feature, feature, src_mask)) + return self.sublayer[2](x, self.feed_forward) + + +class MultiHeadAttention(nn.Layer): + def __init__(self, headers, d_model, dropout): + super(MultiHeadAttention, self).__init__() + + assert d_model % headers == 0 + self.d_k = int(d_model / headers) + self.headers = headers + self.linears = clones(nn.Linear(d_model, d_model), 4) + self.attn = None + self.dropout = nn.Dropout(dropout) + + def forward(self, query, key, value, mask=None): + B = query.shape[0] + + # 1) Do all the linear projections in batch from d_model => h x d_k + query, key, value = [ + l(x).reshape([B, 0, self.headers, self.d_k]).transpose([0, 2, 1, 3]) + for l, x in zip(self.linears, (query, key, value)) + ] + # 2) Apply attention on all the projected vectors in batch + x, self.attn = self_attention( + query, key, value, mask=mask, dropout=self.dropout + ) + x = x.transpose([0, 2, 1, 3]).reshape([B, 0, self.headers * self.d_k]) + return self.linears[-1](x) + + +class FeedForward(nn.Layer): + def __init__(self, d_model, d_ff, dropout): + super(FeedForward, self).__init__() + self.w_1 = nn.Linear(d_model, d_ff) + self.w_2 = nn.Linear(d_ff, d_model) + self.dropout = nn.Dropout(dropout) + + def forward(self, x): + return self.w_2(self.dropout(F.relu(self.w_1(x)))) + + +class SubLayerConnection(nn.Layer): + """ + A residual connection followed by a layer norm. + Note for code simplicity the norm is first as opposed to last. + """ + + def __init__(self, size, dropout): + super(SubLayerConnection, self).__init__() + self.norm = nn.LayerNorm(size) + self.dropout = nn.Dropout(dropout) + + def forward(self, x, sublayer): + return x + self.dropout(sublayer(self.norm(x))) + + +def masked_fill(x, mask, value): + mask = mask.astype(x.dtype) + return x * paddle.logical_not(mask).astype(x.dtype) + mask * value + + +def self_attention(query, key, value, mask=None, dropout=None): + """ + Compute 'Scale Dot Product Attention' + """ + d_k = value.shape[-1] + + score = paddle.matmul(query, key.transpose([0, 1, 3, 2]) / math.sqrt(d_k)) + if mask is not None: + # score = score.masked_fill(mask == 0, -1e9) # b, h, L, L + score = masked_fill(score, mask == 0, -6.55e4) # for fp16 + + p_attn = F.softmax(score, axis=-1) + + if dropout is not None: + p_attn = dropout(p_attn) + return paddle.matmul(p_attn, value), p_attn + + +def clones(module, N): + """Produce N identical layers""" + return nn.LayerList([copy.deepcopy(module) for _ in range(N)]) + + +class Embeddings(nn.Layer): + def __init__(self, d_model, vocab): + super(Embeddings, self).__init__() + self.lut = nn.Embedding(vocab, d_model) + self.d_model = d_model + + def forward(self, *input): + x = input[0] + return self.lut(x) * math.sqrt(self.d_model) + + +class PositionalEncoding(nn.Layer): + """Implement the PE function.""" + + def __init__(self, d_model, dropout=0.0, max_len=5000): + super(PositionalEncoding, self).__init__() + self.dropout = nn.Dropout(p=dropout) + + # Compute the positional encodings once in log space. + pe = paddle.zeros([max_len, d_model]) + position = paddle.arange(0, max_len).unsqueeze(1).astype("float32") + div_term = paddle.exp( + paddle.arange(0, d_model, 2) * -math.log(10000.0) / d_model + ) + pe[:, 0::2] = paddle.sin(position * div_term) + pe[:, 1::2] = paddle.cos(position * div_term) + pe = pe.unsqueeze(0) + self.register_buffer("pe", pe) + + def forward(self, feat, **kwargs): + feat = feat + self.pe[:, : feat.shape[1]] # pe 1*5000*512 + return self.dropout(feat) diff --git a/modules/onnx_ocr_module/src/ppocr/modeling/necks/__init__.py b/modules/onnx_ocr_module/src/ppocr/modeling/necks/__init__.py new file mode 100644 index 0000000..47e4c7e --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/modeling/necks/__init__.py @@ -0,0 +1,57 @@ +# Copyright (c) 2020 PaddlePaddle Authors. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +__all__ = ["build_neck"] + + +def build_neck(config): + from .db_fpn import DBFPN, RSEFPN, LKPAN + from .east_fpn import EASTFPN + from .sast_fpn import SASTFPN + from .rnn import SequenceEncoder + from .pg_fpn import PGFPN + from .table_fpn import TableFPN + from .fpn import FPN + from .fce_fpn import FCEFPN + from .pren_fpn import PRENFPN + from .csp_pan import CSPPAN + from .ct_fpn import CTFPN + from .fpn_unet import FPN_UNet + from .rf_adaptor import RFAdaptor + + support_dict = [ + "FPN", + "FCEFPN", + "LKPAN", + "DBFPN", + "RSEFPN", + "EASTFPN", + "SASTFPN", + "SequenceEncoder", + "PGFPN", + "TableFPN", + "PRENFPN", + "CSPPAN", + "CTFPN", + "RFAdaptor", + "FPN_UNet", + ] + + module_name = config.pop("name") + assert module_name in support_dict, Exception( + "neck only support {}".format(support_dict) + ) + + module_class = eval(module_name)(**config) + return module_class diff --git a/modules/onnx_ocr_module/src/ppocr/modeling/necks/csp_pan.py b/modules/onnx_ocr_module/src/ppocr/modeling/necks/csp_pan.py new file mode 100644 index 0000000..5e8464d --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/modeling/necks/csp_pan.py @@ -0,0 +1,337 @@ +# Copyright (c) 2021 PaddlePaddle Authors. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# The code is based on: +# https://github.com/PaddlePaddle/PaddleDetection/blob/release%2F2.3/ppdet/modeling/necks/csp_pan.py + +import paddle +import paddle.nn as nn +import paddle.nn.functional as F +from paddle import ParamAttr + +__all__ = ["CSPPAN"] + + +class ConvBNLayer(nn.Layer): + def __init__( + self, + in_channel=96, + out_channel=96, + kernel_size=3, + stride=1, + groups=1, + act="leaky_relu", + ): + super(ConvBNLayer, self).__init__() + initializer = nn.initializer.KaimingUniform() + self.act = act + assert self.act in ["leaky_relu", "hard_swish"] + self.conv = nn.Conv2D( + in_channels=in_channel, + out_channels=out_channel, + kernel_size=kernel_size, + groups=groups, + padding=(kernel_size - 1) // 2, + stride=stride, + weight_attr=ParamAttr(initializer=initializer), + bias_attr=False, + ) + self.bn = nn.BatchNorm2D(out_channel) + + def forward(self, x): + x = self.bn(self.conv(x)) + if self.act == "leaky_relu": + x = F.leaky_relu(x) + elif self.act == "hard_swish": + x = F.hardswish(x) + return x + + +class DPModule(nn.Layer): + """ + Depth-wise and point-wise module. + Args: + in_channel (int): The input channels of this Module. + out_channel (int): The output channels of this Module. + kernel_size (int): The conv2d kernel size of this Module. + stride (int): The conv2d's stride of this Module. + act (str): The activation function of this Module, + Now support `leaky_relu` and `hard_swish`. + """ + + def __init__( + self, in_channel=96, out_channel=96, kernel_size=3, stride=1, act="leaky_relu" + ): + super(DPModule, self).__init__() + initializer = nn.initializer.KaimingUniform() + self.act = act + self.dwconv = nn.Conv2D( + in_channels=in_channel, + out_channels=out_channel, + kernel_size=kernel_size, + groups=out_channel, + padding=(kernel_size - 1) // 2, + stride=stride, + weight_attr=ParamAttr(initializer=initializer), + bias_attr=False, + ) + self.bn1 = nn.BatchNorm2D(out_channel) + self.pwconv = nn.Conv2D( + in_channels=out_channel, + out_channels=out_channel, + kernel_size=1, + groups=1, + padding=0, + weight_attr=ParamAttr(initializer=initializer), + bias_attr=False, + ) + self.bn2 = nn.BatchNorm2D(out_channel) + + def act_func(self, x): + if self.act == "leaky_relu": + x = F.leaky_relu(x) + elif self.act == "hard_swish": + x = F.hardswish(x) + return x + + def forward(self, x): + x = self.act_func(self.bn1(self.dwconv(x))) + x = self.act_func(self.bn2(self.pwconv(x))) + return x + + +class DarknetBottleneck(nn.Layer): + """The basic bottleneck block used in Darknet. + Each Block consists of two ConvModules and the input is added to the + final output. Each ConvModule is composed of Conv, BN, and act. + The first convLayer has filter size of 1x1 and the second one has the + filter size of 3x3. + Args: + in_channels (int): The input channels of this Module. + out_channels (int): The output channels of this Module. + expansion (int): The kernel size of the convolution. Default: 0.5 + add_identity (bool): Whether to add identity to the out. + Default: True + use_depthwise (bool): Whether to use depthwise separable convolution. + Default: False + """ + + def __init__( + self, + in_channels, + out_channels, + kernel_size=3, + expansion=0.5, + add_identity=True, + use_depthwise=False, + act="leaky_relu", + ): + super(DarknetBottleneck, self).__init__() + hidden_channels = int(out_channels * expansion) + conv_func = DPModule if use_depthwise else ConvBNLayer + self.conv1 = ConvBNLayer( + in_channel=in_channels, out_channel=hidden_channels, kernel_size=1, act=act + ) + self.conv2 = conv_func( + in_channel=hidden_channels, + out_channel=out_channels, + kernel_size=kernel_size, + stride=1, + act=act, + ) + self.add_identity = add_identity and in_channels == out_channels + + def forward(self, x): + identity = x + out = self.conv1(x) + out = self.conv2(out) + + if self.add_identity: + return out + identity + else: + return out + + +class CSPLayer(nn.Layer): + """Cross Stage Partial Layer. + Args: + in_channels (int): The input channels of the CSP layer. + out_channels (int): The output channels of the CSP layer. + expand_ratio (float): Ratio to adjust the number of channels of the + hidden layer. Default: 0.5 + num_blocks (int): Number of blocks. Default: 1 + add_identity (bool): Whether to add identity in blocks. + Default: True + use_depthwise (bool): Whether to depthwise separable convolution in + blocks. Default: False + """ + + def __init__( + self, + in_channels, + out_channels, + kernel_size=3, + expand_ratio=0.5, + num_blocks=1, + add_identity=True, + use_depthwise=False, + act="leaky_relu", + ): + super().__init__() + mid_channels = int(out_channels * expand_ratio) + self.main_conv = ConvBNLayer(in_channels, mid_channels, 1, act=act) + self.short_conv = ConvBNLayer(in_channels, mid_channels, 1, act=act) + self.final_conv = ConvBNLayer(2 * mid_channels, out_channels, 1, act=act) + + self.blocks = nn.Sequential( + *[ + DarknetBottleneck( + mid_channels, + mid_channels, + kernel_size, + 1.0, + add_identity, + use_depthwise, + act=act, + ) + for _ in range(num_blocks) + ] + ) + + def forward(self, x): + x_short = self.short_conv(x) + + x_main = self.main_conv(x) + x_main = self.blocks(x_main) + + x_final = paddle.concat((x_main, x_short), axis=1) + return self.final_conv(x_final) + + +class Channel_T(nn.Layer): + def __init__(self, in_channels=[116, 232, 464], out_channels=96, act="leaky_relu"): + super(Channel_T, self).__init__() + self.convs = nn.LayerList() + for i in range(len(in_channels)): + self.convs.append(ConvBNLayer(in_channels[i], out_channels, 1, act=act)) + + def forward(self, x): + outs = [self.convs[i](x[i]) for i in range(len(x))] + return outs + + +class CSPPAN(nn.Layer): + """Path Aggregation Network with CSP module. + Args: + in_channels (List[int]): Number of input channels per scale. + out_channels (int): Number of output channels (used at each scale) + kernel_size (int): The conv2d kernel size of this Module. + num_csp_blocks (int): Number of bottlenecks in CSPLayer. Default: 1 + use_depthwise (bool): Whether to depthwise separable convolution in + blocks. Default: True + """ + + def __init__( + self, + in_channels, + out_channels, + kernel_size=5, + num_csp_blocks=1, + use_depthwise=True, + act="hard_swish", + ): + super(CSPPAN, self).__init__() + self.in_channels = in_channels + self.out_channels = [out_channels] * len(in_channels) + conv_func = DPModule if use_depthwise else ConvBNLayer + + self.conv_t = Channel_T(in_channels, out_channels, act=act) + + # build top-down blocks + self.upsample = nn.Upsample(scale_factor=2, mode="nearest") + self.top_down_blocks = nn.LayerList() + for idx in range(len(in_channels) - 1, 0, -1): + self.top_down_blocks.append( + CSPLayer( + out_channels * 2, + out_channels, + kernel_size=kernel_size, + num_blocks=num_csp_blocks, + add_identity=False, + use_depthwise=use_depthwise, + act=act, + ) + ) + + # build bottom-up blocks + self.downsamples = nn.LayerList() + self.bottom_up_blocks = nn.LayerList() + for idx in range(len(in_channels) - 1): + self.downsamples.append( + conv_func( + out_channels, + out_channels, + kernel_size=kernel_size, + stride=2, + act=act, + ) + ) + self.bottom_up_blocks.append( + CSPLayer( + out_channels * 2, + out_channels, + kernel_size=kernel_size, + num_blocks=num_csp_blocks, + add_identity=False, + use_depthwise=use_depthwise, + act=act, + ) + ) + + def forward(self, inputs): + """ + Args: + inputs (tuple[Tensor]): input features. + Returns: + tuple[Tensor]: CSPPAN features. + """ + assert len(inputs) == len(self.in_channels) + inputs = self.conv_t(inputs) + + # top-down path + inner_outs = [inputs[-1]] + for idx in range(len(self.in_channels) - 1, 0, -1): + feat_heigh = inner_outs[0] + feat_low = inputs[idx - 1] + upsample_feat = F.upsample( + feat_heigh, size=feat_low.shape[2:4], mode="nearest" + ) + + inner_out = self.top_down_blocks[len(self.in_channels) - 1 - idx]( + paddle.concat([upsample_feat, feat_low], 1) + ) + inner_outs.insert(0, inner_out) + + # bottom-up path + outs = [inner_outs[0]] + for idx in range(len(self.in_channels) - 1): + feat_low = outs[-1] + feat_height = inner_outs[idx + 1] + downsample_feat = self.downsamples[idx](feat_low) + out = self.bottom_up_blocks[idx]( + paddle.concat([downsample_feat, feat_height], 1) + ) + outs.append(out) + + return tuple(outs) diff --git a/modules/onnx_ocr_module/src/ppocr/modeling/necks/ct_fpn.py b/modules/onnx_ocr_module/src/ppocr/modeling/necks/ct_fpn.py new file mode 100644 index 0000000..c7d5773 --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/modeling/necks/ct_fpn.py @@ -0,0 +1,188 @@ +# copyright (c) 2019 PaddlePaddle Authors. All Rights Reserve. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import paddle +from paddle import nn +import paddle.nn.functional as F +from paddle import ParamAttr +import os +import sys + +import math +from paddle.nn.initializer import TruncatedNormal, Constant, Normal + +ones_ = Constant(value=1.0) +zeros_ = Constant(value=0.0) + +__dir__ = os.path.dirname(os.path.abspath(__file__)) +sys.path.append(__dir__) +sys.path.insert(0, os.path.abspath(os.path.join(__dir__, "../../.."))) + + +class Conv_BN_ReLU(nn.Layer): + def __init__(self, in_planes, out_planes, kernel_size=1, stride=1, padding=0): + super(Conv_BN_ReLU, self).__init__() + self.conv = nn.Conv2D( + in_planes, + out_planes, + kernel_size=kernel_size, + stride=stride, + padding=padding, + bias_attr=False, + ) + self.bn = nn.BatchNorm2D(out_planes) + self.relu = nn.ReLU() + + for m in self.sublayers(): + if isinstance(m, nn.Conv2D): + n = m._kernel_size[0] * m._kernel_size[1] * m._out_channels + normal_ = Normal(mean=0.0, std=math.sqrt(2.0 / n)) + normal_(m.weight) + elif isinstance(m, nn.BatchNorm2D): + zeros_(m.bias) + ones_(m.weight) + + def forward(self, x): + return self.relu(self.bn(self.conv(x))) + + +class FPEM(nn.Layer): + def __init__(self, in_channels, out_channels): + super(FPEM, self).__init__() + planes = out_channels + self.dwconv3_1 = nn.Conv2D( + planes, + planes, + kernel_size=3, + stride=1, + padding=1, + groups=planes, + bias_attr=False, + ) + self.smooth_layer3_1 = Conv_BN_ReLU(planes, planes) + + self.dwconv2_1 = nn.Conv2D( + planes, + planes, + kernel_size=3, + stride=1, + padding=1, + groups=planes, + bias_attr=False, + ) + self.smooth_layer2_1 = Conv_BN_ReLU(planes, planes) + + self.dwconv1_1 = nn.Conv2D( + planes, + planes, + kernel_size=3, + stride=1, + padding=1, + groups=planes, + bias_attr=False, + ) + self.smooth_layer1_1 = Conv_BN_ReLU(planes, planes) + + self.dwconv2_2 = nn.Conv2D( + planes, + planes, + kernel_size=3, + stride=2, + padding=1, + groups=planes, + bias_attr=False, + ) + self.smooth_layer2_2 = Conv_BN_ReLU(planes, planes) + + self.dwconv3_2 = nn.Conv2D( + planes, + planes, + kernel_size=3, + stride=2, + padding=1, + groups=planes, + bias_attr=False, + ) + self.smooth_layer3_2 = Conv_BN_ReLU(planes, planes) + + self.dwconv4_2 = nn.Conv2D( + planes, + planes, + kernel_size=3, + stride=2, + padding=1, + groups=planes, + bias_attr=False, + ) + self.smooth_layer4_2 = Conv_BN_ReLU(planes, planes) + + def _upsample_add(self, x, y): + return F.upsample(x, scale_factor=2, mode="bilinear") + y + + def forward(self, f1, f2, f3, f4): + # up-down + f3 = self.smooth_layer3_1(self.dwconv3_1(self._upsample_add(f4, f3))) + f2 = self.smooth_layer2_1(self.dwconv2_1(self._upsample_add(f3, f2))) + f1 = self.smooth_layer1_1(self.dwconv1_1(self._upsample_add(f2, f1))) + + # down-up + f2 = self.smooth_layer2_2(self.dwconv2_2(self._upsample_add(f2, f1))) + f3 = self.smooth_layer3_2(self.dwconv3_2(self._upsample_add(f3, f2))) + f4 = self.smooth_layer4_2(self.dwconv4_2(self._upsample_add(f4, f3))) + + return f1, f2, f3, f4 + + +class CTFPN(nn.Layer): + def __init__(self, in_channels, out_channel=128): + super(CTFPN, self).__init__() + self.out_channels = out_channel * 4 + + self.reduce_layer1 = Conv_BN_ReLU(in_channels[0], 128) + self.reduce_layer2 = Conv_BN_ReLU(in_channels[1], 128) + self.reduce_layer3 = Conv_BN_ReLU(in_channels[2], 128) + self.reduce_layer4 = Conv_BN_ReLU(in_channels[3], 128) + + self.fpem1 = FPEM(in_channels=(64, 128, 256, 512), out_channels=128) + self.fpem2 = FPEM(in_channels=(64, 128, 256, 512), out_channels=128) + + def _upsample(self, x, scale=1): + return F.upsample(x, scale_factor=scale, mode="bilinear") + + def forward(self, f): + # # reduce channel + f1 = self.reduce_layer1(f[0]) # N,64,160,160 --> N, 128, 160, 160 + f2 = self.reduce_layer2(f[1]) # N, 128, 80, 80 --> N, 128, 80, 80 + f3 = self.reduce_layer3(f[2]) # N, 256, 40, 40 --> N, 128, 40, 40 + f4 = self.reduce_layer4(f[3]) # N, 512, 20, 20 --> N, 128, 20, 20 + + # FPEM + f1_1, f2_1, f3_1, f4_1 = self.fpem1(f1, f2, f3, f4) + f1_2, f2_2, f3_2, f4_2 = self.fpem2(f1_1, f2_1, f3_1, f4_1) + + # FFM + f1 = f1_1 + f1_2 + f2 = f2_1 + f2_2 + f3 = f3_1 + f3_2 + f4 = f4_1 + f4_2 + + f2 = self._upsample(f2, scale=2) + f3 = self._upsample(f3, scale=4) + f4 = self._upsample(f4, scale=8) + ff = paddle.concat((f1, f2, f3, f4), 1) # N,512, 160,160 + return ff diff --git a/modules/onnx_ocr_module/src/ppocr/modeling/necks/db_fpn.py b/modules/onnx_ocr_module/src/ppocr/modeling/necks/db_fpn.py new file mode 100644 index 0000000..ea4ac99 --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/modeling/necks/db_fpn.py @@ -0,0 +1,492 @@ +# copyright (c) 2019 PaddlePaddle Authors. All Rights Reserve. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import paddle +from paddle import nn +import paddle.nn.functional as F +from paddle import ParamAttr +import os +import sys +from ppocr.modeling.necks.intracl import IntraCLBlock + +__dir__ = os.path.dirname(os.path.abspath(__file__)) +sys.path.append(__dir__) +sys.path.insert(0, os.path.abspath(os.path.join(__dir__, "../../.."))) + +from ppocr.modeling.backbones.det_mobilenet_v3 import SEModule + + +class DSConv(nn.Layer): + def __init__( + self, + in_channels, + out_channels, + kernel_size, + padding, + stride=1, + groups=None, + if_act=True, + act="relu", + **kwargs, + ): + super(DSConv, self).__init__() + if groups == None: + groups = in_channels + self.if_act = if_act + self.act = act + self.conv1 = nn.Conv2D( + in_channels=in_channels, + out_channels=in_channels, + kernel_size=kernel_size, + stride=stride, + padding=padding, + groups=groups, + bias_attr=False, + ) + + self.bn1 = nn.BatchNorm(num_channels=in_channels, act=None) + + self.conv2 = nn.Conv2D( + in_channels=in_channels, + out_channels=int(in_channels * 4), + kernel_size=1, + stride=1, + bias_attr=False, + ) + + self.bn2 = nn.BatchNorm(num_channels=int(in_channels * 4), act=None) + + self.conv3 = nn.Conv2D( + in_channels=int(in_channels * 4), + out_channels=out_channels, + kernel_size=1, + stride=1, + bias_attr=False, + ) + self._c = [in_channels, out_channels] + if in_channels != out_channels: + self.conv_end = nn.Conv2D( + in_channels=in_channels, + out_channels=out_channels, + kernel_size=1, + stride=1, + bias_attr=False, + ) + + def forward(self, inputs): + x = self.conv1(inputs) + x = self.bn1(x) + + x = self.conv2(x) + x = self.bn2(x) + if self.if_act: + if self.act == "relu": + x = F.relu(x) + elif self.act == "hardswish": + x = F.hardswish(x) + else: + print( + "The activation function({}) is selected incorrectly.".format( + self.act + ) + ) + exit() + + x = self.conv3(x) + if self._c[0] != self._c[1]: + x = x + self.conv_end(inputs) + return x + + +class DBFPN(nn.Layer): + def __init__(self, in_channels, out_channels, use_asf=False, **kwargs): + super(DBFPN, self).__init__() + self.out_channels = out_channels + self.use_asf = use_asf + weight_attr = paddle.nn.initializer.KaimingUniform() + + self.in2_conv = nn.Conv2D( + in_channels=in_channels[0], + out_channels=self.out_channels, + kernel_size=1, + weight_attr=ParamAttr(initializer=weight_attr), + bias_attr=False, + ) + self.in3_conv = nn.Conv2D( + in_channels=in_channels[1], + out_channels=self.out_channels, + kernel_size=1, + weight_attr=ParamAttr(initializer=weight_attr), + bias_attr=False, + ) + self.in4_conv = nn.Conv2D( + in_channels=in_channels[2], + out_channels=self.out_channels, + kernel_size=1, + weight_attr=ParamAttr(initializer=weight_attr), + bias_attr=False, + ) + self.in5_conv = nn.Conv2D( + in_channels=in_channels[3], + out_channels=self.out_channels, + kernel_size=1, + weight_attr=ParamAttr(initializer=weight_attr), + bias_attr=False, + ) + self.p5_conv = nn.Conv2D( + in_channels=self.out_channels, + out_channels=self.out_channels // 4, + kernel_size=3, + padding=1, + weight_attr=ParamAttr(initializer=weight_attr), + bias_attr=False, + ) + self.p4_conv = nn.Conv2D( + in_channels=self.out_channels, + out_channels=self.out_channels // 4, + kernel_size=3, + padding=1, + weight_attr=ParamAttr(initializer=weight_attr), + bias_attr=False, + ) + self.p3_conv = nn.Conv2D( + in_channels=self.out_channels, + out_channels=self.out_channels // 4, + kernel_size=3, + padding=1, + weight_attr=ParamAttr(initializer=weight_attr), + bias_attr=False, + ) + self.p2_conv = nn.Conv2D( + in_channels=self.out_channels, + out_channels=self.out_channels // 4, + kernel_size=3, + padding=1, + weight_attr=ParamAttr(initializer=weight_attr), + bias_attr=False, + ) + + if self.use_asf is True: + self.asf = ASFBlock(self.out_channels, self.out_channels // 4) + + def forward(self, x): + c2, c3, c4, c5 = x + + in5 = self.in5_conv(c5) + in4 = self.in4_conv(c4) + in3 = self.in3_conv(c3) + in2 = self.in2_conv(c2) + + out4 = in4 + F.upsample( + in5, scale_factor=2, mode="nearest", align_mode=1 + ) # 1/16 + out3 = in3 + F.upsample( + out4, scale_factor=2, mode="nearest", align_mode=1 + ) # 1/8 + out2 = in2 + F.upsample( + out3, scale_factor=2, mode="nearest", align_mode=1 + ) # 1/4 + + p5 = self.p5_conv(in5) + p4 = self.p4_conv(out4) + p3 = self.p3_conv(out3) + p2 = self.p2_conv(out2) + p5 = F.upsample(p5, scale_factor=8, mode="nearest", align_mode=1) + p4 = F.upsample(p4, scale_factor=4, mode="nearest", align_mode=1) + p3 = F.upsample(p3, scale_factor=2, mode="nearest", align_mode=1) + + fuse = paddle.concat([p5, p4, p3, p2], axis=1) + + if self.use_asf is True: + fuse = self.asf(fuse, [p5, p4, p3, p2]) + + return fuse + + +class RSELayer(nn.Layer): + def __init__(self, in_channels, out_channels, kernel_size, shortcut=True): + super(RSELayer, self).__init__() + weight_attr = paddle.nn.initializer.KaimingUniform() + self.out_channels = out_channels + self.in_conv = nn.Conv2D( + in_channels=in_channels, + out_channels=self.out_channels, + kernel_size=kernel_size, + padding=int(kernel_size // 2), + weight_attr=ParamAttr(initializer=weight_attr), + bias_attr=False, + ) + self.se_block = SEModule(self.out_channels) + self.shortcut = shortcut + + def forward(self, ins): + x = self.in_conv(ins) + if self.shortcut: + out = x + self.se_block(x) + else: + out = self.se_block(x) + return out + + +class RSEFPN(nn.Layer): + def __init__(self, in_channels, out_channels, shortcut=True, **kwargs): + super(RSEFPN, self).__init__() + self.out_channels = out_channels + self.ins_conv = nn.LayerList() + self.inp_conv = nn.LayerList() + self.intracl = False + if "intracl" in kwargs.keys() and kwargs["intracl"] is True: + self.intracl = kwargs["intracl"] + self.incl1 = IntraCLBlock(self.out_channels // 4, reduce_factor=2) + self.incl2 = IntraCLBlock(self.out_channels // 4, reduce_factor=2) + self.incl3 = IntraCLBlock(self.out_channels // 4, reduce_factor=2) + self.incl4 = IntraCLBlock(self.out_channels // 4, reduce_factor=2) + + for i in range(len(in_channels)): + self.ins_conv.append( + RSELayer(in_channels[i], out_channels, kernel_size=1, shortcut=shortcut) + ) + self.inp_conv.append( + RSELayer( + out_channels, out_channels // 4, kernel_size=3, shortcut=shortcut + ) + ) + + def forward(self, x): + c2, c3, c4, c5 = x + + in5 = self.ins_conv[3](c5) + in4 = self.ins_conv[2](c4) + in3 = self.ins_conv[1](c3) + in2 = self.ins_conv[0](c2) + + out4 = in4 + F.upsample( + in5, scale_factor=2, mode="nearest", align_mode=1 + ) # 1/16 + out3 = in3 + F.upsample( + out4, scale_factor=2, mode="nearest", align_mode=1 + ) # 1/8 + out2 = in2 + F.upsample( + out3, scale_factor=2, mode="nearest", align_mode=1 + ) # 1/4 + + p5 = self.inp_conv[3](in5) + p4 = self.inp_conv[2](out4) + p3 = self.inp_conv[1](out3) + p2 = self.inp_conv[0](out2) + + if self.intracl is True: + p5 = self.incl4(p5) + p4 = self.incl3(p4) + p3 = self.incl2(p3) + p2 = self.incl1(p2) + + p5 = F.upsample(p5, scale_factor=8, mode="nearest", align_mode=1) + p4 = F.upsample(p4, scale_factor=4, mode="nearest", align_mode=1) + p3 = F.upsample(p3, scale_factor=2, mode="nearest", align_mode=1) + + fuse = paddle.concat([p5, p4, p3, p2], axis=1) + return fuse + + +class LKPAN(nn.Layer): + def __init__(self, in_channels, out_channels, mode="large", **kwargs): + super(LKPAN, self).__init__() + self.out_channels = out_channels + weight_attr = paddle.nn.initializer.KaimingUniform() + + self.ins_conv = nn.LayerList() + self.inp_conv = nn.LayerList() + # pan head + self.pan_head_conv = nn.LayerList() + self.pan_lat_conv = nn.LayerList() + + if mode.lower() == "lite": + p_layer = DSConv + elif mode.lower() == "large": + p_layer = nn.Conv2D + else: + raise ValueError( + "mode can only be one of ['lite', 'large'], but received {}".format( + mode + ) + ) + + for i in range(len(in_channels)): + self.ins_conv.append( + nn.Conv2D( + in_channels=in_channels[i], + out_channels=self.out_channels, + kernel_size=1, + weight_attr=ParamAttr(initializer=weight_attr), + bias_attr=False, + ) + ) + + self.inp_conv.append( + p_layer( + in_channels=self.out_channels, + out_channels=self.out_channels // 4, + kernel_size=9, + padding=4, + weight_attr=ParamAttr(initializer=weight_attr), + bias_attr=False, + ) + ) + + if i > 0: + self.pan_head_conv.append( + nn.Conv2D( + in_channels=self.out_channels // 4, + out_channels=self.out_channels // 4, + kernel_size=3, + padding=1, + stride=2, + weight_attr=ParamAttr(initializer=weight_attr), + bias_attr=False, + ) + ) + self.pan_lat_conv.append( + p_layer( + in_channels=self.out_channels // 4, + out_channels=self.out_channels // 4, + kernel_size=9, + padding=4, + weight_attr=ParamAttr(initializer=weight_attr), + bias_attr=False, + ) + ) + + self.intracl = False + if "intracl" in kwargs.keys() and kwargs["intracl"] is True: + self.intracl = kwargs["intracl"] + self.incl1 = IntraCLBlock(self.out_channels // 4, reduce_factor=2) + self.incl2 = IntraCLBlock(self.out_channels // 4, reduce_factor=2) + self.incl3 = IntraCLBlock(self.out_channels // 4, reduce_factor=2) + self.incl4 = IntraCLBlock(self.out_channels // 4, reduce_factor=2) + + def forward(self, x): + c2, c3, c4, c5 = x + + in5 = self.ins_conv[3](c5) + in4 = self.ins_conv[2](c4) + in3 = self.ins_conv[1](c3) + in2 = self.ins_conv[0](c2) + + out4 = in4 + F.upsample( + in5, scale_factor=2, mode="nearest", align_mode=1 + ) # 1/16 + out3 = in3 + F.upsample( + out4, scale_factor=2, mode="nearest", align_mode=1 + ) # 1/8 + out2 = in2 + F.upsample( + out3, scale_factor=2, mode="nearest", align_mode=1 + ) # 1/4 + + f5 = self.inp_conv[3](in5) + f4 = self.inp_conv[2](out4) + f3 = self.inp_conv[1](out3) + f2 = self.inp_conv[0](out2) + + pan3 = f3 + self.pan_head_conv[0](f2) + pan4 = f4 + self.pan_head_conv[1](pan3) + pan5 = f5 + self.pan_head_conv[2](pan4) + + p2 = self.pan_lat_conv[0](f2) + p3 = self.pan_lat_conv[1](pan3) + p4 = self.pan_lat_conv[2](pan4) + p5 = self.pan_lat_conv[3](pan5) + + if self.intracl is True: + p5 = self.incl4(p5) + p4 = self.incl3(p4) + p3 = self.incl2(p3) + p2 = self.incl1(p2) + + p5 = F.upsample(p5, scale_factor=8, mode="nearest", align_mode=1) + p4 = F.upsample(p4, scale_factor=4, mode="nearest", align_mode=1) + p3 = F.upsample(p3, scale_factor=2, mode="nearest", align_mode=1) + + fuse = paddle.concat([p5, p4, p3, p2], axis=1) + return fuse + + +class ASFBlock(nn.Layer): + """ + This code is referred from: + https://github.com/MhLiao/DB/blob/master/decoders/feature_attention.py + """ + + def __init__(self, in_channels, inter_channels, out_features_num=4): + """ + Adaptive Scale Fusion (ASF) block of DBNet++ + Args: + in_channels: the number of channels in the input data + inter_channels: the number of middle channels + out_features_num: the number of fused stages + """ + super(ASFBlock, self).__init__() + weight_attr = paddle.nn.initializer.KaimingUniform() + self.in_channels = in_channels + self.inter_channels = inter_channels + self.out_features_num = out_features_num + self.conv = nn.Conv2D(in_channels, inter_channels, 3, padding=1) + + self.spatial_scale = nn.Sequential( + # Nx1xHxW + nn.Conv2D( + in_channels=1, + out_channels=1, + kernel_size=3, + bias_attr=False, + padding=1, + weight_attr=ParamAttr(initializer=weight_attr), + ), + nn.ReLU(), + nn.Conv2D( + in_channels=1, + out_channels=1, + kernel_size=1, + bias_attr=False, + weight_attr=ParamAttr(initializer=weight_attr), + ), + nn.Sigmoid(), + ) + + self.channel_scale = nn.Sequential( + nn.Conv2D( + in_channels=inter_channels, + out_channels=out_features_num, + kernel_size=1, + bias_attr=False, + weight_attr=ParamAttr(initializer=weight_attr), + ), + nn.Sigmoid(), + ) + + def forward(self, fuse_features, features_list): + fuse_features = self.conv(fuse_features) + spatial_x = paddle.mean(fuse_features, axis=1, keepdim=True) + attention_scores = self.spatial_scale(spatial_x) + fuse_features + attention_scores = self.channel_scale(attention_scores) + assert len(features_list) == self.out_features_num + + out_list = [] + for i in range(self.out_features_num): + out_list.append(attention_scores[:, i : i + 1] * features_list[i]) + return paddle.concat(out_list, axis=1) diff --git a/modules/onnx_ocr_module/src/ppocr/modeling/necks/east_fpn.py b/modules/onnx_ocr_module/src/ppocr/modeling/necks/east_fpn.py new file mode 100644 index 0000000..1b7f50d --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/modeling/necks/east_fpn.py @@ -0,0 +1,203 @@ +# copyright (c) 2019 PaddlePaddle Authors. All Rights Reserve. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import paddle +from paddle import nn +import paddle.nn.functional as F +from paddle import ParamAttr + + +class ConvBNLayer(nn.Layer): + def __init__( + self, + in_channels, + out_channels, + kernel_size, + stride, + padding, + groups=1, + if_act=True, + act=None, + name=None, + ): + super(ConvBNLayer, self).__init__() + self.if_act = if_act + self.act = act + self.conv = nn.Conv2D( + in_channels=in_channels, + out_channels=out_channels, + kernel_size=kernel_size, + stride=stride, + padding=padding, + groups=groups, + weight_attr=ParamAttr(name=name + "_weights"), + bias_attr=False, + ) + + self.bn = nn.BatchNorm( + num_channels=out_channels, + act=act, + param_attr=ParamAttr(name="bn_" + name + "_scale"), + bias_attr=ParamAttr(name="bn_" + name + "_offset"), + moving_mean_name="bn_" + name + "_mean", + moving_variance_name="bn_" + name + "_variance", + ) + + def forward(self, x): + x = self.conv(x) + x = self.bn(x) + return x + + +class DeConvBNLayer(nn.Layer): + def __init__( + self, + in_channels, + out_channels, + kernel_size, + stride, + padding, + groups=1, + if_act=True, + act=None, + name=None, + ): + super(DeConvBNLayer, self).__init__() + self.if_act = if_act + self.act = act + self.deconv = nn.Conv2DTranspose( + in_channels=in_channels, + out_channels=out_channels, + kernel_size=kernel_size, + stride=stride, + padding=padding, + groups=groups, + weight_attr=ParamAttr(name=name + "_weights"), + bias_attr=False, + ) + self.bn = nn.BatchNorm( + num_channels=out_channels, + act=act, + param_attr=ParamAttr(name="bn_" + name + "_scale"), + bias_attr=ParamAttr(name="bn_" + name + "_offset"), + moving_mean_name="bn_" + name + "_mean", + moving_variance_name="bn_" + name + "_variance", + ) + + def forward(self, x): + x = self.deconv(x) + x = self.bn(x) + return x + + +class EASTFPN(nn.Layer): + def __init__(self, in_channels, model_name, **kwargs): + super(EASTFPN, self).__init__() + self.model_name = model_name + if self.model_name == "large": + self.out_channels = 128 + else: + self.out_channels = 64 + self.in_channels = in_channels[::-1] + self.h1_conv = ConvBNLayer( + in_channels=self.out_channels + self.in_channels[1], + out_channels=self.out_channels, + kernel_size=3, + stride=1, + padding=1, + if_act=True, + act="relu", + name="unet_h_1", + ) + self.h2_conv = ConvBNLayer( + in_channels=self.out_channels + self.in_channels[2], + out_channels=self.out_channels, + kernel_size=3, + stride=1, + padding=1, + if_act=True, + act="relu", + name="unet_h_2", + ) + self.h3_conv = ConvBNLayer( + in_channels=self.out_channels + self.in_channels[3], + out_channels=self.out_channels, + kernel_size=3, + stride=1, + padding=1, + if_act=True, + act="relu", + name="unet_h_3", + ) + self.g0_deconv = DeConvBNLayer( + in_channels=self.in_channels[0], + out_channels=self.out_channels, + kernel_size=4, + stride=2, + padding=1, + if_act=True, + act="relu", + name="unet_g_0", + ) + self.g1_deconv = DeConvBNLayer( + in_channels=self.out_channels, + out_channels=self.out_channels, + kernel_size=4, + stride=2, + padding=1, + if_act=True, + act="relu", + name="unet_g_1", + ) + self.g2_deconv = DeConvBNLayer( + in_channels=self.out_channels, + out_channels=self.out_channels, + kernel_size=4, + stride=2, + padding=1, + if_act=True, + act="relu", + name="unet_g_2", + ) + self.g3_conv = ConvBNLayer( + in_channels=self.out_channels, + out_channels=self.out_channels, + kernel_size=3, + stride=1, + padding=1, + if_act=True, + act="relu", + name="unet_g_3", + ) + + def forward(self, x): + f = x[::-1] + + h = f[0] + g = self.g0_deconv(h) + h = paddle.concat([g, f[1]], axis=1) + h = self.h1_conv(h) + g = self.g1_deconv(h) + h = paddle.concat([g, f[2]], axis=1) + h = self.h2_conv(h) + g = self.g2_deconv(h) + h = paddle.concat([g, f[3]], axis=1) + h = self.h3_conv(h) + g = self.g3_conv(h) + + return g diff --git a/modules/onnx_ocr_module/src/ppocr/modeling/necks/fce_fpn.py b/modules/onnx_ocr_module/src/ppocr/modeling/necks/fce_fpn.py new file mode 100644 index 0000000..a456fd1 --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/modeling/necks/fce_fpn.py @@ -0,0 +1,304 @@ +# copyright (c) 2022 PaddlePaddle Authors. All Rights Reserve. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +""" +This code is refer from: +https://github.com/PaddlePaddle/PaddleDetection/blob/release/2.3/ppdet/modeling/necks/fpn.py +""" + +import paddle.nn as nn +import paddle.nn.functional as F +from paddle import ParamAttr +from paddle.nn.initializer import XavierUniform +from paddle.nn.initializer import Normal +from paddle.regularizer import L2Decay + +__all__ = ["FCEFPN"] + + +class ConvNormLayer(nn.Layer): + def __init__( + self, + ch_in, + ch_out, + filter_size, + stride, + groups=1, + norm_type="bn", + norm_decay=0.0, + norm_groups=32, + lr_scale=1.0, + freeze_norm=False, + initializer=Normal(mean=0.0, std=0.01), + ): + super(ConvNormLayer, self).__init__() + assert norm_type in ["bn", "sync_bn", "gn"] + + bias_attr = False + + self.conv = nn.Conv2D( + in_channels=ch_in, + out_channels=ch_out, + kernel_size=filter_size, + stride=stride, + padding=(filter_size - 1) // 2, + groups=groups, + weight_attr=ParamAttr(initializer=initializer, learning_rate=1.0), + bias_attr=bias_attr, + ) + + norm_lr = 0.0 if freeze_norm else 1.0 + param_attr = ParamAttr( + learning_rate=norm_lr, + regularizer=L2Decay(norm_decay) if norm_decay is not None else None, + ) + bias_attr = ParamAttr( + learning_rate=norm_lr, + regularizer=L2Decay(norm_decay) if norm_decay is not None else None, + ) + if norm_type == "bn": + self.norm = nn.BatchNorm2D( + ch_out, weight_attr=param_attr, bias_attr=bias_attr + ) + elif norm_type == "sync_bn": + self.norm = nn.SyncBatchNorm( + ch_out, weight_attr=param_attr, bias_attr=bias_attr + ) + elif norm_type == "gn": + self.norm = nn.GroupNorm( + num_groups=norm_groups, + num_channels=ch_out, + weight_attr=param_attr, + bias_attr=bias_attr, + ) + + def forward(self, inputs): + out = self.conv(inputs) + out = self.norm(out) + return out + + +class FCEFPN(nn.Layer): + """ + Feature Pyramid Network, see https://arxiv.org/abs/1612.03144 + Args: + in_channels (list[int]): input channels of each level which can be + derived from the output shape of backbone by from_config + out_channels (list[int]): output channel of each level + spatial_scales (list[float]): the spatial scales between input feature + maps and original input image which can be derived from the output + shape of backbone by from_config + has_extra_convs (bool): whether to add extra conv to the last level. + default False + extra_stage (int): the number of extra stages added to the last level. + default 1 + use_c5 (bool): Whether to use c5 as the input of extra stage, + otherwise p5 is used. default True + norm_type (string|None): The normalization type in FPN module. If + norm_type is None, norm will not be used after conv and if + norm_type is string, bn, gn, sync_bn are available. default None + norm_decay (float): weight decay for normalization layer weights. + default 0. + freeze_norm (bool): whether to freeze normalization layer. + default False + relu_before_extra_convs (bool): whether to add relu before extra convs. + default False + + """ + + def __init__( + self, + in_channels, + out_channels, + spatial_scales=[0.25, 0.125, 0.0625, 0.03125], + has_extra_convs=False, + extra_stage=1, + use_c5=True, + norm_type=None, + norm_decay=0.0, + freeze_norm=False, + relu_before_extra_convs=True, + ): + super(FCEFPN, self).__init__() + self.out_channels = out_channels + for s in range(extra_stage): + spatial_scales = spatial_scales + [spatial_scales[-1] / 2.0] + self.spatial_scales = spatial_scales + self.has_extra_convs = has_extra_convs + self.extra_stage = extra_stage + self.use_c5 = use_c5 + self.relu_before_extra_convs = relu_before_extra_convs + self.norm_type = norm_type + self.norm_decay = norm_decay + self.freeze_norm = freeze_norm + + self.lateral_convs = [] + self.fpn_convs = [] + fan = out_channels * 3 * 3 + + # stage index 0,1,2,3 stands for res2,res3,res4,res5 on ResNet Backbone + # 0 <= st_stage < ed_stage <= 3 + st_stage = 4 - len(in_channels) + ed_stage = st_stage + len(in_channels) - 1 + for i in range(st_stage, ed_stage + 1): + if i == 3: + lateral_name = "fpn_inner_res5_sum" + else: + lateral_name = "fpn_inner_res{}_sum_lateral".format(i + 2) + in_c = in_channels[i - st_stage] + if self.norm_type is not None: + lateral = self.add_sublayer( + lateral_name, + ConvNormLayer( + ch_in=in_c, + ch_out=out_channels, + filter_size=1, + stride=1, + norm_type=self.norm_type, + norm_decay=self.norm_decay, + freeze_norm=self.freeze_norm, + initializer=XavierUniform(fan_out=in_c), + ), + ) + else: + lateral = self.add_sublayer( + lateral_name, + nn.Conv2D( + in_channels=in_c, + out_channels=out_channels, + kernel_size=1, + weight_attr=ParamAttr(initializer=XavierUniform(fan_out=in_c)), + ), + ) + self.lateral_convs.append(lateral) + + for i in range(st_stage, ed_stage + 1): + fpn_name = "fpn_res{}_sum".format(i + 2) + if self.norm_type is not None: + fpn_conv = self.add_sublayer( + fpn_name, + ConvNormLayer( + ch_in=out_channels, + ch_out=out_channels, + filter_size=3, + stride=1, + norm_type=self.norm_type, + norm_decay=self.norm_decay, + freeze_norm=self.freeze_norm, + initializer=XavierUniform(fan_out=fan), + ), + ) + else: + fpn_conv = self.add_sublayer( + fpn_name, + nn.Conv2D( + in_channels=out_channels, + out_channels=out_channels, + kernel_size=3, + padding=1, + weight_attr=ParamAttr(initializer=XavierUniform(fan_out=fan)), + ), + ) + self.fpn_convs.append(fpn_conv) + + # add extra conv levels for RetinaNet(use_c5)/FCOS(use_p5) + if self.has_extra_convs: + for i in range(self.extra_stage): + lvl = ed_stage + 1 + i + if i == 0 and self.use_c5: + in_c = in_channels[-1] + else: + in_c = out_channels + extra_fpn_name = "fpn_{}".format(lvl + 2) + if self.norm_type is not None: + extra_fpn_conv = self.add_sublayer( + extra_fpn_name, + ConvNormLayer( + ch_in=in_c, + ch_out=out_channels, + filter_size=3, + stride=2, + norm_type=self.norm_type, + norm_decay=self.norm_decay, + freeze_norm=self.freeze_norm, + initializer=XavierUniform(fan_out=fan), + ), + ) + else: + extra_fpn_conv = self.add_sublayer( + extra_fpn_name, + nn.Conv2D( + in_channels=in_c, + out_channels=out_channels, + kernel_size=3, + stride=2, + padding=1, + weight_attr=ParamAttr( + initializer=XavierUniform(fan_out=fan) + ), + ), + ) + self.fpn_convs.append(extra_fpn_conv) + + @classmethod + def from_config(cls, cfg, input_shape): + return { + "in_channels": [i.channels for i in input_shape], + "spatial_scales": [1.0 / i.stride for i in input_shape], + } + + def forward(self, body_feats): + laterals = [] + num_levels = len(body_feats) + + for i in range(num_levels): + laterals.append(self.lateral_convs[i](body_feats[i])) + + for i in range(1, num_levels): + lvl = num_levels - i + upsample = F.interpolate( + laterals[lvl], + scale_factor=2.0, + mode="nearest", + ) + laterals[lvl - 1] += upsample + + fpn_output = [] + for lvl in range(num_levels): + fpn_output.append(self.fpn_convs[lvl](laterals[lvl])) + + if self.extra_stage > 0: + # use max pool to get more levels on top of outputs (Faster R-CNN, Mask R-CNN) + if not self.has_extra_convs: + assert ( + self.extra_stage == 1 + ), "extra_stage should be 1 if FPN has not extra convs" + fpn_output.append(F.max_pool2d(fpn_output[-1], 1, stride=2)) + # add extra conv levels for RetinaNet(use_c5)/FCOS(use_p5) + else: + if self.use_c5: + extra_source = body_feats[-1] + else: + extra_source = fpn_output[-1] + fpn_output.append(self.fpn_convs[num_levels](extra_source)) + + for i in range(1, self.extra_stage): + if self.relu_before_extra_convs: + fpn_output.append( + self.fpn_convs[num_levels + i](F.relu(fpn_output[-1])) + ) + else: + fpn_output.append( + self.fpn_convs[num_levels + i](fpn_output[-1]) + ) + return fpn_output diff --git a/modules/onnx_ocr_module/src/ppocr/modeling/necks/fpn.py b/modules/onnx_ocr_module/src/ppocr/modeling/necks/fpn.py new file mode 100644 index 0000000..ea5253c --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/modeling/necks/fpn.py @@ -0,0 +1,149 @@ +# copyright (c) 2021 PaddlePaddle Authors. All Rights Reserve. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +""" +This code is refer from: +https://github.com/whai362/PSENet/blob/python3/models/neck/fpn.py +""" + +import paddle.nn as nn +import paddle +import math +import paddle.nn.functional as F + + +class Conv_BN_ReLU(nn.Layer): + def __init__(self, in_planes, out_planes, kernel_size=1, stride=1, padding=0): + super(Conv_BN_ReLU, self).__init__() + self.conv = nn.Conv2D( + in_planes, + out_planes, + kernel_size=kernel_size, + stride=stride, + padding=padding, + bias_attr=False, + ) + self.bn = nn.BatchNorm2D(out_planes, momentum=0.1) + self.relu = nn.ReLU() + + for m in self.sublayers(): + if isinstance(m, nn.Conv2D): + n = m._kernel_size[0] * m._kernel_size[1] * m._out_channels + m.weight = paddle.create_parameter( + shape=m.weight.shape, + dtype="float32", + default_initializer=paddle.nn.initializer.Normal( + 0, math.sqrt(2.0 / n) + ), + ) + elif isinstance(m, nn.BatchNorm2D): + m.weight = paddle.create_parameter( + shape=m.weight.shape, + dtype="float32", + default_initializer=paddle.nn.initializer.Constant(1.0), + ) + m.bias = paddle.create_parameter( + shape=m.bias.shape, + dtype="float32", + default_initializer=paddle.nn.initializer.Constant(0.0), + ) + + def forward(self, x): + return self.relu(self.bn(self.conv(x))) + + +class FPN(nn.Layer): + def __init__(self, in_channels, out_channels): + super(FPN, self).__init__() + + # Top layer + self.toplayer_ = Conv_BN_ReLU( + in_channels[3], out_channels, kernel_size=1, stride=1, padding=0 + ) + # Lateral layers + self.latlayer1_ = Conv_BN_ReLU( + in_channels[2], out_channels, kernel_size=1, stride=1, padding=0 + ) + + self.latlayer2_ = Conv_BN_ReLU( + in_channels[1], out_channels, kernel_size=1, stride=1, padding=0 + ) + + self.latlayer3_ = Conv_BN_ReLU( + in_channels[0], out_channels, kernel_size=1, stride=1, padding=0 + ) + + # Smooth layers + self.smooth1_ = Conv_BN_ReLU( + out_channels, out_channels, kernel_size=3, stride=1, padding=1 + ) + + self.smooth2_ = Conv_BN_ReLU( + out_channels, out_channels, kernel_size=3, stride=1, padding=1 + ) + + self.smooth3_ = Conv_BN_ReLU( + out_channels, out_channels, kernel_size=3, stride=1, padding=1 + ) + + self.out_channels = out_channels * 4 + for m in self.sublayers(): + if isinstance(m, nn.Conv2D): + n = m._kernel_size[0] * m._kernel_size[1] * m._out_channels + m.weight = paddle.create_parameter( + shape=m.weight.shape, + dtype="float32", + default_initializer=paddle.nn.initializer.Normal( + 0, math.sqrt(2.0 / n) + ), + ) + elif isinstance(m, nn.BatchNorm2D): + m.weight = paddle.create_parameter( + shape=m.weight.shape, + dtype="float32", + default_initializer=paddle.nn.initializer.Constant(1.0), + ) + m.bias = paddle.create_parameter( + shape=m.bias.shape, + dtype="float32", + default_initializer=paddle.nn.initializer.Constant(0.0), + ) + + def _upsample(self, x, scale=1): + return F.upsample(x, scale_factor=scale, mode="bilinear") + + def _upsample_add(self, x, y, scale=1): + return F.upsample(x, scale_factor=scale, mode="bilinear") + y + + def forward(self, x): + f2, f3, f4, f5 = x + p5 = self.toplayer_(f5) + + f4 = self.latlayer1_(f4) + p4 = self._upsample_add(p5, f4, 2) + p4 = self.smooth1_(p4) + + f3 = self.latlayer2_(f3) + p3 = self._upsample_add(p4, f3, 2) + p3 = self.smooth2_(p3) + + f2 = self.latlayer3_(f2) + p2 = self._upsample_add(p3, f2, 2) + p2 = self.smooth3_(p2) + + p3 = self._upsample(p3, 2) + p4 = self._upsample(p4, 4) + p5 = self._upsample(p5, 8) + + fuse = paddle.concat([p2, p3, p4, p5], axis=1) + return fuse diff --git a/modules/onnx_ocr_module/src/ppocr/modeling/necks/fpn_unet.py b/modules/onnx_ocr_module/src/ppocr/modeling/necks/fpn_unet.py new file mode 100644 index 0000000..e560cdb --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/modeling/necks/fpn_unet.py @@ -0,0 +1,103 @@ +# copyright (c) 2022 PaddlePaddle Authors. All Rights Reserve. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +""" +This code is refer from: +https://github.com/open-mmlab/mmocr/blob/main/mmocr/models/textdet/necks/fpn_unet.py +""" + +import paddle +import paddle.nn as nn +import paddle.nn.functional as F + + +class UpBlock(nn.Layer): + def __init__(self, in_channels, out_channels): + super().__init__() + + assert isinstance(in_channels, int) + assert isinstance(out_channels, int) + + self.conv1x1 = nn.Conv2D( + in_channels, in_channels, kernel_size=1, stride=1, padding=0 + ) + self.conv3x3 = nn.Conv2D( + in_channels, out_channels, kernel_size=3, stride=1, padding=1 + ) + self.deconv = nn.Conv2DTranspose( + out_channels, out_channels, kernel_size=4, stride=2, padding=1 + ) + + def forward(self, x): + x = F.relu(self.conv1x1(x)) + x = F.relu(self.conv3x3(x)) + x = self.deconv(x) + return x + + +class FPN_UNet(nn.Layer): + def __init__(self, in_channels, out_channels): + super().__init__() + + assert len(in_channels) == 4 + assert isinstance(out_channels, int) + self.out_channels = out_channels + + blocks_out_channels = [out_channels] + [ + min(out_channels * 2**i, 256) for i in range(4) + ] + blocks_in_channels = ( + [blocks_out_channels[1]] + + [in_channels[i] + blocks_out_channels[i + 2] for i in range(3)] + + [in_channels[3]] + ) + + self.up4 = nn.Conv2DTranspose( + blocks_in_channels[4], + blocks_out_channels[4], + kernel_size=4, + stride=2, + padding=1, + ) + self.up_block3 = UpBlock(blocks_in_channels[3], blocks_out_channels[3]) + self.up_block2 = UpBlock(blocks_in_channels[2], blocks_out_channels[2]) + self.up_block1 = UpBlock(blocks_in_channels[1], blocks_out_channels[1]) + self.up_block0 = UpBlock(blocks_in_channels[0], blocks_out_channels[0]) + + def forward(self, x): + """ + Args: + x (list[Tensor] | tuple[Tensor]): A list of four tensors of shape + :math:`(N, C_i, H_i, W_i)`, representing C2, C3, C4, C5 + features respectively. :math:`C_i` should matches the number in + ``in_channels``. + + Returns: + Tensor: Shape :math:`(N, C, H, W)` where :math:`H=4H_0` and + :math:`W=4W_0`. + """ + c2, c3, c4, c5 = x + + x = F.relu(self.up4(c5)) + + x = paddle.concat([x, c4], axis=1) + x = F.relu(self.up_block3(x)) + + x = paddle.concat([x, c3], axis=1) + x = F.relu(self.up_block2(x)) + + x = paddle.concat([x, c2], axis=1) + x = F.relu(self.up_block1(x)) + + x = self.up_block0(x) + return x diff --git a/modules/onnx_ocr_module/src/ppocr/modeling/necks/intracl.py b/modules/onnx_ocr_module/src/ppocr/modeling/necks/intracl.py new file mode 100644 index 0000000..2c4809c --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/modeling/necks/intracl.py @@ -0,0 +1,121 @@ +import paddle +from paddle import nn + +# refer from: https://github.com/ViTAE-Transformer/I3CL/blob/736c80237f66d352d488e83b05f3e33c55201317/mmdet/models/detectors/intra_cl_module.py + + +class IntraCLBlock(nn.Layer): + def __init__(self, in_channels=96, reduce_factor=4): + super(IntraCLBlock, self).__init__() + self.channels = in_channels + self.rf = reduce_factor + weight_attr = paddle.nn.initializer.KaimingUniform() + self.conv1x1_reduce_channel = nn.Conv2D( + self.channels, self.channels // self.rf, kernel_size=1, stride=1, padding=0 + ) + self.conv1x1_return_channel = nn.Conv2D( + self.channels // self.rf, self.channels, kernel_size=1, stride=1, padding=0 + ) + + self.v_layer_7x1 = nn.Conv2D( + self.channels // self.rf, + self.channels // self.rf, + kernel_size=(7, 1), + stride=(1, 1), + padding=(3, 0), + ) + self.v_layer_5x1 = nn.Conv2D( + self.channels // self.rf, + self.channels // self.rf, + kernel_size=(5, 1), + stride=(1, 1), + padding=(2, 0), + ) + self.v_layer_3x1 = nn.Conv2D( + self.channels // self.rf, + self.channels // self.rf, + kernel_size=(3, 1), + stride=(1, 1), + padding=(1, 0), + ) + + self.q_layer_1x7 = nn.Conv2D( + self.channels // self.rf, + self.channels // self.rf, + kernel_size=(1, 7), + stride=(1, 1), + padding=(0, 3), + ) + self.q_layer_1x5 = nn.Conv2D( + self.channels // self.rf, + self.channels // self.rf, + kernel_size=(1, 5), + stride=(1, 1), + padding=(0, 2), + ) + self.q_layer_1x3 = nn.Conv2D( + self.channels // self.rf, + self.channels // self.rf, + kernel_size=(1, 3), + stride=(1, 1), + padding=(0, 1), + ) + + # base + self.c_layer_7x7 = nn.Conv2D( + self.channels // self.rf, + self.channels // self.rf, + kernel_size=(7, 7), + stride=(1, 1), + padding=(3, 3), + ) + self.c_layer_5x5 = nn.Conv2D( + self.channels // self.rf, + self.channels // self.rf, + kernel_size=(5, 5), + stride=(1, 1), + padding=(2, 2), + ) + self.c_layer_3x3 = nn.Conv2D( + self.channels // self.rf, + self.channels // self.rf, + kernel_size=(3, 3), + stride=(1, 1), + padding=(1, 1), + ) + + self.bn = nn.BatchNorm2D(self.channels) + self.relu = nn.ReLU() + + def forward(self, x): + x_new = self.conv1x1_reduce_channel(x) + + x_7_c = self.c_layer_7x7(x_new) + x_7_v = self.v_layer_7x1(x_new) + x_7_q = self.q_layer_1x7(x_new) + x_7 = x_7_c + x_7_v + x_7_q + + x_5_c = self.c_layer_5x5(x_7) + x_5_v = self.v_layer_5x1(x_7) + x_5_q = self.q_layer_1x5(x_7) + x_5 = x_5_c + x_5_v + x_5_q + + x_3_c = self.c_layer_3x3(x_5) + x_3_v = self.v_layer_3x1(x_5) + x_3_q = self.q_layer_1x3(x_5) + x_3 = x_3_c + x_3_v + x_3_q + + x_relation = self.conv1x1_return_channel(x_3) + + x_relation = self.bn(x_relation) + x_relation = self.relu(x_relation) + + return x + x_relation + + +def build_intraclblock_list(num_block): + IntraCLBlock_list = nn.LayerList() + for i in range(num_block): + IntraCLBlock_list.append(IntraCLBlock()) + + return IntraCLBlock_list diff --git a/modules/onnx_ocr_module/src/ppocr/modeling/necks/pg_fpn.py b/modules/onnx_ocr_module/src/ppocr/modeling/necks/pg_fpn.py new file mode 100644 index 0000000..cae4b22 --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/modeling/necks/pg_fpn.py @@ -0,0 +1,345 @@ +# copyright (c) 2021 PaddlePaddle Authors. All Rights Reserve. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import paddle +from paddle import nn +import paddle.nn.functional as F +from paddle import ParamAttr + + +class ConvBNLayer(nn.Layer): + def __init__( + self, + in_channels, + out_channels, + kernel_size, + stride=1, + groups=1, + is_vd_mode=False, + act=None, + name=None, + ): + super(ConvBNLayer, self).__init__() + + self.is_vd_mode = is_vd_mode + self._pool2d_avg = nn.AvgPool2D( + kernel_size=2, stride=2, padding=0, ceil_mode=True + ) + self._conv = nn.Conv2D( + in_channels=in_channels, + out_channels=out_channels, + kernel_size=kernel_size, + stride=stride, + padding=(kernel_size - 1) // 2, + groups=groups, + weight_attr=ParamAttr(name=name + "_weights"), + bias_attr=False, + ) + if name == "conv1": + bn_name = "bn_" + name + else: + bn_name = "bn" + name[3:] + self._batch_norm = nn.BatchNorm( + out_channels, + act=act, + param_attr=ParamAttr(name=bn_name + "_scale"), + bias_attr=ParamAttr(bn_name + "_offset"), + moving_mean_name=bn_name + "_mean", + moving_variance_name=bn_name + "_variance", + use_global_stats=False, + ) + + def forward(self, inputs): + y = self._conv(inputs) + y = self._batch_norm(y) + return y + + +class DeConvBNLayer(nn.Layer): + def __init__( + self, + in_channels, + out_channels, + kernel_size=4, + stride=2, + padding=1, + groups=1, + if_act=True, + act=None, + name=None, + ): + super(DeConvBNLayer, self).__init__() + + self.if_act = if_act + self.act = act + self.deconv = nn.Conv2DTranspose( + in_channels=in_channels, + out_channels=out_channels, + kernel_size=kernel_size, + stride=stride, + padding=padding, + groups=groups, + weight_attr=ParamAttr(name=name + "_weights"), + bias_attr=False, + ) + self.bn = nn.BatchNorm( + num_channels=out_channels, + act=act, + param_attr=ParamAttr(name="bn_" + name + "_scale"), + bias_attr=ParamAttr(name="bn_" + name + "_offset"), + moving_mean_name="bn_" + name + "_mean", + moving_variance_name="bn_" + name + "_variance", + use_global_stats=False, + ) + + def forward(self, x): + x = self.deconv(x) + x = self.bn(x) + return x + + +class PGFPN(nn.Layer): + def __init__(self, in_channels, **kwargs): + super(PGFPN, self).__init__() + num_inputs = [2048, 2048, 1024, 512, 256] + num_outputs = [256, 256, 192, 192, 128] + self.out_channels = 128 + self.conv_bn_layer_1 = ConvBNLayer( + in_channels=3, + out_channels=32, + kernel_size=3, + stride=1, + act=None, + name="FPN_d1", + ) + self.conv_bn_layer_2 = ConvBNLayer( + in_channels=64, + out_channels=64, + kernel_size=3, + stride=1, + act=None, + name="FPN_d2", + ) + self.conv_bn_layer_3 = ConvBNLayer( + in_channels=256, + out_channels=128, + kernel_size=3, + stride=1, + act=None, + name="FPN_d3", + ) + self.conv_bn_layer_4 = ConvBNLayer( + in_channels=32, + out_channels=64, + kernel_size=3, + stride=2, + act=None, + name="FPN_d4", + ) + self.conv_bn_layer_5 = ConvBNLayer( + in_channels=64, + out_channels=64, + kernel_size=3, + stride=1, + act="relu", + name="FPN_d5", + ) + self.conv_bn_layer_6 = ConvBNLayer( + in_channels=64, + out_channels=128, + kernel_size=3, + stride=2, + act=None, + name="FPN_d6", + ) + self.conv_bn_layer_7 = ConvBNLayer( + in_channels=128, + out_channels=128, + kernel_size=3, + stride=1, + act="relu", + name="FPN_d7", + ) + self.conv_bn_layer_8 = ConvBNLayer( + in_channels=128, + out_channels=128, + kernel_size=1, + stride=1, + act=None, + name="FPN_d8", + ) + + self.conv_h0 = ConvBNLayer( + in_channels=num_inputs[0], + out_channels=num_outputs[0], + kernel_size=1, + stride=1, + act=None, + name="conv_h{}".format(0), + ) + self.conv_h1 = ConvBNLayer( + in_channels=num_inputs[1], + out_channels=num_outputs[1], + kernel_size=1, + stride=1, + act=None, + name="conv_h{}".format(1), + ) + self.conv_h2 = ConvBNLayer( + in_channels=num_inputs[2], + out_channels=num_outputs[2], + kernel_size=1, + stride=1, + act=None, + name="conv_h{}".format(2), + ) + self.conv_h3 = ConvBNLayer( + in_channels=num_inputs[3], + out_channels=num_outputs[3], + kernel_size=1, + stride=1, + act=None, + name="conv_h{}".format(3), + ) + self.conv_h4 = ConvBNLayer( + in_channels=num_inputs[4], + out_channels=num_outputs[4], + kernel_size=1, + stride=1, + act=None, + name="conv_h{}".format(4), + ) + + self.dconv0 = DeConvBNLayer( + in_channels=num_outputs[0], + out_channels=num_outputs[0 + 1], + name="dconv_{}".format(0), + ) + self.dconv1 = DeConvBNLayer( + in_channels=num_outputs[1], + out_channels=num_outputs[1 + 1], + act=None, + name="dconv_{}".format(1), + ) + self.dconv2 = DeConvBNLayer( + in_channels=num_outputs[2], + out_channels=num_outputs[2 + 1], + act=None, + name="dconv_{}".format(2), + ) + self.dconv3 = DeConvBNLayer( + in_channels=num_outputs[3], + out_channels=num_outputs[3 + 1], + act=None, + name="dconv_{}".format(3), + ) + self.conv_g1 = ConvBNLayer( + in_channels=num_outputs[1], + out_channels=num_outputs[1], + kernel_size=3, + stride=1, + act="relu", + name="conv_g{}".format(1), + ) + self.conv_g2 = ConvBNLayer( + in_channels=num_outputs[2], + out_channels=num_outputs[2], + kernel_size=3, + stride=1, + act="relu", + name="conv_g{}".format(2), + ) + self.conv_g3 = ConvBNLayer( + in_channels=num_outputs[3], + out_channels=num_outputs[3], + kernel_size=3, + stride=1, + act="relu", + name="conv_g{}".format(3), + ) + self.conv_g4 = ConvBNLayer( + in_channels=num_outputs[4], + out_channels=num_outputs[4], + kernel_size=3, + stride=1, + act="relu", + name="conv_g{}".format(4), + ) + self.convf = ConvBNLayer( + in_channels=num_outputs[4], + out_channels=num_outputs[4], + kernel_size=1, + stride=1, + act=None, + name="conv_f{}".format(4), + ) + + def forward(self, x): + c0, c1, c2, c3, c4, c5, c6 = x + # FPN_Down_Fusion + f = [c0, c1, c2] + g = [None, None, None] + h = [None, None, None] + h[0] = self.conv_bn_layer_1(f[0]) + h[1] = self.conv_bn_layer_2(f[1]) + h[2] = self.conv_bn_layer_3(f[2]) + + g[0] = self.conv_bn_layer_4(h[0]) + g[1] = paddle.add(g[0], h[1]) + g[1] = F.relu(g[1]) + g[1] = self.conv_bn_layer_5(g[1]) + g[1] = self.conv_bn_layer_6(g[1]) + + g[2] = paddle.add(g[1], h[2]) + g[2] = F.relu(g[2]) + g[2] = self.conv_bn_layer_7(g[2]) + f_down = self.conv_bn_layer_8(g[2]) + + # FPN UP Fusion + f1 = [c6, c5, c4, c3, c2] + g = [None, None, None, None, None] + h = [None, None, None, None, None] + h[0] = self.conv_h0(f1[0]) + h[1] = self.conv_h1(f1[1]) + h[2] = self.conv_h2(f1[2]) + h[3] = self.conv_h3(f1[3]) + h[4] = self.conv_h4(f1[4]) + + g[0] = self.dconv0(h[0]) + g[1] = paddle.add(g[0], h[1]) + g[1] = F.relu(g[1]) + g[1] = self.conv_g1(g[1]) + g[1] = self.dconv1(g[1]) + + g[2] = paddle.add(g[1], h[2]) + g[2] = F.relu(g[2]) + g[2] = self.conv_g2(g[2]) + g[2] = self.dconv2(g[2]) + + g[3] = paddle.add(g[2], h[3]) + g[3] = F.relu(g[3]) + g[3] = self.conv_g3(g[3]) + g[3] = self.dconv3(g[3]) + + g[4] = paddle.add(x=g[3], y=h[4]) + g[4] = F.relu(g[4]) + g[4] = self.conv_g4(g[4]) + f_up = self.convf(g[4]) + f_common = paddle.add(f_down, f_up) + f_common = F.relu(f_common) + return f_common diff --git a/modules/onnx_ocr_module/src/ppocr/modeling/necks/pren_fpn.py b/modules/onnx_ocr_module/src/ppocr/modeling/necks/pren_fpn.py new file mode 100644 index 0000000..29c98e9 --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/modeling/necks/pren_fpn.py @@ -0,0 +1,177 @@ +# copyright (c) 2022 PaddlePaddle Authors. All Rights Reserve. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +""" +Code is refer from: +https://github.com/RuijieJ/pren/blob/main/Nets/Aggregation.py +""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import paddle +from paddle import nn +import paddle.nn.functional as F + + +class PoolAggregate(nn.Layer): + def __init__(self, n_r, d_in, d_middle=None, d_out=None): + super(PoolAggregate, self).__init__() + if not d_middle: + d_middle = d_in + if not d_out: + d_out = d_in + + self.d_in = d_in + self.d_middle = d_middle + self.d_out = d_out + self.act = nn.Swish() + + self.n_r = n_r + self.aggs = self._build_aggs() + + def _build_aggs(self): + aggs = [] + for i in range(self.n_r): + aggs.append( + self.add_sublayer( + "{}".format(i), + nn.Sequential( + ( + "conv1", + nn.Conv2D( + self.d_in, self.d_middle, 3, 2, 1, bias_attr=False + ), + ), + ("bn1", nn.BatchNorm(self.d_middle)), + ("act", self.act), + ( + "conv2", + nn.Conv2D( + self.d_middle, self.d_out, 3, 2, 1, bias_attr=False + ), + ), + ("bn2", nn.BatchNorm(self.d_out)), + ), + ) + ) + return aggs + + def forward(self, x): + b = x.shape[0] + outs = [] + for agg in self.aggs: + y = agg(x) + p = F.adaptive_avg_pool2d(y, 1) + outs.append(p.reshape((b, 1, self.d_out))) + out = paddle.concat(outs, 1) + return out + + +class WeightAggregate(nn.Layer): + def __init__(self, n_r, d_in, d_middle=None, d_out=None): + super(WeightAggregate, self).__init__() + if not d_middle: + d_middle = d_in + if not d_out: + d_out = d_in + + self.n_r = n_r + self.d_out = d_out + self.act = nn.Swish() + + self.conv_n = nn.Sequential( + ("conv1", nn.Conv2D(d_in, d_in, 3, 1, 1, bias_attr=False)), + ("bn1", nn.BatchNorm(d_in)), + ("act1", self.act), + ("conv2", nn.Conv2D(d_in, n_r, 1, bias_attr=False)), + ("bn2", nn.BatchNorm(n_r)), + ("act2", nn.Sigmoid()), + ) + self.conv_d = nn.Sequential( + ("conv1", nn.Conv2D(d_in, d_middle, 3, 1, 1, bias_attr=False)), + ("bn1", nn.BatchNorm(d_middle)), + ("act1", self.act), + ("conv2", nn.Conv2D(d_middle, d_out, 1, bias_attr=False)), + ("bn2", nn.BatchNorm(d_out)), + ) + + def forward(self, x): + b, _, h, w = x.shape + + hmaps = self.conv_n(x) + fmaps = self.conv_d(x) + r = paddle.bmm( + hmaps.reshape((b, self.n_r, h * w)), + fmaps.reshape((b, self.d_out, h * w)).transpose((0, 2, 1)), + ) + return r + + +class GCN(nn.Layer): + def __init__(self, d_in, n_in, d_out=None, n_out=None, dropout=0.1): + super(GCN, self).__init__() + if not d_out: + d_out = d_in + if not n_out: + n_out = d_in + + self.conv_n = nn.Conv1D(n_in, n_out, 1) + self.linear = nn.Linear(d_in, d_out) + self.dropout = nn.Dropout(dropout) + self.act = nn.Swish() + + def forward(self, x): + x = self.conv_n(x) + x = self.dropout(self.linear(x)) + return self.act(x) + + +class PRENFPN(nn.Layer): + def __init__(self, in_channels, n_r, d_model, max_len, dropout): + super(PRENFPN, self).__init__() + assert len(in_channels) == 3, "in_channels' length must be 3." + c1, c2, c3 = in_channels # the depths are from big to small + # build fpn + assert d_model % 3 == 0, "{} can't be divided by 3.".format(d_model) + self.agg_p1 = PoolAggregate(n_r, c1, d_out=d_model // 3) + self.agg_p2 = PoolAggregate(n_r, c2, d_out=d_model // 3) + self.agg_p3 = PoolAggregate(n_r, c3, d_out=d_model // 3) + + self.agg_w1 = WeightAggregate(n_r, c1, 4 * c1, d_model // 3) + self.agg_w2 = WeightAggregate(n_r, c2, 4 * c2, d_model // 3) + self.agg_w3 = WeightAggregate(n_r, c3, 4 * c3, d_model // 3) + + self.gcn_pool = GCN(d_model, n_r, d_model, max_len, dropout) + self.gcn_weight = GCN(d_model, n_r, d_model, max_len, dropout) + + self.out_channels = d_model + + def forward(self, inputs): + f3, f5, f7 = inputs + + rp1 = self.agg_p1(f3) + rp2 = self.agg_p2(f5) + rp3 = self.agg_p3(f7) + rp = paddle.concat([rp1, rp2, rp3], 2) # [b,nr,d] + + rw1 = self.agg_w1(f3) + rw2 = self.agg_w2(f5) + rw3 = self.agg_w3(f7) + rw = paddle.concat([rw1, rw2, rw3], 2) # [b,nr,d] + + y1 = self.gcn_pool(rp) + y2 = self.gcn_weight(rw) + y = 0.5 * (y1 + y2) + return y # [b,max_len,d] diff --git a/modules/onnx_ocr_module/src/ppocr/modeling/necks/rf_adaptor.py b/modules/onnx_ocr_module/src/ppocr/modeling/necks/rf_adaptor.py new file mode 100644 index 0000000..e27e894 --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/modeling/necks/rf_adaptor.py @@ -0,0 +1,146 @@ +# copyright (c) 2022 PaddlePaddle Authors. All Rights Reserve. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +""" +This code is refer from: +https://github.com/hikopensource/DAVAR-Lab-OCR/blob/main/davarocr/davar_rcg/models/connects/single_block/RFAdaptor.py +""" + +import paddle +import paddle.nn as nn +from paddle.nn.initializer import TruncatedNormal, Constant, Normal, KaimingNormal + +kaiming_init_ = KaimingNormal() +zeros_ = Constant(value=0.0) +ones_ = Constant(value=1.0) + + +class S2VAdaptor(nn.Layer): + """Semantic to Visual adaptation module""" + + def __init__(self, in_channels=512): + super(S2VAdaptor, self).__init__() + + self.in_channels = in_channels # 512 + + # feature strengthen module, channel attention + self.channel_inter = nn.Linear( + self.in_channels, self.in_channels, bias_attr=False + ) + self.channel_bn = nn.BatchNorm1D(self.in_channels) + self.channel_act = nn.ReLU() + self.apply(self.init_weights) + + def init_weights(self, m): + if isinstance(m, nn.Conv2D): + kaiming_init_(m.weight) + if isinstance(m, nn.Conv2D) and m.bias is not None: + zeros_(m.bias) + elif isinstance(m, (nn.BatchNorm, nn.BatchNorm2D, nn.BatchNorm1D)): + zeros_(m.bias) + ones_(m.weight) + + def forward(self, semantic): + semantic_source = semantic # batch, channel, height, width + + # feature transformation + semantic = semantic.squeeze(2).transpose([0, 2, 1]) # batch, width, channel + channel_att = self.channel_inter(semantic) # batch, width, channel + channel_att = channel_att.transpose([0, 2, 1]) # batch, channel, width + channel_bn = self.channel_bn(channel_att) # batch, channel, width + channel_att = self.channel_act(channel_bn) # batch, channel, width + + # Feature enhancement + channel_output = semantic_source * channel_att.unsqueeze( + -2 + ) # batch, channel, 1, width + + return channel_output + + +class V2SAdaptor(nn.Layer): + """Visual to Semantic adaptation module""" + + def __init__(self, in_channels=512, return_mask=False): + super(V2SAdaptor, self).__init__() + + # parameter initialization + self.in_channels = in_channels + self.return_mask = return_mask + + # output transformation + self.channel_inter = nn.Linear( + self.in_channels, self.in_channels, bias_attr=False + ) + self.channel_bn = nn.BatchNorm1D(self.in_channels) + self.channel_act = nn.ReLU() + + def forward(self, visual): + # Feature enhancement + visual = visual.squeeze(2).transpose([0, 2, 1]) # batch, width, channel + channel_att = self.channel_inter(visual) # batch, width, channel + channel_att = channel_att.transpose([0, 2, 1]) # batch, channel, width + channel_bn = self.channel_bn(channel_att) # batch, channel, width + channel_att = self.channel_act(channel_bn) # batch, channel, width + + # size alignment + channel_output = channel_att.unsqueeze(-2) # batch, width, channel + + if self.return_mask: + return channel_output, channel_att + return channel_output + + +class RFAdaptor(nn.Layer): + def __init__(self, in_channels=512, use_v2s=True, use_s2v=True, **kwargs): + super(RFAdaptor, self).__init__() + if use_v2s is True: + self.neck_v2s = V2SAdaptor(in_channels=in_channels, **kwargs) + else: + self.neck_v2s = None + if use_s2v is True: + self.neck_s2v = S2VAdaptor(in_channels=in_channels, **kwargs) + else: + self.neck_s2v = None + self.out_channels = in_channels + + def forward(self, x): + visual_feature, rcg_feature = x + if visual_feature is not None: + ( + batch, + source_channels, + v_source_height, + v_source_width, + ) = visual_feature.shape + visual_feature = visual_feature.reshape( + [batch, source_channels, 1, v_source_height * v_source_width] + ) + + if self.neck_v2s is not None: + v_rcg_feature = rcg_feature * self.neck_v2s(visual_feature) + else: + v_rcg_feature = rcg_feature + + if self.neck_s2v is not None: + v_visual_feature = visual_feature + self.neck_s2v(rcg_feature) + else: + v_visual_feature = visual_feature + if v_rcg_feature is not None: + batch, source_channels, source_height, source_width = v_rcg_feature.shape + v_rcg_feature = v_rcg_feature.reshape( + [batch, source_channels, 1, source_height * source_width] + ) + + v_rcg_feature = v_rcg_feature.squeeze(2).transpose([0, 2, 1]) + return v_visual_feature, v_rcg_feature diff --git a/modules/onnx_ocr_module/src/ppocr/modeling/necks/rnn.py b/modules/onnx_ocr_module/src/ppocr/modeling/necks/rnn.py new file mode 100644 index 0000000..fa7b8a1 --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/modeling/necks/rnn.py @@ -0,0 +1,284 @@ +# copyright (c) 2020 PaddlePaddle Authors. All Rights Reserve. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import paddle +from paddle import nn + +from ppocr.modeling.heads.rec_ctc_head import get_para_bias_attr +from ppocr.modeling.backbones.rec_svtrnet import ( + Block, + ConvBNLayer, + trunc_normal_, + zeros_, + ones_, +) + + +class Im2Seq(nn.Layer): + def __init__(self, in_channels, **kwargs): + super().__init__() + self.out_channels = in_channels + + def forward(self, x): + B, C, H, W = x.shape + assert H == 1 + x = x.squeeze(axis=2) + x = x.transpose([0, 2, 1]) # (NTC)(batch, width, channels) + return x + + +class EncoderWithRNN(nn.Layer): + def __init__(self, in_channels, hidden_size): + super(EncoderWithRNN, self).__init__() + self.out_channels = hidden_size * 2 + self.lstm = nn.LSTM( + in_channels, hidden_size, direction="bidirectional", num_layers=2 + ) + + def forward(self, x): + x, _ = self.lstm(x) + return x + + +class BidirectionalLSTM(nn.Layer): + def __init__( + self, + input_size, + hidden_size, + output_size=None, + num_layers=1, + dropout=0, + direction=False, + time_major=False, + with_linear=False, + ): + super(BidirectionalLSTM, self).__init__() + self.with_linear = with_linear + self.rnn = nn.LSTM( + input_size, + hidden_size, + num_layers=num_layers, + dropout=dropout, + direction=direction, + time_major=time_major, + ) + + # text recognition the specified structure LSTM with linear + if self.with_linear: + self.linear = nn.Linear(hidden_size * 2, output_size) + + def forward(self, input_feature): + recurrent, _ = self.rnn( + input_feature + ) # batch_size x T x input_size -> batch_size x T x (2*hidden_size) + if self.with_linear: + output = self.linear(recurrent) # batch_size x T x output_size + return output + return recurrent + + +class EncoderWithCascadeRNN(nn.Layer): + def __init__( + self, in_channels, hidden_size, out_channels, num_layers=2, with_linear=False + ): + super(EncoderWithCascadeRNN, self).__init__() + self.out_channels = out_channels[-1] + self.encoder = nn.LayerList( + [ + BidirectionalLSTM( + in_channels if i == 0 else out_channels[i - 1], + hidden_size, + output_size=out_channels[i], + num_layers=1, + direction="bidirectional", + with_linear=with_linear, + ) + for i in range(num_layers) + ] + ) + + def forward(self, x): + for i, l in enumerate(self.encoder): + x = l(x) + return x + + +class EncoderWithFC(nn.Layer): + def __init__(self, in_channels, hidden_size): + super(EncoderWithFC, self).__init__() + self.out_channels = hidden_size + weight_attr, bias_attr = get_para_bias_attr(l2_decay=0.00001, k=in_channels) + self.fc = nn.Linear( + in_channels, + hidden_size, + weight_attr=weight_attr, + bias_attr=bias_attr, + name="reduce_encoder_fea", + ) + + def forward(self, x): + x = self.fc(x) + return x + + +class EncoderWithSVTR(nn.Layer): + def __init__( + self, + in_channels, + dims=64, # XS + depth=2, + hidden_dims=120, + use_guide=False, + num_heads=8, + qkv_bias=True, + mlp_ratio=2.0, + drop_rate=0.1, + attn_drop_rate=0.1, + drop_path=0.0, + kernel_size=[3, 3], + qk_scale=None, + ): + super(EncoderWithSVTR, self).__init__() + self.depth = depth + self.use_guide = use_guide + self.conv1 = ConvBNLayer( + in_channels, + in_channels // 8, + kernel_size=kernel_size, + padding=[kernel_size[0] // 2, kernel_size[1] // 2], + act=nn.Swish, + ) + self.conv2 = ConvBNLayer( + in_channels // 8, hidden_dims, kernel_size=1, act=nn.Swish + ) + + self.svtr_block = nn.LayerList( + [ + Block( + dim=hidden_dims, + num_heads=num_heads, + mixer="Global", + HW=None, + mlp_ratio=mlp_ratio, + qkv_bias=qkv_bias, + qk_scale=qk_scale, + drop=drop_rate, + act_layer=nn.Swish, + attn_drop=attn_drop_rate, + drop_path=drop_path, + norm_layer="nn.LayerNorm", + epsilon=1e-05, + prenorm=False, + ) + for i in range(depth) + ] + ) + self.norm = nn.LayerNorm(hidden_dims, epsilon=1e-6) + self.conv3 = ConvBNLayer(hidden_dims, in_channels, kernel_size=1, act=nn.Swish) + # last conv-nxn, the input is concat of input tensor and conv3 output tensor + self.conv4 = ConvBNLayer( + 2 * in_channels, + in_channels // 8, + kernel_size=kernel_size, + padding=[kernel_size[0] // 2, kernel_size[1] // 2], + act=nn.Swish, + ) + + self.conv1x1 = ConvBNLayer(in_channels // 8, dims, kernel_size=1, act=nn.Swish) + self.out_channels = dims + self.apply(self._init_weights) + + def _init_weights(self, m): + if isinstance(m, nn.Linear): + trunc_normal_(m.weight) + if isinstance(m, nn.Linear) and m.bias is not None: + zeros_(m.bias) + elif isinstance(m, nn.LayerNorm): + zeros_(m.bias) + ones_(m.weight) + + def forward(self, x): + # for use guide + if self.use_guide: + z = x.clone() + z.stop_gradient = True + else: + z = x + # for short cut + h = z + # reduce dim + z = self.conv1(z) + z = self.conv2(z) + # SVTR global block + B, C, H, W = z.shape + z = z.flatten(2).transpose([0, 2, 1]) + for blk in self.svtr_block: + z = blk(z) + z = self.norm(z) + # last stage + z = z.reshape([0, H, W, C]).transpose([0, 3, 1, 2]) + z = self.conv3(z) + z = paddle.concat((h, z), axis=1) + z = self.conv1x1(self.conv4(z)) + return z + + +class SequenceEncoder(nn.Layer): + def __init__(self, in_channels, encoder_type, hidden_size=48, **kwargs): + super(SequenceEncoder, self).__init__() + self.encoder_reshape = Im2Seq(in_channels) + self.out_channels = self.encoder_reshape.out_channels + self.encoder_type = encoder_type + if encoder_type == "reshape": + self.only_reshape = True + else: + support_encoder_dict = { + "reshape": Im2Seq, + "fc": EncoderWithFC, + "rnn": EncoderWithRNN, + "svtr": EncoderWithSVTR, + "cascadernn": EncoderWithCascadeRNN, + } + assert encoder_type in support_encoder_dict, "{} must in {}".format( + encoder_type, support_encoder_dict.keys() + ) + if encoder_type == "svtr": + self.encoder = support_encoder_dict[encoder_type]( + self.encoder_reshape.out_channels, **kwargs + ) + elif encoder_type == "cascadernn": + self.encoder = support_encoder_dict[encoder_type]( + self.encoder_reshape.out_channels, hidden_size, **kwargs + ) + else: + self.encoder = support_encoder_dict[encoder_type]( + self.encoder_reshape.out_channels, hidden_size + ) + self.out_channels = self.encoder.out_channels + self.only_reshape = False + + def forward(self, x): + if self.encoder_type != "svtr": + x = self.encoder_reshape(x) + if not self.only_reshape: + x = self.encoder(x) + return x + else: + x = self.encoder(x) + x = self.encoder_reshape(x) + return x diff --git a/modules/onnx_ocr_module/src/ppocr/modeling/necks/sast_fpn.py b/modules/onnx_ocr_module/src/ppocr/modeling/necks/sast_fpn.py new file mode 100644 index 0000000..4804806 --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/modeling/necks/sast_fpn.py @@ -0,0 +1,368 @@ +# copyright (c) 2019 PaddlePaddle Authors. All Rights Reserve. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import paddle +from paddle import nn +import paddle.nn.functional as F +from paddle import ParamAttr + + +class ConvBNLayer(nn.Layer): + def __init__( + self, + in_channels, + out_channels, + kernel_size, + stride, + groups=1, + if_act=True, + act=None, + name=None, + ): + super(ConvBNLayer, self).__init__() + self.if_act = if_act + self.act = act + self.conv = nn.Conv2D( + in_channels=in_channels, + out_channels=out_channels, + kernel_size=kernel_size, + stride=stride, + padding=(kernel_size - 1) // 2, + groups=groups, + weight_attr=ParamAttr(name=name + "_weights"), + bias_attr=False, + ) + + self.bn = nn.BatchNorm( + num_channels=out_channels, + act=act, + param_attr=ParamAttr(name="bn_" + name + "_scale"), + bias_attr=ParamAttr(name="bn_" + name + "_offset"), + moving_mean_name="bn_" + name + "_mean", + moving_variance_name="bn_" + name + "_variance", + ) + + def forward(self, x): + x = self.conv(x) + x = self.bn(x) + return x + + +class DeConvBNLayer(nn.Layer): + def __init__( + self, + in_channels, + out_channels, + kernel_size, + stride, + groups=1, + if_act=True, + act=None, + name=None, + ): + super(DeConvBNLayer, self).__init__() + self.if_act = if_act + self.act = act + self.deconv = nn.Conv2DTranspose( + in_channels=in_channels, + out_channels=out_channels, + kernel_size=kernel_size, + stride=stride, + padding=(kernel_size - 1) // 2, + groups=groups, + weight_attr=ParamAttr(name=name + "_weights"), + bias_attr=False, + ) + self.bn = nn.BatchNorm( + num_channels=out_channels, + act=act, + param_attr=ParamAttr(name="bn_" + name + "_scale"), + bias_attr=ParamAttr(name="bn_" + name + "_offset"), + moving_mean_name="bn_" + name + "_mean", + moving_variance_name="bn_" + name + "_variance", + ) + + def forward(self, x): + x = self.deconv(x) + x = self.bn(x) + return x + + +class FPN_Up_Fusion(nn.Layer): + def __init__(self, in_channels): + super(FPN_Up_Fusion, self).__init__() + in_channels = in_channels[::-1] + out_channels = [256, 256, 192, 192, 128] + + self.h0_conv = ConvBNLayer( + in_channels[0], out_channels[0], 1, 1, act=None, name="fpn_up_h0" + ) + self.h1_conv = ConvBNLayer( + in_channels[1], out_channels[1], 1, 1, act=None, name="fpn_up_h1" + ) + self.h2_conv = ConvBNLayer( + in_channels[2], out_channels[2], 1, 1, act=None, name="fpn_up_h2" + ) + self.h3_conv = ConvBNLayer( + in_channels[3], out_channels[3], 1, 1, act=None, name="fpn_up_h3" + ) + self.h4_conv = ConvBNLayer( + in_channels[4], out_channels[4], 1, 1, act=None, name="fpn_up_h4" + ) + + self.g0_conv = DeConvBNLayer( + out_channels[0], out_channels[1], 4, 2, act=None, name="fpn_up_g0" + ) + + self.g1_conv = nn.Sequential( + ConvBNLayer( + out_channels[1], out_channels[1], 3, 1, act="relu", name="fpn_up_g1_1" + ), + DeConvBNLayer( + out_channels[1], out_channels[2], 4, 2, act=None, name="fpn_up_g1_2" + ), + ) + self.g2_conv = nn.Sequential( + ConvBNLayer( + out_channels[2], out_channels[2], 3, 1, act="relu", name="fpn_up_g2_1" + ), + DeConvBNLayer( + out_channels[2], out_channels[3], 4, 2, act=None, name="fpn_up_g2_2" + ), + ) + self.g3_conv = nn.Sequential( + ConvBNLayer( + out_channels[3], out_channels[3], 3, 1, act="relu", name="fpn_up_g3_1" + ), + DeConvBNLayer( + out_channels[3], out_channels[4], 4, 2, act=None, name="fpn_up_g3_2" + ), + ) + + self.g4_conv = nn.Sequential( + ConvBNLayer( + out_channels[4], + out_channels[4], + 3, + 1, + act="relu", + name="fpn_up_fusion_1", + ), + ConvBNLayer( + out_channels[4], out_channels[4], 1, 1, act=None, name="fpn_up_fusion_2" + ), + ) + + def _add_relu(self, x1, x2): + x = paddle.add(x=x1, y=x2) + x = F.relu(x) + return x + + def forward(self, x): + f = x[2:][::-1] + h0 = self.h0_conv(f[0]) + h1 = self.h1_conv(f[1]) + h2 = self.h2_conv(f[2]) + h3 = self.h3_conv(f[3]) + h4 = self.h4_conv(f[4]) + + g0 = self.g0_conv(h0) + g1 = self._add_relu(g0, h1) + g1 = self.g1_conv(g1) + g2 = self.g2_conv(self._add_relu(g1, h2)) + g3 = self.g3_conv(self._add_relu(g2, h3)) + g4 = self.g4_conv(self._add_relu(g3, h4)) + + return g4 + + +class FPN_Down_Fusion(nn.Layer): + def __init__(self, in_channels): + super(FPN_Down_Fusion, self).__init__() + out_channels = [32, 64, 128] + + self.h0_conv = ConvBNLayer( + in_channels[0], out_channels[0], 3, 1, act=None, name="fpn_down_h0" + ) + self.h1_conv = ConvBNLayer( + in_channels[1], out_channels[1], 3, 1, act=None, name="fpn_down_h1" + ) + self.h2_conv = ConvBNLayer( + in_channels[2], out_channels[2], 3, 1, act=None, name="fpn_down_h2" + ) + + self.g0_conv = ConvBNLayer( + out_channels[0], out_channels[1], 3, 2, act=None, name="fpn_down_g0" + ) + + self.g1_conv = nn.Sequential( + ConvBNLayer( + out_channels[1], out_channels[1], 3, 1, act="relu", name="fpn_down_g1_1" + ), + ConvBNLayer( + out_channels[1], out_channels[2], 3, 2, act=None, name="fpn_down_g1_2" + ), + ) + + self.g2_conv = nn.Sequential( + ConvBNLayer( + out_channels[2], + out_channels[2], + 3, + 1, + act="relu", + name="fpn_down_fusion_1", + ), + ConvBNLayer( + out_channels[2], + out_channels[2], + 1, + 1, + act=None, + name="fpn_down_fusion_2", + ), + ) + + def forward(self, x): + f = x[:3] + h0 = self.h0_conv(f[0]) + h1 = self.h1_conv(f[1]) + h2 = self.h2_conv(f[2]) + g0 = self.g0_conv(h0) + g1 = paddle.add(x=g0, y=h1) + g1 = F.relu(g1) + g1 = self.g1_conv(g1) + g2 = paddle.add(x=g1, y=h2) + g2 = F.relu(g2) + g2 = self.g2_conv(g2) + return g2 + + +class Cross_Attention(nn.Layer): + def __init__(self, in_channels): + super(Cross_Attention, self).__init__() + self.theta_conv = ConvBNLayer( + in_channels, in_channels, 1, 1, act="relu", name="f_theta" + ) + self.phi_conv = ConvBNLayer( + in_channels, in_channels, 1, 1, act="relu", name="f_phi" + ) + self.g_conv = ConvBNLayer( + in_channels, in_channels, 1, 1, act="relu", name="f_g" + ) + + self.fh_weight_conv = ConvBNLayer( + in_channels, in_channels, 1, 1, act=None, name="fh_weight" + ) + self.fh_sc_conv = ConvBNLayer( + in_channels, in_channels, 1, 1, act=None, name="fh_sc" + ) + + self.fv_weight_conv = ConvBNLayer( + in_channels, in_channels, 1, 1, act=None, name="fv_weight" + ) + self.fv_sc_conv = ConvBNLayer( + in_channels, in_channels, 1, 1, act=None, name="fv_sc" + ) + + self.f_attn_conv = ConvBNLayer( + in_channels * 2, in_channels, 1, 1, act="relu", name="f_attn" + ) + + def _cal_fweight(self, f, shape): + f_theta, f_phi, f_g = f + # flatten + f_theta = paddle.transpose(f_theta, [0, 2, 3, 1]) + f_theta = paddle.reshape(f_theta, [shape[0] * shape[1], shape[2], 128]) + f_phi = paddle.transpose(f_phi, [0, 2, 3, 1]) + f_phi = paddle.reshape(f_phi, [shape[0] * shape[1], shape[2], 128]) + f_g = paddle.transpose(f_g, [0, 2, 3, 1]) + f_g = paddle.reshape(f_g, [shape[0] * shape[1], shape[2], 128]) + # correlation + f_attn = paddle.matmul(f_theta, paddle.transpose(f_phi, [0, 2, 1])) + # scale + f_attn = f_attn / (128**0.5) + f_attn = F.softmax(f_attn) + # weighted sum + f_weight = paddle.matmul(f_attn, f_g) + f_weight = paddle.reshape(f_weight, [shape[0], shape[1], shape[2], 128]) + return f_weight + + def forward(self, f_common): + f_shape = f_common.shape + # print('f_shape: ', f_shape) + + f_theta = self.theta_conv(f_common) + f_phi = self.phi_conv(f_common) + f_g = self.g_conv(f_common) + + ######## horizon ######## + fh_weight = self._cal_fweight( + [f_theta, f_phi, f_g], [f_shape[0], f_shape[2], f_shape[3]] + ) + fh_weight = paddle.transpose(fh_weight, [0, 3, 1, 2]) + fh_weight = self.fh_weight_conv(fh_weight) + # short cut + fh_sc = self.fh_sc_conv(f_common) + f_h = F.relu(fh_weight + fh_sc) + + ######## vertical ######## + fv_theta = paddle.transpose(f_theta, [0, 1, 3, 2]) + fv_phi = paddle.transpose(f_phi, [0, 1, 3, 2]) + fv_g = paddle.transpose(f_g, [0, 1, 3, 2]) + fv_weight = self._cal_fweight( + [fv_theta, fv_phi, fv_g], [f_shape[0], f_shape[3], f_shape[2]] + ) + fv_weight = paddle.transpose(fv_weight, [0, 3, 2, 1]) + fv_weight = self.fv_weight_conv(fv_weight) + # short cut + fv_sc = self.fv_sc_conv(f_common) + f_v = F.relu(fv_weight + fv_sc) + + ######## merge ######## + f_attn = paddle.concat([f_h, f_v], axis=1) + f_attn = self.f_attn_conv(f_attn) + return f_attn + + +class SASTFPN(nn.Layer): + def __init__(self, in_channels, with_cab=False, **kwargs): + super(SASTFPN, self).__init__() + self.in_channels = in_channels + self.with_cab = with_cab + self.FPN_Down_Fusion = FPN_Down_Fusion(self.in_channels) + self.FPN_Up_Fusion = FPN_Up_Fusion(self.in_channels) + self.out_channels = 128 + self.cross_attention = Cross_Attention(self.out_channels) + + def forward(self, x): + # down fpn + f_down = self.FPN_Down_Fusion(x) + + # up fpn + f_up = self.FPN_Up_Fusion(x) + + # fusion + f_common = paddle.add(x=f_down, y=f_up) + f_common = F.relu(f_common) + + if self.with_cab: + # print('enhence f_common with CAB.') + f_common = self.cross_attention(f_common) + + return f_common diff --git a/modules/onnx_ocr_module/src/ppocr/modeling/necks/table_fpn.py b/modules/onnx_ocr_module/src/ppocr/modeling/necks/table_fpn.py new file mode 100644 index 0000000..d273965 --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/modeling/necks/table_fpn.py @@ -0,0 +1,123 @@ +# copyright (c) 2021 PaddlePaddle Authors. All Rights Reserve. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import paddle +from paddle import nn +import paddle.nn.functional as F +from paddle import ParamAttr + + +class TableFPN(nn.Layer): + def __init__(self, in_channels, out_channels, **kwargs): + super(TableFPN, self).__init__() + self.out_channels = 512 + weight_attr = paddle.nn.initializer.KaimingUniform() + self.in2_conv = nn.Conv2D( + in_channels=in_channels[0], + out_channels=self.out_channels, + kernel_size=1, + weight_attr=ParamAttr(initializer=weight_attr), + bias_attr=False, + ) + self.in3_conv = nn.Conv2D( + in_channels=in_channels[1], + out_channels=self.out_channels, + kernel_size=1, + stride=1, + weight_attr=ParamAttr(initializer=weight_attr), + bias_attr=False, + ) + self.in4_conv = nn.Conv2D( + in_channels=in_channels[2], + out_channels=self.out_channels, + kernel_size=1, + weight_attr=ParamAttr(initializer=weight_attr), + bias_attr=False, + ) + self.in5_conv = nn.Conv2D( + in_channels=in_channels[3], + out_channels=self.out_channels, + kernel_size=1, + weight_attr=ParamAttr(initializer=weight_attr), + bias_attr=False, + ) + self.p5_conv = nn.Conv2D( + in_channels=self.out_channels, + out_channels=self.out_channels // 4, + kernel_size=3, + padding=1, + weight_attr=ParamAttr(initializer=weight_attr), + bias_attr=False, + ) + self.p4_conv = nn.Conv2D( + in_channels=self.out_channels, + out_channels=self.out_channels // 4, + kernel_size=3, + padding=1, + weight_attr=ParamAttr(initializer=weight_attr), + bias_attr=False, + ) + self.p3_conv = nn.Conv2D( + in_channels=self.out_channels, + out_channels=self.out_channels // 4, + kernel_size=3, + padding=1, + weight_attr=ParamAttr(initializer=weight_attr), + bias_attr=False, + ) + self.p2_conv = nn.Conv2D( + in_channels=self.out_channels, + out_channels=self.out_channels // 4, + kernel_size=3, + padding=1, + weight_attr=ParamAttr(initializer=weight_attr), + bias_attr=False, + ) + self.fuse_conv = nn.Conv2D( + in_channels=self.out_channels * 4, + out_channels=512, + kernel_size=3, + padding=1, + weight_attr=ParamAttr(initializer=weight_attr), + bias_attr=False, + ) + + def forward(self, x): + c2, c3, c4, c5 = x + + in5 = self.in5_conv(c5) + in4 = self.in4_conv(c4) + in3 = self.in3_conv(c3) + in2 = self.in2_conv(c2) + + out4 = in4 + F.upsample( + in5, size=in4.shape[2:4], mode="nearest", align_mode=1 + ) # 1/16 + out3 = in3 + F.upsample( + out4, size=in3.shape[2:4], mode="nearest", align_mode=1 + ) # 1/8 + out2 = in2 + F.upsample( + out3, size=in2.shape[2:4], mode="nearest", align_mode=1 + ) # 1/4 + + p4 = F.upsample(out4, size=in5.shape[2:4], mode="nearest", align_mode=1) + p3 = F.upsample(out3, size=in5.shape[2:4], mode="nearest", align_mode=1) + p2 = F.upsample(out2, size=in5.shape[2:4], mode="nearest", align_mode=1) + fuse = paddle.concat([in5, p4, p3, p2], axis=1) + fuse_conv = self.fuse_conv(fuse) * 0.005 + return [c5 + fuse_conv] diff --git a/modules/onnx_ocr_module/src/ppocr/modeling/transforms/__init__.py b/modules/onnx_ocr_module/src/ppocr/modeling/transforms/__init__.py new file mode 100644 index 0000000..24aeca3 --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/modeling/transforms/__init__.py @@ -0,0 +1,32 @@ +# Copyright (c) 2020 PaddlePaddle Authors. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +__all__ = ["build_transform"] + + +def build_transform(config): + from .tps import TPS + from .stn import STN_ON + from .tsrn import TSRN + from .tbsrn import TBSRN + from .gaspin_transformer import GA_SPIN_Transformer as GA_SPIN + + support_dict = ["TPS", "STN_ON", "GA_SPIN", "TSRN", "TBSRN"] + + module_name = config.pop("name") + assert module_name in support_dict, Exception( + "transform only support {}".format(support_dict) + ) + module_class = eval(module_name)(**config) + return module_class diff --git a/modules/onnx_ocr_module/src/ppocr/modeling/transforms/gaspin_transformer.py b/modules/onnx_ocr_module/src/ppocr/modeling/transforms/gaspin_transformer.py new file mode 100644 index 0000000..22e9242 --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/modeling/transforms/gaspin_transformer.py @@ -0,0 +1,319 @@ +# copyright (c) 2022 PaddlePaddle Authors. All Rights Reserve. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import math +import paddle +from paddle import nn, ParamAttr +from paddle.nn import functional as F +import numpy as np +import functools +from .tps import GridGenerator + +"""This code is refer from: +https://github.com/hikopensource/DAVAR-Lab-OCR/davarocr/davar_rcg/models/transformations/gaspin_transformation.py +""" + + +class SP_TransformerNetwork(nn.Layer): + """ + Sturture-Preserving Transformation (SPT) as Equa. (2) in Ref. [1] + Ref: [1] SPIN: Structure-Preserving Inner Offset Network for Scene Text Recognition. AAAI-2021. + """ + + def __init__(self, nc=1, default_type=5): + """Based on SPIN + Args: + nc (int): number of input channels (usually in 1 or 3) + default_type (int): the complexity of transformation intensities (by default set to 6 as the paper) + """ + super(SP_TransformerNetwork, self).__init__() + self.power_list = self.cal_K(default_type) + self.sigmoid = nn.Sigmoid() + self.bn = nn.InstanceNorm2D(nc) + + def cal_K(self, k=5): + """ + + Args: + k (int): the complexity of transformation intensities (by default set to 6 as the paper) + + Returns: + List: the normalized intensity of each pixel in [0,1], denoted as \beta [1x(2K+1)] + + """ + from math import log + + x = [] + if k != 0: + for i in range(1, k + 1): + lower = round( + log(1 - (0.5 / (k + 1)) * i) / log((0.5 / (k + 1)) * i), 2 + ) + upper = round(1 / lower, 2) + x.append(lower) + x.append(upper) + x.append(1.00) + return x + + def forward(self, batch_I, weights, offsets, lambda_color=None): + """ + + Args: + batch_I (Tensor): batch of input images [batch_size x nc x I_height x I_width] + weights: + offsets: the predicted offset by AIN, a scalar + lambda_color: the learnable update gate \alpha in Equa. (5) as + g(x) = (1 - \alpha) \odot x + \alpha \odot x_{offsets} + + Returns: + Tensor: transformed images by SPN as Equa. (4) in Ref. [1] + [batch_size x I_channel_num x I_r_height x I_r_width] + + """ + batch_I = (batch_I + 1) * 0.5 + if offsets is not None: + batch_I = batch_I * (1 - lambda_color) + offsets * lambda_color + batch_weight_params = paddle.unsqueeze(paddle.unsqueeze(weights, -1), -1) + batch_I_power = paddle.stack([batch_I.pow(p) for p in self.power_list], axis=1) + + batch_weight_sum = paddle.sum(batch_I_power * batch_weight_params, axis=1) + batch_weight_sum = self.bn(batch_weight_sum) + batch_weight_sum = self.sigmoid(batch_weight_sum) + batch_weight_sum = batch_weight_sum * 2 - 1 + return batch_weight_sum + + +class GA_SPIN_Transformer(nn.Layer): + """ + Geometric-Absorbed SPIN Transformation (GA-SPIN) proposed in Ref. [1] + + + Ref: [1] SPIN: Structure-Preserving Inner Offset Network for Scene Text Recognition. AAAI-2021. + """ + + def __init__( + self, + in_channels=1, + I_r_size=(32, 100), + offsets=False, + norm_type="BN", + default_type=6, + loc_lr=1, + stn=True, + ): + """ + Args: + in_channels (int): channel of input features, + set it to 1 if the grayscale images and 3 if RGB input + I_r_size (tuple): size of rectified images (used in STN transformations) + offsets (bool): set it to False if use SPN w.o. AIN, + and set it to True if use SPIN (both with SPN and AIN) + norm_type (str): the normalization type of the module, + set it to 'BN' by default, 'IN' optionally + default_type (int): the K chromatic space, + set it to 3/5/6 depend on the complexity of transformation intensities + loc_lr (float): learning rate of location network + stn (bool): whether to use stn. + + """ + super(GA_SPIN_Transformer, self).__init__() + self.nc = in_channels + self.spt = True + self.offsets = offsets + self.stn = stn # set to True in GA-SPIN, while set it to False in SPIN + self.I_r_size = I_r_size + self.out_channels = in_channels + if norm_type == "BN": + norm_layer = functools.partial(nn.BatchNorm2D, use_global_stats=True) + elif norm_type == "IN": + norm_layer = functools.partial( + nn.InstanceNorm2D, weight_attr=False, use_global_stats=False + ) + else: + raise NotImplementedError( + "normalization layer [%s] is not found" % norm_type + ) + + if self.spt: + self.sp_net = SP_TransformerNetwork(in_channels, default_type) + self.spt_convnet = nn.Sequential( + # 32*100 + nn.Conv2D(in_channels, 32, 3, 1, 1, bias_attr=False), + norm_layer(32), + nn.ReLU(), + nn.MaxPool2D(kernel_size=2, stride=2), + # 16*50 + nn.Conv2D(32, 64, 3, 1, 1, bias_attr=False), + norm_layer(64), + nn.ReLU(), + nn.MaxPool2D(kernel_size=2, stride=2), + # 8*25 + nn.Conv2D(64, 128, 3, 1, 1, bias_attr=False), + norm_layer(128), + nn.ReLU(), + nn.MaxPool2D(kernel_size=2, stride=2), + # 4*12 + ) + self.stucture_fc1 = nn.Sequential( + nn.Conv2D(128, 256, 3, 1, 1, bias_attr=False), + norm_layer(256), + nn.ReLU(), + nn.MaxPool2D(kernel_size=2, stride=2), + nn.Conv2D(256, 256, 3, 1, 1, bias_attr=False), + norm_layer(256), + nn.ReLU(), # 2*6 + nn.MaxPool2D(kernel_size=2, stride=2), + nn.Conv2D(256, 512, 3, 1, 1, bias_attr=False), + norm_layer(512), + nn.ReLU(), # 1*3 + nn.AdaptiveAvgPool2D(1), + nn.Flatten(1, -1), # batch_size x 512 + nn.Linear(512, 256, weight_attr=nn.initializer.Normal(0.001)), + nn.BatchNorm1D(256), + nn.ReLU(), + ) + self.out_weight = 2 * default_type + 1 + self.spt_length = 2 * default_type + 1 + if offsets: + self.out_weight += 1 + if self.stn: + self.F = 20 + self.out_weight += self.F * 2 + self.GridGenerator = GridGenerator(self.F * 2, self.F) + + # self.out_weight*=nc + # Init structure_fc2 in LocalizationNetwork + initial_bias = self.init_spin(default_type * 2) + initial_bias = initial_bias.reshape(-1) + param_attr = ParamAttr( + learning_rate=loc_lr, + initializer=nn.initializer.Assign(np.zeros([256, self.out_weight])), + ) + bias_attr = ParamAttr( + learning_rate=loc_lr, initializer=nn.initializer.Assign(initial_bias) + ) + self.stucture_fc2 = nn.Linear( + 256, self.out_weight, weight_attr=param_attr, bias_attr=bias_attr + ) + self.sigmoid = nn.Sigmoid() + + if offsets: + self.offset_fc1 = nn.Sequential( + nn.Conv2D(128, 16, 3, 1, 1, bias_attr=False), + norm_layer(16), + nn.ReLU(), + ) + self.offset_fc2 = nn.Conv2D(16, in_channels, 3, 1, 1) + self.pool = nn.MaxPool2D(2, 2) + + def init_spin(self, nz): + """ + Args: + nz (int): number of paired \betas exponents, which means the value of K x 2 + + """ + init_id = [0.00] * nz + [5.00] + if self.offsets: + init_id += [-5.00] + # init_id *=3 + init = np.array(init_id) + + if self.stn: + F = self.F + ctrl_pts_x = np.linspace(-1.0, 1.0, int(F / 2)) + ctrl_pts_y_top = np.linspace(0.0, -1.0, num=int(F / 2)) + ctrl_pts_y_bottom = np.linspace(1.0, 0.0, num=int(F / 2)) + ctrl_pts_top = np.stack([ctrl_pts_x, ctrl_pts_y_top], axis=1) + ctrl_pts_bottom = np.stack([ctrl_pts_x, ctrl_pts_y_bottom], axis=1) + initial_bias = np.concatenate([ctrl_pts_top, ctrl_pts_bottom], axis=0) + initial_bias = initial_bias.reshape(-1) + init = np.concatenate([init, initial_bias], axis=0) + return init + + def forward(self, x, return_weight=False): + """ + Args: + x (Tensor): input image batch + return_weight (bool): set to False by default, + if set to True return the predicted offsets of AIN, denoted as x_{offsets} + + Returns: + Tensor: rectified image [batch_size x I_channel_num x I_height x I_width], the same as the input size + """ + + if self.spt: + feat = self.spt_convnet(x) + fc1 = self.stucture_fc1(feat) + sp_weight_fusion = self.stucture_fc2(fc1) + sp_weight_fusion = sp_weight_fusion.reshape( + [x.shape[0], self.out_weight, 1] + ) + if self.offsets: # SPIN w. AIN + lambda_color = sp_weight_fusion[:, self.spt_length, 0] + lambda_color = ( + self.sigmoid(lambda_color).unsqueeze(-1).unsqueeze(-1).unsqueeze(-1) + ) + sp_weight = sp_weight_fusion[:, : self.spt_length, :] + offsets = self.pool(self.offset_fc2(self.offset_fc1(feat))) + + assert offsets.shape[2] == 2 # 2 + assert offsets.shape[3] == 6 # 16 + offsets = self.sigmoid(offsets) # v12 + + if return_weight: + return offsets + offsets = nn.functional.upsample( + offsets, size=(x.shape[2], x.shape[3]), mode="bilinear" + ) + + if self.stn: + batch_C_prime = sp_weight_fusion[ + :, (self.spt_length + 1) :, : + ].reshape([x.shape[0], self.F, 2]) + build_P_prime = self.GridGenerator(batch_C_prime, self.I_r_size) + build_P_prime_reshape = build_P_prime.reshape( + [build_P_prime.shape[0], self.I_r_size[0], self.I_r_size[1], 2] + ) + + else: # SPIN w.o. AIN + sp_weight = sp_weight_fusion[:, : self.spt_length, :] + lambda_color, offsets = None, None + + if self.stn: + batch_C_prime = sp_weight_fusion[:, self.spt_length :, :].reshape( + [x.shape[0], self.F, 2] + ) + build_P_prime = self.GridGenerator(batch_C_prime, self.I_r_size) + build_P_prime_reshape = build_P_prime.reshape( + [build_P_prime.shape[0], self.I_r_size[0], self.I_r_size[1], 2] + ) + + x = self.sp_net(x, sp_weight, offsets, lambda_color) + if self.stn: + is_fp16 = False + if build_P_prime_reshape.dtype != paddle.float32: + data_type = build_P_prime_reshape.dtype + x = x.cast(paddle.float32) + build_P_prime_reshape = build_P_prime_reshape.cast(paddle.float32) + is_fp16 = True + x = F.grid_sample( + x=x, grid=build_P_prime_reshape, padding_mode="border" + ) + if is_fp16: + x = x.cast(data_type) + return x diff --git a/modules/onnx_ocr_module/src/ppocr/modeling/transforms/stn.py b/modules/onnx_ocr_module/src/ppocr/modeling/transforms/stn.py new file mode 100644 index 0000000..a721184 --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/modeling/transforms/stn.py @@ -0,0 +1,147 @@ +# copyright (c) 2020 PaddlePaddle Authors. All Rights Reserve. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +""" +This code is refer from: +https://github.com/ayumiymk/aster.pytorch/blob/master/lib/models/stn_head.py +""" +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import math +import paddle +from paddle import nn, ParamAttr +from paddle.nn import functional as F +import numpy as np + +from .tps_spatial_transformer import TPSSpatialTransformer + + +def conv3x3_block(in_channels, out_channels, stride=1): + n = 3 * 3 * out_channels + w = math.sqrt(2.0 / n) + conv_layer = nn.Conv2D( + in_channels, + out_channels, + kernel_size=3, + stride=stride, + padding=1, + weight_attr=nn.initializer.Normal(mean=0.0, std=w), + bias_attr=nn.initializer.Constant(0), + ) + block = nn.Sequential(conv_layer, nn.BatchNorm2D(out_channels), nn.ReLU()) + return block + + +class STN(nn.Layer): + def __init__(self, in_channels, num_ctrlpoints, activation="none"): + super(STN, self).__init__() + self.in_channels = in_channels + self.num_ctrlpoints = num_ctrlpoints + self.activation = activation + self.stn_convnet = nn.Sequential( + conv3x3_block(in_channels, 32), # 32x64 + nn.MaxPool2D(kernel_size=2, stride=2), + conv3x3_block(32, 64), # 16x32 + nn.MaxPool2D(kernel_size=2, stride=2), + conv3x3_block(64, 128), # 8*16 + nn.MaxPool2D(kernel_size=2, stride=2), + conv3x3_block(128, 256), # 4*8 + nn.MaxPool2D(kernel_size=2, stride=2), + conv3x3_block(256, 256), # 2*4, + nn.MaxPool2D(kernel_size=2, stride=2), + conv3x3_block(256, 256), + ) # 1*2 + self.stn_fc1 = nn.Sequential( + nn.Linear( + 2 * 256, + 512, + weight_attr=nn.initializer.Normal(0, 0.001), + bias_attr=nn.initializer.Constant(0), + ), + nn.BatchNorm1D(512), + nn.ReLU(), + ) + fc2_bias = self.init_stn() + self.stn_fc2 = nn.Linear( + 512, + num_ctrlpoints * 2, + weight_attr=nn.initializer.Constant(0.0), + bias_attr=nn.initializer.Assign(fc2_bias), + ) + + def init_stn(self): + margin = 0.01 + sampling_num_per_side = int(self.num_ctrlpoints / 2) + ctrl_pts_x = np.linspace(margin, 1.0 - margin, sampling_num_per_side) + ctrl_pts_y_top = np.ones(sampling_num_per_side) * margin + ctrl_pts_y_bottom = np.ones(sampling_num_per_side) * (1 - margin) + ctrl_pts_top = np.stack([ctrl_pts_x, ctrl_pts_y_top], axis=1) + ctrl_pts_bottom = np.stack([ctrl_pts_x, ctrl_pts_y_bottom], axis=1) + ctrl_points = np.concatenate([ctrl_pts_top, ctrl_pts_bottom], axis=0).astype( + np.float32 + ) + if self.activation == "none": + pass + elif self.activation == "sigmoid": + ctrl_points = -np.log(1.0 / ctrl_points - 1.0) + ctrl_points = paddle.to_tensor(ctrl_points) + fc2_bias = paddle.reshape( + ctrl_points, shape=[ctrl_points.shape[0] * ctrl_points.shape[1]] + ) + return fc2_bias + + def forward(self, x): + x = self.stn_convnet(x) + batch_size, _, h, w = x.shape + x = paddle.reshape(x, shape=(batch_size, -1)) + img_feat = self.stn_fc1(x) + x = self.stn_fc2(0.1 * img_feat) + if self.activation == "sigmoid": + x = F.sigmoid(x) + x = paddle.reshape(x, shape=[-1, self.num_ctrlpoints, 2]) + return img_feat, x + + +class STN_ON(nn.Layer): + def __init__( + self, + in_channels, + tps_inputsize, + tps_outputsize, + num_control_points, + tps_margins, + stn_activation, + ): + super(STN_ON, self).__init__() + self.tps = TPSSpatialTransformer( + output_image_size=tuple(tps_outputsize), + num_control_points=num_control_points, + margins=tuple(tps_margins), + ) + self.stn_head = STN( + in_channels=in_channels, + num_ctrlpoints=num_control_points, + activation=stn_activation, + ) + self.tps_inputsize = tps_inputsize + self.out_channels = in_channels + + def forward(self, image): + stn_input = paddle.nn.functional.interpolate( + image, self.tps_inputsize, mode="bilinear", align_corners=True + ) + stn_img_feat, ctrl_points = self.stn_head(stn_input) + x, _ = self.tps(image, ctrl_points) + return x diff --git a/modules/onnx_ocr_module/src/ppocr/modeling/transforms/tbsrn.py b/modules/onnx_ocr_module/src/ppocr/modeling/transforms/tbsrn.py new file mode 100644 index 0000000..a1fa167 --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/modeling/transforms/tbsrn.py @@ -0,0 +1,298 @@ +# copyright (c) 2022 PaddlePaddle Authors. All Rights Reserve. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +""" +This code is refer from: +https://github.com/FudanVI/FudanOCR/blob/main/scene-text-telescope/model/tbsrn.py +""" + +import math +import warnings +import numpy as np +import paddle +from paddle import nn +import string + +warnings.filterwarnings("ignore") + +from .tps_spatial_transformer import TPSSpatialTransformer +from .stn import STN as STNHead +from .tsrn import GruBlock, mish, UpsampleBLock +from ppocr.modeling.heads.sr_rensnet_transformer import ( + Transformer, + LayerNorm, + PositionwiseFeedForward, + MultiHeadedAttention, +) + + +def positionalencoding2d(d_model, height, width): + """ + :param d_model: dimension of the model + :param height: height of the positions + :param width: width of the positions + :return: d_model*height*width position matrix + """ + if d_model % 4 != 0: + raise ValueError( + "Cannot use sin/cos positional encoding with " + "odd dimension (got dim={:d})".format(d_model) + ) + pe = paddle.zeros([d_model, height, width]) + # Each dimension use half of d_model + d_model = int(d_model / 2) + div_term = paddle.exp( + paddle.arange(0.0, d_model, 2, dtype="int64") * -(math.log(10000.0) / d_model) + ) + pos_w = paddle.arange(0.0, width, dtype="float32").unsqueeze(1) + pos_h = paddle.arange(0.0, height, dtype="float32").unsqueeze(1) + + pe[0:d_model:2, :, :] = ( + paddle.sin(pos_w * div_term).transpose([1, 0]).unsqueeze(1).tile([1, height, 1]) + ) + pe[1:d_model:2, :, :] = ( + paddle.cos(pos_w * div_term).transpose([1, 0]).unsqueeze(1).tile([1, height, 1]) + ) + pe[d_model::2, :, :] = ( + paddle.sin(pos_h * div_term).transpose([1, 0]).unsqueeze(2).tile([1, 1, width]) + ) + pe[d_model + 1 :: 2, :, :] = ( + paddle.cos(pos_h * div_term).transpose([1, 0]).unsqueeze(2).tile([1, 1, width]) + ) + + return pe + + +class FeatureEnhancer(nn.Layer): + def __init__(self): + super(FeatureEnhancer, self).__init__() + + self.multihead = MultiHeadedAttention(h=4, d_model=128, dropout=0.1) + self.mul_layernorm1 = LayerNorm(features=128) + + self.pff = PositionwiseFeedForward(128, 128) + self.mul_layernorm3 = LayerNorm(features=128) + + self.linear = nn.Linear(128, 64) + + def forward(self, conv_feature): + """ + text : (batch, seq_len, embedding_size) + global_info: (batch, embedding_size, 1, 1) + conv_feature: (batch, channel, H, W) + """ + batch = conv_feature.shape[0] + position2d = ( + positionalencoding2d(64, 16, 64) + .cast("float32") + .unsqueeze(0) + .reshape([1, 64, 1024]) + ) + position2d = position2d.tile([batch, 1, 1]) + conv_feature = paddle.concat( + [conv_feature, position2d], 1 + ) # batch, 128(64+64), 32, 128 + result = conv_feature.transpose([0, 2, 1]) + origin_result = result + result = self.mul_layernorm1( + origin_result + self.multihead(result, result, result, mask=None)[0] + ) + origin_result = result + result = self.mul_layernorm3(origin_result + self.pff(result)) + result = self.linear(result) + return result.transpose([0, 2, 1]) + + +def str_filt(str_, voc_type): + alpha_dict = { + "digit": string.digits, + "lower": string.digits + string.ascii_lowercase, + "upper": string.digits + string.ascii_letters, + "all": string.digits + string.ascii_letters + string.punctuation, + } + if voc_type == "lower": + str_ = str_.lower() + for char in str_: + if char not in alpha_dict[voc_type]: + str_ = str_.replace(char, "") + str_ = str_.lower() + return str_ + + +class TBSRN(nn.Layer): + def __init__( + self, + in_channels=3, + scale_factor=2, + width=128, + height=32, + STN=True, + srb_nums=5, + mask=False, + hidden_units=32, + infer_mode=False, + ): + super(TBSRN, self).__init__() + in_planes = 3 + if mask: + in_planes = 4 + assert math.log(scale_factor, 2) % 1 == 0 + upsample_block_num = int(math.log(scale_factor, 2)) + self.block1 = nn.Sequential( + nn.Conv2D(in_planes, 2 * hidden_units, kernel_size=9, padding=4), + nn.PReLU(), + # nn.ReLU() + ) + self.srb_nums = srb_nums + for i in range(srb_nums): + setattr(self, "block%d" % (i + 2), RecurrentResidualBlock(2 * hidden_units)) + + setattr( + self, + "block%d" % (srb_nums + 2), + nn.Sequential( + nn.Conv2D(2 * hidden_units, 2 * hidden_units, kernel_size=3, padding=1), + nn.BatchNorm2D(2 * hidden_units), + ), + ) + + # self.non_local = NonLocalBlock2D(64, 64) + block_ = [UpsampleBLock(2 * hidden_units, 2) for _ in range(upsample_block_num)] + block_.append(nn.Conv2D(2 * hidden_units, in_planes, kernel_size=9, padding=4)) + setattr(self, "block%d" % (srb_nums + 3), nn.Sequential(*block_)) + self.tps_inputsize = [height // scale_factor, width // scale_factor] + tps_outputsize = [height // scale_factor, width // scale_factor] + num_control_points = 20 + tps_margins = [0.05, 0.05] + self.stn = STN + self.out_channels = in_channels + if self.stn: + self.tps = TPSSpatialTransformer( + output_image_size=tuple(tps_outputsize), + num_control_points=num_control_points, + margins=tuple(tps_margins), + ) + + self.stn_head = STNHead( + in_channels=in_planes, + num_ctrlpoints=num_control_points, + activation="none", + ) + self.infer_mode = infer_mode + + self.english_alphabet = ( + "-0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" + ) + self.english_dict = {} + for index in range(len(self.english_alphabet)): + self.english_dict[self.english_alphabet[index]] = index + transformer = Transformer(alphabet="-0123456789abcdefghijklmnopqrstuvwxyz") + self.transformer = transformer + for param in self.transformer.parameters(): + param.trainable = False + + def label_encoder(self, label): + batch = len(label) + + length = [len(i) for i in label] + length_tensor = paddle.to_tensor(length, dtype="int64") + + max_length = max(length) + input_tensor = np.zeros((batch, max_length)) + for i in range(batch): + for j in range(length[i] - 1): + input_tensor[i][j + 1] = self.english_dict[label[i][j]] + + text_gt = [] + for i in label: + for j in i: + text_gt.append(self.english_dict[j]) + text_gt = paddle.to_tensor(text_gt, dtype="int64") + + input_tensor = paddle.to_tensor(input_tensor, dtype="int64") + return length_tensor, input_tensor, text_gt + + def forward(self, x): + output = {} + if self.infer_mode: + output["lr_img"] = x + y = x + else: + output["lr_img"] = x[0] + output["hr_img"] = x[1] + y = x[0] + if self.stn and self.training: + _, ctrl_points_x = self.stn_head(y) + y, _ = self.tps(y, ctrl_points_x) + block = {"1": self.block1(y)} + for i in range(self.srb_nums + 1): + block[str(i + 2)] = getattr(self, "block%d" % (i + 2))(block[str(i + 1)]) + + block[str(self.srb_nums + 3)] = getattr(self, "block%d" % (self.srb_nums + 3))( + (block["1"] + block[str(self.srb_nums + 2)]) + ) + + sr_img = paddle.tanh(block[str(self.srb_nums + 3)]) + output["sr_img"] = sr_img + + if self.training: + hr_img = x[1] + + # add transformer + label = [str_filt(i, "lower") + "-" for i in x[2]] + length_tensor, input_tensor, text_gt = self.label_encoder(label) + hr_pred, word_attention_map_gt, hr_correct_list = self.transformer( + hr_img, length_tensor, input_tensor + ) + sr_pred, word_attention_map_pred, sr_correct_list = self.transformer( + sr_img, length_tensor, input_tensor + ) + output["hr_img"] = hr_img + output["hr_pred"] = hr_pred + output["text_gt"] = text_gt + output["word_attention_map_gt"] = word_attention_map_gt + output["sr_pred"] = sr_pred + output["word_attention_map_pred"] = word_attention_map_pred + + return output + + +class RecurrentResidualBlock(nn.Layer): + def __init__(self, channels): + super(RecurrentResidualBlock, self).__init__() + self.conv1 = nn.Conv2D(channels, channels, kernel_size=3, padding=1) + self.bn1 = nn.BatchNorm2D(channels) + self.gru1 = GruBlock(channels, channels) + # self.prelu = nn.ReLU() + self.prelu = mish() + self.conv2 = nn.Conv2D(channels, channels, kernel_size=3, padding=1) + self.bn2 = nn.BatchNorm2D(channels) + self.gru2 = GruBlock(channels, channels) + self.feature_enhancer = FeatureEnhancer() + + for p in self.parameters(): + if p.dim() > 1: + paddle.nn.initializer.XavierUniform(p) + + def forward(self, x): + residual = self.conv1(x) + residual = self.bn1(residual) + residual = self.prelu(residual) + residual = self.conv2(residual) + residual = self.bn2(residual) + + size = residual.shape + residual = residual.reshape([size[0], size[1], -1]) + residual = self.feature_enhancer(residual) + residual = residual.reshape([size[0], size[1], size[2], size[3]]) + return x + residual diff --git a/modules/onnx_ocr_module/src/ppocr/modeling/transforms/tps.py b/modules/onnx_ocr_module/src/ppocr/modeling/transforms/tps.py new file mode 100644 index 0000000..d5681db --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/modeling/transforms/tps.py @@ -0,0 +1,321 @@ +# copyright (c) 2020 PaddlePaddle Authors. All Rights Reserve. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +""" +This code is refer from: +https://github.com/clovaai/deep-text-recognition-benchmark/blob/master/modules/transformation.py +""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import math +import paddle +from paddle import nn, ParamAttr +from paddle.nn import functional as F +import numpy as np + + +class ConvBNLayer(nn.Layer): + def __init__( + self, + in_channels, + out_channels, + kernel_size, + stride=1, + groups=1, + act=None, + name=None, + ): + super(ConvBNLayer, self).__init__() + self.conv = nn.Conv2D( + in_channels=in_channels, + out_channels=out_channels, + kernel_size=kernel_size, + stride=stride, + padding=(kernel_size - 1) // 2, + groups=groups, + weight_attr=ParamAttr(name=name + "_weights"), + bias_attr=False, + ) + bn_name = "bn_" + name + self.bn = nn.BatchNorm( + out_channels, + act=act, + param_attr=ParamAttr(name=bn_name + "_scale"), + bias_attr=ParamAttr(bn_name + "_offset"), + moving_mean_name=bn_name + "_mean", + moving_variance_name=bn_name + "_variance", + ) + + def forward(self, x): + x = self.conv(x) + x = self.bn(x) + return x + + +class LocalizationNetwork(nn.Layer): + def __init__(self, in_channels, num_fiducial, loc_lr, model_name): + super(LocalizationNetwork, self).__init__() + self.F = num_fiducial + F = num_fiducial + if model_name == "large": + num_filters_list = [64, 128, 256, 512] + fc_dim = 256 + else: + num_filters_list = [16, 32, 64, 128] + fc_dim = 64 + + self.block_list = [] + for fno in range(0, len(num_filters_list)): + num_filters = num_filters_list[fno] + name = "loc_conv%d" % fno + conv = self.add_sublayer( + name, + ConvBNLayer( + in_channels=in_channels, + out_channels=num_filters, + kernel_size=3, + act="relu", + name=name, + ), + ) + self.block_list.append(conv) + if fno == len(num_filters_list) - 1: + pool = nn.AdaptiveAvgPool2D(1) + else: + pool = nn.MaxPool2D(kernel_size=2, stride=2, padding=0) + in_channels = num_filters + self.block_list.append(pool) + name = "loc_fc1" + stdv = 1.0 / math.sqrt(num_filters_list[-1] * 1.0) + self.fc1 = nn.Linear( + in_channels, + fc_dim, + weight_attr=ParamAttr( + learning_rate=loc_lr, + name=name + "_w", + initializer=nn.initializer.Uniform(-stdv, stdv), + ), + bias_attr=ParamAttr(name=name + ".b_0"), + name=name, + ) + + # Init fc2 in LocalizationNetwork + initial_bias = self.get_initial_fiducials() + initial_bias = initial_bias.reshape(-1) + name = "loc_fc2" + param_attr = ParamAttr( + learning_rate=loc_lr, + initializer=nn.initializer.Assign(np.zeros([fc_dim, F * 2])), + name=name + "_w", + ) + bias_attr = ParamAttr( + learning_rate=loc_lr, + initializer=nn.initializer.Assign(initial_bias), + name=name + "_b", + ) + self.fc2 = nn.Linear( + fc_dim, F * 2, weight_attr=param_attr, bias_attr=bias_attr, name=name + ) + self.out_channels = F * 2 + + def forward(self, x): + """ + Estimating parameters of geometric transformation + Args: + image: input + Return: + batch_C_prime: the matrix of the geometric transformation + """ + B = x.shape[0] + i = 0 + for block in self.block_list: + x = block(x) + x = x.squeeze(axis=2).squeeze(axis=2) + x = self.fc1(x) + + x = F.relu(x) + x = self.fc2(x) + x = x.reshape(shape=[-1, self.F, 2]) + return x + + def get_initial_fiducials(self): + """see RARE paper Fig. 6 (a)""" + F = self.F + ctrl_pts_x = np.linspace(-1.0, 1.0, int(F / 2)) + ctrl_pts_y_top = np.linspace(0.0, -1.0, num=int(F / 2)) + ctrl_pts_y_bottom = np.linspace(1.0, 0.0, num=int(F / 2)) + ctrl_pts_top = np.stack([ctrl_pts_x, ctrl_pts_y_top], axis=1) + ctrl_pts_bottom = np.stack([ctrl_pts_x, ctrl_pts_y_bottom], axis=1) + initial_bias = np.concatenate([ctrl_pts_top, ctrl_pts_bottom], axis=0) + return initial_bias + + +class GridGenerator(nn.Layer): + def __init__(self, in_channels, num_fiducial): + super(GridGenerator, self).__init__() + self.eps = 1e-6 + self.F = num_fiducial + + name = "ex_fc" + initializer = nn.initializer.Constant(value=0.0) + param_attr = ParamAttr( + learning_rate=0.0, initializer=initializer, name=name + "_w" + ) + bias_attr = ParamAttr( + learning_rate=0.0, initializer=initializer, name=name + "_b" + ) + self.fc = nn.Linear( + in_channels, 6, weight_attr=param_attr, bias_attr=bias_attr, name=name + ) + + def forward(self, batch_C_prime, I_r_size): + """ + Generate the grid for the grid_sampler. + Args: + batch_C_prime: the matrix of the geometric transformation + I_r_size: the shape of the input image + Return: + batch_P_prime: the grid for the grid_sampler + """ + C = self.build_C_paddle() + P = self.build_P_paddle(I_r_size) + + inv_delta_C_tensor = self.build_inv_delta_C_paddle(C).astype("float32") + P_hat_tensor = self.build_P_hat_paddle(C, paddle.to_tensor(P)).astype("float32") + + inv_delta_C_tensor.stop_gradient = True + P_hat_tensor.stop_gradient = True + + batch_C_ex_part_tensor = self.get_expand_tensor(batch_C_prime) + + batch_C_ex_part_tensor.stop_gradient = True + + batch_C_prime_with_zeros = paddle.concat( + [batch_C_prime, batch_C_ex_part_tensor], axis=1 + ) + batch_T = paddle.matmul(inv_delta_C_tensor, batch_C_prime_with_zeros) + batch_P_prime = paddle.matmul(P_hat_tensor, batch_T) + return batch_P_prime + + def build_C_paddle(self): + """Return coordinates of fiducial points in I_r; C""" + F = self.F + ctrl_pts_x = paddle.linspace(-1.0, 1.0, int(F / 2), dtype="float64") + ctrl_pts_y_top = -1 * paddle.ones([int(F / 2)], dtype="float64") + ctrl_pts_y_bottom = paddle.ones([int(F / 2)], dtype="float64") + ctrl_pts_top = paddle.stack([ctrl_pts_x, ctrl_pts_y_top], axis=1) + ctrl_pts_bottom = paddle.stack([ctrl_pts_x, ctrl_pts_y_bottom], axis=1) + C = paddle.concat([ctrl_pts_top, ctrl_pts_bottom], axis=0) + return C # F x 2 + + def build_P_paddle(self, I_r_size): + I_r_height, I_r_width = I_r_size + I_r_grid_x = ( + paddle.arange(-I_r_width, I_r_width, 2, dtype="float64") + 1.0 + ) / paddle.to_tensor(np.array([I_r_width])).astype("float64") + + I_r_grid_y = ( + paddle.arange(-I_r_height, I_r_height, 2, dtype="float64") + 1.0 + ) / paddle.to_tensor(np.array([I_r_height])).astype("float64") + + # P: self.I_r_width x self.I_r_height x 2 + P = paddle.stack(paddle.meshgrid(I_r_grid_x, I_r_grid_y), axis=2) + P = paddle.transpose(P, perm=[1, 0, 2]) + # n (= self.I_r_width x self.I_r_height) x 2 + return P.reshape([-1, 2]) + + def build_inv_delta_C_paddle(self, C): + """Return inv_delta_C which is needed to calculate T""" + F = self.F + hat_eye = paddle.eye(F, dtype="float64") # F x F + hat_C = ( + paddle.norm(C.reshape([1, F, 2]) - C.reshape([F, 1, 2]), axis=2) + hat_eye + ) + hat_C = (hat_C**2) * paddle.log(hat_C) + delta_C = paddle.concat( # F+3 x F+3 + [ + paddle.concat( + [paddle.ones((F, 1), dtype="float64"), C, hat_C], axis=1 + ), # F x F+3 + paddle.concat( + [ + paddle.zeros((2, 3), dtype="float64"), + paddle.transpose(C, perm=[1, 0]), + ], + axis=1, + ), # 2 x F+3 + paddle.concat( + [ + paddle.zeros((1, 3), dtype="float64"), + paddle.ones((1, F), dtype="float64"), + ], + axis=1, + ), # 1 x F+3 + ], + axis=0, + ) + inv_delta_C = paddle.inverse(delta_C) + return inv_delta_C # F+3 x F+3 + + def build_P_hat_paddle(self, C, P): + F = self.F + eps = self.eps + n = P.shape[0] # n (= self.I_r_width x self.I_r_height) + # P_tile: n x 2 -> n x 1 x 2 -> n x F x 2 + P_tile = paddle.tile(paddle.unsqueeze(P, axis=1), (1, F, 1)) + C_tile = paddle.unsqueeze(C, axis=0) # 1 x F x 2 + P_diff = P_tile - C_tile # n x F x 2 + # rbf_norm: n x F + rbf_norm = paddle.norm(P_diff, p=2, axis=2, keepdim=False) + + # rbf: n x F + rbf = paddle.multiply(paddle.square(rbf_norm), paddle.log(rbf_norm + eps)) + P_hat = paddle.concat([paddle.ones((n, 1), dtype="float64"), P, rbf], axis=1) + return P_hat # n x F+3 + + def get_expand_tensor(self, batch_C_prime): + B, H, C = batch_C_prime.shape + batch_C_prime = batch_C_prime.reshape([B, H * C]) + batch_C_ex_part_tensor = self.fc(batch_C_prime) + batch_C_ex_part_tensor = batch_C_ex_part_tensor.reshape([-1, 3, 2]) + return batch_C_ex_part_tensor + + +class TPS(nn.Layer): + def __init__(self, in_channels, num_fiducial, loc_lr, model_name): + super(TPS, self).__init__() + self.loc_net = LocalizationNetwork( + in_channels, num_fiducial, loc_lr, model_name + ) + self.grid_generator = GridGenerator(self.loc_net.out_channels, num_fiducial) + self.out_channels = in_channels + + def forward(self, image): + image.stop_gradient = False + batch_C_prime = self.loc_net(image) + batch_P_prime = self.grid_generator(batch_C_prime, image.shape[2:]) + batch_P_prime = batch_P_prime.reshape([-1, image.shape[2], image.shape[3], 2]) + is_fp16 = False + if batch_P_prime.dtype != paddle.float32: + data_type = batch_P_prime.dtype + image = image.cast(paddle.float32) + batch_P_prime = batch_P_prime.cast(paddle.float32) + is_fp16 = True + batch_I_r = F.grid_sample(x=image, grid=batch_P_prime) + if is_fp16: + batch_I_r = batch_I_r.cast(data_type) + + return batch_I_r diff --git a/modules/onnx_ocr_module/src/ppocr/modeling/transforms/tps_spatial_transformer.py b/modules/onnx_ocr_module/src/ppocr/modeling/transforms/tps_spatial_transformer.py new file mode 100644 index 0000000..fc230b0 --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/modeling/transforms/tps_spatial_transformer.py @@ -0,0 +1,170 @@ +# copyright (c) 2020 PaddlePaddle Authors. All Rights Reserve. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +""" +This code is refer from: +https://github.com/ayumiymk/aster.pytorch/blob/master/lib/models/tps_spatial_transformer.py +""" +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import math +import paddle +from paddle import nn, ParamAttr +from paddle.nn import functional as F +import numpy as np +import itertools + + +def grid_sample(input, grid, canvas=None): + input.stop_gradient = False + + is_fp16 = False + if grid.dtype != paddle.float32: + data_type = grid.dtype + input = input.cast(paddle.float32) + grid = grid.cast(paddle.float32) + is_fp16 = True + output = F.grid_sample(input, grid) + if is_fp16: + output = output.cast(data_type) + grid = grid.cast(data_type) + + if canvas is None: + return output + else: + input_mask = paddle.ones(shape=input.shape) + if is_fp16: + input_mask = input_mask.cast(paddle.float32) + grid = grid.cast(paddle.float32) + output_mask = F.grid_sample(input_mask, grid) + if is_fp16: + output_mask = output_mask.cast(data_type) + padded_output = output * output_mask + canvas * (1 - output_mask) + return padded_output + + +# phi(x1, x2) = r^2 * log(r), where r = ||x1 - x2||_2 +def compute_partial_repr(input_points, control_points): + N = input_points.shape[0] + M = control_points.shape[0] + pairwise_diff = paddle.reshape(input_points, shape=[N, 1, 2]) - paddle.reshape( + control_points, shape=[1, M, 2] + ) + # original implementation, very slow + # pairwise_dist = torch.sum(pairwise_diff ** 2, dim = 2) # square of distance + pairwise_diff_square = pairwise_diff * pairwise_diff + pairwise_dist = pairwise_diff_square[:, :, 0] + pairwise_diff_square[:, :, 1] + repr_matrix = 0.5 * pairwise_dist * paddle.log(pairwise_dist) + # fix numerical error for 0 * log(0), substitute all nan with 0 + mask = np.array(repr_matrix != repr_matrix) + repr_matrix[mask] = 0 + return repr_matrix + + +# output_ctrl_pts are specified, according to our task. +def build_output_control_points(num_control_points, margins): + margin_x, margin_y = margins + num_ctrl_pts_per_side = num_control_points // 2 + ctrl_pts_x = np.linspace(margin_x, 1.0 - margin_x, num_ctrl_pts_per_side) + ctrl_pts_y_top = np.ones(num_ctrl_pts_per_side) * margin_y + ctrl_pts_y_bottom = np.ones(num_ctrl_pts_per_side) * (1.0 - margin_y) + ctrl_pts_top = np.stack([ctrl_pts_x, ctrl_pts_y_top], axis=1) + ctrl_pts_bottom = np.stack([ctrl_pts_x, ctrl_pts_y_bottom], axis=1) + output_ctrl_pts_arr = np.concatenate([ctrl_pts_top, ctrl_pts_bottom], axis=0) + output_ctrl_pts = paddle.to_tensor(output_ctrl_pts_arr) + return output_ctrl_pts + + +class TPSSpatialTransformer(nn.Layer): + def __init__(self, output_image_size=None, num_control_points=None, margins=None): + super(TPSSpatialTransformer, self).__init__() + self.output_image_size = output_image_size + self.num_control_points = num_control_points + self.margins = margins + + self.target_height, self.target_width = output_image_size + target_control_points = build_output_control_points(num_control_points, margins) + N = num_control_points + + # create padded kernel matrix + forward_kernel = paddle.zeros(shape=[N + 3, N + 3]) + target_control_partial_repr = compute_partial_repr( + target_control_points, target_control_points + ) + target_control_partial_repr = paddle.cast( + target_control_partial_repr, forward_kernel.dtype + ) + forward_kernel[:N, :N] = target_control_partial_repr + forward_kernel[:N, -3] = 1 + forward_kernel[-3, :N] = 1 + target_control_points = paddle.cast(target_control_points, forward_kernel.dtype) + forward_kernel[:N, -2:] = target_control_points + forward_kernel[-2:, :N] = paddle.transpose(target_control_points, perm=[1, 0]) + # compute inverse matrix + inverse_kernel = paddle.inverse(forward_kernel) + + # create target coordinate matrix + HW = self.target_height * self.target_width + target_coordinate = list( + itertools.product(range(self.target_height), range(self.target_width)) + ) + target_coordinate = paddle.to_tensor(target_coordinate) # HW x 2 + Y, X = paddle.split(target_coordinate, target_coordinate.shape[1], axis=1) + Y = Y / (self.target_height - 1) + X = X / (self.target_width - 1) + target_coordinate = paddle.concat( + [X, Y], axis=1 + ) # convert from (y, x) to (x, y) + target_coordinate_partial_repr = compute_partial_repr( + target_coordinate, target_control_points + ) + target_coordinate_repr = paddle.concat( + [ + target_coordinate_partial_repr, + paddle.ones(shape=[HW, 1]), + target_coordinate, + ], + axis=1, + ) + + # register precomputed matrices + self.inverse_kernel = inverse_kernel + self.padding_matrix = paddle.zeros(shape=[3, 2]) + self.target_coordinate_repr = target_coordinate_repr + self.target_control_points = target_control_points + + def forward(self, input, source_control_points): + assert source_control_points.ndimension() == 3 + assert source_control_points.shape[1] == self.num_control_points + assert source_control_points.shape[2] == 2 + batch_size = source_control_points.shape[0] + + padding_matrix = paddle.expand(self.padding_matrix, shape=[batch_size, 3, 2]) + Y = paddle.concat( + [source_control_points.astype(padding_matrix.dtype), padding_matrix], 1 + ) + mapping_matrix = paddle.matmul(self.inverse_kernel, Y) + source_coordinate = paddle.matmul(self.target_coordinate_repr, mapping_matrix) + + grid = paddle.reshape( + source_coordinate, shape=[-1, self.target_height, self.target_width, 2] + ) + grid = paddle.clip( + grid, 0, 1 + ) # the source_control_points may be out of [0, 1]. + # the input to grid_sample is normalized [-1, 1], but what we get is [0, 1] + grid = 2.0 * grid - 1.0 + output_maps = grid_sample(input, grid, canvas=None) + return output_maps, source_coordinate diff --git a/modules/onnx_ocr_module/src/ppocr/modeling/transforms/tsrn.py b/modules/onnx_ocr_module/src/ppocr/modeling/transforms/tsrn.py new file mode 100644 index 0000000..93238fa --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/modeling/transforms/tsrn.py @@ -0,0 +1,215 @@ +# copyright (c) 2020 PaddlePaddle Authors. All Rights Reserve. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +""" +This code is refer from: +https://github.com/FudanVI/FudanOCR/blob/main/text-gestalt/model/tsrn.py +""" + +import math +import paddle +import paddle.nn.functional as F +from paddle import nn +from collections import OrderedDict +import sys +import numpy as np +import warnings +import math, copy +import cv2 + +warnings.filterwarnings("ignore") + +from .tps_spatial_transformer import TPSSpatialTransformer +from .stn import STN as STN_model +from ppocr.modeling.heads.sr_rensnet_transformer import Transformer + + +class TSRN(nn.Layer): + def __init__( + self, + in_channels, + scale_factor=2, + width=128, + height=32, + STN=False, + srb_nums=5, + mask=False, + hidden_units=32, + infer_mode=False, + **kwargs, + ): + super(TSRN, self).__init__() + in_planes = 3 + if mask: + in_planes = 4 + assert math.log(scale_factor, 2) % 1 == 0 + upsample_block_num = int(math.log(scale_factor, 2)) + self.block1 = nn.Sequential( + nn.Conv2D(in_planes, 2 * hidden_units, kernel_size=9, padding=4), nn.PReLU() + ) + self.srb_nums = srb_nums + for i in range(srb_nums): + setattr(self, "block%d" % (i + 2), RecurrentResidualBlock(2 * hidden_units)) + + setattr( + self, + "block%d" % (srb_nums + 2), + nn.Sequential( + nn.Conv2D(2 * hidden_units, 2 * hidden_units, kernel_size=3, padding=1), + nn.BatchNorm2D(2 * hidden_units), + ), + ) + + block_ = [UpsampleBLock(2 * hidden_units, 2) for _ in range(upsample_block_num)] + block_.append(nn.Conv2D(2 * hidden_units, in_planes, kernel_size=9, padding=4)) + setattr(self, "block%d" % (srb_nums + 3), nn.Sequential(*block_)) + self.tps_inputsize = [height // scale_factor, width // scale_factor] + tps_outputsize = [height // scale_factor, width // scale_factor] + num_control_points = 20 + tps_margins = [0.05, 0.05] + self.stn = STN + if self.stn: + self.tps = TPSSpatialTransformer( + output_image_size=tuple(tps_outputsize), + num_control_points=num_control_points, + margins=tuple(tps_margins), + ) + + self.stn_head = STN_model( + in_channels=in_planes, + num_ctrlpoints=num_control_points, + activation="none", + ) + self.out_channels = in_channels + + self.r34_transformer = Transformer() + for param in self.r34_transformer.parameters(): + param.trainable = False + self.infer_mode = infer_mode + + def forward(self, x): + output = {} + if self.infer_mode: + output["lr_img"] = x + y = x + else: + output["lr_img"] = x[0] + output["hr_img"] = x[1] + y = x[0] + if self.stn and self.training: + _, ctrl_points_x = self.stn_head(y) + y, _ = self.tps(y, ctrl_points_x) + block = {"1": self.block1(y)} + for i in range(self.srb_nums + 1): + block[str(i + 2)] = getattr(self, "block%d" % (i + 2))(block[str(i + 1)]) + + block[str(self.srb_nums + 3)] = getattr(self, "block%d" % (self.srb_nums + 3))( + (block["1"] + block[str(self.srb_nums + 2)]) + ) + + sr_img = paddle.tanh(block[str(self.srb_nums + 3)]) + + output["sr_img"] = sr_img + + if self.training: + hr_img = x[1] + length = x[2] + input_tensor = x[3] + + # add transformer + sr_pred, word_attention_map_pred, _ = self.r34_transformer( + sr_img, length, input_tensor + ) + + hr_pred, word_attention_map_gt, _ = self.r34_transformer( + hr_img, length, input_tensor + ) + + output["hr_img"] = hr_img + output["hr_pred"] = hr_pred + output["word_attention_map_gt"] = word_attention_map_gt + output["sr_pred"] = sr_pred + output["word_attention_map_pred"] = word_attention_map_pred + + return output + + +class RecurrentResidualBlock(nn.Layer): + def __init__(self, channels): + super(RecurrentResidualBlock, self).__init__() + self.conv1 = nn.Conv2D(channels, channels, kernel_size=3, padding=1) + self.bn1 = nn.BatchNorm2D(channels) + self.gru1 = GruBlock(channels, channels) + self.prelu = mish() + self.conv2 = nn.Conv2D(channels, channels, kernel_size=3, padding=1) + self.bn2 = nn.BatchNorm2D(channels) + self.gru2 = GruBlock(channels, channels) + + def forward(self, x): + residual = self.conv1(x) + residual = self.bn1(residual) + residual = self.prelu(residual) + residual = self.conv2(residual) + residual = self.bn2(residual) + residual = self.gru1(residual.transpose([0, 1, 3, 2])).transpose([0, 1, 3, 2]) + + return self.gru2(x + residual) + + +class UpsampleBLock(nn.Layer): + def __init__(self, in_channels, up_scale): + super(UpsampleBLock, self).__init__() + self.conv = nn.Conv2D( + in_channels, in_channels * up_scale**2, kernel_size=3, padding=1 + ) + + self.pixel_shuffle = nn.PixelShuffle(up_scale) + self.prelu = mish() + + def forward(self, x): + x = self.conv(x) + x = self.pixel_shuffle(x) + x = self.prelu(x) + return x + + +class mish(nn.Layer): + def __init__( + self, + ): + super(mish, self).__init__() + self.activated = True + + def forward(self, x): + if self.activated: + x = x * (paddle.tanh(F.softplus(x))) + return x + + +class GruBlock(nn.Layer): + def __init__(self, in_channels, out_channels): + super(GruBlock, self).__init__() + assert out_channels % 2 == 0 + self.conv1 = nn.Conv2D(in_channels, out_channels, kernel_size=1, padding=0) + self.gru = nn.GRU(out_channels, out_channels // 2, direction="bidirectional") + + def forward(self, x): + # x: b, c, w, h + x = self.conv1(x) + x = x.transpose([0, 2, 3, 1]) # b, w, h, c + batch_size, w, h, c = x.shape + x = x.reshape([-1, h, c]) # b*w, h, c + x, _ = self.gru(x) + x = x.reshape([-1, w, h, c]) + x = x.transpose([0, 3, 1, 2]) + return x diff --git a/modules/onnx_ocr_module/src/ppocr/optimizer/__init__.py b/modules/onnx_ocr_module/src/ppocr/optimizer/__init__.py new file mode 100644 index 0000000..a191a4b --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/optimizer/__init__.py @@ -0,0 +1,66 @@ +# copyright (c) 2020 PaddlePaddle Authors. All Rights Reserve. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function +from __future__ import unicode_literals +import copy +import paddle + +__all__ = ["build_optimizer"] + + +def build_lr_scheduler(lr_config, epochs, step_each_epoch): + from . import learning_rate + + lr_config.update({"epochs": epochs, "step_each_epoch": step_each_epoch}) + lr_name = lr_config.pop("name", "Const") + lr = getattr(learning_rate, lr_name)(**lr_config)() + return lr + + +def build_optimizer(config, epochs, step_each_epoch, model): + from . import regularizer, optimizer + + config = copy.deepcopy(config) + # step1 build lr + lr = build_lr_scheduler(config.pop("lr"), epochs, step_each_epoch) + + # step2 build regularization + if "regularizer" in config and config["regularizer"] is not None: + reg_config = config.pop("regularizer") + reg_name = reg_config.pop("name") + if not hasattr(regularizer, reg_name): + reg_name += "Decay" + reg = getattr(regularizer, reg_name)(**reg_config)() + elif "weight_decay" in config: + reg = config.pop("weight_decay") + else: + reg = None + + # step3 build optimizer + optim_name = config.pop("name") + if "clip_norm" in config: + clip_norm = config.pop("clip_norm") + grad_clip = paddle.nn.ClipGradByNorm(clip_norm=clip_norm) + elif "clip_norm_global" in config: + clip_norm = config.pop("clip_norm_global") + grad_clip = paddle.nn.ClipGradByGlobalNorm(clip_norm=clip_norm) + else: + grad_clip = None + optim = getattr(optimizer, optim_name)( + learning_rate=lr, weight_decay=reg, grad_clip=grad_clip, **config + ) + return optim(model), lr diff --git a/modules/onnx_ocr_module/src/ppocr/optimizer/learning_rate.py b/modules/onnx_ocr_module/src/ppocr/optimizer/learning_rate.py new file mode 100644 index 0000000..257dd3d --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/optimizer/learning_rate.py @@ -0,0 +1,503 @@ +# copyright (c) 2020 PaddlePaddle Authors. All Rights Reserve. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function +from __future__ import unicode_literals + +from paddle.optimizer import lr +from .lr_scheduler import CyclicalCosineDecay, OneCycleDecay, TwoStepCosineDecay + + +class Linear(object): + """ + Linear learning rate decay + Args: + lr (float): The initial learning rate. It is a python float number. + epochs(int): The decay step size. It determines the decay cycle. + end_lr(float, optional): The minimum final learning rate. Default: 0.0001. + power(float, optional): Power of polynomial. Default: 1.0. + last_epoch (int, optional): The index of last epoch. Can be set to restart training. Default: -1, means initial learning rate. + """ + + def __init__( + self, + learning_rate, + epochs, + step_each_epoch, + end_lr=0.0, + power=1.0, + warmup_epoch=0, + last_epoch=-1, + **kwargs, + ): + super(Linear, self).__init__() + self.learning_rate = learning_rate + self.epochs = epochs * step_each_epoch + self.end_lr = end_lr + self.power = power + self.last_epoch = last_epoch + self.warmup_epoch = round(warmup_epoch * step_each_epoch) + + def __call__(self): + learning_rate = lr.PolynomialDecay( + learning_rate=self.learning_rate, + decay_steps=self.epochs, + end_lr=self.end_lr, + power=self.power, + last_epoch=self.last_epoch, + ) + if self.warmup_epoch > 0: + learning_rate = lr.LinearWarmup( + learning_rate=learning_rate, + warmup_steps=self.warmup_epoch, + start_lr=0.0, + end_lr=self.learning_rate, + last_epoch=self.last_epoch, + ) + return learning_rate + + +class Cosine(object): + """ + Cosine learning rate decay + lr = 0.05 * (math.cos(epoch * (math.pi / epochs)) + 1) + Args: + lr(float): initial learning rate + step_each_epoch(int): steps each epoch + epochs(int): total training epochs + last_epoch (int, optional): The index of last epoch. Can be set to restart training. Default: -1, means initial learning rate. + """ + + def __init__( + self, + learning_rate, + step_each_epoch, + epochs, + warmup_epoch=0, + last_epoch=-1, + **kwargs, + ): + super(Cosine, self).__init__() + self.learning_rate = learning_rate + self.T_max = step_each_epoch * epochs + self.last_epoch = last_epoch + self.warmup_epoch = round(warmup_epoch * step_each_epoch) + + def __call__(self): + learning_rate = lr.CosineAnnealingDecay( + learning_rate=self.learning_rate, + T_max=self.T_max, + last_epoch=self.last_epoch, + ) + if self.warmup_epoch > 0: + learning_rate = lr.LinearWarmup( + learning_rate=learning_rate, + warmup_steps=self.warmup_epoch, + start_lr=0.0, + end_lr=self.learning_rate, + last_epoch=self.last_epoch, + ) + return learning_rate + + +class LinearWarmupCosine(object): + """ + LinearWarmupCosine learning rate decay + Args: + learning_rate(float): initial learning rate + step_each_epoch(int): steps each epoch + epochs(int): total training epochs + start_lr (float): Initial learning rate of warm up. + min_lr (float): Minimum learning rate in CosineAnnealingDecay. + last_epoch (int, optional): The index of last epoch. Can be set to restart training. Default: -1, means initial learning rate. + """ + + def __init__( + self, + learning_rate, + step_each_epoch, + epochs, + warmup_steps=5000, + start_lr=1e-5, + min_lr=1e-8, + last_epoch=-1, + **kwargs, + ): + super(LinearWarmupCosine, self).__init__() + self.learning_rate = float(learning_rate) + self.T_max = step_each_epoch * epochs + self.last_epoch = last_epoch + self.warmup_steps = warmup_steps + self.start_lr = float(start_lr) + self.min_lr = float(min_lr) + + def __call__(self): + learning_rate = lr.CosineAnnealingDecay( + learning_rate=self.learning_rate, + T_max=self.T_max, + eta_min=self.min_lr, + last_epoch=self.last_epoch, + ) + if self.warmup_steps > 0: + learning_rate = lr.LinearWarmup( + learning_rate=learning_rate, + warmup_steps=self.warmup_steps, + start_lr=self.start_lr, + end_lr=self.learning_rate, + last_epoch=self.last_epoch, + ) + return learning_rate + + +class Step(object): + """ + Piecewise learning rate decay + Args: + step_each_epoch(int): steps each epoch + learning_rate (float): The initial learning rate. It is a python float number. + step_size (int): the interval to update. + gamma (float, optional): The Ratio that the learning rate will be reduced. ``new_lr = origin_lr * gamma`` . + It should be less than 1.0. Default: 0.1. + last_epoch (int, optional): The index of last epoch. Can be set to restart training. Default: -1, means initial learning rate. + """ + + def __init__( + self, + learning_rate, + step_size, + step_each_epoch, + gamma, + warmup_epoch=0, + last_epoch=-1, + **kwargs, + ): + super(Step, self).__init__() + self.step_size = step_each_epoch * step_size + self.learning_rate = learning_rate + self.gamma = gamma + self.last_epoch = last_epoch + self.warmup_epoch = round(warmup_epoch * step_each_epoch) + + def __call__(self): + learning_rate = lr.StepDecay( + learning_rate=self.learning_rate, + step_size=self.step_size, + gamma=self.gamma, + last_epoch=self.last_epoch, + ) + if self.warmup_epoch > 0: + learning_rate = lr.LinearWarmup( + learning_rate=learning_rate, + warmup_steps=self.warmup_epoch, + start_lr=0.0, + end_lr=self.learning_rate, + last_epoch=self.last_epoch, + ) + return learning_rate + + +class Piecewise(object): + """ + Piecewise learning rate decay + Args: + boundaries(list): A list of steps numbers. The type of element in the list is python int. + values(list): A list of learning rate values that will be picked during different epoch boundaries. + The type of element in the list is python float. + last_epoch (int, optional): The index of last epoch. Can be set to restart training. Default: -1, means initial learning rate. + """ + + def __init__( + self, + step_each_epoch, + decay_epochs, + values, + warmup_epoch=0, + last_epoch=-1, + **kwargs, + ): + super(Piecewise, self).__init__() + self.boundaries = [step_each_epoch * e for e in decay_epochs] + self.values = values + self.last_epoch = last_epoch + self.warmup_epoch = round(warmup_epoch * step_each_epoch) + + def __call__(self): + learning_rate = lr.PiecewiseDecay( + boundaries=self.boundaries, values=self.values, last_epoch=self.last_epoch + ) + if self.warmup_epoch > 0: + learning_rate = lr.LinearWarmup( + learning_rate=learning_rate, + warmup_steps=self.warmup_epoch, + start_lr=0.0, + end_lr=self.values[0], + last_epoch=self.last_epoch, + ) + return learning_rate + + +class CyclicalCosine(object): + """ + Cyclical cosine learning rate decay + Args: + learning_rate(float): initial learning rate + step_each_epoch(int): steps each epoch + epochs(int): total training epochs + cycle(int): period of the cosine learning rate + last_epoch (int, optional): The index of last epoch. Can be set to restart training. Default: -1, means initial learning rate. + """ + + def __init__( + self, + learning_rate, + step_each_epoch, + epochs, + cycle, + warmup_epoch=0, + last_epoch=-1, + **kwargs, + ): + super(CyclicalCosine, self).__init__() + self.learning_rate = learning_rate + self.T_max = step_each_epoch * epochs + self.last_epoch = last_epoch + self.warmup_epoch = round(warmup_epoch * step_each_epoch) + self.cycle = round(cycle * step_each_epoch) + + def __call__(self): + learning_rate = CyclicalCosineDecay( + learning_rate=self.learning_rate, + T_max=self.T_max, + cycle=self.cycle, + last_epoch=self.last_epoch, + ) + if self.warmup_epoch > 0: + learning_rate = lr.LinearWarmup( + learning_rate=learning_rate, + warmup_steps=self.warmup_epoch, + start_lr=0.0, + end_lr=self.learning_rate, + last_epoch=self.last_epoch, + ) + return learning_rate + + +class OneCycle(object): + """ + One Cycle learning rate decay + Args: + max_lr(float): Upper learning rate boundaries + epochs(int): total training epochs + step_each_epoch(int): steps each epoch + anneal_strategy(str): {‘cos’, ‘linear’} Specifies the annealing strategy: “cos” for cosine annealing, “linear” for linear annealing. + Default: ‘cos’ + three_phase(bool): If True, use a third phase of the schedule to annihilate the learning rate according to ‘final_div_factor’ + instead of modifying the second phase (the first two phases will be symmetrical about the step indicated by ‘pct_start’). + last_epoch (int, optional): The index of last epoch. Can be set to restart training. Default: -1, means initial learning rate. + """ + + def __init__( + self, + max_lr, + epochs, + step_each_epoch, + anneal_strategy="cos", + three_phase=False, + warmup_epoch=0, + last_epoch=-1, + **kwargs, + ): + super(OneCycle, self).__init__() + self.max_lr = max_lr + self.epochs = epochs + self.steps_per_epoch = step_each_epoch + self.anneal_strategy = anneal_strategy + self.three_phase = three_phase + self.last_epoch = last_epoch + self.warmup_epoch = round(warmup_epoch * step_each_epoch) + + def __call__(self): + learning_rate = OneCycleDecay( + max_lr=self.max_lr, + epochs=self.epochs, + steps_per_epoch=self.steps_per_epoch, + anneal_strategy=self.anneal_strategy, + three_phase=self.three_phase, + last_epoch=self.last_epoch, + ) + if self.warmup_epoch > 0: + learning_rate = lr.LinearWarmup( + learning_rate=learning_rate, + warmup_steps=self.warmup_epoch, + start_lr=0.0, + end_lr=self.max_lr, + last_epoch=self.last_epoch, + ) + return learning_rate + + +class Const(object): + """ + Const learning rate decay + Args: + learning_rate(float): initial learning rate + step_each_epoch(int): steps each epoch + last_epoch (int, optional): The index of last epoch. Can be set to restart training. Default: -1, means initial learning rate. + """ + + def __init__( + self, learning_rate, step_each_epoch, warmup_epoch=0, last_epoch=-1, **kwargs + ): + super(Const, self).__init__() + self.learning_rate = learning_rate + self.last_epoch = last_epoch + self.warmup_epoch = round(warmup_epoch * step_each_epoch) + + def __call__(self): + learning_rate = self.learning_rate + if self.warmup_epoch > 0: + learning_rate = lr.LinearWarmup( + learning_rate=learning_rate, + warmup_steps=self.warmup_epoch, + start_lr=0.0, + end_lr=self.learning_rate, + last_epoch=self.last_epoch, + ) + return learning_rate + + +class DecayLearningRate(object): + """ + DecayLearningRate learning rate decay + new_lr = (lr - end_lr) * (1 - epoch/decay_steps)**power + end_lr + Args: + learning_rate(float): initial learning rate + step_each_epoch(int): steps each epoch + epochs(int): total training epochs + factor(float): Power of polynomial, should greater than 0.0 to get learning rate decay. Default: 0.9 + end_lr(float): The minimum final learning rate. Default: 0.0. + """ + + def __init__( + self, learning_rate, step_each_epoch, epochs, factor=0.9, end_lr=0, **kwargs + ): + super(DecayLearningRate, self).__init__() + self.learning_rate = learning_rate + self.epochs = epochs + 1 + self.factor = factor + self.end_lr = 0 + self.decay_steps = step_each_epoch * epochs + + def __call__(self): + learning_rate = lr.PolynomialDecay( + learning_rate=self.learning_rate, + decay_steps=self.decay_steps, + power=self.factor, + end_lr=self.end_lr, + ) + return learning_rate + + +class MultiStepDecay(object): + """ + Piecewise learning rate decay + Args: + step_each_epoch(int): steps each epoch + learning_rate (float): The initial learning rate. It is a python float number. + step_size (int): the interval to update. + gamma (float, optional): The Ratio that the learning rate will be reduced. ``new_lr = origin_lr * gamma`` . + It should be less than 1.0. Default: 0.1. + last_epoch (int, optional): The index of last epoch. Can be set to restart training. Default: -1, means initial learning rate. + """ + + def __init__( + self, + learning_rate, + milestones, + step_each_epoch, + gamma, + warmup_epoch=0, + last_epoch=-1, + **kwargs, + ): + super(MultiStepDecay, self).__init__() + self.milestones = [step_each_epoch * e for e in milestones] + self.learning_rate = learning_rate + self.gamma = gamma + self.last_epoch = last_epoch + self.warmup_epoch = round(warmup_epoch * step_each_epoch) + + def __call__(self): + learning_rate = lr.MultiStepDecay( + learning_rate=self.learning_rate, + milestones=self.milestones, + gamma=self.gamma, + last_epoch=self.last_epoch, + ) + if self.warmup_epoch > 0: + learning_rate = lr.LinearWarmup( + learning_rate=learning_rate, + warmup_steps=self.warmup_epoch, + start_lr=0.0, + end_lr=self.learning_rate, + last_epoch=self.last_epoch, + ) + return learning_rate + + +class TwoStepCosine(object): + """ + Cosine learning rate decay + lr = 0.05 * (math.cos(epoch * (math.pi / epochs)) + 1) + Args: + lr(float): initial learning rate + step_each_epoch(int): steps each epoch + epochs(int): total training epochs + last_epoch (int, optional): The index of last epoch. Can be set to restart training. Default: -1, means initial learning rate. + """ + + def __init__( + self, + learning_rate, + step_each_epoch, + epochs, + warmup_epoch=0, + last_epoch=-1, + **kwargs, + ): + super(TwoStepCosine, self).__init__() + self.learning_rate = learning_rate + self.T_max1 = step_each_epoch * 200 + self.T_max2 = step_each_epoch * epochs + self.last_epoch = last_epoch + self.warmup_epoch = round(warmup_epoch * step_each_epoch) + + def __call__(self): + learning_rate = TwoStepCosineDecay( + learning_rate=self.learning_rate, + T_max1=self.T_max1, + T_max2=self.T_max2, + last_epoch=self.last_epoch, + ) + if self.warmup_epoch > 0: + learning_rate = lr.LinearWarmup( + learning_rate=learning_rate, + warmup_steps=self.warmup_epoch, + start_lr=0.0, + end_lr=self.learning_rate, + last_epoch=self.last_epoch, + ) + return learning_rate diff --git a/modules/onnx_ocr_module/src/ppocr/optimizer/lr_scheduler.py b/modules/onnx_ocr_module/src/ppocr/optimizer/lr_scheduler.py new file mode 100644 index 0000000..1393e40 --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/optimizer/lr_scheduler.py @@ -0,0 +1,240 @@ +# copyright (c) 2020 PaddlePaddle Authors. All Rights Reserve. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import math +from paddle.optimizer.lr import LRScheduler + + +class CyclicalCosineDecay(LRScheduler): + def __init__( + self, learning_rate, T_max, cycle=1, last_epoch=-1, eta_min=0.0, verbose=False + ): + """ + Cyclical cosine learning rate decay + A learning rate which can be referred in https://arxiv.org/pdf/2012.12645.pdf + Args: + learning rate(float): learning rate + T_max(int): maximum epoch num + cycle(int): period of the cosine decay + last_epoch (int, optional): The index of last epoch. Can be set to restart training. Default: -1, means initial learning rate. + eta_min(float): minimum learning rate during training + verbose(bool): whether to print learning rate for each epoch + """ + super(CyclicalCosineDecay, self).__init__(learning_rate, last_epoch, verbose) + self.cycle = cycle + self.eta_min = eta_min + + def get_lr(self): + if self.last_epoch == 0: + return self.base_lr + reletive_epoch = self.last_epoch % self.cycle + lr = self.eta_min + 0.5 * (self.base_lr - self.eta_min) * ( + 1 + math.cos(math.pi * reletive_epoch / self.cycle) + ) + return lr + + +class OneCycleDecay(LRScheduler): + """ + One Cycle learning rate decay + A learning rate which can be referred in https://arxiv.org/abs/1708.07120 + Code referred in https://pytorch.org/docs/stable/_modules/torch/optim/lr_scheduler.html#OneCycleLR + """ + + def __init__( + self, + max_lr, + epochs=None, + steps_per_epoch=None, + pct_start=0.3, + anneal_strategy="cos", + div_factor=25.0, + final_div_factor=1e4, + three_phase=False, + last_epoch=-1, + verbose=False, + ): + # Validate total_steps + if epochs <= 0 or not isinstance(epochs, int): + raise ValueError( + "Expected positive integer epochs, but got {}".format(epochs) + ) + if steps_per_epoch <= 0 or not isinstance(steps_per_epoch, int): + raise ValueError( + "Expected positive integer steps_per_epoch, but got {}".format( + steps_per_epoch + ) + ) + self.total_steps = epochs * steps_per_epoch + + self.max_lr = max_lr + self.initial_lr = self.max_lr / div_factor + self.min_lr = self.initial_lr / final_div_factor + + if three_phase: + self._schedule_phases = [ + { + "end_step": float(pct_start * self.total_steps) - 1, + "start_lr": self.initial_lr, + "end_lr": self.max_lr, + }, + { + "end_step": float(2 * pct_start * self.total_steps) - 2, + "start_lr": self.max_lr, + "end_lr": self.initial_lr, + }, + { + "end_step": self.total_steps - 1, + "start_lr": self.initial_lr, + "end_lr": self.min_lr, + }, + ] + else: + self._schedule_phases = [ + { + "end_step": float(pct_start * self.total_steps) - 1, + "start_lr": self.initial_lr, + "end_lr": self.max_lr, + }, + { + "end_step": self.total_steps - 1, + "start_lr": self.max_lr, + "end_lr": self.min_lr, + }, + ] + + # Validate pct_start + if pct_start < 0 or pct_start > 1 or not isinstance(pct_start, float): + raise ValueError( + "Expected float between 0 and 1 pct_start, but got {}".format(pct_start) + ) + + # Validate anneal_strategy + if anneal_strategy not in ["cos", "linear"]: + raise ValueError( + "anneal_strategy must by one of 'cos' or 'linear', instead got {}".format( + anneal_strategy + ) + ) + elif anneal_strategy == "cos": + self.anneal_func = self._annealing_cos + elif anneal_strategy == "linear": + self.anneal_func = self._annealing_linear + + super(OneCycleDecay, self).__init__(max_lr, last_epoch, verbose) + + def _annealing_cos(self, start, end, pct): + "Cosine anneal from `start` to `end` as pct goes from 0.0 to 1.0." + cos_out = math.cos(math.pi * pct) + 1 + return end + (start - end) / 2.0 * cos_out + + def _annealing_linear(self, start, end, pct): + "Linearly anneal from `start` to `end` as pct goes from 0.0 to 1.0." + return (end - start) * pct + start + + def get_lr(self): + computed_lr = 0.0 + step_num = self.last_epoch + + if step_num > self.total_steps: + raise ValueError( + "Tried to step {} times. The specified number of total steps is {}".format( + step_num + 1, self.total_steps + ) + ) + start_step = 0 + for i, phase in enumerate(self._schedule_phases): + end_step = phase["end_step"] + if step_num <= end_step or i == len(self._schedule_phases) - 1: + pct = (step_num - start_step) / (end_step - start_step) + computed_lr = self.anneal_func(phase["start_lr"], phase["end_lr"], pct) + break + start_step = phase["end_step"] + + return computed_lr + + +class TwoStepCosineDecay(LRScheduler): + def __init__( + self, learning_rate, T_max1, T_max2, eta_min=0, last_epoch=-1, verbose=False + ): + if not isinstance(T_max1, int): + raise TypeError( + "The type of 'T_max1' in 'CosineAnnealingDecay' must be 'int', but received %s." + % type(T_max1) + ) + if not isinstance(T_max2, int): + raise TypeError( + "The type of 'T_max2' in 'CosineAnnealingDecay' must be 'int', but received %s." + % type(T_max2) + ) + if not isinstance(eta_min, (float, int)): + raise TypeError( + "The type of 'eta_min' in 'CosineAnnealingDecay' must be 'float, int', but received %s." + % type(eta_min) + ) + assert T_max1 > 0 and isinstance( + T_max1, int + ), " 'T_max1' must be a positive integer." + assert T_max2 > 0 and isinstance( + T_max2, int + ), " 'T_max1' must be a positive integer." + self.T_max1 = T_max1 + self.T_max2 = T_max2 + self.eta_min = float(eta_min) + super(TwoStepCosineDecay, self).__init__(learning_rate, last_epoch, verbose) + + def get_lr(self): + if self.last_epoch <= self.T_max1: + if self.last_epoch == 0: + return self.base_lr + elif (self.last_epoch - 1 - self.T_max1) % (2 * self.T_max1) == 0: + return ( + self.last_lr + + (self.base_lr - self.eta_min) + * (1 - math.cos(math.pi / self.T_max1)) + / 2 + ) + + return (1 + math.cos(math.pi * self.last_epoch / self.T_max1)) / ( + 1 + math.cos(math.pi * (self.last_epoch - 1) / self.T_max1) + ) * (self.last_lr - self.eta_min) + self.eta_min + else: + if (self.last_epoch - 1 - self.T_max2) % (2 * self.T_max2) == 0: + return ( + self.last_lr + + (self.base_lr - self.eta_min) + * (1 - math.cos(math.pi / self.T_max2)) + / 2 + ) + + return (1 + math.cos(math.pi * self.last_epoch / self.T_max2)) / ( + 1 + math.cos(math.pi * (self.last_epoch - 1) / self.T_max2) + ) * (self.last_lr - self.eta_min) + self.eta_min + + def _get_closed_form_lr(self): + if self.last_epoch <= self.T_max1: + return ( + self.eta_min + + (self.base_lr - self.eta_min) + * (1 + math.cos(math.pi * self.last_epoch / self.T_max1)) + / 2 + ) + else: + return ( + self.eta_min + + (self.base_lr - self.eta_min) + * (1 + math.cos(math.pi * self.last_epoch / self.T_max2)) + / 2 + ) diff --git a/modules/onnx_ocr_module/src/ppocr/optimizer/optimizer.py b/modules/onnx_ocr_module/src/ppocr/optimizer/optimizer.py new file mode 100644 index 0000000..d7f78a5 --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/optimizer/optimizer.py @@ -0,0 +1,292 @@ +# copyright (c) 2020 PaddlePaddle Authors. All Rights Reserve. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function +from __future__ import unicode_literals + +from paddle import optimizer as optim + + +class Momentum(object): + """ + Simple Momentum optimizer with velocity state. + Args: + learning_rate (float|Variable) - The learning rate used to update parameters. + Can be a float value or a Variable with one float value as data element. + momentum (float) - Momentum factor. + regularization (WeightDecayRegularizer, optional) - The strategy of regularization. + """ + + def __init__( + self, learning_rate, momentum, weight_decay=None, grad_clip=None, **args + ): + super(Momentum, self).__init__() + self.learning_rate = learning_rate + self.momentum = momentum + self.weight_decay = weight_decay + self.grad_clip = grad_clip + + def __call__(self, model): + train_params = [ + param for param in model.parameters() if param.trainable is True + ] + opt = optim.Momentum( + learning_rate=self.learning_rate, + momentum=self.momentum, + weight_decay=self.weight_decay, + grad_clip=self.grad_clip, + parameters=train_params, + ) + return opt + + +class Adam(object): + def __init__( + self, + learning_rate=0.001, + beta1=0.9, + beta2=0.999, + epsilon=1e-08, + parameter_list=None, + weight_decay=None, + grad_clip=None, + name=None, + lazy_mode=False, + **kwargs, + ): + self.learning_rate = learning_rate + self.beta1 = beta1 + self.beta2 = beta2 + self.epsilon = epsilon + self.parameter_list = parameter_list + self.learning_rate = learning_rate + self.weight_decay = weight_decay + self.grad_clip = grad_clip + self.name = name + self.lazy_mode = lazy_mode + self.group_lr = kwargs.get("group_lr", False) + self.training_step = kwargs.get("training_step", None) + + def __call__(self, model): + if self.group_lr: + if self.training_step == "LF_2": + import paddle + + if isinstance(model, paddle.DataParallel): # multi gpu + mlm = model._layers.head.MLM_VRM.MLM.parameters() + pre_mlm_pp = ( + model._layers.head.MLM_VRM.Prediction.pp_share.parameters() + ) + pre_mlm_w = ( + model._layers.head.MLM_VRM.Prediction.w_share.parameters() + ) + else: # single gpu + mlm = model.head.MLM_VRM.MLM.parameters() + pre_mlm_pp = model.head.MLM_VRM.Prediction.pp_share.parameters() + pre_mlm_w = model.head.MLM_VRM.Prediction.w_share.parameters() + + total = [] + for param in mlm: + total.append(id(param)) + for param in pre_mlm_pp: + total.append(id(param)) + for param in pre_mlm_w: + total.append(id(param)) + + group_base_params = [ + param for param in model.parameters() if id(param) in total + ] + group_small_params = [ + param for param in model.parameters() if id(param) not in total + ] + train_params = [ + {"params": group_base_params}, + { + "params": group_small_params, + "learning_rate": self.learning_rate.values[0] * 0.1, + }, + ] + + else: + print("group lr currently only support VisionLAN in LF_2 training step") + train_params = [ + param for param in model.parameters() if param.trainable is True + ] + else: + train_params = [ + param for param in model.parameters() if param.trainable is True + ] + + opt = optim.Adam( + learning_rate=self.learning_rate, + beta1=self.beta1, + beta2=self.beta2, + epsilon=self.epsilon, + weight_decay=self.weight_decay, + grad_clip=self.grad_clip, + name=self.name, + lazy_mode=self.lazy_mode, + parameters=train_params, + ) + return opt + + +class RMSProp(object): + """ + Root Mean Squared Propagation (RMSProp) is an unpublished, adaptive learning rate method. + Args: + learning_rate (float|Variable) - The learning rate used to update parameters. + Can be a float value or a Variable with one float value as data element. + momentum (float) - Momentum factor. + rho (float) - rho value in equation. + epsilon (float) - avoid division by zero, default is 1e-6. + regularization (WeightDecayRegularizer, optional) - The strategy of regularization. + """ + + def __init__( + self, + learning_rate, + momentum=0.0, + rho=0.95, + epsilon=1e-6, + weight_decay=None, + grad_clip=None, + **args, + ): + super(RMSProp, self).__init__() + self.learning_rate = learning_rate + self.momentum = momentum + self.rho = rho + self.epsilon = epsilon + self.weight_decay = weight_decay + self.grad_clip = grad_clip + + def __call__(self, model): + train_params = [ + param for param in model.parameters() if param.trainable is True + ] + opt = optim.RMSProp( + learning_rate=self.learning_rate, + momentum=self.momentum, + rho=self.rho, + epsilon=self.epsilon, + weight_decay=self.weight_decay, + grad_clip=self.grad_clip, + parameters=train_params, + ) + return opt + + +class Adadelta(object): + def __init__( + self, + learning_rate=0.001, + epsilon=1e-08, + rho=0.95, + parameter_list=None, + weight_decay=None, + grad_clip=None, + name=None, + **kwargs, + ): + self.learning_rate = learning_rate + self.epsilon = epsilon + self.rho = rho + self.parameter_list = parameter_list + self.learning_rate = learning_rate + self.weight_decay = weight_decay + self.grad_clip = grad_clip + self.name = name + + def __call__(self, model): + train_params = [ + param for param in model.parameters() if param.trainable is True + ] + opt = optim.Adadelta( + learning_rate=self.learning_rate, + epsilon=self.epsilon, + rho=self.rho, + weight_decay=self.weight_decay, + grad_clip=self.grad_clip, + name=self.name, + parameters=train_params, + ) + return opt + + +class AdamW(object): + def __init__( + self, + learning_rate=0.001, + beta1=0.9, + beta2=0.999, + epsilon=1e-8, + weight_decay=0.01, + multi_precision=False, + grad_clip=None, + no_weight_decay_name=None, + one_dim_param_no_weight_decay=False, + name=None, + lazy_mode=False, + **args, + ): + super().__init__() + self.learning_rate = learning_rate + self.beta1 = beta1 + self.beta2 = beta2 + self.epsilon = epsilon + self.grad_clip = grad_clip + self.weight_decay = 0.01 if weight_decay is None else weight_decay + self.grad_clip = grad_clip + self.name = name + self.lazy_mode = lazy_mode + self.multi_precision = multi_precision + self.no_weight_decay_name_list = ( + no_weight_decay_name.split() if no_weight_decay_name else [] + ) + self.one_dim_param_no_weight_decay = one_dim_param_no_weight_decay + + def __call__(self, model): + parameters = [param for param in model.parameters() if param.trainable is True] + + self.no_weight_decay_param_name_list = [ + p.name + for n, p in model.named_parameters() + if any(nd in n for nd in self.no_weight_decay_name_list) + ] + + if self.one_dim_param_no_weight_decay: + self.no_weight_decay_param_name_list += [ + p.name for n, p in model.named_parameters() if len(p.shape) == 1 + ] + + opt = optim.AdamW( + learning_rate=self.learning_rate, + beta1=self.beta1, + beta2=self.beta2, + epsilon=self.epsilon, + parameters=parameters, + weight_decay=self.weight_decay, + multi_precision=self.multi_precision, + grad_clip=self.grad_clip, + name=self.name, + lazy_mode=self.lazy_mode, + apply_decay_param_fun=self._apply_decay_param_fun, + ) + return opt + + def _apply_decay_param_fun(self, name): + return name not in self.no_weight_decay_param_name_list diff --git a/modules/onnx_ocr_module/src/ppocr/optimizer/regularizer.py b/modules/onnx_ocr_module/src/ppocr/optimizer/regularizer.py new file mode 100644 index 0000000..740ad1c --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/optimizer/regularizer.py @@ -0,0 +1,51 @@ +# copyright (c) 2020 PaddlePaddle Authors. All Rights Reserve. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function +from __future__ import unicode_literals + +import paddle + + +class L1Decay(object): + """ + L1 Weight Decay Regularization, which encourages the weights to be sparse. + Args: + factor(float): regularization coeff. Default:0.0. + """ + + def __init__(self, factor=0.0): + super(L1Decay, self).__init__() + self.coeff = factor + + def __call__(self): + reg = paddle.regularizer.L1Decay(self.coeff) + return reg + + +class L2Decay(object): + """ + L2 Weight Decay Regularization, which helps to prevent the model over-fitting. + Args: + factor(float): regularization coeff. Default:0.0. + """ + + def __init__(self, factor=0.0): + super(L2Decay, self).__init__() + self.coeff = float(factor) + + def __call__(self): + return self.coeff diff --git a/modules/onnx_ocr_module/src/ppocr/postprocess/__init__.py b/modules/onnx_ocr_module/src/ppocr/postprocess/__init__.py new file mode 100644 index 0000000..e427ee9 --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/postprocess/__init__.py @@ -0,0 +1,121 @@ +# copyright (c) 2020 PaddlePaddle Authors. All Rights Reserve. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function +from __future__ import unicode_literals + +import os +import copy + +__all__ = ["build_post_process"] + +from .db_postprocess import DBPostProcess, DistillationDBPostProcess +from .east_postprocess import EASTPostProcess +from .sast_postprocess import SASTPostProcess +from .fce_postprocess import FCEPostProcess +from .rec_postprocess import ( + CTCLabelDecode, + AttnLabelDecode, + SRNLabelDecode, + DistillationCTCLabelDecode, + NRTRLabelDecode, + SARLabelDecode, + SEEDLabelDecode, + PRENLabelDecode, + ViTSTRLabelDecode, + ABINetLabelDecode, + SPINLabelDecode, + VLLabelDecode, + RFLLabelDecode, + SATRNLabelDecode, + ParseQLabelDecode, + CPPDLabelDecode, + LaTeXOCRDecode, + UniMERNetDecode, +) +from .cls_postprocess import ClsPostProcess +from .pg_postprocess import PGPostProcess +from .vqa_token_ser_layoutlm_postprocess import ( + VQASerTokenLayoutLMPostProcess, + DistillationSerPostProcess, +) +from .vqa_token_re_layoutlm_postprocess import ( + VQAReTokenLayoutLMPostProcess, + DistillationRePostProcess, +) +from .table_postprocess import TableMasterLabelDecode, TableLabelDecode +from .picodet_postprocess import PicoDetPostProcess +from .ct_postprocess import CTPostProcess +from .drrg_postprocess import DRRGPostprocess +from .rec_postprocess import CANLabelDecode + + +def build_post_process(config, global_config=None): + support_dict = [ + "DBPostProcess", + "EASTPostProcess", + "SASTPostProcess", + "FCEPostProcess", + "CTCLabelDecode", + "AttnLabelDecode", + "ClsPostProcess", + "SRNLabelDecode", + "PGPostProcess", + "DistillationCTCLabelDecode", + "TableLabelDecode", + "DistillationDBPostProcess", + "NRTRLabelDecode", + "SARLabelDecode", + "SEEDLabelDecode", + "VQASerTokenLayoutLMPostProcess", + "VQAReTokenLayoutLMPostProcess", + "PRENLabelDecode", + "DistillationSARLabelDecode", + "ViTSTRLabelDecode", + "ABINetLabelDecode", + "TableMasterLabelDecode", + "SPINLabelDecode", + "DistillationSerPostProcess", + "DistillationRePostProcess", + "VLLabelDecode", + "PicoDetPostProcess", + "CTPostProcess", + "RFLLabelDecode", + "DRRGPostprocess", + "CANLabelDecode", + "SATRNLabelDecode", + "ParseQLabelDecode", + "CPPDLabelDecode", + "LaTeXOCRDecode", + "UniMERNetDecode", + ] + + if config["name"] == "PSEPostProcess": + from .pse_postprocess import PSEPostProcess + + support_dict.append("PSEPostProcess") + + config = copy.deepcopy(config) + module_name = config.pop("name") + if module_name == "None": + return + if global_config is not None: + config.update(global_config) + assert module_name in support_dict, Exception( + "post process only support {}".format(support_dict) + ) + module_class = eval(module_name)(**config) + return module_class diff --git a/modules/onnx_ocr_module/src/ppocr/postprocess/__pycache__/__init__.cpython-311.pyc b/modules/onnx_ocr_module/src/ppocr/postprocess/__pycache__/__init__.cpython-311.pyc new file mode 100644 index 0000000..a11eada Binary files /dev/null and b/modules/onnx_ocr_module/src/ppocr/postprocess/__pycache__/__init__.cpython-311.pyc differ diff --git a/modules/onnx_ocr_module/src/ppocr/postprocess/__pycache__/cls_postprocess.cpython-311.pyc b/modules/onnx_ocr_module/src/ppocr/postprocess/__pycache__/cls_postprocess.cpython-311.pyc new file mode 100644 index 0000000..3c0ea8e Binary files /dev/null and b/modules/onnx_ocr_module/src/ppocr/postprocess/__pycache__/cls_postprocess.cpython-311.pyc differ diff --git a/modules/onnx_ocr_module/src/ppocr/postprocess/__pycache__/ct_postprocess.cpython-311.pyc b/modules/onnx_ocr_module/src/ppocr/postprocess/__pycache__/ct_postprocess.cpython-311.pyc new file mode 100644 index 0000000..2992098 Binary files /dev/null and b/modules/onnx_ocr_module/src/ppocr/postprocess/__pycache__/ct_postprocess.cpython-311.pyc differ diff --git a/modules/onnx_ocr_module/src/ppocr/postprocess/__pycache__/db_postprocess.cpython-311.pyc b/modules/onnx_ocr_module/src/ppocr/postprocess/__pycache__/db_postprocess.cpython-311.pyc new file mode 100644 index 0000000..443af22 Binary files /dev/null and b/modules/onnx_ocr_module/src/ppocr/postprocess/__pycache__/db_postprocess.cpython-311.pyc differ diff --git a/modules/onnx_ocr_module/src/ppocr/postprocess/__pycache__/drrg_postprocess.cpython-311.pyc b/modules/onnx_ocr_module/src/ppocr/postprocess/__pycache__/drrg_postprocess.cpython-311.pyc new file mode 100644 index 0000000..67bf068 Binary files /dev/null and b/modules/onnx_ocr_module/src/ppocr/postprocess/__pycache__/drrg_postprocess.cpython-311.pyc differ diff --git a/modules/onnx_ocr_module/src/ppocr/postprocess/__pycache__/east_postprocess.cpython-311.pyc b/modules/onnx_ocr_module/src/ppocr/postprocess/__pycache__/east_postprocess.cpython-311.pyc new file mode 100644 index 0000000..36a6a2c Binary files /dev/null and b/modules/onnx_ocr_module/src/ppocr/postprocess/__pycache__/east_postprocess.cpython-311.pyc differ diff --git a/modules/onnx_ocr_module/src/ppocr/postprocess/__pycache__/fce_postprocess.cpython-311.pyc b/modules/onnx_ocr_module/src/ppocr/postprocess/__pycache__/fce_postprocess.cpython-311.pyc new file mode 100644 index 0000000..9bddc0b Binary files /dev/null and b/modules/onnx_ocr_module/src/ppocr/postprocess/__pycache__/fce_postprocess.cpython-311.pyc differ diff --git a/modules/onnx_ocr_module/src/ppocr/postprocess/__pycache__/locality_aware_nms.cpython-311.pyc b/modules/onnx_ocr_module/src/ppocr/postprocess/__pycache__/locality_aware_nms.cpython-311.pyc new file mode 100644 index 0000000..b46a213 Binary files /dev/null and b/modules/onnx_ocr_module/src/ppocr/postprocess/__pycache__/locality_aware_nms.cpython-311.pyc differ diff --git a/modules/onnx_ocr_module/src/ppocr/postprocess/__pycache__/pg_postprocess.cpython-311.pyc b/modules/onnx_ocr_module/src/ppocr/postprocess/__pycache__/pg_postprocess.cpython-311.pyc new file mode 100644 index 0000000..b88b602 Binary files /dev/null and b/modules/onnx_ocr_module/src/ppocr/postprocess/__pycache__/pg_postprocess.cpython-311.pyc differ diff --git a/modules/onnx_ocr_module/src/ppocr/postprocess/__pycache__/picodet_postprocess.cpython-311.pyc b/modules/onnx_ocr_module/src/ppocr/postprocess/__pycache__/picodet_postprocess.cpython-311.pyc new file mode 100644 index 0000000..92ba052 Binary files /dev/null and b/modules/onnx_ocr_module/src/ppocr/postprocess/__pycache__/picodet_postprocess.cpython-311.pyc differ diff --git a/modules/onnx_ocr_module/src/ppocr/postprocess/__pycache__/rec_postprocess.cpython-311.pyc b/modules/onnx_ocr_module/src/ppocr/postprocess/__pycache__/rec_postprocess.cpython-311.pyc new file mode 100644 index 0000000..4564ce1 Binary files /dev/null and b/modules/onnx_ocr_module/src/ppocr/postprocess/__pycache__/rec_postprocess.cpython-311.pyc differ diff --git a/modules/onnx_ocr_module/src/ppocr/postprocess/__pycache__/sast_postprocess.cpython-311.pyc b/modules/onnx_ocr_module/src/ppocr/postprocess/__pycache__/sast_postprocess.cpython-311.pyc new file mode 100644 index 0000000..003bc55 Binary files /dev/null and b/modules/onnx_ocr_module/src/ppocr/postprocess/__pycache__/sast_postprocess.cpython-311.pyc differ diff --git a/modules/onnx_ocr_module/src/ppocr/postprocess/__pycache__/table_postprocess.cpython-311.pyc b/modules/onnx_ocr_module/src/ppocr/postprocess/__pycache__/table_postprocess.cpython-311.pyc new file mode 100644 index 0000000..eb5d522 Binary files /dev/null and b/modules/onnx_ocr_module/src/ppocr/postprocess/__pycache__/table_postprocess.cpython-311.pyc differ diff --git a/modules/onnx_ocr_module/src/ppocr/postprocess/__pycache__/vqa_token_re_layoutlm_postprocess.cpython-311.pyc b/modules/onnx_ocr_module/src/ppocr/postprocess/__pycache__/vqa_token_re_layoutlm_postprocess.cpython-311.pyc new file mode 100644 index 0000000..0ce6de5 Binary files /dev/null and b/modules/onnx_ocr_module/src/ppocr/postprocess/__pycache__/vqa_token_re_layoutlm_postprocess.cpython-311.pyc differ diff --git a/modules/onnx_ocr_module/src/ppocr/postprocess/__pycache__/vqa_token_ser_layoutlm_postprocess.cpython-311.pyc b/modules/onnx_ocr_module/src/ppocr/postprocess/__pycache__/vqa_token_ser_layoutlm_postprocess.cpython-311.pyc new file mode 100644 index 0000000..e93115f Binary files /dev/null and b/modules/onnx_ocr_module/src/ppocr/postprocess/__pycache__/vqa_token_ser_layoutlm_postprocess.cpython-311.pyc differ diff --git a/modules/onnx_ocr_module/src/ppocr/postprocess/cls_postprocess.py b/modules/onnx_ocr_module/src/ppocr/postprocess/cls_postprocess.py new file mode 100644 index 0000000..06c7693 --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/postprocess/cls_postprocess.py @@ -0,0 +1,43 @@ +# copyright (c) 2020 PaddlePaddle Authors. All Rights Reserve. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +import paddle + + +class ClsPostProcess(object): + """Convert between text-label and text-index""" + + def __init__(self, label_list=None, key=None, **kwargs): + super(ClsPostProcess, self).__init__() + self.label_list = label_list + self.key = key + + def __call__(self, preds, label=None, *args, **kwargs): + if self.key is not None: + preds = preds[self.key] + + label_list = self.label_list + if label_list is None: + label_list = {idx: idx for idx in range(preds.shape[-1])} + + if isinstance(preds, paddle.Tensor): + preds = preds.numpy() + + pred_idxs = preds.argmax(axis=1) + decode_out = [ + (label_list[idx], preds[i, idx]) for i, idx in enumerate(pred_idxs) + ] + if label is None: + return decode_out + label = [(label_list[idx], 1.0) for idx in label] + return decode_out, label diff --git a/modules/onnx_ocr_module/src/ppocr/postprocess/ct_postprocess.py b/modules/onnx_ocr_module/src/ppocr/postprocess/ct_postprocess.py new file mode 100644 index 0000000..3a9ce5c --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/postprocess/ct_postprocess.py @@ -0,0 +1,158 @@ +# Copyright (c) 2020 PaddlePaddle Authors. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +""" +This code is referred from: +https://github.com/shengtao96/CentripetalText/blob/main/test.py +""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import os +import os.path as osp +import numpy as np +import cv2 +import paddle +import pyclipper + + +class CTPostProcess(object): + """ + The post process for Centripetal Text (CT). + """ + + def __init__(self, min_score=0.88, min_area=16, box_type="poly", **kwargs): + self.min_score = min_score + self.min_area = min_area + self.box_type = box_type + + self.coord = np.zeros((2, 300, 300), dtype=np.int32) + for i in range(300): + for j in range(300): + self.coord[0, i, j] = j + self.coord[1, i, j] = i + + def __call__(self, preds, batch): + outs = preds["maps"] + out_scores = preds["score"] + + if isinstance(outs, paddle.Tensor): + outs = outs.numpy() + if isinstance(out_scores, paddle.Tensor): + out_scores = out_scores.numpy() + + batch_size = outs.shape[0] + boxes_batch = [] + for idx in range(batch_size): + bboxes = [] + scores = [] + + img_shape = batch[idx] + + org_img_size = img_shape[:3] + img_shape = img_shape[3:] + img_size = img_shape[:2] + + out = np.expand_dims(outs[idx], axis=0) + outputs = dict() + + score = np.expand_dims(out_scores[idx], axis=0) + + kernel = out[:, 0, :, :] > 0.2 + loc = out[:, 1:, :, :].astype("float32") + + score = score[0].astype(np.float32) + kernel = kernel[0].astype(np.uint8) + loc = loc[0].astype(np.float32) + + label_num, label_kernel = cv2.connectedComponents(kernel, connectivity=4) + + for i in range(1, label_num): + ind = label_kernel == i + if ind.sum() < 10: # pixel number less than 10, treated as background + label_kernel[ind] = 0 + + label = np.zeros_like(label_kernel) + h, w = label_kernel.shape + pixels = self.coord[:, :h, :w].reshape(2, -1) + points = pixels.transpose([1, 0]).astype(np.float32) + + off_points = (points + 10.0 / 4.0 * loc[:, pixels[1], pixels[0]].T).astype( + np.int32 + ) + off_points[:, 0] = np.clip(off_points[:, 0], 0, label.shape[1] - 1) + off_points[:, 1] = np.clip(off_points[:, 1], 0, label.shape[0] - 1) + + label[pixels[1], pixels[0]] = label_kernel[ + off_points[:, 1], off_points[:, 0] + ] + label[label_kernel > 0] = label_kernel[label_kernel > 0] + + score_pocket = [0.0] + for i in range(1, label_num): + ind = label_kernel == i + if ind.sum() == 0: + score_pocket.append(0.0) + continue + score_i = np.mean(score[ind]) + score_pocket.append(score_i) + + label_num = np.max(label) + 1 + label = cv2.resize( + label, (img_size[1], img_size[0]), interpolation=cv2.INTER_NEAREST + ) + + scale = ( + float(org_img_size[1]) / float(img_size[1]), + float(org_img_size[0]) / float(img_size[0]), + ) + + for i in range(1, label_num): + ind = label == i + points = np.array(np.where(ind)).transpose((1, 0)) + + if points.shape[0] < self.min_area: + continue + + score_i = score_pocket[i] + if score_i < self.min_score: + continue + + if self.box_type == "rect": + rect = cv2.minAreaRect(points[:, ::-1]) + bbox = cv2.boxPoints(rect) * scale + z = bbox.mean(0) + bbox = z + (bbox - z) * 0.85 + elif self.box_type == "poly": + binary = np.zeros(label.shape, dtype="uint8") + binary[ind] = 1 + try: + _, contours, _ = cv2.findContours( + binary, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE + ) + except BaseException: + contours, _ = cv2.findContours( + binary, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE + ) + + bbox = contours[0] * scale + + bbox = bbox.astype("int32") + bboxes.append(bbox.reshape(-1, 2)) + scores.append(score_i) + + boxes_batch.append({"points": bboxes}) + + return boxes_batch diff --git a/modules/onnx_ocr_module/src/ppocr/postprocess/db_postprocess.py b/modules/onnx_ocr_module/src/ppocr/postprocess/db_postprocess.py new file mode 100644 index 0000000..f7e84d3 --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/postprocess/db_postprocess.py @@ -0,0 +1,289 @@ +# Copyright (c) 2020 PaddlePaddle Authors. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +""" +This code is referred from: +https://github.com/WenmuZhou/DBNet.pytorch/blob/master/post_processing/seg_detector_representer.py +""" +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import numpy as np +import cv2 +import paddle +from shapely.geometry import Polygon +import pyclipper + + +class DBPostProcess(object): + """ + The post process for Differentiable Binarization (DB). + """ + + def __init__( + self, + thresh=0.3, + box_thresh=0.7, + max_candidates=1000, + unclip_ratio=2.0, + use_dilation=False, + score_mode="fast", + box_type="quad", + **kwargs, + ): + self.thresh = thresh + self.box_thresh = box_thresh + self.max_candidates = max_candidates + self.unclip_ratio = unclip_ratio + self.min_size = 3 + self.score_mode = score_mode + self.box_type = box_type + assert score_mode in [ + "slow", + "fast", + ], "Score mode must be in [slow, fast] but got: {}".format(score_mode) + + self.dilation_kernel = None if not use_dilation else np.array([[1, 1], [1, 1]]) + + def polygons_from_bitmap(self, pred, _bitmap, dest_width, dest_height): + """ + _bitmap: single map with shape (1, H, W), + whose values are binarized as {0, 1} + """ + + bitmap = _bitmap + height, width = bitmap.shape + + boxes = [] + scores = [] + + contours, _ = cv2.findContours( + (bitmap * 255).astype(np.uint8), cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE + ) + + for contour in contours[: self.max_candidates]: + epsilon = 0.002 * cv2.arcLength(contour, True) + approx = cv2.approxPolyDP(contour, epsilon, True) + points = approx.reshape((-1, 2)) + if points.shape[0] < 4: + continue + + score = self.box_score_fast(pred, points.reshape(-1, 2)) + if self.box_thresh > score: + continue + + if points.shape[0] > 2: + box = self.unclip(points, self.unclip_ratio) + if len(box) > 1: + continue + else: + continue + box = np.array(box).reshape(-1, 2) + if len(box) == 0: + continue + + _, sside = self.get_mini_boxes(box.reshape((-1, 1, 2))) + if sside < self.min_size + 2: + continue + + box = np.array(box) + box[:, 0] = np.clip(np.round(box[:, 0] / width * dest_width), 0, dest_width) + box[:, 1] = np.clip( + np.round(box[:, 1] / height * dest_height), 0, dest_height + ) + boxes.append(box.tolist()) + scores.append(score) + return boxes, scores + + def boxes_from_bitmap(self, pred, _bitmap, dest_width, dest_height): + """ + _bitmap: single map with shape (1, H, W), + whose values are binarized as {0, 1} + """ + + bitmap = _bitmap + height, width = bitmap.shape + + outs = cv2.findContours( + (bitmap * 255).astype(np.uint8), cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE + ) + if len(outs) == 3: + img, contours, _ = outs[0], outs[1], outs[2] + elif len(outs) == 2: + contours, _ = outs[0], outs[1] + + num_contours = min(len(contours), self.max_candidates) + + boxes = [] + scores = [] + for index in range(num_contours): + contour = contours[index] + points, sside = self.get_mini_boxes(contour) + if sside < self.min_size: + continue + points = np.array(points) + if self.score_mode == "fast": + score = self.box_score_fast(pred, points.reshape(-1, 2)) + else: + score = self.box_score_slow(pred, contour) + if self.box_thresh > score: + continue + + box = self.unclip(points, self.unclip_ratio) + if len(box) > 1: + continue + box = np.array(box).reshape(-1, 1, 2) + box, sside = self.get_mini_boxes(box) + if sside < self.min_size + 2: + continue + box = np.array(box) + + box[:, 0] = np.clip(np.round(box[:, 0] / width * dest_width), 0, dest_width) + box[:, 1] = np.clip( + np.round(box[:, 1] / height * dest_height), 0, dest_height + ) + boxes.append(box.astype("int32")) + scores.append(score) + return np.array(boxes, dtype="int32"), scores + + def unclip(self, box, unclip_ratio): + poly = Polygon(box) + distance = poly.area * unclip_ratio / poly.length + offset = pyclipper.PyclipperOffset() + offset.AddPath(box, pyclipper.JT_ROUND, pyclipper.ET_CLOSEDPOLYGON) + expanded = offset.Execute(distance) + return expanded + + def get_mini_boxes(self, contour): + bounding_box = cv2.minAreaRect(contour) + points = sorted(list(cv2.boxPoints(bounding_box)), key=lambda x: x[0]) + + index_1, index_2, index_3, index_4 = 0, 1, 2, 3 + if points[1][1] > points[0][1]: + index_1 = 0 + index_4 = 1 + else: + index_1 = 1 + index_4 = 0 + if points[3][1] > points[2][1]: + index_2 = 2 + index_3 = 3 + else: + index_2 = 3 + index_3 = 2 + + box = [points[index_1], points[index_2], points[index_3], points[index_4]] + return box, min(bounding_box[1]) + + def box_score_fast(self, bitmap, _box): + """ + box_score_fast: use bbox mean score as the mean score + """ + h, w = bitmap.shape[:2] + box = _box.copy() + xmin = np.clip(np.floor(box[:, 0].min()).astype("int32"), 0, w - 1) + xmax = np.clip(np.ceil(box[:, 0].max()).astype("int32"), 0, w - 1) + ymin = np.clip(np.floor(box[:, 1].min()).astype("int32"), 0, h - 1) + ymax = np.clip(np.ceil(box[:, 1].max()).astype("int32"), 0, h - 1) + + mask = np.zeros((ymax - ymin + 1, xmax - xmin + 1), dtype=np.uint8) + box[:, 0] = box[:, 0] - xmin + box[:, 1] = box[:, 1] - ymin + cv2.fillPoly(mask, box.reshape(1, -1, 2).astype("int32"), 1) + return cv2.mean(bitmap[ymin : ymax + 1, xmin : xmax + 1], mask)[0] + + def box_score_slow(self, bitmap, contour): + """ + box_score_slow: use polyon mean score as the mean score + """ + h, w = bitmap.shape[:2] + contour = contour.copy() + contour = np.reshape(contour, (-1, 2)) + + xmin = np.clip(np.min(contour[:, 0]), 0, w - 1) + xmax = np.clip(np.max(contour[:, 0]), 0, w - 1) + ymin = np.clip(np.min(contour[:, 1]), 0, h - 1) + ymax = np.clip(np.max(contour[:, 1]), 0, h - 1) + + mask = np.zeros((ymax - ymin + 1, xmax - xmin + 1), dtype=np.uint8) + + contour[:, 0] = contour[:, 0] - xmin + contour[:, 1] = contour[:, 1] - ymin + + cv2.fillPoly(mask, contour.reshape(1, -1, 2).astype("int32"), 1) + return cv2.mean(bitmap[ymin : ymax + 1, xmin : xmax + 1], mask)[0] + + def __call__(self, outs_dict, shape_list): + pred = outs_dict["maps"] + if isinstance(pred, paddle.Tensor): + pred = pred.numpy() + pred = pred[:, 0, :, :] + segmentation = pred > self.thresh + + boxes_batch = [] + for batch_index in range(pred.shape[0]): + src_h, src_w, ratio_h, ratio_w = shape_list[batch_index] + if self.dilation_kernel is not None: + mask = cv2.dilate( + np.array(segmentation[batch_index]).astype(np.uint8), + self.dilation_kernel, + ) + else: + mask = segmentation[batch_index] + if self.box_type == "poly": + boxes, scores = self.polygons_from_bitmap( + pred[batch_index], mask, src_w, src_h + ) + elif self.box_type == "quad": + boxes, scores = self.boxes_from_bitmap( + pred[batch_index], mask, src_w, src_h + ) + else: + raise ValueError("box_type can only be one of ['quad', 'poly']") + + boxes_batch.append({"points": boxes}) + return boxes_batch + + +class DistillationDBPostProcess(object): + def __init__( + self, + model_name=["student"], + key=None, + thresh=0.3, + box_thresh=0.6, + max_candidates=1000, + unclip_ratio=1.5, + use_dilation=False, + score_mode="fast", + box_type="quad", + **kwargs, + ): + self.model_name = model_name + self.key = key + self.post_process = DBPostProcess( + thresh=thresh, + box_thresh=box_thresh, + max_candidates=max_candidates, + unclip_ratio=unclip_ratio, + use_dilation=use_dilation, + score_mode=score_mode, + box_type=box_type, + ) + + def __call__(self, predicts, shape_list): + results = {} + for k in self.model_name: + results[k] = self.post_process(predicts[k], shape_list=shape_list) + return results diff --git a/modules/onnx_ocr_module/src/ppocr/postprocess/drrg_postprocess.py b/modules/onnx_ocr_module/src/ppocr/postprocess/drrg_postprocess.py new file mode 100644 index 0000000..f1241c4 --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/postprocess/drrg_postprocess.py @@ -0,0 +1,338 @@ +# copyright (c) 2022 PaddlePaddle Authors. All Rights Reserve. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +""" +This code is refer from: +https://github.com/open-mmlab/mmocr/blob/main/mmocr/models/textdet/postprocess/drrg_postprocessor.py +""" + +import functools +import operator + +import numpy as np +import paddle +from numpy.linalg import norm +import cv2 + + +class Node: + def __init__(self, ind): + self.__ind = ind + self.__links = set() + + @property + def ind(self): + return self.__ind + + @property + def links(self): + return set(self.__links) + + def add_link(self, link_node): + self.__links.add(link_node) + link_node.__links.add(self) + + +def graph_propagation(edges, scores, text_comps, edge_len_thr=50.0): + assert edges.ndim == 2 + assert edges.shape[1] == 2 + assert edges.shape[0] == scores.shape[0] + assert text_comps.ndim == 2 + assert isinstance(edge_len_thr, float) + + edges = np.sort(edges, axis=1) + score_dict = {} + for i, edge in enumerate(edges): + if text_comps is not None: + box1 = text_comps[edge[0], :8].reshape(4, 2) + box2 = text_comps[edge[1], :8].reshape(4, 2) + center1 = np.mean(box1, axis=0) + center2 = np.mean(box2, axis=0) + distance = norm(center1 - center2) + if distance > edge_len_thr: + scores[i] = 0 + if (edge[0], edge[1]) in score_dict: + score_dict[edge[0], edge[1]] = 0.5 * ( + score_dict[edge[0], edge[1]] + scores[i] + ) + else: + score_dict[edge[0], edge[1]] = scores[i] + + nodes = np.sort(np.unique(edges.flatten())) + mapping = -1 * np.ones((np.max(nodes) + 1), dtype=np.int32) + mapping[nodes] = np.arange(nodes.shape[0]) + order_inds = mapping[edges] + vertices = [Node(node) for node in nodes] + for ind in order_inds: + vertices[ind[0]].add_link(vertices[ind[1]]) + + return vertices, score_dict + + +def connected_components(nodes, score_dict, link_thr): + assert isinstance(nodes, list) + assert all([isinstance(node, Node) for node in nodes]) + assert isinstance(score_dict, dict) + assert isinstance(link_thr, float) + + clusters = [] + nodes = set(nodes) + while nodes: + node = nodes.pop() + cluster = {node} + node_queue = [node] + while node_queue: + node = node_queue.pop(0) + neighbors = set( + [ + neighbor + for neighbor in node.links + if score_dict[tuple(sorted([node.ind, neighbor.ind]))] >= link_thr + ] + ) + neighbors.difference_update(cluster) + nodes.difference_update(neighbors) + cluster.update(neighbors) + node_queue.extend(neighbors) + clusters.append(list(cluster)) + return clusters + + +def clusters2labels(clusters, num_nodes): + assert isinstance(clusters, list) + assert all([isinstance(cluster, list) for cluster in clusters]) + assert all([isinstance(node, Node) for cluster in clusters for node in cluster]) + assert isinstance(num_nodes, int) + + node_labels = np.zeros(num_nodes) + for cluster_ind, cluster in enumerate(clusters): + for node in cluster: + node_labels[node.ind] = cluster_ind + return node_labels + + +def remove_single(text_comps, comp_pred_labels): + assert text_comps.ndim == 2 + assert text_comps.shape[0] == comp_pred_labels.shape[0] + + single_flags = np.zeros_like(comp_pred_labels) + pred_labels = np.unique(comp_pred_labels) + for label in pred_labels: + current_label_flag = comp_pred_labels == label + if np.sum(current_label_flag) == 1: + single_flags[np.where(current_label_flag)[0][0]] = 1 + keep_ind = [i for i in range(len(comp_pred_labels)) if not single_flags[i]] + filtered_text_comps = text_comps[keep_ind, :] + filtered_labels = comp_pred_labels[keep_ind] + + return filtered_text_comps, filtered_labels + + +def norm2(point1, point2): + return ((point1[0] - point2[0]) ** 2 + (point1[1] - point2[1]) ** 2) ** 0.5 + + +def min_connect_path(points): + assert isinstance(points, list) + assert all([isinstance(point, list) for point in points]) + assert all([isinstance(coord, int) for point in points for coord in point]) + + points_queue = points.copy() + shortest_path = [] + current_edge = [[], []] + + edge_dict0 = {} + edge_dict1 = {} + current_edge[0] = points_queue[0] + current_edge[1] = points_queue[0] + points_queue.remove(points_queue[0]) + while points_queue: + for point in points_queue: + length0 = norm2(point, current_edge[0]) + edge_dict0[length0] = [point, current_edge[0]] + length1 = norm2(current_edge[1], point) + edge_dict1[length1] = [current_edge[1], point] + key0 = min(edge_dict0.keys()) + key1 = min(edge_dict1.keys()) + + if key0 <= key1: + start = edge_dict0[key0][0] + end = edge_dict0[key0][1] + shortest_path.insert(0, [points.index(start), points.index(end)]) + points_queue.remove(start) + current_edge[0] = start + else: + start = edge_dict1[key1][0] + end = edge_dict1[key1][1] + shortest_path.append([points.index(start), points.index(end)]) + points_queue.remove(end) + current_edge[1] = end + + edge_dict0 = {} + edge_dict1 = {} + + shortest_path = functools.reduce(operator.concat, shortest_path) + shortest_path = sorted(set(shortest_path), key=shortest_path.index) + + return shortest_path + + +def in_contour(cont, point): + x, y = point + is_inner = cv2.pointPolygonTest(cont, (int(x), int(y)), False) > 0.5 + return is_inner + + +def fix_corner(top_line, bot_line, start_box, end_box): + assert isinstance(top_line, list) + assert all(isinstance(point, list) for point in top_line) + assert isinstance(bot_line, list) + assert all(isinstance(point, list) for point in bot_line) + assert start_box.shape == end_box.shape == (4, 2) + + contour = np.array(top_line + bot_line[::-1]) + start_left_mid = (start_box[0] + start_box[3]) / 2 + start_right_mid = (start_box[1] + start_box[2]) / 2 + end_left_mid = (end_box[0] + end_box[3]) / 2 + end_right_mid = (end_box[1] + end_box[2]) / 2 + if not in_contour(contour, start_left_mid): + top_line.insert(0, start_box[0].tolist()) + bot_line.insert(0, start_box[3].tolist()) + elif not in_contour(contour, start_right_mid): + top_line.insert(0, start_box[1].tolist()) + bot_line.insert(0, start_box[2].tolist()) + if not in_contour(contour, end_left_mid): + top_line.append(end_box[0].tolist()) + bot_line.append(end_box[3].tolist()) + elif not in_contour(contour, end_right_mid): + top_line.append(end_box[1].tolist()) + bot_line.append(end_box[2].tolist()) + return top_line, bot_line + + +def comps2boundaries(text_comps, comp_pred_labels): + assert text_comps.ndim == 2 + assert len(text_comps) == len(comp_pred_labels) + boundaries = [] + if len(text_comps) < 1: + return boundaries + for cluster_ind in range(0, int(np.max(comp_pred_labels)) + 1): + cluster_comp_inds = np.where(comp_pred_labels == cluster_ind) + text_comp_boxes = ( + text_comps[cluster_comp_inds, :8].reshape((-1, 4, 2)).astype(np.int32) + ) + score = np.mean(text_comps[cluster_comp_inds, -1]) + + if text_comp_boxes.shape[0] < 1: + continue + + elif text_comp_boxes.shape[0] > 1: + centers = np.mean(text_comp_boxes, axis=1).astype(np.int32).tolist() + shortest_path = min_connect_path(centers) + text_comp_boxes = text_comp_boxes[shortest_path] + top_line = ( + np.mean(text_comp_boxes[:, 0:2, :], axis=1).astype(np.int32).tolist() + ) + bot_line = ( + np.mean(text_comp_boxes[:, 2:4, :], axis=1).astype(np.int32).tolist() + ) + top_line, bot_line = fix_corner( + top_line, bot_line, text_comp_boxes[0], text_comp_boxes[-1] + ) + boundary_points = top_line + bot_line[::-1] + + else: + top_line = text_comp_boxes[0, 0:2, :].astype(np.int32).tolist() + bot_line = text_comp_boxes[0, 2:4:-1, :].astype(np.int32).tolist() + boundary_points = top_line + bot_line + + boundary = [p for coord in boundary_points for p in coord] + [score] + boundaries.append(boundary) + + return boundaries + + +class DRRGPostprocess(object): + """Merge text components and construct boundaries of text instances. + + Args: + link_thr (float): The edge score threshold. + """ + + def __init__(self, link_thr, **kwargs): + assert isinstance(link_thr, float) + self.link_thr = link_thr + + def __call__(self, preds, shape_list): + """ + Args: + edges (ndarray): The edge array of shape N * 2, each row is a node + index pair that makes up an edge in graph. + scores (ndarray): The edge score array of shape (N,). + text_comps (ndarray): The text components. + + Returns: + List[list[float]]: The predicted boundaries of text instances. + """ + edges, scores, text_comps = preds + if edges is not None: + if isinstance(edges, paddle.Tensor): + edges = edges.numpy() + if isinstance(scores, paddle.Tensor): + scores = scores.numpy() + if isinstance(text_comps, paddle.Tensor): + text_comps = text_comps.numpy() + assert len(edges) == len(scores) + assert text_comps.ndim == 2 + assert text_comps.shape[1] == 9 + + vertices, score_dict = graph_propagation(edges, scores, text_comps) + clusters = connected_components(vertices, score_dict, self.link_thr) + pred_labels = clusters2labels(clusters, text_comps.shape[0]) + text_comps, pred_labels = remove_single(text_comps, pred_labels) + boundaries = comps2boundaries(text_comps, pred_labels) + else: + boundaries = [] + + boundaries, scores = self.resize_boundary( + boundaries, (1 / shape_list[0, 2:]).tolist()[::-1] + ) + boxes_batch = [dict(points=boundaries, scores=scores)] + return boxes_batch + + def resize_boundary(self, boundaries, scale_factor): + """Rescale boundaries via scale_factor. + + Args: + boundaries (list[list[float]]): The boundary list. Each boundary + with size 2k+1 with k>=4. + scale_factor(ndarray): The scale factor of size (4,). + + Returns: + boundaries (list[list[float]]): The scaled boundaries. + """ + boxes = [] + scores = [] + for b in boundaries: + sz = len(b) + scores.append(b[-1]) + b = ( + ( + np.array(b[: sz - 1]) + * (np.tile(scale_factor[:2], int((sz - 1) / 2)).reshape(1, sz - 1)) + ) + .flatten() + .tolist() + ) + boxes.append(np.array(b).reshape([-1, 2])) + return boxes, scores diff --git a/modules/onnx_ocr_module/src/ppocr/postprocess/east_postprocess.py b/modules/onnx_ocr_module/src/ppocr/postprocess/east_postprocess.py new file mode 100644 index 0000000..3db2fe8 --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/postprocess/east_postprocess.py @@ -0,0 +1,141 @@ +# Copyright (c) 2020 PaddlePaddle Authors. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import numpy as np +from .locality_aware_nms import nms_locality +import cv2 +import paddle + +import os +from ppocr.utils.utility import check_install +import sys + + +class EASTPostProcess(object): + """ + The post process for EAST. + """ + + def __init__(self, score_thresh=0.8, cover_thresh=0.1, nms_thresh=0.2, **kwargs): + self.score_thresh = score_thresh + self.cover_thresh = cover_thresh + self.nms_thresh = nms_thresh + + def restore_rectangle_quad(self, origin, geometry): + """ + Restore rectangle from quadrangle. + """ + # quad + origin_concat = np.concatenate( + (origin, origin, origin, origin), axis=1 + ) # (n, 8) + pred_quads = origin_concat - geometry + pred_quads = pred_quads.reshape((-1, 4, 2)) # (n, 4, 2) + return pred_quads + + def detect( + self, score_map, geo_map, score_thresh=0.8, cover_thresh=0.1, nms_thresh=0.2 + ): + """ + restore text boxes from score map and geo map + """ + + score_map = score_map[0] + geo_map = np.swapaxes(geo_map, 1, 0) + geo_map = np.swapaxes(geo_map, 1, 2) + # filter the score map + xy_text = np.argwhere(score_map > score_thresh) + if len(xy_text) == 0: + return [] + # sort the text boxes via the y axis + xy_text = xy_text[np.argsort(xy_text[:, 0])] + # restore quad proposals + text_box_restored = self.restore_rectangle_quad( + xy_text[:, ::-1] * 4, geo_map[xy_text[:, 0], xy_text[:, 1], :] + ) + boxes = np.zeros((text_box_restored.shape[0], 9), dtype=np.float32) + boxes[:, :8] = text_box_restored.reshape((-1, 8)) + boxes[:, 8] = score_map[xy_text[:, 0], xy_text[:, 1]] + + try: + check_install("lanms", "lanms-nova") + import lanms + + boxes = lanms.merge_quadrangle_n9(boxes, nms_thresh) + except: + print( + "You should install lanms by pip3 install lanms-nova to speed up nms_locality" + ) + boxes = nms_locality(boxes.astype(np.float64), nms_thresh) + if boxes.shape[0] == 0: + return [] + # Here we filter some low score boxes by the average score map, + # this is different from the original paper. + for i, box in enumerate(boxes): + mask = np.zeros_like(score_map, dtype=np.uint8) + cv2.fillPoly(mask, box[:8].reshape((-1, 4, 2)).astype(np.int32) // 4, 1) + boxes[i, 8] = cv2.mean(score_map, mask)[0] + boxes = boxes[boxes[:, 8] > cover_thresh] + return boxes + + def sort_poly(self, p): + """ + Sort polygons. + """ + min_axis = np.argmin(np.sum(p, axis=1)) + p = p[[min_axis, (min_axis + 1) % 4, (min_axis + 2) % 4, (min_axis + 3) % 4]] + if abs(p[0, 0] - p[1, 0]) > abs(p[0, 1] - p[1, 1]): + return p + else: + return p[[0, 3, 2, 1]] + + def __call__(self, outs_dict, shape_list): + score_list = outs_dict["f_score"] + geo_list = outs_dict["f_geo"] + if isinstance(score_list, paddle.Tensor): + score_list = score_list.numpy() + geo_list = geo_list.numpy() + img_num = len(shape_list) + dt_boxes_list = [] + for ino in range(img_num): + score = score_list[ino] + geo = geo_list[ino] + boxes = self.detect( + score_map=score, + geo_map=geo, + score_thresh=self.score_thresh, + cover_thresh=self.cover_thresh, + nms_thresh=self.nms_thresh, + ) + boxes_norm = [] + if len(boxes) > 0: + h, w = score.shape[1:] + src_h, src_w, ratio_h, ratio_w = shape_list[ino] + boxes = boxes[:, :8].reshape((-1, 4, 2)) + boxes[:, :, 0] /= ratio_w + boxes[:, :, 1] /= ratio_h + for i_box, box in enumerate(boxes): + box = self.sort_poly(box.astype(np.int32)) + if ( + np.linalg.norm(box[0] - box[1]) < 5 + or np.linalg.norm(box[3] - box[0]) < 5 + ): + continue + boxes_norm.append(box) + dt_boxes_list.append({"points": np.array(boxes_norm)}) + return dt_boxes_list diff --git a/modules/onnx_ocr_module/src/ppocr/postprocess/fce_postprocess.py b/modules/onnx_ocr_module/src/ppocr/postprocess/fce_postprocess.py new file mode 100644 index 0000000..bff9316 --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/postprocess/fce_postprocess.py @@ -0,0 +1,250 @@ +# copyright (c) 2022 PaddlePaddle Authors. All Rights Reserve. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +""" +This code is refer from: +https://github.com/open-mmlab/mmocr/blob/v0.3.0/mmocr/models/textdet/postprocess/wrapper.py +""" + +import cv2 +import paddle +import numpy as np +from numpy.fft import ifft +from ppocr.utils.poly_nms import poly_nms, valid_boundary + + +def fill_hole(input_mask): + h, w = input_mask.shape + canvas = np.zeros((h + 2, w + 2), np.uint8) + canvas[1 : h + 1, 1 : w + 1] = input_mask.copy() + + mask = np.zeros((h + 4, w + 4), np.uint8) + + cv2.floodFill(canvas, mask, (0, 0), 1) + canvas = canvas[1 : h + 1, 1 : w + 1].astype(np.bool_) + + return ~canvas | input_mask + + +def fourier2poly(fourier_coeff, num_reconstr_points=50): + """Inverse Fourier transform + Args: + fourier_coeff (ndarray): Fourier coefficients shaped (n, 2k+1), + with n and k being candidates number and Fourier degree + respectively. + num_reconstr_points (int): Number of reconstructed polygon points. + Returns: + Polygons (ndarray): The reconstructed polygons shaped (n, n') + """ + + a = np.zeros((len(fourier_coeff), num_reconstr_points), dtype="complex") + k = (len(fourier_coeff[0]) - 1) // 2 + + a[:, 0 : k + 1] = fourier_coeff[:, k:] + a[:, -k:] = fourier_coeff[:, :k] + + poly_complex = ifft(a) * num_reconstr_points + polygon = np.zeros((len(fourier_coeff), num_reconstr_points, 2)) + polygon[:, :, 0] = poly_complex.real + polygon[:, :, 1] = poly_complex.imag + return polygon.astype("int32").reshape((len(fourier_coeff), -1)) + + +class FCEPostProcess(object): + """ + The post process for FCENet. + """ + + def __init__( + self, + scales, + fourier_degree=5, + num_reconstr_points=50, + decoding_type="fcenet", + score_thr=0.3, + nms_thr=0.1, + alpha=1.0, + beta=1.0, + box_type="poly", + **kwargs, + ): + self.scales = scales + self.fourier_degree = fourier_degree + self.num_reconstr_points = num_reconstr_points + self.decoding_type = decoding_type + self.score_thr = score_thr + self.nms_thr = nms_thr + self.alpha = alpha + self.beta = beta + self.box_type = box_type + + def __call__(self, preds, shape_list): + score_maps = [] + for key, value in preds.items(): + if isinstance(value, paddle.Tensor): + value = value.numpy() + cls_res = value[:, :4, :, :] + reg_res = value[:, 4:, :, :] + score_maps.append([cls_res, reg_res]) + + return self.get_boundary(score_maps, shape_list) + + def resize_boundary(self, boundaries, scale_factor): + """Rescale boundaries via scale_factor. + + Args: + boundaries (list[list[float]]): The boundary list. Each boundary + with size 2k+1 with k>=4. + scale_factor(ndarray): The scale factor of size (4,). + + Returns: + boundaries (list[list[float]]): The scaled boundaries. + """ + boxes = [] + scores = [] + for b in boundaries: + sz = len(b) + valid_boundary(b, True) + scores.append(b[-1]) + b = ( + ( + np.array(b[: sz - 1]) + * (np.tile(scale_factor[:2], int((sz - 1) / 2)).reshape(1, sz - 1)) + ) + .flatten() + .tolist() + ) + boxes.append(np.array(b).reshape([-1, 2])) + + return np.array(boxes, dtype=np.float32), scores + + def get_boundary(self, score_maps, shape_list): + assert len(score_maps) == len(self.scales) + boundaries = [] + for idx, score_map in enumerate(score_maps): + scale = self.scales[idx] + boundaries = boundaries + self._get_boundary_single(score_map, scale) + + # nms + boundaries = poly_nms(boundaries, self.nms_thr) + boundaries, scores = self.resize_boundary( + boundaries, (1 / shape_list[0, 2:]).tolist()[::-1] + ) + + boxes_batch = [dict(points=boundaries, scores=scores)] + return boxes_batch + + def _get_boundary_single(self, score_map, scale): + assert len(score_map) == 2 + assert score_map[1].shape[1] == 4 * self.fourier_degree + 2 + + return self.fcenet_decode( + preds=score_map, + fourier_degree=self.fourier_degree, + num_reconstr_points=self.num_reconstr_points, + scale=scale, + alpha=self.alpha, + beta=self.beta, + box_type=self.box_type, + score_thr=self.score_thr, + nms_thr=self.nms_thr, + ) + + def fcenet_decode( + self, + preds, + fourier_degree, + num_reconstr_points, + scale, + alpha=1.0, + beta=2.0, + box_type="poly", + score_thr=0.3, + nms_thr=0.1, + ): + """Decoding predictions of FCENet to instances. + + Args: + preds (list(Tensor)): The head output tensors. + fourier_degree (int): The maximum Fourier transform degree k. + num_reconstr_points (int): The points number of the polygon + reconstructed from predicted Fourier coefficients. + scale (int): The down-sample scale of the prediction. + alpha (float) : The parameter to calculate final scores. Score_{final} + = (Score_{text region} ^ alpha) + * (Score_{text center region}^ beta) + beta (float) : The parameter to calculate final score. + box_type (str): Boundary encoding type 'poly' or 'quad'. + score_thr (float) : The threshold used to filter out the final + candidates. + nms_thr (float) : The threshold of nms. + + Returns: + boundaries (list[list[float]]): The instance boundary and confidence + list. + """ + assert isinstance(preds, list) + assert len(preds) == 2 + assert box_type in ["poly", "quad"] + + cls_pred = preds[0][0] + tr_pred = cls_pred[0:2] + tcl_pred = cls_pred[2:] + + reg_pred = preds[1][0].transpose([1, 2, 0]) + x_pred = reg_pred[:, :, : 2 * fourier_degree + 1] + y_pred = reg_pred[:, :, 2 * fourier_degree + 1 :] + + score_pred = (tr_pred[1] ** alpha) * (tcl_pred[1] ** beta) + tr_pred_mask = (score_pred) > score_thr + tr_mask = fill_hole(tr_pred_mask) + + tr_contours, _ = cv2.findContours( + tr_mask.astype(np.uint8), cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE + ) # opencv4 + + mask = np.zeros_like(tr_mask) + boundaries = [] + for cont in tr_contours: + deal_map = mask.copy().astype(np.int8) + cv2.drawContours(deal_map, [cont], -1, 1, -1) + + score_map = score_pred * deal_map + score_mask = score_map > 0 + xy_text = np.argwhere(score_mask) + dxy = xy_text[:, 1] + xy_text[:, 0] * 1j + + x, y = x_pred[score_mask], y_pred[score_mask] + c = x + y * 1j + c[:, fourier_degree] = c[:, fourier_degree] + dxy + c *= scale + + polygons = fourier2poly(c, num_reconstr_points) + score = score_map[score_mask].reshape(-1, 1) + polygons = poly_nms(np.hstack((polygons, score)).tolist(), nms_thr) + + boundaries = boundaries + polygons + + boundaries = poly_nms(boundaries, nms_thr) + + if box_type == "quad": + new_boundaries = [] + for boundary in boundaries: + poly = np.array(boundary[:-1]).reshape(-1, 2).astype(np.float32) + score = boundary[-1] + points = cv2.boxPoints(cv2.minAreaRect(poly)) + points = np.int64(points) + new_boundaries.append(points.reshape(-1).tolist() + [score]) + boundaries = new_boundaries + + return boundaries diff --git a/modules/onnx_ocr_module/src/ppocr/postprocess/locality_aware_nms.py b/modules/onnx_ocr_module/src/ppocr/postprocess/locality_aware_nms.py new file mode 100644 index 0000000..296ea6f --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/postprocess/locality_aware_nms.py @@ -0,0 +1,198 @@ +""" +Locality aware nms. +This code is referred from: https://github.com/songdejia/EAST/blob/master/locality_aware_nms.py +""" + +import numpy as np +from shapely.geometry import Polygon + + +def intersection(g, p): + """ + Intersection. + """ + g = Polygon(g[:8].reshape((4, 2))) + p = Polygon(p[:8].reshape((4, 2))) + g = g.buffer(0) + p = p.buffer(0) + if not g.is_valid or not p.is_valid: + return 0 + inter = Polygon(g).intersection(Polygon(p)).area + union = g.area + p.area - inter + if union == 0: + return 0 + else: + return inter / union + + +def intersection_iog(g, p): + """ + Intersection_iog. + """ + g = Polygon(g[:8].reshape((4, 2))) + p = Polygon(p[:8].reshape((4, 2))) + if not g.is_valid or not p.is_valid: + return 0 + inter = Polygon(g).intersection(Polygon(p)).area + # union = g.area + p.area - inter + union = p.area + if union == 0: + print("p_area is very small") + return 0 + else: + return inter / union + + +def weighted_merge(g, p): + """ + Weighted merge. + """ + g[:8] = (g[8] * g[:8] + p[8] * p[:8]) / (g[8] + p[8]) + g[8] = g[8] + p[8] + return g + + +def standard_nms(S, thres): + """ + Standard nms. + """ + order = np.argsort(S[:, 8])[::-1] + keep = [] + while order.size > 0: + i = order[0] + keep.append(i) + ovr = np.array([intersection(S[i], S[t]) for t in order[1:]]) + + inds = np.where(ovr <= thres)[0] + order = order[inds + 1] + + return S[keep] + + +def standard_nms_inds(S, thres): + """ + Standard nms, return inds. + """ + order = np.argsort(S[:, 8])[::-1] + keep = [] + while order.size > 0: + i = order[0] + keep.append(i) + ovr = np.array([intersection(S[i], S[t]) for t in order[1:]]) + + inds = np.where(ovr <= thres)[0] + order = order[inds + 1] + + return keep + + +def nms(S, thres): + """ + nms. + """ + order = np.argsort(S[:, 8])[::-1] + keep = [] + while order.size > 0: + i = order[0] + keep.append(i) + ovr = np.array([intersection(S[i], S[t]) for t in order[1:]]) + + inds = np.where(ovr <= thres)[0] + order = order[inds + 1] + + return keep + + +def soft_nms(boxes_in, Nt_thres=0.3, threshold=0.8, sigma=0.5, method=2): + """ + soft_nms + :para boxes_in, N x 9 (coords + score) + :para threshould, eliminate cases min score(0.001) + :para Nt_thres, iou_threshi + :para sigma, gaussian weght + :method, linear or gaussian + """ + boxes = boxes_in.copy() + N = boxes.shape[0] + if N is None or N < 1: + return np.array([]) + pos, maxpos = 0, 0 + weight = 0.0 + inds = np.arange(N) + tbox, sbox = boxes[0].copy(), boxes[0].copy() + for i in range(N): + maxscore = boxes[i, 8] + maxpos = i + tbox = boxes[i].copy() + ti = inds[i] + pos = i + 1 + # get max box + while pos < N: + if maxscore < boxes[pos, 8]: + maxscore = boxes[pos, 8] + maxpos = pos + pos = pos + 1 + # add max box as a detection + boxes[i, :] = boxes[maxpos, :] + inds[i] = inds[maxpos] + # swap + boxes[maxpos, :] = tbox + inds[maxpos] = ti + tbox = boxes[i].copy() + pos = i + 1 + # NMS iteration + while pos < N: + sbox = boxes[pos].copy() + ts_iou_val = intersection(tbox, sbox) + if ts_iou_val > 0: + if method == 1: + if ts_iou_val > Nt_thres: + weight = 1 - ts_iou_val + else: + weight = 1 + elif method == 2: + weight = np.exp(-1.0 * ts_iou_val**2 / sigma) + else: + if ts_iou_val > Nt_thres: + weight = 0 + else: + weight = 1 + boxes[pos, 8] = weight * boxes[pos, 8] + # if box score falls below threshold, discard the box by + # swapping last box update N + if boxes[pos, 8] < threshold: + boxes[pos, :] = boxes[N - 1, :] + inds[pos] = inds[N - 1] + N = N - 1 + pos = pos - 1 + pos = pos + 1 + + return boxes[:N] + + +def nms_locality(polys, thres=0.3): + """ + locality aware nms of EAST + :param polys: a N*9 numpy array. first 8 coordinates, then prob + :return: boxes after nms + """ + S = [] + p = None + for g in polys: + if p is not None and intersection(g, p) > thres: + p = weighted_merge(g, p) + else: + if p is not None: + S.append(p) + p = g + if p is not None: + S.append(p) + + if len(S) == 0: + return np.array([]) + return standard_nms(np.array(S), thres) + + +if __name__ == "__main__": + # 343,350,448,135,474,143,369,359 + print(Polygon(np.array([[343, 350], [448, 135], [474, 143], [369, 359]])).area) diff --git a/modules/onnx_ocr_module/src/ppocr/postprocess/pg_postprocess.py b/modules/onnx_ocr_module/src/ppocr/postprocess/pg_postprocess.py new file mode 100644 index 0000000..31761fa --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/postprocess/pg_postprocess.py @@ -0,0 +1,66 @@ +# Copyright (c) 2021 PaddlePaddle Authors. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import os +import sys + +__dir__ = os.path.dirname(__file__) +sys.path.append(__dir__) +sys.path.append(os.path.join(__dir__, "..")) +from ppocr.utils.e2e_utils.pgnet_pp_utils import PGNet_PostProcess + + +class PGPostProcess(object): + """ + The post process for PGNet. + """ + + def __init__( + self, + character_dict_path, + valid_set, + score_thresh, + mode, + point_gather_mode=None, + **kwargs, + ): + self.character_dict_path = character_dict_path + self.valid_set = valid_set + self.score_thresh = score_thresh + self.mode = mode + self.point_gather_mode = point_gather_mode + + # c++ la-nms is faster, but only support python 3.5 + self.is_python35 = False + if sys.version_info.major == 3 and sys.version_info.minor == 5: + self.is_python35 = True + + def __call__(self, outs_dict, shape_list): + post = PGNet_PostProcess( + self.character_dict_path, + self.valid_set, + self.score_thresh, + outs_dict, + shape_list, + point_gather_mode=self.point_gather_mode, + ) + if self.mode == "fast": + data = post.pg_postprocess_fast() + else: + data = post.pg_postprocess_slow() + return data diff --git a/modules/onnx_ocr_module/src/ppocr/postprocess/picodet_postprocess.py b/modules/onnx_ocr_module/src/ppocr/postprocess/picodet_postprocess.py new file mode 100644 index 0000000..9189c8e --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/postprocess/picodet_postprocess.py @@ -0,0 +1,297 @@ +# Copyright (c) 2021 PaddlePaddle Authors. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import numpy as np +from scipy.special import softmax + + +def hard_nms(box_scores, iou_threshold, top_k=-1, candidate_size=200): + """ + Args: + box_scores (N, 5): boxes in corner-form and probabilities. + iou_threshold: intersection over union threshold. + top_k: keep top_k results. If k <= 0, keep all the results. + candidate_size: only consider the candidates with the highest scores. + Returns: + picked: a list of indexes of the kept boxes + """ + scores = box_scores[:, -1] + boxes = box_scores[:, :-1] + picked = [] + indexes = np.argsort(scores) + indexes = indexes[-candidate_size:] + while len(indexes) > 0: + current = indexes[-1] + picked.append(current) + if 0 < top_k == len(picked) or len(indexes) == 1: + break + current_box = boxes[current, :] + indexes = indexes[:-1] + rest_boxes = boxes[indexes, :] + iou = iou_of( + rest_boxes, + np.expand_dims(current_box, axis=0), + ) + indexes = indexes[iou <= iou_threshold] + + return box_scores[picked, :] + + +def iou_of(boxes0, boxes1, eps=1e-5): + """Return intersection-over-union (Jaccard index) of boxes. + Args: + boxes0 (N, 4): ground truth boxes. + boxes1 (N or 1, 4): predicted boxes. + eps: a small number to avoid 0 as denominator. + Returns: + iou (N): IoU values. + """ + overlap_left_top = np.maximum(boxes0[..., :2], boxes1[..., :2]) + overlap_right_bottom = np.minimum(boxes0[..., 2:], boxes1[..., 2:]) + + overlap_area = area_of(overlap_left_top, overlap_right_bottom) + area0 = area_of(boxes0[..., :2], boxes0[..., 2:]) + area1 = area_of(boxes1[..., :2], boxes1[..., 2:]) + return overlap_area / (area0 + area1 - overlap_area + eps) + + +def area_of(left_top, right_bottom): + """Compute the areas of rectangles given two corners. + Args: + left_top (N, 2): left top corner. + right_bottom (N, 2): right bottom corner. + Returns: + area (N): return the area. + """ + hw = np.clip(right_bottom - left_top, 0.0, None) + return hw[..., 0] * hw[..., 1] + + +def calculate_containment(boxes0, boxes1): + """ + Calculate the containment of the boxes. + Args: + boxes0 (N, 4): ground truth boxes. + boxes1 (N or 1, 4): predicted boxes. + Returns: + containment (N): containment values. + """ + overlap_left_top = np.maximum(boxes0[..., :2], boxes1[..., :2]) + overlap_right_bottom = np.minimum(boxes0[..., 2:], boxes1[..., 2:]) + + overlap_area = area_of(overlap_left_top, overlap_right_bottom) + area0 = area_of(boxes0[..., :2], boxes0[..., 2:]) + area1 = area_of(boxes1[..., :2], boxes1[..., 2:]) + return overlap_area / np.minimum(area0, np.expand_dims(area1, axis=0)) + + +class PicoDetPostProcess(object): + """ + Args: + input_shape (int): network input image size + ori_shape (int): ori image shape of before padding + scale_factor (float): scale factor of ori image + enable_mkldnn (bool): whether to open MKLDNN + """ + + def __init__( + self, + layout_dict_path, + strides=[8, 16, 32, 64], + score_threshold=0.4, + nms_threshold=0.5, + nms_top_k=1000, + keep_top_k=100, + ): + self.labels = self.load_layout_dict(layout_dict_path) + self.strides = strides + self.score_threshold = score_threshold + self.nms_threshold = nms_threshold + self.nms_top_k = nms_top_k + self.keep_top_k = keep_top_k + + def load_layout_dict(self, layout_dict_path): + with open(layout_dict_path, "r", encoding="utf-8") as fp: + labels = fp.readlines() + return [label.strip("\n") for label in labels] + + def warp_boxes(self, boxes, ori_shape): + """Apply transform to boxes""" + width, height = ori_shape[1], ori_shape[0] + n = len(boxes) + if n: + # warp points + xy = np.ones((n * 4, 3)) + xy[:, :2] = boxes[:, [0, 1, 2, 3, 0, 3, 2, 1]].reshape( + n * 4, 2 + ) # x1y1, x2y2, x1y2, x2y1 + # xy = xy @ M.T # transform + xy = (xy[:, :2] / xy[:, 2:3]).reshape(n, 8) # rescale + # create new boxes + x = xy[:, [0, 2, 4, 6]] + y = xy[:, [1, 3, 5, 7]] + xy = ( + np.concatenate((x.min(1), y.min(1), x.max(1), y.max(1))).reshape(4, n).T + ) + # clip boxes + xy[:, [0, 2]] = xy[:, [0, 2]].clip(0, width) + xy[:, [1, 3]] = xy[:, [1, 3]].clip(0, height) + return xy.astype(np.float32) + else: + return boxes + + def img_info(self, ori_img, img): + origin_shape = ori_img.shape + resize_shape = img.shape + im_scale_y = resize_shape[2] / float(origin_shape[0]) + im_scale_x = resize_shape[3] / float(origin_shape[1]) + scale_factor = np.array([im_scale_y, im_scale_x], dtype=np.float32) + img_shape = np.array(img.shape[2:], dtype=np.float32) + + input_shape = np.array(img).astype("float32").shape[2:] + ori_shape = np.array((img_shape,)).astype("float32") + scale_factor = np.array((scale_factor,)).astype("float32") + return ori_shape, input_shape, scale_factor + + def __call__(self, ori_img, img, preds): + scores, raw_boxes = preds["boxes"], preds["boxes_num"] + batch_size = raw_boxes[0].shape[0] + reg_max = int(raw_boxes[0].shape[-1] / 4 - 1) + out_boxes_num = [] + out_boxes_list = [] + results = [] + ori_shape, input_shape, scale_factor = self.img_info(ori_img, img) + + for batch_id in range(batch_size): + # generate centers + decode_boxes = [] + select_scores = [] + for stride, box_distribute, score in zip(self.strides, raw_boxes, scores): + box_distribute = box_distribute[batch_id] + score = score[batch_id] + # centers + fm_h = input_shape[0] / stride + fm_w = input_shape[1] / stride + h_range = np.arange(fm_h) + w_range = np.arange(fm_w) + ww, hh = np.meshgrid(w_range, h_range) + ct_row = (hh.flatten() + 0.5) * stride + ct_col = (ww.flatten() + 0.5) * stride + center = np.stack((ct_col, ct_row, ct_col, ct_row), axis=1) + + # box distribution to distance + reg_range = np.arange(reg_max + 1) + box_distance = box_distribute.reshape((-1, reg_max + 1)) + box_distance = softmax(box_distance, axis=1) + box_distance = box_distance * np.expand_dims(reg_range, axis=0) + box_distance = np.sum(box_distance, axis=1).reshape((-1, 4)) + box_distance = box_distance * stride + + # top K candidate + topk_idx = np.argsort(score.max(axis=1))[::-1] + topk_idx = topk_idx[: self.nms_top_k] + center = center[topk_idx] + score = score[topk_idx] + box_distance = box_distance[topk_idx] + + # decode box + decode_box = center + [-1, -1, 1, 1] * box_distance + + select_scores.append(score) + decode_boxes.append(decode_box) + + # nms + bboxes = np.concatenate(decode_boxes, axis=0) + confidences = np.concatenate(select_scores, axis=0) + picked_box_probs = [] + picked_labels = [] + for class_index in range(0, confidences.shape[1]): + probs = confidences[:, class_index] + mask = probs > self.score_threshold + probs = probs[mask] + if probs.shape[0] == 0: + continue + subset_boxes = bboxes[mask, :] + box_probs = np.concatenate([subset_boxes, probs.reshape(-1, 1)], axis=1) + box_probs = hard_nms( + box_probs, + iou_threshold=self.nms_threshold, + top_k=self.keep_top_k, + ) + picked_box_probs.append(box_probs) + picked_labels.extend([class_index] * box_probs.shape[0]) + + if len(picked_box_probs) == 0: + out_boxes_list.append(np.empty((0, 4))) + out_boxes_num.append(0) + + else: + picked_box_probs = np.concatenate(picked_box_probs) + + # resize output boxes + picked_box_probs[:, :4] = self.warp_boxes( + picked_box_probs[:, :4], ori_shape[batch_id] + ) + im_scale = np.concatenate( + [scale_factor[batch_id][::-1], scale_factor[batch_id][::-1]] + ) + picked_box_probs[:, :4] /= im_scale + # clas score box + out_boxes_list.append( + np.concatenate( + [ + np.expand_dims(np.array(picked_labels), axis=-1), + np.expand_dims(picked_box_probs[:, 4], axis=-1), + picked_box_probs[:, :4], + ], + axis=1, + ) + ) + out_boxes_num.append(len(picked_labels)) + + out_boxes_list = np.concatenate(out_boxes_list, axis=0) + out_boxes_num = np.asarray(out_boxes_num).astype(np.int32) + + for dt in out_boxes_list: + clsid, bbox, score = int(dt[0]), dt[2:], dt[1] + label = self.labels[clsid] + result = {"bbox": bbox, "label": label, "score": score} + results.append(result) + + # Handle conflict where a box is simultaneously recognized as multiple labels. + # Use IoU to find similar boxes. Prioritize labels as table, text, and others when deduplicate similar boxes. + bboxes = np.array([x["bbox"] for x in results]) + duplicate_idx = list() + for i in range(len(results)): + if i in duplicate_idx: + continue + containments = calculate_containment(bboxes, bboxes[i, ...]) + overlaps = np.where(containments > 0.5)[0] + if len(overlaps) > 1: + table_box = [x for x in overlaps if results[x]["label"] == "table"] + if len(table_box) > 0: + keep = sorted( + [(x, results[x]) for x in table_box], + key=lambda x: x[1]["score"], + reverse=True, + )[0][0] + else: + keep = sorted( + [(x, results[x]) for x in overlaps], + key=lambda x: x[1]["score"], + reverse=True, + )[0][0] + duplicate_idx.extend([x for x in overlaps if x != keep]) + results = [x for i, x in enumerate(results) if i not in duplicate_idx] + return results diff --git a/modules/onnx_ocr_module/src/ppocr/postprocess/pse_postprocess/__init__.py b/modules/onnx_ocr_module/src/ppocr/postprocess/pse_postprocess/__init__.py new file mode 100644 index 0000000..eed166a --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/postprocess/pse_postprocess/__init__.py @@ -0,0 +1,15 @@ +# Copyright (c) 2021 PaddlePaddle Authors. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from .pse_postprocess import PSEPostProcess diff --git a/modules/onnx_ocr_module/src/ppocr/postprocess/pse_postprocess/pse/README.md b/modules/onnx_ocr_module/src/ppocr/postprocess/pse_postprocess/pse/README.md new file mode 100644 index 0000000..6a19d5d --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/postprocess/pse_postprocess/pse/README.md @@ -0,0 +1,6 @@ +## 编译 +This code is refer from: +https://github.com/whai362/PSENet/blob/python3/models/post_processing/pse +```python +python3 setup.py build_ext --inplace +``` diff --git a/modules/onnx_ocr_module/src/ppocr/postprocess/pse_postprocess/pse/__init__.py b/modules/onnx_ocr_module/src/ppocr/postprocess/pse_postprocess/pse/__init__.py new file mode 100644 index 0000000..6028802 --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/postprocess/pse_postprocess/pse/__init__.py @@ -0,0 +1,33 @@ +# copyright (c) 2020 PaddlePaddle Authors. All Rights Reserve. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +import sys +import os +import subprocess + +python_path = sys.executable + +ori_path = os.getcwd() +os.chdir("ppocr/postprocess/pse_postprocess/pse") +if ( + subprocess.call("{} setup.py build_ext --inplace".format(python_path), shell=True) + != 0 +): + raise RuntimeError( + "Cannot compile pse: {}, if your system is windows, you need to install all the default components of `desktop development using C++` in visual studio 2019+".format( + os.path.dirname(os.path.realpath(__file__)) + ) + ) +os.chdir(ori_path) + +from .pse import pse diff --git a/modules/onnx_ocr_module/src/ppocr/postprocess/pse_postprocess/pse/pse.pyx b/modules/onnx_ocr_module/src/ppocr/postprocess/pse_postprocess/pse/pse.pyx new file mode 100644 index 0000000..dfa90f3 --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/postprocess/pse_postprocess/pse/pse.pyx @@ -0,0 +1,70 @@ + +import numpy as np +import cv2 +cimport numpy as np +cimport cython +cimport libcpp +cimport libcpp.pair +cimport libcpp.queue +from libcpp.pair cimport * +from libcpp.queue cimport * + +@cython.boundscheck(False) +@cython.wraparound(False) +cdef np.ndarray[np.int32_t, ndim=2] _pse(np.ndarray[np.uint8_t, ndim=3] kernels, + np.ndarray[np.int32_t, ndim=2] label, + int kernel_num, + int label_num, + float min_area=0): + cdef np.ndarray[np.int32_t, ndim=2] pred + pred = np.zeros((label.shape[0], label.shape[1]), dtype=np.int32) + + for label_idx in range(1, label_num): + if np.sum(label == label_idx) < min_area: + label[label == label_idx] = 0 + + cdef libcpp.queue.queue[libcpp.pair.pair[np.int16_t,np.int16_t]] que = \ + queue[libcpp.pair.pair[np.int16_t,np.int16_t]]() + cdef libcpp.queue.queue[libcpp.pair.pair[np.int16_t,np.int16_t]] nxt_que = \ + queue[libcpp.pair.pair[np.int16_t,np.int16_t]]() + cdef np.int16_t* dx = [-1, 1, 0, 0] + cdef np.int16_t* dy = [0, 0, -1, 1] + cdef np.int16_t tmpx, tmpy + + points = np.array(np.where(label > 0)).transpose((1, 0)) + for point_idx in range(points.shape[0]): + tmpx, tmpy = points[point_idx, 0], points[point_idx, 1] + que.push(pair[np.int16_t,np.int16_t](tmpx, tmpy)) + pred[tmpx, tmpy] = label[tmpx, tmpy] + + cdef libcpp.pair.pair[np.int16_t,np.int16_t] cur + cdef int cur_label + for kernel_idx in range(kernel_num - 1, -1, -1): + while not que.empty(): + cur = que.front() + que.pop() + cur_label = pred[cur.first, cur.second] + + is_edge = True + for j in range(4): + tmpx = cur.first + dx[j] + tmpy = cur.second + dy[j] + if tmpx < 0 or tmpx >= label.shape[0] or tmpy < 0 or tmpy >= label.shape[1]: + continue + if kernels[kernel_idx, tmpx, tmpy] == 0 or pred[tmpx, tmpy] > 0: + continue + + que.push(pair[np.int16_t,np.int16_t](tmpx, tmpy)) + pred[tmpx, tmpy] = cur_label + is_edge = False + if is_edge: + nxt_que.push(cur) + + que, nxt_que = nxt_que, que + + return pred + +def pse(kernels, min_area): + kernel_num = kernels.shape[0] + label_num, label = cv2.connectedComponents(kernels[-1], connectivity=4) + return _pse(kernels[:-1], label, kernel_num, label_num, min_area) diff --git a/modules/onnx_ocr_module/src/ppocr/postprocess/pse_postprocess/pse/setup.py b/modules/onnx_ocr_module/src/ppocr/postprocess/pse_postprocess/pse/setup.py new file mode 100644 index 0000000..6f8913e --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/postprocess/pse_postprocess/pse/setup.py @@ -0,0 +1,18 @@ +from distutils.core import setup, Extension +from Cython.Build import cythonize +import numpy + +setup( + ext_modules=cythonize( + Extension( + "pse", + sources=["pse.pyx"], + language="c++", + include_dirs=[numpy.get_include()], + library_dirs=[], + libraries=[], + extra_compile_args=["-O3"], + extra_link_args=[], + ) + ) +) diff --git a/modules/onnx_ocr_module/src/ppocr/postprocess/pse_postprocess/pse_postprocess.py b/modules/onnx_ocr_module/src/ppocr/postprocess/pse_postprocess/pse_postprocess.py new file mode 100644 index 0000000..4050dce --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/postprocess/pse_postprocess/pse_postprocess.py @@ -0,0 +1,122 @@ +# copyright (c) 2021 PaddlePaddle Authors. All Rights Reserve. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +""" +This code is refer from: +https://github.com/whai362/PSENet/blob/python3/models/head/psenet_head.py +""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import numpy as np +import cv2 +import paddle +from paddle.nn import functional as F + +from ppocr.postprocess.pse_postprocess.pse import pse + + +class PSEPostProcess(object): + """ + The post process for PSE. + """ + + def __init__( + self, + thresh=0.5, + box_thresh=0.85, + min_area=16, + box_type="quad", + scale=4, + **kwargs, + ): + assert box_type in ["quad", "poly"], "Only quad and poly is supported" + self.thresh = thresh + self.box_thresh = box_thresh + self.min_area = min_area + self.box_type = box_type + self.scale = scale + + def __call__(self, outs_dict, shape_list): + pred = outs_dict["maps"] + if not isinstance(pred, paddle.Tensor): + pred = paddle.to_tensor(pred) + pred = F.interpolate(pred, scale_factor=4 // self.scale, mode="bilinear") + + score = F.sigmoid(pred[:, 0, :, :]) + + kernels = (pred > self.thresh).astype("float32") + text_mask = kernels[:, 0, :, :] + text_mask = paddle.unsqueeze(text_mask, axis=1) + + kernels[:, 0:, :, :] = kernels[:, 0:, :, :] * text_mask + + score = score.numpy() + kernels = kernels.numpy().astype(np.uint8) + + boxes_batch = [] + for batch_index in range(pred.shape[0]): + boxes, scores = self.boxes_from_bitmap( + score[batch_index], kernels[batch_index], shape_list[batch_index] + ) + + boxes_batch.append({"points": boxes, "scores": scores}) + return boxes_batch + + def boxes_from_bitmap(self, score, kernels, shape): + label = pse(kernels, self.min_area) + return self.generate_box(score, label, shape) + + def generate_box(self, score, label, shape): + src_h, src_w, ratio_h, ratio_w = shape + label_num = np.max(label) + 1 + + boxes = [] + scores = [] + for i in range(1, label_num): + ind = label == i + points = np.array(np.where(ind)).transpose((1, 0))[:, ::-1] + + if points.shape[0] < self.min_area: + label[ind] = 0 + continue + + score_i = np.mean(score[ind]) + if score_i < self.box_thresh: + label[ind] = 0 + continue + + if self.box_type == "quad": + rect = cv2.minAreaRect(points) + bbox = cv2.boxPoints(rect) + elif self.box_type == "poly": + box_height = np.max(points[:, 1]) + 10 + box_width = np.max(points[:, 0]) + 10 + + mask = np.zeros((box_height, box_width), np.uint8) + mask[points[:, 1], points[:, 0]] = 255 + + contours, _ = cv2.findContours( + mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE + ) + bbox = np.squeeze(contours[0], 1) + else: + raise NotImplementedError + + bbox[:, 0] = np.clip(np.round(bbox[:, 0] / ratio_w), 0, src_w) + bbox[:, 1] = np.clip(np.round(bbox[:, 1] / ratio_h), 0, src_h) + boxes.append(bbox) + scores.append(score_i) + return boxes, scores diff --git a/modules/onnx_ocr_module/src/ppocr/postprocess/rec_postprocess.py b/modules/onnx_ocr_module/src/ppocr/postprocess/rec_postprocess.py new file mode 100644 index 0000000..d6d56b7 --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/postprocess/rec_postprocess.py @@ -0,0 +1,1524 @@ +# copyright (c) 2020 PaddlePaddle Authors. All Rights Reserve. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import os +import numpy as np +import re +import json + +# ONNX 전용: paddle 없이 동작하도록 numpy 기반 보조 함수 +def _to_numpy(x): + try: + return x.numpy() + except Exception: + return x + +def _softmax_np(x, axis=1): + x = np.asarray(x, dtype=np.float32) + x = x - np.max(x, axis=axis, keepdims=True) + e = np.exp(x) + return e / np.sum(e, axis=axis, keepdims=True) + + +class BaseRecLabelDecode(object): + """Convert between text-label and text-index""" + + def __init__(self, character_dict_path=None, use_space_char=False): + self.beg_str = "sos" + self.end_str = "eos" + self.reverse = False + self.character_str = [] + + if character_dict_path is None: + self.character_str = "0123456789abcdefghijklmnopqrstuvwxyz" + dict_character = list(self.character_str) + else: + # UTF-8 인코딩 문제 해결을 위한 안전한 파일 읽기 + try: + with open(character_dict_path, "r", encoding="utf-8") as fin: + lines = fin.readlines() + for line in lines: + line = line.strip("\n").strip("\r\n") + self.character_str.append(line) + except UnicodeDecodeError: + # UTF-8 실패 시 다른 인코딩 시도 + encodings = ['cp949', 'euc-kr', 'latin1', 'cp1252'] + success = False + for encoding in encodings: + try: + with open(character_dict_path, "r", encoding=encoding) as fin: + lines = fin.readlines() + for line in lines: + line = line.strip("\n").strip("\r\n") + self.character_str.append(line) + success = True + break + except UnicodeDecodeError: + continue + + if not success: + # 모든 인코딩 실패 시 바이너리 모드로 시도 (기존 방식) + with open(character_dict_path, "rb") as fin: + lines = fin.readlines() + for line in lines: + try: + line = line.decode("utf-8").strip("\n").strip("\r\n") + self.character_str.append(line) + except UnicodeDecodeError: + # 개별 라인 디코딩 실패 시 건너뛰기 + continue + if use_space_char: + self.character_str.append(" ") + dict_character = list(self.character_str) + if "arabic" in character_dict_path: + self.reverse = True + + dict_character = self.add_special_char(dict_character) + self.dict = {} + for i, char in enumerate(dict_character): + self.dict[char] = i + self.character = dict_character + + def pred_reverse(self, pred): + pred_re = [] + c_current = "" + for c in pred: + if not bool(re.search("[a-zA-Z0-9 :*./%+-]", c)): + if c_current != "": + pred_re.append(c_current) + pred_re.append(c) + c_current = "" + else: + c_current += c + if c_current != "": + pred_re.append(c_current) + + return "".join(pred_re[::-1]) + + def add_special_char(self, dict_character): + return dict_character + + def get_word_info(self, text, selection): + """ + Group the decoded characters and record the corresponding decoded positions. + + Args: + text: the decoded text + selection: the bool array that identifies which columns of features are decoded as non-separated characters + Returns: + word_list: list of the grouped words + word_col_list: list of decoding positions corresponding to each character in the grouped word + state_list: list of marker to identify the type of grouping words, including two types of grouping words: + - 'cn': continuous chinese characters (e.g., 你好啊) + - 'en&num': continuous english characters (e.g., hello), number (e.g., 123, 1.123), or mixed of them connected by '-' (e.g., VGG-16) + The remaining characters in text are treated as separators between groups (e.g., space, '(', ')', etc.). + """ + state = None + word_content = [] + word_col_content = [] + word_list = [] + word_col_list = [] + state_list = [] + valid_col = np.where(selection == True)[0] + + for c_i, char in enumerate(text): + if "\u4e00" <= char <= "\u9fff": + c_state = "cn" + elif bool(re.search("[a-zA-Z0-9]", char)): + c_state = "en&num" + else: + c_state = "splitter" + + if ( + char == "." + and state == "en&num" + and c_i + 1 < len(text) + and bool(re.search("[0-9]", text[c_i + 1])) + ): # grouping floating number + c_state = "en&num" + if ( + char == "-" and state == "en&num" + ): # grouping word with '-', such as 'state-of-the-art' + c_state = "en&num" + + if state == None: + state = c_state + + if state != c_state: + if len(word_content) != 0: + word_list.append(word_content) + word_col_list.append(word_col_content) + state_list.append(state) + word_content = [] + word_col_content = [] + state = c_state + + if state != "splitter": + word_content.append(char) + word_col_content.append(valid_col[c_i]) + + if len(word_content) != 0: + word_list.append(word_content) + word_col_list.append(word_col_content) + state_list.append(state) + + return word_list, word_col_list, state_list + + def decode( + self, + text_index, + text_prob=None, + is_remove_duplicate=False, + return_word_box=False, + ): + """convert text-index into text-label.""" + result_list = [] + ignored_tokens = self.get_ignored_tokens() + batch_size = len(text_index) + for batch_idx in range(batch_size): + selection = np.ones(len(text_index[batch_idx]), dtype=bool) + if is_remove_duplicate: + selection[1:] = text_index[batch_idx][1:] != text_index[batch_idx][:-1] + for ignored_token in ignored_tokens: + selection &= text_index[batch_idx] != ignored_token + + char_list = [ + self.character[text_id] for text_id in text_index[batch_idx][selection] + ] + if text_prob is not None: + conf_list = text_prob[batch_idx][selection] + else: + conf_list = [1] * len(selection) + if len(conf_list) == 0: + conf_list = [0] + + text = "".join(char_list) + + if self.reverse: # for arabic rec + text = self.pred_reverse(text) + + if return_word_box: + word_list, word_col_list, state_list = self.get_word_info( + text, selection + ) + result_list.append( + ( + text, + np.mean(conf_list).tolist(), + [ + len(text_index[batch_idx]), + word_list, + word_col_list, + state_list, + ], + ) + ) + else: + result_list.append((text, np.mean(conf_list).tolist())) + return result_list + + def get_ignored_tokens(self): + return [0] # for ctc blank + + +class CTCLabelDecode(BaseRecLabelDecode): + """Convert between text-label and text-index""" + + def __init__(self, character_dict_path=None, use_space_char=False, **kwargs): + super(CTCLabelDecode, self).__init__(character_dict_path, use_space_char) + + def __call__(self, preds, label=None, return_word_box=False, *args, **kwargs): + if isinstance(preds, tuple) or isinstance(preds, list): + preds = preds[-1] + if hasattr(preds, "numpy"): + preds = preds.numpy() + preds_idx = preds.argmax(axis=2) + preds_prob = preds.max(axis=2) + text = self.decode( + preds_idx, + preds_prob, + is_remove_duplicate=True, + return_word_box=return_word_box, + ) + if return_word_box: + for rec_idx, rec in enumerate(text): + wh_ratio = kwargs["wh_ratio_list"][rec_idx] + max_wh_ratio = kwargs["max_wh_ratio"] + rec[2][0] = rec[2][0] * (wh_ratio / max_wh_ratio) + if label is None: + return text + label = self.decode(label) + return text, label + + def add_special_char(self, dict_character): + dict_character = ["blank"] + dict_character + return dict_character + + +class DistillationCTCLabelDecode(CTCLabelDecode): + """ + Convert + Convert between text-label and text-index + """ + + def __init__( + self, + character_dict_path=None, + use_space_char=False, + model_name=["student"], + key=None, + multi_head=False, + **kwargs, + ): + super(DistillationCTCLabelDecode, self).__init__( + character_dict_path, use_space_char + ) + if not isinstance(model_name, list): + model_name = [model_name] + self.model_name = model_name + + self.key = key + self.multi_head = multi_head + + def __call__(self, preds, label=None, *args, **kwargs): + output = dict() + for name in self.model_name: + pred = preds[name] + if self.key is not None: + pred = pred[self.key] + if self.multi_head and isinstance(pred, dict): + pred = pred["ctc"] + output[name] = super().__call__(pred, label=label, *args, **kwargs) + return output + + +class AttnLabelDecode(BaseRecLabelDecode): + """Convert between text-label and text-index""" + + def __init__(self, character_dict_path=None, use_space_char=False, **kwargs): + super(AttnLabelDecode, self).__init__(character_dict_path, use_space_char) + + def add_special_char(self, dict_character): + self.beg_str = "sos" + self.end_str = "eos" + dict_character = dict_character + dict_character = [self.beg_str] + dict_character + [self.end_str] + return dict_character + + def decode(self, text_index, text_prob=None, is_remove_duplicate=False): + """convert text-index into text-label.""" + result_list = [] + ignored_tokens = self.get_ignored_tokens() + [beg_idx, end_idx] = self.get_ignored_tokens() + batch_size = len(text_index) + for batch_idx in range(batch_size): + char_list = [] + conf_list = [] + for idx in range(len(text_index[batch_idx])): + if text_index[batch_idx][idx] in ignored_tokens: + continue + if int(text_index[batch_idx][idx]) == int(end_idx): + break + if is_remove_duplicate: + # only for predict + if ( + idx > 0 + and text_index[batch_idx][idx - 1] == text_index[batch_idx][idx] + ): + continue + char_list.append(self.character[int(text_index[batch_idx][idx])]) + if text_prob is not None: + conf_list.append(text_prob[batch_idx][idx]) + else: + conf_list.append(1) + text = "".join(char_list) + result_list.append((text, np.mean(conf_list).tolist())) + return result_list + + def __call__(self, preds, label=None, *args, **kwargs): + """ + text = self.decode(text) + if label is None: + return text + else: + label = self.decode(label, is_remove_duplicate=False) + return text, label + """ + if isinstance(preds, paddle.Tensor): + preds = preds.numpy() + + preds_idx = preds.argmax(axis=2) + preds_prob = preds.max(axis=2) + text = self.decode(preds_idx, preds_prob, is_remove_duplicate=False) + if label is None: + return text + label = self.decode(label, is_remove_duplicate=False) + return text, label + + def get_ignored_tokens(self): + beg_idx = self.get_beg_end_flag_idx("beg") + end_idx = self.get_beg_end_flag_idx("end") + return [beg_idx, end_idx] + + def get_beg_end_flag_idx(self, beg_or_end): + if beg_or_end == "beg": + idx = np.array(self.dict[self.beg_str]) + elif beg_or_end == "end": + idx = np.array(self.dict[self.end_str]) + else: + assert False, "unsupported type %s in get_beg_end_flag_idx" % beg_or_end + return idx + + +class RFLLabelDecode(BaseRecLabelDecode): + """Convert between text-label and text-index""" + + def __init__(self, character_dict_path=None, use_space_char=False, **kwargs): + super(RFLLabelDecode, self).__init__(character_dict_path, use_space_char) + + def add_special_char(self, dict_character): + self.beg_str = "sos" + self.end_str = "eos" + dict_character = dict_character + dict_character = [self.beg_str] + dict_character + [self.end_str] + return dict_character + + def decode(self, text_index, text_prob=None, is_remove_duplicate=False): + """convert text-index into text-label.""" + result_list = [] + ignored_tokens = self.get_ignored_tokens() + [beg_idx, end_idx] = self.get_ignored_tokens() + batch_size = len(text_index) + for batch_idx in range(batch_size): + char_list = [] + conf_list = [] + for idx in range(len(text_index[batch_idx])): + if text_index[batch_idx][idx] in ignored_tokens: + continue + if int(text_index[batch_idx][idx]) == int(end_idx): + break + if is_remove_duplicate: + # only for predict + if ( + idx > 0 + and text_index[batch_idx][idx - 1] == text_index[batch_idx][idx] + ): + continue + char_list.append(self.character[int(text_index[batch_idx][idx])]) + if text_prob is not None: + conf_list.append(text_prob[batch_idx][idx]) + else: + conf_list.append(1) + text = "".join(char_list) + result_list.append((text, np.mean(conf_list).tolist())) + return result_list + + def __call__(self, preds, label=None, *args, **kwargs): + # if seq_outputs is not None: + if isinstance(preds, tuple) or isinstance(preds, list): + cnt_outputs, seq_outputs = preds + if hasattr(seq_outputs, "numpy"): + seq_outputs = seq_outputs.numpy() + preds_idx = seq_outputs.argmax(axis=2) + preds_prob = seq_outputs.max(axis=2) + text = self.decode(preds_idx, preds_prob, is_remove_duplicate=False) + + if label is None: + return text + label = self.decode(label, is_remove_duplicate=False) + return text, label + + else: + cnt_outputs = preds + if hasattr(cnt_outputs, "numpy"): + cnt_outputs = cnt_outputs.numpy() + cnt_length = [] + for lens in cnt_outputs: + length = round(np.sum(lens)) + cnt_length.append(length) + if label is None: + return cnt_length + label = self.decode(label, is_remove_duplicate=False) + length = [len(res[0]) for res in label] + return cnt_length, length + + def get_ignored_tokens(self): + beg_idx = self.get_beg_end_flag_idx("beg") + end_idx = self.get_beg_end_flag_idx("end") + return [beg_idx, end_idx] + + def get_beg_end_flag_idx(self, beg_or_end): + if beg_or_end == "beg": + idx = np.array(self.dict[self.beg_str]) + elif beg_or_end == "end": + idx = np.array(self.dict[self.end_str]) + else: + assert False, "unsupported type %s in get_beg_end_flag_idx" % beg_or_end + return idx + + +class SEEDLabelDecode(BaseRecLabelDecode): + """Convert between text-label and text-index""" + + def __init__(self, character_dict_path=None, use_space_char=False, **kwargs): + super(SEEDLabelDecode, self).__init__(character_dict_path, use_space_char) + + def add_special_char(self, dict_character): + self.padding_str = "padding" + self.end_str = "eos" + self.unknown = "unknown" + dict_character = dict_character + [self.end_str, self.padding_str, self.unknown] + return dict_character + + def get_ignored_tokens(self): + end_idx = self.get_beg_end_flag_idx("eos") + return [end_idx] + + def get_beg_end_flag_idx(self, beg_or_end): + if beg_or_end == "sos": + idx = np.array(self.dict[self.beg_str]) + elif beg_or_end == "eos": + idx = np.array(self.dict[self.end_str]) + else: + assert False, "unsupported type %s in get_beg_end_flag_idx" % beg_or_end + return idx + + def decode(self, text_index, text_prob=None, is_remove_duplicate=False): + """convert text-index into text-label.""" + result_list = [] + [end_idx] = self.get_ignored_tokens() + batch_size = len(text_index) + for batch_idx in range(batch_size): + char_list = [] + conf_list = [] + for idx in range(len(text_index[batch_idx])): + if int(text_index[batch_idx][idx]) == int(end_idx): + break + if is_remove_duplicate: + # only for predict + if ( + idx > 0 + and text_index[batch_idx][idx - 1] == text_index[batch_idx][idx] + ): + continue + char_list.append(self.character[int(text_index[batch_idx][idx])]) + if text_prob is not None: + conf_list.append(text_prob[batch_idx][idx]) + else: + conf_list.append(1) + text = "".join(char_list) + result_list.append((text, np.mean(conf_list).tolist())) + return result_list + + def __call__(self, preds, label=None, *args, **kwargs): + """ + text = self.decode(text) + if label is None: + return text + else: + label = self.decode(label, is_remove_duplicate=False) + return text, label + """ + preds_idx = preds["rec_pred"] + if hasattr(preds_idx, "numpy"): + preds_idx = preds_idx.numpy() + if "rec_pred_scores" in preds: + preds_idx = preds["rec_pred"] + preds_prob = preds["rec_pred_scores"] + else: + preds_idx = preds["rec_pred"].argmax(axis=2) + preds_prob = preds["rec_pred"].max(axis=2) + text = self.decode(preds_idx, preds_prob, is_remove_duplicate=False) + if label is None: + return text + label = self.decode(label, is_remove_duplicate=False) + return text, label + + +class SRNLabelDecode(BaseRecLabelDecode): + """Convert between text-label and text-index""" + + def __init__(self, character_dict_path=None, use_space_char=False, **kwargs): + super(SRNLabelDecode, self).__init__(character_dict_path, use_space_char) + self.max_text_length = kwargs.get("max_text_length", 25) + + def __call__(self, preds, label=None, *args, **kwargs): + pred = preds["predict"] + char_num = len(self.character_str) + 2 + if hasattr(pred, "numpy"): + pred = pred.numpy() + pred = np.reshape(pred, [-1, char_num]) + + preds_idx = np.argmax(pred, axis=1) + preds_prob = np.max(pred, axis=1) + + preds_idx = np.reshape(preds_idx, [-1, self.max_text_length]) + + preds_prob = np.reshape(preds_prob, [-1, self.max_text_length]) + + text = self.decode(preds_idx, preds_prob) + + if label is None: + text = self.decode(preds_idx, preds_prob, is_remove_duplicate=False) + return text + label = self.decode(label) + return text, label + + def decode(self, text_index, text_prob=None, is_remove_duplicate=False): + """convert text-index into text-label.""" + result_list = [] + ignored_tokens = self.get_ignored_tokens() + batch_size = len(text_index) + + for batch_idx in range(batch_size): + char_list = [] + conf_list = [] + for idx in range(len(text_index[batch_idx])): + if text_index[batch_idx][idx] in ignored_tokens: + continue + if is_remove_duplicate: + # only for predict + if ( + idx > 0 + and text_index[batch_idx][idx - 1] == text_index[batch_idx][idx] + ): + continue + char_list.append(self.character[int(text_index[batch_idx][idx])]) + if text_prob is not None: + conf_list.append(text_prob[batch_idx][idx]) + else: + conf_list.append(1) + + text = "".join(char_list) + result_list.append((text, np.mean(conf_list).tolist())) + return result_list + + def add_special_char(self, dict_character): + dict_character = dict_character + [self.beg_str, self.end_str] + return dict_character + + def get_ignored_tokens(self): + beg_idx = self.get_beg_end_flag_idx("beg") + end_idx = self.get_beg_end_flag_idx("end") + return [beg_idx, end_idx] + + def get_beg_end_flag_idx(self, beg_or_end): + if beg_or_end == "beg": + idx = np.array(self.dict[self.beg_str]) + elif beg_or_end == "end": + idx = np.array(self.dict[self.end_str]) + else: + assert False, "unsupported type %s in get_beg_end_flag_idx" % beg_or_end + return idx + + +class ParseQLabelDecode(BaseRecLabelDecode): + """Convert between text-label and text-index""" + + BOS = "[B]" + EOS = "[E]" + PAD = "[P]" + + def __init__(self, character_dict_path=None, use_space_char=False, **kwargs): + super(ParseQLabelDecode, self).__init__(character_dict_path, use_space_char) + self.max_text_length = kwargs.get("max_text_length", 25) + + def __call__(self, preds, label=None, *args, **kwargs): + if isinstance(preds, dict): + pred = preds["predict"] + else: + pred = preds + + char_num = ( + len(self.character_str) + 1 + ) # We don't predict nor , with only addition + if isinstance(pred, paddle.Tensor): + pred = pred.numpy() + B, L = pred.shape[:2] + pred = np.reshape(pred, [-1, char_num]) + + preds_idx = np.argmax(pred, axis=1) + preds_prob = np.max(pred, axis=1) + + preds_idx = np.reshape(preds_idx, [B, L]) + preds_prob = np.reshape(preds_prob, [B, L]) + + if label is None: + text = self.decode(preds_idx, preds_prob, raw=False) + return text + + text = self.decode(preds_idx, preds_prob, raw=False) + label = self.decode(label, None, False) + + return text, label + + def decode(self, text_index, text_prob=None, raw=False): + """convert text-index into text-label.""" + result_list = [] + ignored_tokens = self.get_ignored_tokens() + batch_size = len(text_index) + + for batch_idx in range(batch_size): + char_list = [] + conf_list = [] + + index = text_index[batch_idx, :] + prob = None + if text_prob is not None: + prob = text_prob[batch_idx, :] + + if not raw: + index, prob = self._filter(index, prob) + + for idx in range(len(index)): + if index[idx] in ignored_tokens: + continue + char_list.append(self.character[int(index[idx])]) + if text_prob is not None: + conf_list.append(prob[idx]) + else: + conf_list.append(1) + + text = "".join(char_list) + result_list.append((text, np.mean(conf_list).tolist())) + + return result_list + + def add_special_char(self, dict_character): + dict_character = [self.EOS] + dict_character + [self.BOS, self.PAD] + return dict_character + + def _filter(self, ids, probs=None): + ids = ids.tolist() + try: + eos_idx = ids.index(self.dict[self.EOS]) + except ValueError: + eos_idx = len(ids) # Nothing to truncate. + # Truncate after EOS + ids = ids[:eos_idx] + if probs is not None: + probs = probs[: eos_idx + 1] # but include prob. for EOS (if it exists) + return ids, probs + + def get_ignored_tokens(self): + return [self.dict[self.BOS], self.dict[self.EOS], self.dict[self.PAD]] + + +class SARLabelDecode(BaseRecLabelDecode): + """Convert between text-label and text-index""" + + def __init__(self, character_dict_path=None, use_space_char=False, **kwargs): + super(SARLabelDecode, self).__init__(character_dict_path, use_space_char) + + self.rm_symbol = kwargs.get("rm_symbol", False) + + def add_special_char(self, dict_character): + beg_end_str = "" + unknown_str = "" + padding_str = "" + dict_character = dict_character + [unknown_str] + self.unknown_idx = len(dict_character) - 1 + dict_character = dict_character + [beg_end_str] + self.start_idx = len(dict_character) - 1 + self.end_idx = len(dict_character) - 1 + dict_character = dict_character + [padding_str] + self.padding_idx = len(dict_character) - 1 + return dict_character + + def decode(self, text_index, text_prob=None, is_remove_duplicate=False): + """convert text-index into text-label.""" + result_list = [] + ignored_tokens = self.get_ignored_tokens() + + batch_size = len(text_index) + for batch_idx in range(batch_size): + char_list = [] + conf_list = [] + for idx in range(len(text_index[batch_idx])): + if text_index[batch_idx][idx] in ignored_tokens: + continue + if int(text_index[batch_idx][idx]) == int(self.end_idx): + if text_prob is None and idx == 0: + continue + else: + break + if is_remove_duplicate: + # only for predict + if ( + idx > 0 + and text_index[batch_idx][idx - 1] == text_index[batch_idx][idx] + ): + continue + char_list.append(self.character[int(text_index[batch_idx][idx])]) + if text_prob is not None: + conf_list.append(text_prob[batch_idx][idx]) + else: + conf_list.append(1) + text = "".join(char_list) + if self.rm_symbol: + comp = re.compile("[^A-Z^a-z^0-9^\u4e00-\u9fa5]") + text = text.lower() + text = comp.sub("", text) + result_list.append((text, np.mean(conf_list).tolist())) + return result_list + + def __call__(self, preds, label=None, *args, **kwargs): + if hasattr(preds, "numpy"): + preds = preds.numpy() + preds_idx = preds.argmax(axis=2) + preds_prob = preds.max(axis=2) + + text = self.decode(preds_idx, preds_prob, is_remove_duplicate=False) + + if label is None: + return text + label = self.decode(label, is_remove_duplicate=False) + return text, label + + def get_ignored_tokens(self): + return [self.padding_idx] + + +class SATRNLabelDecode(BaseRecLabelDecode): + """Convert between text-label and text-index""" + + def __init__(self, character_dict_path=None, use_space_char=False, **kwargs): + super(SATRNLabelDecode, self).__init__(character_dict_path, use_space_char) + + self.rm_symbol = kwargs.get("rm_symbol", False) + + def add_special_char(self, dict_character): + beg_end_str = "" + unknown_str = "" + padding_str = "" + dict_character = dict_character + [unknown_str] + self.unknown_idx = len(dict_character) - 1 + dict_character = dict_character + [beg_end_str] + self.start_idx = len(dict_character) - 1 + self.end_idx = len(dict_character) - 1 + dict_character = dict_character + [padding_str] + self.padding_idx = len(dict_character) - 1 + return dict_character + + def decode(self, text_index, text_prob=None, is_remove_duplicate=False): + """convert text-index into text-label.""" + result_list = [] + ignored_tokens = self.get_ignored_tokens() + + batch_size = len(text_index) + for batch_idx in range(batch_size): + char_list = [] + conf_list = [] + for idx in range(len(text_index[batch_idx])): + if text_index[batch_idx][idx] in ignored_tokens: + continue + if int(text_index[batch_idx][idx]) == int(self.end_idx): + if text_prob is None and idx == 0: + continue + else: + break + if is_remove_duplicate: + # only for predict + if ( + idx > 0 + and text_index[batch_idx][idx - 1] == text_index[batch_idx][idx] + ): + continue + char_list.append(self.character[int(text_index[batch_idx][idx])]) + if text_prob is not None: + conf_list.append(text_prob[batch_idx][idx]) + else: + conf_list.append(1) + text = "".join(char_list) + if self.rm_symbol: + comp = re.compile("[^A-Z^a-z^0-9^\u4e00-\u9fa5]") + text = text.lower() + text = comp.sub("", text) + result_list.append((text, np.mean(conf_list).tolist())) + return result_list + + def __call__(self, preds, label=None, *args, **kwargs): + if isinstance(preds, paddle.Tensor): + preds = preds.numpy() + preds_idx = preds.argmax(axis=2) + preds_prob = preds.max(axis=2) + + text = self.decode(preds_idx, preds_prob, is_remove_duplicate=False) + + if label is None: + return text + label = self.decode(label, is_remove_duplicate=False) + return text, label + + def get_ignored_tokens(self): + return [self.padding_idx] + + +class DistillationSARLabelDecode(SARLabelDecode): + """ + Convert + Convert between text-label and text-index + """ + + def __init__( + self, + character_dict_path=None, + use_space_char=False, + model_name=["student"], + key=None, + multi_head=False, + **kwargs, + ): + super(DistillationSARLabelDecode, self).__init__( + character_dict_path, use_space_char + ) + if not isinstance(model_name, list): + model_name = [model_name] + self.model_name = model_name + + self.key = key + self.multi_head = multi_head + + def __call__(self, preds, label=None, *args, **kwargs): + output = dict() + for name in self.model_name: + pred = preds[name] + if self.key is not None: + pred = pred[self.key] + if self.multi_head and isinstance(pred, dict): + pred = pred["sar"] + output[name] = super().__call__(pred, label=label, *args, **kwargs) + return output + + +class PRENLabelDecode(BaseRecLabelDecode): + """Convert between text-label and text-index""" + + def __init__(self, character_dict_path=None, use_space_char=False, **kwargs): + super(PRENLabelDecode, self).__init__(character_dict_path, use_space_char) + + def add_special_char(self, dict_character): + padding_str = "" # 0 + end_str = "" # 1 + unknown_str = "" # 2 + + dict_character = [padding_str, end_str, unknown_str] + dict_character + self.padding_idx = 0 + self.end_idx = 1 + self.unknown_idx = 2 + + return dict_character + + def decode(self, text_index, text_prob=None): + """convert text-index into text-label.""" + result_list = [] + batch_size = len(text_index) + + for batch_idx in range(batch_size): + char_list = [] + conf_list = [] + for idx in range(len(text_index[batch_idx])): + if text_index[batch_idx][idx] == self.end_idx: + break + if text_index[batch_idx][idx] in [self.padding_idx, self.unknown_idx]: + continue + char_list.append(self.character[int(text_index[batch_idx][idx])]) + if text_prob is not None: + conf_list.append(text_prob[batch_idx][idx]) + else: + conf_list.append(1) + + text = "".join(char_list) + if len(text) > 0: + result_list.append((text, np.mean(conf_list).tolist())) + else: + # here confidence of empty recog result is 1 + result_list.append(("", 1)) + return result_list + + def __call__(self, preds, label=None, *args, **kwargs): + if isinstance(preds, paddle.Tensor): + preds = preds.numpy() + preds_idx = preds.argmax(axis=2) + preds_prob = preds.max(axis=2) + text = self.decode(preds_idx, preds_prob) + if label is None: + return text + label = self.decode(label) + return text, label + + +class NRTRLabelDecode(BaseRecLabelDecode): + """Convert between text-label and text-index""" + + def __init__(self, character_dict_path=None, use_space_char=True, **kwargs): + super(NRTRLabelDecode, self).__init__(character_dict_path, use_space_char) + + def __call__(self, preds, label=None, *args, **kwargs): + if len(preds) == 2: + preds_id = preds[0] + preds_prob = preds[1] + if hasattr(preds_id, "numpy"): + preds_id = preds_id.numpy() + if hasattr(preds_prob, "numpy"): + preds_prob = preds_prob.numpy() + if preds_id[0][0] == 2: + preds_idx = preds_id[:, 1:] + preds_prob = preds_prob[:, 1:] + else: + preds_idx = preds_id + text = self.decode(preds_idx, preds_prob, is_remove_duplicate=False) + if label is None: + return text + label = self.decode(label[:, 1:]) + else: + if hasattr(preds, "numpy"): + preds = preds.numpy() + preds_idx = preds.argmax(axis=2) + preds_prob = preds.max(axis=2) + text = self.decode(preds_idx, preds_prob, is_remove_duplicate=False) + if label is None: + return text + label = self.decode(label[:, 1:]) + return text, label + + def add_special_char(self, dict_character): + dict_character = ["blank", "", "", ""] + dict_character + return dict_character + + def decode(self, text_index, text_prob=None, is_remove_duplicate=False): + """convert text-index into text-label.""" + result_list = [] + batch_size = len(text_index) + for batch_idx in range(batch_size): + char_list = [] + conf_list = [] + for idx in range(len(text_index[batch_idx])): + try: + char_idx = self.character[int(text_index[batch_idx][idx])] + except: + continue + if char_idx == "": # end + break + char_list.append(char_idx) + if text_prob is not None: + conf_list.append(text_prob[batch_idx][idx]) + else: + conf_list.append(1) + text = "".join(char_list) + result_list.append((text, np.mean(conf_list).tolist())) + return result_list + + +class ViTSTRLabelDecode(NRTRLabelDecode): + """Convert between text-label and text-index""" + + def __init__(self, character_dict_path=None, use_space_char=False, **kwargs): + super(ViTSTRLabelDecode, self).__init__(character_dict_path, use_space_char) + + def __call__(self, preds, label=None, *args, **kwargs): + if hasattr(preds, "numpy"): + preds = preds[:, 1:].numpy() + else: + preds = preds[:, 1:] + preds_idx = preds.argmax(axis=2) + preds_prob = preds.max(axis=2) + text = self.decode(preds_idx, preds_prob, is_remove_duplicate=False) + if label is None: + return text + label = self.decode(label[:, 1:]) + return text, label + + def add_special_char(self, dict_character): + dict_character = ["", ""] + dict_character + return dict_character + + +class ABINetLabelDecode(NRTRLabelDecode): + """Convert between text-label and text-index""" + + def __init__(self, character_dict_path=None, use_space_char=False, **kwargs): + super(ABINetLabelDecode, self).__init__(character_dict_path, use_space_char) + + def __call__(self, preds, label=None, *args, **kwargs): + if isinstance(preds, dict): + preds = preds["align"][-1].numpy() + elif hasattr(preds, "numpy"): + preds = preds.numpy() + else: + preds = preds + + preds_idx = preds.argmax(axis=2) + preds_prob = preds.max(axis=2) + text = self.decode(preds_idx, preds_prob, is_remove_duplicate=False) + if label is None: + return text + label = self.decode(label) + return text, label + + def add_special_char(self, dict_character): + dict_character = [""] + dict_character + return dict_character + + +class SPINLabelDecode(AttnLabelDecode): + """Convert between text-label and text-index""" + + def __init__(self, character_dict_path=None, use_space_char=False, **kwargs): + super(SPINLabelDecode, self).__init__(character_dict_path, use_space_char) + + def add_special_char(self, dict_character): + self.beg_str = "sos" + self.end_str = "eos" + dict_character = dict_character + dict_character = [self.beg_str] + [self.end_str] + dict_character + return dict_character + + +class VLLabelDecode(BaseRecLabelDecode): + """Convert between text-label and text-index""" + + def __init__(self, character_dict_path=None, use_space_char=False, **kwargs): + super(VLLabelDecode, self).__init__(character_dict_path, use_space_char) + self.max_text_length = kwargs.get("max_text_length", 25) + self.nclass = len(self.character) + 1 + + def decode(self, text_index, text_prob=None, is_remove_duplicate=False): + """convert text-index into text-label.""" + result_list = [] + ignored_tokens = self.get_ignored_tokens() + batch_size = len(text_index) + for batch_idx in range(batch_size): + selection = np.ones(len(text_index[batch_idx]), dtype=bool) + if is_remove_duplicate: + selection[1:] = text_index[batch_idx][1:] != text_index[batch_idx][:-1] + for ignored_token in ignored_tokens: + selection &= text_index[batch_idx] != ignored_token + + char_list = [ + self.character[text_id - 1] + for text_id in text_index[batch_idx][selection] + ] + if text_prob is not None: + conf_list = text_prob[batch_idx][selection] + else: + conf_list = [1] * len(selection) + if len(conf_list) == 0: + conf_list = [0] + + text = "".join(char_list) + result_list.append((text, np.mean(conf_list).tolist())) + return result_list + + def __call__(self, preds, label=None, length=None, *args, **kwargs): + if len(preds) == 2: # eval mode + text_pre, x = preds + b = text_pre.shape[1] + lenText = self.max_text_length + nsteps = self.max_text_length + + if not isinstance(text_pre, paddle.Tensor): + text_pre = paddle.to_tensor(text_pre, dtype="float32") + + out_res = paddle.zeros(shape=[lenText, b, self.nclass], dtype=x.dtype) + out_length = paddle.zeros(shape=[b], dtype=x.dtype) + now_step = 0 + for _ in range(nsteps): + if 0 in out_length and now_step < nsteps: + tmp_result = text_pre[now_step, :, :] + out_res[now_step] = tmp_result + tmp_result = tmp_result.topk(1)[1].squeeze(axis=1) + for j in range(b): + if out_length[j] == 0 and tmp_result[j] == 0: + out_length[j] = now_step + 1 + now_step += 1 + for j in range(0, b): + if int(out_length[j]) == 0: + out_length[j] = nsteps + start = 0 + output = paddle.zeros( + shape=[int(out_length.sum()), self.nclass], dtype=x.dtype + ) + for i in range(0, b): + cur_length = int(out_length[i]) + output[start : start + cur_length] = out_res[0:cur_length, i, :] + start += cur_length + net_out = output + length = out_length + + else: # train mode + net_out = preds[0] + length = length + net_out = paddle.concat([t[:l] for t, l in zip(net_out, length)]) + text = [] + if not isinstance(net_out, paddle.Tensor): + net_out = paddle.to_tensor(net_out, dtype="float32") + net_out = F.softmax(net_out, axis=1) + for i in range(0, length.shape[0]): + if i == 0: + start_idx = 0 + end_idx = int(length[i]) + else: + start_idx = int(length[:i].sum()) + end_idx = int(length[:i].sum() + length[i]) + preds_idx = net_out[start_idx:end_idx].topk(1)[1][:, 0].tolist() + preds_text = "".join( + [ + ( + self.character[idx - 1] + if idx > 0 and idx <= len(self.character) + else "" + ) + for idx in preds_idx + ] + ) + preds_prob = net_out[start_idx:end_idx].topk(1)[0][:, 0] + preds_prob = paddle.exp( + paddle.log(preds_prob).sum() / (preds_prob.shape[0] + 1e-6) + ) + text.append((preds_text, float(preds_prob))) + if label is None: + return text + label = self.decode(label) + return text, label + + +class CANLabelDecode(BaseRecLabelDecode): + """Convert between latex-symbol and symbol-index""" + + def __init__(self, character_dict_path=None, use_space_char=False, **kwargs): + super(CANLabelDecode, self).__init__(character_dict_path, use_space_char) + + def decode(self, text_index, preds_prob=None): + result_list = [] + batch_size = len(text_index) + for batch_idx in range(batch_size): + seq_end = text_index[batch_idx].argmin(0) + idx_list = text_index[batch_idx][:seq_end].tolist() + symbol_list = [self.character[idx] for idx in idx_list] + probs = [] + if preds_prob is not None: + probs = preds_prob[batch_idx][: len(symbol_list)].tolist() + + result_list.append([" ".join(symbol_list), probs]) + return result_list + + def __call__(self, preds, label=None, *args, **kwargs): + pred_prob, _, _, _ = preds + preds_idx = pred_prob.argmax(axis=2) + + text = self.decode(preds_idx) + if label is None: + return text + label = self.decode(label) + return text, label + + +class CPPDLabelDecode(NRTRLabelDecode): + """Convert between text-label and text-index""" + + def __init__(self, character_dict_path=None, use_space_char=False, **kwargs): + super(CPPDLabelDecode, self).__init__(character_dict_path, use_space_char) + + def __call__(self, preds, label=None, *args, **kwargs): + if isinstance(preds, tuple): + if isinstance(preds[-1], dict): + preds = preds[-1]["align"][-1].numpy() + else: + preds = preds[-1].numpy() + if isinstance(preds, paddle.Tensor): + preds = preds.numpy() + else: + preds = preds + preds_idx = preds.argmax(axis=2) + preds_prob = preds.max(axis=2) + text = self.decode(preds_idx, preds_prob, is_remove_duplicate=False) + if label is None: + return text + label = self.decode(label) + return text, label + + def add_special_char(self, dict_character): + dict_character = [""] + dict_character + return dict_character + + +class LaTeXOCRDecode(object): + """Convert between latex-symbol and symbol-index""" + + def __init__(self, rec_char_dict_path, **kwargs): + from tokenizers import Tokenizer as TokenizerFast + + super(LaTeXOCRDecode, self).__init__() + self.tokenizer = TokenizerFast.from_file(rec_char_dict_path) + + def post_process(self, s): + text_reg = r"(\\(operatorname|mathrm|text|mathbf)\s?\*? {.*?})" + letter = "[a-zA-Z]" + noletter = "[\W_^\d]" + names = [x[0].replace(" ", "") for x in re.findall(text_reg, s)] + s = re.sub(text_reg, lambda match: str(names.pop(0)), s) + news = s + while True: + s = news + news = re.sub(r"(?!\\ )(%s)\s+?(%s)" % (noletter, noletter), r"\1\2", s) + news = re.sub(r"(?!\\ )(%s)\s+?(%s)" % (noletter, letter), r"\1\2", news) + news = re.sub(r"(%s)\s+?(%s)" % (letter, noletter), r"\1\2", news) + if news == s: + break + return s + + def decode(self, tokens): + if len(tokens.shape) == 1: + tokens = tokens[None, :] + dec = [self.tokenizer.decode(tok) for tok in tokens] + dec_str_list = [ + "".join(detok.split(" ")) + .replace("Ġ", " ") + .replace("[EOS]", "") + .replace("[BOS]", "") + .replace("[PAD]", "") + .strip() + for detok in dec + ] + return [self.post_process(dec_str) for dec_str in dec_str_list] + + def __call__(self, preds, label=None, mode="eval", *args, **kwargs): + if mode == "train": + preds_idx = np.array(preds.argmax(axis=2)) + text = self.decode(preds_idx) + else: + text = self.decode(np.array(preds)) + if label is None: + return text + label = self.decode(np.array(label)) + return text, label + + +class UniMERNetDecode(object): + + SPECIAL_TOKENS_ATTRIBUTES = [ + "bos_token", + "eos_token", + "unk_token", + "sep_token", + "pad_token", + "cls_token", + "mask_token", + "additional_special_tokens", + ] + + def __init__( + self, + rec_char_dict_path, + **kwargs, + ): + from tokenizers import Tokenizer as TokenizerFast + from tokenizers import AddedToken + + self._unk_token = "" + self._bos_token = "" + self._eos_token = "" + self._pad_token = "" + self._sep_token = None + self._cls_token = None + self._mask_token = None + self._additional_special_tokens = [] + self.model_input_names = ["input_ids", "token_type_ids", "attention_mask"] + self.max_seq_len = 2048 + self.pad_token_id = 1 + self.bos_token_id = 0 + self.eos_token_id = 2 + self.padding_side = "right" + self.pad_token_id = 1 + self.pad_token = "" + self.pad_token_type_id = 0 + self.pad_to_multiple_of = None + fast_tokenizer_file = os.path.join(rec_char_dict_path, "tokenizer.json") + tokenizer_config_file = os.path.join( + rec_char_dict_path, "tokenizer_config.json" + ) + self.tokenizer = TokenizerFast.from_file(fast_tokenizer_file) + added_tokens_decoder = {} + added_tokens_map = {} + if tokenizer_config_file is not None: + with open( + tokenizer_config_file, encoding="utf-8" + ) as tokenizer_config_handle: + init_kwargs = json.load(tokenizer_config_handle) + if "added_tokens_decoder" in init_kwargs: + for idx, token in init_kwargs["added_tokens_decoder"].items(): + if isinstance(token, dict): + token = AddedToken(**token) + if isinstance(token, AddedToken): + added_tokens_decoder[int(idx)] = token + added_tokens_map[str(token)] = token + else: + raise ValueError( + f"Found a {token.__class__} in the saved `added_tokens_decoder`, should be a dictionary or an AddedToken instance" + ) + init_kwargs["added_tokens_decoder"] = added_tokens_decoder + added_tokens_decoder = init_kwargs.pop("added_tokens_decoder", {}) + tokens_to_add = [ + token + for index, token in sorted( + added_tokens_decoder.items(), key=lambda x: x[0] + ) + if token not in added_tokens_decoder + ] + added_tokens_encoder = self.added_tokens_encoder(added_tokens_decoder) + encoder = list(added_tokens_encoder.keys()) + [ + str(token) for token in tokens_to_add + ] + tokens_to_add += [ + token + for token in self.all_special_tokens_extended + if token not in encoder and token not in tokens_to_add + ] + if len(tokens_to_add) > 0: + is_last_special = None + tokens = [] + special_tokens = self.all_special_tokens + for token in tokens_to_add: + is_special = ( + (token.special or str(token) in special_tokens) + if isinstance(token, AddedToken) + else str(token) in special_tokens + ) + if is_last_special is None or is_last_special == is_special: + tokens.append(token) + else: + self._add_tokens(tokens, special_tokens=is_last_special) + tokens = [token] + is_last_special = is_special + if tokens: + self._add_tokens(tokens, special_tokens=is_last_special) + + def _add_tokens(self, new_tokens, special_tokens=False) -> int: + if special_tokens: + return self.tokenizer.add_special_tokens(new_tokens) + + return self.tokenizer.add_tokens(new_tokens) + + def added_tokens_encoder(self, added_tokens_decoder): + return { + k.content: v + for v, k in sorted(added_tokens_decoder.items(), key=lambda item: item[0]) + } + + @property + def all_special_tokens(self): + + all_toks = [str(s) for s in self.all_special_tokens_extended] + return all_toks + + @property + def all_special_tokens_extended(self): + all_tokens = [] + seen = set() + for value in self.special_tokens_map_extended.values(): + if isinstance(value, (list, tuple)): + tokens_to_add = [token for token in value if str(token) not in seen] + else: + tokens_to_add = [value] if str(value) not in seen else [] + seen.update(map(str, tokens_to_add)) + all_tokens.extend(tokens_to_add) + return all_tokens + + @property + def special_tokens_map_extended(self): + set_attr = {} + for attr in self.SPECIAL_TOKENS_ATTRIBUTES: + attr_value = getattr(self, "_" + attr) + if attr_value: + set_attr[attr] = attr_value + return set_attr + + def convert_ids_to_tokens(self, ids, skip_special_tokens: bool = False): + if isinstance(ids, int): + return self.tokenizer.id_to_token(ids) + tokens = [] + for index in ids: + index = int(index) + if skip_special_tokens and index in self.all_special_ids: + continue + tokens.append(self.tokenizer.id_to_token(index)) + return tokens + + def detokenize(self, tokens): + self.tokenizer.bos_token = "" + self.tokenizer.eos_token = "" + self.tokenizer.pad_token = "" + toks = [self.convert_ids_to_tokens(tok) for tok in tokens] + for b in range(len(toks)): + for i in reversed(range(len(toks[b]))): + if toks[b][i] is None: + toks[b][i] = "" + toks[b][i] = toks[b][i].replace("Ġ", " ").strip() + if toks[b][i] in ( + [ + self.tokenizer.bos_token, + self.tokenizer.eos_token, + self.tokenizer.pad_token, + ] + ): + del toks[b][i] + return toks + + def token2str(self, token_ids) -> list: + generated_text = [] + for tok_id in token_ids: + end_idx = np.argwhere(tok_id == 2) + if len(end_idx) > 0: + end_idx = int(end_idx[0][0]) + tok_id = tok_id[: end_idx + 1] + generated_text.append( + self.tokenizer.decode(tok_id, skip_special_tokens=True) + ) + generated_text = [self.post_process(text) for text in generated_text] + return generated_text + + def normalize(self, s): + text_reg = r"(\\(operatorname|mathrm|text|mathbf)\s?\*? {.*?})" + letter = "[a-zA-Z]" + noletter = "[\W_^\d]" + names = [x[0].replace(" ", "") for x in re.findall(text_reg, s)] + s = re.sub(text_reg, lambda match: str(names.pop(0)), s) + news = s + while True: + s = news + news = re.sub(r"(?!\\ )(%s)\s+?(%s)" % (noletter, noletter), r"\1\2", s) + news = re.sub(r"(?!\\ )(%s)\s+?(%s)" % (noletter, letter), r"\1\2", news) + news = re.sub(r"(%s)\s+?(%s)" % (letter, noletter), r"\1\2", news) + if news == s: + break + return s + + def post_process(self, text): + from ftfy import fix_text + + text = fix_text(text) + text = self.normalize(text) + return text + + def __call__(self, preds, label=None, mode="eval", *args, **kwargs): + if mode == "train": + preds_idx = np.array(preds.argmax(axis=2)) + text = self.token2str(preds_idx) + else: + text = self.token2str(np.array(preds)) + if label is None: + return text + label = self.token2str(np.array(label)) + return text, label diff --git a/modules/onnx_ocr_module/src/ppocr/postprocess/sast_postprocess.py b/modules/onnx_ocr_module/src/ppocr/postprocess/sast_postprocess.py new file mode 100644 index 0000000..fb49738 --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/postprocess/sast_postprocess.py @@ -0,0 +1,371 @@ +# Copyright (c) 2020 PaddlePaddle Authors. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import os +import sys + +__dir__ = os.path.dirname(__file__) +sys.path.append(__dir__) +sys.path.append(os.path.join(__dir__, "..")) + +import numpy as np +from .locality_aware_nms import nms_locality +import paddle +import cv2 +import time + + +class SASTPostProcess(object): + """ + The post process for SAST. + """ + + def __init__( + self, + score_thresh=0.5, + nms_thresh=0.2, + sample_pts_num=2, + shrink_ratio_of_width=0.3, + expand_scale=1.0, + tcl_map_thresh=0.5, + **kwargs, + ): + self.score_thresh = score_thresh + self.nms_thresh = nms_thresh + self.sample_pts_num = sample_pts_num + self.shrink_ratio_of_width = shrink_ratio_of_width + self.expand_scale = expand_scale + self.tcl_map_thresh = tcl_map_thresh + + # c++ la-nms is faster, but only support python 3.5 + self.is_python35 = False + if sys.version_info.major == 3 and sys.version_info.minor == 5: + self.is_python35 = True + + def point_pair2poly(self, point_pair_list): + """ + Transfer vertical point_pairs into poly point in clockwise. + """ + # construct poly + point_num = len(point_pair_list) * 2 + point_list = [0] * point_num + for idx, point_pair in enumerate(point_pair_list): + point_list[idx] = point_pair[0] + point_list[point_num - 1 - idx] = point_pair[1] + return np.array(point_list).reshape(-1, 2) + + def shrink_quad_along_width(self, quad, begin_width_ratio=0.0, end_width_ratio=1.0): + """ + Generate shrink_quad_along_width. + """ + ratio_pair = np.array( + [[begin_width_ratio], [end_width_ratio]], dtype=np.float32 + ) + p0_1 = quad[0] + (quad[1] - quad[0]) * ratio_pair + p3_2 = quad[3] + (quad[2] - quad[3]) * ratio_pair + return np.array([p0_1[0], p0_1[1], p3_2[1], p3_2[0]]) + + def expand_poly_along_width(self, poly, shrink_ratio_of_width=0.3): + """ + expand poly along width. + """ + point_num = poly.shape[0] + left_quad = np.array([poly[0], poly[1], poly[-2], poly[-1]], dtype=np.float32) + left_ratio = ( + -shrink_ratio_of_width + * np.linalg.norm(left_quad[0] - left_quad[3]) + / (np.linalg.norm(left_quad[0] - left_quad[1]) + 1e-6) + ) + left_quad_expand = self.shrink_quad_along_width(left_quad, left_ratio, 1.0) + right_quad = np.array( + [ + poly[point_num // 2 - 2], + poly[point_num // 2 - 1], + poly[point_num // 2], + poly[point_num // 2 + 1], + ], + dtype=np.float32, + ) + right_ratio = 1.0 + shrink_ratio_of_width * np.linalg.norm( + right_quad[0] - right_quad[3] + ) / (np.linalg.norm(right_quad[0] - right_quad[1]) + 1e-6) + right_quad_expand = self.shrink_quad_along_width(right_quad, 0.0, right_ratio) + poly[0] = left_quad_expand[0] + poly[-1] = left_quad_expand[-1] + poly[point_num // 2 - 1] = right_quad_expand[1] + poly[point_num // 2] = right_quad_expand[2] + return poly + + def restore_quad(self, tcl_map, tcl_map_thresh, tvo_map): + """Restore quad.""" + xy_text = np.argwhere(tcl_map[:, :, 0] > tcl_map_thresh) + xy_text = xy_text[:, ::-1] # (n, 2) + + # Sort the text boxes via the y axis + xy_text = xy_text[np.argsort(xy_text[:, 1])] + + scores = tcl_map[xy_text[:, 1], xy_text[:, 0], 0] + scores = scores[:, np.newaxis] + + # Restore + point_num = int(tvo_map.shape[-1] / 2) + assert point_num == 4 + tvo_map = tvo_map[xy_text[:, 1], xy_text[:, 0], :] + xy_text_tile = np.tile(xy_text, (1, point_num)) # (n, point_num * 2) + quads = xy_text_tile - tvo_map + + return scores, quads, xy_text + + def quad_area(self, quad): + """ + compute area of a quad. + """ + edge = [ + (quad[1][0] - quad[0][0]) * (quad[1][1] + quad[0][1]), + (quad[2][0] - quad[1][0]) * (quad[2][1] + quad[1][1]), + (quad[3][0] - quad[2][0]) * (quad[3][1] + quad[2][1]), + (quad[0][0] - quad[3][0]) * (quad[0][1] + quad[3][1]), + ] + return np.sum(edge) / 2.0 + + def nms(self, dets): + if self.is_python35: + from ppocr.utils.utility import check_install + + check_install("lanms", "lanms-nova") + import lanms + + dets = lanms.merge_quadrangle_n9(dets, self.nms_thresh) + else: + dets = nms_locality(dets, self.nms_thresh) + return dets + + def cluster_by_quads_tco(self, tcl_map, tcl_map_thresh, quads, tco_map): + """ + Cluster pixels in tcl_map based on quads. + """ + instance_count = quads.shape[0] + 1 # contain background + instance_label_map = np.zeros(tcl_map.shape[:2], dtype=np.int32) + if instance_count == 1: + return instance_count, instance_label_map + + # predict text center + xy_text = np.argwhere(tcl_map[:, :, 0] > tcl_map_thresh) + n = xy_text.shape[0] + xy_text = xy_text[:, ::-1] # (n, 2) + tco = tco_map[xy_text[:, 1], xy_text[:, 0], :] # (n, 2) + pred_tc = xy_text - tco + + # get gt text center + m = quads.shape[0] + gt_tc = np.mean(quads, axis=1) # (m, 2) + + pred_tc_tile = np.tile(pred_tc[:, np.newaxis, :], (1, m, 1)) # (n, m, 2) + gt_tc_tile = np.tile(gt_tc[np.newaxis, :, :], (n, 1, 1)) # (n, m, 2) + dist_mat = np.linalg.norm(pred_tc_tile - gt_tc_tile, axis=2) # (n, m) + xy_text_assign = np.argmin(dist_mat, axis=1) + 1 # (n,) + + instance_label_map[xy_text[:, 1], xy_text[:, 0]] = xy_text_assign + return instance_count, instance_label_map + + def estimate_sample_pts_num(self, quad, xy_text): + """ + Estimate sample points number. + """ + eh = ( + np.linalg.norm(quad[0] - quad[3]) + np.linalg.norm(quad[1] - quad[2]) + ) / 2.0 + ew = ( + np.linalg.norm(quad[0] - quad[1]) + np.linalg.norm(quad[2] - quad[3]) + ) / 2.0 + + dense_sample_pts_num = max(2, int(ew)) + dense_xy_center_line = xy_text[ + np.linspace( + 0, + xy_text.shape[0] - 1, + dense_sample_pts_num, + endpoint=True, + dtype=np.float32, + ).astype(np.int32) + ] + + dense_xy_center_line_diff = dense_xy_center_line[1:] - dense_xy_center_line[:-1] + estimate_arc_len = np.sum(np.linalg.norm(dense_xy_center_line_diff, axis=1)) + + sample_pts_num = max(2, int(estimate_arc_len / eh)) + return sample_pts_num + + def detect_sast( + self, + tcl_map, + tvo_map, + tbo_map, + tco_map, + ratio_w, + ratio_h, + src_w, + src_h, + shrink_ratio_of_width=0.3, + tcl_map_thresh=0.5, + offset_expand=1.0, + out_strid=4.0, + ): + """ + first resize the tcl_map, tvo_map and tbo_map to the input_size, then restore the polys + """ + # restore quad + scores, quads, xy_text = self.restore_quad(tcl_map, tcl_map_thresh, tvo_map) + dets = np.hstack((quads, scores)).astype(np.float32, copy=False) + dets = self.nms(dets) + if dets.shape[0] == 0: + return [] + quads = dets[:, :-1].reshape(-1, 4, 2) + + # Compute quad area + quad_areas = [] + for quad in quads: + quad_areas.append(-self.quad_area(quad)) + + # instance segmentation + # instance_count, instance_label_map = cv2.connectedComponents(tcl_map.astype(np.uint8), connectivity=8) + instance_count, instance_label_map = self.cluster_by_quads_tco( + tcl_map, tcl_map_thresh, quads, tco_map + ) + + # restore single poly with tcl instance. + poly_list = [] + for instance_idx in range(1, instance_count): + xy_text = np.argwhere(instance_label_map == instance_idx)[:, ::-1] + quad = quads[instance_idx - 1] + q_area = quad_areas[instance_idx - 1] + if q_area < 5: + continue + + # + len1 = float(np.linalg.norm(quad[0] - quad[1])) + len2 = float(np.linalg.norm(quad[1] - quad[2])) + min_len = min(len1, len2) + if min_len < 3: + continue + + # filter small CC + if xy_text.shape[0] <= 0: + continue + + # filter low confidence instance + xy_text_scores = tcl_map[xy_text[:, 1], xy_text[:, 0], 0] + if np.sum(xy_text_scores) / quad_areas[instance_idx - 1] < 0.1: + # if np.sum(xy_text_scores) / quad_areas[instance_idx - 1] < 0.05: + continue + + # sort xy_text + left_center_pt = np.array( + [[(quad[0, 0] + quad[-1, 0]) / 2.0, (quad[0, 1] + quad[-1, 1]) / 2.0]] + ) # (1, 2) + right_center_pt = np.array( + [[(quad[1, 0] + quad[2, 0]) / 2.0, (quad[1, 1] + quad[2, 1]) / 2.0]] + ) # (1, 2) + proj_unit_vec = (right_center_pt - left_center_pt) / ( + np.linalg.norm(right_center_pt - left_center_pt) + 1e-6 + ) + proj_value = np.sum(xy_text * proj_unit_vec, axis=1) + xy_text = xy_text[np.argsort(proj_value)] + + # Sample pts in tcl map + if self.sample_pts_num == 0: + sample_pts_num = self.estimate_sample_pts_num(quad, xy_text) + else: + sample_pts_num = self.sample_pts_num + xy_center_line = xy_text[ + np.linspace( + 0, + xy_text.shape[0] - 1, + sample_pts_num, + endpoint=True, + dtype=np.float32, + ).astype(np.int32) + ] + + point_pair_list = [] + for x, y in xy_center_line: + # get corresponding offset + offset = tbo_map[y, x, :].reshape(2, 2) + if offset_expand != 1.0: + offset_length = np.linalg.norm(offset, axis=1, keepdims=True) + expand_length = np.clip( + offset_length * (offset_expand - 1), a_min=0.5, a_max=3.0 + ) + offset_detal = offset / offset_length * expand_length + offset = offset + offset_detal + # original point + ori_yx = np.array([y, x], dtype=np.float32) + point_pair = ( + (ori_yx + offset)[:, ::-1] + * out_strid + / np.array([ratio_w, ratio_h]).reshape(-1, 2) + ) + point_pair_list.append(point_pair) + + # ndarry: (x, 2), expand poly along width + detected_poly = self.point_pair2poly(point_pair_list) + detected_poly = self.expand_poly_along_width( + detected_poly, shrink_ratio_of_width + ) + detected_poly[:, 0] = np.clip(detected_poly[:, 0], a_min=0, a_max=src_w) + detected_poly[:, 1] = np.clip(detected_poly[:, 1], a_min=0, a_max=src_h) + poly_list.append(detected_poly) + + return poly_list + + def __call__(self, outs_dict, shape_list): + score_list = outs_dict["f_score"] + border_list = outs_dict["f_border"] + tvo_list = outs_dict["f_tvo"] + tco_list = outs_dict["f_tco"] + if isinstance(score_list, paddle.Tensor): + score_list = score_list.numpy() + border_list = border_list.numpy() + tvo_list = tvo_list.numpy() + tco_list = tco_list.numpy() + + img_num = len(shape_list) + poly_lists = [] + for ino in range(img_num): + p_score = score_list[ino].transpose((1, 2, 0)) + p_border = border_list[ino].transpose((1, 2, 0)) + p_tvo = tvo_list[ino].transpose((1, 2, 0)) + p_tco = tco_list[ino].transpose((1, 2, 0)) + src_h, src_w, ratio_h, ratio_w = shape_list[ino] + + poly_list = self.detect_sast( + p_score, + p_tvo, + p_border, + p_tco, + ratio_w, + ratio_h, + src_w, + src_h, + shrink_ratio_of_width=self.shrink_ratio_of_width, + tcl_map_thresh=self.tcl_map_thresh, + offset_expand=self.expand_scale, + ) + poly_lists.append({"points": np.array(poly_list)}) + + return poly_lists diff --git a/modules/onnx_ocr_module/src/ppocr/postprocess/table_postprocess.py b/modules/onnx_ocr_module/src/ppocr/postprocess/table_postprocess.py new file mode 100644 index 0000000..11a877a --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/postprocess/table_postprocess.py @@ -0,0 +1,191 @@ +# Copyright (c) 2021 PaddlePaddle Authors. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import numpy as np +import paddle + +from .rec_postprocess import AttnLabelDecode + + +class TableLabelDecode(AttnLabelDecode): + """ """ + + def __init__(self, character_dict_path, merge_no_span_structure=False, **kwargs): + dict_character = [] + with open(character_dict_path, "rb") as fin: + lines = fin.readlines() + for line in lines: + line = line.decode("utf-8").strip("\n").strip("\r\n") + dict_character.append(line) + + if merge_no_span_structure: + if "" not in dict_character: + dict_character.append("") + if "" in dict_character: + dict_character.remove("") + + dict_character = self.add_special_char(dict_character) + self.dict = {} + for i, char in enumerate(dict_character): + self.dict[char] = i + self.character = dict_character + self.td_token = ["", ""] + + def __call__(self, preds, batch=None): + structure_probs = preds["structure_probs"] + bbox_preds = preds["loc_preds"] + if isinstance(structure_probs, paddle.Tensor): + structure_probs = structure_probs.numpy() + if isinstance(bbox_preds, paddle.Tensor): + bbox_preds = bbox_preds.numpy() + shape_list = batch[-1] + result = self.decode(structure_probs, bbox_preds, shape_list) + if len(batch) == 1: # only contains shape + return result + + label_decode_result = self.decode_label(batch) + return result, label_decode_result + + def decode(self, structure_probs, bbox_preds, shape_list): + """convert text-label into text-index.""" + ignored_tokens = self.get_ignored_tokens() + end_idx = self.dict[self.end_str] + + structure_idx = structure_probs.argmax(axis=2) + structure_probs = structure_probs.max(axis=2) + + structure_batch_list = [] + bbox_batch_list = [] + batch_size = len(structure_idx) + for batch_idx in range(batch_size): + structure_list = [] + bbox_list = [] + score_list = [] + for idx in range(len(structure_idx[batch_idx])): + char_idx = int(structure_idx[batch_idx][idx]) + if idx > 0 and char_idx == end_idx: + break + if char_idx in ignored_tokens: + continue + text = self.character[char_idx] + if text in self.td_token: + bbox = bbox_preds[batch_idx, idx] + bbox = self._bbox_decode(bbox, shape_list[batch_idx]) + bbox_list.append(bbox) + structure_list.append(text) + score_list.append(structure_probs[batch_idx, idx]) + structure_batch_list.append([structure_list, np.mean(score_list)]) + bbox_batch_list.append(np.array(bbox_list)) + result = { + "bbox_batch_list": bbox_batch_list, + "structure_batch_list": structure_batch_list, + } + return result + + def decode_label(self, batch): + """convert text-label into text-index.""" + structure_idx = batch[1] + gt_bbox_list = batch[2] + shape_list = batch[-1] + ignored_tokens = self.get_ignored_tokens() + end_idx = self.dict[self.end_str] + + structure_batch_list = [] + bbox_batch_list = [] + batch_size = len(structure_idx) + for batch_idx in range(batch_size): + structure_list = [] + bbox_list = [] + for idx in range(len(structure_idx[batch_idx])): + char_idx = int(structure_idx[batch_idx][idx]) + if idx > 0 and char_idx == end_idx: + break + if char_idx in ignored_tokens: + continue + structure_list.append(self.character[char_idx]) + + bbox = gt_bbox_list[batch_idx][idx] + if bbox.sum() != 0: + bbox = self._bbox_decode(bbox, shape_list[batch_idx]) + bbox_list.append(bbox) + structure_batch_list.append(structure_list) + bbox_batch_list.append(bbox_list) + result = { + "bbox_batch_list": bbox_batch_list, + "structure_batch_list": structure_batch_list, + } + return result + + def _bbox_decode(self, bbox, shape): + h, w, ratio_h, ratio_w, pad_h, pad_w = shape + h, w = pad_h, pad_w + bbox[0::2] *= w + bbox[1::2] *= h + bbox[0::2] /= ratio_w + bbox[1::2] /= ratio_h + return bbox + + +class TableMasterLabelDecode(TableLabelDecode): + """ """ + + def __init__( + self, + character_dict_path, + box_shape="ori", + merge_no_span_structure=True, + **kwargs, + ): + super(TableMasterLabelDecode, self).__init__( + character_dict_path, merge_no_span_structure + ) + self.box_shape = box_shape + assert box_shape in [ + "ori", + "pad", + ], "The shape used for box normalization must be ori or pad" + + def add_special_char(self, dict_character): + self.beg_str = "" + self.end_str = "" + self.unknown_str = "" + self.pad_str = "" + dict_character = dict_character + dict_character = dict_character + [ + self.unknown_str, + self.beg_str, + self.end_str, + self.pad_str, + ] + return dict_character + + def get_ignored_tokens(self): + pad_idx = self.dict[self.pad_str] + start_idx = self.dict[self.beg_str] + end_idx = self.dict[self.end_str] + unknown_idx = self.dict[self.unknown_str] + return [start_idx, end_idx, pad_idx, unknown_idx] + + def _bbox_decode(self, bbox, shape): + h, w, ratio_h, ratio_w, pad_h, pad_w = shape + if self.box_shape == "pad": + h, w = pad_h, pad_w + bbox[0::2] *= w + bbox[1::2] *= h + bbox[0::2] /= ratio_w + bbox[1::2] /= ratio_h + x, y, w, h = bbox + x1, y1, x2, y2 = x - w // 2, y - h // 2, x + w // 2, y + h // 2 + bbox = np.array([x1, y1, x2, y2]) + return bbox diff --git a/modules/onnx_ocr_module/src/ppocr/postprocess/vqa_token_re_layoutlm_postprocess.py b/modules/onnx_ocr_module/src/ppocr/postprocess/vqa_token_re_layoutlm_postprocess.py new file mode 100644 index 0000000..efdfffe --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/postprocess/vqa_token_re_layoutlm_postprocess.py @@ -0,0 +1,96 @@ +# Copyright (c) 2021 PaddlePaddle Authors. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +import paddle + + +class VQAReTokenLayoutLMPostProcess(object): + """Convert between text-label and text-index""" + + def __init__(self, **kwargs): + super(VQAReTokenLayoutLMPostProcess, self).__init__() + + def __call__(self, preds, label=None, *args, **kwargs): + pred_relations = preds["pred_relations"] + if isinstance(preds["pred_relations"], paddle.Tensor): + pred_relations = pred_relations.numpy() + pred_relations = self.decode_pred(pred_relations) + + if label is not None: + return self._metric(pred_relations, label) + else: + return self._infer(pred_relations, *args, **kwargs) + + def _metric(self, pred_relations, label): + return pred_relations, label[-1], label[-2] + + def _infer(self, pred_relations, *args, **kwargs): + ser_results = kwargs["ser_results"] + entity_idx_dict_batch = kwargs["entity_idx_dict_batch"] + + # merge relations and ocr info + results = [] + for pred_relation, ser_result, entity_idx_dict in zip( + pred_relations, ser_results, entity_idx_dict_batch + ): + result = [] + used_tail_id = [] + for relation in pred_relation: + if relation["tail_id"] in used_tail_id: + continue + used_tail_id.append(relation["tail_id"]) + ocr_info_head = ser_result[entity_idx_dict[relation["head_id"]]] + ocr_info_tail = ser_result[entity_idx_dict[relation["tail_id"]]] + result.append((ocr_info_head, ocr_info_tail)) + results.append(result) + return results + + def decode_pred(self, pred_relations): + pred_relations_new = [] + for pred_relation in pred_relations: + pred_relation_new = [] + pred_relation = pred_relation[1 : pred_relation[0, 0, 0] + 1] + for relation in pred_relation: + relation_new = dict() + relation_new["head_id"] = relation[0, 0] + relation_new["head"] = tuple(relation[1]) + relation_new["head_type"] = relation[2, 0] + relation_new["tail_id"] = relation[3, 0] + relation_new["tail"] = tuple(relation[4]) + relation_new["tail_type"] = relation[5, 0] + relation_new["type"] = relation[6, 0] + pred_relation_new.append(relation_new) + pred_relations_new.append(pred_relation_new) + return pred_relations_new + + +class DistillationRePostProcess(VQAReTokenLayoutLMPostProcess): + """ + DistillationRePostProcess + """ + + def __init__(self, model_name=["Student"], key=None, **kwargs): + super().__init__(**kwargs) + if not isinstance(model_name, list): + model_name = [model_name] + self.model_name = model_name + self.key = key + + def __call__(self, preds, *args, **kwargs): + output = dict() + for name in self.model_name: + pred = preds[name] + if self.key is not None: + pred = pred[self.key] + output[name] = super().__call__(pred, *args, **kwargs) + return output diff --git a/modules/onnx_ocr_module/src/ppocr/postprocess/vqa_token_ser_layoutlm_postprocess.py b/modules/onnx_ocr_module/src/ppocr/postprocess/vqa_token_ser_layoutlm_postprocess.py new file mode 100644 index 0000000..a10f070 --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/postprocess/vqa_token_ser_layoutlm_postprocess.py @@ -0,0 +1,116 @@ +# Copyright (c) 2021 PaddlePaddle Authors. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +import numpy as np +import paddle +from ppocr.utils.utility import load_vqa_bio_label_maps + + +class VQASerTokenLayoutLMPostProcess(object): + """Convert between text-label and text-index""" + + def __init__(self, class_path, **kwargs): + super(VQASerTokenLayoutLMPostProcess, self).__init__() + label2id_map, self.id2label_map = load_vqa_bio_label_maps(class_path) + + self.label2id_map_for_draw = dict() + for key in label2id_map: + if key.startswith("I-"): + self.label2id_map_for_draw[key] = label2id_map["B" + key[1:]] + else: + self.label2id_map_for_draw[key] = label2id_map[key] + + self.id2label_map_for_show = dict() + for key in self.label2id_map_for_draw: + val = self.label2id_map_for_draw[key] + if key == "O": + self.id2label_map_for_show[val] = key + if key.startswith("B-") or key.startswith("I-"): + self.id2label_map_for_show[val] = key[2:] + else: + self.id2label_map_for_show[val] = key + + def __call__(self, preds, batch=None, *args, **kwargs): + if isinstance(preds, tuple): + preds = preds[0] + if isinstance(preds, paddle.Tensor): + preds = preds.numpy() + + if batch is not None: + return self._metric(preds, batch[5]) + else: + return self._infer(preds, **kwargs) + + def _metric(self, preds, label): + pred_idxs = preds.argmax(axis=2) + decode_out_list = [[] for _ in range(pred_idxs.shape[0])] + label_decode_out_list = [[] for _ in range(pred_idxs.shape[0])] + + for i in range(pred_idxs.shape[0]): + for j in range(pred_idxs.shape[1]): + if label[i, j] != -100: + label_decode_out_list[i].append(self.id2label_map[label[i, j]]) + decode_out_list[i].append(self.id2label_map[pred_idxs[i, j]]) + return decode_out_list, label_decode_out_list + + def _infer(self, preds, segment_offset_ids, ocr_infos): + results = [] + + for pred, segment_offset_id, ocr_info in zip( + preds, segment_offset_ids, ocr_infos + ): + pred = np.argmax(pred, axis=1) + pred = [self.id2label_map[idx] for idx in pred] + + for idx in range(len(segment_offset_id)): + if idx == 0: + start_id = 0 + else: + start_id = segment_offset_id[idx - 1] + + end_id = segment_offset_id[idx] + + curr_pred = pred[start_id:end_id] + curr_pred = [self.label2id_map_for_draw[p] for p in curr_pred] + + if len(curr_pred) <= 0: + pred_id = 0 + else: + counts = np.bincount(curr_pred) + pred_id = np.argmax(counts) + ocr_info[idx]["pred_id"] = int(pred_id) + ocr_info[idx]["pred"] = self.id2label_map_for_show[int(pred_id)] + results.append(ocr_info) + return results + + +class DistillationSerPostProcess(VQASerTokenLayoutLMPostProcess): + """ + DistillationSerPostProcess + """ + + def __init__(self, class_path, model_name=["Student"], key=None, **kwargs): + super().__init__(class_path, **kwargs) + if not isinstance(model_name, list): + model_name = [model_name] + self.model_name = model_name + self.key = key + + def __call__(self, preds, batch=None, *args, **kwargs): + output = dict() + for name in self.model_name: + pred = preds[name] + if self.key is not None: + pred = pred[self.key] + output[name] = super().__call__(pred, batch=batch, *args, **kwargs) + return output diff --git a/modules/onnx_ocr_module/src/ppocr/utils/EN_symbol_dict.txt b/modules/onnx_ocr_module/src/ppocr/utils/EN_symbol_dict.txt new file mode 100644 index 0000000..87c6d67 --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/utils/EN_symbol_dict.txt @@ -0,0 +1,94 @@ +0 +1 +2 +3 +4 +5 +6 +7 +8 +9 +a +b +c +d +e +f +g +h +i +j +k +l +m +n +o +p +q +r +s +t +u +v +w +x +y +z +A +B +C +D +E +F +G +H +I +J +K +L +M +N +O +P +Q +R +S +T +U +V +W +X +Y +Z +! +" +# +$ +% +& +' +( +) +* ++ +, +- +. +/ +: +; +< += +> +? +@ +[ +\ +] +^ +_ +` +{ +| +} +~ diff --git a/modules/onnx_ocr_module/src/ppocr/utils/__init__.py b/modules/onnx_ocr_module/src/ppocr/utils/__init__.py new file mode 100644 index 0000000..abf198b --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/utils/__init__.py @@ -0,0 +1,13 @@ +# Copyright (c) 2020 PaddlePaddle Authors. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. diff --git a/modules/onnx_ocr_module/src/ppocr/utils/__pycache__/__init__.cpython-311.pyc b/modules/onnx_ocr_module/src/ppocr/utils/__pycache__/__init__.cpython-311.pyc new file mode 100644 index 0000000..c27f686 Binary files /dev/null and b/modules/onnx_ocr_module/src/ppocr/utils/__pycache__/__init__.cpython-311.pyc differ diff --git a/modules/onnx_ocr_module/src/ppocr/utils/__pycache__/logging.cpython-311.pyc b/modules/onnx_ocr_module/src/ppocr/utils/__pycache__/logging.cpython-311.pyc new file mode 100644 index 0000000..ae4c6ee Binary files /dev/null and b/modules/onnx_ocr_module/src/ppocr/utils/__pycache__/logging.cpython-311.pyc differ diff --git a/modules/onnx_ocr_module/src/ppocr/utils/__pycache__/network.cpython-311.pyc b/modules/onnx_ocr_module/src/ppocr/utils/__pycache__/network.cpython-311.pyc new file mode 100644 index 0000000..2e60d37 Binary files /dev/null and b/modules/onnx_ocr_module/src/ppocr/utils/__pycache__/network.cpython-311.pyc differ diff --git a/modules/onnx_ocr_module/src/ppocr/utils/__pycache__/poly_nms.cpython-311.pyc b/modules/onnx_ocr_module/src/ppocr/utils/__pycache__/poly_nms.cpython-311.pyc new file mode 100644 index 0000000..5ebfbd6 Binary files /dev/null and b/modules/onnx_ocr_module/src/ppocr/utils/__pycache__/poly_nms.cpython-311.pyc differ diff --git a/modules/onnx_ocr_module/src/ppocr/utils/__pycache__/utility.cpython-311.pyc b/modules/onnx_ocr_module/src/ppocr/utils/__pycache__/utility.cpython-311.pyc new file mode 100644 index 0000000..cc48983 Binary files /dev/null and b/modules/onnx_ocr_module/src/ppocr/utils/__pycache__/utility.cpython-311.pyc differ diff --git a/modules/onnx_ocr_module/src/ppocr/utils/__pycache__/visual.cpython-311.pyc b/modules/onnx_ocr_module/src/ppocr/utils/__pycache__/visual.cpython-311.pyc new file mode 100644 index 0000000..eed4846 Binary files /dev/null and b/modules/onnx_ocr_module/src/ppocr/utils/__pycache__/visual.cpython-311.pyc differ diff --git a/modules/onnx_ocr_module/src/ppocr/utils/dict/README.md b/modules/onnx_ocr_module/src/ppocr/utils/dict/README.md new file mode 100644 index 0000000..7b68200 --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/utils/dict/README.md @@ -0,0 +1,5 @@ +## Dictionary and Corpus + +Dictionary files (usually character level vocabulary) are included here for easier configuration. Corpus contributed by OSS contributors are listed here, please respect copyrights when using them at your own risk. + +- Burmese corpus: https://github.com/1chimaruGin/BurmeseCorpus diff --git a/modules/onnx_ocr_module/src/ppocr/utils/dict/ar_dict.txt b/modules/onnx_ocr_module/src/ppocr/utils/dict/ar_dict.txt new file mode 100644 index 0000000..fc63802 --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/utils/dict/ar_dict.txt @@ -0,0 +1,117 @@ +a +r +b +i +c +_ +m +g +/ +1 +0 +I +L +S +V +R +C +2 +v +l +6 +3 +9 +. +j +p +ا +ل +م +ر +ج +و +ح +ي +ة +5 +8 +7 +أ +ب +ض +4 +ك +س +ه +ث +ن +ط +ع +ت +غ +خ +ف +ئ +ز +إ +د +ص +ظ +ذ +ش +ى +ق +ؤ +آ +ء +s +e +n +w +t +u +z +d +A +N +G +h +o +E +T +H +O +B +y +F +U +J +X +W +P +Z +M +k +q +Y +Q +D +f +K +x +' +% +- +# +@ +! +& +$ +, +: +é +? ++ +É +( + diff --git a/modules/onnx_ocr_module/src/ppocr/utils/dict/arabic_dict.txt b/modules/onnx_ocr_module/src/ppocr/utils/dict/arabic_dict.txt new file mode 100644 index 0000000..916d421 --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/utils/dict/arabic_dict.txt @@ -0,0 +1,161 @@ +! +# +$ +% +& +' +( ++ +, +- +. +/ +0 +1 +2 +3 +4 +5 +6 +7 +8 +9 +: +? +@ +A +B +C +D +E +F +G +H +I +J +K +L +M +N +O +P +Q +R +S +T +U +V +W +X +Y +Z +_ +a +b +c +d +e +f +g +h +i +j +k +l +m +n +o +p +q +r +s +t +u +v +w +x +y +z +É +é +ء +آ +أ +ؤ +إ +ئ +ا +ب +ة +ت +ث +ج +ح +خ +د +ذ +ر +ز +س +ش +ص +ض +ط +ظ +ع +غ +ف +ق +ك +ل +م +ن +ه +و +ى +ي +ً +ٌ +ٍ +َ +ُ +ِ +ّ +ْ +ٓ +ٔ +ٰ +ٱ +ٹ +پ +چ +ڈ +ڑ +ژ +ک +ڭ +گ +ں +ھ +ۀ +ہ +ۂ +ۃ +ۆ +ۇ +ۈ +ۋ +ی +ې +ے +ۓ +ە +١ +٢ +٣ +٤ +٥ +٦ +٧ +٨ +٩ diff --git a/modules/onnx_ocr_module/src/ppocr/utils/dict/be_dict.txt b/modules/onnx_ocr_module/src/ppocr/utils/dict/be_dict.txt new file mode 100644 index 0000000..f8458ba --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/utils/dict/be_dict.txt @@ -0,0 +1,145 @@ +b +e +_ +i +m +g +/ +2 +0 +I +L +S +V +R +C +1 +v +a +l +6 +9 +4 +3 +. +j +p +п +а +з +б +у +г +н +ц +ь +8 +м +л +і +о +ў +ы +7 +5 +М +х +с +р +ф +я +е +д +ж +ю +ч +й +к +Д +в +Б +т +І +ш +ё +э +К +Л +Н +А +Ж +Г +В +П +З +Е +О +Р +С +У +Ё +Й +Т +Ч +Э +Ц +Ю +Ш +Ф +Х +Я +Ь +Ы +Ў +s +c +n +w +M +o +t +T +E +A +B +u +h +y +k +r +H +d +Y +O +U +F +f +x +D +G +N +K +P +z +J +X +W +Z +Q +% +- +q +@ +' +! +# +& +, +: +$ +( +? +é ++ +É + diff --git a/modules/onnx_ocr_module/src/ppocr/utils/dict/bengali_dict.txt b/modules/onnx_ocr_module/src/ppocr/utils/dict/bengali_dict.txt new file mode 100644 index 0000000..89ffadf --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/utils/dict/bengali_dict.txt @@ -0,0 +1,74 @@ +হ +থ +শ +৫ +ক +ও +য +০ +গ +দ +ড় +খ +য় +ঋ +ন +অ +৪ +এ +ব +ঠ +ঢ +৭ +৯ +ধ +ঙ +ট +ঝ +ৎ +ণ +ত +র +২ +চ +ঌ +ড +৬ +ঔ +প +ভ +ম +ঢ় +ঈ +৮ +ঘ +১ +ষ +৩ +ফ +ছ +ল +জ +আ +। +ঊ +ই +স +ঐ +উ +ঞ +া +্ +ু +ী +ে +ং +ি +় +ঁ +ৃ +ো +ূ +ৈ +ৌ +ঃ diff --git a/modules/onnx_ocr_module/src/ppocr/utils/dict/bg_dict.txt b/modules/onnx_ocr_module/src/ppocr/utils/dict/bg_dict.txt new file mode 100644 index 0000000..84713c3 --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/utils/dict/bg_dict.txt @@ -0,0 +1,140 @@ +! +# +$ +% +& +' +( ++ +, +- +. +/ +0 +1 +2 +3 +4 +5 +6 +7 +8 +9 +: +? +@ +A +B +C +D +E +F +G +H +I +J +K +L +M +N +O +P +Q +R +S +T +U +V +W +X +Y +Z +_ +a +b +c +d +e +f +g +h +i +j +k +l +m +n +o +p +q +r +s +t +u +v +w +x +y +z +É +é +А +Б +В +Г +Д +Е +Ж +З +И +Й +К +Л +М +Н +О +П +Р +С +Т +У +Ф +Х +Ц +Ч +Ш +Щ +Ъ +Ю +Я +а +б +в +г +д +е +ж +з +и +й +к +л +м +н +о +п +р +с +т +у +ф +х +ц +ч +ш +щ +ъ +ь +ю +я + diff --git a/modules/onnx_ocr_module/src/ppocr/utils/dict/bm_dict.txt b/modules/onnx_ocr_module/src/ppocr/utils/dict/bm_dict.txt new file mode 100644 index 0000000..bd68de9 --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/utils/dict/bm_dict.txt @@ -0,0 +1,160 @@ +က +ခ +ဂ +ဃ +င +စ +ဆ +ဇ +ဈ +ဉ +ည +ဋ +ဌ +ဍ +ဎ +ဏ +တ +ထ +ဒ +ဓ +န +ပ +ဖ +ဗ +ဘ +မ +ယ +ရ +လ +ဝ +သ +ဟ +ဠ +အ +ဢ +ဣ +ဤ +ဥ +ဦ +ဧ +ဨ +ဩ +ဪ +ါ +ာ +ိ +ီ +ု +ူ +ေ +ဲ +ဳ +ဴ +ဵ +ံ +့ +း +္ +် +ျ +ြ +ွ +ှ +ဿ +၀ +၁ +၂ +၃ +၄ +၅ +၆ +၇ +၈ +၉ +၊ +။ +၌ +၍ +၎ +၏ +ၐ +ၑ +ၒ +ၓ +ၔ +ၕ +ၖ +ၗ +ၘ +ၙ +ၚ +ၛ +ၜ +ၝ +ၞ +ၟ +ၠ +ၡ +ၢ +ၣ +ၤ +ၥ +ၦ +ၧ +ၨ +ၩ +ၪ +ၫ +ၬ +ၭ +ၮ +ၯ +ၰ +ၱ +ၲ +ၳ +ၴ +ၵ +ၶ +ၷ +ၸ +ၹ +ၺ +ၻ +ၼ +ၽ +ၾ +ၿ +ႀ +ႁ +ႂ +ႃ +ႄ +ႅ +ႆ +ႇ +ႈ +ႉ +ႊ +ႋ +ႌ +ႍ +ႎ +ႏ +႐ +႑ +႒ +႓ +႔ +႕ +႖ +႗ +႘ +႙ +ႚ +ႛ +ႜ +ႝ +႞ +႟ diff --git a/modules/onnx_ocr_module/src/ppocr/utils/dict/bm_dict_add.txt b/modules/onnx_ocr_module/src/ppocr/utils/dict/bm_dict_add.txt new file mode 100644 index 0000000..29b6155 --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/utils/dict/bm_dict_add.txt @@ -0,0 +1,3219 @@ +က +ခ +ဂ +ဃ +င +စ +ဆ +ဇ +ဈ +ဉ +ည +ဋ +ဌ +ဍ +ဎ +ဏ +တ +ထ +ဒ +ဓ +န +ပ +ဖ +ဗ +ဘ +မ +ယ +ရ +လ +ဝ +သ +ဟ +ဠ +အ +ဢ +ဣ +ဤ +ဥ +ဦ +ဧ +ဨ +ဩ +ဪ +ါ +ာ +ိ +ီ +ု +ူ +ေ +ဲ +ဳ +ဴ +ဵ +ံ +့ +း +္ +် +ျ +ြ +ွ +ှ +ဿ +၀ +၁ +၂ +၃ +၄ +၅ +၆ +၇ +၈ +၉ +၊ +။ +၌ +၍ +၎ +၏ +ၐ +ၑ +ၒ +ၓ +ၔ +ၕ +ၖ +ၗ +ၘ +ၙ +ၚ +ၛ +ၜ +ၝ +ၞ +ၟ +ၠ +ၡ +ၢ +ၣ +ၤ +ၥ +ၦ +ၧ +ၨ +ၩ +ၪ +ၫ +ၬ +ၭ +ၮ +ၯ +ၰ +ၱ +ၲ +ၳ +ၴ +ၵ +ၶ +ၷ +ၸ +ၹ +ၺ +ၻ +ၼ +ၽ +ၾ +ၿ +ႀ +ႁ +ႂ +ႃ +ႄ +ႅ +ႆ +ႇ +ႈ +ႉ +ႊ +ႋ +ႌ +ႍ +ႎ +ႏ +႐ +႑ +႒ +႓ +႔ +႕ +႖ +႗ +႘ +႙ +ႚ +ႛ +ႜ +ႝ +႞ +႟ + +Ⴀ +Ⴁ +Ⴂ +Ⴃ +Ⴄ +Ⴅ +Ⴆ +Ⴇ +Ⴈ +Ⴉ +Ⴊ +Ⴋ +ᄀ +ᄁ +ᄂ +ᄃ +ᄄ +ᄅ +ᄆ +ᄇ +ᄈ +ᄉ +ᄊ +ᄋ +ᄌ +ᄍ +ᄎ +ᄏ +ᄐ +ᄑ +ᄒ + +ဠ +အ +ဢ +ဣ +ဤ +ဥ +ဦ +ဧ +ဨ +ဩ +ဪ +ါ +ာ +ိ +ီ +ု +ူ +ေ +ဲ +ဳ +ဴ +ဵ +ံ +့ +း +္ +် +ျ +ြ +ွ +ှ +ဿ +၀ +၁ +၂ +၃ +၄ +၅ +၆ +၇ +၈ +၉ +၊ +။ +၌ +၍ +၎ +၏ +ၐ +ၑ +ၒ +ၓ +ၔ +ၕ +ၖ +ၗ +ၘ +ၙ +ၚ +ၛ +ၜ +ၝ +ၞ +ၟ +ၠ +ၡ +ၢ +ၣ +ၤ +ၥ +ၦ +ၧ +ၨ +ၩ +ၪ +ၫ +ၬ +ၭ +ၮ +ၯ +ၰ +ၱ +ၲ +ၳ +ၴ +ၵ +ၶ +ၷ +ၸ +ၹ +ၺ +ၻ +ၼ +ၽ +ၾ +ၿ +ႀ +ႁ +ႂ +ႃ +ႄ +ႅ +ႆ +ႇ +ႈ +ႉ +ႊ +ႋ +ႌ +ႍ +ႎ +ႏ +႐ +႑ +႒ +႓ +႔ +႕ +႖ +႗ +႘ +႙ +ႚ +ႛ +ႜ +ႝ +႞ +႟ + +က +ခ +ဂ +ဃ +င +စ +ဆ +ဇ +ဈ +ဉ +ည +ဋ +ဌ +ဍ +ဎ +ဏ +တ +ထ +ဒ +ဓ +န +ပ +ဖ +ဗ +ဘ +မ +ယ +ရ +လ +ဝ +သ +ဟ +ဠ +အ +ဢ +ဣ +ဤ +ဥ +ဦ +ဧ +ဨ +ဩ +ဪ +ါ +ာ +ိ +ီ +ု +ူ +ေ +ဲ +ဳ +ဴ +ဵ +ံ +့ +း +္ +် +ျ +ြ +ွ +ှ +ဿ +၀ +၁ +၂ +၃ +၄ +၅ +၆ +၇ +၈ +၉ +၊ +။ +၌ +၍ +၎ +၏ +ၐ +ၑ +ၒ +ၓ +ၔ +ၕ +ၖ +ၗ +ၘ +ၙ +ၚ +ၛ +ၜ +ၝ +ၞ +ၟ +ၠ +ၡ +ၢ +ၣ +ၤ +ၥ +ၦ +ၧ +ၨ +ၩ +ၪ +ၫ +ၬ +ၭ +ၮ +ၯ +ၰ +ၱ +ၲ +ၳ +ၴ +ၵ +ၶ +ၷ +ၸ +ၹ +ၺ +ၻ +ၼ +ၽ +ၾ +ၿ +ႀ +ႁ +ႂ +ႃ +ႄ +ႅ +ႆ +ႇ +ႈ +ႉ +ႊ +ႋ +ႌ +ႍ +ႎ +ႏ +႐ +႑ +႒ +႓ +႔ +႕ +႖ +႗ +႘ +႙ +ႚ +ႛ +ႜ +ႝ +႞ +႟ + +Ⴀ +Ⴁ +Ⴂ +Ⴃ +Ⴄ +Ⴅ +Ⴆ +Ⴇ +Ⴈ +Ⴉ +Ⴊ +Ⴋ +ᄀ +ᄁ +ᄂ +ᄃ +ᄄ +ᄅ +ᄆ +ᄇ +ᄈ +ᄉ +ᄊ +ᄋ +ᄌ +ᄍ +ᄎ +ᄏ +ᄐ +ᄑ +ᄒ + +ဠ +အ +ဢ +ဣ +ဤ +ဥ +ဦ +ဧ +ဨ +ဩ +ဪ +ါ +ာ +ိ +ီ +ု +ူ +ေ +ဲ +ဳ +ဴ +ဵ +ံ +့ +း +္ +် +ျ +ြ +ွ +ှ +ဿ +၀ +၁ +၂ +၃ +၄ +၅ +၆ +၇ +၈ +၉ +၊ +။ +၌ +၍ +၎ +၏ +ၐ +ၑ +ၒ +ၓ +ၔ +ၕ +ၖ +ၗ +ၘ +ၙ +ၚ +ၛ +ၜ +ၝ +ၞ +ၟ +ၠ +ၡ +ၢ +ၣ +ၤ +ၥ +ၦ +ၧ +ၨ +ၩ +ၪ +ၫ +ၬ +ၭ +ၮ +ၯ +ၰ +ၱ +ၲ +ၳ +ၴ +ၵ +ၶ +ၷ +ၸ +ၹ +ၺ +ၻ +ၼ +ၽ +ၾ +ၿ +ႀ +ႁ +ႂ +ႃ +ႄ +ႅ +ႆ +ႇ +ႈ +ႉ +ႊ +ႋ +ႌ +ႍ +ႎ +ႏ +႐ +႑ +႒ +႓ +႔ +႕ +႖ +႗ +႘ +႙ +ႚ +ႛ +ႜ +ႝ +႞ +႟ + +က +ခ +ဂ +ဃ +င +စ +ဆ +ဇ +ဈ +ဉ +ည +ဋ +ဌ +ဍ +ဎ +ဏ +တ +ထ +ဒ +ဓ +န +ပ +ဖ +ဗ +ဘ +မ +ယ +ရ +လ +ဝ +သ +ဟ +ဠ +အ +ဢ +ဣ +ဤ +ဥ +ဦ +ဧ +ဨ +ဩ +ဪ +ါ +ာ +ိ +ီ +ု +ူ +ေ +ဲ +ဳ +ဴ +ဵ +ံ +့ +း +္ +် +ျ +ြ +ွ +ှ +ဿ +၀ +၁ +၂ +၃ +၄ +၅ +၆ +၇ +၈ +၉ +၊ +။ +၌ +၍ +၎ +၏ +ၐ +ၑ +ၒ +ၓ +ၔ +ၕ +ၖ +ၗ +ၘ +ၙ +ၚ +ၛ +ၜ +ၝ +ၞ +ၟ +ၠ +ၡ +ၢ +ၣ +ၤ +ၥ +ၦ +ၧ +ၨ +ၩ +ၪ +ၫ +ၬ +ၭ +ၮ +ၯ +ၰ +ၱ +ၲ +ၳ +ၴ +ၵ +ၶ +ၷ +ၸ +ၹ +ၺ +ၻ +ၼ +ၽ +ၾ +ၿ +ႀ +ႁ +ႂ +ႃ +ႄ +ႅ +ႆ +ႇ +ႈ +ႉ +ႊ +ႋ +ႌ +ႍ +ႎ +ႏ +႐ +႑ +႒ +႓ +႔ +႕ +႖ +႗ +႘ +႙ +ႚ +ႛ +ႜ +ႝ +႞ +႟ + +Ⴀ +Ⴁ +Ⴂ +Ⴃ +Ⴄ +Ⴅ +Ⴆ +Ⴇ +Ⴈ +Ⴉ +Ⴊ +Ⴋ +ᄀ +ᄁ +ᄂ +ᄃ +ᄄ +ᄅ +ᄆ +ᄇ +ᄈ +ᄉ +ᄊ +ᄋ +ᄌ +ᄍ +ᄎ +ᄏ +ᄐ +ᄑ +ᄒ + +ဠ +အ +ဢ +ဣ +ဤ +ဥ +ဦ +ဧ +ဨ +ဩ +ဪ +ါ +ာ +ိ +ီ +ု +ူ +ေ +ဲ +ဳ +ဴ +ဵ +ံ +့ +း +္ +် +ျ +ြ +ွ +ှ +ဿ +၀ +၁ +၂ +၃ +၄ +၅ +၆ +၇ +၈ +၉ +၊ +။ +၌ +၍ +၎ +၏ +ၐ +ၑ +ၒ +ၓ +ၔ +ၕ +ၖ +ၗ +ၘ +ၙ +ၚ +ၛ +ၜ +ၝ +ၞ +ၟ +ၠ +ၡ +ၢ +ၣ +ၤ +ၥ +ၦ +ၧ +ၨ +ၩ +ၪ +ၫ +ၬ +ၭ +ၮ +ၯ +ၰ +ၱ +ၲ +ၳ +ၴ +ၵ +ၶ +ၷ +ၸ +ၹ +ၺ +ၻ +ၼ +ၽ +ၾ +ၿ +ႀ +ႁ +ႂ +ႃ +ႄ +ႅ +ႆ +ႇ +ႈ +ႉ +ႊ +ႋ +ႌ +ႍ +ႎ +ႏ +႐ +႑ +႒ +႓ +႔ +႕ +႖ +႗ +႘ +႙ +ႚ +ႛ +ႜ +ႝ +႞ +႟ + +က +ခ +ဂ +ဃ +င +စ +ဆ +ဇ +ဈ +ဉ +ည +ဋ +ဌ +ဍ +ဎ +ဏ +တ +ထ +ဒ +ဓ +န +ပ +ဖ +ဗ +ဘ +မ +ယ +ရ +လ +ဝ +သ +ဟ +ဠ +အ +ဢ +ဣ +ဤ +ဥ +ဦ +ဧ +ဨ +ဩ +ဪ +ါ +ာ +ိ +ီ +ု +ူ +ေ +ဲ +ဳ +ဴ +ဵ +ံ +့ +း +္ +် +ျ +ြ +ွ +ှ +ဿ +၀ +၁ +၂ +၃ +၄ +၅ +၆ +၇ +၈ +၉ +၊ +။ +၌ +၍ +၎ +၏ +ၐ +ၑ +ၒ +ၓ +ၔ +ၕ +ၖ +ၗ +ၘ +ၙ +ၚ +ၛ +ၜ +ၝ +ၞ +ၟ +ၠ +ၡ +ၢ +ၣ +ၤ +ၥ +ၦ +ၧ +ၨ +ၩ +ၪ +ၫ +ၬ +ၭ +ၮ +ၯ +ၰ +ၱ +ၲ +ၳ +ၴ +ၵ +ၶ +ၷ +ၸ +ၹ +ၺ +ၻ +ၼ +ၽ +ၾ +ၿ +ႀ +ႁ +ႂ +ႃ +ႄ +ႅ +ႆ +ႇ +ႈ +ႉ +ႊ +ႋ +ႌ +ႍ +ႎ +ႏ +႐ +႑ +႒ +႓ +႔ +႕ +႖ +႗ +႘ +႙ +ႚ +ႛ +ႜ +ႝ +႞ +႟ + +Ⴀ +Ⴁ +Ⴂ +Ⴃ +Ⴄ +Ⴅ +Ⴆ +Ⴇ +Ⴈ +Ⴉ +Ⴊ +Ⴋ +ᄀ +ᄁ +ᄂ +ᄃ +ᄄ +ᄅ +ᄆ +ᄇ +ᄈ +ᄉ +ᄊ +ᄋ +ᄌ +ᄍ +ᄎ +ᄏ +ᄐ +ᄑ +ᄒ + +ဠ +အ +ဢ +ဣ +ဤ +ဥ +ဦ +ဧ +ဨ +ဩ +ဪ +ါ +ာ +ိ +ီ +ု +ူ +ေ +ဲ +ဳ +ဴ +ဵ +ံ +့ +း +္ +် +ျ +ြ +ွ +ှ +ဿ +၀ +၁ +၂ +၃ +၄ +၅ +၆ +၇ +၈ +၉ +၊ +။ +၌ +၍ +၎ +၏ +ၐ +ၑ +ၒ +ၓ +ၔ +ၕ +ၖ +ၗ +ၘ +ၙ +ၚ +ၛ +ၜ +ၝ +ၞ +ၟ +ၠ +ၡ +ၢ +ၣ +ၤ +ၥ +ၦ +ၧ +ၨ +ၩ +ၪ +ၫ +ၬ +ၭ +ၮ +ၯ +ၰ +ၱ +ၲ +ၳ +ၴ +ၵ +ၶ +ၷ +ၸ +ၹ +ၺ +ၻ +ၼ +ၽ +ၾ +ၿ +ႀ +ႁ +ႂ +ႃ +ႄ +ႅ +ႆ +ႇ +ႈ +ႉ +ႊ +ႋ +ႌ +ႍ +ႎ +ႏ +႐ +႑ +႒ +႓ +႔ +႕ +႖ +႗ +႘ +႙ +ႚ +ႛ +ႜ +ႝ +႞ +႟ + +က +ခ +ဂ +ဃ +င +စ +ဆ +ဇ +ဈ +ဉ +ည +ဋ +ဌ +ဍ +ဎ +ဏ +တ +ထ +ဒ +ဓ +န +ပ +ဖ +ဗ +ဘ +မ +ယ +ရ +လ +ဝ +သ +ဟ +ဠ +အ +ဢ +ဣ +ဤ +ဥ +ဦ +ဧ +ဨ +ဩ +ဪ +ါ +ာ +ိ +ီ +ု +ူ +ေ +ဲ +ဳ +ဴ +ဵ +ံ +့ +း +္ +် +ျ +ြ +ွ +ှ +ဿ +၀ +၁ +၂ +၃ +၄ +၅ +၆ +၇ +၈ +၉ +၊ +။ +၌ +၍ +၎ +၏ +ၐ +ၑ +ၒ +ၓ +ၔ +ၕ +ၖ +ၗ +ၘ +ၙ +ၚ +ၛ +ၜ +ၝ +ၞ +ၟ +ၠ +ၡ +ၢ +ၣ +ၤ +ၥ +ၦ +ၧ +ၨ +ၩ +ၪ +ၫ +ၬ +ၭ +ၮ +ၯ +ၰ +ၱ +ၲ +ၳ +ၴ +ၵ +ၶ +ၷ +ၸ +ၹ +ၺ +ၻ +ၼ +ၽ +ၾ +ၿ +ႀ +ႁ +ႂ +ႃ +ႄ +ႅ +ႆ +ႇ +ႈ +ႉ +ႊ +ႋ +ႌ +ႍ +ႎ +ႏ +႐ +႑ +႒ +႓ +႔ +႕ +႖ +႗ +႘ +႙ +ႚ +ႛ +ႜ +ႝ +႞ +႟ + +Ⴀ +Ⴁ +Ⴂ +Ⴃ +Ⴄ +Ⴅ +Ⴆ +Ⴇ +Ⴈ +Ⴉ +Ⴊ +Ⴋ +ᄀ +ᄁ +ᄂ +ᄃ +ᄄ +ᄅ +ᄆ +ᄇ +ᄈ +ᄉ +ᄊ +ᄋ +ᄌ +ᄍ +ᄎ +ᄏ +ᄐ +ᄑ +ᄒ + +ဠ +အ +ဢ +ဣ +ဤ +ဥ +ဦ +ဧ +ဨ +ဩ +ဪ +ါ +ာ +ိ +ီ +ု +ူ +ေ +ဲ +ဳ +ဴ +ဵ +ံ +့ +း +္ +် +ျ +ြ +ွ +ှ +ဿ +၀ +၁ +၂ +၃ +၄ +၅ +၆ +၇ +၈ +၉ +၊ +။ +၌ +၍ +၎ +၏ +ၐ +ၑ +ၒ +ၓ +ၔ +ၕ +ၖ +ၗ +ၘ +ၙ +ၚ +ၛ +ၜ +ၝ +ၞ +ၟ +ၠ +ၡ +ၢ +ၣ +ၤ +ၥ +ၦ +ၧ +ၨ +ၩ +ၪ +ၫ +ၬ +ၭ +ၮ +ၯ +ၰ +ၱ +ၲ +ၳ +ၴ +ၵ +ၶ +ၷ +ၸ +ၹ +ၺ +ၻ +ၼ +ၽ +ၾ +ၿ +ႀ +ႁ +ႂ +ႃ +ႄ +ႅ +ႆ +ႇ +ႈ +ႉ +ႊ +ႋ +ႌ +ႍ +ႎ +ႏ +႐ +႑ +႒ +႓ +႔ +႕ +႖ +႗ +႘ +႙ +ႚ +ႛ +ႜ +ႝ +႞ +႟ + +က +ခ +ဂ +ဃ +င +စ +ဆ +ဇ +ဈ +ဉ +ည +ဋ +ဌ +ဍ +ဎ +ဏ +တ +ထ +ဒ +ဓ +န +ပ +ဖ +ဗ +ဘ +မ +ယ +ရ +လ +ဝ +သ +ဟ +ဠ +အ +ဢ +ဣ +ဤ +ဥ +ဦ +ဧ +ဨ +ဩ +ဪ +ါ +ာ +ိ +ီ +ု +ူ +ေ +ဲ +ဳ +ဴ +ဵ +ံ +့ +း +္ +် +ျ +ြ +ွ +ှ +ဿ +၀ +၁ +၂ +၃ +၄ +၅ +၆ +၇ +၈ +၉ +၊ +။ +၌ +၍ +၎ +၏ +ၐ +ၑ +ၒ +ၓ +ၔ +ၕ +ၖ +ၗ +ၘ +ၙ +ၚ +ၛ +ၜ +ၝ +ၞ +ၟ +ၠ +ၡ +ၢ +ၣ +ၤ +ၥ +ၦ +ၧ +ၨ +ၩ +ၪ +ၫ +ၬ +ၭ +ၮ +ၯ +ၰ +ၱ +ၲ +ၳ +ၴ +ၵ +ၶ +ၷ +ၸ +ၹ +ၺ +ၻ +ၼ +ၽ +ၾ +ၿ +ႀ +ႁ +ႂ +ႃ +ႄ +ႅ +ႆ +ႇ +ႈ +ႉ +ႊ +ႋ +ႌ +ႍ +ႎ +ႏ +႐ +႑ +႒ +႓ +႔ +႕ +႖ +႗ +႘ +႙ +ႚ +ႛ +ႜ +ႝ +႞ +႟ + +Ⴀ +Ⴁ +Ⴂ +Ⴃ +Ⴄ +Ⴅ +Ⴆ +Ⴇ +Ⴈ +Ⴉ +Ⴊ +Ⴋ +ᄀ +ᄁ +ᄂ +ᄃ +ᄄ +ᄅ +ᄆ +ᄇ +ᄈ +ᄉ +ᄊ +ᄋ +ᄌ +ᄍ +ᄎ +ᄏ +ᄐ +ᄑ +ᄒ + +ဠ +အ +ဢ +ဣ +ဤ +ဥ +ဦ +ဧ +ဨ +ဩ +ဪ +ါ +ာ +ိ +ီ +ု +ူ +ေ +ဲ +ဳ +ဴ +ဵ +ံ +့ +း +္ +် +ျ +ြ +ွ +ှ +ဿ +၀ +၁ +၂ +၃ +၄ +၅ +၆ +၇ +၈ +၉ +၊ +။ +၌ +၍ +၎ +၏ +ၐ +ၑ +ၒ +ၓ +ၔ +ၕ +ၖ +ၗ +ၘ +ၙ +ၚ +ၛ +ၜ +ၝ +ၞ +ၟ +ၠ +ၡ +ၢ +ၣ +ၤ +ၥ +ၦ +ၧ +ၨ +ၩ +ၪ +ၫ +ၬ +ၭ +ၮ +ၯ +ၰ +ၱ +ၲ +ၳ +ၴ +ၵ +ၶ +ၷ +ၸ +ၹ +ၺ +ၻ +ၼ +ၽ +ၾ +ၿ +ႀ +ႁ +ႂ +ႃ +ႄ +ႅ +ႆ +ႇ +ႈ +ႉ +ႊ +ႋ +ႌ +ႍ +ႎ +ႏ +႐ +႑ +႒ +႓ +႔ +႕ +႖ +႗ +႘ +႙ +ႚ +ႛ +ႜ +ႝ +႞ +႟ + +က +ခ +ဂ +ဃ +င +စ +ဆ +ဇ +ဈ +ဉ +ည +ဋ +ဌ +ဍ +ဎ +ဏ +တ +ထ +ဒ +ဓ +န +ပ +ဖ +ဗ +ဘ +မ +ယ +ရ +လ +ဝ +သ +ဟ +ဠ +အ +ဢ +ဣ +ဤ +ဥ +ဦ +ဧ +ဨ +ဩ +ဪ +ါ +ာ +ိ +ီ +ု +ူ +ေ +ဲ +ဳ +ဴ +ဵ +ံ +့ +း +္ +် +ျ +ြ +ွ +ှ +ဿ +၀ +၁ +၂ +၃ +၄ +၅ +၆ +၇ +၈ +၉ +၊ +။ +၌ +၍ +၎ +၏ +ၐ +ၑ +ၒ +ၓ +ၔ +ၕ +ၖ +ၗ +ၘ +ၙ +ၚ +ၛ +ၜ +ၝ +ၞ +ၟ +ၠ +ၡ +ၢ +ၣ +ၤ +ၥ +ၦ +ၧ +ၨ +ၩ +ၪ +ၫ +ၬ +ၭ +ၮ +ၯ +ၰ +ၱ +ၲ +ၳ +ၴ +ၵ +ၶ +ၷ +ၸ +ၹ +ၺ +ၻ +ၼ +ၽ +ၾ +ၿ +ႀ +ႁ +ႂ +ႃ +ႄ +ႅ +ႆ +ႇ +ႈ +ႉ +ႊ +ႋ +ႌ +ႍ +ႎ +ႏ +႐ +႑ +႒ +႓ +႔ +႕ +႖ +႗ +႘ +႙ +ႚ +ႛ +ႜ +ႝ +႞ +႟ + +Ⴀ +Ⴁ +Ⴂ +Ⴃ +Ⴄ +Ⴅ +Ⴆ +Ⴇ +Ⴈ +Ⴉ +Ⴊ +Ⴋ +ᄀ +ᄁ +ᄂ +ᄃ +ᄄ +ᄅ +ᄆ +ᄇ +ᄈ +ᄉ +ᄊ +ᄋ +ᄌ +ᄍ +ᄎ +ᄏ +ᄐ +ᄑ +ᄒ + +ဠ +အ +ဢ +ဣ +ဤ +ဥ +ဦ +ဧ +ဨ +ဩ +ဪ +ါ +ာ +ိ +ီ +ု +ူ +ေ +ဲ +ဳ +ဴ +ဵ +ံ +့ +း +္ +် +ျ +ြ +ွ +ှ +ဿ +၀ +၁ +၂ +၃ +၄ +၅ +၆ +၇ +၈ +၉ +၊ +။ +၌ +၍ +၎ +၏ +ၐ +ၑ +ၒ +ၓ +ၔ +ၕ +ၖ +ၗ +ၘ +ၙ +ၚ +ၛ +ၜ +ၝ +ၞ +ၟ +ၠ +ၡ +ၢ +ၣ +ၤ +ၥ +ၦ +ၧ +ၨ +ၩ +ၪ +ၫ +ၬ +ၭ +ၮ +ၯ +ၰ +ၱ +ၲ +ၳ +ၴ +ၵ +ၶ +ၷ +ၸ +ၹ +ၺ +ၻ +ၼ +ၽ +ၾ +ၿ +ႀ +ႁ +ႂ +ႃ +ႄ +ႅ +ႆ +ႇ +ႈ +ႉ +ႊ +ႋ +ႌ +ႍ +ႎ +ႏ +႐ +႑ +႒ +႓ +႔ +႕ +႖ +႗ +႘ +႙ +ႚ +ႛ +ႜ +ႝ +႞ +႟ + +က +ခ +ဂ +ဃ +င +စ +ဆ +ဇ +ဈ +ဉ +ည +ဋ +ဌ +ဍ +ဎ +ဏ +တ +ထ +ဒ +ဓ +န +ပ +ဖ +ဗ +ဘ +မ +ယ +ရ +လ +ဝ +သ +ဟ +ဠ +အ +ဢ +ဣ +ဤ +ဥ +ဦ +ဧ +ဨ +ဩ +ဪ +ါ +ာ +ိ +ီ +ု +ူ +ေ +ဲ +ဳ +ဴ +ဵ +ံ +့ +း +္ +် +ျ +ြ +ွ +ှ +ဿ +၀ +၁ +၂ +၃ +၄ +၅ +၆ +၇ +၈ +၉ +၊ +။ +၌ +၍ +၎ +၏ +ၐ +ၑ +ၒ +ၓ +ၔ +ၕ +ၖ +ၗ +ၘ +ၙ +ၚ +ၛ +ၜ +ၝ +ၞ +ၟ +ၠ +ၡ +ၢ +ၣ +ၤ +ၥ +ၦ +ၧ +ၨ +ၩ +ၪ +ၫ +ၬ +ၭ +ၮ +ၯ +ၰ +ၱ +ၲ +ၳ +ၴ +ၵ +ၶ +ၷ +ၸ +ၹ +ၺ +ၻ +ၼ +ၽ +ၾ +ၿ +ႀ +ႁ +ႂ +ႃ +ႄ +ႅ +ႆ +ႇ +ႈ +ႉ +ႊ +ႋ +ႌ +ႍ +ႎ +ႏ +႐ +႑ +႒ +႓ +႔ +႕ +႖ +႗ +႘ +႙ +ႚ +ႛ +ႜ +ႝ +႞ +႟ + +Ⴀ +Ⴁ +Ⴂ +Ⴃ +Ⴄ +Ⴅ +Ⴆ +Ⴇ +Ⴈ +Ⴉ +Ⴊ +Ⴋ +ᄀ +ᄁ +ᄂ +ᄃ +ᄄ +ᄅ +ᄆ +ᄇ +ᄈ +ᄉ +ᄊ +ᄋ +ᄌ +ᄍ +ᄎ +ᄏ +ᄐ +ᄑ +ᄒ + +ဠ +အ +ဢ +ဣ +ဤ +ဥ +ဦ +ဧ +ဨ +ဩ +ဪ +ါ +ာ +ိ +ီ +ု +ူ +ေ +ဲ +ဳ +ဴ +ဵ +ံ +့ +း +္ +် +ျ +ြ +ွ +ှ +ဿ +၀ +၁ +၂ +၃ +၄ +၅ +၆ +၇ +၈ +၉ +၊ +။ +၌ +၍ +၎ +၏ +ၐ +ၑ +ၒ +ၓ +ၔ +ၕ +ၖ +ၗ +ၘ +ၙ +ၚ +ၛ +ၜ +ၝ +ၞ +ၟ +ၠ +ၡ +ၢ +ၣ +ၤ +ၥ +ၦ +ၧ +ၨ +ၩ +ၪ +ၫ +ၬ +ၭ +ၮ +ၯ +ၰ +ၱ +ၲ +ၳ +ၴ +ၵ +ၶ +ၷ +ၸ +ၹ +ၺ +ၻ +ၼ +ၽ +ၾ +ၿ +ႀ +ႁ +ႂ +ႃ +ႄ +ႅ +ႆ +ႇ +ႈ +ႉ +ႊ +ႋ +ႌ +ႍ +ႎ +ႏ +႐ +႑ +႒ +႓ +႔ +႕ +႖ +႗ +႘ +႙ +ႚ +ႛ +ႜ +ႝ +႞ +႟ + +က +ခ +ဂ +ဃ +င +စ +ဆ +ဇ +ဈ +ဉ +ည +ဋ +ဌ +ဍ +ဎ +ဏ +တ +ထ +ဒ +ဓ +န +ပ +ဖ +ဗ +ဘ +မ +ယ +ရ +လ +ဝ +သ +ဟ +ဠ +အ +ဢ +ဣ +ဤ +ဥ +ဦ +ဧ +ဨ +ဩ +ဪ +ါ +ာ +ိ +ီ +ု +ူ +ေ +ဲ +ဳ +ဴ +ဵ +ံ +့ +း +္ +် +ျ +ြ +ွ +ှ +ဿ +၀ +၁ +၂ +၃ +၄ +၅ +၆ +၇ +၈ +၉ +၊ +။ +၌ +၍ +၎ +၏ +ၐ +ၑ +ၒ +ၓ +ၔ +ၕ +ၖ +ၗ +ၘ +ၙ +ၚ +ၛ +ၜ +ၝ +ၞ +ၟ +ၠ +ၡ +ၢ +ၣ +ၤ +ၥ +ၦ +ၧ +ၨ +ၩ +ၪ +ၫ +ၬ +ၭ +ၮ +ၯ +ၰ +ၱ +ၲ +ၳ +ၴ +ၵ +ၶ +ၷ +ၸ +ၹ +ၺ +ၻ +ၼ +ၽ +ၾ +ၿ +ႀ +ႁ +ႂ +ႃ +ႄ +ႅ +ႆ +ႇ +ႈ +ႉ +ႊ +ႋ +ႌ +ႍ +ႎ +ႏ +႐ +႑ +႒ +႓ +႔ +႕ +႖ +႗ +႘ +႙ +ႚ +ႛ +ႜ +ႝ +႞ +႟ + +Ⴀ +Ⴁ +Ⴂ +Ⴃ +Ⴄ +Ⴅ +Ⴆ +Ⴇ +Ⴈ +Ⴉ +Ⴊ +Ⴋ +ᄀ +ᄁ +ᄂ +ᄃ +ᄄ +ᄅ +ᄆ +ᄇ +ᄈ +ᄉ +ᄊ +ᄋ +ᄌ +ᄍ +ᄎ +ᄏ +ᄐ +ᄑ +ᄒ + +ဠ +အ +ဢ +ဣ +ဤ +ဥ +ဦ +ဧ +ဨ +ဩ +ဪ +ါ +ာ +ိ +ီ +ု +ူ +ေ +ဲ +ဳ +ဴ +ဵ +ံ +့ +း +္ +် +ျ +ြ +ွ +ှ +ဿ +၀ +၁ +၂ +၃ +၄ +၅ +၆ +၇ +၈ +၉ +၊ +။ +၌ +၍ +၎ +၏ +ၐ +ၑ +ၒ +ၓ +ၔ +ၕ +ၖ +ၗ +ၘ +ၙ +ၚ +ၛ +ၜ +ၝ +ၞ +ၟ +ၠ +ၡ +ၢ +ၣ +ၤ +ၥ +ၦ +ၧ +ၨ +ၩ +ၪ +ၫ +ၬ +ၭ +ၮ +ၯ +ၰ +ၱ +ၲ +ၳ +ၴ +ၵ +ၶ +ၷ +ၸ +ၹ +ၺ +ၻ +ၼ +ၽ +ၾ +ၿ +ႀ +ႁ +ႂ +ႃ +ႄ +ႅ +ႆ +ႇ +ႈ +ႉ +ႊ +ႋ +ႌ +ႍ +ႎ +ႏ +႐ +႑ +႒ +႓ +႔ +႕ +႖ +႗ +႘ +႙ +ႚ +ႛ +ႜ +ႝ +႞ +႟ + +က +ခ +ဂ +ဃ +င +စ +ဆ +ဇ +ဈ +ဉ +ည +ဋ +ဌ +ဍ +ဎ +ဏ +တ +ထ +ဒ +ဓ +န +ပ +ဖ +ဗ +ဘ +မ +ယ +ရ +လ +ဝ +သ +ဟ +ဠ +အ +ဢ +ဣ +ဤ +ဥ +ဦ +ဧ +ဨ +ဩ +ဪ +ါ +ာ +ိ +ီ +ု +ူ +ေ +ဲ +ဳ +ဴ +ဵ +ံ +့ +း +္ +် +ျ +ြ +ွ +ှ +ဿ +၀ +၁ +၂ +၃ +၄ +၅ +၆ +၇ +၈ +၉ +၊ +။ +၌ +၍ +၎ +၏ +ၐ +ၑ +ၒ +ၓ +ၔ +ၕ +ၖ +ၗ +ၘ +ၙ +ၚ +ၛ +ၜ +ၝ +ၞ +ၟ +ၠ +ၡ +ၢ +ၣ +ၤ +ၥ +ၦ +ၧ +ၨ +ၩ +ၪ +ၫ +ၬ +ၭ +ၮ +ၯ +ၰ +ၱ +ၲ +ၳ +ၴ +ၵ +ၶ +ၷ +ၸ +ၹ +ၺ +ၻ +ၼ +ၽ +ၾ +ၿ +ႀ +ႁ +ႂ +ႃ +ႄ +ႅ +ႆ +ႇ +ႈ +ႉ +ႊ +ႋ +ႌ +ႍ +ႎ +ႏ +႐ +႑ +႒ +႓ +႔ +႕ +႖ +႗ +႘ +႙ +ႚ +ႛ +ႜ +ႝ +႞ +႟ + +Ⴀ +Ⴁ +Ⴂ +Ⴃ +Ⴄ +Ⴅ +Ⴆ +Ⴇ +Ⴈ +Ⴉ +Ⴊ +Ⴋ +ᄀ +ᄁ +ᄂ +ᄃ +ᄄ +ᄅ +ᄆ +ᄇ +ᄈ +ᄉ +ᄊ +ᄋ +ᄌ +ᄍ +ᄎ +ᄏ +ᄐ +ᄑ +ᄒ + +ဠ +အ +ဢ +ဣ +ဤ +ဥ +ဦ +ဧ +ဨ +ဩ +ဪ +ါ +ာ +ိ +ီ +ု +ူ +ေ +ဲ +ဳ +ဴ +ဵ +ံ +့ +း +္ +် +ျ +ြ +ွ +ှ +ဿ +၀ +၁ +၂ +၃ +၄ +၅ +၆ +၇ +၈ +၉ +၊ +။ +၌ +၍ +၎ +၏ +ၐ +ၑ +ၒ +ၓ +ၔ +ၕ +ၖ +ၗ +ၘ +ၙ +ၚ +ၛ +ၜ +ၝ +ၞ +ၟ +ၠ +ၡ +ၢ +ၣ +ၤ +ၥ +ၦ +ၧ +ၨ +ၩ +ၪ +ၫ +ၬ +ၭ +ၮ +ၯ +ၰ +ၱ +ၲ +ၳ +ၴ +ၵ +ၶ +ၷ +ၸ +ၹ +ၺ +ၻ +ၼ +ၽ +ၾ +ၿ +ႀ +ႁ +ႂ +ႃ +ႄ +ႅ +ႆ +ႇ +ႈ +ႉ +ႊ +ႋ +ႌ +ႍ +ႎ +ႏ +႐ +႑ +႒ +႓ +႔ +႕ +႖ +႗ +႘ +႙ +ႚ +ႛ +ႜ +ႝ +႞ +႟ diff --git a/modules/onnx_ocr_module/src/ppocr/utils/dict/bn_dict.txt b/modules/onnx_ocr_module/src/ppocr/utils/dict/bn_dict.txt new file mode 100644 index 0000000..655c585 --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/utils/dict/bn_dict.txt @@ -0,0 +1,477 @@ + +! +# +$ +% +& +' +( ++ + +- +. +/ +0 +1 +2 +3 +4 +5 +6 +7 +8 +9 +: +? +@ +A +B +C +D +E +F +G +H +I +J +K +L +M +N +O +P +Q +R +S +T +U +V +W +X +Y +Z +_ +a +b +c +d +e +f +g +h +i +j +k +l +m +n +o +p +q +r +s +t +u +v +w +x +y +z +É +é +' +" +ঃ +, +; +! +? +। +— + ঃ- +- +( +) +[ +] +{ +} +√ +... +. +/ +\ += +< +> +৳ +° +% +অ +আ +ই +ঈ +উ +ঊ +ঋ +এ +ঐ +ও +ঔ +্‌ +া +ি +ী +ু +ূ +ে +ৈ +ো +ৌ +ৃ +‍্য +‍্র +‍্ব +ক +খ +গ +ঘ +ঙ +চ +ছ +জ +ঝ +ঞ +ট +ঠ +ড +ঢ +ণ +ত +থ +দ +ধ +ন +প +ফ +ব +ভ +ম +য +র +ল +শ +ষ +স +হ +ড় +ঢ় +য় +ং +ৎ +ঁ +ক্ক +ক্ট +ক্ত +ত্র +ক্ব +ক্ম +ক্য +ক্র +ক্ল +ক্ষ +ক্ষ্ণ +ক্ষ্ব +ক্ষ্ম +ক্ষ্য +ক্স +র্ক +র্ক্য +খ্য +খ্র +র্খ +গ্ণ +গ্ধ +গ্ধ্য +গ্ধ্র +গ্ন +গ্ন্য +গ্ব +গ্ম +গ্য +গ্র +গ্র্য +গ্ল +র্গ +র্গ্য +র্গ্র +ঘ্ন +ঘ্য +ঘ্র +র্ঘ্য +র্ঘ +ঙ্ক +ঙ্ক্য +ঙ্ক্ষ +ঙ্খ +ঙ্খ্য +ঙ্গ +ঙ্গ্য +ঙ্ঘ +ঙ্ঘ্য +ঙ্ঘ্র +ঙ্ম +র্ঙ্গ +চ্চ +চ্ছ +চ্ছ্ব +চ্ছ্র +চ্ঞ +চ্ব +চ্য +র্চ্য +র্চ +র্ছ +জ্জ +জ্জ্ব +জ্ঝ +জ্ঞ +জ্ব +জ্য +জ্র +র্জ্য +র্জ্জ +র্জ্ঞ +র্জ +র্ঝ +ঞ্চ +ঞ্ছ +ঞ্জ +ঞ্ঝ +ট্ট +ট্ব +ট্ম +ট্য +ট্র +র্ট +ড্ড +ড্ব +ড্ম +ড্য +ড্র +র্ড +ঢ্য +ঢ্র +র্ঢ্য +ণ্ট +ণ্ঠ +ণ্ঠ্য +ণ্ড +ণ্ড্য +ণ্ড্র +ণ্ঢ +ণ্ণ +ণ্ব +ণ্ম +ণ্য +র্ণ্য +র্ণ +ত্ত +ত্ত্ব +ত্ত্য +ত্থ +ত্ন +ত্ব +ত্ম +ত্ম্য +ত্য +ত্র +ত্র্য +র্ত্য +র্ত +র্ত্ম +র্ত্র +থ্ব +থ্য +থ্র +র্থ্য +র্থ +দ্গ +দ্ঘ +দ্দ +দ্দ্ব +দ্ধ +দ্ব +দ্ভ +দ্ভ্র +দ্ম +দ্য +দ্র +দ্র্য +র্দ +র্দ্ব +র্দ্র +ধ্ন +ধ্ব +ধ্ম +ধ্য +ধ্র +র্ধ +র্ধ্ব +ন্ট +ন্ট্র +ন্ঠ +ন্ড +ন্ড্র +ন্ত +ন্ত্ব +ন্ত্য +ন্ত্র +ন্ত্র্য +ন্থ +ন্থ্র +ন্দ +ন্দ্য +ন্দ্ব +ন্দ্র +ন্ধ +ন্ধ্য +ন্ধ্র +ন্ন +ন্ব +ন্ম +ন্য +র্ন +প্ট +প্ত +প্ন +প্প +প্য +প্র +প্র্য +প্ল +প্স +র্প +ফ্র +ফ্ল +র্ফ +ব্জ +ব্দ +ব্ধ +ব্ব +ব্য +ব্র +ব্ল +র্ব্য +র্ব +ভ্ব +ভ্য +ভ্র +ভ্ল +র্ভ +ম্ন +ম্প +ম্প্র +ম্ফ +ম্ব +ম্ব্র +ম্ভ +ম্ভ্র +ম্ম +ম্য +ম্র +ম্ল +র্ম্য +র্ম +য্য +র্য +ল্ক +ল্ক্য +ল্গ +ল্ট +ল্ড +ল্প +ল্ফ +ল্ব +ল্ভ +ল্ম +ল্য +ল্ল +র্ল +শ্চ +শ্ছ +শ্ন +শ্ব +শ্ম +শ্য +শ্র +শ্ল +র্শ্য +র্শ +র্শ্ব +ষ্ক +ষ্ক্ব +ষ্ক্র +ষ্ট +ষ্ট্য +ষ্ট্র +ষ্ঠ +ষ্ঠ্য +ষ্ণ +ষ্ণ্ব +ষ্প +ষ্প্র +ষ্ফ +ষ্ব +ষ্ম +ষ্য +র্ষ্য +র্ষ +র্ষ্ট +র্ষ্ণ +র্ষ্ণ্য +স্ক +স্ক্র +স্খ +স্ট +স্ট্র +স্ত +স্ত্ব +স্ত্য +স্ত্র +স্থ +স্থ্য +স্ন +স্ন্য +স্প +স্প্র +স্প্ল +স্ফ +স্ব +স্ম +স্য +স্র +স্ল +স্ক্ল +র্স +হ্ণ +হ্ন +হ্ব +হ্ম +হ্য +হ্র +হ্ল +র্হ্য +র্হ +ড়্গ +র্ৎ +০ +১ +২ +৩ +৪ +৫ +৬ +৭ +৮ +৯ diff --git a/modules/onnx_ocr_module/src/ppocr/utils/dict/chinese_cht_dict.txt b/modules/onnx_ocr_module/src/ppocr/utils/dict/chinese_cht_dict.txt new file mode 100644 index 0000000..cc1aa47 --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/utils/dict/chinese_cht_dict.txt @@ -0,0 +1,8421 @@ +! +" +# +$ +% +& +' +( +) +* ++ +, +- +. +/ +0 +1 +2 +3 +4 +5 +6 +7 +8 +9 +: +; +< += +> +? +@ +A +B +C +D +E +F +G +H +I +J +K +L +M +N +O +P +Q +R +S +T +U +V +W +X +Y +Z +[ +\ +] +^ +_ +` +a +b +c +d +e +f +g +h +i +j +k +l +m +n +o +p +q +r +s +t +u +v +w +x +y +z +{ +| +} +~ +¥ +® +° +± +² +´ +· +» +É +Ë +Ó +× +Ü +à +á +ä +è +é +ì +í +ò +ó +÷ +ú +ü +ā +ē +ī +ō +ū +ǐ +ǒ +ɔ +ɡ +ʌ +ˋ +Λ +Ο +Φ +Ω +α +β +ε +θ +μ +π +З +И +Й +П +Я +г +— +‖ +‘ +’ +“ +” +• +… +‧ +′ +″ +※ +℃ +№ +™ +Ⅱ +Ⅲ +Ⅳ +← +↑ +→ +↓ +⇋ +∈ +∑ +√ +∞ +∣ +∧ +∩ +∫ +∶ +≈ +≠ +≤ +≥ +⊙ +⊥ +① +② +③ +④ +⑧ +⑴ +⑵ +⑶ +─ +│ +┅ +┌ +├ +█ +▎ +▏ +▕ +■ +□ +▪ +▲ +△ +▼ +◆ +◇ +○ +◎ +● +◥ +★ +☆ +❋ +❤ +  +、 +。 +〇 +〉 +《 +》 +「 +」 +『 +』 +【 +】 +〔 +〕 +〖 +〗 +の +サ +シ +ジ +マ +ㄱ +ㆍ +㎏ +㎡ +㐂 +㐱 +㙟 +㴪 +㸃 +䖝 +䝉 +䰾 +䲁 +一 +丁 +七 +丄 +丈 +三 +上 +下 +丌 +不 +与 +丏 +丐 +丑 +且 +丕 +世 +丘 +丙 +丞 +丟 +両 +並 +丨 +丫 +中 +丰 +串 +丶 +丸 +丹 +主 +丼 +丿 +乂 +乃 +久 +么 +之 +乍 +乎 +乏 +乒 +乓 +乖 +乗 +乘 +乙 +乚 +乜 +九 +乞 +也 +乩 +乭 +乳 +乸 +乹 +乾 +亀 +亂 +亅 +了 +予 +亊 +事 +二 +亍 +云 +互 +亓 +五 +井 +亘 +些 +亜 +亞 +亟 +亠 +亡 +亢 +交 +亥 +亦 +亨 +享 +京 +亭 +亮 +亰 +亳 +亶 +亹 +人 +亻 +什 +仁 +仂 +仃 +仄 +仇 +仉 +今 +介 +仍 +仏 +仔 +仕 +他 +仗 +付 +仙 +仛 +仝 +仞 +仟 +仡 +代 +令 +以 +仨 +仫 +仮 +仰 +仲 +仳 +仵 +件 +仺 +任 +仼 +份 +仿 +企 +伃 +伈 +伉 +伊 +伋 +伍 +伎 +伏 +伐 +休 +伕 +伙 +伝 +伢 +伯 +估 +伱 +伴 +伶 +伷 +伸 +伺 +似 +伽 +伾 +佀 +佁 +佃 +但 +佇 +佈 +佉 +佋 +位 +低 +住 +佐 +佑 +体 +佔 +何 +佗 +佘 +余 +佚 +佛 +作 +佝 +佞 +佟 +你 +佣 +佤 +佧 +佩 +佬 +佯 +佰 +佳 +併 +佶 +佹 +佺 +佼 +佾 +使 +侁 +侃 +侄 +侅 +來 +侈 +侊 +例 +侍 +侏 +侑 +侖 +侗 +侘 +侚 +供 +依 +侞 +価 +侮 +侯 +侵 +侶 +侷 +侹 +便 +俁 +係 +促 +俄 +俅 +俊 +俋 +俌 +俍 +俎 +俏 +俐 +俑 +俗 +俘 +俚 +俛 +保 +俞 +俟 +俠 +信 +俬 +修 +俯 +俱 +俳 +俴 +俵 +俶 +俸 +俺 +俽 +俾 +倆 +倈 +倉 +個 +倌 +倍 +們 +倒 +倓 +倔 +倖 +倗 +倘 +候 +倚 +倜 +倞 +借 +倡 +倢 +倣 +値 +倦 +倧 +倩 +倪 +倫 +倬 +倭 +倮 +倻 +值 +偁 +偃 +假 +偈 +偉 +偊 +偌 +偍 +偎 +偏 +偓 +偕 +做 +停 +健 +偪 +偲 +側 +偵 +偶 +偷 +偸 +偽 +傀 +傃 +傅 +傈 +傉 +傍 +傑 +傒 +傕 +傖 +傘 +備 +傜 +傢 +傣 +催 +傭 +傲 +傳 +債 +傷 +傻 +傾 +僅 +僉 +僊 +働 +像 +僑 +僔 +僕 +僖 +僙 +僚 +僜 +僡 +僧 +僩 +僭 +僮 +僰 +僱 +僳 +僴 +僵 +價 +僻 +儀 +儁 +儂 +億 +儆 +儇 +儈 +儉 +儋 +儐 +儒 +儔 +儕 +儘 +儚 +儞 +償 +儡 +儥 +儦 +優 +儫 +儱 +儲 +儷 +儺 +儻 +儼 +兀 +允 +元 +兄 +充 +兆 +先 +光 +克 +兌 +免 +児 +兒 +兔 +兕 +兗 +兜 +入 +內 +全 +兩 +兪 +八 +公 +六 +兮 +共 +兵 +其 +具 +典 +兼 +兿 +冀 +冂 +円 +冇 +冉 +冊 +再 +冏 +冑 +冒 +冕 +冖 +冗 +冚 +冠 +冢 +冤 +冥 +冧 +冨 +冪 +冫 +冬 +冮 +冰 +冴 +冶 +冷 +冼 +冽 +凃 +凄 +准 +凈 +凋 +凌 +凍 +凖 +凜 +凝 +凞 +几 +凡 +処 +凪 +凬 +凰 +凱 +凳 +凵 +凶 +凸 +凹 +出 +函 +刀 +刁 +刂 +刃 +刄 +分 +切 +刈 +刊 +刎 +刑 +划 +列 +初 +判 +別 +刦 +刧 +刨 +利 +刪 +刮 +到 +制 +刷 +券 +刺 +刻 +刼 +剁 +剃 +則 +削 +剋 +剌 +前 +剎 +剏 +剔 +剖 +剛 +剝 +剡 +剣 +剩 +剪 +剮 +副 +割 +創 +剿 +劃 +劄 +劇 +劈 +劉 +劊 +劌 +劍 +劑 +劔 +力 +功 +加 +劣 +助 +努 +劫 +劬 +劭 +劵 +効 +劼 +劾 +勁 +勃 +勅 +勇 +勉 +勐 +勑 +勒 +勔 +動 +勖 +勗 +勘 +務 +勛 +勝 +勞 +募 +勢 +勣 +勤 +勦 +勰 +勱 +勲 +勳 +勵 +勷 +勸 +勺 +勻 +勾 +勿 +匂 +匄 +包 +匆 +匈 +匋 +匍 +匏 +匐 +匕 +化 +北 +匙 +匚 +匝 +匠 +匡 +匣 +匪 +匯 +匱 +匸 +匹 +匾 +匿 +區 +十 +千 +卅 +升 +午 +卉 +半 +卋 +卍 +卐 +卑 +卒 +卓 +協 +南 +博 +卜 +卞 +卟 +占 +卡 +卣 +卦 +卧 +卩 +卬 +卮 +卯 +印 +危 +卲 +即 +卵 +卷 +卸 +卹 +卺 +卻 +卽 +卿 +厄 +厓 +厔 +厙 +厚 +厝 +原 +厥 +厭 +厰 +厲 +厴 +厶 +去 +參 +叄 +又 +叉 +及 +友 +反 +収 +叔 +叕 +取 +受 +叛 +叟 +叡 +叢 +口 +古 +句 +另 +叨 +叩 +只 +叫 +召 +叭 +叮 +可 +台 +叱 +史 +右 +叵 +司 +叻 +叼 +吁 +吃 +各 +吆 +合 +吉 +吊 +吋 +同 +名 +后 +吏 +吐 +向 +吒 +吔 +吖 +君 +吝 +吞 +吟 +吠 +吡 +吥 +否 +吧 +吩 +含 +吮 +吱 +吲 +吳 +吵 +吶 +吸 +吹 +吻 +吼 +吾 +呀 +呂 +呃 +呈 +呉 +告 +呋 +呎 +呢 +呤 +呦 +周 +呱 +味 +呵 +呷 +呸 +呼 +命 +呾 +咀 +咁 +咂 +咄 +咅 +咆 +咋 +和 +咎 +咑 +咒 +咔 +咕 +咖 +咗 +咘 +咚 +咟 +咤 +咥 +咧 +咨 +咩 +咪 +咫 +咬 +咭 +咯 +咱 +咲 +咳 +咸 +咻 +咼 +咽 +咾 +咿 +哀 +品 +哂 +哄 +哆 +哇 +哈 +哉 +哌 +哎 +哏 +哐 +哖 +哚 +哞 +員 +哥 +哦 +哨 +哩 +哪 +哭 +哮 +哱 +哲 +哺 +哼 +唃 +唄 +唆 +唇 +唉 +唏 +唐 +唑 +唔 +唘 +唧 +唫 +唬 +唭 +售 +唯 +唱 +唳 +唵 +唷 +唸 +唻 +唾 +啁 +啃 +啄 +商 +啉 +啊 +啍 +問 +啓 +啖 +啚 +啜 +啞 +啟 +啡 +啣 +啤 +啥 +啦 +啪 +啫 +啯 +啰 +啱 +啲 +啵 +啶 +啷 +啻 +啼 +啾 +喀 +喂 +喃 +善 +喆 +喇 +喈 +喉 +喊 +喋 +喏 +喔 +喘 +喙 +喚 +喜 +喝 +喢 +喦 +喧 +喪 +喫 +喬 +單 +喰 +喱 +喲 +喳 +喵 +喹 +喻 +喼 +嗄 +嗅 +嗆 +嗇 +嗊 +嗎 +嗑 +嗒 +嗓 +嗔 +嗖 +嗚 +嗜 +嗝 +嗞 +嗡 +嗢 +嗣 +嗦 +嗨 +嗩 +嗪 +嗮 +嗯 +嗲 +嗶 +嗹 +嗽 +嘀 +嘅 +嘆 +嘉 +嘌 +嘍 +嘎 +嘏 +嘔 +嘗 +嘚 +嘛 +嘜 +嘞 +嘟 +嘢 +嘣 +嘥 +嘧 +嘩 +嘬 +嘮 +嘯 +嘰 +嘲 +嘴 +嘶 +嘸 +嘹 +嘻 +嘿 +噁 +噌 +噍 +噏 +噓 +噗 +噝 +噠 +噢 +噤 +噥 +噦 +器 +噩 +噪 +噬 +噯 +噰 +噲 +噴 +噶 +噸 +噹 +噻 +嚇 +嚈 +嚎 +嚏 +嚐 +嚒 +嚓 +嚕 +嚗 +嚙 +嚞 +嚟 +嚤 +嚦 +嚧 +嚨 +嚩 +嚮 +嚳 +嚴 +嚶 +嚷 +嚼 +嚿 +囀 +囂 +囃 +囉 +囊 +囍 +囑 +囒 +囓 +囗 +囚 +四 +囝 +回 +因 +囡 +団 +囤 +囧 +囪 +囮 +囯 +困 +囲 +図 +囶 +囷 +囹 +固 +囿 +圂 +圃 +圄 +圈 +圉 +國 +圍 +圏 +園 +圓 +圖 +圗 +團 +圜 +土 +圧 +在 +圩 +圪 +圭 +圯 +地 +圳 +圻 +圾 +址 +均 +坊 +坋 +坌 +坍 +坎 +坐 +坑 +坖 +坡 +坣 +坤 +坦 +坨 +坩 +坪 +坫 +坬 +坭 +坮 +坯 +坳 +坵 +坶 +坷 +坻 +垂 +垃 +垈 +型 +垍 +垓 +垕 +垚 +垛 +垞 +垟 +垠 +垢 +垣 +垮 +垯 +垰 +垵 +垸 +垻 +垿 +埃 +埅 +埇 +埈 +埋 +埌 +城 +埏 +埒 +埔 +埕 +埗 +埜 +域 +埠 +埡 +埤 +埧 +埨 +埪 +埭 +埮 +埴 +埵 +執 +培 +基 +埻 +埼 +堀 +堂 +堃 +堅 +堆 +堇 +堈 +堉 +堊 +堍 +堖 +堝 +堡 +堤 +堦 +堪 +堮 +堯 +堰 +報 +場 +堵 +堷 +堺 +塀 +塅 +塆 +塊 +塋 +塌 +塍 +塏 +塑 +塔 +塗 +塘 +塙 +塜 +塞 +塡 +塢 +塤 +塨 +塩 +填 +塬 +塭 +塰 +塱 +塲 +塵 +塹 +塽 +塾 +墀 +境 +墅 +墉 +墊 +墎 +墓 +増 +墘 +墜 +增 +墟 +墡 +墣 +墨 +墩 +墫 +墬 +墮 +墱 +墳 +墺 +墼 +墾 +壁 +壄 +壆 +壇 +壋 +壌 +壎 +壐 +壑 +壓 +壔 +壕 +壘 +壙 +壞 +壟 +壠 +壢 +壤 +壩 +士 +壬 +壯 +壱 +壴 +壹 +壺 +壽 +夀 +夆 +変 +夊 +夋 +夌 +夏 +夔 +夕 +外 +夙 +多 +夜 +夠 +夢 +夤 +夥 +大 +天 +太 +夫 +夬 +夭 +央 +夯 +失 +夷 +夾 +奀 +奄 +奇 +奈 +奉 +奎 +奏 +奐 +契 +奓 +奔 +奕 +套 +奘 +奚 +奠 +奢 +奣 +奧 +奩 +奪 +奫 +奭 +奮 +女 +奴 +奶 +她 +好 +妀 +妁 +如 +妃 +妄 +妊 +妍 +妏 +妑 +妒 +妓 +妖 +妙 +妝 +妞 +妠 +妤 +妥 +妧 +妨 +妭 +妮 +妯 +妲 +妳 +妸 +妹 +妺 +妻 +妾 +姀 +姁 +姃 +姆 +姈 +姉 +姊 +始 +姌 +姍 +姐 +姑 +姒 +姓 +委 +姚 +姜 +姝 +姣 +姥 +姦 +姨 +姪 +姫 +姬 +姮 +姵 +姶 +姸 +姻 +姿 +威 +娃 +娉 +娋 +娌 +娍 +娎 +娑 +娖 +娘 +娛 +娜 +娟 +娠 +娣 +娥 +娩 +娫 +娳 +娶 +娸 +娼 +娽 +婀 +婁 +婆 +婉 +婊 +婑 +婕 +婚 +婢 +婦 +婧 +婪 +婭 +婯 +婷 +婺 +婻 +婼 +婿 +媃 +媄 +媊 +媐 +媒 +媓 +媖 +媗 +媚 +媛 +媜 +媞 +媧 +媭 +媯 +媲 +媳 +媺 +媼 +媽 +媾 +媿 +嫁 +嫂 +嫄 +嫈 +嫉 +嫌 +嫖 +嫘 +嫚 +嫡 +嫣 +嫦 +嫩 +嫪 +嫲 +嫳 +嫵 +嫺 +嫻 +嬅 +嬈 +嬉 +嬋 +嬌 +嬗 +嬛 +嬝 +嬡 +嬤 +嬨 +嬪 +嬬 +嬭 +嬰 +嬴 +嬸 +嬾 +嬿 +孀 +孃 +孆 +孋 +孌 +子 +孑 +孔 +孕 +孖 +字 +存 +孚 +孛 +孜 +孝 +孟 +孢 +季 +孤 +孩 +孫 +孬 +孮 +孰 +孳 +孵 +學 +孺 +孻 +孽 +孿 +宀 +它 +宅 +宇 +守 +安 +宋 +完 +宍 +宏 +宓 +宕 +宗 +官 +宙 +定 +宛 +宜 +実 +客 +宣 +室 +宥 +宦 +宧 +宮 +宰 +害 +宴 +宵 +家 +宸 +容 +宿 +寀 +寁 +寂 +寄 +寅 +密 +寇 +寈 +寊 +富 +寐 +寒 +寓 +寔 +寕 +寖 +寗 +寘 +寛 +寜 +寞 +察 +寡 +寢 +寤 +寥 +實 +寧 +寨 +審 +寫 +寬 +寮 +寯 +寰 +寳 +寵 +寶 +寸 +寺 +対 +封 +専 +尃 +射 +將 +專 +尉 +尊 +尋 +對 +導 +小 +尐 +少 +尓 +尕 +尖 +尗 +尙 +尚 +尢 +尤 +尨 +尪 +尬 +就 +尷 +尹 +尺 +尻 +尼 +尾 +尿 +局 +屁 +屄 +居 +屆 +屇 +屈 +屋 +屌 +屍 +屎 +屏 +屐 +屑 +屓 +展 +屚 +屜 +屠 +屢 +層 +履 +屬 +屭 +屯 +山 +屹 +屺 +屻 +岀 +岈 +岌 +岐 +岑 +岔 +岡 +岢 +岣 +岧 +岩 +岪 +岫 +岬 +岰 +岱 +岳 +岵 +岷 +岸 +岻 +峁 +峅 +峇 +峋 +峍 +峒 +峘 +峙 +峚 +峠 +峨 +峩 +峪 +峭 +峯 +峰 +峴 +島 +峻 +峼 +峽 +崁 +崆 +崇 +崈 +崋 +崍 +崎 +崐 +崑 +崒 +崔 +崖 +崗 +崘 +崙 +崚 +崛 +崞 +崟 +崠 +崢 +崤 +崧 +崩 +崬 +崮 +崱 +崴 +崵 +崶 +崽 +嵇 +嵊 +嵋 +嵌 +嵎 +嵐 +嵒 +嵕 +嵖 +嵗 +嵙 +嵛 +嵜 +嵨 +嵩 +嵬 +嵮 +嵯 +嵰 +嵴 +嵻 +嵿 +嶁 +嶂 +嶃 +嶄 +嶇 +嶋 +嶌 +嶍 +嶒 +嶔 +嶗 +嶝 +嶠 +嶢 +嶦 +嶧 +嶪 +嶬 +嶰 +嶲 +嶴 +嶷 +嶸 +嶺 +嶼 +嶽 +巂 +巄 +巆 +巋 +巌 +巍 +巎 +巑 +巒 +巔 +巖 +巘 +巛 +川 +州 +巡 +巢 +工 +左 +巧 +巨 +巫 +差 +巰 +己 +已 +巳 +巴 +巶 +巷 +巻 +巽 +巾 +巿 +市 +布 +帆 +希 +帑 +帔 +帕 +帖 +帘 +帙 +帚 +帛 +帝 +帡 +帢 +帥 +師 +席 +帯 +帰 +帳 +帶 +帷 +常 +帽 +幀 +幃 +幄 +幅 +幌 +幔 +幕 +幗 +幚 +幛 +幟 +幡 +幢 +幣 +幪 +幫 +干 +平 +年 +幵 +幷 +幸 +幹 +幺 +幻 +幼 +幽 +幾 +庀 +庁 +広 +庇 +床 +序 +底 +庖 +店 +庚 +府 +庠 +庢 +庥 +度 +座 +庫 +庭 +庲 +庵 +庶 +康 +庸 +庹 +庼 +庾 +廁 +廂 +廄 +廆 +廈 +廉 +廊 +廋 +廌 +廍 +廑 +廓 +廔 +廕 +廖 +廙 +廚 +廝 +廞 +廟 +廠 +廡 +廢 +廣 +廧 +廨 +廩 +廬 +廰 +廱 +廳 +延 +廷 +廸 +建 +廻 +廼 +廿 +弁 +弄 +弅 +弇 +弈 +弉 +弊 +弋 +弍 +式 +弐 +弒 +弓 +弔 +引 +弖 +弗 +弘 +弛 +弟 +弢 +弦 +弧 +弨 +弩 +弭 +弱 +張 +強 +弸 +弼 +弾 +彀 +彄 +彅 +彆 +彈 +彊 +彌 +彎 +彐 +彔 +彖 +彗 +彘 +彙 +彜 +彞 +彠 +彡 +形 +彣 +彤 +彥 +彧 +彩 +彪 +彫 +彬 +彭 +彰 +影 +彳 +彷 +役 +彼 +彿 +往 +征 +徂 +待 +徇 +很 +徉 +徊 +律 +後 +徐 +徑 +徒 +得 +徘 +徙 +徜 +從 +徠 +御 +徧 +徨 +復 +循 +徫 +徬 +徭 +微 +徳 +徴 +徵 +德 +徸 +徹 +徽 +心 +忄 +必 +忉 +忌 +忍 +忐 +忑 +忒 +志 +忘 +忙 +応 +忝 +忞 +忠 +快 +忬 +忯 +忱 +忳 +念 +忻 +忽 +忿 +怍 +怎 +怒 +怕 +怖 +怙 +怛 +思 +怠 +怡 +急 +怦 +性 +怨 +怪 +怯 +怵 +恁 +恂 +恃 +恆 +恊 +恍 +恐 +恕 +恙 +恢 +恣 +恤 +恥 +恨 +恩 +恪 +恬 +恭 +息 +恰 +恵 +恿 +悄 +悅 +悆 +悉 +悌 +悍 +悔 +悖 +悚 +悛 +悝 +悞 +悟 +悠 +患 +悧 +您 +悪 +悰 +悲 +悳 +悵 +悶 +悸 +悼 +情 +惆 +惇 +惑 +惔 +惕 +惘 +惚 +惜 +惟 +惠 +惡 +惣 +惦 +惰 +惱 +惲 +想 +惶 +惹 +惺 +愁 +愃 +愆 +愈 +愉 +愍 +意 +愐 +愒 +愔 +愕 +愚 +愛 +愜 +感 +愣 +愧 +愨 +愫 +愭 +愴 +愷 +愼 +愾 +愿 +慄 +慈 +態 +慌 +慎 +慕 +慘 +慚 +慜 +慟 +慢 +慣 +慥 +慧 +慨 +慮 +慰 +慳 +慵 +慶 +慷 +慾 +憂 +憊 +憋 +憍 +憎 +憐 +憑 +憓 +憕 +憙 +憚 +憤 +憧 +憨 +憩 +憫 +憬 +憲 +憶 +憺 +憻 +憾 +懂 +懃 +懇 +懈 +應 +懋 +懌 +懍 +懐 +懣 +懦 +懮 +懲 +懵 +懶 +懷 +懸 +懺 +懼 +懽 +懾 +懿 +戀 +戇 +戈 +戊 +戌 +戍 +戎 +成 +我 +戒 +戔 +戕 +或 +戙 +戚 +戛 +戟 +戡 +戢 +戥 +戦 +戩 +截 +戮 +戰 +戱 +戲 +戳 +戴 +戶 +戸 +戻 +戽 +戾 +房 +所 +扁 +扆 +扇 +扈 +扉 +手 +扌 +才 +扎 +扒 +打 +扔 +托 +扙 +扛 +扞 +扣 +扥 +扦 +扭 +扮 +扯 +扳 +扶 +批 +扼 +找 +承 +技 +抃 +抄 +抇 +抉 +把 +抑 +抒 +抓 +投 +抖 +抗 +折 +抦 +披 +抬 +抱 +抵 +抹 +抻 +押 +抽 +抿 +拂 +拆 +拇 +拈 +拉 +拋 +拌 +拍 +拎 +拏 +拐 +拒 +拓 +拔 +拖 +拗 +拘 +拙 +拚 +招 +拜 +拝 +拡 +括 +拭 +拮 +拯 +拱 +拳 +拴 +拷 +拺 +拼 +拽 +拾 +拿 +持 +指 +按 +挎 +挑 +挖 +挙 +挨 +挪 +挫 +振 +挲 +挵 +挹 +挺 +挻 +挾 +捂 +捆 +捉 +捌 +捍 +捎 +捏 +捐 +捒 +捕 +捜 +捦 +捧 +捨 +捩 +捫 +捭 +捱 +捲 +捶 +捷 +捺 +捻 +掀 +掂 +掃 +掄 +掇 +授 +掉 +掌 +掏 +掐 +排 +掖 +掘 +掙 +掛 +掞 +掟 +掠 +採 +探 +掣 +接 +控 +推 +掩 +措 +掬 +掰 +掾 +揀 +揄 +揆 +揉 +揍 +描 +提 +插 +揔 +揖 +揚 +換 +握 +揪 +揭 +揮 +援 +揸 +揺 +損 +搏 +搐 +搓 +搔 +搖 +搗 +搜 +搞 +搠 +搢 +搪 +搬 +搭 +搳 +搴 +搵 +搶 +搽 +搾 +摂 +摒 +摔 +摘 +摜 +摞 +摟 +摠 +摧 +摩 +摭 +摯 +摳 +摴 +摵 +摶 +摸 +摹 +摺 +摻 +摽 +撃 +撇 +撈 +撐 +撒 +撓 +撕 +撖 +撙 +撚 +撞 +撣 +撤 +撥 +撩 +撫 +撬 +播 +撮 +撰 +撲 +撳 +撻 +撼 +撾 +撿 +擀 +擁 +擂 +擅 +擇 +擊 +擋 +操 +擎 +擒 +擔 +擘 +據 +擠 +擢 +擥 +擦 +擬 +擯 +擰 +擱 +擲 +擴 +擷 +擺 +擼 +擾 +攀 +攏 +攔 +攖 +攘 +攜 +攝 +攞 +攢 +攣 +攤 +攪 +攫 +攬 +支 +攴 +攵 +收 +攷 +攸 +改 +攻 +攽 +放 +政 +故 +效 +敍 +敎 +敏 +救 +敔 +敕 +敖 +敗 +敘 +教 +敝 +敞 +敟 +敢 +散 +敦 +敫 +敬 +敭 +敲 +整 +敵 +敷 +數 +敻 +敾 +斂 +斃 +文 +斌 +斎 +斐 +斑 +斕 +斖 +斗 +料 +斛 +斜 +斝 +斟 +斡 +斤 +斥 +斧 +斬 +斯 +新 +斷 +方 +於 +施 +斿 +旁 +旂 +旃 +旄 +旅 +旉 +旋 +旌 +旎 +族 +旖 +旗 +旙 +旛 +旡 +既 +日 +旦 +旨 +早 +旬 +旭 +旱 +旲 +旳 +旺 +旻 +旼 +旽 +旾 +旿 +昀 +昂 +昃 +昆 +昇 +昉 +昊 +昌 +昍 +明 +昏 +昐 +易 +昔 +昕 +昚 +昛 +昜 +昝 +昞 +星 +映 +昡 +昣 +昤 +春 +昧 +昨 +昪 +昫 +昭 +是 +昰 +昱 +昴 +昵 +昶 +昺 +晁 +時 +晃 +晈 +晉 +晊 +晏 +晗 +晙 +晚 +晛 +晝 +晞 +晟 +晤 +晦 +晧 +晨 +晩 +晪 +晫 +晭 +普 +景 +晰 +晳 +晴 +晶 +晷 +晸 +智 +晾 +暃 +暄 +暅 +暇 +暈 +暉 +暊 +暌 +暎 +暏 +暐 +暑 +暕 +暖 +暗 +暘 +暝 +暟 +暠 +暢 +暦 +暨 +暫 +暮 +暱 +暲 +暴 +暸 +暹 +暻 +暾 +曄 +曅 +曆 +曇 +曉 +曌 +曔 +曖 +曙 +曜 +曝 +曠 +曦 +曧 +曨 +曩 +曬 +曮 +曰 +曲 +曳 +更 +曶 +曷 +書 +曹 +曺 +曼 +曽 +曾 +替 +最 +會 +月 +有 +朊 +朋 +服 +朏 +朐 +朓 +朔 +朕 +朖 +朗 +望 +朝 +期 +朦 +朧 +木 +未 +末 +本 +札 +朱 +朴 +朵 +朶 +朽 +朿 +杁 +杉 +杋 +杌 +李 +杏 +材 +村 +杓 +杖 +杙 +杜 +杞 +束 +杠 +杣 +杤 +杧 +杬 +杭 +杯 +東 +杲 +杳 +杴 +杵 +杷 +杻 +杼 +松 +板 +极 +枇 +枉 +枋 +枏 +析 +枕 +枖 +林 +枚 +枛 +果 +枝 +枠 +枡 +枯 +枰 +枱 +枲 +枳 +架 +枷 +枸 +枹 +枼 +柁 +柃 +柄 +柉 +柊 +柎 +柏 +某 +柑 +柒 +染 +柔 +柘 +柚 +柜 +柝 +柞 +柟 +查 +柩 +柬 +柯 +柰 +柱 +柳 +柴 +柵 +柶 +柷 +査 +柾 +柿 +栃 +栄 +栐 +栒 +栓 +栜 +栝 +栞 +校 +栢 +栨 +栩 +株 +栲 +栴 +核 +根 +栻 +格 +栽 +桀 +桁 +桂 +桃 +桄 +桅 +框 +案 +桉 +桌 +桎 +桐 +桑 +桓 +桔 +桕 +桖 +桙 +桜 +桝 +桫 +桱 +桲 +桴 +桶 +桷 +桼 +桿 +梀 +梁 +梂 +梃 +梅 +梆 +梉 +梏 +梓 +梔 +梗 +梘 +條 +梟 +梠 +梢 +梣 +梧 +梨 +梫 +梭 +梯 +械 +梱 +梳 +梵 +梶 +梽 +棄 +棆 +棉 +棋 +棍 +棐 +棒 +棓 +棕 +棖 +棗 +棘 +棚 +棛 +棟 +棠 +棡 +棣 +棧 +棨 +棩 +棪 +棫 +森 +棱 +棲 +棵 +棶 +棹 +棺 +棻 +棼 +棽 +椅 +椆 +椇 +椋 +植 +椎 +椏 +椒 +椙 +椥 +椪 +椰 +椲 +椴 +椵 +椹 +椽 +椿 +楂 +楊 +楓 +楔 +楗 +楙 +楚 +楝 +楞 +楠 +楡 +楢 +楣 +楤 +楦 +楧 +楨 +楫 +業 +楮 +楯 +楳 +極 +楷 +楸 +楹 +楽 +楿 +概 +榆 +榊 +榍 +榎 +榑 +榔 +榕 +榖 +榗 +榘 +榛 +榜 +榞 +榢 +榣 +榤 +榦 +榧 +榨 +榫 +榭 +榮 +榲 +榴 +榷 +榻 +榿 +槀 +槁 +槃 +槊 +構 +槌 +槍 +槎 +槐 +槓 +槔 +槗 +様 +槙 +槤 +槩 +槭 +槰 +槱 +槲 +槳 +槺 +槻 +槼 +槽 +槿 +樀 +樁 +樂 +樅 +樆 +樊 +樋 +樑 +樓 +樗 +樘 +標 +樞 +樟 +模 +樣 +樨 +権 +樫 +樵 +樸 +樹 +樺 +樻 +樽 +樾 +橄 +橇 +橈 +橋 +橐 +橒 +橓 +橘 +橙 +橚 +機 +橡 +橢 +橪 +橫 +橿 +檀 +檄 +檇 +檉 +檊 +檎 +檐 +檔 +檗 +檜 +檞 +檠 +檡 +檢 +檣 +檦 +檨 +檫 +檬 +檯 +檳 +檵 +檸 +檻 +檽 +櫂 +櫃 +櫆 +櫈 +櫓 +櫚 +櫛 +櫞 +櫟 +櫥 +櫨 +櫪 +櫱 +櫸 +櫻 +櫾 +櫿 +欄 +欉 +權 +欏 +欒 +欖 +欞 +欠 +次 +欣 +欥 +欲 +欸 +欹 +欺 +欽 +款 +歆 +歇 +歉 +歊 +歌 +歎 +歐 +歓 +歙 +歛 +歡 +止 +正 +此 +步 +武 +歧 +歩 +歪 +歲 +歳 +歴 +歷 +歸 +歹 +死 +歿 +殂 +殃 +殄 +殆 +殉 +殊 +殑 +殖 +殘 +殛 +殞 +殟 +殤 +殭 +殮 +殯 +殲 +殳 +段 +殷 +殺 +殻 +殼 +殿 +毀 +毅 +毆 +毉 +毋 +毌 +母 +毎 +每 +毐 +毒 +毓 +比 +毖 +毗 +毘 +毛 +毫 +毬 +毯 +毴 +毸 +毽 +毿 +氂 +氈 +氍 +氏 +氐 +民 +氓 +氖 +気 +氘 +氙 +氚 +氛 +氟 +氣 +氦 +氧 +氨 +氪 +氫 +氬 +氮 +氯 +氰 +水 +氵 +氷 +永 +氹 +氻 +氽 +氾 +汀 +汁 +求 +汊 +汎 +汐 +汕 +汗 +汛 +汜 +汝 +汞 +江 +池 +污 +汧 +汨 +汩 +汪 +汭 +汰 +汲 +汴 +汶 +決 +汽 +汾 +沁 +沂 +沃 +沄 +沅 +沆 +沇 +沈 +沉 +沌 +沍 +沏 +沐 +沒 +沓 +沔 +沖 +沘 +沙 +沚 +沛 +沜 +沢 +沨 +沫 +沭 +沮 +沯 +沱 +河 +沸 +油 +沺 +治 +沼 +沽 +沾 +沿 +況 +泂 +泄 +泆 +泇 +泉 +泊 +泌 +泐 +泓 +泔 +法 +泖 +泗 +泚 +泛 +泠 +泡 +波 +泣 +泥 +泩 +泫 +泮 +泯 +泰 +泱 +泳 +泵 +洄 +洋 +洌 +洎 +洗 +洙 +洛 +洞 +洢 +洣 +洤 +津 +洨 +洩 +洪 +洮 +洱 +洲 +洳 +洵 +洸 +洹 +洺 +活 +洽 +派 +流 +浄 +浙 +浚 +浛 +浜 +浞 +浟 +浠 +浡 +浣 +浤 +浥 +浦 +浩 +浪 +浮 +浯 +浴 +浵 +海 +浸 +浹 +涅 +涇 +消 +涉 +涌 +涎 +涑 +涓 +涔 +涕 +涙 +涪 +涫 +涮 +涯 +液 +涵 +涸 +涼 +涿 +淄 +淅 +淆 +淇 +淋 +淌 +淍 +淎 +淏 +淑 +淓 +淖 +淘 +淙 +淚 +淛 +淝 +淞 +淠 +淡 +淤 +淥 +淦 +淨 +淩 +淪 +淫 +淬 +淮 +淯 +淰 +深 +淳 +淵 +淶 +混 +淸 +淹 +淺 +添 +淼 +淽 +渃 +清 +済 +渉 +渋 +渕 +渙 +渚 +減 +渝 +渟 +渠 +渡 +渣 +渤 +渥 +渦 +渫 +測 +渭 +港 +渲 +渴 +游 +渺 +渼 +渽 +渾 +湃 +湄 +湉 +湊 +湍 +湓 +湔 +湖 +湘 +湛 +湜 +湞 +湟 +湣 +湥 +湧 +湫 +湮 +湯 +湳 +湴 +湼 +満 +溁 +溇 +溈 +溉 +溋 +溎 +溏 +源 +準 +溙 +溜 +溝 +溟 +溢 +溥 +溦 +溧 +溪 +溫 +溯 +溱 +溲 +溴 +溵 +溶 +溺 +溼 +滀 +滁 +滂 +滄 +滅 +滇 +滈 +滉 +滋 +滌 +滎 +滏 +滑 +滓 +滔 +滕 +滘 +滙 +滝 +滬 +滯 +滲 +滴 +滷 +滸 +滹 +滻 +滽 +滾 +滿 +漁 +漂 +漆 +漇 +漈 +漎 +漏 +漓 +演 +漕 +漚 +漠 +漢 +漣 +漩 +漪 +漫 +漬 +漯 +漱 +漲 +漳 +漴 +漵 +漷 +漸 +漼 +漾 +漿 +潁 +潑 +潔 +潘 +潛 +潞 +潟 +潢 +潤 +潭 +潮 +潯 +潰 +潲 +潺 +潼 +潽 +潾 +潿 +澀 +澁 +澂 +澄 +澆 +澇 +澈 +澉 +澋 +澌 +澍 +澎 +澔 +澗 +澠 +澡 +澣 +澤 +澥 +澧 +澪 +澮 +澯 +澱 +澳 +澶 +澹 +澻 +激 +濁 +濂 +濃 +濉 +濊 +濋 +濕 +濘 +濙 +濛 +濞 +濟 +濠 +濡 +濤 +濫 +濬 +濮 +濯 +濰 +濱 +濲 +濶 +濺 +濼 +濾 +瀁 +瀅 +瀆 +瀉 +瀍 +瀏 +瀑 +瀔 +瀕 +瀘 +瀚 +瀛 +瀝 +瀞 +瀟 +瀠 +瀣 +瀦 +瀧 +瀨 +瀬 +瀰 +瀲 +瀴 +瀶 +瀹 +瀾 +灃 +灊 +灌 +灑 +灘 +灝 +灞 +灡 +灣 +灤 +灧 +火 +灰 +灴 +灸 +灼 +災 +炁 +炅 +炆 +炊 +炎 +炒 +炔 +炕 +炘 +炙 +炟 +炣 +炤 +炫 +炬 +炭 +炮 +炯 +炱 +炲 +炳 +炷 +炸 +為 +炻 +烈 +烉 +烊 +烋 +烏 +烒 +烔 +烘 +烙 +烜 +烝 +烤 +烯 +烱 +烴 +烷 +烹 +烺 +烽 +焃 +焄 +焉 +焊 +焌 +焓 +焗 +焙 +焚 +焜 +焞 +無 +焦 +焯 +焰 +焱 +焴 +然 +焻 +焼 +焿 +煇 +煉 +煊 +煌 +煎 +煐 +煒 +煔 +煕 +煖 +煙 +煚 +煜 +煞 +煠 +煤 +煥 +煦 +照 +煨 +煩 +煬 +煮 +煲 +煳 +煵 +煶 +煸 +煽 +熄 +熅 +熇 +熈 +熊 +熏 +熒 +熔 +熖 +熗 +熘 +熙 +熜 +熟 +熠 +熤 +熥 +熨 +熬 +熯 +熱 +熲 +熳 +熵 +熹 +熺 +熼 +熾 +熿 +燁 +燃 +燄 +燈 +燉 +燊 +燎 +燏 +燐 +燒 +燔 +燕 +燘 +燙 +燚 +燜 +燝 +營 +燥 +燦 +燧 +燫 +燬 +燭 +燮 +燴 +燹 +燻 +燼 +燾 +燿 +爀 +爆 +爌 +爍 +爐 +爔 +爚 +爛 +爝 +爨 +爪 +爬 +爭 +爯 +爰 +爲 +爵 +父 +爸 +爹 +爺 +爻 +爽 +爾 +爿 +牁 +牂 +牆 +片 +版 +牌 +牒 +牕 +牖 +牘 +牙 +牛 +牝 +牟 +牠 +牡 +牢 +牧 +物 +牯 +牲 +特 +牻 +牼 +牽 +犀 +犁 +犂 +犇 +犍 +犎 +犖 +犛 +犢 +犧 +犨 +犬 +犯 +犰 +犴 +犽 +狀 +狂 +狄 +狍 +狎 +狐 +狒 +狓 +狗 +狙 +狛 +狟 +狠 +狡 +狦 +狨 +狩 +狳 +狶 +狷 +狸 +狹 +狻 +狼 +猁 +猄 +猇 +猊 +猗 +猙 +猛 +猜 +猝 +猞 +猢 +猥 +猨 +猩 +猳 +猴 +猶 +猷 +猺 +猻 +猾 +猿 +獁 +獃 +獄 +獅 +獇 +獎 +獏 +獐 +獒 +獠 +獢 +獣 +獨 +獬 +獮 +獯 +獰 +獲 +獴 +獵 +獷 +獸 +獺 +獻 +獼 +獾 +玀 +玄 +玆 +率 +玉 +王 +玎 +玏 +玓 +玕 +玖 +玗 +玘 +玙 +玟 +玠 +玡 +玢 +玥 +玧 +玨 +玩 +玫 +玭 +玲 +玳 +玶 +玷 +玹 +玻 +玾 +珀 +珂 +珅 +珈 +珉 +珊 +珌 +珍 +珎 +珏 +珖 +珙 +珝 +珞 +珠 +珡 +珣 +珤 +珥 +珦 +珧 +珩 +珪 +班 +珮 +珵 +珹 +珺 +珽 +現 +琁 +球 +琄 +琅 +理 +琇 +琉 +琊 +琍 +琎 +琚 +琛 +琡 +琢 +琤 +琥 +琦 +琨 +琪 +琬 +琮 +琯 +琰 +琱 +琳 +琴 +琵 +琶 +琹 +琺 +琿 +瑀 +瑁 +瑂 +瑄 +瑅 +瑆 +瑈 +瑊 +瑋 +瑑 +瑒 +瑕 +瑗 +瑙 +瑚 +瑛 +瑜 +瑝 +瑞 +瑟 +瑠 +瑢 +瑣 +瑤 +瑥 +瑧 +瑨 +瑩 +瑪 +瑭 +瑯 +瑰 +瑱 +瑳 +瑴 +瑺 +瑾 +璀 +璁 +璃 +璄 +璆 +璇 +璈 +璉 +璋 +璌 +璐 +璕 +璘 +璙 +璚 +璜 +璞 +璟 +璠 +璡 +璣 +璥 +璦 +璧 +璨 +璩 +璪 +璫 +璬 +璮 +環 +璱 +璵 +璸 +璹 +璽 +璿 +瓈 +瓊 +瓌 +瓏 +瓑 +瓔 +瓖 +瓘 +瓚 +瓛 +瓜 +瓞 +瓠 +瓢 +瓣 +瓤 +瓦 +瓮 +瓴 +瓶 +瓷 +瓿 +甂 +甄 +甌 +甍 +甑 +甕 +甘 +甙 +甚 +甜 +生 +甡 +產 +産 +甥 +甦 +用 +甩 +甪 +甫 +甬 +甯 +田 +由 +甲 +申 +男 +甸 +甹 +町 +甾 +畀 +畇 +畈 +畊 +畋 +界 +畎 +畏 +畐 +畑 +畔 +留 +畜 +畝 +畠 +畢 +略 +畦 +畧 +番 +畫 +畬 +畯 +異 +畲 +畳 +畵 +當 +畷 +畸 +畹 +畿 +疃 +疆 +疇 +疊 +疋 +疌 +疍 +疏 +疑 +疒 +疕 +疙 +疚 +疝 +疣 +疤 +疥 +疫 +疲 +疳 +疵 +疸 +疹 +疼 +疽 +疾 +痂 +病 +症 +痊 +痍 +痔 +痕 +痘 +痙 +痛 +痞 +痟 +痠 +痢 +痣 +痤 +痧 +痩 +痰 +痱 +痲 +痴 +痹 +痺 +痿 +瘀 +瘁 +瘊 +瘋 +瘍 +瘓 +瘙 +瘜 +瘞 +瘟 +瘠 +瘡 +瘢 +瘤 +瘦 +瘧 +瘩 +瘰 +瘴 +瘺 +癀 +療 +癆 +癇 +癌 +癒 +癖 +癘 +癜 +癟 +癡 +癢 +癤 +癥 +癩 +癬 +癭 +癮 +癯 +癰 +癱 +癲 +癸 +発 +登 +發 +白 +百 +皂 +的 +皆 +皇 +皈 +皋 +皎 +皐 +皓 +皖 +皙 +皚 +皛 +皝 +皞 +皮 +皰 +皴 +皷 +皸 +皺 +皿 +盂 +盃 +盅 +盆 +盈 +益 +盋 +盌 +盎 +盒 +盔 +盛 +盜 +盞 +盟 +盡 +監 +盤 +盥 +盦 +盧 +盨 +盩 +盪 +盫 +目 +盯 +盱 +盲 +直 +盷 +相 +盹 +盺 +盼 +盾 +眀 +省 +眉 +看 +県 +眙 +眛 +眜 +眞 +真 +眠 +眥 +眨 +眩 +眭 +眯 +眵 +眶 +眷 +眸 +眺 +眼 +眾 +着 +睇 +睛 +睜 +睞 +睡 +睢 +督 +睥 +睦 +睨 +睪 +睫 +睭 +睹 +睺 +睽 +睾 +睿 +瞄 +瞅 +瞋 +瞌 +瞎 +瞑 +瞓 +瞞 +瞢 +瞥 +瞧 +瞪 +瞫 +瞬 +瞭 +瞰 +瞳 +瞻 +瞼 +瞽 +瞿 +矇 +矍 +矗 +矚 +矛 +矜 +矞 +矢 +矣 +知 +矧 +矩 +短 +矮 +矯 +石 +矸 +矽 +砂 +砋 +砌 +砍 +砒 +研 +砝 +砢 +砥 +砦 +砧 +砩 +砫 +砭 +砮 +砯 +砰 +砲 +砳 +破 +砵 +砷 +砸 +砼 +硂 +硃 +硅 +硇 +硏 +硐 +硒 +硓 +硚 +硜 +硝 +硤 +硨 +硫 +硬 +硭 +硯 +硼 +碁 +碇 +碉 +碌 +碎 +碑 +碓 +碕 +碗 +碘 +碚 +碟 +碡 +碣 +碧 +碩 +碪 +碭 +碰 +碲 +碳 +碴 +碶 +碸 +確 +碻 +碼 +碽 +碾 +磁 +磅 +磊 +磋 +磐 +磔 +磕 +磘 +磙 +磚 +磜 +磡 +磨 +磪 +磬 +磯 +磱 +磲 +磵 +磷 +磺 +磻 +磾 +礁 +礄 +礎 +礐 +礑 +礒 +礙 +礠 +礦 +礪 +礫 +礬 +礮 +礱 +礴 +示 +礻 +礽 +社 +祀 +祁 +祂 +祆 +祇 +祈 +祉 +祋 +祏 +祐 +祓 +祕 +祖 +祗 +祙 +祚 +祛 +祜 +祝 +神 +祟 +祠 +祥 +祧 +票 +祭 +祹 +祺 +祼 +祿 +禁 +禃 +禇 +禍 +禎 +福 +禑 +禓 +禔 +禕 +禘 +禛 +禟 +禠 +禤 +禦 +禧 +禨 +禩 +禪 +禮 +禰 +禱 +禵 +禹 +禺 +禼 +禽 +禾 +禿 +秀 +私 +秈 +秉 +秋 +科 +秒 +秕 +秘 +租 +秠 +秣 +秤 +秦 +秧 +秩 +秭 +秳 +秸 +移 +稀 +稅 +稈 +稉 +程 +稍 +稑 +稔 +稗 +稘 +稙 +稚 +稜 +稞 +稟 +稠 +種 +稱 +稲 +稷 +稹 +稺 +稻 +稼 +稽 +稾 +稿 +穀 +穂 +穆 +穈 +穉 +穌 +積 +穎 +穗 +穟 +穠 +穡 +穢 +穣 +穩 +穫 +穰 +穴 +穵 +究 +穹 +空 +穿 +突 +窄 +窅 +窈 +窋 +窒 +窕 +窖 +窗 +窘 +窟 +窠 +窣 +窨 +窩 +窪 +窮 +窯 +窰 +窶 +窺 +窿 +竄 +竅 +竇 +竈 +竊 +立 +竑 +站 +竜 +竟 +章 +竣 +童 +竦 +竩 +竭 +端 +競 +竹 +竺 +竻 +竿 +笄 +笆 +笈 +笏 +笑 +笘 +笙 +笛 +笞 +笠 +笥 +符 +笨 +笩 +笪 +第 +笭 +笮 +笯 +笱 +笳 +笹 +筅 +筆 +等 +筊 +筋 +筌 +筍 +筏 +筐 +筒 +答 +策 +筘 +筠 +筥 +筦 +筧 +筬 +筭 +筱 +筲 +筳 +筵 +筶 +筷 +筻 +箆 +箇 +箋 +箍 +箏 +箐 +箑 +箒 +箔 +箕 +算 +箜 +管 +箬 +箭 +箱 +箴 +箸 +節 +篁 +範 +篆 +篇 +築 +篊 +篋 +篌 +篔 +篙 +篝 +篠 +篡 +篤 +篥 +篦 +篩 +篪 +篭 +篯 +篳 +篷 +簀 +簃 +簇 +簉 +簋 +簍 +簑 +簕 +簗 +簞 +簠 +簡 +簧 +簪 +簫 +簷 +簸 +簹 +簺 +簽 +簾 +簿 +籀 +籃 +籌 +籍 +籐 +籙 +籛 +籜 +籝 +籟 +籠 +籣 +籤 +籥 +籪 +籬 +籮 +籲 +米 +籽 +籾 +粄 +粉 +粍 +粑 +粒 +粕 +粗 +粘 +粟 +粢 +粥 +粦 +粧 +粩 +粱 +粲 +粳 +粵 +粹 +粼 +粽 +精 +粿 +糀 +糅 +糊 +糌 +糍 +糎 +糕 +糖 +糙 +糜 +糝 +糞 +糟 +糠 +糢 +糧 +糬 +糯 +糰 +糴 +糶 +糸 +糹 +糺 +系 +糾 +紀 +紂 +約 +紅 +紆 +紇 +紈 +紉 +紊 +紋 +納 +紐 +紑 +紓 +純 +紕 +紗 +紘 +紙 +級 +紛 +紜 +紝 +紞 +素 +紡 +索 +紫 +紮 +累 +細 +紱 +紲 +紳 +紵 +紹 +紺 +紿 +終 +絃 +組 +絆 +経 +絎 +結 +絕 +絛 +絜 +絞 +絡 +絢 +給 +絨 +絪 +絮 +統 +絲 +絳 +絵 +絶 +絹 +絺 +綁 +綃 +綈 +綉 +綎 +綏 +經 +綖 +継 +続 +綜 +綝 +綞 +綠 +綢 +綣 +綦 +綧 +綫 +綬 +維 +綮 +綰 +綱 +網 +綳 +綴 +綸 +綺 +綻 +綽 +綾 +綿 +緁 +緃 +緄 +緈 +緊 +緋 +総 +緑 +緒 +緖 +緘 +線 +緜 +緝 +緞 +締 +緡 +緣 +緤 +編 +緩 +緬 +緯 +緱 +緲 +練 +緹 +緻 +縂 +縄 +縈 +縉 +縊 +縕 +縛 +縝 +縞 +縠 +縡 +縣 +縤 +縫 +縮 +縯 +縱 +縴 +縵 +縷 +縹 +縻 +總 +績 +繁 +繃 +繆 +繇 +繒 +織 +繕 +繖 +繙 +繚 +繞 +繡 +繩 +繪 +繫 +繭 +繰 +繳 +繹 +繻 +繼 +繽 +繾 +纁 +纂 +纈 +續 +纍 +纏 +纓 +纔 +纕 +纖 +纘 +纛 +纜 +缐 +缶 +缸 +缺 +缽 +罃 +罄 +罅 +罈 +罉 +罌 +罍 +罐 +罔 +罕 +罘 +罟 +罡 +罨 +罩 +罪 +置 +罰 +罱 +署 +罳 +罵 +罶 +罷 +罹 +罽 +羂 +羅 +羆 +羈 +羊 +羋 +羌 +美 +羔 +羕 +羗 +羙 +羚 +羞 +羡 +羣 +群 +羥 +羧 +羨 +義 +羯 +羰 +羱 +羲 +羸 +羹 +羽 +羿 +翀 +翁 +翂 +翃 +翅 +翊 +翌 +翎 +翏 +習 +翔 +翕 +翙 +翜 +翟 +翠 +翡 +翥 +翦 +翩 +翬 +翮 +翰 +翱 +翳 +翹 +翻 +翼 +耀 +老 +考 +耄 +者 +耆 +而 +耍 +耎 +耐 +耑 +耒 +耔 +耕 +耗 +耘 +耙 +耜 +耦 +耨 +耬 +耳 +耵 +耶 +耷 +耽 +耿 +聃 +聆 +聊 +聒 +聖 +聘 +聚 +聞 +聟 +聨 +聯 +聰 +聱 +聲 +聳 +聴 +聶 +職 +聽 +聾 +聿 +肄 +肅 +肆 +肇 +肉 +肋 +肌 +肏 +肖 +肘 +肚 +肛 +肜 +肝 +肟 +股 +肢 +肥 +肩 +肪 +肫 +肯 +肱 +育 +肸 +肹 +肺 +肼 +肽 +胂 +胃 +胄 +胅 +胇 +胊 +背 +胍 +胎 +胖 +胗 +胙 +胚 +胛 +胝 +胞 +胡 +胤 +胥 +胬 +胭 +胰 +胱 +胳 +胴 +胸 +胺 +胼 +能 +脂 +脅 +脆 +脇 +脈 +脊 +脒 +脖 +脘 +脛 +脣 +脩 +脫 +脬 +脭 +脯 +脲 +脳 +脷 +脹 +脾 +腆 +腈 +腊 +腋 +腌 +腎 +腐 +腑 +腓 +腔 +腕 +腥 +腦 +腧 +腩 +腫 +腮 +腰 +腱 +腳 +腴 +腸 +腹 +腺 +腿 +膀 +膂 +膈 +膊 +膏 +膚 +膛 +膜 +膝 +膠 +膣 +膥 +膦 +膨 +膩 +膮 +膳 +膺 +膽 +膾 +膿 +臀 +臂 +臃 +臆 +臉 +臊 +臍 +臏 +臘 +臚 +臞 +臟 +臠 +臣 +臧 +臨 +自 +臭 +臯 +至 +致 +臺 +臻 +臼 +臾 +舂 +舅 +與 +興 +舉 +舊 +舌 +舍 +舎 +舒 +舔 +舖 +舘 +舛 +舜 +舞 +舟 +舢 +舥 +舨 +舩 +航 +舫 +般 +舲 +舵 +舶 +舷 +舸 +船 +舺 +艅 +艇 +艉 +艋 +艎 +艏 +艔 +艘 +艙 +艚 +艦 +艮 +良 +艱 +色 +艶 +艷 +艸 +艽 +艾 +艿 +芃 +芊 +芋 +芍 +芎 +芑 +芒 +芘 +芙 +芛 +芝 +芡 +芥 +芨 +芩 +芪 +芫 +芬 +芭 +芮 +芯 +花 +芳 +芴 +芷 +芸 +芹 +芻 +芽 +芾 +苄 +苅 +苑 +苒 +苓 +苔 +苕 +苗 +苛 +苜 +苝 +苞 +苟 +苡 +苣 +苤 +若 +苦 +苧 +苪 +苫 +苯 +英 +苳 +苴 +苷 +苺 +苻 +苼 +苾 +茀 +茁 +茂 +范 +茄 +茅 +茆 +茇 +茈 +茉 +茌 +茗 +茘 +茚 +茛 +茜 +茝 +茨 +茫 +茬 +茭 +茮 +茯 +茱 +茲 +茴 +茵 +茶 +茷 +茸 +茹 +茺 +茼 +荀 +荃 +荅 +荇 +草 +荊 +荎 +荏 +荒 +荔 +荖 +荘 +荳 +荷 +荸 +荻 +荼 +荽 +莆 +莉 +莊 +莎 +莒 +莓 +莕 +莖 +莘 +莙 +莛 +莜 +莞 +莠 +莢 +莧 +莨 +莩 +莪 +莫 +莽 +莿 +菀 +菁 +菅 +菇 +菈 +菉 +菊 +菌 +菍 +菏 +菑 +菓 +菔 +菖 +菘 +菜 +菝 +菟 +菠 +菡 +菥 +菩 +菪 +菫 +華 +菰 +菱 +菲 +菴 +菶 +菸 +菹 +菺 +菼 +菽 +菾 +萁 +萃 +萄 +萇 +萊 +萌 +萍 +萎 +萐 +萘 +萜 +萠 +萡 +萣 +萩 +萬 +萭 +萱 +萵 +萸 +萹 +萼 +落 +葃 +葆 +葉 +葊 +葎 +葑 +葒 +著 +葙 +葚 +葛 +葜 +葝 +葡 +董 +葦 +葩 +葫 +葬 +葭 +葯 +葰 +葳 +葵 +葶 +葷 +葺 +蒂 +蒄 +蒍 +蒎 +蒐 +蒓 +蒔 +蒗 +蒙 +蒜 +蒞 +蒟 +蒡 +蒢 +蒤 +蒧 +蒨 +蒭 +蒯 +蒲 +蒴 +蒸 +蒹 +蒺 +蒻 +蒼 +蒽 +蒾 +蒿 +蓀 +蓁 +蓂 +蓄 +蓆 +蓉 +蓋 +蓍 +蓑 +蓓 +蓖 +蓘 +蓚 +蓧 +蓨 +蓪 +蓬 +蓭 +蓮 +蓯 +蓳 +蓼 +蓽 +蓿 +蔆 +蔎 +蔑 +蔓 +蔔 +蔕 +蔗 +蔘 +蔚 +蔝 +蔞 +蔡 +蔣 +蔥 +蔦 +蔬 +蔭 +蔴 +蔵 +蔻 +蔽 +蕁 +蕃 +蕅 +蕈 +蕉 +蕊 +蕎 +蕑 +蕒 +蕖 +蕘 +蕙 +蕚 +蕟 +蕡 +蕢 +蕤 +蕨 +蕩 +蕪 +蕭 +蕷 +蕹 +蕺 +蕻 +蕾 +薀 +薄 +薆 +薇 +薈 +薊 +薌 +薏 +薐 +薑 +薔 +薗 +薘 +薙 +薛 +薜 +薞 +薟 +薡 +薦 +薨 +薩 +薪 +薫 +薬 +薯 +薰 +薲 +薷 +薸 +薹 +薺 +薾 +薿 +藁 +藉 +藍 +藎 +藏 +藐 +藔 +藕 +藜 +藝 +藟 +藤 +藥 +藦 +藨 +藩 +藪 +藶 +藸 +藹 +藺 +藻 +藿 +蘂 +蘄 +蘅 +蘆 +蘇 +蘊 +蘋 +蘐 +蘑 +蘓 +蘗 +蘘 +蘚 +蘞 +蘢 +蘧 +蘩 +蘭 +蘵 +蘶 +蘸 +蘼 +蘿 +虉 +虎 +虐 +虓 +虔 +處 +虖 +虛 +虜 +虞 +號 +虢 +虧 +虨 +虯 +虱 +虵 +虹 +虺 +虻 +蚆 +蚊 +蚋 +蚌 +蚍 +蚓 +蚖 +蚜 +蚝 +蚡 +蚢 +蚣 +蚤 +蚧 +蚨 +蚩 +蚪 +蚯 +蚱 +蚴 +蚵 +蚶 +蚺 +蚼 +蛀 +蛄 +蛇 +蛉 +蛋 +蛍 +蛐 +蛑 +蛔 +蛙 +蛛 +蛞 +蛟 +蛤 +蛭 +蛯 +蛸 +蛹 +蛺 +蛻 +蛾 +蜀 +蜂 +蜃 +蜆 +蜇 +蜈 +蜉 +蜊 +蜍 +蜑 +蜒 +蜓 +蜘 +蜚 +蜛 +蜜 +蜞 +蜢 +蜣 +蜥 +蜨 +蜮 +蜯 +蜱 +蜴 +蜷 +蜻 +蜾 +蜿 +蝀 +蝌 +蝍 +蝎 +蝓 +蝕 +蝗 +蝘 +蝙 +蝚 +蝟 +蝠 +蝣 +蝤 +蝦 +蝨 +蝮 +蝯 +蝰 +蝲 +蝴 +蝶 +蝸 +蝽 +螂 +螃 +螄 +螅 +螈 +螋 +融 +螐 +螔 +螞 +螟 +螠 +螢 +螣 +螥 +螫 +螭 +螯 +螳 +螶 +螺 +螻 +螽 +螾 +蟀 +蟄 +蟅 +蟆 +蟊 +蟋 +蟌 +蟎 +蟑 +蟒 +蟜 +蟠 +蟥 +蟪 +蟫 +蟬 +蟯 +蟲 +蟳 +蟴 +蟶 +蟹 +蟻 +蟾 +蠂 +蠃 +蠄 +蠅 +蠆 +蠊 +蠋 +蠍 +蠐 +蠑 +蠓 +蠔 +蠕 +蠖 +蠘 +蠙 +蠟 +蠡 +蠢 +蠣 +蠱 +蠲 +蠵 +蠶 +蠷 +蠹 +蠻 +血 +衂 +衆 +行 +衍 +衎 +術 +衕 +衖 +街 +衙 +衚 +衛 +衜 +衝 +衞 +衡 +衢 +衣 +表 +衩 +衫 +衰 +衲 +衷 +衽 +衾 +衿 +袁 +袂 +袈 +袋 +袍 +袓 +袖 +袛 +袞 +袤 +袪 +被 +袱 +袴 +袾 +裁 +裂 +裊 +裎 +裒 +裔 +裕 +裖 +裘 +裙 +補 +裝 +裟 +裡 +裨 +裬 +裱 +裳 +裴 +裵 +裸 +裹 +製 +裾 +裿 +褀 +褂 +複 +褌 +褍 +褎 +褐 +褒 +褓 +褔 +褘 +褙 +褚 +褞 +褥 +褧 +褪 +褫 +褭 +褲 +褶 +褸 +褻 +襄 +襌 +襖 +襞 +襟 +襠 +襤 +襦 +襪 +襯 +襲 +襴 +襶 +襻 +襾 +西 +要 +覃 +覆 +覇 +覈 +見 +覌 +規 +覓 +視 +覚 +覡 +覦 +覧 +親 +覬 +覲 +観 +覺 +覽 +覿 +觀 +角 +觔 +觙 +觚 +觜 +解 +觭 +觱 +觴 +觶 +觸 +觿 +言 +訁 +訂 +訃 +訇 +計 +訊 +訌 +討 +訏 +訐 +訒 +訓 +訔 +訕 +訖 +託 +記 +訛 +訝 +訟 +訣 +訥 +訪 +設 +許 +訴 +訶 +診 +註 +証 +訾 +詁 +詆 +詈 +詐 +詒 +詔 +評 +詛 +詞 +詠 +詡 +詢 +詣 +詥 +試 +詧 +詩 +詫 +詭 +詮 +詰 +話 +該 +詳 +詵 +詹 +詼 +誄 +誅 +誇 +誌 +認 +誒 +誓 +誕 +誘 +語 +誠 +誡 +誣 +誤 +誥 +誦 +誨 +說 +説 +読 +誰 +課 +誴 +誹 +誼 +誾 +調 +談 +請 +諍 +諏 +諒 +論 +諗 +諜 +諟 +諠 +諡 +諤 +諦 +諧 +諪 +諫 +諭 +諮 +諱 +諲 +諳 +諴 +諶 +諷 +諸 +諺 +諼 +諾 +謀 +謁 +謂 +謄 +謇 +謊 +謌 +謎 +謏 +謐 +謔 +謖 +謗 +謙 +謚 +講 +謜 +謝 +謠 +謢 +謤 +謨 +謩 +謫 +謬 +謳 +謹 +謾 +證 +譏 +譓 +譔 +識 +譙 +譚 +譜 +譞 +警 +譫 +譬 +譭 +譯 +議 +譲 +譳 +譴 +護 +譽 +譿 +讀 +讃 +變 +讌 +讎 +讓 +讖 +讙 +讚 +讜 +讞 +谷 +谿 +豁 +豆 +豇 +豈 +豉 +豊 +豌 +豎 +豐 +豔 +豕 +豚 +象 +豢 +豨 +豪 +豫 +豬 +豳 +豸 +豹 +豺 +豿 +貂 +貅 +貉 +貊 +貌 +貐 +貒 +貓 +貔 +貘 +貝 +貞 +負 +財 +貢 +貤 +貧 +貨 +販 +貪 +貫 +責 +貭 +貮 +貯 +貲 +貳 +貴 +貶 +買 +貸 +貺 +費 +貼 +貽 +貿 +賀 +賁 +賂 +賃 +賄 +資 +賈 +賊 +賑 +賒 +賓 +賔 +賕 +賚 +賜 +賞 +賠 +賡 +賢 +賣 +賤 +賦 +賨 +質 +賬 +賭 +賴 +賹 +賺 +賻 +購 +賽 +賾 +贄 +贅 +贇 +贈 +贊 +贌 +贍 +贏 +贓 +贔 +贖 +贛 +赤 +赦 +赧 +赫 +赬 +赭 +走 +赳 +赴 +起 +趁 +超 +越 +趐 +趕 +趖 +趙 +趟 +趣 +趨 +足 +趴 +趵 +趺 +趼 +趾 +跅 +跆 +跋 +跌 +跏 +跑 +跖 +跗 +跛 +距 +跟 +跡 +跣 +跤 +跨 +跩 +跪 +路 +跳 +踎 +踏 +踐 +踝 +踞 +踢 +踩 +踰 +踴 +踹 +踺 +蹂 +蹄 +蹇 +蹈 +蹉 +蹊 +蹋 +蹕 +蹙 +蹟 +蹠 +蹤 +蹦 +蹬 +蹭 +蹯 +蹲 +蹴 +蹶 +蹺 +蹻 +蹼 +躁 +躂 +躄 +躉 +躋 +躍 +躑 +躒 +躔 +躝 +躪 +身 +躬 +躰 +躲 +躺 +軀 +車 +軋 +軌 +軍 +軎 +軒 +軔 +軛 +軟 +転 +軫 +軲 +軸 +軹 +軺 +軻 +軼 +軽 +軾 +較 +輄 +輅 +載 +輋 +輒 +輓 +輔 +輕 +輛 +輝 +輞 +輟 +輥 +輦 +輩 +輪 +輬 +輭 +輯 +輶 +輸 +輻 +輾 +輿 +轀 +轂 +轄 +轅 +轆 +轉 +轍 +轎 +轘 +轝 +轟 +轤 +辛 +辜 +辟 +辣 +辦 +辧 +辨 +辭 +辮 +辯 +辰 +辱 +農 +辵 +辺 +辻 +込 +迂 +迄 +迅 +迎 +近 +返 +迢 +迤 +迥 +迦 +迪 +迫 +迭 +迮 +述 +迴 +迵 +迷 +迸 +迺 +追 +退 +送 +逃 +逄 +逅 +逆 +逈 +逋 +逌 +逍 +逎 +透 +逐 +逑 +途 +逕 +逖 +逗 +這 +通 +逛 +逝 +逞 +速 +造 +逢 +連 +逤 +逨 +逮 +逯 +進 +逴 +逵 +逸 +逹 +逺 +逼 +逾 +遁 +遂 +遄 +遇 +遊 +運 +遍 +過 +遏 +遐 +遒 +道 +達 +違 +遘 +遙 +遛 +遜 +遞 +遠 +遢 +遣 +遨 +適 +遭 +遮 +遯 +遲 +遴 +遵 +遶 +遷 +選 +遹 +遺 +遼 +避 +邀 +邁 +邂 +邃 +還 +邇 +邈 +邉 +邊 +邋 +邏 +邑 +邕 +邗 +邙 +邛 +邠 +邡 +邢 +那 +邦 +邨 +邪 +邯 +邰 +邱 +邲 +邳 +邴 +邵 +邸 +邽 +邾 +郁 +郃 +郄 +郅 +郇 +郊 +郋 +郎 +郗 +郛 +郜 +郝 +郞 +郟 +郡 +郢 +郤 +部 +郪 +郫 +郭 +郯 +郳 +郴 +郵 +郷 +都 +郾 +郿 +鄂 +鄃 +鄄 +鄆 +鄉 +鄋 +鄑 +鄒 +鄔 +鄖 +鄗 +鄘 +鄙 +鄚 +鄜 +鄞 +鄠 +鄢 +鄣 +鄤 +鄧 +鄩 +鄫 +鄭 +鄯 +鄰 +鄱 +鄲 +鄳 +鄴 +鄺 +酃 +酆 +酈 +酉 +酊 +酋 +酌 +配 +酎 +酏 +酐 +酒 +酔 +酗 +酚 +酞 +酡 +酢 +酣 +酥 +酩 +酪 +酬 +酮 +酯 +酰 +酴 +酵 +酶 +酷 +酸 +酺 +酼 +醁 +醂 +醃 +醅 +醇 +醉 +醋 +醌 +醍 +醐 +醒 +醚 +醛 +醜 +醞 +醢 +醣 +醪 +醫 +醬 +醮 +醯 +醴 +醺 +醾 +醿 +釀 +釁 +釆 +采 +釉 +釋 +里 +重 +野 +量 +釐 +金 +釒 +釓 +釔 +釕 +釗 +釘 +釙 +釚 +釜 +針 +釣 +釤 +釦 +釧 +釩 +釪 +釭 +釴 +釵 +釷 +釹 +釺 +鈀 +鈁 +鈄 +鈇 +鈈 +鈉 +鈊 +鈍 +鈏 +鈐 +鈑 +鈔 +鈕 +鈖 +鈞 +鈢 +鈣 +鈥 +鈦 +鈫 +鈮 +鈰 +鈳 +鈴 +鈷 +鈸 +鈹 +鈺 +鈾 +鈿 +鉀 +鉄 +鉅 +鉆 +鉈 +鉉 +鉋 +鉌 +鉍 +鉏 +鉑 +鉓 +鉗 +鉚 +鉛 +鉞 +鉟 +鉤 +鉦 +鉬 +鉭 +鉲 +鉶 +鉷 +鉸 +鉻 +鉾 +鉿 +銀 +銂 +銃 +銅 +銋 +銍 +銑 +銓 +銕 +銖 +銘 +銚 +銜 +銠 +銣 +銥 +銦 +銨 +銩 +銪 +銫 +銬 +銭 +銱 +銲 +銳 +銶 +銷 +銹 +銻 +銼 +銾 +鋁 +鋅 +鋆 +鋇 +鋌 +鋏 +鋐 +鋒 +鋕 +鋗 +鋙 +鋡 +鋤 +鋥 +鋦 +鋨 +鋪 +鋮 +鋯 +鋰 +鋱 +鋳 +鋶 +鋸 +鋹 +鋼 +錀 +錄 +錏 +錐 +錒 +錕 +錘 +錚 +錞 +錟 +錠 +錡 +錢 +錦 +錨 +錫 +錬 +錮 +錯 +錳 +錶 +錸 +錻 +鍀 +鍇 +鍈 +鍉 +鍊 +鍋 +鍍 +鍏 +鍔 +鍘 +鍛 +鍝 +鍟 +鍠 +鍥 +鍩 +鍬 +鍱 +鍳 +鍵 +鍶 +鍷 +鍺 +鍼 +鍾 +鎂 +鎅 +鎊 +鎌 +鎏 +鎓 +鎔 +鎖 +鎗 +鎘 +鎚 +鎛 +鎢 +鎣 +鎦 +鎧 +鎪 +鎬 +鎭 +鎮 +鎰 +鎳 +鎵 +鎻 +鏃 +鏇 +鏈 +鏊 +鏌 +鏐 +鏑 +鏓 +鏖 +鏗 +鏘 +鏜 +鏝 +鏞 +鏟 +鏡 +鏢 +鏤 +鏦 +鏳 +鏴 +鏵 +鏷 +鏻 +鏽 +鐃 +鐇 +鐈 +鐓 +鐔 +鐘 +鐙 +鐠 +鐡 +鐤 +鐦 +鐧 +鐫 +鐬 +鐭 +鐮 +鐲 +鐳 +鐵 +鐸 +鐺 +鐽 +鐿 +鑀 +鑁 +鑂 +鑄 +鑅 +鑊 +鑌 +鑑 +鑒 +鑛 +鑠 +鑣 +鑨 +鑪 +鑫 +鑭 +鑰 +鑲 +鑴 +鑷 +鑼 +鑽 +鑾 +鑿 +長 +門 +閂 +閃 +閆 +閉 +開 +閎 +閏 +閑 +閒 +間 +閔 +閘 +閜 +閞 +閟 +関 +閣 +閥 +閦 +閨 +閩 +閬 +閭 +閰 +閱 +閶 +閹 +閻 +閼 +閾 +閿 +闆 +闇 +闈 +闊 +闋 +闌 +闍 +闐 +闓 +闔 +闕 +闖 +闘 +關 +闞 +闡 +闢 +闥 +阜 +阝 +阡 +阪 +阭 +阮 +阯 +阱 +防 +阻 +阿 +陀 +陁 +陂 +附 +陋 +陌 +降 +限 +陔 +陘 +陛 +陜 +陝 +陞 +陟 +陡 +院 +陣 +除 +陪 +陬 +陰 +陲 +陳 +陵 +陶 +陷 +陸 +険 +陽 +隄 +隅 +隆 +隈 +隊 +隋 +隍 +階 +隔 +隕 +隗 +隘 +隙 +際 +障 +隣 +隧 +隨 +險 +隰 +隱 +隲 +隳 +隴 +隷 +隸 +隹 +隻 +隼 +雀 +雁 +雄 +雅 +集 +雇 +雉 +雋 +雌 +雍 +雎 +雑 +雒 +雕 +雖 +雙 +雛 +雜 +雝 +雞 +離 +難 +雨 +雩 +雪 +雫 +雯 +雱 +雲 +零 +雷 +雹 +電 +需 +霄 +霅 +霆 +震 +霈 +霉 +霊 +霍 +霎 +霏 +霑 +霓 +霖 +霙 +霜 +霞 +霤 +霧 +霨 +霰 +露 +霶 +霸 +霹 +霽 +霾 +靁 +靂 +靄 +靈 +靉 +靑 +青 +靖 +靚 +靛 +靜 +非 +靠 +靡 +面 +革 +靫 +靬 +靭 +靳 +靴 +靶 +靺 +靼 +鞅 +鞆 +鞋 +鞍 +鞏 +鞘 +鞞 +鞠 +鞣 +鞥 +鞦 +鞨 +鞭 +鞮 +鞴 +韁 +韃 +韆 +韋 +韌 +韑 +韓 +韙 +韜 +韞 +韠 +韡 +韭 +韮 +音 +韶 +韺 +韻 +韾 +響 +頁 +頂 +頃 +項 +順 +須 +頊 +頌 +頍 +頎 +頏 +預 +頑 +頒 +頓 +頔 +頗 +領 +頜 +頠 +頡 +頤 +頦 +頫 +頭 +頰 +頴 +頵 +頷 +頸 +頹 +頻 +頼 +顆 +題 +額 +顎 +顏 +顒 +顓 +顔 +顕 +顗 +願 +顙 +顛 +類 +顥 +顧 +顫 +顯 +顰 +顱 +顳 +顴 +風 +颮 +颯 +颱 +颶 +颺 +颼 +飄 +飆 +飈 +飛 +食 +飠 +飡 +飢 +飥 +飩 +飪 +飫 +飬 +飭 +飮 +飯 +飲 +飴 +飼 +飽 +飾 +餃 +餄 +餅 +餉 +養 +餌 +餎 +餐 +餒 +餓 +餗 +餘 +餚 +餛 +餞 +餠 +餡 +館 +餮 +餵 +餺 +餾 +餿 +饃 +饅 +饋 +饌 +饑 +饒 +饕 +饗 +饞 +饟 +饢 +首 +馗 +馘 +香 +馛 +馥 +馦 +馨 +馬 +馭 +馮 +馯 +馱 +馳 +馴 +馼 +駁 +駄 +駅 +駆 +駐 +駑 +駒 +駔 +駕 +駘 +駙 +駛 +駝 +駟 +駢 +駭 +駰 +駱 +駿 +騁 +騂 +騄 +騅 +騋 +騎 +騏 +験 +騖 +騙 +騤 +騨 +騫 +騭 +騮 +騰 +騶 +騷 +騾 +驁 +驃 +驄 +驅 +驊 +驌 +驍 +驎 +驒 +驕 +驗 +驚 +驛 +驟 +驢 +驤 +驥 +驩 +驪 +骨 +骯 +骰 +骶 +骷 +骸 +骼 +髀 +髂 +髎 +髏 +髑 +髒 +髓 +體 +高 +髙 +髡 +髦 +髪 +髭 +髮 +髯 +髲 +髷 +髹 +髻 +鬃 +鬄 +鬅 +鬆 +鬍 +鬚 +鬟 +鬢 +鬣 +鬥 +鬧 +鬨 +鬩 +鬪 +鬬 +鬮 +鬯 +鬱 +鬲 +鬹 +鬻 +鬼 +魁 +魂 +魃 +魄 +魅 +魈 +魋 +魍 +魎 +魏 +魔 +魕 +魘 +魚 +魛 +魞 +魟 +魣 +魨 +魩 +魮 +魯 +魴 +魷 +鮀 +鮁 +鮃 +鮄 +鮊 +鮋 +鮍 +鮐 +鮑 +鮒 +鮓 +鮗 +鮜 +鮟 +鮠 +鮡 +鮣 +鮨 +鮪 +鮫 +鮭 +鮮 +鮰 +鮸 +鮹 +鮻 +鯀 +鯁 +鯃 +鯇 +鯉 +鯊 +鯏 +鯒 +鯓 +鯔 +鯕 +鯖 +鯗 +鯙 +鯛 +鯡 +鯢 +鯤 +鯧 +鯨 +鯪 +鯭 +鯮 +鯰 +鯶 +鯷 +鯻 +鯽 +鯿 +鰂 +鰃 +鰆 +鰈 +鰉 +鰍 +鰏 +鰒 +鰓 +鰕 +鰗 +鰛 +鰜 +鰟 +鰣 +鰤 +鰧 +鰨 +鰩 +鰭 +鰮 +鰱 +鰲 +鰳 +鰶 +鰷 +鰹 +鰺 +鰻 +鰼 +鰾 +鱀 +鱂 +鱅 +鱇 +鱈 +鱉 +鱊 +鱒 +鱓 +鱔 +鱖 +鱗 +鱘 +鱚 +鱝 +鱟 +鱠 +鱣 +鱥 +鱧 +鱨 +鱬 +鱮 +鱰 +鱲 +鱵 +鱷 +鱸 +鱺 +鱻 +鳥 +鳧 +鳩 +鳯 +鳰 +鳳 +鳴 +鳶 +鳽 +鴆 +鴇 +鴉 +鴒 +鴓 +鴕 +鴗 +鴛 +鴝 +鴞 +鴟 +鴡 +鴣 +鴦 +鴨 +鴫 +鴯 +鴰 +鴴 +鴻 +鴿 +鵂 +鵄 +鵎 +鵐 +鵑 +鵒 +鵓 +鵙 +鵜 +鵝 +鵞 +鵟 +鵠 +鵡 +鵪 +鵬 +鵯 +鵰 +鵲 +鵵 +鵼 +鵾 +鶆 +鶇 +鶉 +鶏 +鶒 +鶓 +鶘 +鶚 +鶡 +鶥 +鶩 +鶬 +鶯 +鶲 +鶴 +鶹 +鶺 +鶻 +鶼 +鶿 +鷂 +鷄 +鷉 +鷎 +鷓 +鷗 +鷙 +鷚 +鷟 +鷥 +鷦 +鷫 +鷯 +鷲 +鷳 +鷸 +鷹 +鷺 +鸊 +鸌 +鸐 +鸑 +鸕 +鸘 +鸚 +鸛 +鸜 +鸝 +鸞 +鹮 +鹵 +鹹 +鹼 +鹽 +鹿 +麂 +麅 +麇 +麈 +麊 +麋 +麐 +麒 +麓 +麗 +麝 +麞 +麟 +麥 +麩 +麪 +麯 +麴 +麵 +麹 +麺 +麻 +麼 +麽 +麾 +麿 +黁 +黃 +黇 +黌 +黍 +黎 +黏 +黐 +黑 +黒 +黔 +默 +黙 +黛 +黜 +黝 +點 +黟 +黥 +黧 +黨 +黯 +黴 +黶 +黻 +黼 +黽 +黿 +鼂 +鼇 +鼈 +鼉 +鼎 +鼐 +鼒 +鼓 +鼕 +鼙 +鼠 +鼢 +鼩 +鼬 +鼯 +鼱 +鼴 +鼷 +鼻 +鼽 +鼾 +齊 +齋 +齒 +齕 +齡 +齣 +齦 +齧 +齲 +齶 +龍 +龎 +龐 +龑 +龔 +龕 +龜 +龝 +龠 +龢 +郎 +凉 +﹑ +﹗ +﹝ +﹞ +﹢ +! +" +# +$ +% +& +' +( +) +* ++ +, +- +. +/ +0 +1 +2 +3 +4 +5 +6 +7 +8 +9 +: +; +< += +> +? +A +B +C +D +E +F +G +H +I +K +L +M +N +O +P +R +S +T +U +V +W +Y +Z +[ +] +` +a +b +c +d +e +f +g +h +i +j +k +l +m +n +o +p +r +s +t +u +z +{ +| +} +~ +¥ +𣇉 + diff --git a/modules/onnx_ocr_module/src/ppocr/utils/dict/confuse.pkl b/modules/onnx_ocr_module/src/ppocr/utils/dict/confuse.pkl new file mode 100644 index 0000000..e5d4853 Binary files /dev/null and b/modules/onnx_ocr_module/src/ppocr/utils/dict/confuse.pkl differ diff --git a/modules/onnx_ocr_module/src/ppocr/utils/dict/cyrillic_dict.txt b/modules/onnx_ocr_module/src/ppocr/utils/dict/cyrillic_dict.txt new file mode 100644 index 0000000..2b6f664 --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/utils/dict/cyrillic_dict.txt @@ -0,0 +1,163 @@ + +! +# +$ +% +& +' +( ++ +, +- +. +/ +0 +1 +2 +3 +4 +5 +6 +7 +8 +9 +: +? +@ +A +B +C +D +E +F +G +H +I +J +K +L +M +N +O +P +Q +R +S +T +U +V +W +X +Y +Z +_ +a +b +c +d +e +f +g +h +i +j +k +l +m +n +o +p +q +r +s +t +u +v +w +x +y +z +É +é +Ё +Є +І +Ј +Љ +Ў +А +Б +В +Г +Д +Е +Ж +З +И +Й +К +Л +М +Н +О +П +Р +С +Т +У +Ф +Х +Ц +Ч +Ш +Щ +Ъ +Ы +Ь +Э +Ю +Я +а +б +в +г +д +е +ж +з +и +й +к +л +м +н +о +п +р +с +т +у +ф +х +ц +ч +ш +щ +ъ +ы +ь +э +ю +я +ё +ђ +є +і +ј +љ +њ +ћ +ў +џ +Ґ +ґ diff --git a/modules/onnx_ocr_module/src/ppocr/utils/dict/devanagari_dict.txt b/modules/onnx_ocr_module/src/ppocr/utils/dict/devanagari_dict.txt new file mode 100644 index 0000000..f559230 --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/utils/dict/devanagari_dict.txt @@ -0,0 +1,167 @@ + +! +# +$ +% +& +' +( ++ +, +- +. +/ +0 +1 +2 +3 +4 +5 +6 +7 +8 +9 +: +? +@ +A +B +C +D +E +F +G +H +I +J +K +L +M +N +O +P +Q +R +S +T +U +V +W +X +Y +Z +_ +a +b +c +d +e +f +g +h +i +j +k +l +m +n +o +p +q +r +s +t +u +v +w +x +y +z +É +é +ँ +ं +ः +अ +आ +इ +ई +उ +ऊ +ऋ +ए +ऐ +ऑ +ओ +औ +क +ख +ग +घ +ङ +च +छ +ज +झ +ञ +ट +ठ +ड +ढ +ण +त +थ +द +ध +न +ऩ +प +फ +ब +भ +म +य +र +ऱ +ल +ळ +व +श +ष +स +ह +़ +ा +ि +ी +ु +ू +ृ +ॅ +े +ै +ॉ +ो +ौ +् +॒ +क़ +ख़ +ग़ +ज़ +ड़ +ढ़ +फ़ +ॠ +। +० +१ +२ +३ +४ +५ +६ +७ +८ +९ +॰ diff --git a/modules/onnx_ocr_module/src/ppocr/utils/dict/en_dict.txt b/modules/onnx_ocr_module/src/ppocr/utils/dict/en_dict.txt new file mode 100644 index 0000000..6fbd99f --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/utils/dict/en_dict.txt @@ -0,0 +1,63 @@ +0 +1 +2 +3 +4 +5 +6 +7 +8 +9 +a +b +c +d +e +f +g +h +i +j +k +l +m +n +o +p +q +r +s +t +u +v +w +x +y +z +A +B +C +D +E +F +G +H +I +J +K +L +M +N +O +P +Q +R +S +T +U +V +W +X +Y +Z + diff --git a/modules/onnx_ocr_module/src/ppocr/utils/dict/fa_dict.txt b/modules/onnx_ocr_module/src/ppocr/utils/dict/fa_dict.txt new file mode 100644 index 0000000..2328fbd --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/utils/dict/fa_dict.txt @@ -0,0 +1,136 @@ +f +a +_ +i +m +g +/ +1 +3 +I +L +S +V +R +C +2 +0 +v +l +6 +8 +5 +. +j +p +و +د +ر +ك +ن +ش +ه +ا +4 +9 +ی +ج +ِ +7 +غ +ل +س +ز +ّ +ت +ک +گ +ي +م +ب +ف +چ +خ +ق +ژ +آ +ص +پ +َ +ع +ئ +ح +ٔ +ض +ُ +ذ +أ +ى +ط +ظ +ث +ة +ً +ء +ؤ +ْ +ۀ +إ +ٍ +ٌ +ٰ +ٓ +ٱ +s +c +e +n +w +N +E +W +Y +D +O +H +A +d +z +r +T +G +o +t +x +h +b +B +M +Z +u +P +F +y +q +U +K +k +J +Q +' +X +# +? +% +$ +, +: +& +! +- +( +É +@ +é ++ + diff --git a/modules/onnx_ocr_module/src/ppocr/utils/dict/french_dict.txt b/modules/onnx_ocr_module/src/ppocr/utils/dict/french_dict.txt new file mode 100644 index 0000000..e8f657d --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/utils/dict/french_dict.txt @@ -0,0 +1,136 @@ +f +e +n +c +h +_ +i +m +g +/ +r +v +a +l +t +w +o +d +6 +1 +. +p +B +u +2 +à +3 +R +y +4 +U +E +A +5 +P +O +S +T +D +7 +Z +8 +I +N +L +G +M +H +0 +J +K +- +9 +F +C +V +é +X +' +s +Q +: +è +x +b +Y +Œ +É +z +W +Ç +È +k +Ô +ô +€ +À +Ê +q +ù +° +ê +î +* + +j +" +, +â +% +û +ç +ü +? +! +; +ö +( +) +ï +º +ó +ø +å ++ +™ +á +Ë +< +² +Á +Î +& +@ +œ +ε +Ü +ë +[ +] +í +ò +Ö +ä +ß +« +» +ú +ñ +æ +µ +³ +Å +$ +# + diff --git a/modules/onnx_ocr_module/src/ppocr/utils/dict/german_dict.txt b/modules/onnx_ocr_module/src/ppocr/utils/dict/german_dict.txt new file mode 100644 index 0000000..5e121af --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/utils/dict/german_dict.txt @@ -0,0 +1,143 @@ + +! +" +# +$ +% +& +' +( +) +* ++ +, +- +. +/ +0 +1 +2 +3 +4 +5 +6 +7 +8 +9 +: +; += +> +? +@ +A +B +C +D +E +F +G +H +I +J +K +L +M +N +O +P +Q +R +S +T +U +V +W +X +Y +Z +[ +] +_ +a +b +c +d +e +f +g +h +i +j +k +l +m +n +o +p +q +r +s +t +u +v +w +x +y +z +£ +§ +­ +° +´ +µ +· +º +¿ +Á +Ä +Å +É +Ï +Ô +Ö +Ü +ß +à +á +â +ã +ä +å +æ +ç +è +é +ê +ë +í +ï +ñ +ò +ó +ô +ö +ø +ù +ú +û +ü +ō +Š +Ÿ +ʒ +β +δ +з +Ṡ +‘ +€ +© +ª +« +¬ diff --git a/modules/onnx_ocr_module/src/ppocr/utils/dict/gujarati_dict.txt b/modules/onnx_ocr_module/src/ppocr/utils/dict/gujarati_dict.txt new file mode 100644 index 0000000..36f5589 --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/utils/dict/gujarati_dict.txt @@ -0,0 +1,48 @@ +અ +આ +ઇ +ઈ +ઉ +ઊ +ઋ +ઌ +એ +ઐ +ઓ +ઔ +અં +અઃ +ક +ખ +ગ +ઘ +ઙ +ચ +છ +જ +ઝ +ઞ +ટ +ઠ +ડ +ઢ +ણ +ત +થ +દ +ધ +ન +પ +ફ +બ +ભ +મ +ય +ર +લ +ળ +વ +શ +ષ +સ +હ diff --git a/modules/onnx_ocr_module/src/ppocr/utils/dict/hebrew_dict.txt b/modules/onnx_ocr_module/src/ppocr/utils/dict/hebrew_dict.txt new file mode 100644 index 0000000..ed30137 --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/utils/dict/hebrew_dict.txt @@ -0,0 +1,214 @@ +! +# +$ +% +& +' +( ++ +, +- +. +/ +0 +1 +2 +3 +4 +5 +6 +7 +8 +9 +: +? +@ +A +B +C +D +E +F +G +H +I +J +K +L +M +N +O +P +Q +R +S +T +U +V +W +X +Y +Z +_ +a +b +c +d +e +f +g +h +i +j +k +l +m +n +o +p +q +r +s +t +u +v +w +x +y +z +É +é +֑ +֒ +֓ +֔ +֕ +֖ +֗ +֘ +֙ +֚ +֛ +֜ +֝ +֞ +֟ +֠ +֡ +֢ +֣ +֤ +֥ +֦ +֧ +֨ +֩ +֪ +֫ +֬ +֭ +֮ +֯ +ְ +ֱ +ֲ +ֳ +ִ +ֵ +ֶ +ַ +ָ +ֹ +ֺ +ֻ +ּ +ֽ +־ +ֿ +׀ +ׁ +ׂ +׃ +ׄ +ׅ +׆ +ׇ +א +ב +ג +ד +ה +ו +ז +ח +ט +י +ך +כ +ל +ם +מ +ן +נ +ס +ע +ף +פ +ץ +צ +ק +ר +ש +ת +ׯ +װ +ױ +ײ +׳ +״ +יִ +ﬞ +ײַ +ﬠ +ﬡ +ﬢ +ﬣ +ﬤ +ﬥ +ﬦ +ﬧ +ﬨ +﬩ +שׁ +שׂ +שּׁ +שּׂ +אַ +אָ +אּ +בּ +גּ +דּ +הּ +וּ +זּ +טּ +יּ +ךּ +כּ +לּ +מּ +נּ +סּ +ףּ +פּ +צּ +קּ +רּ +שּ +תּ +וֹ +בֿ +כֿ +פֿ +ﭏ diff --git a/modules/onnx_ocr_module/src/ppocr/utils/dict/hi_dict.txt b/modules/onnx_ocr_module/src/ppocr/utils/dict/hi_dict.txt new file mode 100644 index 0000000..8dfedb5 --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/utils/dict/hi_dict.txt @@ -0,0 +1,162 @@ + +! +# +$ +% +& +' +( ++ +, +- +. +/ +0 +1 +2 +3 +4 +5 +6 +7 +8 +9 +: +? +@ +A +B +C +D +E +F +G +H +I +J +K +L +M +N +O +P +Q +R +S +T +U +V +W +X +Y +Z +_ +a +b +c +d +e +f +g +h +i +j +k +l +m +n +o +p +q +r +s +t +u +v +w +x +y +z +É +é +ँ +ं +ः +अ +आ +इ +ई +उ +ऊ +ऋ +ए +ऐ +ऑ +ओ +औ +क +ख +ग +घ +ङ +च +छ +ज +झ +ञ +ट +ठ +ड +ढ +ण +त +थ +द +ध +न +प +फ +ब +भ +म +य +र +ल +ळ +व +श +ष +स +ह +़ +ा +ि +ी +ु +ू +ृ +ॅ +े +ै +ॉ +ो +ौ +् +क़ +ख़ +ग़ +ज़ +ड़ +ढ़ +फ़ +० +१ +२ +३ +४ +५ +६ +७ +८ +९ +॰ diff --git a/modules/onnx_ocr_module/src/ppocr/utils/dict/it_dict.txt b/modules/onnx_ocr_module/src/ppocr/utils/dict/it_dict.txt new file mode 100644 index 0000000..e692c6d --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/utils/dict/it_dict.txt @@ -0,0 +1,118 @@ +i +t +_ +m +g +/ +5 +I +L +S +V +R +C +2 +0 +1 +v +a +l +7 +8 +9 +6 +. +j +p + +e +r +o +d +s +n +3 +4 +P +u +c +A +- +, +" +z +h +f +b +q +ì +' +à +O +è +G +ù +é +ò +; +F +E +B +N +H +k +: +U +T +X +D +K +? +[ +M +­ +x +y +( +) +W +ö +º +w +] +Q +J ++ +ü +! +È +á +% += +» +ñ +Ö +Y +ä +í +Z +« +@ +ó +ø +ï +ú +ê +ç +Á +É +Å +ß +{ +} +& +` +û +î +# +$ diff --git a/modules/onnx_ocr_module/src/ppocr/utils/dict/japan_dict.txt b/modules/onnx_ocr_module/src/ppocr/utils/dict/japan_dict.txt new file mode 100644 index 0000000..339d4b8 --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/utils/dict/japan_dict.txt @@ -0,0 +1,4399 @@ +! +" +# +$ +% +& +' +( +) +* ++ +, +- +. +/ +0 +1 +2 +3 +4 +5 +6 +7 +8 +9 +: +; +< += +> +? +A +B +C +D +E +F +G +H +I +J +K +L +M +N +O +P +Q +R +S +T +U +V +W +X +Y +Z +[ +] +_ +` +a +b +c +d +e +f +g +h +i +j +k +l +m +n +o +p +q +r +s +t +u +v +w +x +y +z +© +° +² +´ +½ +Á +Ä +Å +Ç +È +É +Í +Ó +Ö +× +Ü +ß +à +á +â +ã +ä +å +æ +ç +è +é +ê +ë +í +ð +ñ +ò +ó +ô +õ +ö +ø +ú +û +ü +ý +ā +ă +ą +ć +Č +č +đ +ē +ė +ę +ğ +ī +ı +Ł +ł +ń +ň +ō +ř +Ş +ş +Š +š +ţ +ū +ż +Ž +ž +Ș +ș +ț +Δ +α +λ +μ +φ +Г +О +а +в +л +о +р +с +т +я +ồ +​ +— +― +’ +“ +” +… +℃ +→ +∇ +− +■ +☆ +  +、 +。 +々 +〆 +〈 +〉 +「 +」 +『 +』 +〔 +〕 +〜 +ぁ +あ +ぃ +い +う +ぇ +え +ぉ +お +か +が +き +ぎ +く +ぐ +け +げ +こ +ご +さ +ざ +し +じ +す +ず +せ +ぜ +そ +ぞ +た +だ +ち +ぢ +っ +つ +づ +て +で +と +ど +な +に +ぬ +ね +の +は +ば +ぱ +ひ +び +ぴ +ふ +ぶ +ぷ +へ +べ +ぺ +ほ +ぼ +ぽ +ま +み +む +め +も +ゃ +や +ゅ +ゆ +ょ +よ +ら +り +る +れ +ろ +わ +ゑ +を +ん +ゝ +ゞ +ァ +ア +ィ +イ +ゥ +ウ +ェ +エ +ォ +オ +カ +ガ +キ +ギ +ク +グ +ケ +ゲ +コ +ゴ +サ +ザ +シ +ジ +ス +ズ +セ +ゼ +ソ +ゾ +タ +ダ +チ +ヂ +ッ +ツ +ヅ +テ +デ +ト +ド +ナ +ニ +ヌ +ネ +ノ +ハ +バ +パ +ヒ +ビ +ピ +フ +ブ +プ +ヘ +ベ +ペ +ホ +ボ +ポ +マ +ミ +ム +メ +モ +ャ +ヤ +ュ +ユ +ョ +ヨ +ラ +リ +ル +レ +ロ +ワ +ヰ +ン +ヴ +ヵ +ヶ +・ +ー +㈱ +一 +丁 +七 +万 +丈 +三 +上 +下 +不 +与 +丑 +且 +世 +丘 +丙 +丞 +両 +並 +中 +串 +丸 +丹 +主 +丼 +丿 +乃 +久 +之 +乎 +乏 +乗 +乘 +乙 +九 +乞 +也 +乱 +乳 +乾 +亀 +了 +予 +争 +事 +二 +于 +互 +五 +井 +亘 +亙 +些 +亜 +亟 +亡 +交 +亥 +亦 +亨 +享 +京 +亭 +亮 +人 +什 +仁 +仇 +今 +介 +仍 +仏 +仔 +仕 +他 +仗 +付 +仙 +代 +令 +以 +仮 +仰 +仲 +件 +任 +企 +伊 +伍 +伎 +伏 +伐 +休 +会 +伝 +伯 +估 +伴 +伶 +伸 +伺 +似 +伽 +佃 +但 +位 +低 +住 +佐 +佑 +体 +何 +余 +佚 +佛 +作 +佩 +佳 +併 +佶 +使 +侈 +例 +侍 +侏 +侑 +侘 +供 +依 +侠 +価 +侮 +侯 +侵 +侶 +便 +係 +促 +俄 +俊 +俔 +俗 +俘 +保 +信 +俣 +俤 +修 +俯 +俳 +俵 +俸 +俺 +倉 +個 +倍 +倒 +候 +借 +倣 +値 +倫 +倭 +倶 +倹 +偃 +假 +偈 +偉 +偏 +偐 +偕 +停 +健 +側 +偵 +偶 +偽 +傀 +傅 +傍 +傑 +傘 +備 +催 +傭 +傲 +傳 +債 +傷 +傾 +僊 +働 +像 +僑 +僕 +僚 +僧 +僭 +僮 +儀 +億 +儇 +儒 +儛 +償 +儡 +優 +儲 +儺 +儼 +兀 +允 +元 +兄 +充 +兆 +先 +光 +克 +兌 +免 +兎 +児 +党 +兜 +入 +全 +八 +公 +六 +共 +兵 +其 +具 +典 +兼 +内 +円 +冊 +再 +冑 +冒 +冗 +写 +冠 +冤 +冥 +冨 +冬 +冲 +决 +冶 +冷 +准 +凉 +凋 +凌 +凍 +凛 +凝 +凞 +几 +凡 +処 +凪 +凰 +凱 +凶 +凸 +凹 +出 +函 +刀 +刃 +分 +切 +刈 +刊 +刎 +刑 +列 +初 +判 +別 +利 +刪 +到 +制 +刷 +券 +刹 +刺 +刻 +剃 +則 +削 +剋 +前 +剖 +剛 +剣 +剤 +剥 +剪 +副 +剰 +割 +創 +剽 +劇 +劉 +劔 +力 +功 +加 +劣 +助 +努 +劫 +劭 +励 +労 +効 +劾 +勃 +勅 +勇 +勉 +勒 +動 +勘 +務 +勝 +募 +勢 +勤 +勧 +勲 +勺 +勾 +勿 +匁 +匂 +包 +匏 +化 +北 +匙 +匝 +匠 +匡 +匣 +匯 +匲 +匹 +区 +医 +匿 +十 +千 +升 +午 +卉 +半 +卍 +卑 +卒 +卓 +協 +南 +単 +博 +卜 +占 +卦 +卯 +印 +危 +即 +却 +卵 +卸 +卿 +厄 +厚 +原 +厠 +厨 +厩 +厭 +厳 +去 +参 +又 +叉 +及 +友 +双 +反 +収 +叔 +取 +受 +叙 +叛 +叟 +叡 +叢 +口 +古 +句 +叩 +只 +叫 +召 +可 +台 +叱 +史 +右 +叶 +号 +司 +吃 +各 +合 +吉 +吊 +同 +名 +后 +吏 +吐 +向 +君 +吝 +吟 +吠 +否 +含 +吸 +吹 +吻 +吽 +吾 +呂 +呆 +呈 +呉 +告 +呑 +周 +呪 +呰 +味 +呼 +命 +咀 +咄 +咋 +和 +咒 +咫 +咲 +咳 +咸 +哀 +品 +哇 +哉 +員 +哨 +哩 +哭 +哲 +哺 +唄 +唆 +唇 +唐 +唖 +唯 +唱 +唳 +唸 +唾 +啄 +商 +問 +啓 +啼 +善 +喋 +喚 +喜 +喝 +喧 +喩 +喪 +喫 +喬 +單 +喰 +営 +嗅 +嗇 +嗔 +嗚 +嗜 +嗣 +嘆 +嘉 +嘗 +嘘 +嘩 +嘯 +嘱 +嘲 +嘴 +噂 +噌 +噛 +器 +噴 +噺 +嚆 +嚢 +囀 +囃 +囉 +囚 +四 +回 +因 +団 +困 +囲 +図 +固 +国 +圀 +圃 +國 +圏 +園 +圓 +團 +圜 +土 +圧 +在 +圭 +地 +址 +坂 +均 +坊 +坐 +坑 +坡 +坤 +坦 +坪 +垂 +型 +垢 +垣 +埃 +埋 +城 +埒 +埔 +域 +埠 +埴 +埵 +執 +培 +基 +埼 +堀 +堂 +堅 +堆 +堕 +堤 +堪 +堯 +堰 +報 +場 +堵 +堺 +塀 +塁 +塊 +塑 +塔 +塗 +塘 +塙 +塚 +塞 +塩 +填 +塵 +塾 +境 +墉 +墓 +増 +墜 +墟 +墨 +墳 +墺 +墻 +墾 +壁 +壇 +壊 +壌 +壕 +士 +壬 +壮 +声 +壱 +売 +壷 +壹 +壺 +壽 +変 +夏 +夕 +外 +夙 +多 +夜 +夢 +夥 +大 +天 +太 +夫 +夬 +夭 +央 +失 +夷 +夾 +奄 +奇 +奈 +奉 +奎 +奏 +契 +奔 +奕 +套 +奘 +奠 +奢 +奥 +奨 +奪 +奮 +女 +奴 +奸 +好 +如 +妃 +妄 +妊 +妍 +妓 +妖 +妙 +妥 +妨 +妬 +妲 +妹 +妻 +妾 +姉 +始 +姐 +姓 +委 +姚 +姜 +姞 +姥 +姦 +姨 +姪 +姫 +姶 +姻 +姿 +威 +娑 +娘 +娟 +娠 +娩 +娯 +娼 +婆 +婉 +婚 +婢 +婦 +婬 +婿 +媄 +媒 +媓 +媚 +媛 +媞 +媽 +嫁 +嫄 +嫉 +嫌 +嫐 +嫗 +嫡 +嬉 +嬌 +嬢 +嬪 +嬬 +嬾 +孁 +子 +孔 +字 +存 +孚 +孝 +孟 +季 +孤 +学 +孫 +孵 +學 +宅 +宇 +守 +安 +宋 +完 +宍 +宏 +宕 +宗 +官 +宙 +定 +宛 +宜 +宝 +実 +客 +宣 +室 +宥 +宮 +宰 +害 +宴 +宵 +家 +宸 +容 +宿 +寂 +寄 +寅 +密 +寇 +富 +寒 +寓 +寔 +寛 +寝 +察 +寡 +實 +寧 +審 +寮 +寵 +寶 +寸 +寺 +対 +寿 +封 +専 +射 +将 +尉 +尊 +尋 +對 +導 +小 +少 +尖 +尚 +尤 +尪 +尭 +就 +尹 +尺 +尻 +尼 +尽 +尾 +尿 +局 +居 +屈 +届 +屋 +屍 +屎 +屏 +屑 +屓 +展 +属 +屠 +層 +履 +屯 +山 +岐 +岑 +岡 +岩 +岫 +岬 +岳 +岷 +岸 +峠 +峡 +峨 +峯 +峰 +島 +峻 +崇 +崋 +崎 +崑 +崖 +崗 +崛 +崩 +嵌 +嵐 +嵩 +嵯 +嶂 +嶋 +嶠 +嶺 +嶼 +嶽 +巀 +巌 +巒 +巖 +川 +州 +巡 +巣 +工 +左 +巧 +巨 +巫 +差 +己 +巳 +巴 +巷 +巻 +巽 +巾 +市 +布 +帆 +希 +帖 +帚 +帛 +帝 +帥 +師 +席 +帯 +帰 +帳 +帷 +常 +帽 +幄 +幅 +幇 +幌 +幔 +幕 +幟 +幡 +幢 +幣 +干 +平 +年 +并 +幸 +幹 +幻 +幼 +幽 +幾 +庁 +広 +庄 +庇 +床 +序 +底 +庖 +店 +庚 +府 +度 +座 +庫 +庭 +庵 +庶 +康 +庸 +廂 +廃 +廉 +廊 +廓 +廟 +廠 +廣 +廬 +延 +廷 +建 +廻 +廼 +廿 +弁 +弄 +弉 +弊 +弌 +式 +弐 +弓 +弔 +引 +弖 +弗 +弘 +弛 +弟 +弥 +弦 +弧 +弱 +張 +強 +弼 +弾 +彈 +彊 +彌 +彎 +当 +彗 +彙 +彝 +形 +彦 +彩 +彫 +彬 +彭 +彰 +影 +彷 +役 +彼 +往 +征 +徂 +径 +待 +律 +後 +徐 +徑 +徒 +従 +得 +徠 +御 +徧 +徨 +復 +循 +徭 +微 +徳 +徴 +德 +徹 +徽 +心 +必 +忉 +忌 +忍 +志 +忘 +忙 +応 +忠 +快 +忯 +念 +忻 +忽 +忿 +怒 +怖 +思 +怠 +怡 +急 +性 +怨 +怪 +怯 +恂 +恋 +恐 +恒 +恕 +恣 +恤 +恥 +恨 +恩 +恬 +恭 +息 +恵 +悉 +悌 +悍 +悔 +悟 +悠 +患 +悦 +悩 +悪 +悲 +悼 +情 +惇 +惑 +惚 +惜 +惟 +惠 +惣 +惧 +惨 +惰 +想 +惹 +惺 +愈 +愉 +愍 +意 +愔 +愚 +愛 +感 +愷 +愿 +慈 +態 +慌 +慎 +慕 +慢 +慣 +慧 +慨 +慮 +慰 +慶 +憂 +憎 +憐 +憑 +憙 +憤 +憧 +憩 +憬 +憲 +憶 +憾 +懇 +應 +懌 +懐 +懲 +懸 +懺 +懽 +懿 +戈 +戊 +戌 +戎 +成 +我 +戒 +戔 +或 +戚 +戟 +戦 +截 +戮 +戯 +戴 +戸 +戻 +房 +所 +扁 +扇 +扈 +扉 +手 +才 +打 +払 +托 +扮 +扱 +扶 +批 +承 +技 +抄 +把 +抑 +抓 +投 +抗 +折 +抜 +択 +披 +抱 +抵 +抹 +押 +抽 +担 +拇 +拈 +拉 +拍 +拏 +拐 +拒 +拓 +拘 +拙 +招 +拝 +拠 +拡 +括 +拭 +拳 +拵 +拶 +拾 +拿 +持 +挂 +指 +按 +挑 +挙 +挟 +挨 +振 +挺 +挽 +挿 +捉 +捕 +捗 +捜 +捧 +捨 +据 +捺 +捻 +掃 +掄 +授 +掌 +排 +掖 +掘 +掛 +掟 +採 +探 +掣 +接 +控 +推 +掩 +措 +掬 +掲 +掴 +掻 +掾 +揃 +揄 +揆 +揉 +描 +提 +揖 +揚 +換 +握 +揮 +援 +揶 +揺 +損 +搦 +搬 +搭 +携 +搾 +摂 +摘 +摩 +摸 +摺 +撃 +撒 +撞 +撤 +撥 +撫 +播 +撮 +撰 +撲 +撹 +擁 +操 +擔 +擦 +擬 +擾 +攘 +攝 +攣 +支 +收 +改 +攻 +放 +政 +故 +敏 +救 +敗 +教 +敢 +散 +敦 +敬 +数 +整 +敵 +敷 +斂 +文 +斉 +斎 +斐 +斑 +斗 +料 +斜 +斟 +斤 +斥 +斧 +斬 +断 +斯 +新 +方 +於 +施 +旁 +旅 +旋 +旌 +族 +旗 +旛 +无 +旡 +既 +日 +旦 +旧 +旨 +早 +旬 +旭 +旺 +旻 +昂 +昆 +昇 +昉 +昌 +明 +昏 +易 +昔 +星 +映 +春 +昧 +昨 +昪 +昭 +是 +昵 +昼 +晁 +時 +晃 +晋 +晏 +晒 +晟 +晦 +晧 +晩 +普 +景 +晴 +晶 +智 +暁 +暇 +暈 +暉 +暑 +暖 +暗 +暘 +暢 +暦 +暫 +暮 +暲 +暴 +暹 +暾 +曄 +曇 +曉 +曖 +曙 +曜 +曝 +曠 +曰 +曲 +曳 +更 +書 +曹 +曼 +曽 +曾 +替 +最 +會 +月 +有 +朋 +服 +朏 +朔 +朕 +朗 +望 +朝 +期 +朧 +木 +未 +末 +本 +札 +朱 +朴 +机 +朽 +杁 +杉 +李 +杏 +材 +村 +杓 +杖 +杜 +杞 +束 +条 +杢 +杣 +来 +杭 +杮 +杯 +東 +杲 +杵 +杷 +杼 +松 +板 +枅 +枇 +析 +枓 +枕 +林 +枚 +果 +枝 +枠 +枡 +枢 +枯 +枳 +架 +柄 +柊 +柏 +某 +柑 +染 +柔 +柘 +柚 +柯 +柱 +柳 +柴 +柵 +査 +柾 +柿 +栂 +栃 +栄 +栖 +栗 +校 +株 +栲 +栴 +核 +根 +栻 +格 +栽 +桁 +桂 +桃 +框 +案 +桐 +桑 +桓 +桔 +桜 +桝 +桟 +桧 +桴 +桶 +桾 +梁 +梅 +梆 +梓 +梔 +梗 +梛 +條 +梟 +梢 +梧 +梨 +械 +梱 +梲 +梵 +梶 +棄 +棋 +棒 +棗 +棘 +棚 +棟 +棠 +森 +棲 +棹 +棺 +椀 +椅 +椋 +植 +椎 +椏 +椒 +椙 +検 +椥 +椹 +椿 +楊 +楓 +楕 +楚 +楞 +楠 +楡 +楢 +楨 +楪 +楫 +業 +楮 +楯 +楳 +極 +楷 +楼 +楽 +概 +榊 +榎 +榕 +榛 +榜 +榮 +榱 +榴 +槃 +槇 +槊 +構 +槌 +槍 +槐 +様 +槙 +槻 +槽 +槿 +樂 +樋 +樓 +樗 +標 +樟 +模 +権 +横 +樫 +樵 +樹 +樺 +樽 +橇 +橋 +橘 +機 +橿 +檀 +檄 +檎 +檐 +檗 +檜 +檣 +檥 +檬 +檮 +檸 +檻 +櫃 +櫓 +櫛 +櫟 +櫨 +櫻 +欄 +欅 +欠 +次 +欣 +欧 +欲 +欺 +欽 +款 +歌 +歎 +歓 +止 +正 +此 +武 +歩 +歪 +歯 +歳 +歴 +死 +殆 +殉 +殊 +残 +殖 +殯 +殴 +段 +殷 +殺 +殻 +殿 +毀 +毅 +母 +毎 +毒 +比 +毘 +毛 +毫 +毬 +氈 +氏 +民 +気 +水 +氷 +永 +氾 +汀 +汁 +求 +汎 +汐 +汗 +汚 +汝 +江 +池 +汪 +汰 +汲 +決 +汽 +沂 +沃 +沅 +沆 +沈 +沌 +沐 +沓 +沖 +沙 +没 +沢 +沱 +河 +沸 +油 +治 +沼 +沽 +沿 +況 +泉 +泊 +泌 +法 +泗 +泡 +波 +泣 +泥 +注 +泯 +泰 +泳 +洋 +洒 +洗 +洛 +洞 +津 +洩 +洪 +洲 +洸 +洹 +活 +洽 +派 +流 +浄 +浅 +浙 +浚 +浜 +浣 +浦 +浩 +浪 +浮 +浴 +海 +浸 +涅 +消 +涌 +涙 +涛 +涯 +液 +涵 +涼 +淀 +淄 +淆 +淇 +淋 +淑 +淘 +淡 +淤 +淨 +淫 +深 +淳 +淵 +混 +淹 +添 +清 +済 +渉 +渋 +渓 +渕 +渚 +減 +渟 +渠 +渡 +渤 +渥 +渦 +温 +渫 +測 +港 +游 +渾 +湊 +湖 +湘 +湛 +湧 +湫 +湯 +湾 +湿 +満 +源 +準 +溜 +溝 +溢 +溥 +溪 +溶 +溺 +滄 +滅 +滋 +滌 +滑 +滕 +滝 +滞 +滴 +滸 +滹 +滿 +漁 +漂 +漆 +漉 +漏 +漑 +演 +漕 +漠 +漢 +漣 +漫 +漬 +漱 +漸 +漿 +潅 +潔 +潙 +潜 +潟 +潤 +潭 +潮 +潰 +潴 +澁 +澂 +澄 +澎 +澗 +澤 +澪 +澱 +澳 +激 +濁 +濃 +濟 +濠 +濡 +濤 +濫 +濯 +濱 +濾 +瀉 +瀋 +瀑 +瀕 +瀞 +瀟 +瀧 +瀬 +瀾 +灌 +灑 +灘 +火 +灯 +灰 +灸 +災 +炉 +炊 +炎 +炒 +炭 +炮 +炷 +点 +為 +烈 +烏 +烙 +烝 +烹 +焔 +焙 +焚 +無 +焦 +然 +焼 +煇 +煉 +煌 +煎 +煕 +煙 +煤 +煥 +照 +煩 +煬 +煮 +煽 +熈 +熊 +熙 +熟 +熨 +熱 +熹 +熾 +燃 +燈 +燎 +燔 +燕 +燗 +燥 +燭 +燻 +爆 +爐 +爪 +爬 +爲 +爵 +父 +爺 +爼 +爽 +爾 +片 +版 +牌 +牒 +牘 +牙 +牛 +牝 +牟 +牡 +牢 +牧 +物 +牲 +特 +牽 +犂 +犠 +犬 +犯 +状 +狂 +狄 +狐 +狗 +狙 +狛 +狡 +狩 +独 +狭 +狷 +狸 +狼 +猊 +猛 +猟 +猥 +猨 +猩 +猪 +猫 +献 +猴 +猶 +猷 +猾 +猿 +獄 +獅 +獏 +獣 +獲 +玄 +玅 +率 +玉 +王 +玖 +玩 +玲 +珀 +珂 +珈 +珉 +珊 +珍 +珎 +珞 +珠 +珣 +珥 +珪 +班 +現 +球 +理 +琉 +琢 +琥 +琦 +琮 +琲 +琳 +琴 +琵 +琶 +瑁 +瑋 +瑙 +瑚 +瑛 +瑜 +瑞 +瑠 +瑤 +瑩 +瑪 +瑳 +瑾 +璃 +璋 +璜 +璞 +璧 +璨 +環 +璵 +璽 +璿 +瓊 +瓔 +瓜 +瓢 +瓦 +瓶 +甍 +甑 +甕 +甘 +甚 +甞 +生 +産 +甥 +用 +甫 +田 +由 +甲 +申 +男 +町 +画 +界 +畏 +畑 +畔 +留 +畜 +畝 +畠 +畢 +略 +番 +異 +畳 +當 +畷 +畸 +畺 +畿 +疆 +疇 +疋 +疎 +疏 +疑 +疫 +疱 +疲 +疹 +疼 +疾 +病 +症 +痒 +痔 +痕 +痘 +痙 +痛 +痢 +痩 +痴 +痺 +瘍 +瘡 +瘧 +療 +癇 +癌 +癒 +癖 +癡 +癪 +発 +登 +白 +百 +的 +皆 +皇 +皋 +皐 +皓 +皮 +皺 +皿 +盂 +盃 +盆 +盈 +益 +盒 +盗 +盛 +盞 +盟 +盡 +監 +盤 +盥 +盧 +目 +盲 +直 +相 +盾 +省 +眉 +看 +県 +眞 +真 +眠 +眷 +眺 +眼 +着 +睡 +督 +睦 +睨 +睿 +瞋 +瞑 +瞞 +瞬 +瞭 +瞰 +瞳 +瞻 +瞼 +瞿 +矍 +矛 +矜 +矢 +知 +矧 +矩 +短 +矮 +矯 +石 +砂 +砌 +研 +砕 +砥 +砦 +砧 +砲 +破 +砺 +硝 +硫 +硬 +硯 +碁 +碇 +碌 +碑 +碓 +碕 +碗 +碣 +碧 +碩 +確 +碾 +磁 +磐 +磔 +磧 +磨 +磬 +磯 +礁 +礎 +礒 +礙 +礫 +礬 +示 +礼 +社 +祀 +祁 +祇 +祈 +祉 +祐 +祓 +祕 +祖 +祗 +祚 +祝 +神 +祟 +祠 +祢 +祥 +票 +祭 +祷 +祺 +禁 +禄 +禅 +禊 +禍 +禎 +福 +禔 +禖 +禛 +禦 +禧 +禮 +禰 +禹 +禽 +禿 +秀 +私 +秋 +科 +秒 +秘 +租 +秤 +秦 +秩 +称 +移 +稀 +程 +税 +稔 +稗 +稙 +稚 +稜 +稠 +種 +稱 +稲 +稷 +稻 +稼 +稽 +稿 +穀 +穂 +穆 +積 +穎 +穏 +穗 +穜 +穢 +穣 +穫 +穴 +究 +空 +突 +窃 +窄 +窒 +窓 +窟 +窠 +窩 +窪 +窮 +窯 +竃 +竄 +竈 +立 +站 +竜 +竝 +竟 +章 +童 +竪 +竭 +端 +竴 +競 +竹 +竺 +竽 +竿 +笄 +笈 +笏 +笑 +笙 +笛 +笞 +笠 +笥 +符 +第 +笹 +筅 +筆 +筇 +筈 +等 +筋 +筌 +筍 +筏 +筐 +筑 +筒 +答 +策 +筝 +筥 +筧 +筬 +筮 +筯 +筰 +筵 +箆 +箇 +箋 +箏 +箒 +箔 +箕 +算 +箙 +箜 +管 +箪 +箭 +箱 +箸 +節 +篁 +範 +篆 +篇 +築 +篋 +篌 +篝 +篠 +篤 +篥 +篦 +篩 +篭 +篳 +篷 +簀 +簒 +簡 +簧 +簪 +簫 +簺 +簾 +簿 +籀 +籃 +籌 +籍 +籐 +籟 +籠 +籤 +籬 +米 +籾 +粂 +粉 +粋 +粒 +粕 +粗 +粘 +粛 +粟 +粥 +粧 +粮 +粳 +精 +糊 +糖 +糜 +糞 +糟 +糠 +糧 +糯 +糸 +糺 +系 +糾 +紀 +約 +紅 +紋 +納 +紐 +純 +紗 +紘 +紙 +級 +紛 +素 +紡 +索 +紫 +紬 +累 +細 +紳 +紵 +紹 +紺 +絁 +終 +絃 +組 +絅 +経 +結 +絖 +絞 +絡 +絣 +給 +統 +絲 +絵 +絶 +絹 +絽 +綏 +經 +継 +続 +綜 +綟 +綬 +維 +綱 +網 +綴 +綸 +綺 +綽 +綾 +綿 +緊 +緋 +総 +緑 +緒 +線 +締 +緥 +編 +緩 +緬 +緯 +練 +緻 +縁 +縄 +縅 +縒 +縛 +縞 +縢 +縣 +縦 +縫 +縮 +縹 +總 +績 +繁 +繊 +繋 +繍 +織 +繕 +繝 +繦 +繧 +繰 +繹 +繼 +纂 +纈 +纏 +纐 +纒 +纛 +缶 +罔 +罠 +罧 +罪 +置 +罰 +署 +罵 +罷 +罹 +羂 +羅 +羆 +羇 +羈 +羊 +羌 +美 +群 +羨 +義 +羯 +羲 +羹 +羽 +翁 +翅 +翌 +習 +翔 +翛 +翠 +翡 +翫 +翰 +翺 +翻 +翼 +耀 +老 +考 +者 +耆 +而 +耐 +耕 +耗 +耨 +耳 +耶 +耽 +聊 +聖 +聘 +聚 +聞 +聟 +聡 +聨 +聯 +聰 +聲 +聴 +職 +聾 +肄 +肆 +肇 +肉 +肋 +肌 +肖 +肘 +肛 +肝 +股 +肢 +肥 +肩 +肪 +肯 +肱 +育 +肴 +肺 +胃 +胆 +背 +胎 +胖 +胚 +胝 +胞 +胡 +胤 +胱 +胴 +胸 +能 +脂 +脅 +脆 +脇 +脈 +脊 +脚 +脛 +脩 +脱 +脳 +腋 +腎 +腐 +腑 +腔 +腕 +腫 +腰 +腱 +腸 +腹 +腺 +腿 +膀 +膏 +膚 +膜 +膝 +膠 +膣 +膨 +膩 +膳 +膵 +膾 +膿 +臂 +臆 +臈 +臍 +臓 +臘 +臚 +臣 +臥 +臨 +自 +臭 +至 +致 +臺 +臼 +舂 +舅 +與 +興 +舌 +舍 +舎 +舒 +舖 +舗 +舘 +舜 +舞 +舟 +舩 +航 +般 +舳 +舶 +船 +艇 +艘 +艦 +艮 +良 +色 +艶 +芋 +芒 +芙 +芝 +芥 +芦 +芬 +芭 +芯 +花 +芳 +芸 +芹 +芻 +芽 +芿 +苅 +苑 +苔 +苗 +苛 +苞 +苡 +若 +苦 +苧 +苫 +英 +苴 +苻 +茂 +范 +茄 +茅 +茎 +茗 +茘 +茜 +茨 +茲 +茵 +茶 +茸 +茹 +草 +荊 +荏 +荒 +荘 +荷 +荻 +荼 +莞 +莪 +莫 +莬 +莱 +莵 +莽 +菅 +菊 +菌 +菓 +菖 +菘 +菜 +菟 +菩 +菫 +華 +菱 +菴 +萄 +萊 +萌 +萍 +萎 +萠 +萩 +萬 +萱 +落 +葉 +著 +葛 +葡 +董 +葦 +葩 +葬 +葭 +葱 +葵 +葺 +蒋 +蒐 +蒔 +蒙 +蒟 +蒡 +蒲 +蒸 +蒻 +蒼 +蒿 +蓄 +蓆 +蓉 +蓋 +蓑 +蓬 +蓮 +蓼 +蔀 +蔑 +蔓 +蔚 +蔡 +蔦 +蔬 +蔭 +蔵 +蔽 +蕃 +蕉 +蕊 +蕎 +蕨 +蕩 +蕪 +蕭 +蕾 +薄 +薇 +薊 +薔 +薗 +薙 +薛 +薦 +薨 +薩 +薪 +薫 +薬 +薭 +薮 +藁 +藉 +藍 +藏 +藐 +藝 +藤 +藩 +藪 +藷 +藹 +藺 +藻 +蘂 +蘆 +蘇 +蘊 +蘭 +虎 +虐 +虔 +虚 +虜 +虞 +號 +虫 +虹 +虻 +蚊 +蚕 +蛇 +蛉 +蛍 +蛎 +蛙 +蛛 +蛟 +蛤 +蛭 +蛮 +蛸 +蛹 +蛾 +蜀 +蜂 +蜃 +蜆 +蜊 +蜘 +蜜 +蜷 +蜻 +蝉 +蝋 +蝕 +蝙 +蝠 +蝦 +蝶 +蝿 +螂 +融 +螣 +螺 +蟄 +蟇 +蟠 +蟷 +蟹 +蟻 +蠢 +蠣 +血 +衆 +行 +衍 +衒 +術 +街 +衙 +衛 +衝 +衞 +衡 +衢 +衣 +表 +衫 +衰 +衵 +衷 +衽 +衾 +衿 +袁 +袈 +袋 +袍 +袒 +袖 +袙 +袞 +袢 +被 +袰 +袱 +袴 +袷 +袿 +裁 +裂 +裃 +装 +裏 +裔 +裕 +裘 +裙 +補 +裟 +裡 +裲 +裳 +裴 +裸 +裹 +製 +裾 +褂 +褄 +複 +褌 +褐 +褒 +褥 +褪 +褶 +褻 +襄 +襖 +襞 +襟 +襠 +襦 +襪 +襲 +襴 +襷 +西 +要 +覆 +覇 +覈 +見 +規 +視 +覗 +覚 +覧 +親 +覲 +観 +覺 +觀 +角 +解 +触 +言 +訂 +計 +討 +訓 +託 +記 +訛 +訟 +訢 +訥 +訪 +設 +許 +訳 +訴 +訶 +診 +註 +証 +詐 +詔 +評 +詛 +詞 +詠 +詢 +詣 +試 +詩 +詫 +詮 +詰 +話 +該 +詳 +誄 +誅 +誇 +誉 +誌 +認 +誓 +誕 +誘 +語 +誠 +誡 +誣 +誤 +誥 +誦 +説 +読 +誰 +課 +誼 +誾 +調 +談 +請 +諌 +諍 +諏 +諒 +論 +諚 +諜 +諟 +諡 +諦 +諧 +諫 +諭 +諮 +諱 +諶 +諷 +諸 +諺 +諾 +謀 +謄 +謌 +謎 +謗 +謙 +謚 +講 +謝 +謡 +謫 +謬 +謹 +證 +識 +譚 +譛 +譜 +警 +譬 +譯 +議 +譲 +譴 +護 +讀 +讃 +讐 +讒 +谷 +谿 +豅 +豆 +豊 +豎 +豐 +豚 +象 +豪 +豫 +豹 +貌 +貝 +貞 +負 +財 +貢 +貧 +貨 +販 +貪 +貫 +責 +貯 +貰 +貴 +買 +貸 +費 +貼 +貿 +賀 +賁 +賂 +賃 +賄 +資 +賈 +賊 +賎 +賑 +賓 +賛 +賜 +賞 +賠 +賢 +賣 +賤 +賦 +質 +賭 +購 +賽 +贄 +贅 +贈 +贋 +贔 +贖 +赤 +赦 +走 +赴 +起 +超 +越 +趙 +趣 +足 +趺 +趾 +跋 +跏 +距 +跡 +跨 +跪 +路 +跳 +践 +踊 +踏 +踐 +踞 +踪 +踵 +蹄 +蹉 +蹊 +蹟 +蹲 +蹴 +躅 +躇 +躊 +躍 +躑 +躙 +躪 +身 +躬 +躯 +躰 +車 +軋 +軌 +軍 +軒 +軟 +転 +軸 +軻 +軽 +軾 +較 +載 +輌 +輔 +輜 +輝 +輦 +輩 +輪 +輯 +輸 +輿 +轄 +轍 +轟 +轢 +辛 +辞 +辟 +辥 +辦 +辨 +辰 +辱 +農 +辺 +辻 +込 +迂 +迅 +迎 +近 +返 +迢 +迦 +迪 +迫 +迭 +述 +迷 +迹 +追 +退 +送 +逃 +逅 +逆 +逍 +透 +逐 +逓 +途 +逕 +逗 +這 +通 +逝 +逞 +速 +造 +逢 +連 +逮 +週 +進 +逸 +逼 +遁 +遂 +遅 +遇 +遊 +運 +遍 +過 +遐 +道 +達 +違 +遙 +遜 +遠 +遡 +遣 +遥 +適 +遭 +遮 +遯 +遵 +遷 +選 +遺 +遼 +避 +邀 +邁 +邂 +邃 +還 +邇 +邉 +邊 +邑 +那 +邦 +邨 +邪 +邯 +邵 +邸 +郁 +郊 +郎 +郡 +郢 +部 +郭 +郴 +郵 +郷 +都 +鄂 +鄙 +鄭 +鄰 +鄲 +酉 +酋 +酌 +配 +酎 +酒 +酔 +酢 +酥 +酪 +酬 +酵 +酷 +酸 +醍 +醐 +醒 +醗 +醜 +醤 +醪 +醵 +醸 +采 +釈 +釉 +釋 +里 +重 +野 +量 +釐 +金 +釘 +釜 +針 +釣 +釧 +釿 +鈍 +鈎 +鈐 +鈔 +鈞 +鈦 +鈴 +鈷 +鈸 +鈿 +鉄 +鉇 +鉉 +鉋 +鉛 +鉢 +鉤 +鉦 +鉱 +鉾 +銀 +銃 +銅 +銈 +銑 +銕 +銘 +銚 +銜 +銭 +鋏 +鋒 +鋤 +鋭 +鋲 +鋳 +鋸 +鋺 +鋼 +錆 +錍 +錐 +錘 +錠 +錣 +錦 +錫 +錬 +錯 +録 +錵 +鍋 +鍍 +鍑 +鍔 +鍛 +鍬 +鍮 +鍵 +鍼 +鍾 +鎌 +鎖 +鎗 +鎚 +鎧 +鎬 +鎮 +鎰 +鎹 +鏃 +鏑 +鏡 +鐃 +鐇 +鐐 +鐔 +鐘 +鐙 +鐚 +鐡 +鐵 +鐸 +鑁 +鑊 +鑑 +鑒 +鑚 +鑠 +鑢 +鑰 +鑵 +鑷 +鑼 +鑽 +鑿 +長 +門 +閃 +閇 +閉 +開 +閏 +閑 +間 +閔 +閘 +関 +閣 +閤 +閥 +閦 +閨 +閬 +閲 +閻 +閼 +閾 +闇 +闍 +闔 +闕 +闘 +關 +闡 +闢 +闥 +阜 +阪 +阮 +阯 +防 +阻 +阿 +陀 +陂 +附 +陌 +降 +限 +陛 +陞 +院 +陣 +除 +陥 +陪 +陬 +陰 +陳 +陵 +陶 +陸 +険 +陽 +隅 +隆 +隈 +隊 +隋 +階 +随 +隔 +際 +障 +隠 +隣 +隧 +隷 +隻 +隼 +雀 +雁 +雄 +雅 +集 +雇 +雉 +雊 +雋 +雌 +雍 +雑 +雖 +雙 +雛 +離 +難 +雨 +雪 +雫 +雰 +雲 +零 +雷 +雹 +電 +需 +震 +霊 +霍 +霖 +霜 +霞 +霧 +霰 +露 +靈 +青 +靖 +静 +靜 +非 +面 +革 +靫 +靭 +靱 +靴 +靺 +鞁 +鞄 +鞆 +鞋 +鞍 +鞏 +鞘 +鞠 +鞨 +鞭 +韋 +韓 +韜 +韮 +音 +韶 +韻 +響 +頁 +頂 +頃 +項 +順 +須 +頌 +預 +頑 +頒 +頓 +領 +頚 +頬 +頭 +頴 +頸 +頻 +頼 +顆 +題 +額 +顎 +顔 +顕 +顗 +願 +顛 +類 +顧 +顯 +風 +飛 +食 +飢 +飩 +飫 +飯 +飲 +飴 +飼 +飽 +飾 +餃 +餅 +餉 +養 +餌 +餐 +餓 +餘 +餝 +餡 +館 +饂 +饅 +饉 +饋 +饌 +饒 +饗 +首 +馗 +香 +馨 +馬 +馳 +馴 +駄 +駅 +駆 +駈 +駐 +駒 +駕 +駝 +駿 +騁 +騎 +騏 +騒 +験 +騙 +騨 +騰 +驕 +驚 +驛 +驢 +骨 +骸 +髄 +體 +高 +髙 +髢 +髪 +髭 +髮 +髷 +髻 +鬘 +鬚 +鬢 +鬨 +鬯 +鬱 +鬼 +魁 +魂 +魄 +魅 +魏 +魔 +魚 +魯 +鮎 +鮑 +鮒 +鮪 +鮫 +鮭 +鮮 +鯉 +鯔 +鯖 +鯛 +鯨 +鯰 +鯱 +鰐 +鰒 +鰭 +鰯 +鰰 +鰹 +鰻 +鱈 +鱒 +鱗 +鱧 +鳥 +鳩 +鳰 +鳳 +鳴 +鳶 +鴈 +鴉 +鴎 +鴛 +鴟 +鴦 +鴨 +鴫 +鴻 +鵄 +鵜 +鵞 +鵡 +鵬 +鵲 +鵺 +鶉 +鶏 +鶯 +鶴 +鷄 +鷙 +鷲 +鷹 +鷺 +鸚 +鸞 +鹸 +鹽 +鹿 +麁 +麒 +麓 +麗 +麝 +麞 +麟 +麦 +麩 +麹 +麺 +麻 +麾 +麿 +黄 +黌 +黍 +黒 +黙 +黛 +黠 +鼈 +鼉 +鼎 +鼓 +鼠 +鼻 +齊 +齋 +齟 +齢 +齬 +龍 +龕 +龗 +! +# +% +& +( +) ++ +, +- +. +/ +0 +1 +2 +3 +4 +5 +6 +7 +8 +9 +: +; += +? +@ +A +B +C +D +E +F +G +H +I +J +K +L +M +N +O +P +R +S +T +U +V +W +X +Z +a +c +d +e +f +h +i +j +k +l +m +n +o +p +r +s +t +u +y +z +~ +・ + diff --git a/modules/onnx_ocr_module/src/ppocr/utils/dict/ka_dict.txt b/modules/onnx_ocr_module/src/ppocr/utils/dict/ka_dict.txt new file mode 100644 index 0000000..d506b69 --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/utils/dict/ka_dict.txt @@ -0,0 +1,153 @@ +k +a +_ +i +m +g +/ +1 +2 +I +L +S +V +R +C +0 +v +l +6 +4 +8 +. +j +p +ಗ +ು +ಣ +ಪ +ಡ +ಿ +ಸ +ಲ +ಾ +ದ +್ +7 +5 +3 +ವ +ಷ +ಬ +ಹ +ೆ +9 +ಅ +ಳ +ನ +ರ +ಉ +ಕ +ಎ +ೇ +ಂ +ೈ +ೊ +ೀ +ಯ +ೋ +ತ +ಶ +ಭ +ಧ +ಚ +ಜ +ೂ +ಮ +ಒ +ೃ +ಥ +ಇ +ಟ +ಖ +ಆ +ಞ +ಫ +- +ಢ +ಊ +ಓ +ಐ +ಃ +ಘ +ಝ +ೌ +ಠ +ಛ +ಔ +ಏ +ಈ +ಋ +೨ +೦ +೧ +೮ +೯ +೪ +, +೫ +೭ +೩ +೬ +ಙ +s +c +e +n +w +o +u +t +d +E +A +T +B +Z +N +G +O +q +z +r +x +P +K +M +J +U +D +f +F +h +b +W +Y +y +H +X +Q +' +# +& +! +@ +$ +: +% +é +É +( +? ++ + diff --git a/modules/onnx_ocr_module/src/ppocr/utils/dict/kazakh_dict.txt b/modules/onnx_ocr_module/src/ppocr/utils/dict/kazakh_dict.txt new file mode 100644 index 0000000..007d4f5 --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/utils/dict/kazakh_dict.txt @@ -0,0 +1,42 @@ +А +Ә +Б +В +Г +Ғ +Д +Е +Ё +Ж +З +И +Й +К +Қ +Л +М +Н +Ң +О +Ө +П +Р +С +Т +У +Ұ +Ү +Ф +Х +Һ +Ц +Ч +Ш +Щ +Ъ +Ы +І +Ь +Э +Ю +Я diff --git a/modules/onnx_ocr_module/src/ppocr/utils/dict/kie_dict/xfund_class_list.txt b/modules/onnx_ocr_module/src/ppocr/utils/dict/kie_dict/xfund_class_list.txt new file mode 100644 index 0000000..faded9f --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/utils/dict/kie_dict/xfund_class_list.txt @@ -0,0 +1,4 @@ +OTHER +QUESTION +ANSWER +HEADER diff --git a/modules/onnx_ocr_module/src/ppocr/utils/dict/korean_dict.txt b/modules/onnx_ocr_module/src/ppocr/utils/dict/korean_dict.txt new file mode 100644 index 0000000..a13899f --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/utils/dict/korean_dict.txt @@ -0,0 +1,3688 @@ +! +" +# +$ +% +& +' +* ++ +- +/ +0 +1 +2 +3 +4 +5 +6 +7 +8 +9 +: +; +< += +> +? +A +B +C +D +E +F +G +H +I +J +K +L +M +N +O +P +Q +R +S +T +U +V +W +X +Y +Z +[ +\ +] +^ +_ +` +a +b +c +d +e +f +g +h +i +j +k +l +m +n +o +p +q +r +s +t +u +v +w +x +y +z +{ +| +} +~ +© +° +² +½ +Á +Ä +Å +Ç +É +Í +Î +Ó +Ö +× +Ü +ß +à +á +â +ã +ä +å +æ +ç +è +é +ê +ë +ì +í +î +ï +ð +ñ +ò +ó +ô +õ +ö +ø +ú +û +ü +ý +ā +ă +ą +ć +Č +č +đ +ē +ė +ę +ě +ğ +ī +İ +ı +Ł +ł +ń +ň +ō +ř +Ş +ş +Š +š +ţ +ū +ź +ż +Ž +ž +Ș +ș +Α +Δ +α +λ +φ +Г +О +а +в +л +о +р +с +т +я +​ +’ +“ +” +→ +∇ +∼ +「 +」 +ア +カ +グ +ニ +ラ +ン +ㄱ +ㄴ +ㄷ +ㄸ +ㄹ +ㅂ +ㅅ +ㅆ +ㅇ +ㅈ +ㅊ +ㅋ +ㅌ +ㅎ +ㅓ +ㅜ +ㅣ +一 +丁 +七 +三 +上 +下 +不 +丑 +世 +丘 +丞 +中 +丸 +丹 +主 +乃 +久 +之 +乎 +乘 +九 +也 +乳 +乾 +事 +二 +云 +互 +五 +井 +亞 +亡 +交 +亥 +亨 +享 +京 +亭 +人 +仁 +今 +他 +仙 +代 +令 +以 +仰 +仲 +件 +任 +企 +伊 +伍 +伎 +伏 +伐 +休 +伯 +伴 +伸 +佃 +佈 +位 +低 +住 +佐 +何 +佛 +作 +使 +來 +供 +依 +侯 +侵 +侶 +便 +俗 +保 +俠 +信 +修 +俱 +俳 +倉 +個 +倍 +倒 +候 +借 +値 +倫 +倭 +假 +偈 +偉 +偏 +停 +偶 +傅 +傑 +傳 +傷 +傾 +像 +僞 +僥 +僧 +價 +儀 +儉 +儒 +優 +儼 +兀 +允 +元 +兆 +先 +光 +克 +兒 +入 +內 +全 +八 +公 +六 +共 +兵 +其 +具 +典 +兼 +再 +冠 +冥 +冶 +准 +凞 +凡 +凱 +出 +函 +刀 +分 +刊 +刑 +列 +初 +判 +別 +利 +到 +制 +券 +刺 +刻 +則 +前 +剛 +副 +創 +劃 +劑 +力 +功 +加 +劣 +助 +劫 +勇 +動 +務 +勝 +勢 +勳 +勸 +匈 +化 +北 +匠 +區 +十 +千 +午 +半 +卍 +卑 +卒 +卓 +南 +博 +卜 +占 +卦 +印 +危 +卵 +卷 +卽 +卿 +厄 +原 +厦 +去 +參 +又 +叉 +友 +反 +叔 +受 +口 +古 +句 +可 +台 +史 +右 +司 +各 +合 +吉 +同 +名 +后 +吏 +吐 +君 +吠 +吳 +呂 +告 +周 +味 +呵 +命 +和 +咳 +咸 +咽 +哀 +品 +哨 +哮 +哲 +唐 +唯 +唱 +商 +問 +啼 +善 +喆 +喉 +喜 +喩 +喪 +嘗 +器 +嚴 +囊 +四 +回 +因 +困 +固 +圈 +國 +圍 +園 +圓 +圖 +團 +土 +在 +地 +均 +坊 +坐 +坑 +坵 +型 +垢 +城 +域 +埴 +執 +培 +基 +堂 +堅 +堆 +堤 +堯 +報 +場 +塔 +塚 +塞 +塵 +境 +墜 +墟 +墨 +墳 +墾 +壁 +壇 +壓 +壤 +士 +壬 +壯 +壺 +壽 +夏 +夕 +外 +多 +夜 +夢 +大 +天 +太 +夫 +央 +失 +夷 +奄 +奇 +奉 +奎 +奏 +契 +奔 +奮 +女 +奴 +好 +如 +妄 +妊 +妖 +妙 +始 +姑 +姓 +姚 +姜 +威 +婆 +婚 +婦 +媒 +媚 +子 +孔 +字 +存 +孝 +孟 +季 +孤 +孫 +學 +孺 +宇 +守 +安 +宋 +宗 +官 +宙 +定 +客 +宣 +室 +宮 +害 +家 +容 +寂 +寃 +寄 +寅 +密 +寇 +富 +寒 +寓 +實 +審 +寫 +寬 +寶 +寸 +寺 +封 +將 +專 +尊 +對 +小 +少 +尙 +尹 +尼 +尿 +局 +居 +屈 +屋 +屍 +屎 +屛 +層 +屬 +山 +岐 +岡 +岩 +岳 +岸 +峙 +峰 +島 +峻 +峽 +崇 +崔 +崖 +崩 +嶋 +巖 +川 +州 +巢 +工 +左 +巧 +巨 +巫 +差 +己 +巷 +市 +布 +帝 +師 +帶 +常 +帽 +幕 +干 +平 +年 +幹 +幻 +幼 +幽 +庇 +序 +店 +府 +度 +座 +庫 +庭 +康 +廟 +廣 +廳 +延 +廷 +建 +廻 +弁 +式 +弑 +弓 +引 +弘 +弟 +弱 +張 +强 +弼 +彌 +彛 +形 +彬 +影 +役 +彼 +彿 +往 +征 +待 +律 +後 +徐 +徑 +得 +從 +循 +微 +德 +徹 +心 +必 +忌 +忍 +志 +忠 +思 +怡 +急 +性 +恐 +恒 +恨 +恩 +悅 +悖 +患 +悲 +情 +惑 +惟 +惠 +惡 +想 +惺 +愁 +意 +愚 +愛 +感 +愼 +慈 +態 +慕 +慣 +慧 +慾 +憂 +憤 +憺 +應 +懸 +戎 +成 +我 +戟 +戮 +戰 +戴 +戶 +房 +所 +手 +才 +打 +批 +承 +技 +抄 +把 +抗 +抱 +抽 +拇 +拓 +拘 +拙 +拜 +拾 +持 +指 +捌 +捨 +捿 +授 +掌 +排 +接 +推 +提 +揚 +揭 +援 +損 +搗 +摩 +播 +操 +擒 +擔 +擘 +據 +擧 +攘 +攝 +攬 +支 +改 +攻 +放 +政 +故 +敍 +敎 +救 +敗 +散 +敬 +整 +數 +文 +斗 +料 +斛 +斜 +斧 +斯 +新 +斷 +方 +於 +施 +旋 +族 +旗 +日 +旨 +早 +旱 +昌 +明 +易 +昔 +星 +春 +昧 +昭 +是 +時 +晉 +晋 +晩 +普 +景 +晴 +晶 +智 +暈 +暑 +暗 +暘 +曉 +曜 +曠 +曦 +曰 +曲 +書 +曹 +曼 +曾 +最 +會 +月 +有 +朋 +服 +望 +朝 +期 +木 +未 +末 +本 +朱 +朴 +李 +材 +村 +杖 +杜 +杞 +杭 +杯 +東 +松 +板 +林 +果 +枝 +枯 +枰 +枾 +柏 +柑 +柱 +栗 +校 +栢 +核 +根 +格 +桀 +桂 +案 +桎 +桑 +桓 +桔 +梁 +梏 +梓 +梗 +條 +梨 +梵 +棗 +棟 +森 +植 +椒 +楊 +楓 +楚 +業 +楮 +極 +榮 +槃 +槍 +樂 +樓 +樗 +樣 +樸 +樹 +樺 +樽 +橄 +橋 +橘 +機 +橡 +檀 +檎 +權 +欌 +欖 +次 +欲 +歌 +歐 +止 +正 +此 +步 +武 +歲 +歸 +死 +殖 +段 +殷 +殺 +殿 +毅 +母 +毒 +比 +毛 +氏 +民 +氣 +水 +永 +求 +汎 +汗 +江 +池 +沅 +沒 +沖 +沙 +沛 +河 +油 +治 +沼 +沿 +泉 +泊 +法 +泗 +泡 +波 +注 +泰 +洋 +洙 +洛 +洞 +津 +洲 +活 +派 +流 +浅 +浦 +浮 +浴 +海 +涅 +涇 +消 +涌 +液 +淑 +淡 +淨 +淫 +深 +淳 +淵 +淸 +渠 +渡 +游 +渾 +湖 +湯 +源 +溪 +溫 +溶 +滄 +滅 +滋 +滯 +滿 +漁 +漆 +漢 +漫 +漸 +潑 +潤 +潭 +澄 +澎 +澤 +澳 +澹 +濁 +濕 +濟 +濤 +濯 +瀋 +瀝 +灣 +火 +灰 +灸 +災 +炎 +炭 +点 +烈 +烏 +烙 +焚 +無 +焦 +然 +煌 +煎 +照 +煬 +煮 +熟 +熱 +燁 +燈 +燔 +燕 +燥 +燧 +燮 +爲 +爵 +父 +片 +版 +牌 +牛 +牝 +牟 +牡 +物 +特 +犧 +犬 +狀 +狗 +猥 +猩 +猪 +獨 +獵 +獸 +獻 +玄 +玉 +王 +玲 +珍 +珠 +珪 +班 +現 +球 +理 +琴 +瑞 +瑟 +瑪 +璃 +璋 +璽 +瓜 +瓦 +甑 +甘 +生 +産 +用 +甫 +田 +由 +甲 +申 +男 +界 +畏 +留 +畜 +畢 +略 +番 +異 +畵 +當 +畸 +疏 +疑 +疫 +疹 +疼 +病 +症 +痔 +痛 +痺 +瘀 +瘍 +瘡 +療 +癌 +癖 +登 +發 +白 +百 +的 +皆 +皇 +皮 +盂 +盆 +益 +盛 +盜 +盟 +盡 +盤 +盧 +目 +直 +相 +省 +看 +眞 +眼 +睡 +督 +瞋 +矢 +矣 +知 +短 +石 +破 +碍 +碑 +磁 +磨 +磬 +示 +社 +祇 +祖 +祝 +神 +祥 +祭 +祺 +禁 +禅 +禍 +福 +禦 +禪 +禮 +禹 +禽 +禾 +秀 +私 +秉 +秋 +科 +秘 +秤 +秦 +秩 +移 +稀 +稗 +種 +稱 +稷 +稼 +稽 +穀 +穆 +積 +空 +窮 +竅 +立 +章 +童 +竭 +端 +竹 +笑 +符 +第 +筆 +等 +筍 +答 +策 +箋 +箕 +管 +箱 +節 +篇 +簡 +米 +粉 +粘 +粥 +精 +糖 +糞 +系 +紀 +紂 +約 +紅 +紋 +純 +紙 +級 +素 +索 +紫 +紬 +累 +細 +紳 +終 +組 +結 +絡 +統 +絲 +絶 +絹 +經 +綠 +維 +綱 +網 +綸 +綽 +緖 +線 +緣 +緯 +縣 +縱 +總 +織 +繡 +繩 +繪 +繭 +纂 +續 +罕 +置 +罰 +羅 +羊 +美 +群 +義 +羽 +翁 +習 +翟 +老 +考 +者 +而 +耐 +耕 +耳 +聃 +聖 +聞 +聰 +聲 +職 +肇 +肉 +肖 +肝 +股 +肥 +育 +肺 +胃 +胎 +胚 +胞 +胡 +胥 +能 +脂 +脈 +脚 +脛 +脣 +脩 +脫 +脯 +脾 +腋 +腎 +腫 +腸 +腹 +膜 +膠 +膨 +膽 +臆 +臟 +臣 +臥 +臨 +自 +至 +致 +臺 +臼 +臾 +與 +興 +舊 +舌 +舍 +舒 +舜 +舟 +般 +船 +艦 +良 +色 +芋 +花 +芳 +芽 +苑 +苔 +苕 +苛 +苞 +若 +苦 +英 +茂 +茵 +茶 +茹 +荀 +荇 +草 +荒 +荷 +莊 +莫 +菊 +菌 +菜 +菩 +菫 +華 +菴 +菽 +萊 +萍 +萬 +落 +葉 +著 +葛 +董 +葬 +蒙 +蒜 +蒲 +蒸 +蒿 +蓮 +蔓 +蔘 +蔡 +蔬 +蕃 +蕉 +蕓 +薄 +薑 +薛 +薩 +薪 +薺 +藏 +藝 +藤 +藥 +藩 +藻 +蘆 +蘇 +蘊 +蘚 +蘭 +虎 +處 +虛 +虞 +虹 +蜀 +蜂 +蜜 +蝕 +蝶 +融 +蟬 +蟲 +蠶 +蠻 +血 +衆 +行 +術 +衛 +衡 +衣 +表 +袁 +裔 +裕 +裙 +補 +製 +複 +襄 +西 +要 +見 +視 +親 +覺 +觀 +角 +解 +言 +訂 +訊 +訓 +託 +記 +訣 +設 +診 +註 +評 +詩 +話 +詵 +誅 +誌 +認 +誕 +語 +誠 +誤 +誥 +誦 +說 +調 +談 +諍 +論 +諡 +諫 +諭 +諸 +謙 +講 +謝 +謠 +證 +識 +譚 +譜 +譯 +議 +護 +讀 +變 +谷 +豆 +豊 +豚 +象 +豪 +豫 +貝 +貞 +財 +貧 +貨 +貪 +貫 +貴 +貸 +費 +資 +賊 +賓 +賞 +賢 +賣 +賦 +質 +贍 +赤 +赫 +走 +起 +超 +越 +趙 +趣 +趨 +足 +趾 +跋 +跡 +路 +踏 +蹟 +身 +躬 +車 +軍 +軒 +軟 +載 +輓 +輕 +輪 +輯 +輸 +輻 +輿 +轅 +轉 +辨 +辭 +辯 +辰 +農 +近 +迦 +述 +追 +逆 +透 +逐 +通 +逝 +造 +逢 +連 +進 +逵 +遂 +遊 +運 +遍 +過 +道 +達 +遠 +遡 +適 +遷 +選 +遺 +遽 +還 +邊 +邑 +那 +邪 +郞 +郡 +部 +都 +鄒 +鄕 +鄭 +鄲 +配 +酒 +酸 +醉 +醫 +醯 +釋 +里 +重 +野 +量 +釐 +金 +針 +鈍 +鈴 +鉞 +銀 +銅 +銘 +鋼 +錄 +錢 +錦 +鎭 +鏡 +鐘 +鐵 +鑑 +鑛 +長 +門 +閃 +開 +間 +閔 +閣 +閥 +閭 +閻 +闕 +關 +阪 +防 +阿 +陀 +降 +限 +陝 +院 +陰 +陳 +陵 +陶 +陸 +陽 +隆 +隊 +隋 +階 +際 +障 +隣 +隨 +隱 +隷 +雀 +雄 +雅 +集 +雇 +雌 +雖 +雙 +雜 +離 +難 +雨 +雪 +雲 +電 +霜 +露 +靈 +靑 +靖 +靜 +非 +面 +革 +靴 +鞏 +韓 +音 +韶 +韻 +順 +須 +頊 +頌 +領 +頭 +顔 +願 +顚 +類 +顯 +風 +飛 +食 +飢 +飮 +飯 +飾 +養 +餓 +餘 +首 +香 +馨 +馬 +駒 +騫 +騷 +驕 +骨 +骸 +髓 +體 +高 +髥 +髮 +鬪 +鬱 +鬼 +魏 +魔 +魚 +魯 +鮮 +鰍 +鰐 +鳥 +鳧 +鳳 +鴨 +鵲 +鶴 +鷄 +鷹 +鹽 +鹿 +麗 +麥 +麻 +黃 +黑 +默 +點 +黨 +鼎 +齊 +齋 +齒 +龍 +龜 +가 +각 +간 +갇 +갈 +갉 +감 +갑 +값 +갓 +갔 +강 +갖 +갗 +같 +갚 +갛 +개 +객 +갠 +갤 +갬 +갭 +갯 +갰 +갱 +갸 +걀 +걔 +걘 +거 +걱 +건 +걷 +걸 +검 +겁 +것 +겄 +겅 +겆 +겉 +겊 +겋 +게 +겐 +겔 +겟 +겠 +겡 +겨 +격 +겪 +견 +결 +겸 +겹 +겻 +겼 +경 +곁 +계 +곕 +곗 +고 +곡 +곤 +곧 +골 +곪 +곬 +곯 +곰 +곱 +곳 +공 +곶 +과 +곽 +관 +괄 +괌 +광 +괘 +괜 +괭 +괴 +괸 +굉 +교 +구 +국 +군 +굳 +굴 +굵 +굶 +굼 +굽 +굿 +궁 +궂 +궈 +권 +궐 +궜 +궝 +궤 +귀 +귄 +귈 +귓 +규 +균 +귤 +그 +극 +근 +글 +긁 +금 +급 +긋 +긍 +기 +긴 +길 +김 +깁 +깃 +깅 +깊 +까 +깍 +깎 +깐 +깔 +깜 +깝 +깟 +깡 +깥 +깨 +깬 +깰 +깻 +깼 +깽 +꺄 +꺼 +꺽 +꺾 +껀 +껄 +껌 +껍 +껏 +껐 +껑 +께 +껴 +꼈 +꼍 +꼐 +꼬 +꼭 +꼴 +꼼 +꼽 +꼿 +꽁 +꽂 +꽃 +꽉 +꽝 +꽤 +꽥 +꾀 +꾜 +꾸 +꾹 +꾼 +꿀 +꿇 +꿈 +꿉 +꿋 +꿍 +꿎 +꿔 +꿨 +꿩 +꿰 +꿴 +뀄 +뀌 +뀐 +뀔 +뀜 +뀝 +끄 +끈 +끊 +끌 +끓 +끔 +끕 +끗 +끙 +끝 +끼 +끽 +낀 +낄 +낌 +낍 +낏 +낑 +나 +낙 +낚 +난 +낟 +날 +낡 +남 +납 +낫 +났 +낭 +낮 +낯 +낱 +낳 +내 +낵 +낸 +낼 +냄 +냅 +냇 +냈 +냉 +냐 +냔 +냘 +냥 +너 +넉 +넋 +넌 +널 +넓 +넘 +넙 +넛 +넜 +넝 +넣 +네 +넥 +넨 +넬 +넴 +넵 +넷 +넸 +넹 +녀 +녁 +년 +념 +녔 +녕 +녘 +녜 +노 +녹 +논 +놀 +놈 +놋 +농 +높 +놓 +놔 +놨 +뇌 +뇨 +뇩 +뇽 +누 +눅 +눈 +눌 +눔 +눕 +눗 +눠 +눴 +뉘 +뉜 +뉩 +뉴 +늄 +늅 +늉 +느 +늑 +는 +늘 +늙 +늠 +늡 +능 +늦 +늪 +늬 +니 +닉 +닌 +닐 +님 +닙 +닛 +닝 +닢 +다 +닥 +닦 +단 +닫 +달 +닭 +닮 +닯 +닳 +담 +답 +닷 +당 +닻 +닿 +대 +댁 +댄 +댈 +댐 +댑 +댓 +댔 +댕 +댜 +더 +덕 +덖 +던 +덜 +덟 +덤 +덥 +덧 +덩 +덫 +덮 +데 +덱 +덴 +델 +뎀 +뎃 +뎅 +뎌 +뎠 +뎨 +도 +독 +돈 +돋 +돌 +돔 +돕 +돗 +동 +돛 +돝 +돼 +됐 +되 +된 +될 +됨 +됩 +됴 +두 +둑 +둔 +둘 +둠 +둡 +둣 +둥 +둬 +뒀 +뒤 +뒬 +뒷 +뒹 +듀 +듈 +듐 +드 +득 +든 +듣 +들 +듦 +듬 +듭 +듯 +등 +듸 +디 +딕 +딘 +딛 +딜 +딤 +딥 +딧 +딨 +딩 +딪 +따 +딱 +딴 +딸 +땀 +땄 +땅 +때 +땐 +땔 +땜 +땝 +땠 +땡 +떠 +떡 +떤 +떨 +떫 +떰 +떱 +떳 +떴 +떵 +떻 +떼 +떽 +뗀 +뗄 +뗍 +뗏 +뗐 +뗑 +또 +똑 +똘 +똥 +뙤 +뚜 +뚝 +뚤 +뚫 +뚱 +뛰 +뛴 +뛸 +뜀 +뜁 +뜨 +뜩 +뜬 +뜯 +뜰 +뜸 +뜻 +띄 +띈 +띌 +띔 +띕 +띠 +띤 +띨 +띱 +띵 +라 +락 +란 +랄 +람 +랍 +랏 +랐 +랑 +랒 +랗 +래 +랙 +랜 +랠 +램 +랩 +랫 +랬 +랭 +랴 +략 +량 +러 +럭 +런 +럴 +럼 +럽 +럿 +렀 +렁 +렇 +레 +렉 +렌 +렐 +렘 +렙 +렛 +렝 +려 +력 +련 +렬 +렴 +렵 +렷 +렸 +령 +례 +로 +록 +론 +롤 +롬 +롭 +롯 +롱 +롸 +롹 +뢰 +뢴 +뢸 +룃 +료 +룐 +룡 +루 +룩 +룬 +룰 +룸 +룹 +룻 +룽 +뤄 +뤘 +뤼 +류 +륙 +륜 +률 +륨 +륭 +르 +륵 +른 +를 +름 +릅 +릇 +릉 +릎 +리 +릭 +린 +릴 +림 +립 +릿 +링 +마 +막 +만 +많 +맏 +말 +맑 +맘 +맙 +맛 +망 +맞 +맡 +맣 +매 +맥 +맨 +맬 +맴 +맵 +맷 +맸 +맹 +맺 +먀 +먁 +머 +먹 +먼 +멀 +멈 +멋 +멍 +멎 +메 +멕 +멘 +멜 +멤 +멥 +멧 +멩 +며 +멱 +면 +멸 +몄 +명 +몇 +모 +목 +몫 +몬 +몰 +몸 +몹 +못 +몽 +뫼 +묘 +무 +묵 +묶 +문 +묻 +물 +묽 +뭄 +뭅 +뭇 +뭉 +뭍 +뭏 +뭐 +뭔 +뭘 +뭡 +뭣 +뮈 +뮌 +뮐 +뮤 +뮬 +므 +믈 +믐 +미 +믹 +민 +믿 +밀 +밈 +밉 +밋 +밌 +밍 +및 +밑 +바 +박 +밖 +반 +받 +발 +밝 +밟 +밤 +밥 +밧 +방 +밭 +배 +백 +밴 +밸 +뱀 +뱁 +뱃 +뱄 +뱅 +뱉 +뱍 +뱐 +버 +벅 +번 +벌 +범 +법 +벗 +벙 +벚 +베 +벡 +벤 +벨 +벰 +벱 +벳 +벵 +벼 +벽 +변 +별 +볍 +볏 +볐 +병 +볕 +보 +복 +볶 +본 +볼 +봄 +봅 +봇 +봉 +봐 +봤 +뵈 +뵐 +뵙 +부 +북 +분 +붇 +불 +붉 +붐 +붓 +붕 +붙 +뷔 +뷰 +뷴 +뷸 +브 +븐 +블 +비 +빅 +빈 +빌 +빔 +빕 +빗 +빙 +빚 +빛 +빠 +빡 +빤 +빨 +빳 +빴 +빵 +빻 +빼 +빽 +뺀 +뺄 +뺌 +뺏 +뺐 +뺑 +뺨 +뻐 +뻑 +뻔 +뻗 +뻘 +뻣 +뻤 +뻥 +뻬 +뼈 +뼉 +뼘 +뽀 +뽈 +뽐 +뽑 +뽕 +뾰 +뿌 +뿍 +뿐 +뿔 +뿜 +쁘 +쁜 +쁠 +쁨 +삐 +삔 +삘 +사 +삭 +삯 +산 +살 +삵 +삶 +삼 +삽 +삿 +샀 +상 +샅 +새 +색 +샌 +샐 +샘 +샙 +샛 +샜 +생 +샤 +샨 +샬 +샴 +샵 +샷 +샹 +서 +석 +섞 +선 +섣 +설 +섬 +섭 +섯 +섰 +성 +섶 +세 +섹 +센 +셀 +셈 +셉 +셋 +셌 +셍 +셔 +션 +셜 +셨 +셰 +셴 +셸 +소 +속 +손 +솔 +솜 +솝 +솟 +송 +솥 +쇄 +쇠 +쇤 +쇳 +쇼 +숀 +숄 +숍 +수 +숙 +순 +숟 +술 +숨 +숩 +숫 +숭 +숯 +숱 +숲 +숴 +쉐 +쉘 +쉬 +쉭 +쉰 +쉴 +쉼 +쉽 +슈 +슐 +슘 +슛 +슝 +스 +슥 +슨 +슬 +슭 +슴 +습 +슷 +승 +시 +식 +신 +싣 +실 +싫 +심 +십 +싯 +싱 +싶 +싸 +싹 +싼 +쌀 +쌈 +쌉 +쌌 +쌍 +쌓 +쌔 +쌘 +쌩 +써 +썩 +썬 +썰 +썸 +썹 +썼 +썽 +쎄 +쎈 +쏘 +쏙 +쏜 +쏟 +쏠 +쏭 +쏴 +쐈 +쐐 +쐬 +쑤 +쑥 +쑨 +쒀 +쒔 +쓰 +쓱 +쓴 +쓸 +씀 +씁 +씌 +씨 +씩 +씬 +씰 +씸 +씹 +씻 +씽 +아 +악 +안 +앉 +않 +알 +앎 +앓 +암 +압 +앗 +았 +앙 +앞 +애 +액 +앤 +앨 +앰 +앱 +앳 +앴 +앵 +야 +약 +얀 +얄 +얇 +얌 +얍 +얏 +양 +얕 +얗 +얘 +얜 +어 +억 +언 +얹 +얻 +얼 +얽 +엄 +업 +없 +엇 +었 +엉 +엊 +엌 +엎 +에 +엑 +엔 +엘 +엠 +엡 +엣 +엥 +여 +역 +엮 +연 +열 +엷 +염 +엽 +엾 +엿 +였 +영 +옅 +옆 +옇 +예 +옌 +옐 +옙 +옛 +오 +옥 +온 +올 +옭 +옮 +옳 +옴 +옵 +옷 +옹 +옻 +와 +왁 +완 +왈 +왑 +왓 +왔 +왕 +왜 +왠 +왱 +외 +왼 +요 +욕 +욘 +욜 +욤 +용 +우 +욱 +운 +울 +움 +웁 +웃 +웅 +워 +웍 +원 +월 +웜 +웠 +웡 +웨 +웬 +웰 +웸 +웹 +위 +윅 +윈 +윌 +윔 +윗 +윙 +유 +육 +윤 +율 +윱 +윳 +융 +으 +윽 +은 +을 +읊 +음 +읍 +응 +의 +읜 +읠 +이 +익 +인 +일 +읽 +잃 +임 +입 +잇 +있 +잉 +잊 +잎 +자 +작 +잔 +잖 +잘 +잠 +잡 +잣 +잤 +장 +잦 +재 +잭 +잰 +잴 +잽 +잿 +쟀 +쟁 +쟈 +쟉 +쟤 +저 +적 +전 +절 +젊 +점 +접 +젓 +정 +젖 +제 +젝 +젠 +젤 +젬 +젭 +젯 +져 +젼 +졀 +졌 +졍 +조 +족 +존 +졸 +좀 +좁 +종 +좇 +좋 +좌 +좍 +좽 +죄 +죠 +죤 +주 +죽 +준 +줄 +줌 +줍 +줏 +중 +줘 +줬 +쥐 +쥔 +쥘 +쥬 +쥴 +즈 +즉 +즌 +즐 +즘 +즙 +증 +지 +직 +진 +짇 +질 +짊 +짐 +집 +짓 +징 +짖 +짙 +짚 +짜 +짝 +짠 +짢 +짤 +짧 +짬 +짭 +짰 +짱 +째 +짹 +짼 +쨀 +쨉 +쨋 +쨌 +쨍 +쩄 +쩌 +쩍 +쩐 +쩔 +쩜 +쩝 +쩡 +쩨 +쪄 +쪘 +쪼 +쪽 +쪾 +쫀 +쫄 +쫑 +쫓 +쫙 +쬐 +쭈 +쭉 +쭐 +쭙 +쯔 +쯤 +쯧 +찌 +찍 +찐 +찔 +찜 +찝 +찡 +찢 +찧 +차 +착 +찬 +찮 +찰 +참 +찹 +찻 +찼 +창 +찾 +채 +책 +챈 +챌 +챔 +챕 +챗 +챘 +챙 +챠 +챤 +처 +척 +천 +철 +첨 +첩 +첫 +청 +체 +첵 +첸 +첼 +쳄 +쳇 +쳉 +쳐 +쳔 +쳤 +초 +촉 +촌 +촘 +촛 +총 +촨 +촬 +최 +쵸 +추 +축 +춘 +출 +춤 +춥 +춧 +충 +춰 +췄 +췌 +취 +췬 +츄 +츠 +측 +츨 +츰 +층 +치 +칙 +친 +칠 +칡 +침 +칩 +칫 +칭 +카 +칵 +칸 +칼 +캄 +캅 +캇 +캉 +캐 +캔 +캘 +캠 +캡 +캣 +캤 +캥 +캬 +커 +컥 +컨 +컫 +컬 +컴 +컵 +컷 +컸 +컹 +케 +켄 +켈 +켐 +켓 +켕 +켜 +켠 +켤 +켭 +켯 +켰 +코 +콕 +콘 +콜 +콤 +콥 +콧 +콩 +콰 +콱 +콴 +콸 +쾅 +쾌 +쾡 +쾨 +쾰 +쿄 +쿠 +쿡 +쿤 +쿨 +쿰 +쿵 +쿼 +퀀 +퀄 +퀘 +퀭 +퀴 +퀵 +퀸 +퀼 +큐 +큘 +크 +큰 +클 +큼 +큽 +키 +킥 +킨 +킬 +킴 +킵 +킷 +킹 +타 +탁 +탄 +탈 +탉 +탐 +탑 +탓 +탔 +탕 +태 +택 +탠 +탤 +탬 +탭 +탯 +탰 +탱 +터 +턱 +턴 +털 +텀 +텁 +텃 +텄 +텅 +테 +텍 +텐 +텔 +템 +텝 +텡 +텨 +톈 +토 +톡 +톤 +톨 +톰 +톱 +톳 +통 +퇴 +툇 +투 +툭 +툰 +툴 +툼 +퉁 +퉈 +퉜 +튀 +튄 +튈 +튕 +튜 +튠 +튤 +튬 +트 +특 +튼 +튿 +틀 +틈 +틉 +틋 +틔 +티 +틱 +틴 +틸 +팀 +팁 +팅 +파 +팍 +팎 +판 +팔 +팜 +팝 +팟 +팠 +팡 +팥 +패 +팩 +팬 +팰 +팸 +팻 +팼 +팽 +퍼 +퍽 +펀 +펄 +펌 +펍 +펐 +펑 +페 +펙 +펜 +펠 +펨 +펩 +펫 +펭 +펴 +편 +펼 +폄 +폈 +평 +폐 +포 +폭 +폰 +폴 +폼 +폿 +퐁 +표 +푭 +푸 +푹 +푼 +풀 +품 +풋 +풍 +퓨 +퓬 +퓰 +퓸 +프 +픈 +플 +픔 +픕 +피 +픽 +핀 +필 +핌 +핍 +핏 +핑 +하 +학 +한 +할 +핥 +함 +합 +핫 +항 +해 +핵 +핸 +핼 +햄 +햅 +햇 +했 +행 +햐 +향 +헀 +허 +헉 +헌 +헐 +험 +헙 +헛 +헝 +헤 +헥 +헨 +헬 +헴 +헵 +헷 +헹 +혀 +혁 +현 +혈 +혐 +협 +혓 +혔 +형 +혜 +호 +혹 +혼 +홀 +홈 +홉 +홋 +홍 +홑 +화 +확 +환 +활 +홧 +황 +홰 +홱 +횃 +회 +획 +횝 +횟 +횡 +효 +후 +훅 +훈 +훌 +훑 +훔 +훗 +훤 +훨 +훼 +휄 +휑 +휘 +휙 +휜 +휠 +휩 +휭 +휴 +휼 +흄 +흉 +흐 +흑 +흔 +흘 +흙 +흠 +흡 +흣 +흥 +흩 +희 +흰 +흽 +히 +힉 +힌 +힐 +힘 +힙 +힝 +車 +滑 +金 +奈 +羅 +洛 +卵 +欄 +蘭 +郎 +來 +盧 +老 +魯 +綠 +鹿 +論 +雷 +樓 +縷 +凌 +樂 +不 +參 +葉 +沈 +若 +兩 +凉 +梁 +呂 +女 +廬 +麗 +黎 +曆 +歷 +戀 +蓮 +連 +列 +烈 +裂 +念 +獵 +靈 +領 +例 +禮 +醴 +惡 +尿 +料 +遼 +龍 +暈 +柳 +流 +類 +六 +陸 +倫 +律 +栗 +利 +李 +梨 +理 +離 +燐 +林 +臨 +立 +茶 +切 +宅 + diff --git a/modules/onnx_ocr_module/src/ppocr/utils/dict/latex_ocr_tokenizer.json b/modules/onnx_ocr_module/src/ppocr/utils/dict/latex_ocr_tokenizer.json new file mode 100644 index 0000000..e8fd4f6 --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/utils/dict/latex_ocr_tokenizer.json @@ -0,0 +1 @@ +{"version":"1.0","truncation":null,"padding":null,"added_tokens":[{"id":0,"special":true,"content":"[PAD]","single_word":false,"lstrip":false,"rstrip":false,"normalized":false},{"id":1,"special":true,"content":"[BOS]","single_word":false,"lstrip":false,"rstrip":false,"normalized":false},{"id":2,"special":true,"content":"[EOS]","single_word":false,"lstrip":false,"rstrip":false,"normalized":false}],"normalizer":null,"pre_tokenizer":{"type":"ByteLevel","add_prefix_space":false,"trim_offsets":true},"post_processor":null,"decoder":null,"model":{"dropout":null,"unk_token":null,"continuing_subword_prefix":null,"end_of_word_suffix":null,"fuse_unk":false,"vocab":{"[PAD]":0,"[BOS]":1,"[EOS]":2,"!":3,"\"":4,"#":5,"$":6,"&":7,"'":8,"(":9,")":10,"*":11,"+":12,",":13,"-":14,".":15,"/":16,"0":17,"1":18,"2":19,"3":20,"4":21,"5":22,"6":23,"7":24,"8":25,"9":26,":":27,";":28,"<":29,"=":30,">":31,"?":32,"@":33,"A":34,"B":35,"C":36,"D":37,"E":38,"F":39,"G":40,"H":41,"I":42,"J":43,"K":44,"L":45,"M":46,"N":47,"O":48,"P":49,"Q":50,"R":51,"S":52,"T":53,"U":54,"V":55,"W":56,"X":57,"Y":58,"Z":59,"[":60,"\\":61,"]":62,"^":63,"_":64,"`":65,"a":66,"b":67,"c":68,"d":69,"e":70,"f":71,"g":72,"h":73,"i":74,"j":75,"k":76,"l":77,"m":78,"n":79,"o":80,"p":81,"q":82,"r":83,"s":84,"t":85,"u":86,"v":87,"w":88,"x":89,"y":90,"z":91,"{":92,"|":93,"}":94,"~":95,"½":96,"¿":97,"ï":98,"Ċ":99,"č":100,"Ġ":101,"Ġ}":102,"Ġ{":103,"Ġ\\":104,"Ġ_":105,"Ġ^":106,"Ġ2":107,"Ġ)":108,"Ġ(":109,"Ġ1":110,"ra":111,"Ġ=":112,"Ġ-":113,"čĊ":114,"Ġ,":115,"fra":116,"frac":117,"Ġ+":118,"ma":119,"ta":120,"ig":121,"Ġ0":122,"ar":123,"al":124,"le":125,"Ġi":126,"th":127,"Ġx":128,"ft":129,"igh":130,"me":131,"righ":132,"math":133,"Ġn":134,"Ġ.":135,"Ġ\\,":136,"in":137,"ph":138,"Ġd":139,"left":140,"Ġa":141,"right":142,"am":143,"eta":144,"ti":145,"Ġm":146,"mu":147,"Ġ3":148,"Ġk":149,"Ġt":150,"Ġe":151,"Ġr":152,"Ġ&":153,"Ġc":154,"Ġp":155,"si":156,"rm":157,"de":158,"mathrm":159,"Ġ4":160,"Ġs":161,"pr":162,"Ġ~":163,"pha":164,"Ġl":165,"alpha":166,"da":167,"ĠA":168,"Ġ\\;":169,"ot":170,"pi":171,"par":172,"tial":173,"partial":174,"ime":175,"prime":176,"psi":177,"dot":178,"Ġj":179,"Ġb":180,"Ġf":181,"lta":182,"Ġ|":183,"amma":184,"bda":185,"ambda":186,"phi":187,"Ġq":188,"bf":189,"Ġg":190,"nu":191,"Ġz":192,"ray":193,"array":194,"ĠM":195,"ĠT":196,"Ġy":197,"cal":198,"bar":199,"ĠN":200,"igma":201,"ĠR":202,"rt":203,"lambda":204,"int":205,"ĠB":206,"ve":207,"ng":208,"qu":209,"ĠL":210,"Ġ/":211,"lo":212,"beta":213,"ngle":214,"Ġu":215,"delta":216,"sq":217,"sqrt":218,"theta":219,"Ġ\\\\":220,"gamma":221,"Ġ]":222,"sigma":223,"ga":224,"mega":225,"ĠD":226,"ĠF":227,"Ġ[":228,"ĠS":229,"mathbf":230,"su":231,"ĠP":232,"lon":233,"Ġv":234,"sum":235,"psilon":236,"ĠV":237,"ĠC":238,"cdot":239,"epsilon":240,"at":241,"hat":242,"ad":243,"quad":244,"Ġh":245,"ho":246,"rho":247,"hi":248,"to":249,"ĠE":250,"la":251,"ĠH":252,"lde":253,"tilde":254,"ĠQ":255,"Ġ5":256,"var":257,"ĠX":258,"ĠG":259,"be":260,"nd":261,"omega":262,"end":263,"gin":264,"begin":265,"tau":266,"Ġ6":267,"na":268,"vec":269,"ĠI":270,"Ġo":271,"rangle":272,"Ġ*":273,"De":274,"Delta":275,"Gamma":276,"pe":277,"fty":278,"infty":279,"ĠK":280,"xi":281,"Ġ8":282,"ow":283,"ĠJ":284,"ĠU":285,"row":286,"tar":287,"ge":288,"Phi":289,"ap":290,"ĠW":291,"co":292,"mes":293,"times":294,"sin":295,"ĠZ":296,"langle":297,"ope":298,"rna":299,"rato":300,"operato":301,"rname":302,"operatorname":303,"tarrow":304,"lin":305,"line":306,"varphi":307,"pm":308,"rline":309,"Lambda":310,"Ġ\\!":311,"Ġ;":312,"dots":313,"cos":314,"Ġw":315,"rightarrow":316,"big":317,"chi":318,"ove":319,"Ġ\\}":320,"overline":321,"Ġ7":322,"ex":323,"pa":324,"st":325,"pro":326,"qquad":327,"iv":328,"equ":329,"equiv":330,"ĠO":331,"ln":332,"Omega":333,"ll":334,"Ġ9":335,"kap":336,"kappa":337,"Big":338,"Ġ\\{":339,"dag":340,"ĠY":341,"\\{":342,"varepsilon":343,"cdots":344,"Ġ:":345,"mathcal":346,"Psi":347,"Ġ>":348,"bo":349,"bol":350,"Ġ<":351,"ger":352,"dagger":353,"ldots":354,"ell":355,"bla":356,"nabla":357,"exp":358,"yle":359,"style":360,"zeta":361,"Sigma":362,"wi":363,"wide":364,"sim":365,"leq":366,"Ġ!":367,"bigg":368,"mathb":369,"mathbb":370,"Ġ\\:":371,"hbar":372,"otimes":373,"bold":374,"\\}":375,"mi":376,"prox":377,"approx":378,"Pi":379,"log":380,"mid":381,"sp":382,"vert":383,"di":384,"prod":385,"per":386,"perp":387,"ystyle":388,"laystyle":389,"splaystyle":390,"displaystyle":391,"meq":392,"simeq":393,"ed":394,"wed":395,"wedge":396,"widetilde":397,"sy":398,"sym":399,"symbol":400,"boldsymbol":401,"ck":402,"tex":403,"text":404,"ri":405,"Th":406,"Theta":407,"geq":408,"se":409,"eq":410,"nde":411,"unde":412,"tan":413,"sc":414,"ast":415,"rc":416,"set":417,"pt":418,"widehat":419,"ci":420,"circ":421,"re":422,"ript":423,"script":424,"underline":425,"Ġ\\|":426,"rel":427,"neq":428,"sta":429,"stack":430,"stackrel":431,"sinh":432,"op":433,"us":434,"cosh":435,"Bigg":436,"ce":437,"textstyle":438,"star":439,"not":440,"frak":441,"mathfrak":442,"mp":443,"biggr":444,"lus":445,"oplus":446,"vartheta":447,"biggl":448,"Bigr":449,"bra":450,"Bigl":451,"fo":452,"sf":453,"sub":454,"subset":455,"ngrightarrow":456,"ec":457,"boldmath":458,"rall":459,"forall":460,"scriptstyle":461,"ect":462,"parrow":463,"uparrow":464,"bj":465,"bject":466,"pto":467,"propto":468,"Ġ'":469,"longrightarrow":470,"bigl":471,"bigr":472,"oint":473,"ps":474,"maps":475,"mapsto":476,"om":477,"lle":478,"\\|":479,"ddot":480,"cu":481,"bin":482,"binom":483,"vdots":484,"angle":485,"leftrightarrow":486,"over":487,"or":488,"mathsf":489,"cup":490,"brace":491,"no":492,"arc":493,"flo":494,"floor":495,"tri":496,"triangle":497,"Xi":498,"cot":499,"bot":500,"cong":501,"it":502,"mbe":503,"numbe":504,"nonumbe":505,"nonumber":506,"cap":507,"Righ":508,"Rightarrow":509,"ze":510,"size":511,"textrm":512,"ne":513,"arctan":514,"ralle":515,"paralle":516,"parallel":517,"cfrac":518,"Ġ--":519,"object":520,"ĠObject":521,"brack":522,"sh":523,"arrow":524,"own":525,"varrho":526,"subseteq":527,"rbrace":528,"textbf":529,"imath":530,"od":531,"down":532,"he":533,"land":534,"scriptscriptstyle":535,"scriptsize":536,"che":537,"check":538,"sla":539,"overrightarrow":540,"downarrow":541,"Biggl":542,"gg":543,"nto":544,"phanto":545,"phantom":546,"exi":547,"hline":548,"sts":549,"exists":550,"Biggr":551,"bu":552,"rfloor":553,"ddots":554,"io":555,"iota":556,"llet":557,"bullet":558,"colon":559,"inus":560,"Upsilon":561,"lfloor":562,"lbrack":563,"underbrace":564,"neg":565,"Im":566,"mathit":567,"tin":568,"tiny":569,"jmath":570,"lef":571,"slash":572,"vee":573,"minus":574,"setminus":575,"Re":576,"iint":577,"leftarrow":578,"Ve":579,"Vert":580,"atop":581,"sup":582,"bigcup":583,"wp":584,"dim":585,"sec":586,"supset":587,"Lo":588,"lor":589,"pmod":590,"mod":591,"bigoplus":592,"il":593,"bmod":594,"coth":595,"Le":596,"ftrightarrow":597,"Leftrightarrow":598,"ngleftrightarrow":599,"sma":600,"upsilon":601,"\\,":602,"csc":603,"eph":604,"aleph":605,"bigwedge":606,"arcsin":607,"small":608,"odot":609,"overset":610,"rbrack":611,"mit":612,"lbrace":613,"li":614,"arp":615,"arge":616,"Ġ\\#":617,"bre":618,"textsf":619,"Longrightarrow":620,"breve":621,"em":622,"yset":623,"varpi":624,"ptyset":625,"emptyset":626,"ff":627,"iff":628,"nt":629,"er":630,"lap":631,"lnot":632,"dash":633,"under":634,"slant":635,"arg":636,"underset":637,"Bo":638,"Box":639,"Ġ\"":640,"spa":641,"space":642,"deg":643,"iiint":644,"oo":645,"otnot":646,"footnot":647,"arpoo":648,"footnote":649,"rlap":650,"es":651,"imp":652,"sb":653,"te":654,"bigtriangle":655,"lies":656,"implies":657,"\\;":658,"ker":659,"footnotesize":660,"tharpoo":661,"up":662,"acu":663,"acute":664,"longleftrightarrow":665,"eil":666,"lce":667,"rceil":668,"lceil":669,"vphantom":670,"en":671,"thin":672,"ack":673,"back":674,"tt":675,"backslash":676,"xrightarrow":677,"vdash":678,"top":679,"rightharpoo":680,"varsigma":681,"Longleftrightarrow":682,"mathop":683,"large":684,"bigcap":685,"leqslant":686,"Ġ`":687,"overbrace":688,"nup":689,"rightharpoonup":690,"bigotimes":691,"triangleq":692,"Large":693,"ru":694,"null":695,"bigtriangleup":696,"varno":697,"thing":698,"varnothing":699,"doteq":700,"Ġ\\_":701,"overleftarrow":702,"hf":703,"bigstar":704,"enspace":705,"\\!":706,"stru":707,"strut":708,"ominus":709,"div":710,"ond":711,"amond":712,"ddagger":713,"Ġcm":714,"ni":715,"sk":716,"diamond":717,"rVert":718,"prot":719,"protect":720,"ip":721,"varDelta":722,"notin":723,"skip":724,"lVert":725,"Ġ\\/":726,"dotsc":727,"ill":728,"ule":729,"\\:":730,"hfill":731,"krightarrow":732,"okrightarrow":733,"hookrightarrow":734,"sharp":735,"Vdash":736,"bigvee":737,"subsetneq":738,"supseteq":739,"Ġ?":740,"ngmapsto":741,"longmapsto":742,"cdotp":743,"geqslant":744,"bigtriangledown":745,"dotsb":746,"lim":747,"fl":748,"triangleleft":749,"flat":750,"sl":751,"box":752,"Ġ---":753,"sqcup":754,"jlim":755,"ls":756,"mo":757,"dels":758,"ref":759,"models":760,"tag":761,"Pr":762,"mal":763,"ou":764,"llap":765,"thinspace":766,"enskip":767,"Vec":768,"ebox":769,"kebox":770,"nor":771,"rd":772,"squ":773,"vline":774,"¿½":775,"�":776,"Ġ�":777,"makebox":778,"surd":779,"normal":780,"are":781,"square":782,"pou":783,"mathrel":784,"varOmega":785,"nds":786,"smallsetminus":787,"pounds":788,"ns":789,"ss":790,"smi":791,"mathor":792,"rightlef":793,"textup":794,"tharpoons":795,"smile":796,"mathord":797,"rightleftharpoons":798,"cc":799,"Ġ\\-":800,"succ":801,"ftarrow":802,"rtimes":803,"det":804,"prec":805,"texttt":806,"oslash":807,"Ġ\\&":808,"arrowvert":809,"lg":810,"Ġmm":811,"inter":812,"ngleftarrow":813,"hfil":814,"intercal":815,"frow":816,"Ġ\\*":817,"frown":818,"mpe":819,"Ġpt":820,"varpro":821,"searrow":822,"bumpe":823,"varprojlim":824,"bumpeq":825,"Down":826,"SS":827,"cd":828,"ere":829,"gcd":830,"ohe":831,"tw":832,"leme":833,"there":834,"injlim":835,"tit":836,"adrightarrow":837,"varinjlim":838,"comp":839,"textit":840,"fore":841,"overleftrightarrow":842,"Downarrow":843,"oheadrightarrow":844,"twoheadrightarrow":845,"lement":846,"therefore":847,"complement":848,"ca":849,"thi":850,"longleftarrow":851,"bigm":852,"triangleright":853,"nearrow":854,"\\#":855,"nce":856,"ral":857,"cance":858,"thick":859,"cancel":860,"Uparrow":861,"nat":862,"ural":863,"mathstrut":864,"suit":865,"bigcirc":866,"smallskip":867,"diamondsuit":868,"normalsize":869,"natural":870,"gt":871,"less":872,"mathtt":873,"bigsqcup":874,"thicksim":875,"lesssim":876,"bow":877,"llde":878,"tie":879,"nullde":880,"miter":881,"limiter":882,"kern":883,"bowtie":884,"nulldelimiter":885,"nulldelimiterspace":886,"Da":887,"hphantom":888,"ro":889,"vDa":890,"barwedge":891,"beth":892,"eqno":893,"vDash":894,"AR":895,"Di":896,"GE":897,"LAR":898,"dskip":899,"ts":900,"Ġ@":901,"medskip":902,"ndown":903,"gets":904,"coprod":905,"dotsm":906,"smash":907,"rightharpoondown":908,"Diamond":909,"LARGE":910,"nrightarrow":911,"nleftrightarrow":912,"rsim":913,"rne":914,"warrow":915,"mathc":916,"corne":917,"textnormal":918,"preceq":919,"gtrsim":920,"roup":921,"corner":922,"Ġ\\[":923,"Ġ\\]":924,"mathope":925,"lefteq":926,"lose":927,"varkappa":928,"Bigm":929,"Biggm":930,"mathclose":931,"mathopen":932,"lefteqn":933,"Bar":934,"Ti":935,"lr":936,"swarrow":937,"uge":938,"vru":939,"xleftarrow":940,"mathnormal":941,"rightrightarrow":942,"rightleftarrow":943,"sqsubseteq":944,"succeq":945,"Tilde":946,"lrcorner":947,"vrule":948,"rightrightarrows":949,"rightleftarrows":950,"AA":951,"Hat":952,"ak":953,"ble":954,"dou":955,"hss":956,"min":957,"nright":958,"nleftarrow":959,"uph":960,"wbre":961,"allo":962,"side":963,"sqcap":964,"hom":965,"bigodot":966,"arpoonright":967,"blebarwedge":968,"doublebarwedge":969,"upharpoonright":970,"wbreak":971,"allowbreak":972,"sideset":973,"--":974,"Huge":975,"amal":976,"do":977,"fbox":978,"group":979,"hskip":980,"lse":981,"pprox":982,"rk":983,"rgroup":984,"rapprox":985,"Ġin":986,"arrayco":987,"sure":988,"varlim":989,"pmb":990,"cite":991,"substack":992,"leftrightarrows":993,"supsetneq":994,"Longleftarrow":995,"updownarrow":996,"ensure":997,"lgroup":998,"gtrapprox":999,"amalg":1000,"lsep":1001,"arraycolsep":1002,"ensuremath":1003,"asym":1004,"ch":1005,"dig":1006,"ddag":1007,"ew":1008,"gra":1009,"gime":1010,"jo":1011,"ltimes":1012,"nleq":1013,"tch":1014,"frame":1015,"max":1016,"thde":1017,"inrel":1018,"ver":1019,"withde":1020,"ointop":1021,"notag":1022,"smallint":1023,"skew":1024,"lims":1025,"asymp":1026,"digamma":1027,"grave":1028,"gimel":1029,"joinrel":1030,"framebox":1031,"withdelims":1032,"Ar":1033,"Rrightarrow":1034,"ae":1035,"ag":1036,"fill":1037,"hspace":1038,"huge":1039,"lq":1040,"nwarrow":1041,"wline":1042,"Ġ14":1043,"mark":1044,"led":1045,"inf":1046,"inde":1047,"Ġex":1048,"pitch":1049,"dotsi":1050,"intop":1051,"rowvert":1052,"llcorner":1053,"black":1054,"leqq":1055,"biggm":1056,"approxeq":1057,"diag":1058,"textsc":1059,"textsl":1060,"circled":1061,"fork":1062,"cur":1063,"newline":1064,"negthick":1065,"atopwithdelims":1066,"Leftarrow":1067,"footnotemark":1068,"uplus":1069,"subsetneqq":1070,"---":1071,"varlimsup":1072,"varliminf":1073,"verb":1074,"Arrowvert":1075,"pitchfork":1076,"blacksquare":1077,"diagup":1078,"negthickspace":1079,"23":1080,"25":1081,"\\-":1082,"\\/":1083,"ape":1084,"ckap":1085,"dddot":1086,"erline":1087,"ever":1088,"ij":1089,"ice":1090,"ly":1091,"md":1092,"nda":1093,"nnu":1094,"nmid":1095,"nRightarrow":1096,"nVdash":1097,"of":1098,"off":1099,"sho":1100,"spe":1101,"wr":1102,"ymath":1103,"Ġ#":1104,"Ġ\\'":1105,"Ġ\\^":1106,"Ġ10":1107,"Ġ15":1108,"mannu":1109,"igarrow":1110,"fter":1111,"meral":1112,"leftrightharpoo":1113,"rightsqu":1114,"def":1115,"arrayst":1116,"rtmid":1117,"interline":1118,"vearrow":1119,"ngeq":1120,"hoice":1121,"lax":1122,"varGamma":1123,"varpropto":1124,"vartriangle":1125,"varUpsilon":1126,"biguplus":1127,"expa":1128,"Ġ<$":1129,"mathbin":1130,"perca":1131,"textcircled":1132,"textmd":1133,"scsh":1134,"cial":1135,"retch":1136,"relax":1137,"overwithdelims":1138,"noinde":1139,"owns":1140,"veebar":1141,"underbar":1142,"underrightarrow":1143,"upperca":1144,"backsimeq":1145,"trianglelefteq":1146,"boxtimes":1147,"boxed":1148,"preccur":1149,"thickap":1150,"root":1151,"romannu":1152,"mathchoice":1153,"index":1154,"circledcirc":1155,"curvearrow":1156,"everymath":1157,"lyeq":1158,"ndafter":1159,"offinterline":1160,"shortmid":1161,"special":1162,"leftrightharpoons":1163,"rightsquigarrow":1164,"arraystretch":1165,"expandafter":1166,"scshape":1167,"noindent":1168,"uppercase":1169,"preccurlyeq":1170,"thickapprox":1171,"romannumeral":1172,"curvearrowright":1173,"offinterlineskip":1174},"merges":["Ġ }","Ġ {","Ġ \\","Ġ _","Ġ ^","Ġ 2","Ġ )","Ġ (","Ġ 1","r a","Ġ =","Ġ -","č Ċ","Ġ ,","f ra","fra c","Ġ +","m a","t a","i g","Ġ 0","a r","a l","l e","Ġ i","t h","Ġ x","f t","ig h","m e","r igh","ma th","Ġ n","Ġ .","Ġ\\ ,","i n","p h","Ġ d","le ft","Ġ a","righ t","a m","e ta","t i","Ġ m","m u","Ġ 3","Ġ k","Ġ t","Ġ e","Ġ r","Ġ &","Ġ c","Ġ p","s i","r m","d e","math rm","Ġ 4","Ġ s","p r","Ġ ~","ph a","Ġ l","al pha","d a","Ġ A","Ġ\\ ;","o t","p i","p ar","ti al","par tial","i me","pr ime","p si","d ot","Ġ j","Ġ b","Ġ f","l ta","Ġ |","am ma","b da","am bda","ph i","Ġ q","b f","Ġ g","n u","Ġ z","ra y","ar ray","Ġ M","Ġ T","Ġ y","c al","b ar","Ġ N","ig ma","Ġ R","r t","l ambda","in t","Ġ B","v e","n g","q u","Ġ L","Ġ /","l o","b eta","ng le","Ġ u","de lta","s q","sq rt","th eta","Ġ\\ \\","g amma","Ġ ]","s igma","g a","me ga","Ġ D","Ġ F","Ġ [","Ġ S","math bf","s u","Ġ P","lo n","Ġ v","su m","psi lon","Ġ V","Ġ C","c dot","e psilon","a t","h at","a d","qu ad","Ġ h","h o","r ho","h i","t o","Ġ E","l a","Ġ H","l de","ti lde","Ġ Q","Ġ 5","v ar","Ġ X","Ġ G","b e","n d","o mega","e nd","g in","be gin","ta u","Ġ 6","n a","ve c","Ġ I","Ġ o","ra ngle","Ġ *","D e","De lta","G amma","p e","ft y","in fty","Ġ K","x i","Ġ 8","o w","Ġ J","Ġ U","r ow","ta r","g e","P hi","a p","Ġ W","c o","me s","ti mes","s in","Ġ Z","la ngle","o pe","r na","ra to","ope rato","rna me","operato rname","tar row","l in","lin e","var phi","p m","r line","L ambda","Ġ\\ !","Ġ ;","dot s","co s","Ġ w","righ tarrow","b ig","c hi","o ve","Ġ\\ }","ove rline","Ġ 7","e x","p a","s t","pr o","q quad","i v","e qu","equ iv","Ġ O","l n","O mega","l l","Ġ 9","k ap","kap pa","B ig","Ġ\\ {","da g","Ġ Y","\\ {","var epsilon","cdot s","Ġ :","math cal","P si","Ġ >","b o","bo l","Ġ <","ge r","dag ger","l dots","e ll","b la","na bla","ex p","y le","st yle","z eta","S igma","w i","wi de","si m","le q","Ġ !","big g","math b","mathb b","Ġ\\ :","h bar","o times","bol d","\\ }","m i","pro x","ap prox","P i","lo g","mi d","s p","ve rt","d i","pro d","pe r","per p","y style","la ystyle","sp laystyle","di splaystyle","me q","si meq","e d","w ed","wed ge","wide tilde","s y","sy m","sym bol","bold symbol","c k","t ex","tex t","r i","T h","Th eta","ge q","s e","e q","n de","u nde","ta n","s c","a st","r c","se t","p t","wide hat","c i","ci rc","r e","ri pt","sc ript","unde rline","Ġ\\ |","re l","n eq","s ta","sta ck","stack rel","sin h","o p","u s","cos h","Big g","c e","text style","s tar","n ot","fra k","math frak","m p","bigg r","l us","op lus","var theta","bigg l","Big r","b ra","Big l","f o","s f","su b","sub set","ng rightarrow","e c","bold math","ra ll","fo rall","script style","ec t","par row","u parrow","b j","bj ect","p to","pro pto","Ġ '","lo ngrightarrow","big l","big r","o int","p s","ma ps","maps to","o m","l le","\\ |","d dot","c u","b in","bin om","v dots","a ngle","left rightarrow","ove r","o r","math sf","cu p","bra ce","n o","ar c","f lo","flo or","t ri","tri angle","X i","c ot","b ot","co ng","i t","m be","nu mbe","no numbe","nonumbe r","c ap","R igh","Righ tarrow","z e","si ze","text rm","n e","arc tan","ra lle","pa ralle","paralle l","c frac","Ġ- -","o bject","ĠO bject","bra ck","s h","ar row","ow n","var rho","subset eq","r brace","text bf","i math","o d","d own","h e","la nd","script scriptstyle","script size","c he","che ck","s la","over rightarrow","down arrow","Bigg l","g g","n to","pha nto","phanto m","e xi","h line","st s","exi sts","Bigg r","b u","r floor","d dots","i o","io ta","lle t","bu llet","co lon","in us","U psilon","l floor","l brack","unde rbrace","ne g","I m","math it","t in","tin y","j math","le f","sla sh","ve e","m inus","set minus","R e","i int","lef tarrow","V e","Ve rt","at op","su p","big cup","w p","di m","se c","sup set","L o","lo r","pm od","m od","big oplus","i l","b mod","co th","L e","ft rightarrow","Le ftrightarrow","ng leftrightarrow","s ma","u psilon","\\ ,","c sc","e ph","al eph","big wedge","arc sin","sma ll","o dot","over set","r brack","mi t","l brace","l i","ar p","ar ge","Ġ\\ #","b re","text sf","Lo ngrightarrow","bre ve","e m","y set","var pi","pt yset","em ptyset","f f","i ff","n t","e r","la p","ln ot","da sh","unde r","sla nt","ar g","under set","B o","Bo x","Ġ \"","s pa","spa ce","de g","i iint","o o","ot not","fo otnot","arp oo","footnot e","r lap","e s","i mp","s b","t e","big triangle","li es","imp lies","\\ ;","k er","footnote size","th arpoo","u p","a cu","acu te","lo ngleftrightarrow","e il","l ce","rc eil","lce il","v phantom","e n","th in","a ck","b ack","t t","back slash","x rightarrow","v dash","to p","righ tharpoo","var sigma","Lo ngleftrightarrow","math op","l arge","big cap","leq slant","Ġ `","over brace","nu p","rightharpoo nup","big otimes","triangle q","L arge","r u","nu ll","bigtriangle up","var no","thin g","varno thing","dot eq","Ġ\\ _","over leftarrow","h f","big star","en space","\\ !","st ru","stru t","om inus","d iv","o nd","am ond","d dagger","Ġc m","n i","s k","di amond","r Vert","pr ot","prot ect","i p","var Delta","not in","sk ip","l Vert","Ġ\\ /","dots c","i ll","u le","\\ :","hf ill","k rightarrow","o krightarrow","ho okrightarrow","sh arp","V dash","big vee","subset neq","supset eq","Ġ ?","ng mapsto","lo ngmapsto","cdot p","geq slant","bigtriangle down","dots b","li m","f l","triangle left","fl at","s l","bo x","Ġ-- -","sq cup","j lim","l s","m o","de ls","re f","mo dels","ta g","P r","ma l","o u","l lap","thin space","en skip","V ec","e box","k ebox","n or","r d","s qu","v line","¿ ½","ï ¿½","Ġ �","ma kebox","su rd","nor mal","ar e","squ are","p ou","math rel","var Omega","nd s","small setminus","pou nds","n s","s s","s mi","math or","right lef","text up","tharpoo ns","smi le","mathor d","rightlef tharpoons","c c","Ġ\\ -","su cc","f tarrow","r times","de t","pr ec","text tt","o slash","Ġ\\ &","arrow vert","l g","Ġm m","int er","ngle ftarrow","hf il","inter cal","f row","Ġ\\ *","frow n","m pe","Ġp t","var pro","se arrow","bu mpe","varpro jlim","bumpe q","D own","S S","c d","e re","g cd","o he","t w","le me","th ere","in jlim","ti t","ad rightarrow","var injlim","co mp","tex tit","fo re","over leftrightarrow","Down arrow","ohe adrightarrow","tw oheadrightarrow","leme nt","there fore","comp lement","c a","th i","lo ngleftarrow","big m","triangle right","ne arrow","\\ #","n ce","ra l","ca nce","thi ck","cance l","U parrow","n at","u ral","math strut","su it","big circ","small skip","diamond suit","normal size","nat ural","g t","le ss","math tt","big sqcup","thick sim","less sim","b ow","l lde","ti e","nu llde","mit er","li miter","ker n","bow tie","nullde limiter","nulldelimiter space","D a","h phantom","r o","v Da","bar wedge","be th","eq no","vDa sh","A R","D i","G E","L AR","d skip","t s","Ġ @","me dskip","nd own","ge ts","co prod","dots m","sma sh","rightharpoo ndown","Di amond","LAR GE","n rightarrow","n leftrightarrow","r sim","r ne","w arrow","math c","co rne","text normal","prec eq","gt rsim","ro up","corne r","Ġ\\ [","Ġ\\ ]","math ope","left eq","lo se","var kappa","Big m","Bigg m","mathc lose","mathope n","lefteq n","B ar","T i","l r","s warrow","u ge","v ru","x leftarrow","math normal","right rightarrow","right leftarrow","sq subseteq","succ eq","Ti lde","lr corner","vru le","rightrightarrow s","rightleftarrow s","A A","H at","a k","b le","d ou","h ss","m in","n right","n leftarrow","u ph","w bre","al lo","si de","sq cap","ho m","big odot","arpoo nright","ble barwedge","dou blebarwedge","uph arpoonright","wbre ak","allo wbreak","side set","- -","H uge","a mal","d o","f box","g roup","h skip","l se","p prox","r k","r group","ra pprox","Ġi n","array co","su re","var lim","pm b","ci te","sub stack","leftrightarrow s","supset neq","Lo ngleftarrow","up downarrow","en sure","lg roup","gt rapprox","amal g","lse p","arrayco lsep","ensure math","a sym","c h","d ig","d dag","e w","g ra","g ime","j o","l times","n leq","t ch","fra me","ma x","th de","in rel","ve r","wi thde","oint op","no tag","small int","sk ew","lim s","asym p","dig amma","gra ve","gime l","jo inrel","frame box","withde lims","A r","R rightarrow","a e","a g","f ill","h space","h uge","l q","n warrow","w line","Ġ1 4","ma rk","le d","in f","in de","Ġe x","pi tch","dot si","int op","row vert","ll corner","bla ck","leq q","bigg m","approx eq","di ag","text sc","text sl","circ led","fo rk","cu r","ne wline","neg thick","atop withdelims","Le ftarrow","footnote mark","up lus","subsetneq q","-- -","varlim sup","varlim inf","ver b","Ar rowvert","pitch fork","black square","diag up","negthick space","2 3","2 5","\\ -","\\ /","a pe","c kap","d ddot","e rline","e ver","i j","i ce","l y","m d","n da","n nu","n mid","n Rightarrow","n Vdash","o f","o ff","s ho","s pe","w r","y math","Ġ #","Ġ\\ '","Ġ\\ ^","Ġ1 0","Ġ1 5","ma nnu","ig arrow","ft er","me ral","left rightharpoo","right squ","de f","array st","rt mid","int erline","ve arrow","ng eq","ho ice","la x","var Gamma","var propto","var triangle","var Upsilon","big uplus","ex pa","Ġ< $","mathb in","per ca","text circled","text md","sc sh","ci al","re tch","re lax","over withdelims","no inde","own s","vee bar","under bar","under rightarrow","up perca","back simeq","triangleleft eq","box times","box ed","prec cur","thi ckap","ro ot","ro mannu","mathc hoice","inde x","circled circ","cur vearrow","ever ymath","ly eq","nda fter","off interline","sho rtmid","spe cial","leftrightharpoo ns","rightsqu igarrow","arrayst retch","expa ndafter","scsh ape","noinde nt","upperca se","preccur lyeq","thickap prox","romannu meral","curvearrow right","offinterline skip"]}} diff --git a/modules/onnx_ocr_module/src/ppocr/utils/dict/latex_symbol_dict.txt b/modules/onnx_ocr_module/src/ppocr/utils/dict/latex_symbol_dict.txt new file mode 100644 index 0000000..69b1b84 --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/utils/dict/latex_symbol_dict.txt @@ -0,0 +1,111 @@ +eos +sos +! +' +( +) ++ +, +- +. +/ +0 +1 +2 +3 +4 +5 +6 +7 +8 +9 +< += +> +A +B +C +E +F +G +H +I +L +M +N +P +R +S +T +V +X +Y +[ +\Delta +\alpha +\beta +\cdot +\cdots +\cos +\div +\exists +\forall +\frac +\gamma +\geq +\in +\infty +\int +\lambda +\ldots +\leq +\lim +\log +\mu +\neq +\phi +\pi +\pm +\prime +\rightarrow +\sigma +\sin +\sqrt +\sum +\tan +\theta +\times +] +a +b +c +d +e +f +g +h +i +j +k +l +m +n +o +p +q +r +s +t +u +v +w +x +y +z +\{ +| +\} +{ +} +^ +_ diff --git a/modules/onnx_ocr_module/src/ppocr/utils/dict/latin_dict.txt b/modules/onnx_ocr_module/src/ppocr/utils/dict/latin_dict.txt new file mode 100644 index 0000000..e166bf3 --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/utils/dict/latin_dict.txt @@ -0,0 +1,185 @@ + +! +" +# +$ +% +& +' +( +) +* ++ +, +- +. +/ +0 +1 +2 +3 +4 +5 +6 +7 +8 +9 +: +; +< += +> +? +@ +A +B +C +D +E +F +G +H +I +J +K +L +M +N +O +P +Q +R +S +T +U +V +W +X +Y +Z +[ +] +_ +` +a +b +c +d +e +f +g +h +i +j +k +l +m +n +o +p +q +r +s +t +u +v +w +x +y +z +{ +} +¡ +£ +§ +ª +« +­ +° +² +³ +´ +µ +· +º +» +¿ +À +Á + +Ä +Å +Ç +È +É +Ê +Ë +Ì +Í +Î +Ï +Ò +Ó +Ô +Õ +Ö +Ú +Ü +Ý +ß +à +á +â +ã +ä +å +æ +ç +è +é +ê +ë +ì +í +î +ï +ñ +ò +ó +ô +õ +ö +ø +ù +ú +û +ü +ý +ą +Ć +ć +Č +č +Đ +đ +ę +ı +Ł +ł +ō +Œ +œ +Š +š +Ÿ +Ž +ž +ʒ +β +δ +ε +з +Ṡ +‘ +€ +™ diff --git a/modules/onnx_ocr_module/src/ppocr/utils/dict/layout_dict/layout_cdla_dict.txt b/modules/onnx_ocr_module/src/ppocr/utils/dict/layout_dict/layout_cdla_dict.txt new file mode 100644 index 0000000..5380334 --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/utils/dict/layout_dict/layout_cdla_dict.txt @@ -0,0 +1,10 @@ +text +title +figure +figure_caption +table +table_caption +header +footer +reference +equation diff --git a/modules/onnx_ocr_module/src/ppocr/utils/dict/layout_dict/layout_publaynet_dict.txt b/modules/onnx_ocr_module/src/ppocr/utils/dict/layout_dict/layout_publaynet_dict.txt new file mode 100644 index 0000000..d9c92da --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/utils/dict/layout_dict/layout_publaynet_dict.txt @@ -0,0 +1,5 @@ +text +title +list +table +figure diff --git a/modules/onnx_ocr_module/src/ppocr/utils/dict/layout_dict/layout_table_dict.txt b/modules/onnx_ocr_module/src/ppocr/utils/dict/layout_dict/layout_table_dict.txt new file mode 100644 index 0000000..ecbe2e6 --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/utils/dict/layout_dict/layout_table_dict.txt @@ -0,0 +1 @@ +table diff --git a/modules/onnx_ocr_module/src/ppocr/utils/dict/mr_dict.txt b/modules/onnx_ocr_module/src/ppocr/utils/dict/mr_dict.txt new file mode 100644 index 0000000..283b150 --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/utils/dict/mr_dict.txt @@ -0,0 +1,153 @@ + +! +# +$ +% +& +' +( ++ +, +- +. +/ +0 +1 +2 +3 +4 +5 +6 +7 +8 +9 +: +? +@ +A +B +C +D +E +F +G +H +I +J +K +L +M +N +O +P +Q +R +S +T +U +V +W +X +Y +Z +_ +a +b +c +d +e +f +g +h +i +j +k +l +m +n +o +p +q +r +s +t +u +v +w +x +y +z +É +é +ँ +ं +ः +अ +आ +इ +ई +उ +ऊ +ए +ऐ +ऑ +ओ +औ +क +ख +ग +घ +च +छ +ज +झ +ञ +ट +ठ +ड +ढ +ण +त +थ +द +ध +न +प +फ +ब +भ +म +य +र +ऱ +ल +ळ +व +श +ष +स +ह +़ +ा +ि +ी +ु +ू +ृ +ॅ +े +ै +ॉ +ो +ौ +् +० +१ +२ +३ +४ +५ +६ +७ +८ +९ diff --git a/modules/onnx_ocr_module/src/ppocr/utils/dict/ne_dict.txt b/modules/onnx_ocr_module/src/ppocr/utils/dict/ne_dict.txt new file mode 100644 index 0000000..5a7df95 --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/utils/dict/ne_dict.txt @@ -0,0 +1,153 @@ + +! +# +$ +% +& +' +( ++ +, +- +. +/ +0 +1 +2 +3 +4 +5 +6 +7 +8 +9 +: +? +@ +A +B +C +D +E +F +G +H +I +J +K +L +M +N +O +P +Q +R +S +T +U +V +W +X +Y +Z +_ +a +b +c +d +e +f +g +h +i +j +k +l +m +n +o +p +q +r +s +t +u +v +w +x +y +z +É +é +ः +अ +आ +इ +ई +उ +ऊ +ऋ +ए +ऐ +ओ +औ +क +ख +ग +घ +ङ +च +छ +ज +झ +ञ +ट +ठ +ड +ढ +ण +त +थ +द +ध +न +ऩ +प +फ +ब +भ +म +य +र +ऱ +ल +व +श +ष +स +ह +़ +ा +ि +ी +ु +ू +ृ +े +ै +ो +ौ +् +॒ +ॠ +। +० +१ +२ +३ +४ +५ +६ +७ +८ +९ diff --git a/modules/onnx_ocr_module/src/ppocr/utils/dict/oc_dict.txt b/modules/onnx_ocr_module/src/ppocr/utils/dict/oc_dict.txt new file mode 100644 index 0000000..e88af8b --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/utils/dict/oc_dict.txt @@ -0,0 +1,96 @@ +o +c +_ +i +m +g +/ +2 +0 +I +L +S +V +R +C +1 +v +a +l +4 +3 +. +j +p +r +e +è +t +9 +7 +5 +8 +n +' +b +s +6 +q +u +á +d +ò +à +h +z +f +ï +í +A +ç +x +ó +é +P +O +Ò +ü +k +À +F +- +ú +­ +æ +Á +D +E +w +K +T +N +y +U +Z +G +B +J +H +M +W +Y +X +Q +% +$ +, +@ +& +! +: +( +# +? ++ +É + diff --git a/modules/onnx_ocr_module/src/ppocr/utils/dict/parseq_dict.txt b/modules/onnx_ocr_module/src/ppocr/utils/dict/parseq_dict.txt new file mode 100644 index 0000000..87c6d67 --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/utils/dict/parseq_dict.txt @@ -0,0 +1,94 @@ +0 +1 +2 +3 +4 +5 +6 +7 +8 +9 +a +b +c +d +e +f +g +h +i +j +k +l +m +n +o +p +q +r +s +t +u +v +w +x +y +z +A +B +C +D +E +F +G +H +I +J +K +L +M +N +O +P +Q +R +S +T +U +V +W +X +Y +Z +! +" +# +$ +% +& +' +( +) +* ++ +, +- +. +/ +: +; +< += +> +? +@ +[ +\ +] +^ +_ +` +{ +| +} +~ diff --git a/modules/onnx_ocr_module/src/ppocr/utils/dict/ppocrv4_doc_dict.txt b/modules/onnx_ocr_module/src/ppocr/utils/dict/ppocrv4_doc_dict.txt new file mode 100644 index 0000000..09e275b --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/utils/dict/ppocrv4_doc_dict.txt @@ -0,0 +1,15629 @@ +' +疗 +绚 +诚 +娇 +溜 +题 +贿 +者 +廖 +更 +纳 +加 +奉 +公 +一 +就 +汴 +计 +与 +路 +房 +原 +妇 +2 +0 +8 +- +7 +其 +> +: +] +, +, +骑 +刈 +全 +消 +昏 +傈 +安 +久 +钟 +嗅 +不 +影 +处 +驽 +蜿 +资 +关 +椤 +地 +瘸 +专 +问 +忖 +票 +嫉 +炎 +韵 +要 +月 +田 +节 +陂 +鄙 +捌 +备 +拳 +伺 +眼 +网 +盎 +大 +傍 +心 +东 +愉 +汇 +蹿 +科 +每 +业 +里 +航 +晏 +字 +平 +录 +先 +1 +3 +彤 +鲶 +产 +稍 +督 +腴 +有 +象 +岳 +注 +绍 +在 +泺 +文 +定 +核 +名 +水 +过 +理 +让 +偷 +率 +等 +这 +发 +” +为 +含 +肥 +酉 +相 +鄱 +七 +编 +猥 +锛 +日 +镀 +蒂 +掰 +倒 +辆 +栾 +栗 +综 +涩 +州 +雌 +滑 +馀 +了 +机 +块 +司 +宰 +甙 +兴 +矽 +抚 +保 +用 +沧 +秩 +如 +收 +息 +滥 +页 +疑 +埠 +! +! +姥 +异 +橹 +钇 +向 +下 +跄 +的 +椴 +沫 +国 +绥 +獠 +报 +开 +民 +蜇 +何 +分 +凇 +长 +讥 +藏 +掏 +施 +羽 +中 +讲 +派 +嘟 +人 +提 +浼 +间 +世 +而 +古 +多 +倪 +唇 +饯 +控 +庚 +首 +赛 +蜓 +味 +断 +制 +觉 +技 +替 +艰 +溢 +潮 +夕 +钺 +外 +摘 +枋 +动 +双 +单 +啮 +户 +枇 +确 +锦 +曜 +杜 +或 +能 +效 +霜 +盒 +然 +侗 +电 +晁 +放 +步 +鹃 +新 +杖 +蜂 +吒 +濂 +瞬 +评 +总 +隍 +对 +独 +合 +也 +是 +府 +青 +天 +诲 +墙 +组 +滴 +级 +邀 +帘 +示 +已 +时 +骸 +仄 +泅 +和 +遨 +店 +雇 +疫 +持 +巍 +踮 +境 +只 +亨 +目 +鉴 +崤 +闲 +体 +泄 +杂 +作 +般 +轰 +化 +解 +迂 +诿 +蛭 +璀 +腾 +告 +版 +服 +省 +师 +小 +规 +程 +线 +海 +办 +引 +二 +桧 +牌 +砺 +洄 +裴 +修 +图 +痫 +胡 +许 +犊 +事 +郛 +基 +柴 +呼 +食 +研 +奶 +律 +蛋 +因 +葆 +察 +戏 +褒 +戒 +再 +李 +骁 +工 +貂 +油 +鹅 +章 +啄 +休 +场 +给 +睡 +纷 +豆 +器 +捎 +说 +敏 +学 +会 +浒 +设 +诊 +格 +廓 +查 +来 +霓 +室 +溆 +¢ +诡 +寥 +焕 +舜 +柒 +狐 +回 +戟 +砾 +厄 +实 +翩 +尿 +五 +入 +径 +惭 +喹 +股 +宇 +篝 +| +; +美 +期 +云 +九 +祺 +扮 +靠 +锝 +槌 +系 +企 +酰 +阊 +暂 +蚕 +忻 +豁 +本 +羹 +执 +条 +钦 +H +獒 +限 +进 +季 +楦 +于 +芘 +玖 +铋 +茯 +未 +答 +粘 +括 +样 +精 +欠 +矢 +甥 +帷 +嵩 +扣 +令 +仔 +风 +皈 +行 +支 +部 +蓉 +刮 +站 +蜡 +救 +钊 +汗 +松 +嫌 +成 +可 +. +鹤 +院 +从 +交 +政 +怕 +活 +调 +球 +局 +验 +髌 +第 +韫 +谗 +串 +到 +圆 +年 +米 +/ +* +友 +忿 +检 +区 +看 +自 +敢 +刃 +个 +兹 +弄 +流 +留 +同 +没 +齿 +星 +聆 +轼 +湖 +什 +三 +建 +蛔 +儿 +椋 +汕 +震 +颧 +鲤 +跟 +力 +情 +璺 +铨 +陪 +务 +指 +族 +训 +滦 +鄣 +濮 +扒 +商 +箱 +十 +召 +慷 +辗 +所 +莞 +管 +护 +臭 +横 +硒 +嗓 +接 +侦 +六 +露 +党 +馋 +驾 +剖 +高 +侬 +妪 +幂 +猗 +绺 +骐 +央 +酐 +孝 +筝 +课 +徇 +缰 +门 +男 +西 +项 +句 +谙 +瞒 +秃 +篇 +教 +碲 +罚 +声 +呐 +景 +前 +富 +嘴 +鳌 +稀 +免 +朋 +啬 +睐 +去 +赈 +鱼 +住 +肩 +愕 +速 +旁 +波 +厅 +健 +茼 +厥 +鲟 +谅 +投 +攸 +炔 +数 +方 +击 +呋 +谈 +绩 +别 +愫 +僚 +躬 +鹧 +胪 +炳 +招 +喇 +膨 +泵 +蹦 +毛 +结 +5 +4 +谱 +识 +陕 +粽 +婚 +拟 +构 +且 +搜 +任 +潘 +比 +郢 +妨 +醪 +陀 +桔 +碘 +扎 +选 +哈 +骷 +楷 +亿 +明 +缆 +脯 +监 +睫 +逻 +婵 +共 +赴 +淝 +凡 +惦 +及 +达 +揖 +谩 +澹 +减 +焰 +蛹 +番 +祁 +柏 +员 +禄 +怡 +峤 +龙 +白 +叽 +生 +闯 +起 +细 +装 +谕 +竟 +聚 +钙 +上 +导 +渊 +按 +艾 +辘 +挡 +耒 +盹 +饪 +臀 +记 +邮 +蕙 +受 +各 +医 +搂 +普 +滇 +朗 +茸 +带 +翻 +酚 +( +光 +堤 +墟 +蔷 +万 +幻 +〓 +瑙 +辈 +昧 +盏 +亘 +蛀 +吉 +铰 +请 +子 +假 +闻 +税 +井 +诩 +哨 +嫂 +好 +面 +琐 +校 +馊 +鬣 +缂 +营 +访 +炖 +占 +农 +缀 +否 +经 +钚 +棵 +趟 +张 +亟 +吏 +茶 +谨 +捻 +论 +迸 +堂 +玉 +信 +吧 +瞠 +乡 +姬 +寺 +咬 +溏 +苄 +皿 +意 +赉 +宝 +尔 +钰 +艺 +特 +唳 +踉 +都 +荣 +倚 +登 +荐 +丧 +奇 +涵 +批 +炭 +近 +符 +傩 +感 +道 +着 +菊 +虹 +仲 +众 +懈 +濯 +颞 +眺 +南 +释 +北 +缝 +标 +既 +茗 +整 +撼 +迤 +贲 +挎 +耱 +拒 +某 +妍 +卫 +哇 +英 +矶 +藩 +治 +他 +元 +领 +膜 +遮 +穗 +蛾 +飞 +荒 +棺 +劫 +么 +市 +火 +温 +拈 +棚 +洼 +转 +果 +奕 +卸 +迪 +伸 +泳 +斗 +邡 +侄 +涨 +屯 +萋 +胭 +氡 +崮 +枞 +惧 +冒 +彩 +斜 +手 +豚 +随 +旭 +淑 +妞 +形 +菌 +吲 +沱 +争 +驯 +歹 +挟 +兆 +柱 +传 +至 +包 +内 +响 +临 +红 +功 +弩 +衡 +寂 +禁 +老 +棍 +耆 +渍 +织 +害 +氵 +渑 +布 +载 +靥 +嗬 +虽 +苹 +咨 +娄 +库 +雉 +榜 +帜 +嘲 +套 +瑚 +亲 +簸 +欧 +边 +6 +腿 +旮 +抛 +吹 +瞳 +得 +镓 +梗 +厨 +继 +漾 +愣 +憨 +士 +策 +窑 +抑 +躯 +襟 +脏 +参 +贸 +言 +干 +绸 +鳄 +穷 +藜 +音 +折 +详 +) +举 +悍 +甸 +癌 +黎 +谴 +死 +罩 +迁 +寒 +驷 +袖 +媒 +蒋 +掘 +模 +纠 +恣 +观 +祖 +蛆 +碍 +位 +稿 +主 +澧 +跌 +筏 +京 +锏 +帝 +贴 +证 +糠 +才 +黄 +鲸 +略 +炯 +饱 +四 +出 +园 +犀 +牧 +容 +汉 +杆 +浈 +汰 +瑷 +造 +虫 +瘩 +怪 +驴 +济 +应 +花 +沣 +谔 +夙 +旅 +价 +矿 +以 +考 +s +u +呦 +晒 +巡 +茅 +准 +肟 +瓴 +詹 +仟 +褂 +译 +桌 +混 +宁 +怦 +郑 +抿 +些 +余 +鄂 +饴 +攒 +珑 +群 +阖 +岔 +琨 +藓 +预 +环 +洮 +岌 +宀 +杲 +瀵 +最 +常 +囡 +周 +踊 +女 +鼓 +袭 +喉 +简 +范 +薯 +遐 +疏 +粱 +黜 +禧 +法 +箔 +斤 +遥 +汝 +奥 +直 +贞 +撑 +置 +绱 +集 +她 +馅 +逗 +钧 +橱 +魉 +[ +恙 +躁 +唤 +9 +旺 +膘 +待 +脾 +惫 +购 +吗 +依 +盲 +度 +瘿 +蠖 +俾 +之 +镗 +拇 +鲵 +厝 +簧 +续 +款 +展 +啃 +表 +剔 +品 +钻 +腭 +损 +清 +锶 +统 +涌 +寸 +滨 +贪 +链 +吠 +冈 +伎 +迥 +咏 +吁 +览 +防 +迅 +失 +汾 +阔 +逵 +绀 +蔑 +列 +川 +凭 +努 +熨 +揪 +利 +俱 +绉 +抢 +鸨 +我 +即 +责 +膦 +易 +毓 +鹊 +刹 +玷 +岿 +空 +嘞 +绊 +排 +术 +估 +锷 +违 +们 +苟 +铜 +播 +肘 +件 +烫 +审 +鲂 +广 +像 +铌 +惰 +铟 +巳 +胍 +鲍 +康 +憧 +色 +恢 +想 +拷 +尤 +疳 +知 +S +Y +F +D +A +峄 +裕 +帮 +握 +搔 +氐 +氘 +难 +墒 +沮 +雨 +叁 +缥 +悴 +藐 +湫 +娟 +苑 +稠 +颛 +簇 +后 +阕 +闭 +蕤 +缚 +怎 +佞 +码 +嘤 +蔡 +痊 +舱 +螯 +帕 +赫 +昵 +升 +烬 +岫 +、 +疵 +蜻 +髁 +蕨 +隶 +烛 +械 +丑 +盂 +梁 +强 +鲛 +由 +拘 +揉 +劭 +龟 +撤 +钩 +呕 +孛 +费 +妻 +漂 +求 +阑 +崖 +秤 +甘 +通 +深 +补 +赃 +坎 +床 +啪 +承 +吼 +量 +暇 +钼 +烨 +阂 +擎 +脱 +逮 +称 +P +神 +属 +矗 +华 +届 +狍 +葑 +汹 +育 +患 +窒 +蛰 +佼 +静 +槎 +运 +鳗 +庆 +逝 +曼 +疱 +克 +代 +官 +此 +麸 +耧 +蚌 +晟 +例 +础 +榛 +副 +测 +唰 +缢 +迹 +灬 +霁 +身 +岁 +赭 +扛 +又 +菡 +乜 +雾 +板 +读 +陷 +徉 +贯 +郁 +虑 +变 +钓 +菜 +圾 +现 +琢 +式 +乐 +维 +渔 +浜 +左 +吾 +脑 +钡 +警 +T +啵 +拴 +偌 +漱 +湿 +硕 +止 +骼 +魄 +积 +燥 +联 +踢 +玛 +则 +窿 +见 +振 +畿 +送 +班 +钽 +您 +赵 +刨 +印 +讨 +踝 +籍 +谡 +舌 +崧 +汽 +蔽 +沪 +酥 +绒 +怖 +财 +帖 +肱 +私 +莎 +勋 +羔 +霸 +励 +哼 +帐 +将 +帅 +渠 +纪 +婴 +娩 +岭 +厘 +滕 +吻 +伤 +坝 +冠 +戊 +隆 +瘁 +介 +涧 +物 +黍 +并 +姗 +奢 +蹑 +掣 +垸 +锴 +命 +箍 +捉 +病 +辖 +琰 +眭 +迩 +艘 +绌 +繁 +寅 +若 +毋 +思 +诉 +类 +诈 +燮 +轲 +酮 +狂 +重 +反 +职 +筱 +县 +委 +磕 +绣 +奖 +晋 +濉 +志 +徽 +肠 +呈 +獐 +坻 +口 +片 +碰 +几 +村 +柿 +劳 +料 +获 +亩 +惕 +晕 +厌 +号 +罢 +池 +正 +鏖 +煨 +家 +棕 +复 +尝 +懋 +蜥 +锅 +岛 +扰 +队 +坠 +瘾 +钬 +@ +卧 +疣 +镇 +譬 +冰 +彷 +频 +黯 +据 +垄 +采 +八 +缪 +瘫 +型 +熹 +砰 +楠 +襁 +箐 +但 +嘶 +绳 +啤 +拍 +盥 +穆 +傲 +洗 +盯 +塘 +怔 +筛 +丿 +台 +恒 +喂 +葛 +永 +¥ +烟 +酒 +桦 +书 +砂 +蚝 +缉 +态 +瀚 +袄 +圳 +轻 +蛛 +超 +榧 +遛 +姒 +奘 +铮 +右 +荽 +望 +偻 +卡 +丶 +氰 +附 +做 +革 +索 +戚 +坨 +桷 +唁 +垅 +榻 +岐 +偎 +坛 +莨 +山 +殊 +微 +骇 +陈 +爨 +推 +嗝 +驹 +澡 +藁 +呤 +卤 +嘻 +糅 +逛 +侵 +郓 +酌 +德 +摇 +※ +鬃 +被 +慨 +殡 +羸 +昌 +泡 +戛 +鞋 +河 +宪 +沿 +玲 +鲨 +翅 +哽 +源 +铅 +语 +照 +邯 +址 +荃 +佬 +顺 +鸳 +町 +霭 +睾 +瓢 +夸 +椁 +晓 +酿 +痈 +咔 +侏 +券 +噎 +湍 +签 +嚷 +离 +午 +尚 +社 +锤 +背 +孟 +使 +浪 +缦 +潍 +鞅 +军 +姹 +驶 +笑 +鳟 +鲁 +》 +孽 +钜 +绿 +洱 +礴 +焯 +椰 +颖 +囔 +乌 +孔 +巴 +互 +性 +椽 +哞 +聘 +昨 +早 +暮 +胶 +炀 +隧 +低 +彗 +昝 +铁 +呓 +氽 +藉 +喔 +癖 +瑗 +姨 +权 +胱 +韦 +堑 +蜜 +酋 +楝 +砝 +毁 +靓 +歙 +锲 +究 +屋 +喳 +骨 +辨 +碑 +武 +鸠 +宫 +辜 +烊 +适 +坡 +殃 +培 +佩 +供 +走 +蜈 +迟 +翼 +况 +姣 +凛 +浔 +吃 +飘 +债 +犟 +金 +促 +苛 +崇 +坂 +莳 +畔 +绂 +兵 +蠕 +斋 +根 +砍 +亢 +欢 +恬 +崔 +剁 +餐 +榫 +快 +扶 +‖ +濒 +缠 +鳜 +当 +彭 +驭 +浦 +篮 +昀 +锆 +秸 +钳 +弋 +娣 +瞑 +夷 +龛 +苫 +拱 +致 +% +嵊 +障 +隐 +弑 +初 +娓 +抉 +汩 +累 +蓖 +" +唬 +助 +苓 +昙 +押 +毙 +破 +城 +郧 +逢 +嚏 +獭 +瞻 +溱 +婿 +赊 +跨 +恼 +璧 +萃 +姻 +貉 +灵 +炉 +密 +氛 +陶 +砸 +谬 +衔 +点 +琛 +沛 +枳 +层 +岱 +诺 +脍 +榈 +埂 +征 +冷 +裁 +打 +蹴 +素 +瘘 +逞 +蛐 +聊 +激 +腱 +萘 +踵 +飒 +蓟 +吆 +取 +咙 +簋 +涓 +矩 +曝 +挺 +揣 +座 +你 +史 +舵 +焱 +尘 +苏 +笈 +脚 +溉 +榨 +诵 +樊 +邓 +焊 +义 +庶 +儋 +蟋 +蒲 +赦 +呷 +杞 +诠 +豪 +还 +试 +颓 +茉 +太 +除 +紫 +逃 +痴 +草 +充 +鳕 +珉 +祗 +墨 +渭 +烩 +蘸 +慕 +璇 +镶 +穴 +嵘 +恶 +骂 +险 +绋 +幕 +碉 +肺 +戳 +刘 +潞 +秣 +纾 +潜 +銮 +洛 +须 +罘 +销 +瘪 +汞 +兮 +屉 +r +林 +厕 +质 +探 +划 +狸 +殚 +善 +煊 +烹 +〒 +锈 +逯 +宸 +辍 +泱 +柚 +袍 +远 +蹋 +嶙 +绝 +峥 +娥 +缍 +雀 +徵 +认 +镱 +谷 += +贩 +勉 +撩 +鄯 +斐 +洋 +非 +祚 +泾 +诒 +饿 +撬 +威 +晷 +搭 +芍 +锥 +笺 +蓦 +候 +琊 +档 +礁 +沼 +卵 +荠 +忑 +朝 +凹 +瑞 +头 +仪 +弧 +孵 +畏 +铆 +突 +衲 +车 +浩 +气 +茂 +悖 +厢 +枕 +酝 +戴 +湾 +邹 +飚 +攘 +锂 +写 +宵 +翁 +岷 +无 +喜 +丈 +挑 +嗟 +绛 +殉 +议 +槽 +具 +醇 +淞 +笃 +郴 +阅 +饼 +底 +壕 +砚 +弈 +询 +缕 +庹 +翟 +零 +筷 +暨 +舟 +闺 +甯 +撞 +麂 +茌 +蔼 +很 +珲 +捕 +棠 +角 +阉 +媛 +娲 +诽 +剿 +尉 +爵 +睬 +韩 +诰 +匣 +危 +糍 +镯 +立 +浏 +阳 +少 +盆 +舔 +擘 +匪 +申 +尬 +铣 +旯 +抖 +赘 +瓯 +居 +ˇ +哮 +游 +锭 +茏 +歌 +坏 +甚 +秒 +舞 +沙 +仗 +劲 +潺 +阿 +燧 +郭 +嗖 +霏 +忠 +材 +奂 +耐 +跺 +砀 +输 +岖 +媳 +氟 +极 +摆 +灿 +今 +扔 +腻 +枝 +奎 +药 +熄 +吨 +话 +q +额 +慑 +嘌 +协 +喀 +壳 +埭 +视 +著 +於 +愧 +陲 +翌 +峁 +颅 +佛 +腹 +聋 +侯 +咎 +叟 +秀 +颇 +存 +较 +罪 +哄 +岗 +扫 +栏 +钾 +羌 +己 +璨 +枭 +霉 +煌 +涸 +衿 +键 +镝 +益 +岢 +奏 +连 +夯 +睿 +冥 +均 +糖 +狞 +蹊 +稻 +爸 +刿 +胥 +煜 +丽 +肿 +璃 +掸 +跚 +灾 +垂 +樾 +濑 +乎 +莲 +窄 +犹 +撮 +战 +馄 +软 +络 +显 +鸢 +胸 +宾 +妲 +恕 +埔 +蝌 +份 +遇 +巧 +瞟 +粒 +恰 +剥 +桡 +博 +讯 +凯 +堇 +阶 +滤 +卖 +斌 +骚 +彬 +兑 +磺 +樱 +舷 +两 +娱 +福 +仃 +差 +找 +桁 +÷ +净 +把 +阴 +污 +戬 +雷 +碓 +蕲 +楚 +罡 +焖 +抽 +妫 +咒 +仑 +闱 +尽 +邑 +菁 +爱 +贷 +沥 +鞑 +牡 +嗉 +崴 +骤 +塌 +嗦 +订 +拮 +滓 +捡 +锻 +次 +坪 +杩 +臃 +箬 +融 +珂 +鹗 +宗 +枚 +降 +鸬 +妯 +阄 +堰 +盐 +毅 +必 +杨 +崃 +俺 +甬 +状 +莘 +货 +耸 +菱 +腼 +铸 +唏 +痤 +孚 +澳 +懒 +溅 +翘 +疙 +杷 +淼 +缙 +骰 +喊 +悉 +砻 +坷 +艇 +赁 +界 +谤 +纣 +宴 +晃 +茹 +归 +饭 +梢 +铡 +街 +抄 +肼 +鬟 +苯 +颂 +撷 +戈 +炒 +咆 +茭 +瘙 +负 +仰 +客 +琉 +铢 +封 +卑 +珥 +椿 +镧 +窨 +鬲 +寿 +御 +袤 +铃 +萎 +砖 +餮 +脒 +裳 +肪 +孕 +嫣 +馗 +嵇 +恳 +氯 +江 +石 +褶 +冢 +祸 +阻 +狈 +羞 +银 +靳 +透 +咳 +叼 +敷 +芷 +啥 +它 +瓤 +兰 +痘 +懊 +逑 +肌 +往 +捺 +坊 +甩 +呻 +〃 +沦 +忘 +膻 +祟 +菅 +剧 +崆 +智 +坯 +臧 +霍 +墅 +攻 +眯 +倘 +拢 +骠 +铐 +庭 +岙 +瓠 +′ +缺 +泥 +迢 +捶 +? +? +郏 +喙 +掷 +沌 +纯 +秘 +种 +听 +绘 +固 +螨 +团 +香 +盗 +妒 +埚 +蓝 +拖 +旱 +荞 +铀 +血 +遏 +汲 +辰 +叩 +拽 +幅 +硬 +惶 +桀 +漠 +措 +泼 +唑 +齐 +肾 +念 +酱 +虚 +屁 +耶 +旗 +砦 +闵 +婉 +馆 +拭 +绅 +韧 +忏 +窝 +醋 +葺 +顾 +辞 +倜 +堆 +辋 +逆 +玟 +贱 +疾 +董 +惘 +倌 +锕 +淘 +嘀 +莽 +俭 +笏 +绑 +鲷 +杈 +择 +蟀 +粥 +嗯 +驰 +逾 +案 +谪 +褓 +胫 +哩 +昕 +颚 +鲢 +绠 +躺 +鹄 +崂 +儒 +俨 +丝 +尕 +泌 +啊 +萸 +彰 +幺 +吟 +骄 +苣 +弦 +脊 +瑰 +〈 +诛 +镁 +析 +闪 +剪 +侧 +哟 +框 +螃 +守 +嬗 +燕 +狭 +铈 +缮 +概 +迳 +痧 +鲲 +俯 +售 +笼 +痣 +扉 +挖 +满 +咋 +援 +邱 +扇 +歪 +便 +玑 +绦 +峡 +蛇 +叨 +〖 +泽 +胃 +斓 +喋 +怂 +坟 +猪 +该 +蚬 +炕 +弥 +赞 +棣 +晔 +娠 +挲 +狡 +创 +疖 +铕 +镭 +稷 +挫 +弭 +啾 +翔 +粉 +履 +苘 +哦 +楼 +秕 +铂 +土 +锣 +瘟 +挣 +栉 +习 +享 +桢 +袅 +磨 +桂 +谦 +延 +坚 +蔚 +噗 +署 +谟 +猬 +钎 +恐 +嬉 +雒 +倦 +衅 +亏 +璩 +睹 +刻 +殿 +王 +算 +雕 +麻 +丘 +柯 +骆 +丸 +塍 +谚 +添 +鲈 +垓 +桎 +蚯 +芥 +予 +飕 +镦 +谌 +窗 +醚 +菀 +亮 +搪 +莺 +蒿 +羁 +足 +J +真 +轶 +悬 +衷 +靛 +翊 +掩 +哒 +炅 +掐 +冼 +妮 +l +谐 +稚 +荆 +擒 +犯 +陵 +虏 +浓 +崽 +刍 +陌 +傻 +孜 +千 +靖 +演 +矜 +钕 +煽 +杰 +酗 +渗 +伞 +栋 +俗 +泫 +戍 +罕 +沾 +疽 +灏 +煦 +芬 +磴 +叱 +阱 +榉 +湃 +蜀 +叉 +醒 +彪 +租 +郡 +篷 +屎 +良 +垢 +隗 +弱 +陨 +峪 +砷 +掴 +颁 +胎 +雯 +绵 +贬 +沐 +撵 +隘 +篙 +暖 +曹 +陡 +栓 +填 +臼 +彦 +瓶 +琪 +潼 +哪 +鸡 +摩 +啦 +俟 +锋 +域 +耻 +蔫 +疯 +纹 +撇 +毒 +绶 +痛 +酯 +忍 +爪 +赳 +歆 +嘹 +辕 +烈 +册 +朴 +钱 +吮 +毯 +癜 +娃 +谀 +邵 +厮 +炽 +璞 +邃 +丐 +追 +词 +瓒 +忆 +轧 +芫 +谯 +喷 +弟 +半 +冕 +裙 +掖 +墉 +绮 +寝 +苔 +势 +顷 +褥 +切 +衮 +君 +佳 +嫒 +蚩 +霞 +佚 +洙 +逊 +镖 +暹 +唛 +& +殒 +顶 +碗 +獗 +轭 +铺 +蛊 +废 +恹 +汨 +崩 +珍 +那 +杵 +曲 +纺 +夏 +薰 +傀 +闳 +淬 +姘 +舀 +拧 +卷 +楂 +恍 +讪 +厩 +寮 +篪 +赓 +乘 +灭 +盅 +鞣 +沟 +慎 +挂 +饺 +鼾 +杳 +树 +缨 +丛 +絮 +娌 +臻 +嗳 +篡 +侩 +述 +衰 +矛 +圈 +蚜 +匕 +筹 +匿 +濞 +晨 +叶 +骋 +郝 +挚 +蚴 +滞 +增 +侍 +描 +瓣 +吖 +嫦 +蟒 +匾 +圣 +赌 +毡 +癞 +恺 +百 +曳 +需 +篓 +肮 +庖 +帏 +卿 +驿 +遗 +蹬 +鬓 +骡 +歉 +芎 +胳 +屐 +禽 +烦 +晌 +寄 +媾 +狄 +翡 +苒 +船 +廉 +终 +痞 +殇 +々 +畦 +饶 +改 +拆 +悻 +萄 +£ +瓿 +乃 +訾 +桅 +匮 +溧 +拥 +纱 +铍 +骗 +蕃 +龋 +缬 +父 +佐 +疚 +栎 +醍 +掳 +蓄 +x +惆 +颜 +鲆 +榆 +〔 +猎 +敌 +暴 +谥 +鲫 +贾 +罗 +玻 +缄 +扦 +芪 +癣 +落 +徒 +臾 +恿 +猩 +托 +邴 +肄 +牵 +春 +陛 +耀 +刊 +拓 +蓓 +邳 +堕 +寇 +枉 +淌 +啡 +湄 +兽 +酷 +萼 +碚 +濠 +萤 +夹 +旬 +戮 +梭 +琥 +椭 +昔 +勺 +蜊 +绐 +晚 +孺 +僵 +宣 +摄 +冽 +旨 +萌 +忙 +蚤 +眉 +噼 +蟑 +付 +契 +瓜 +悼 +颡 +壁 +曾 +窕 +颢 +澎 +仿 +俑 +浑 +嵌 +浣 +乍 +碌 +褪 +乱 +蔟 +隙 +玩 +剐 +葫 +箫 +纲 +围 +伐 +决 +伙 +漩 +瑟 +刑 +肓 +镳 +缓 +蹭 +氨 +皓 +典 +畲 +坍 +铑 +檐 +塑 +洞 +倬 +储 +胴 +淳 +戾 +吐 +灼 +惺 +妙 +毕 +珐 +缈 +虱 +盖 +羰 +鸿 +磅 +谓 +髅 +娴 +苴 +唷 +蚣 +霹 +抨 +贤 +唠 +犬 +誓 +逍 +庠 +逼 +麓 +籼 +釉 +呜 +碧 +秧 +氩 +摔 +霄 +穸 +纨 +辟 +妈 +映 +完 +牛 +缴 +嗷 +炊 +恩 +荔 +茆 +掉 +紊 +慌 +莓 +羟 +阙 +萁 +磐 +另 +蕹 +辱 +鳐 +湮 +吡 +吩 +唐 +睦 +垠 +舒 +圜 +冗 +瞿 +溺 +芾 +囱 +匠 +僳 +汐 +菩 +饬 +漓 +黑 +霰 +浸 +濡 +窥 +毂 +蒡 +兢 +驻 +鹉 +芮 +诙 +迫 +雳 +厂 +忐 +臆 +猴 +鸣 +蚪 +栈 +箕 +羡 +渐 +莆 +捍 +眈 +哓 +趴 +蹼 +埕 +嚣 +骛 +宏 +淄 +斑 +噜 +严 +瑛 +垃 +椎 +诱 +压 +庾 +绞 +焘 +廿 +抡 +迄 +棘 +夫 +纬 +锹 +眨 +瞌 +侠 +脐 +竞 +瀑 +孳 +骧 +遁 +姜 +颦 +荪 +滚 +萦 +伪 +逸 +粳 +爬 +锁 +矣 +役 +趣 +洒 +颔 +诏 +逐 +奸 +甭 +惠 +攀 +蹄 +泛 +尼 +拼 +阮 +鹰 +亚 +颈 +惑 +勒 +〉 +际 +肛 +爷 +刚 +钨 +丰 +养 +冶 +鲽 +辉 +蔻 +画 +覆 +皴 +妊 +麦 +返 +醉 +皂 +擀 +〗 +酶 +凑 +粹 +悟 +诀 +硖 +港 +卜 +z +杀 +涕 +± +舍 +铠 +抵 +弛 +段 +敝 +镐 +奠 +拂 +轴 +跛 +袱 +e +t +沉 +菇 +俎 +薪 +峦 +秭 +蟹 +历 +盟 +菠 +寡 +液 +肢 +喻 +染 +裱 +悱 +抱 +氙 +赤 +捅 +猛 +跑 +氮 +谣 +仁 +尺 +辊 +窍 +烙 +衍 +架 +擦 +倏 +璐 +瑁 +币 +楞 +胖 +夔 +趸 +邛 +惴 +饕 +虔 +蝎 +§ +哉 +贝 +宽 +辫 +炮 +扩 +饲 +籽 +魏 +菟 +锰 +伍 +猝 +末 +琳 +哚 +蛎 +邂 +呀 +姿 +鄞 +却 +歧 +仙 +恸 +椐 +森 +牒 +寤 +袒 +婆 +虢 +雅 +钉 +朵 +贼 +欲 +苞 +寰 +故 +龚 +坭 +嘘 +咫 +礼 +硷 +兀 +睢 +汶 +’ +铲 +烧 +绕 +诃 +浃 +钿 +哺 +柜 +讼 +颊 +璁 +腔 +洽 +咐 +脲 +簌 +筠 +镣 +玮 +鞠 +谁 +兼 +姆 +挥 +梯 +蝴 +谘 +漕 +刷 +躏 +宦 +弼 +b +垌 +劈 +麟 +莉 +揭 +笙 +渎 +仕 +嗤 +仓 +配 +怏 +抬 +错 +泯 +镊 +孰 +猿 +邪 +仍 +秋 +鼬 +壹 +歇 +吵 +炼 +< +尧 +射 +柬 +廷 +胧 +霾 +凳 +隋 +肚 +浮 +梦 +祥 +株 +堵 +退 +L +鹫 +跎 +凶 +毽 +荟 +炫 +栩 +玳 +甜 +沂 +鹿 +顽 +伯 +爹 +赔 +蛴 +徐 +匡 +欣 +狰 +缸 +雹 +蟆 +疤 +默 +沤 +啜 +痂 +衣 +禅 +w +i +h +辽 +葳 +黝 +钗 +停 +沽 +棒 +馨 +颌 +肉 +吴 +硫 +悯 +劾 +娈 +马 +啧 +吊 +悌 +镑 +峭 +帆 +瀣 +涉 +咸 +疸 +滋 +泣 +翦 +拙 +癸 +钥 +蜒 ++ +尾 +庄 +凝 +泉 +婢 +渴 +谊 +乞 +陆 +锉 +糊 +鸦 +淮 +I +B +N +晦 +弗 +乔 +庥 +葡 +尻 +席 +橡 +傣 +渣 +拿 +惩 +麋 +斛 +缃 +矮 +蛏 +岘 +鸽 +姐 +膏 +催 +奔 +镒 +喱 +蠡 +摧 +钯 +胤 +柠 +拐 +璋 +鸥 +卢 +荡 +倾 +^ +_ +珀 +逄 +萧 +塾 +掇 +贮 +笆 +聂 +圃 +冲 +嵬 +M +滔 +笕 +值 +炙 +偶 +蜱 +搐 +梆 +汪 +蔬 +腑 +鸯 +蹇 +敞 +绯 +仨 +祯 +谆 +梧 +糗 +鑫 +啸 +豺 +囹 +猾 +巢 +柄 +瀛 +筑 +踌 +沭 +暗 +苁 +鱿 +蹉 +脂 +蘖 +牢 +热 +木 +吸 +溃 +宠 +序 +泞 +偿 +拜 +檩 +厚 +朐 +毗 +螳 +吞 +媚 +朽 +担 +蝗 +橘 +畴 +祈 +糟 +盱 +隼 +郜 +惜 +珠 +裨 +铵 +焙 +琚 +唯 +咚 +噪 +骊 +丫 +滢 +勤 +棉 +呸 +咣 +淀 +隔 +蕾 +窈 +饨 +挨 +煅 +短 +匙 +粕 +镜 +赣 +撕 +墩 +酬 +馁 +豌 +颐 +抗 +酣 +氓 +佑 +搁 +哭 +递 +耷 +涡 +桃 +贻 +碣 +截 +瘦 +昭 +镌 +蔓 +氚 +甲 +猕 +蕴 +蓬 +散 +拾 +纛 +狼 +猷 +铎 +埋 +旖 +矾 +讳 +囊 +糜 +迈 +粟 +蚂 +紧 +鲳 +瘢 +栽 +稼 +羊 +锄 +斟 +睁 +桥 +瓮 +蹙 +祉 +醺 +鼻 +昱 +剃 +跳 +篱 +跷 +蒜 +翎 +宅 +晖 +嗑 +壑 +峻 +癫 +屏 +狠 +陋 +袜 +途 +憎 +祀 +莹 +滟 +佶 +溥 +臣 +约 +盛 +峰 +磁 +慵 +婪 +拦 +莅 +朕 +鹦 +粲 +裤 +哎 +疡 +嫖 +琵 +窟 +堪 +谛 +嘉 +儡 +鳝 +斩 +郾 +驸 +酊 +妄 +胜 +贺 +徙 +傅 +噌 +钢 +栅 +庇 +恋 +匝 +巯 +邈 +尸 +锚 +粗 +佟 +蛟 +薹 +纵 +蚊 +郅 +绢 +锐 +苗 +俞 +篆 +淆 +膀 +鲜 +煎 +诶 +秽 +寻 +涮 +刺 +怀 +噶 +巨 +褰 +魅 +灶 +灌 +桉 +藕 +谜 +舸 +薄 +搀 +恽 +借 +牯 +痉 +渥 +愿 +亓 +耘 +杠 +柩 +锔 +蚶 +钣 +珈 +喘 +蹒 +幽 +赐 +稗 +晤 +莱 +泔 +扯 +肯 +菪 +裆 +腩 +豉 +疆 +骜 +腐 +倭 +珏 +唔 +粮 +亡 +润 +慰 +伽 +橄 +玄 +誉 +醐 +胆 +龊 +粼 +塬 +陇 +彼 +削 +嗣 +绾 +芽 +妗 +垭 +瘴 +爽 +薏 +寨 +龈 +泠 +弹 +赢 +漪 +猫 +嘧 +涂 +恤 +圭 +茧 +烽 +屑 +痕 +巾 +赖 +荸 +凰 +腮 +畈 +亵 +蹲 +偃 +苇 +澜 +艮 +换 +骺 +烘 +苕 +梓 +颉 +肇 +哗 +悄 +氤 +涠 +葬 +屠 +鹭 +植 +竺 +佯 +诣 +鲇 +瘀 +鲅 +邦 +移 +滁 +冯 +耕 +癔 +戌 +茬 +沁 +巩 +悠 +湘 +洪 +痹 +锟 +循 +谋 +腕 +鳃 +钠 +捞 +焉 +迎 +碱 +伫 +急 +榷 +奈 +邝 +卯 +辄 +皲 +卟 +醛 +畹 +忧 +稳 +雄 +昼 +缩 +阈 +睑 +扌 +耗 +曦 +涅 +捏 +瞧 +邕 +淖 +漉 +铝 +耦 +禹 +湛 +喽 +莼 +琅 +诸 +苎 +纂 +硅 +始 +嗨 +傥 +燃 +臂 +赅 +嘈 +呆 +贵 +屹 +壮 +肋 +亍 +蚀 +卅 +豹 +腆 +邬 +迭 +浊 +} +童 +螂 +捐 +圩 +勐 +触 +寞 +汊 +壤 +荫 +膺 +渌 +芳 +懿 +遴 +螈 +泰 +蓼 +蛤 +茜 +舅 +枫 +朔 +膝 +眙 +避 +梅 +判 +鹜 +璜 +牍 +缅 +垫 +藻 +黔 +侥 +惚 +懂 +踩 +腰 +腈 +札 +丞 +唾 +慈 +顿 +摹 +荻 +琬 +~ +斧 +沈 +滂 +胁 +胀 +幄 +莜 +Z +匀 +鄄 +掌 +绰 +茎 +焚 +赋 +萱 +谑 +汁 +铒 +瞎 +夺 +蜗 +野 +娆 +冀 +弯 +篁 +懵 +灞 +隽 +芡 +脘 +俐 +辩 +芯 +掺 +喏 +膈 +蝈 +觐 +悚 +踹 +蔗 +熠 +鼠 +呵 +抓 +橼 +峨 +畜 +缔 +禾 +崭 +弃 +熊 +摒 +凸 +拗 +穹 +蒙 +抒 +祛 +劝 +闫 +扳 +阵 +醌 +踪 +喵 +侣 +搬 +仅 +荧 +赎 +蝾 +琦 +买 +婧 +瞄 +寓 +皎 +冻 +赝 +箩 +莫 +瞰 +郊 +笫 +姝 +筒 +枪 +遣 +煸 +袋 +舆 +痱 +涛 +母 +〇 +启 +践 +耙 +绲 +盘 +遂 +昊 +搞 +槿 +诬 +纰 +泓 +惨 +檬 +亻 +越 +C +o +憩 +熵 +祷 +钒 +暧 +塔 +阗 +胰 +咄 +娶 +魔 +琶 +钞 +邻 +扬 +杉 +殴 +咽 +弓 +〆 +髻 +】 +吭 +揽 +霆 +拄 +殖 +脆 +彻 +岩 +芝 +勃 +辣 +剌 +钝 +嘎 +甄 +佘 +皖 +伦 +授 +徕 +憔 +挪 +皇 +庞 +稔 +芜 +踏 +溴 +兖 +卒 +擢 +饥 +鳞 +煲 +‰ +账 +颗 +叻 +斯 +捧 +鳍 +琮 +讹 +蛙 +纽 +谭 +酸 +兔 +莒 +睇 +伟 +觑 +羲 +嗜 +宜 +褐 +旎 +辛 +卦 +诘 +筋 +鎏 +溪 +挛 +熔 +阜 +晰 +鳅 +丢 +奚 +灸 +呱 +献 +陉 +黛 +鸪 +甾 +萨 +疮 +拯 +洲 +疹 +辑 +叙 +恻 +谒 +允 +柔 +烂 +氏 +逅 +漆 +拎 +惋 +扈 +湟 +纭 +啕 +掬 +擞 +哥 +忽 +涤 +鸵 +靡 +郗 +瓷 +扁 +廊 +怨 +雏 +钮 +敦 +E +懦 +憋 +汀 +拚 +啉 +腌 +岸 +f +痼 +瞅 +尊 +咀 +眩 +飙 +忌 +仝 +迦 +熬 +毫 +胯 +篑 +茄 +腺 +凄 +舛 +碴 +锵 +诧 +羯 +後 +漏 +汤 +宓 +仞 +蚁 +壶 +谰 +皑 +铄 +棰 +罔 +辅 +晶 +苦 +牟 +闽 +\ +烃 +饮 +聿 +丙 +蛳 +朱 +煤 +涔 +鳖 +犁 +罐 +荼 +砒 +淦 +妤 +黏 +戎 +孑 +婕 +瑾 +戢 +钵 +枣 +捋 +砥 +衩 +狙 +桠 +稣 +阎 +肃 +梏 +诫 +孪 +昶 +婊 +衫 +嗔 +侃 +塞 +蜃 +樵 +峒 +貌 +屿 +欺 +缫 +阐 +栖 +诟 +珞 +荭 +吝 +萍 +嗽 +恂 +啻 +蜴 +磬 +峋 +俸 +豫 +谎 +徊 +镍 +韬 +魇 +晴 +U +囟 +猜 +蛮 +坐 +囿 +伴 +亭 +肝 +佗 +蝠 +妃 +胞 +滩 +榴 +氖 +垩 +苋 +砣 +扪 +馏 +姓 +轩 +厉 +夥 +侈 +禀 +垒 +岑 +赏 +钛 +辐 +痔 +披 +纸 +碳 +“ +坞 +蠓 +挤 +荥 +沅 +悔 +铧 +帼 +蒌 +蝇 +a +p +y +n +g +哀 +浆 +瑶 +凿 +桶 +馈 +皮 +奴 +苜 +佤 +伶 +晗 +铱 +炬 +优 +弊 +氢 +恃 +甫 +攥 +端 +锌 +灰 +稹 +炝 +曙 +邋 +亥 +眶 +碾 +拉 +萝 +绔 +捷 +浍 +腋 +姑 +菖 +凌 +涞 +麽 +锢 +桨 +潢 +绎 +镰 +殆 +锑 +渝 +铬 +困 +绽 +觎 +匈 +糙 +暑 +裹 +鸟 +盔 +肽 +迷 +綦 +『 +亳 +佝 +俘 +钴 +觇 +骥 +仆 +疝 +跪 +婶 +郯 +瀹 +唉 +脖 +踞 +针 +晾 +忒 +扼 +瞩 +叛 +椒 +疟 +嗡 +邗 +肆 +跆 +玫 +忡 +捣 +咧 +唆 +艄 +蘑 +潦 +笛 +阚 +沸 +泻 +掊 +菽 +贫 +斥 +髂 +孢 +镂 +赂 +麝 +鸾 +屡 +衬 +苷 +恪 +叠 +希 +粤 +爻 +喝 +茫 +惬 +郸 +绻 +庸 +撅 +碟 +宄 +妹 +膛 +叮 +饵 +崛 +嗲 +椅 +冤 +搅 +咕 +敛 +尹 +垦 +闷 +蝉 +霎 +勰 +败 +蓑 +泸 +肤 +鹌 +幌 +焦 +浠 +鞍 +刁 +舰 +乙 +竿 +裔 +。 +茵 +函 +伊 +兄 +丨 +娜 +匍 +謇 +莪 +宥 +似 +蝽 +翳 +酪 +翠 +粑 +薇 +祢 +骏 +赠 +叫 +Q +噤 +噻 +竖 +芗 +莠 +潭 +俊 +羿 +耜 +O +郫 +趁 +嗪 +囚 +蹶 +芒 +洁 +笋 +鹑 +敲 +硝 +啶 +堡 +渲 +揩 +』 +携 +宿 +遒 +颍 +扭 +棱 +割 +萜 +蔸 +葵 +琴 +捂 +饰 +衙 +耿 +掠 +募 +岂 +窖 +涟 +蔺 +瘤 +柞 +瞪 +怜 +匹 +距 +楔 +炜 +哆 +秦 +缎 +幼 +茁 +绪 +痨 +恨 +楸 +娅 +瓦 +桩 +雪 +嬴 +伏 +榔 +妥 +铿 +拌 +眠 +雍 +缇 +‘ +卓 +搓 +哌 +觞 +噩 +屈 +哧 +髓 +咦 +巅 +娑 +侑 +淫 +膳 +祝 +勾 +姊 +莴 +胄 +疃 +薛 +蜷 +胛 +巷 +芙 +芋 +熙 +闰 +勿 +窃 +狱 +剩 +钏 +幢 +陟 +铛 +慧 +靴 +耍 +k +浙 +浇 +飨 +惟 +绗 +祜 +澈 +啼 +咪 +磷 +摞 +诅 +郦 +抹 +跃 +壬 +吕 +肖 +琏 +颤 +尴 +剡 +抠 +凋 +赚 +泊 +津 +宕 +殷 +倔 +氲 +漫 +邺 +涎 +怠 +$ +垮 +荬 +遵 +俏 +叹 +噢 +饽 +蜘 +孙 +筵 +疼 +鞭 +羧 +牦 +箭 +潴 +c +眸 +祭 +髯 +啖 +坳 +愁 +芩 +驮 +倡 +巽 +穰 +沃 +胚 +怒 +凤 +槛 +剂 +趵 +嫁 +v +邢 +灯 +鄢 +桐 +睽 +檗 +锯 +槟 +婷 +嵋 +圻 +诗 +蕈 +颠 +遭 +痢 +芸 +怯 +馥 +竭 +锗 +徜 +恭 +遍 +籁 +剑 +嘱 +苡 +龄 +僧 +桑 +潸 +弘 +澶 +楹 +悲 +讫 +愤 +腥 +悸 +谍 +椹 +呢 +桓 +葭 +攫 +阀 +翰 +躲 +敖 +柑 +郎 +笨 +橇 +呃 +魁 +燎 +脓 +葩 +磋 +垛 +玺 +狮 +沓 +砜 +蕊 +锺 +罹 +蕉 +翱 +虐 +闾 +巫 +旦 +茱 +嬷 +枯 +鹏 +贡 +芹 +汛 +矫 +绁 +拣 +禺 +佃 +讣 +舫 +惯 +乳 +趋 +疲 +挽 +岚 +虾 +衾 +蠹 +蹂 +飓 +氦 +铖 +孩 +稞 +瑜 +壅 +掀 +勘 +妓 +畅 +髋 +W +庐 +牲 +蓿 +榕 +练 +垣 +唱 +邸 +菲 +昆 +婺 +穿 +绡 +麒 +蚱 +掂 +愚 +泷 +涪 +漳 +妩 +娉 +榄 +讷 +觅 +旧 +藤 +煮 +呛 +柳 +腓 +叭 +庵 +烷 +阡 +罂 +蜕 +擂 +猖 +咿 +媲 +脉 +【 +沏 +貅 +黠 +熏 +哲 +烁 +坦 +酵 +兜 +× +潇 +撒 +剽 +珩 +圹 +乾 +摸 +樟 +帽 +嗒 +襄 +魂 +轿 +憬 +锡 +〕 +喃 +皆 +咖 +隅 +脸 +残 +泮 +袂 +鹂 +珊 +囤 +捆 +咤 +误 +徨 +闹 +淙 +芊 +淋 +怆 +囗 +拨 +梳 +渤 +R +G +绨 +蚓 +婀 +幡 +狩 +麾 +谢 +唢 +裸 +旌 +伉 +纶 +裂 +驳 +砼 +咛 +澄 +樨 +蹈 +宙 +澍 +倍 +貔 +操 +勇 +蟠 +摈 +砧 +虬 +够 +缁 +悦 +藿 +撸 +艹 +摁 +淹 +豇 +虎 +榭 +ˉ +吱 +d +° +喧 +荀 +踱 +侮 +奋 +偕 +饷 +犍 +惮 +坑 +璎 +徘 +宛 +妆 +袈 +倩 +窦 +昂 +荏 +乖 +K +怅 +撰 +鳙 +牙 +袁 +酞 +X +痿 +琼 +闸 +雁 +趾 +荚 +虻 +涝 +《 +杏 +韭 +偈 +烤 +绫 +鞘 +卉 +症 +遢 +蓥 +诋 +杭 +荨 +匆 +竣 +簪 +辙 +敕 +虞 +丹 +缭 +咩 +黟 +m +淤 +瑕 +咂 +铉 +硼 +茨 +嶂 +痒 +畸 +敬 +涿 +粪 +窘 +熟 +叔 +嫔 +盾 +忱 +裘 +憾 +梵 +赡 +珙 +咯 +娘 +庙 +溯 +胺 +葱 +痪 +摊 +荷 +卞 +乒 +髦 +寐 +铭 +坩 +胗 +枷 +爆 +溟 +嚼 +羚 +砬 +轨 +惊 +挠 +罄 +竽 +菏 +氧 +浅 +楣 +盼 +枢 +炸 +阆 +杯 +谏 +噬 +淇 +渺 +俪 +秆 +墓 +泪 +跻 +砌 +痰 +垡 +渡 +耽 +釜 +讶 +鳎 +煞 +呗 +韶 +舶 +绷 +鹳 +缜 +旷 +铊 +皱 +龌 +檀 +霖 +奄 +槐 +艳 +蝶 +旋 +哝 +赶 +骞 +蚧 +腊 +盈 +丁 +` +蜚 +矸 +蝙 +睨 +嚓 +僻 +鬼 +醴 +夜 +彝 +磊 +笔 +拔 +栀 +糕 +厦 +邰 +纫 +逭 +纤 +眦 +膊 +馍 +躇 +烯 +蘼 +冬 +诤 +暄 +骶 +哑 +瘠 +」 +臊 +丕 +愈 +咱 +螺 +擅 +跋 +搏 +硪 +谄 +笠 +淡 +嘿 +骅 +谧 +鼎 +皋 +姚 +歼 +蠢 +驼 +耳 +胬 +挝 +涯 +狗 +蒽 +孓 +犷 +凉 +芦 +箴 +铤 +孤 +嘛 +坤 +V +茴 +朦 +挞 +尖 +橙 +诞 +搴 +碇 +洵 +浚 +帚 +蜍 +漯 +柘 +嚎 +讽 +芭 +荤 +咻 +祠 +秉 +跖 +埃 +吓 +糯 +眷 +馒 +惹 +娼 +鲑 +嫩 +讴 +轮 +瞥 +靶 +褚 +乏 +缤 +宋 +帧 +删 +驱 +碎 +扑 +俩 +俄 +偏 +涣 +竹 +噱 +皙 +佰 +渚 +唧 +斡 +# +镉 +刀 +崎 +筐 +佣 +夭 +贰 +肴 +峙 +哔 +艿 +匐 +牺 +镛 +缘 +仡 +嫡 +劣 +枸 +堀 +梨 +簿 +鸭 +蒸 +亦 +稽 +浴 +{ +衢 +束 +槲 +j +阁 +揍 +疥 +棋 +潋 +聪 +窜 +乓 +睛 +插 +冉 +阪 +苍 +搽 +「 +蟾 +螟 +幸 +仇 +樽 +撂 +慢 +跤 +幔 +俚 +淅 +覃 +觊 +溶 +妖 +帛 +侨 +曰 +妾 +泗 +· +: +瀘 +風 +Ë +( +) +∶ +紅 +紗 +瑭 +雲 +頭 +鶏 +財 +許 +• +¥ +樂 +焗 +麗 +— +; +滙 +東 +榮 +繪 +興 +… +門 +業 +π +楊 +國 +顧 +é +盤 +寳 +Λ +龍 +鳳 +島 +誌 +緣 +結 +銭 +萬 +勝 +祎 +璟 +優 +歡 +臨 +時 +購 += +★ +藍 +昇 +鐵 +觀 +勅 +農 +聲 +畫 +兿 +術 +發 +劉 +記 +專 +耑 +園 +書 +壴 +種 +Ο +● +褀 +號 +銀 +匯 +敟 +锘 +葉 +橪 +廣 +進 +蒄 +鑽 +阝 +祙 +貢 +鍋 +豊 +夬 +喆 +團 +閣 +開 +燁 +賓 +館 +酡 +沔 +順 ++ +硚 +劵 +饸 +陽 +車 +湓 +復 +萊 +氣 +軒 +華 +堃 +迮 +纟 +戶 +馬 +學 +裡 +電 +嶽 +獨 +マ +シ +サ +ジ +燘 +袪 +環 +❤ +臺 +灣 +専 +賣 +孖 +聖 +攝 +線 +▪ +α +傢 +俬 +夢 +達 +莊 +喬 +貝 +薩 +劍 +羅 +壓 +棛 +饦 +尃 +璈 +囍 +醫 +G +I +A +# +N +鷄 +髙 +嬰 +啓 +約 +隹 +潔 +賴 +藝 +~ +寶 +籣 +麺 +  +嶺 +√ +義 +網 +峩 +長 +∧ +魚 +機 +構 +② +鳯 +偉 +L +B +㙟 +畵 +鴿 +' +詩 +溝 +嚞 +屌 +藔 +佧 +玥 +蘭 +織 +1 +3 +9 +0 +7 +點 +砭 +鴨 +鋪 +銘 +廳 +弍 +‧ +創 +湯 +坶 +℃ +卩 +骝 +& +烜 +荘 +當 +潤 +扞 +係 +懷 +碶 +钅 +蚨 +讠 +☆ +叢 +爲 +埗 +涫 +塗 +→ +楽 +現 +鯨 +愛 +瑪 +鈺 +忄 +悶 +藥 +飾 +樓 +視 +孬 +ㆍ +燚 +苪 +師 +① +丼 +锽 +│ +韓 +標 +è +兒 +閏 +匋 +張 +漢 +Ü +髪 +會 +閑 +檔 +習 +裝 +の +峯 +菘 +輝 +И +雞 +釣 +億 +浐 +K +O +R +8 +H +E +P +T +W +D +S +C +M +F +姌 +饹 +» +晞 +廰 +ä +嵯 +鷹 +負 +飲 +絲 +冚 +楗 +澤 +綫 +區 +❋ +← +質 +靑 +揚 +③ +滬 +統 +産 +協 +﹑ +乸 +畐 +經 +運 +際 +洺 +岽 +為 +粵 +諾 +崋 +豐 +碁 +ɔ +V +2 +6 +齋 +誠 +訂 +´ +勑 +雙 +陳 +無 +í +泩 +媄 +夌 +刂 +i +c +t +o +r +a +嘢 +耄 +燴 +暃 +壽 +媽 +靈 +抻 +體 +唻 +É +冮 +甹 +鎮 +錦 +ʌ +蜛 +蠄 +尓 +駕 +戀 +飬 +逹 +倫 +貴 +極 +Я +Й +寬 +磚 +嶪 +郎 +職 +| +間 +n +d +剎 +伈 +課 +飛 +橋 +瘊 +№ +譜 +骓 +圗 +滘 +縣 +粿 +咅 +養 +濤 +彳 +® +% +Ⅱ +啰 +㴪 +見 +矞 +薬 +糁 +邨 +鲮 +顔 +罱 +З +選 +話 +贏 +氪 +俵 +競 +瑩 +繡 +枱 +β +綉 +á +獅 +爾 +™ +麵 +戋 +淩 +徳 +個 +劇 +場 +務 +簡 +寵 +h +實 +膠 +轱 +圖 +築 +嘣 +樹 +㸃 +營 +耵 +孫 +饃 +鄺 +飯 +麯 +遠 +輸 +坫 +孃 +乚 +閃 +鏢 +㎡ +題 +廠 +關 +↑ +爺 +將 +軍 +連 +篦 +覌 +參 +箸 +- +窠 +棽 +寕 +夀 +爰 +歐 +呙 +閥 +頡 +熱 +雎 +垟 +裟 +凬 +勁 +帑 +馕 +夆 +疌 +枼 +馮 +貨 +蒤 +樸 +彧 +旸 +靜 +龢 +暢 +㐱 +鳥 +珺 +鏡 +灡 +爭 +堷 +廚 +Ó +騰 +診 +┅ +蘇 +褔 +凱 +頂 +豕 +亞 +帥 +嘬 +⊥ +仺 +桖 +複 +饣 +絡 +穂 +顏 +棟 +納 +▏ +濟 +親 +設 +計 +攵 +埌 +烺 +ò +頤 +燦 +蓮 +撻 +節 +講 +濱 +濃 +娽 +洳 +朿 +燈 +鈴 +護 +膚 +铔 +過 +補 +Z +U +5 +4 +坋 +闿 +䖝 +餘 +缐 +铞 +貿 +铪 +桼 +趙 +鍊 +[ +㐂 +垚 +菓 +揸 +捲 +鐘 +滏 +𣇉 +爍 +輪 +燜 +鴻 +鮮 +動 +鹞 +鷗 +丄 +慶 +鉌 +翥 +飮 +腸 +⇋ +漁 +覺 +來 +熘 +昴 +翏 +鲱 +圧 +鄉 +萭 +頔 +爐 +嫚 +г +貭 +類 +聯 +幛 +輕 +訓 +鑒 +夋 +锨 +芃 +珣 +䝉 +扙 +嵐 +銷 +處 +ㄱ +語 +誘 +苝 +歸 +儀 +燒 +楿 +內 +粢 +葒 +奧 +麥 +礻 +滿 +蠔 +穵 +瞭 +態 +鱬 +榞 +硂 +鄭 +黃 +煙 +祐 +奓 +逺 +* +瑄 +獲 +聞 +薦 +讀 +這 +樣 +決 +問 +啟 +們 +執 +説 +轉 +單 +隨 +唘 +帶 +倉 +庫 +還 +贈 +尙 +皺 +■ +餅 +產 +○ +∈ +報 +狀 +楓 +賠 +琯 +嗮 +禮 +` +傳 +> +≤ +嗞 +Φ +≥ +換 +咭 +∣ +↓ +曬 +ε +応 +寫 +″ +終 +様 +純 +費 +療 +聨 +凍 +壐 +郵 +ü +黒 +∫ +製 +塊 +調 +軽 +確 +撃 +級 +馴 +Ⅲ +涇 +繹 +數 +碼 +證 +狒 +処 +劑 +< +晧 +賀 +衆 +] +櫥 +兩 +陰 +絶 +對 +鯉 +憶 +◎ +p +e +Y +蕒 +煖 +頓 +測 +試 +鼽 +僑 +碩 +妝 +帯 +≈ +鐡 +舖 +權 +喫 +倆 +ˋ +該 +悅 +ā +俫 +. +f +s +b +m +k +g +u +j +貼 +淨 +濕 +針 +適 +備 +l +/ +給 +謢 +強 +觸 +衛 +與 +⊙ +$ +緯 +變 +⑴ +⑵ +⑶ +㎏ +殺 +∩ +幚 +─ +價 +▲ +離 +ú +ó +飄 +烏 +関 +閟 +﹝ +﹞ +邏 +輯 +鍵 +驗 +訣 +導 +歷 +屆 +層 +▼ +儱 +錄 +熳 +ē +艦 +吋 +錶 +辧 +飼 +顯 +④ +禦 +販 +気 +対 +枰 +閩 +紀 +幹 +瞓 +貊 +淚 +△ +眞 +墊 +Ω +獻 +褲 +縫 +緑 +亜 +鉅 +餠 +{ +} +◆ +蘆 +薈 +█ +◇ +溫 +彈 +晳 +粧 +犸 +穩 +訊 +崬 +凖 +熥 +П +舊 +條 +紋 +圍 +Ⅳ +筆 +尷 +難 +雜 +錯 +綁 +識 +頰 +鎖 +艶 +□ +殁 +殼 +⑧ +├ +▕ +鵬 +ǐ +ō +ǒ +糝 +綱 +▎ +μ +盜 +饅 +醬 +籤 +蓋 +釀 +鹽 +據 +à +ɡ +辦 +◥ +彐 +┌ +婦 +獸 +鲩 +伱 +ī +蒟 +蒻 +齊 +袆 +腦 +寧 +凈 +妳 +煥 +詢 +偽 +謹 +啫 +鯽 +騷 +鱸 +損 +傷 +鎻 +髮 +買 +冏 +儥 +両 +﹢ +∞ +載 +喰 +z +羙 +悵 +燙 +曉 +員 +組 +徹 +艷 +痠 +鋼 +鼙 +縮 +細 +嚒 +爯 +≠ +維 +" +鱻 +壇 +厍 +帰 +浥 +犇 +薡 +軎 +² +應 +醜 +刪 +緻 +鶴 +賜 +噁 +軌 +尨 +镔 +鷺 +槗 +彌 +葚 +濛 +請 +溇 +緹 +賢 +訪 +獴 +瑅 +資 +縤 +陣 +蕟 +栢 +韻 +祼 +恁 +伢 +謝 +劃 +涑 +總 +衖 +踺 +砋 +凉 +籃 +駿 +苼 +瘋 +昽 +紡 +驊 +腎 +﹗ +響 +杋 +剛 +嚴 +禪 +歓 +槍 +傘 +檸 +檫 +炣 +勢 +鏜 +鎢 +銑 +尐 +減 +奪 +惡 +θ +僮 +婭 +臘 +ū +ì +殻 +鉄 +∑ +蛲 +焼 +緖 +續 +紹 +懮! +䰾 +䲁 +丌 +丏 +丟 +並 +乂 +乗 +乩 +乭 +乹 +亀 +亂 +亅 +亊 +亠 +亰 +亶 +亹 +仂 +仉 +仏 +仛 +仫 +仮 +仳 +仵 +仼 +伃 +伋 +伕 +伝 +伷 +伾 +佀 +佁 +佇 +佈 +佉 +佋 +佔 +併 +佹 +佺 +佾 +侁 +侅 +侊 +侖 +侘 +侚 +侞 +価 +侶 +侷 +侹 +俁 +俅 +俋 +俌 +俍 +俛 +俠 +俳 +俴 +俶 +俽 +倈 +倓 +倖 +倗 +倞 +倢 +倣 +値 +倧 +倮 +倻 +偁 +偊 +偍 +偓 +偪 +偲 +側 +偵 +偸 +傃 +傉 +傑 +傒 +傕 +傖 +傜 +傭 +債 +傾 +僅 +僉 +僊 +働 +僔 +僕 +僖 +僙 +僜 +僡 +僩 +僭 +僰 +僱 +僴 +儁 +儂 +儆 +儇 +儈 +儉 +儐 +儔 +儕 +儘 +儚 +儞 +償 +儦 +儫 +儲 +儷 +儺 +儻 +儼 +兌 +児 +兕 +兗 +兪 +冂 +円 +冇 +冊 +冑 +冖 +冧 +冨 +冪 +冫 +冴 +凃 +凜 +凞 +凪 +凵 +刄 +刎 +別 +刦 +刧 +刼 +則 +剋 +剏 +剝 +剣 +剮 +劄 +劊 +劌 +劔 +劬 +効 +劼 +勔 +勖 +勗 +勛 +勞 +勣 +勦 +勱 +勲 +勳 +勵 +勷 +勸 +勻 +匂 +匄 +匏 +匚 +匱 +匸 +卋 +卍 +卐 +卣 +卬 +卮 +卲 +卹 +卺 +卻 +卽 +厓 +厔 +厙 +厭 +厰 +厲 +厴 +厶 +叄 +収 +叕 +叡 +叵 +吔 +吥 +吳 +吶 +呂 +呉 +呎 +呾 +咁 +咑 +咗 +咘 +咟 +咥 +咲 +咼 +咾 +哂 +哏 +哐 +哖 +哱 +唃 +唄 +唫 +唭 +唵 +唸 +啁 +啍 +啚 +啞 +啣 +啯 +啱 +啲 +啷 +喈 +喚 +喢 +喦 +喪 +喲 +喼 +嗄 +嗆 +嗇 +嗊 +嗎 +嗚 +嗢 +嗩 +嗶 +嗹 +嘅 +嘆 +嘍 +嘏 +嘔 +嘗 +嘚 +嘜 +嘥 +嘩 +嘮 +嘯 +嘰 +嘸 +噍 +噏 +噓 +噝 +噠 +噥 +噦 +噯 +噰 +噲 +噴 +噸 +噹 +嚇 +嚈 +嚐 +嚕 +嚗 +嚙 +嚟 +嚤 +嚦 +嚧 +嚨 +嚩 +嚮 +嚳 +嚶 +嚿 +囀 +囂 +囃 +囉 +囑 +囒 +囓 +囝 +団 +囧 +囪 +囮 +囯 +囲 +図 +囶 +囷 +圂 +圄 +圉 +圏 +圓 +圪 +圯 +坌 +坖 +坣 +坬 +坮 +坵 +垈 +垍 +垕 +垞 +垯 +垰 +垵 +垻 +垿 +埅 +埇 +埈 +埏 +埒 +埜 +埡 +埤 +埧 +埨 +埪 +埮 +埴 +埵 +埻 +埼 +堅 +堈 +堉 +堊 +堍 +堖 +堝 +堦 +堮 +堯 +堺 +塀 +塅 +塆 +塋 +塏 +塙 +塜 +塡 +塢 +塤 +塨 +塩 +塭 +塰 +塱 +塲 +塵 +塹 +塽 +墀 +墎 +増 +墘 +墜 +墡 +墣 +墫 +墬 +墮 +墱 +墳 +墺 +墼 +墾 +壄 +壆 +壋 +壌 +壎 +壔 +壘 +壙 +壞 +壟 +壠 +壢 +壩 +壯 +壱 +壺 +変 +夊 +夠 +夤 +夾 +奀 +奐 +奣 +奩 +奫 +奭 +奮 +妀 +妁 +妏 +妑 +妠 +妧 +妭 +妸 +妺 +姀 +姁 +姃 +姈 +姉 +姍 +姦 +姪 +姫 +姮 +姵 +姶 +姸 +娋 +娍 +娎 +娖 +娛 +娫 +娳 +娸 +婁 +婑 +婯 +婻 +婼 +媃 +媊 +媐 +媓 +媖 +媗 +媜 +媞 +媧 +媭 +媯 +媺 +媼 +媿 +嫄 +嫈 +嫘 +嫪 +嫲 +嫳 +嫵 +嫺 +嫻 +嬅 +嬈 +嬋 +嬌 +嬛 +嬝 +嬡 +嬤 +嬨 +嬪 +嬬 +嬭 +嬸 +嬾 +嬿 +孀 +孆 +孋 +孌 +孮 +孻 +孿 +宍 +実 +宧 +宮 +寀 +寁 +寈 +寊 +寔 +寖 +寗 +寘 +寛 +寜 +寢 +審 +寯 +尋 +尗 +尢 +尪 +屄 +屇 +屍 +屓 +屚 +屜 +屢 +屬 +屭 +屺 +屻 +岀 +岈 +岡 +岣 +岧 +岪 +岬 +岰 +岵 +岻 +峅 +峇 +峍 +峘 +峚 +峠 +峴 +峼 +峽 +崁 +崈 +崍 +崐 +崑 +崒 +崗 +崘 +崙 +崚 +崞 +崟 +崠 +崢 +崱 +崵 +崶 +嵎 +嵒 +嵕 +嵖 +嵗 +嵙 +嵛 +嵜 +嵨 +嵮 +嵰 +嵴 +嵻 +嵿 +嶁 +嶃 +嶄 +嶇 +嶋 +嶌 +嶍 +嶒 +嶔 +嶗 +嶝 +嶠 +嶢 +嶦 +嶧 +嶬 +嶰 +嶲 +嶴 +嶷 +嶸 +嶼 +巂 +巄 +巆 +巋 +巌 +巎 +巑 +巒 +巔 +巖 +巘 +巛 +巰 +巶 +巻 +巿 +帔 +帙 +帡 +帢 +帳 +幀 +幃 +幗 +幟 +幣 +幪 +幫 +幵 +幷 +幾 +庀 +庁 +広 +庢 +庲 +庼 +廁 +廂 +廄 +廆 +廈 +廋 +廌 +廍 +廑 +廔 +廕 +廙 +廝 +廞 +廟 +廡 +廢 +廧 +廨 +廩 +廬 +廱 +廸 +廻 +廼 +弁 +弅 +弇 +弉 +弐 +弒 +弔 +弖 +弢 +弨 +弸 +弾 +彀 +彄 +彅 +彆 +彊 +彎 +彔 +彖 +彘 +彙 +彜 +彞 +彠 +彡 +彣 +彥 +彫 +彿 +徂 +徑 +從 +徠 +徧 +徫 +徬 +徭 +徴 +徸 +忉 +忝 +忞 +忬 +忯 +忳 +怍 +怙 +怛 +怵 +恆 +恊 +恥 +恵 +悆 +悛 +悝 +悞 +悧 +悪 +悰 +悳 +惇 +惔 +惣 +惱 +惲 +愃 +愆 +愍 +愐 +愒 +愔 +愜 +愨 +愭 +愴 +愷 +愼 +愾 +慄 +慘 +慚 +慜 +慟 +慣 +慥 +慮 +慳 +慾 +憂 +憊 +憍 +憐 +憑 +憓 +憕 +憙 +憚 +憤 +憫 +憲 +憺 +憻 +懃 +懇 +懌 +懍 +懐 +懣 +懮 +懲 +懶 +懸 +懺 +懼 +懽 +懾 +戇 +戔 +戕 +戙 +戡 +戥 +戦 +戩 +戰 +戱 +戲 +戸 +戻 +戽 +扆 +扥 +抃 +抇 +抦 +拋 +拏 +拝 +拡 +拺 +挙 +挵 +挹 +挻 +挾 +捒 +捜 +捦 +捨 +捩 +捫 +捭 +捱 +掃 +掄 +掙 +掛 +掞 +掟 +採 +掾 +揀 +揄 +揆 +揔 +揮 +揺 +搖 +搗 +搠 +搢 +搳 +搵 +搶 +搾 +摂 +摜 +摟 +摠 +摭 +摯 +摳 +摴 +摵 +摶 +摺 +摻 +摽 +撈 +撐 +撓 +撖 +撙 +撚 +撣 +撥 +撫 +撲 +撳 +撾 +撿 +擁 +擇 +擊 +擋 +擔 +擠 +擥 +擬 +擯 +擰 +擱 +擲 +擴 +擷 +擺 +擼 +擾 +攏 +攔 +攖 +攜 +攞 +攢 +攣 +攤 +攪 +攬 +攴 +攷 +攽 +敍 +敎 +敔 +敗 +敘 +敫 +敭 +敵 +敻 +敾 +斂 +斃 +斎 +斕 +斖 +斝 +斬 +斷 +斿 +旂 +旃 +旄 +旉 +旙 +旛 +旡 +旲 +旳 +旻 +旼 +旽 +旾 +旿 +昃 +昉 +昍 +昐 +昚 +昛 +昜 +昞 +昡 +昣 +昤 +昪 +昫 +昰 +昺 +晈 +晉 +晊 +晙 +晛 +晝 +晩 +晪 +晫 +晭 +晸 +暅 +暈 +暉 +暊 +暌 +暎 +暏 +暐 +暕 +暘 +暝 +暟 +暠 +暦 +暫 +暱 +暲 +暸 +暻 +暾 +曄 +曅 +曆 +曇 +曌 +曔 +曖 +曠 +曧 +曨 +曩 +曮 +曶 +曷 +曺 +曽 +朊 +朏 +朓 +朖 +朧 +朶 +杁 +杌 +杓 +杙 +杣 +杤 +杧 +杬 +杴 +杻 +杼 +枏 +枖 +枛 +枠 +枡 +枲 +枹 +柁 +柃 +柉 +柊 +柎 +柝 +柟 +柰 +柵 +柶 +柷 +査 +柾 +栃 +栄 +栐 +栒 +栜 +栝 +栞 +栨 +栲 +栴 +栻 +桄 +桕 +桙 +桜 +桝 +桫 +桱 +桲 +桴 +桿 +梀 +梂 +梃 +梉 +梔 +梘 +梟 +梠 +梣 +梫 +梱 +梶 +梽 +棄 +棆 +棐 +棓 +棖 +棗 +棡 +棧 +棨 +棩 +棪 +棫 +棲 +棶 +棹 +棻 +棼 +椆 +椇 +椏 +椙 +椥 +椪 +椲 +椵 +楙 +楡 +楢 +楤 +楧 +楨 +楫 +楮 +楯 +楳 +榊 +榍 +榎 +榑 +榖 +榗 +榘 +榢 +榣 +榤 +榦 +榲 +榿 +槀 +槁 +槃 +槊 +槓 +槔 +槙 +槤 +槩 +槭 +槰 +槱 +槳 +槺 +槻 +槼 +樀 +樁 +樅 +樆 +樋 +樑 +樗 +樘 +樞 +権 +樫 +樺 +樻 +橈 +橐 +橒 +橓 +橚 +橢 +橫 +橿 +檄 +檇 +檉 +檊 +檎 +檜 +檞 +檠 +檡 +檢 +檣 +檦 +檨 +檯 +檳 +檵 +檻 +檽 +櫂 +櫃 +櫆 +櫈 +櫓 +櫚 +櫛 +櫞 +櫟 +櫨 +櫪 +櫱 +櫸 +櫻 +櫾 +櫿 +欄 +欉 +欏 +欒 +欖 +欞 +欥 +欸 +欹 +欽 +歊 +歎 +歛 +歩 +歲 +歳 +歴 +歿 +殂 +殄 +殑 +殘 +殛 +殞 +殟 +殤 +殭 +殮 +殯 +殲 +殳 +毀 +毆 +毉 +毌 +毎 +毐 +毖 +毘 +毬 +毴 +毸 +毿 +氂 +氈 +氍 +氫 +氬 +氷 +氹 +氻 +氾 +汎 +汜 +汧 +汭 +沄 +沆 +沇 +沍 +沒 +沖 +沘 +沚 +沜 +沢 +沨 +沯 +沺 +況 +泂 +泆 +泇 +泐 +泖 +泚 +洌 +洎 +洢 +洣 +洤 +洨 +洩 +洸 +洹 +浄 +浛 +浞 +浟 +浡 +浤 +浯 +浵 +浹 +涙 +涼 +淍 +淎 +淏 +淓 +淛 +淠 +淥 +淪 +淯 +淰 +淵 +淶 +淸 +淺 +淽 +渃 +済 +渉 +渋 +渕 +渙 +渟 +渦 +渫 +渼 +渽 +渾 +湉 +湊 +湔 +湜 +湞 +湣 +湥 +湧 +湳 +湴 +湼 +満 +溁 +溈 +溋 +溎 +準 +溙 +溦 +溲 +溵 +溼 +滀 +滄 +滅 +滈 +滉 +滌 +滎 +滝 +滯 +滲 +滷 +滸 +滹 +滻 +滽 +滾 +漇 +漈 +漎 +漚 +漣 +漬 +漲 +漴 +漵 +漷 +漸 +漼 +漿 +潁 +潑 +潛 +潟 +潯 +潰 +潲 +潽 +潾 +潿 +澀 +澁 +澂 +澆 +澇 +澉 +澋 +澌 +澔 +澗 +澠 +澣 +澥 +澪 +澮 +澯 +澱 +澻 +濁 +濊 +濋 +濘 +濙 +濫 +濬 +濰 +濲 +濶 +濺 +濼 +濾 +瀁 +瀅 +瀆 +瀉 +瀍 +瀏 +瀔 +瀕 +瀝 +瀞 +瀟 +瀠 +瀦 +瀧 +瀨 +瀬 +瀰 +瀲 +瀴 +瀶 +瀾 +灃 +灊 +灑 +灘 +灝 +灤 +灧 +灴 +災 +炁 +炆 +炘 +炟 +炤 +炱 +炲 +炷 +炻 +烉 +烋 +烒 +烔 +烝 +烱 +烴 +焃 +焄 +焌 +焓 +焜 +焞 +焴 +焻 +焿 +煇 +煉 +煐 +煒 +煔 +煕 +煚 +煠 +煩 +煬 +煳 +煵 +煶 +熅 +熇 +熈 +熒 +熖 +熗 +熜 +熤 +熯 +熲 +熺 +熼 +熾 +熿 +燄 +燉 +燊 +燏 +燐 +燔 +燝 +燫 +燬 +燭 +燹 +燻 +燼 +燾 +燿 +爀 +爌 +爔 +爚 +爛 +爝 +爿 +牁 +牂 +牆 +牕 +牖 +牘 +牝 +牠 +牻 +牼 +牽 +犂 +犎 +犖 +犛 +犢 +犧 +犨 +犰 +犴 +犽 +狎 +狓 +狛 +狟 +狦 +狨 +狳 +狶 +狷 +狹 +狻 +猁 +猄 +猇 +猊 +猙 +猞 +猢 +猨 +猳 +猶 +猺 +猻 +獁 +獃 +獄 +獇 +獎 +獏 +獢 +獣 +獬 +獮 +獯 +獰 +獵 +獷 +獺 +獼 +獾 +玀 +玆 +玎 +玏 +玓 +玕 +玗 +玘 +玙 +玠 +玡 +玢 +玧 +玨 +玭 +玶 +玹 +玾 +珅 +珌 +珎 +珖 +珝 +珡 +珤 +珦 +珧 +珪 +珮 +珵 +珹 +珽 +琁 +琄 +琇 +琍 +琎 +琡 +琤 +琱 +琹 +琺 +琿 +瑀 +瑂 +瑆 +瑈 +瑊 +瑋 +瑑 +瑒 +瑝 +瑠 +瑢 +瑣 +瑤 +瑥 +瑧 +瑨 +瑯 +瑱 +瑳 +瑴 +瑺 +璄 +璆 +璉 +璌 +璕 +璘 +璙 +璚 +璠 +璡 +璣 +璥 +璦 +璪 +璫 +璬 +璮 +璱 +璵 +璸 +璹 +璽 +璿 +瓈 +瓊 +瓌 +瓏 +瓑 +瓔 +瓖 +瓘 +瓚 +瓛 +瓞 +甂 +甌 +甍 +甑 +甕 +甡 +甦 +甪 +畀 +畇 +畊 +畋 +畎 +畑 +畝 +畠 +畢 +畧 +畬 +畯 +異 +畳 +畷 +疇 +疊 +疋 +疍 +疒 +疕 +痍 +痙 +痟 +痩 +痲 +痺 +瘍 +瘓 +瘜 +瘞 +瘡 +瘧 +瘰 +瘺 +癀 +癆 +癇 +癒 +癘 +癟 +癡 +癢 +癤 +癥 +癩 +癬 +癭 +癮 +癯 +癰 +癱 +癲 +発 +皐 +皚 +皛 +皝 +皞 +皰 +皷 +皸 +盃 +盋 +盌 +盞 +盡 +監 +盦 +盧 +盨 +盩 +盪 +盫 +盷 +盺 +眀 +県 +眛 +眜 +眥 +眵 +眾 +睜 +睞 +睥 +睪 +睭 +睺 +瞋 +瞞 +瞢 +瞫 +瞼 +瞽 +矇 +矍 +矚 +矧 +矯 +砢 +砩 +砫 +砮 +砯 +砲 +砳 +砵 +硃 +硇 +硏 +硐 +硓 +硜 +硤 +硨 +硭 +硯 +碕 +碡 +碪 +碭 +碸 +碻 +碽 +磔 +磘 +磙 +磜 +磡 +磪 +磯 +磱 +磲 +磵 +磻 +磾 +礄 +礎 +礐 +礑 +礒 +礙 +礠 +礦 +礪 +礫 +礬 +礮 +礱 +礽 +祂 +祆 +祇 +祋 +祏 +祓 +祕 +祧 +祹 +祿 +禃 +禇 +禍 +禎 +禑 +禓 +禔 +禕 +禘 +禛 +禟 +禠 +禤 +禨 +禩 +禰 +禱 +禵 +禼 +禿 +秈 +秠 +秳 +稅 +稈 +稉 +稑 +稘 +稙 +稜 +稟 +稱 +稲 +稺 +稾 +穀 +穈 +穉 +穌 +積 +穎 +穟 +穠 +穡 +穢 +穣 +穫 +窅 +窋 +窣 +窩 +窪 +窮 +窯 +窰 +窶 +窺 +竄 +竅 +竇 +竈 +竊 +竑 +竜 +竦 +竩 +竻 +笄 +笘 +笞 +笥 +笩 +笪 +笭 +笮 +笯 +笱 +笳 +笹 +筅 +筊 +筌 +筍 +筘 +筥 +筦 +筧 +筬 +筭 +筲 +筳 +筶 +筻 +箆 +箇 +箋 +箏 +箑 +箒 +箜 +範 +篊 +篋 +篌 +篔 +篠 +篤 +篥 +篩 +篭 +篯 +篳 +簀 +簃 +簉 +簍 +簑 +簕 +簗 +簞 +簠 +簫 +簷 +簹 +簺 +簽 +簾 +籀 +籌 +籐 +籙 +籛 +籜 +籝 +籟 +籠 +籥 +籪 +籬 +籮 +籲 +籾 +粄 +粍 +粦 +粩 +糀 +糌 +糎 +糞 +糢 +糧 +糬 +糰 +糴 +糶 +糸 +糹 +糺 +糾 +紂 +紆 +紇 +紈 +紉 +紐 +紑 +紓 +紕 +紘 +紙 +紛 +紜 +紝 +紞 +紮 +紱 +紲 +紳 +紵 +紺 +紿 +絃 +絆 +経 +絎 +絕 +絛 +絜 +絞 +絢 +絨 +絪 +絳 +絵 +絹 +絺 +綃 +綈 +綎 +綏 +綖 +継 +続 +綜 +綝 +綞 +綠 +綢 +綣 +綧 +綬 +綮 +綰 +綳 +綴 +綸 +綺 +綻 +綽 +綾 +綿 +緁 +緃 +緄 +緈 +緊 +緋 +総 +緒 +緘 +緜 +緝 +緞 +締 +緡 +緤 +編 +緩 +緬 +緱 +緲 +練 +縂 +縄 +縈 +縉 +縊 +縕 +縛 +縝 +縞 +縠 +縡 +縯 +縱 +縴 +縵 +縷 +縹 +縻 +績 +繃 +繆 +繇 +繒 +繕 +繖 +繙 +繚 +繞 +繩 +繫 +繭 +繰 +繳 +繻 +繼 +繽 +繾 +纁 +纈 +纍 +纏 +纓 +纔 +纕 +纖 +纘 +纜 +缶 +缽 +罃 +罅 +罈 +罉 +罌 +罍 +罟 +罨 +罰 +罳 +罵 +罶 +罷 +罽 +羂 +羆 +羈 +羋 +羕 +羗 +羣 +羥 +羨 +羱 +翀 +翂 +翃 +翕 +翙 +翜 +翬 +翮 +翹 +耎 +耔 +耨 +耬 +聃 +聒 +聟 +聰 +聱 +聳 +聴 +聶 +聽 +聾 +肅 +肏 +肜 +肫 +肸 +肹 +胂 +胅 +胇 +胊 +胙 +胝 +胼 +脅 +脇 +脈 +脛 +脣 +脩 +脫 +脬 +脭 +脳 +脷 +脹 +腧 +腫 +腳 +膂 +膣 +膥 +膩 +膮 +膽 +膾 +膿 +臉 +臍 +臏 +臚 +臞 +臟 +臠 +臯 +舂 +舉 +舎 +舘 +舢 +舥 +舨 +舩 +舲 +舺 +艅 +艉 +艋 +艎 +艏 +艔 +艙 +艚 +艱 +艸 +艽 +芑 +芛 +芨 +芴 +芻 +苅 +苤 +苧 +苳 +苺 +苻 +苾 +茀 +茇 +茈 +茘 +茚 +茛 +茝 +茮 +茲 +茷 +茺 +荅 +荇 +荊 +荎 +荖 +荳 +莕 +莖 +莙 +莛 +莢 +莧 +莩 +莿 +菈 +菉 +菍 +菑 +菔 +菝 +菥 +菫 +菰 +菴 +菶 +菸 +菹 +菺 +菼 +菾 +萇 +萐 +萠 +萡 +萣 +萩 +萵 +萹 +葃 +葊 +葎 +葙 +葜 +葝 +葦 +葯 +葰 +葶 +葷 +蒍 +蒎 +蒐 +蒓 +蒔 +蒗 +蒞 +蒢 +蒧 +蒨 +蒭 +蒯 +蒴 +蒹 +蒺 +蒼 +蒾 +蓀 +蓁 +蓂 +蓆 +蓍 +蓘 +蓚 +蓧 +蓨 +蓪 +蓭 +蓯 +蓳 +蓽 +蔆 +蔎 +蔔 +蔕 +蔘 +蔝 +蔞 +蔣 +蔥 +蔦 +蔭 +蔴 +蔵 +蕁 +蕅 +蕎 +蕑 +蕖 +蕘 +蕚 +蕡 +蕢 +蕩 +蕪 +蕭 +蕷 +蕺 +蕻 +薀 +薆 +薊 +薌 +薐 +薑 +薔 +薗 +薘 +薙 +薜 +薞 +薟 +薨 +薫 +薲 +薷 +薸 +薺 +薾 +薿 +藎 +藟 +藦 +藨 +藪 +藶 +藸 +藹 +藺 +蘂 +蘄 +蘅 +蘊 +蘋 +蘐 +蘓 +蘗 +蘘 +蘚 +蘞 +蘢 +蘧 +蘩 +蘵 +蘶 +蘿 +虉 +虓 +虖 +虛 +虜 +虧 +虨 +虯 +虵 +虺 +蚆 +蚋 +蚍 +蚖 +蚡 +蚢 +蚵 +蚺 +蚼 +蛄 +蛉 +蛍 +蛑 +蛞 +蛯 +蛸 +蛺 +蛻 +蜆 +蜉 +蜑 +蜞 +蜢 +蜣 +蜨 +蜮 +蜯 +蜾 +蝀 +蝍 +蝓 +蝕 +蝘 +蝚 +蝟 +蝣 +蝤 +蝦 +蝨 +蝮 +蝯 +蝰 +蝲 +蝸 +螄 +螅 +螋 +螐 +螔 +螞 +螠 +螢 +螣 +螥 +螫 +螭 +螶 +螻 +螽 +螾 +蟄 +蟅 +蟊 +蟌 +蟎 +蟜 +蟥 +蟪 +蟫 +蟬 +蟯 +蟲 +蟳 +蟴 +蟶 +蟻 +蠂 +蠃 +蠅 +蠆 +蠊 +蠋 +蠍 +蠐 +蠑 +蠘 +蠙 +蠟 +蠣 +蠱 +蠲 +蠵 +蠶 +蠷 +蠻 +衂 +衎 +衕 +衚 +衜 +衝 +衞 +衽 +袓 +袛 +袞 +袴 +袾 +裊 +裎 +裒 +裖 +裬 +裵 +裾 +裿 +褌 +褍 +褎 +褘 +褙 +褞 +褧 +褫 +褭 +褸 +褻 +襌 +襖 +襞 +襠 +襤 +襦 +襪 +襯 +襲 +襴 +襶 +襻 +襾 +覇 +覈 +規 +覓 +覚 +覡 +覦 +覧 +覬 +覲 +観 +覽 +覿 +觔 +觙 +觚 +觜 +觭 +觱 +觴 +觶 +觿 +訁 +訃 +訇 +訌 +討 +訏 +訐 +訒 +訔 +訕 +訖 +託 +訛 +訝 +訟 +訥 +訴 +訶 +註 +証 +詁 +詆 +詈 +詐 +詒 +詔 +評 +詛 +詞 +詠 +詡 +詣 +詥 +詧 +詫 +詭 +詮 +詰 +詳 +詵 +詼 +誄 +誅 +誇 +認 +誒 +誕 +誡 +誣 +誤 +誥 +誦 +誨 +說 +読 +誰 +誴 +誹 +誼 +誾 +談 +諍 +諏 +諒 +論 +諗 +諜 +諟 +諠 +諡 +諤 +諦 +諧 +諪 +諫 +諭 +諮 +諱 +諲 +諳 +諴 +諶 +諷 +諸 +諺 +諼 +謀 +謁 +謂 +謄 +謊 +謌 +謎 +謏 +謐 +謔 +謖 +謗 +謙 +謚 +謜 +謠 +謤 +謨 +謩 +謫 +謬 +謳 +謾 +譏 +譓 +譔 +譙 +譚 +譞 +譫 +譭 +譯 +議 +譲 +譳 +譴 +譽 +譿 +讃 +讌 +讎 +讓 +讖 +讙 +讚 +讜 +讞 +谿 +豈 +豎 +豔 +豢 +豨 +豬 +豳 +豸 +豿 +貐 +貒 +貓 +貘 +貞 +貤 +貧 +貪 +貫 +責 +貮 +貯 +貲 +貳 +貶 +貸 +貺 +貽 +賁 +賂 +賃 +賄 +賈 +賊 +賑 +賒 +賔 +賕 +賚 +賞 +賡 +賤 +賦 +賨 +賬 +賭 +賹 +賺 +賻 +賽 +賾 +贄 +贅 +贇 +贊 +贌 +贍 +贓 +贔 +贖 +贛 +赧 +赬 +趐 +趕 +趖 +趨 +趺 +趼 +跅 +跏 +跗 +跡 +跣 +跩 +踎 +踐 +踰 +踴 +蹕 +蹟 +蹠 +蹤 +蹯 +蹺 +蹻 +躂 +躄 +躉 +躋 +躍 +躑 +躒 +躔 +躝 +躪 +躰 +軀 +軋 +軔 +軛 +軟 +転 +軫 +軲 +軸 +軹 +軺 +軻 +軼 +軾 +較 +輄 +輅 +輋 +輒 +輓 +輔 +輛 +輞 +輟 +輥 +輦 +輩 +輬 +輭 +輶 +輻 +輾 +輿 +轀 +轂 +轄 +轅 +轆 +轍 +轎 +轘 +轝 +轟 +轤 +辭 +辮 +辯 +辵 +辺 +辻 +込 +迴 +迵 +迺 +逈 +逋 +逌 +逎 +逕 +逖 +逤 +逨 +逴 +遄 +遊 +違 +遘 +遙 +遜 +遞 +遯 +遲 +遶 +遷 +遹 +遺 +遼 +邁 +邇 +邉 +邊 +邙 +邠 +邲 +邽 +邾 +郃 +郄 +郇 +郋 +郞 +郟 +郤 +郪 +郳 +郷 +郿 +鄃 +鄆 +鄋 +鄑 +鄒 +鄔 +鄖 +鄗 +鄘 +鄚 +鄜 +鄠 +鄤 +鄧 +鄩 +鄫 +鄰 +鄲 +鄳 +鄴 +酃 +酆 +酈 +酎 +酏 +酔 +酢 +酩 +酴 +酺 +酼 +醁 +醂 +醃 +醅 +醞 +醢 +醣 +醮 +醯 +醾 +醿 +釁 +釆 +釋 +釐 +釒 +釓 +釔 +釕 +釗 +釘 +釙 +釚 +釤 +釦 +釧 +釩 +釪 +釭 +釴 +釵 +釷 +釹 +釺 +鈀 +鈁 +鈄 +鈇 +鈈 +鈉 +鈊 +鈍 +鈏 +鈐 +鈑 +鈔 +鈕 +鈖 +鈞 +鈢 +鈣 +鈥 +鈦 +鈫 +鈮 +鈰 +鈳 +鈷 +鈸 +鈹 +鈾 +鈿 +鉀 +鉆 +鉈 +鉉 +鉋 +鉍 +鉏 +鉑 +鉓 +鉗 +鉚 +鉛 +鉞 +鉟 +鉤 +鉦 +鉬 +鉭 +鉲 +鉶 +鉷 +鉸 +鉻 +鉾 +鉿 +銂 +銃 +銅 +銋 +銍 +銓 +銕 +銖 +銚 +銜 +銠 +銣 +銥 +銦 +銨 +銩 +銪 +銫 +銬 +銱 +銲 +銳 +銶 +銹 +銻 +銼 +銾 +鋁 +鋅 +鋆 +鋇 +鋌 +鋏 +鋐 +鋒 +鋕 +鋗 +鋙 +鋡 +鋤 +鋥 +鋦 +鋨 +鋮 +鋯 +鋰 +鋱 +鋳 +鋶 +鋸 +鋹 +錀 +錏 +錐 +錒 +錕 +錘 +錚 +錞 +錟 +錠 +錡 +錢 +錨 +錫 +錬 +錮 +錳 +錸 +錻 +鍀 +鍇 +鍈 +鍉 +鍍 +鍏 +鍔 +鍘 +鍛 +鍝 +鍟 +鍠 +鍥 +鍩 +鍬 +鍱 +鍳 +鍶 +鍷 +鍺 +鍼 +鍾 +鎂 +鎅 +鎊 +鎌 +鎓 +鎔 +鎗 +鎘 +鎚 +鎛 +鎣 +鎦 +鎧 +鎪 +鎬 +鎭 +鎰 +鎳 +鎵 +鏃 +鏇 +鏈 +鏊 +鏌 +鏐 +鏑 +鏓 +鏗 +鏘 +鏝 +鏞 +鏟 +鏤 +鏦 +鏳 +鏴 +鏵 +鏷 +鏻 +鏽 +鐃 +鐇 +鐈 +鐓 +鐔 +鐙 +鐠 +鐤 +鐦 +鐧 +鐫 +鐬 +鐭 +鐮 +鐲 +鐳 +鐸 +鐺 +鐽 +鐿 +鑀 +鑁 +鑂 +鑄 +鑅 +鑊 +鑌 +鑑 +鑛 +鑠 +鑣 +鑨 +鑪 +鑭 +鑰 +鑲 +鑴 +鑷 +鑼 +鑾 +鑿 +閂 +閆 +閉 +閎 +閒 +閔 +閘 +閜 +閞 +閦 +閨 +閬 +閭 +閰 +閱 +閶 +閹 +閻 +閼 +閾 +閿 +闆 +闇 +闈 +闊 +闋 +闌 +闍 +闐 +闓 +闔 +闕 +闖 +闘 +闞 +闡 +闢 +闥 +阭 +阯 +陁 +陔 +陘 +陜 +陝 +陞 +陬 +陸 +険 +隄 +隈 +隊 +階 +隕 +隣 +險 +隰 +隱 +隲 +隳 +隴 +隷 +隸 +隻 +雋 +雑 +雖 +雛 +雝 +雩 +雫 +雱 +霅 +霈 +霊 +霑 +霙 +霤 +霧 +霨 +霶 +霽 +靁 +靂 +靄 +靉 +靚 +靫 +靬 +靭 +靺 +靼 +鞆 +鞏 +鞞 +鞥 +鞦 +鞨 +鞮 +鞴 +韁 +韃 +韆 +韋 +韌 +韑 +韙 +韜 +韞 +韠 +韡 +韮 +韺 +韾 +頁 +頃 +項 +須 +頊 +頌 +頍 +頎 +頏 +預 +頑 +頒 +頗 +領 +頜 +頠 +頦 +頫 +頴 +頵 +頷 +頸 +頹 +頻 +頼 +顆 +額 +顎 +顒 +顓 +顕 +顗 +願 +顙 +顛 +顥 +顫 +顰 +顱 +顳 +顴 +颮 +颯 +颱 +颶 +颺 +颼 +飆 +飈 +飠 +飡 +飢 +飥 +飩 +飪 +飫 +飭 +飴 +飽 +餃 +餄 +餉 +餌 +餎 +餒 +餓 +餗 +餚 +餛 +餞 +餡 +餵 +餺 +餾 +餿 +饋 +饌 +饑 +饒 +饗 +饞 +饟 +饢 +馘 +馛 +馦 +馭 +馯 +馱 +馳 +馼 +駁 +駄 +駅 +駆 +駐 +駑 +駒 +駔 +駘 +駙 +駛 +駝 +駟 +駢 +駭 +駰 +駱 +騁 +騂 +騄 +騅 +騋 +騎 +騏 +験 +騖 +騙 +騤 +騨 +騫 +騭 +騮 +騶 +騾 +驁 +驃 +驄 +驅 +驌 +驍 +驎 +驒 +驕 +驚 +驛 +驟 +驢 +驤 +驥 +驩 +驪 +骯 +髀 +髎 +髏 +髑 +髒 +髡 +髭 +髲 +髷 +髹 +鬄 +鬅 +鬆 +鬍 +鬚 +鬢 +鬥 +鬧 +鬨 +鬩 +鬪 +鬬 +鬮 +鬯 +鬱 +鬹 +鬻 +魃 +魈 +魋 +魍 +魎 +魕 +魘 +魛 +魞 +魟 +魣 +魨 +魩 +魮 +魯 +魴 +魷 +鮀 +鮁 +鮃 +鮄 +鮊 +鮋 +鮍 +鮐 +鮑 +鮒 +鮓 +鮗 +鮜 +鮟 +鮠 +鮡 +鮣 +鮨 +鮪 +鮫 +鮭 +鮰 +鮸 +鮹 +鮻 +鯀 +鯁 +鯃 +鯇 +鯊 +鯏 +鯒 +鯓 +鯔 +鯕 +鯖 +鯗 +鯙 +鯛 +鯡 +鯢 +鯤 +鯧 +鯪 +鯭 +鯮 +鯰 +鯶 +鯷 +鯻 +鯿 +鰂 +鰃 +鰆 +鰈 +鰉 +鰍 +鰏 +鰒 +鰓 +鰕 +鰗 +鰛 +鰜 +鰟 +鰣 +鰤 +鰧 +鰨 +鰩 +鰭 +鰮 +鰱 +鰲 +鰳 +鰶 +鰷 +鰹 +鰺 +鰻 +鰼 +鰾 +鱀 +鱂 +鱅 +鱇 +鱈 +鱉 +鱊 +鱒 +鱓 +鱔 +鱖 +鱗 +鱘 +鱚 +鱝 +鱟 +鱠 +鱣 +鱥 +鱧 +鱨 +鱮 +鱰 +鱲 +鱵 +鱷 +鱺 +鳧 +鳩 +鳰 +鳴 +鳶 +鳽 +鴆 +鴇 +鴉 +鴒 +鴓 +鴕 +鴗 +鴛 +鴝 +鴞 +鴟 +鴡 +鴣 +鴦 +鴫 +鴯 +鴰 +鴴 +鵂 +鵄 +鵎 +鵐 +鵑 +鵒 +鵓 +鵙 +鵜 +鵝 +鵞 +鵟 +鵠 +鵡 +鵪 +鵯 +鵰 +鵲 +鵵 +鵼 +鵾 +鶆 +鶇 +鶉 +鶒 +鶓 +鶘 +鶚 +鶡 +鶥 +鶩 +鶬 +鶯 +鶲 +鶹 +鶺 +鶻 +鶼 +鶿 +鷂 +鷉 +鷎 +鷓 +鷙 +鷚 +鷟 +鷥 +鷦 +鷫 +鷯 +鷲 +鷳 +鷸 +鸊 +鸌 +鸐 +鸑 +鸕 +鸘 +鸚 +鸛 +鸜 +鸝 +鸞 +鹮 +鹵 +鹹 +鹼 +麅 +麇 +麈 +麊 +麐 +麞 +麩 +麪 +麴 +麹 +麼 +麿 +黁 +黇 +黌 +黐 +黙 +黥 +黧 +黨 +黴 +黶 +黻 +黼 +黽 +黿 +鼂 +鼇 +鼈 +鼉 +鼐 +鼒 +鼕 +鼢 +鼩 +鼯 +鼱 +鼴 +鼷 +齒 +齕 +齡 +齣 +齦 +齧 +齲 +齶 +龎 +龐 +龑 +龔 +龕 +龜 +龝 +龠 +ず +梌 +叀 +晢 +媸 +錾 +鐖 +䰡 +櫬 +锱 +υ +鼗 +媪 +澴 +苈 +眴 +𝜏 +缱 +𝜶 +조 +晡 +≡ +ࠀ +н +廇 +嗛 +篚 +ώ +莰 +윤 +纚 +𢢞 +闼 +熌 +饎 +蓊 +倅 +년 +聭 +耩 +≅ + +≺ +诌 + + + +耰 +菗 +僦 +⇣ +甊 +冓 +缷 +枊 +沕 +𝐴 +❹ +형 +秾 + +щ +厹 + +˗ +疔 +䩦 +髴 +⨂ +莏 +≧ +垆 +銌 +桤 +隤 +ギ +벽 +⑸ +✘ +̣ +辶 +铼 +게 +へ +獶 +藳 +祍 +黉 +跱 +⽬ +埙 +だ +蓣 +亯 +구 + +鹎 + +⾃ +楩 +⌘ +汏 +虒 +谖 + +﹜ +劖 +じ +瑇 +㮑 +揕 +⇔ +𤔲 +薉 +𝑾 +硗 +〈 +は +盍 +狽 +ж +я +挆 +槨 +γ +阏 +襕 +𝜉 +❖ +└ +총 +시 + +ν +刲 +ด +嬲 +绤 +𝐰 +飦 +扱 +帻 +辀 +廴 +к +蔖 +– +같 +熭 +巣 + +裛 +𝑶 +蓺 +蔊 +그 +匳 +玚 +Ц +璲 +련 +𨒅 +변 +㤵 +饫 +𨚵 +X +筇 +镡 +ⅳ +𝛿 +轸 +𝑭 +鋈 +鵩 +縁 +˙ +ɿ +𝒴 +㝮 +𝜂 +栠 +橦 +緇 +肰 + +跼 +䭜 +蜅 +訸 +㻶 +𝑉 +เ + +嚢 +鼔 +𝒆 +閫 +阃 +𥞹 +杪 +誊 + +鲋 +骍 +τ +莾 +凊 +﹡ +箚 +蛱 +樯 +喾 +幞 +欕 +搡 +戉 +瘖 +᙭ +砟 +ས +∤ +ี +メ +𝝁 +穑 +渶 +𦬁 +서 +⊗ +穇 +⌊ +を +鐻 +蘤 +≫ +◐ +汙 +蒒 +⑷ +蹨 +x +裥 +嶤 +ァ +従 +침 +稂 +𪧶 +で +𝑹 +⑫ +闩 +槫 +舮 +𝑿 +戁 +간 +戯 + +ོ +æ +わ +チ +砉 +Ψ +劂 +・ +В +鬭 +钔 +盭 +黓 +⎯ +𝐏 +함 +钪 +𝑸 +澰 +래 +藒 +龃 +瞀 +伧 +♂ +¹ +ƞ +澼 +餍 +倶 +ð + +嚱 +跬 +貙 +磿 +娬 +氿 +鹘 +𝐁 +摅 +ヱ +傰 +พ +湝 +ˆ +Л +翾 +≃ +에 +滫 +С +嫕 +あ +㈣ +ⅇ +垧 +⺮ +∠ +躐 +硌 +眢 +乧 +𝑐 +泃 +轫 +↔ +㎝ +≜ +⽇ +撟 +⟹ +脿 + +㸁 +靯 +う +⁠ +懬 +搷 +瀓 +ˁ +ⅲ +훈 + +お +𝛄 +瓅 +葻 +猋 +ら +⾳ +喣 +⽿ +č +鈎 +⑤ +å +阸 +름 +て +圮 +⚫ +⻄ +胨 +琠 +戄 +箄 +𝒳 +鼍 +й +⼲ +廪 +睃 +囫 +͞ +죄 + +호 +み +饩 + +⊆ +х +欚 +瘚 +≯ +瞗 +ž +嗵 +근 +ま + +⾔ +罥 +ʹ +鼃 +д +✳ +ゃ +悊 +𝐅 +영 +@ +ɣ +𝛷 +𝜁 +ǜ +犄 +⽂ +ཆ +胒 +﹦ +谫 +є +・ +𝐻 +狺 +백 +舳 +𝑁 +ษ + +𝜓 +𝒦 +盕 +유 +𪯐 +茑 +礤 +거 +コ +肂 +鸻 +ã +⑬ +铚 +걸 +磳 +綷 +𝒚 +舭 +腚 +㈩ +榱 +𝐌 +畾 +馐 +罾 +∕ +𝔛 +𝑬 +ç +楬 +櫽 +顼 +阋 + +꺼 +諛 +̌ +้ +㮀 +乵 +沬 +⼀ +ư +鲠 +䜩 +樉 +鹈 +搧 +轾 +䟒 +등 +𝝉 +잠 +짤 +า +蘨 +愪 +ྟ +慪 +鮝 +𝛑 +び +𥞪 +𝐾 +レ +교 +ྲ +달 +𝐩 + +殹 +踇 +狥 +ベ +미 +매 +⑭ +钁 +Θ +못 +𝜇 +侂 +ę +ฟ +邶 +諣 +颃 +𡢕 +昑 +𝒖 +讱 +﹤ +緵 +骢 +朢 +骘 +ℜ + +ゞ +愬 +鹬 + +ッ +ར +급 +‚ +鸶 +蒫 +餽 +蓃 +ข +辠 +ğ +氺 +暆 +笿 +迚 +甝 +ή +徼 +旣 +ϖ +ヲ +倕 +匽 +蓱 +리 +剷 +ู +逪 + +나 +堋 +焠 +Δ +炑 +爫 +蒖 +𝒓 +悫 +𝛱 + +𝐮 +騧 +ⅴ +饾 +贠 +𝚲 +崀 +磀 +柤 +肈 +⻮ +鶄 +狲 +跫 +지 +鳇 +痖 +跂 +秫 +ʒ +합 +ไ +迨 +𝜐 + +屦 +𝐶 +; +辎 +∵ +鴁 +撏 +ς +⟶ +薮 +㟪 +犮 +ب +ビ +藡 +甏 + +眡 +訿 +鉥 +媵 + +柫 +𝒞 +ь +萏 +ค +트 +訮 +汚 +眚 +〞 +き +ほ +刖 +髄 +蘀 +や +ة +诹 +т +ན +𝒃 +掼 +䓁 +僥 +팰 +枵 +✔ +³ +ེ +鼖 +屖 +鍮 +砇 +カ +舐 +牴 +𝜎 +㡿 +攉 +⽤ +晅 +労 +蛕 +𝐽 +Ʃ +く +穽 +孥 +𝒏 + + +ɬ +玦 +檮 +ョ +∥ +중 +萯 +呲 +䰈 +새 + +釶 + +ɢ +⊂ +臮 + +梼 +デ +骖 +ス +蹩 +羼 +▽ +Π +≪ +匛 +𝐼 +稊 +่ +茠 +䢉 +秝 +茐 +齎 +そ + +芕 +噚 +癉 +蹱 +蓜 +𝐬 +ϑ +е +瀋 +ϕ +χ +镟 +霂 +隒 +▱ +ヶ +撄 +둔 +¢ +こ +跲 +莻 +𝑠 +輮 +็ +堠 +푟 +赕 +◦ +ا +런 +帒 +汘 +̱ +尥 +蘠 +𦟜 +옥 +腠 +夨 +⩾ +𝑝 +歯 +刱 +여 +け +溘 +釰 +肍 +擗 +矱 +鍌 +芧 +술 +발 +鼫 +舾 +⼯ +𝝓 +ƒ +怸 +པ +𣐼 +疎 +铷 +Η +⑺ +蒏 +림 +⃛ +゜ +褴 +𨒪 +れ +揢 +さ +櫫 +櫑 +䋎 +灋 +櫜 +诓 +❶ +𝐃 +Q +袳 +ℒ +菂 + +荙 +ℛ +⁄ +堙 +贋 +̅ +鳏 +̂ +、 +茍 +泜 +𝑈 +즉 +噔 + + +迓 +Ⅸ +❷ +이 +_ +⾊ +Ö +铥 +耹 +䶮 + +무 + +セ +饳 +อ +篾 +통 +‒ +ย +덕 +말 +艨 +Ω +𝐨 +螓 +澐 +巠 +⋅ +钶 +도 +鸱 +齍 +恑 +褛 +剟 +준 +勶 +𠟠 +ß +箅 +𝑆 +悃 +蘥 + +Ξ +𝑘 +妣 +𝑖 +𝐑 +纡 +釿 +⺌ +ヴ +𝕀 +涻 +箙 +塚 + +⼠ +墈 +∷ +疴 +ク +ㄕ +𝒂 +蒪 +蓡 + +鷇 +瘏 +𣹳 +橰 +嵚 +帀 +주 +ド +盓 +爇 +φ +觋 +𝜑 +钍 +화 +표 +Ɛ +篰 +명 +週 +с +蓛 +裢 +穜 +㱃 +玊 +鲕 +蒕 +箪 +⑯ +苽 +矦 +偰 +盝 +佊 +僨 +駉 +𝑳 +머 +ª +絅 + +锒 + +苆 +ั +𝛻 +碹 +咺 +竝 +и +づ +강 +辁 +́ +铽 +纩 +齑 +𝝎 +어 +ユ +躡 +𝒄 +ซ +畛 +鸰 +ླ + +骉 +❸ +揲 +廃 +湋 +𝑲 + + +旤 + +蹷 +钌 +국 +豙 +鬳 + +ɛ +轳 +俜 +眄 +萮 +𝐡 +颵 +箓 +魑 +𝑅 +漍 +ℤ + +逡 +학 +浖 +ょ +¬ +怴 +𝛤 +怿 +祌 +纥 +𝒑 +⃑ +棅 +笵 +낭 +栦 +⑰ +บ +𝔽 +𝑇 +埝 +⽓ +孱 +埶 +匜 +鸼 + + +벌 +ル +锸 +斫 +妟 +뽀 +昬 +댁 +ʂ +暯 +夳 +ノ +堞 +懘 +榼 +鞫 +오 +𝑡 +偑 +戗 +∴ +伥 +끝 +𬌗 +稯 +岜 +Ε +犲 +𩓞 +연 +鹚 + +ག +诜 +嗍 +倥 +鳣 +庑 +屾 +雚 + +椄 +颏 +酤 +𝒋 +欛 + +း +려 +缋 +¾ +ゴ +籑 +笤 +鞛 +鏺 +蓒 +설 +緍 +⑩ +迀 +鼋 +ɮ +위 +锪 +∨ +滆 +€ +躅 +鋓 +柀 +䐶 +啎 +𝛵 +骃 +ć +갈 +卨 +い +𝑺 +鸲 +壻 +偯 +𝑞 +譖 +곤 +溍 + +噫 +순 + +𝑽 +ы +赑 +蓸 +鸮 +稃 +っ +詗 +으 +⨀ +屮 +俦 +伛 +畱 +늬 +𝑂 +朼 +沰 +겨 +з +骀 +鸩 +𝜈 +º +苊 +诎 +皤 + +하 +̀ +砑 +凷 +翄 +𝑛 +赪 +≮ +浗 +𝐍 +û +オ +ƹ +𝜅 +묘 +曛 +鳊 +𝛩 +癹 +磒 +ば +⑨ +礆 + +乼 + +∽ +褱 +藴 +縶 +觥 +に +식 +凫 + +佥 +槷 +阍 +䰍 +졸 +전 +葢 +㝸 +も +⻔ +遽 + +蹰 +𝛺 +裏 +། +를 + +ろ +짭 + +ぐ +싶 +渰 +⊤ +浳 +൯ +∃ +옛 +蟞 +과 +芠 +飖 +⼆ +敶 +粝 +𥃩 +坿 +䩉 +𝑯 +「 +矰 + +사 +𝛶 +𝑎 +挐 +푎 +동 +ℝ +Γ +︃ +珒 +鹍 +κ +鑓 +傁 +惓 +臿 +丣 +悒 +侔 +ñ +訳 +櫭 +賛 +觏 +辂 +覅 +濓 +堿 +擪 +฀ +𝑵 +扨 +嫫 +珰 + +寃 +𝒔 +曱 +髣 +인 +≌ +莵 +踳 +ⅱ +Ø +⌋ +¯ +挢 +̇ +﹪ +哕 +𦫳 + +襛 +昳 +铙 +铫 +軱 +汔 +ネ + +躩 +옷 +ถ +엄 +皊 +臑 +𧄝 +𝑃 + +䢅 +𝐝 +𝒍 +ℱ +𝐓 +蓾 +𝑻 +䋁 +裼 +개 +ത +𝒊 +僪 +瞂 +𦞠 +요 + ̄ +荍 +𝜔 +ф +峣 +庋 +檏 +袢 +绬 +Σ +향 +钫 +え +枅 +≝ +荦 +들 +勍 +ö +𝒕 +툰 +遬 +𝐵 +擧 +咢 +钘 + +𝒢 +Ⅷ +➢ +讧 +ω +簟 +廐 +刳 +阘 +б +⊘ +髟 +臓 +루 +⎧ +诳 +у +诮 +蠪 +梹 +耤 +パ +ن +∆ + +𝑫 +น +べ +坼 + +𝑤 +褽 +憼 +심 +∇ +迖 +휆 +叚 +없 +⼿ +钖 +斠 +䪵 +胠 +𝜋 +殽 +剜 +⾝ +− +慸 +𝛽 +椔 +⟩ +皦 +筚 +奰 +Å +물 +𝒐 +嫱 +钆 +ï +∪ +⇢ +ş + +㖞 +璗 +葸 +殢 +𝜺 +夲 +骒 +ち +회 +선 +睒 +轡 +ξ + +鲧 +镞 +碜 +놈 +Å +紴 + +⇤ +ྷ +⑪ +喟 +𦼰 + +蔩 +埦 +𝜆 +耋 +˜ +한 +舣 +馓 +⑻ + +ɐ +椘 +し +莐 +辔 +憰 +碛 +⁃ +飏 +颀 +跽 +⇥ +赀 +撺 +襜 +ɒ +袧 +л +정 +꾸 +콩 + +박 +缑 +柈 + +樲 +𝑮 +詘 +µ +𝑷 +鹪 +𝛼 +차 +讬 +掯 +硎 +𝑨 +舄 +‹ +누 +バ +ก +萀 +兇 +숙 +貍 + +踈 +친 +𝜽 +摰 +甿 +坜 +遑 +삼 +배 +Μ +을 +譊 +沩 +빈 +飑 +钹 +镨 +鐉 +宭 +桗 +ə +歺 +А +⇒ +锞 +𝒪 +棊 +愓 +莶 +琲 +འ +プ +་ +𝐿 +艟 +欬 +ิ +в +ų +纻 +㎎ +婄 +Ρ +歟 +椢 +粜 +종 +خ +ね +剞 +베 +斄 +幠 +ト +疛 +よ +╳ +醳 +군 +諂 +芰 +穋 +禆 +길 +秊 +噙 +y +锓 +⁵ + +拠 +Ĥ +𝑒 +窬 +抈 +︰ +퐶 +铳 +각 +ش +錉 +ù +臝 +闶 +𝒟 +芐 +韎 +권 +萚 + +ど +羮 +镕 +欔 +瘐 +받 +𝑚 +𢦟 +髤 +腙 +⽽ +상 +铘 +장 +𤇈 +ニ +凂 +ȷ +육 +а +살 +雠 +荑 +태 +穤 +ɯ + +圬 +楑 +단 +ง +⾯ +λ +⁰ +성 +萿 +缌 + +毣 +矅 + +푚 +˘ +貣 +∂ +은 +ė +䌛 +경 +せ + +拫 +⅞ +餕 +鐨 +翭 +ื +ɵ +⺍ +Փ +▬ +ว +희 +짐 +屙 +洫 +ေ +∏ +臜 + +剸 +芓 +운 +∓ +계 +祔 +鶵 +𝝅 +柂 +訢 +禊 +扽 +恫 +𝜙 +狢 +勠 +ི +𝜒 +จ +铯 +ྱ +𝑙 +蟇 +울 +莤 +牱 +𝒗 +詇 +靃 +殓 +栍 +踟 +ي + +鲄 +㓷 +贳 +ナ +鲓 +𝒙 +薁 +Χ +侪 +恌 +㰤 +목 +̄ +丱 +― +𝛔 +𝑔 + +鸷 +﹣ +籢 +脢 +δ +窭 +‐ +阒 +석 +아 +ォ +두 +𝐦 +浬 +搰 +褃 + +ལ +乇 +腘 +眊 +偬 +Ⅻ +ℳ +畤 +芟 +曈 +飧 +堌 +═ +谶 +櫝 +嬑 +冋 +嗌 +抜 + +腜 +공 +𝜕 +ん +鲭 +郐 +酓 +𝑍 +⾏ +⼹ +㐬 +고 +𝟑 +缯 +碤 +濩 +ʰ +佻 +Υ +∗ +賅 +집 +跹 + +ɾ +蔧 +다 +栫 +庰 +欤 +洿 +捾 +𝜍 +𝑄 + +攆 +夂 +檿 +荜 +ц +柖 +唅 +ท +ɦ +讦 +습 +锿 + +纆 +檑 +殰 +歠 +鼑 +Ä +و +☑ +緦 +悁 +偞 +ส +絭 +저 +踯 +騀 +쉰 +蒷 +揗 +儵 +ρ +薅 +ャ +‗ +犒 +旟 + +승 +ང +소 +𝛴 +瀜 +锜 +𣱼 +谳 + +軑 +ポ +楁 +𝑜 +袚 +ྐ +Á +𝑑 +鲀 +牾 +鬌 +푥 +¤ +呴 +‑ +✓ +민 +⼦ +ⅰ +⽉ +擿 +ч +➝ +가 +≳ +漥 +踖 +枧 +莝 +⻘ +傧 +𝑢 +ю +赍 +q +絫 +о +ア +ℐ +髫 +齢 +湎 +甓 +揿 + +ℋ +怹 +자 +⑦ +져 +椟 +鶟 +浕 +ー +𝛂 +偾 +⃗ +喑 +鹡 +≦ +磽 +ⅆ + +葂 +鶱 +ン +貇 +褡 +▴ +것 +喿 +つ +闚 + +盳 +𝟒 +雔 +洭 +殫 +楎 +£ +^ +葲 +𝟖 +眗 +棸 +潏 +熕 +𝟐 +품 +သ +樳 +⁴ +イ +㈢ +렴 +脰 +돈 +⑮ +钲 +𝒘 +訽 +爟 +幨 +枻 +亷 +猃 +σ +黩 +嘑 +榹 +⁡ +鍧 +𝑋 +枘 +𝑥 +원 +睚 +饔 +酲 + +顸 +람 +𝐫 +脁 +½ +긴 +ʔ +Ⅰ +旆 +죽 + +궐 + +奡 +㭃 +杝 +忾 +ม +掮 +饍 +摛 +쓰 +慊 +踣 +푅 +悽 +礅 +毄 +俓 +데 +冣 +만 +驖 +𤉣 +̃ +廾 +匵 +阇 +𤸫 +戣 +刌 +剕 +杅 +ο +蒥 +ː +癃 +蒬 +â +À +嗥 +우 +ケ +」 +聩 +ë +吽 +檌 +苰 +⑹ +Ÿ + +⑥ +노 + +˄ +鸫 +廛 +㱿 +鹛 +赟 +℅ +菿 +辳 +阼 +𝒇 +哋 +♀ +氕 +砤 +† +舡 +偝 +飜 +넓 +鈜 +ầ +닝 +禚 +匲 +〉 +Ф +锊 +ϵ +∙ +惛 +箧 +𝑦 +宬 +𝑀 +臙 +𩡶 + +¡ +潀 +수 +敃 +か +أ + +苌 +饘 +咝 +𝑼 +∘ +涷 +樍 +厣 +蝼 +墻 +Ñ +秅 +︒ +∅ +↵ +葹 +ỽ +𝑗 +た +일 +蒊 +치 +竢 +¨ +佢 +潵 +櫼 +軵 +𧕿 +倨 +歱 +瘅 +𝐭 +黾 +脼 +ê +땋 +鶷 +ё +鹯 +掲 +\ +𨳡 + +Г +ι +탁 +溞 +殪 +菭 +𝛥 +擛 +録 +㥥 +∀ +锇 +锃 +편 +餬 +瘻 +ཟ +豤 +로 +ɸ +ℎ +랑 +ʃ +鼹 +臬 +ŋ +巵 +譁 +w +窳 +蓔 +䉜 +浧 +酂 +⒀ +མ +椠 +槖 + +衄 +𨐨 +̿ +ご +⺗ +顇 +𝒫 +搕 +ミ +𪪋 +« +䣛 +鹩 +鴈 +п +는 +䋰 +𝛹 +犕 +呌 +𝒮 +𝑪 +鋎 +嚻 +杚 +䕊 +蠜 +ザ +𝐂 +☐ +𥘔 + +赜 +Ν +廦 +瓾 +↦ +龉 +⽅ +棂 +𝜌 +큰 +踔 +ラ +。 +剤 +황 +⅜ +僈 +骈 +ɕ +О +м +椑 +𝑟 +纇 +𝑓 +𝐖 +Ш +⎦ +旹 +삶 +ึ +囵 + +す +ⅈ +ت +踽 +陴 +餱 +ป +막 +紟 +방 +剀 +簖 +闬 +キ +鋉 +납 +タ +谵 +詑 +족 +垔 +荋 +旰 +𥘸 +窾 + +신 +𝐎 +𝛃 + + +﹒ +縰 +猲 +郘 +파 +⊕ +镘 +𠊃 + +呔 +𝜗 +ʊ +𝛬 +辏 +陭 +𝑕 +庴 +ʐ +瀌 +倄 +蕞 +ل +絷 +러 +든 +བ +柅 +› +傚 +睠 +Ⅺ +饐 +蔮 +ɟ +莈 +𤨨 +⋯ +犭 +𝜃 +𥹳 +초 +⎞ +遌 +眇 +蓗 +綅 +토 +裰 + +⼼ +虘 +𝑌 +觯 +漶 +钤 +讒 +げ +螬 +鲹 +咷 +蓞 +僂 +𝒉 +が +桮 +포 +쟈 +柽 +ウ +綟 +缟 +䁾 +钭 +烕 +厠 + +孭 +礉 +­ +谲 + +⼤ +𝒒 +旒 +㫄 + + +鳓 +挈 +재 +ད +𧊒 +蝝 +𝐺 +懱 +芢 + +ⅼ +Ú +𝑱 +翯 +芶 +厽 +遉 +鲒 +η +𝛾 +趮 +虆 +汸 +嬖 +糈 +窸 + +추 +棬 +懔 +硁 +ぶ +抟 +胕 +𝑧 +⌦ +碫 +Ⅵ +속 +𝐚 + +Ç +행 +Ɵ +⑱ +贽 +箤 +р +敒 +衤 +풍 +⊛ +慉 +ψ +© +광 +ℕ +屣 +臌 +旵 +臁 +‡ +癎 +閡 +𡵂 +襐 +畟 + +萪 +娒 +瘼 +庳 +천 +觌 +Α +と +奁 +煃 +؟ +◯ +의 +攎 + +𝐞 +J +𢦒 +❑ +벤 +𝐒 +リ +蒉 +𝐱 +朹 +㈤ +„ +䗬 +Ι +ཀ +𡜵 +俣 +疬 + +墥 +吣 +У +榀 +絟 + +旓 +𝐛 +𝜷 +瑮 +≔ +笾 +ζ +김 +暵 +𝜹 +逶 +萙 +欇 +俧 +籴 +絰 +揶 +ǔ +宂 +伩 +Ө +菞 +梕 +エ +蹚 +제 +Š +沝 + +𦳢 +𝒱 +揠 +ℏ +𝐹 +箝 +규 +氒 +⼊ +鰌 +筮 +⼩ +대 +𝔾 +䄃 +𝐸 +﹕ +부 + +刓 +ȵ +缛 +기 +缊 +𝟎 +𨟻 +め +捃 +⽚ +鍪 +灆 +迻 +⾦ +荗 +v +𡒊 +汍 +斲 +姕 + +儴 +偒 +辤 +芀 +蝥 +ń +臥 +椀 +㪚 +š +담 +ø +䈰 +睏 +テ +﹐ + +참 +楘 +𝒌 +劓 +ɪ +醑 +绹 +諓 +𝛉 +ズ +怼 +埘 +臽 +잡 +镢 +𝜖 +진 +踬 +谠 +﹥ +髺 +腞 +현 +嘭 +ʑ +蓌 +〜 +锠 +蓶 +る + +∼ +枎 +緗 +薠 +芈 +耪 +𝒎 +謼 + +瘳 +诨 +忤 +œ +⇡ + +鲣 +ⅵ +Τ +㯮 + +㶲 +ⅹ +䙴 +坴 +馑 +缹 +椦 +ô +⼈ +フ +誏 +э +哙 +愎 + +埽 +祲 +마 +殍 +菋 +懑 + +辇 +鍤 +𝜀 +ɜ +り +𝐷 +㕞 +瑵 + +蔨 +Ⅶ +镴 +ภ +𝝀 +𢶡 +⃝ +ơ +柢 +𧴗 +ʁ +攙 + +な +𝑏 +挴 +餧 +絇 +怄 +曏 +洟 +軷 +∉ +咍 +⎪ +樛 +𝑣 +웃 +椊 +黢 +𝑩 +誩 +伓 +戠 +橥 +⟨ +豰 +懥 +涖 +綘 +詬 +ွ +˚ +刽 +ɑ +격 +稖 +𝟏 +禝 +墦 +𝑊 + +択 +檙 +∝ +颟 +诂 +𝐧 +踲 +𝜛 +𝑰 + +鲬 +⁸ +ǎ +문 + +紬 +楲 +䊭 +枨 +膑 +õ +던 +Ⅴ +秏 +馔 +拊 +缗 +隠 +牀 +淲 +鬰 +綵 +鶑 +詎 +慙 +劒 +閲 +鎡 +淒 +屨 +鉢 +扃 +鳬 +閤 +馿 +翛 +駸 +蛩 +驂 +嵲 +覩 +牋 +湲 +蓴 +賸 +遡 +翫 +嫋 +惻 +妬 +罇 +龏 +鵷 +閙 +鎸 +朅 +巉 +僞 +洶 +磧 +筯 +慇 +鷁 +斾 +斸 +酹 +幘 +羶 +閽 +薤 +泝 +覯 +怱 +缾 +氳 +躊 +檝 +擣 +虀 +誚 +甃 +艤 +櫳 +醖 +壚 +涴 +崦 +秪 +潄 +濆 +駡 +坰 +闉 +縑 +躕 +颻 +燠 +輙 +鏁 +嶮 +薖 +輈 +綆 +覷 +蹔 +攄 +鐩 +鑱 +羃 +轓 +麤 +驀 +欵 +亙 +朮 +邐 +箠 +怳 +鋩 +鷃 +篘 +蔌 +諄 +旐 +慍 +欷 +頽 +蜺 +覊 +禋 +秔 +蜩 +嚬 +櫺 +軿 +痾 +笻 +猱 +毳 +泬 +竚 +齪 +搘 +欻 +釂 +嚥 +誑 +籩 +韉 +幙 +舠 +飣 +颭 +颸 +歔 +屧 +巇 +艫 +壖 +牓 +薝 +銛 +皪 +枿 +剗 +歘 +鸂 +邅 +衒 +荄 +鴂 +嫗 +顦 +瀼 +繄 +搆 +狖 +貰 +醆 +秖 +蹀 +頳 +纒 +憇 +溽 +澦 +讐 +灩 +箎 +螿 +鰥 +飀 +澒 +矻 +枌 +擡 +鷖 +齬 +纊 +挼 +齟 +錙 +屩 +蠧 +氅 +漭 +躚 +翺 +瘵 +螘 +鵶 +㶁 +斵 +饜 +岏 +䍦 +哢 +彴 +豗 +靨 +鋋 +禳 +覘 +鞚 +擻 +涘 +溷 +沴 +嶓 +褊 +罏 +齏 +醲 +繅 +舴 +釃 +厖 +闤 +閴 +藂 +譍 +糲 +籞 +躞 +餳 +遰 +倐 +嘖 +鷀 +暍 +韝 +蘺 +齁 +醽 +醨 +憀 +䕷 +跕 +拶 +垤 +鸎 +漙 +躭 +傴 +蕕 +嘒 +晻 +糵 +閈 +嫠 +斁 +鶗 +詶 +囘 +羇 +橛 +鞬 +磈 +粃 +阨 +塿 +敧 +氊 +芼 +襆 +迍 +鬛 +憒 +釅 +蓐 +奬 +頲 +髠 +抔 +葅 +槧 +跧 +揜 +渇 +餔 +罝 +裯 +蹁 +椶 +幰 +逰 +饁 +棃 +噀 +轔 +囁 +惸 +騑 +呪 +鬐 +綌 +醤 +䆉 +艣 +鐶 +夐 +摐 +鸇 +睎 +羝 +紼 +鞿 +噉 +磑 +闗 +筩 +駮 +蹌 +縢 +闠 +鬙 +谹 +榾 +觳 +皁 +晼 +啗 +簣 +騕 +蹣 +麰 +觧 +怊 +朞 +鱍 +蟣 +畚 +蠁 +舁 +瞇 +劚 +鰐 +籯 +鬖 +柮 +飱 +帟 +酇 +崿 +霪 +緌 +踆 +欃 +縟 +搦 +琖 +搥 +倀 +觫 +遝 +嚅 +聵 +藋 +筴 +喁 +窻 +穏 +牥 +鎩 +礲 +膴 +鞵 +醵 +斚 +縲 +裀 +齷 +騃 +袠 +谺 +靦 +帬 +鯈 +曀 +灔 +崷 +趂 +徯 +闃 +洧 +獪 +稏 +煢 +歈 +呶 +壈 +襃 +旴 +檟 +簦 +谽 +箵 +謡 +慝 +餖 +稌 +朣 +禖 +嚀 +嵂 +黷 +甖 +洑 +釡 +蕝 +甆 +翣 +篸 +隮 +滃 +裌 +蔀 +籖 +秬 +鷴 +啅 +慼 +捄 +咮 +睟 +譎 +嘷 +駃 +檥 +蹐 +窊 +駞 +雘 +趯 +篲 +讋 +睍 +毰 +憗 +鳷 +嚲 +圞 +歃 +緼 +賫 +籋 +繐 +麏 +灕 +礧 +歜 +飇 +鵁 +疢 +賖 +窆 +螮 +毹 +硉 +耡 +甔 +篛 +娭 +髩 +燋 +輜 +籧 +撝 +嬙 +徃 +驦 +𡏖 +麕 +馹 +覔 +鶠 +褷 +綍 +螗 +嗈 +彯 +篨 +炰 +鄮 +噞 +尅 +鷰 +鋭 +饉 +霢 +䔩 +坱 +裓 +帨 +忺 +豅 +栱 +謦 +傯 +誵 +骭 +潩 +鬒 +嵫 +悮 +扊 +扅 +轢 +惝 +臈 +舃 +鞾 +譟 +袵 +眎 +簏 +埸 +堧 +憸 +雰 +腷 +嵓 +隩 +趄 +墐 +褦 +艑 +狴 +玿 +竪 +恧 +姱 +抆 +恓 +霣 +躓 +鞲 +晬 +簴 +唼 +曵 +褕 +罣 +縐 +衘 +巃 +攲 +輀 +貎 +哳 +醭 +鋣 +僛 +迕 +蠭 +膓 +欝 +洊 +敺 +纎 +栟 +鞓 +蛬 +灺 +痏 +恡 +踸 +霔 +濵 +昻 +鉘 +楖 +竛 +竮 +窱 +幬 +慤 +儗 +黮 +嘐 +睆 +頇 +麑 +壼 +㦸 +顋 +瘥 +苖 +韈 +盻 +袷 +矼 +塼 +鐍 +傞 +苶 +吷 +噇 +鶖 +僣 +髧 +䅉 +鯫 +襏 +縳 +蠨 +痡 +髽 +剉 +蝱 +鄽 +匼 +嚚 +襫 +缿 +鵊 +燖 +忸 +摝 +攅 +牷 +氎 +騣 +颿 +虡 +腯 +漘 +矓 +祫 +顢 +綀 +弮 +柙 +蔾 +胾 +筤 +馽 +砆 +冩 +賙 +唶 +麛 +簜 +蹏 +屼 +鞶 +煑 +踠 +愀 +蠒 +頬 +韲 +戞 +畆 +笊 +搨 +捽 +絙 +覉 +澨 +趫 +矹 +穮 +愠 +劘 +轣 +卭 +鼪 +杕 +轗 +擐 +蚿 +恚 +檛 +𩕳 +靆 +轕 +餼 +頮 +槹 +蔉 +皜 +扄 +鮆 +轑 +蹡 +嵽 +甎 +蟈 +橅 +笴 +膰 +蕣 +澘 +髿 +樕 +褵 +蜋 +窼 +櫧 +雊 +胷 +嘵 +麄 +裋 +繢 +啐 +臛 +簁 +巓 +羜 +攧 +簮 +壊 +齩 +晹 +臲 +鬵 +齠 +媮 +幮 +壍 +蠛 +槜 +羖 +窓 +隃 +嚘 +輳 +籹 +凴 +崕 +獍 +嗸 +趦 +囅 +戺 +涬 +諉 +箯 +輊 +桹 +嵷 +㲲 +愊 +蒱 +洚 +赩 +輴 +幈 +齔 +嗁 +阽 +圠 +荈 +碔 +揎 +巀 +洏 +卼 +𨁝 +痁 +黳 +嗾 +䆗 +戃 +蕆 +頋 +悤 +掎 +㯝 +吚 +猘 +鮎 +鬴 +墁 +飋 +呿 +窀 +沲 +枒 +窌 +爼 +頞 +譡 +鶋 +湩 +㦬 +僾 +斒 +醼 +鶂 +磥 +揫 +犗 +齗 +鄶 +囏 +崪 +爞 +籓 +斮 +觝 +嵸 +驔 +䨴 +頺 +萑 +珓 +牸 +闒 +凘 +悢 +蟭 +濈 +嬄 +翽 +旍 +鶢 +罋 +輠 +怩 +頖 +趍 +壝 +嫮 +蕋 +踦 +轇 +眘 +巗 +嶭 +糓 +甽 +籺 +矟 +佖 +絏 +憮 +懡 +駈 +擕 +淟 +皡 +膋 +潨 +鳲 +趠 +麨 +頩 +漻 +輗 +墄 +賮 +㴩 +莟 +縦 +岝 +醻 +曚 +䙰 +噭 +醥 +筰 +躧 +踘 +鑕 +咈 +僶 +鶊 +鬂 +聼 +騐 +壒 +磎 +歗 +淈 +隟 +狃 +縋 +媻 +趲 +惙 +呫 +聮 +羾 +尫 +佽 +髼 +繋 +鬘 +旜 +疐 +阬 +䰐 +塈 +徤 +祊 +灂 +祅 +樷 +颾 +凟 +頀 +蠏 +塒 +衹 +婬 +裩 +粞 +憯 +匭 +筈 +盬 +霮 +黕 +靮 +伻 +緺 +瘝 +羑 +醸 +樝 +僎 +絓 +噆 +愞 +痗 +愽 +岊 +黤 +湑 +搉 +綯 +焮 +疉 +楛 +玼 +喤 +粔 +飂 +贐 +緉 +覰 +胔 +鞳 +摑 +墢 +斅 +誶 +僝 +鹺 +諌 +齅 +嵼 +讟 +冦 +脝 +婣 +緐 +茰 +飶 +欎 +慁 +抝 +瘉 +𡎺 +鈯 +瘃 +麫 +匊 +窞 +羓 +氄 +嚌 +姤 +橑 +駬 +冺 +騠 +㕙 +楶 +靸 +圎 +尀 +䙀 +鄏 +軃 +竁 +㹠 +刜 +剨 +罛 +鏹 +鬉 +簨 +藭 +藷 +僇 +瀫 +袨 +忮 +冡 +齯 +楪 +囋 +蟉 +醱 +尠 +牣 +攟 +袿 +齾 +甞 +啑 +潚 +樐 +絖 +酖 +觖 +骹 +嶅 +玃 +嫜 +廹 +儤 +矂 +艓 +挱 +骳 +嵳 +洴 +礓 +厪 +﨑 +禜 +籊 +瓻 +彛 +狁 +腪 +骾 +娯 +罻 +璅 +簳 +姢 +猰 +眹 +䴥 +堘 +搯 +怤 +緫 +聫 +涊 +熛 +輤 +䡾 +譌 +髇 +攛 +稭 +媕 +鬷 +跰 +縚 +鉧 +踧 +嚭 +襮 +藞 +滮 +颷 +荂 +蓰 +怫 +閧 +臕 +稛 +怗 +闑 +抶 +薶 +嶕 +瀺 +𥫗 +墝 +埆 +皥 +惷 +鞔 +鞺 +蟛 +瀡 +鎁 +酧 +恝 +齓 +嚄 +簔 +蟺 +㔶 +胹 +憖 +惄 +鸒 +貛 +軏 +縗 +蓻 +娵 +抺 +鼛 +虩 +歕 +矑 +繂 +襚 +倂 +廥 +諝 +虗 +弜 +兟 +繿 +偘 +翶 +肻 +棙 +斆 +碨 +醎 +蟢 +銙 +躠 +櫩 +椮 +絀 +鷾 +溳 +詖 +葓 +谼 +𦩘 +韔 +翿 +呑 +馡 +騊 +堁 +蓏 +䟃 +頟 +渢 +趑 +堄 +滛 +擫 +豭 +騩 +騘 +䍧 +彍 +忭 +餙 +馺 +忩 +芣 +矴 +噂 +滍 +慫 +𨍏 +怲 +扵 +搊 +昩 +嶻 +禬 +憃 +忼 +榰 +箾 +撁 +鈆 +袗 +脤 +騞 +哤 +螀 +靧 +梲 +囦 +魖 +褠 +䭔 +煆 +挃 +宷 +熉 +朘 +憭 +䒠 +謭 +鷤 +拕 +骫 +穾 +襭 +喓 +冞 +勩 +媢 +麚 +椓 +俙 +幐 +磝 +蜎 +灙 +漦 +㛹 +䭀 +㜷 +粻 +懟 +箳 +滣 +糉 +冐 +韤 +湱 +糭 +栳 +勌 +慱 +㸌 +罫 +筞 +霿 +躶 +玞 +磉 +罦 +祴 +媟 +猒 +擭 +恠 +嵁 +屴 +慆 +庬 +蟁 +㹀 +薧 +鷕 +渻 +朂 +愯 +齚 +蝻 +胏 +饙 +鳦 +鸃 +叅 +肧 +篂 +脗 +雺 +飰 +筀 +頥 +毶 +弌 +逓 +瞍 +絁 +鏚 +嚵 +攂 +醄 +奼 +獫 +絣 +靷 +畮 +褉 +棁 +揑 +楥 +橤 +襥 +蹮 +窔 +忪 +潠 +杇 +黲 +擄 +蚻 +蘙 +虙 +袐 +陿 +帊 +醟 +髖 +㞦 +鱭 +譸 +鮞 +栧 +扺 +脽 +擉 +岨 +黈 +餻 +佪 +遻 +鋟 +瞶 +廽 +懨 +墖 +玁 +籉 +宼 +鰋 +瑽 +垖 +酕 +漰 +戹 +蝛 +瑲 +阤 +褆 +儛 +䍽 +觕 +箘 +碯 +灨 +燀 +膇 +韀 +䳏 +詿 +禂 +韣 +踡 +碏 +尵 +莭 +庻 +篿 +狤 +㘞 +艭 +殱 +鵔 +槮 +猧 +劙 +獝 +㭊 +㾕 +蠚 +帤 +蹢 +蛚 +輼 +麀 +檃 +䰒 +䪫 +悾 +濳 +杗 +揾 +駏 +撦 +耈 +蟟 +狌 +鸖 +䨥 +餫 +鍰 +耉 +毚 +袽 +䱥 +慓 +䔿 +艖 +舋 +弰 +蠺 +嫓 +蚳 +髾 +喨 +鴐 +䍥 +韍 +柹 +掁 +薋 +攕 +飺 +凢 +麌 +嫰 +鑚 +黦 +葠 +吿 +栰 +踶 +芿 +穭 +啝 +筓 +褁 +稇 +顚 +䎘 +恇 +珷 +緪 +墠 +蛣 +蛜 +讕 +疻 +惎 +袝 +霡 +罸 +鬽 +苢 +喭 +飊 +唎 +澾 +襋 +皭 +廏 +蔿 +穊 +䝟 +駊 +獹 +夣 +褾 +慴 +軥 +讁 +軰 +瞷 +𡋯 +晜 +潗 +衋 +揵 +覼 +鱐 +醡 +䏰 +侐 +亁 +桞 +驘 +鬋 +鷽 +懞 +㵳 +儳 +豝 +傺 +搒 +縧 +硾 +䏶 +覻 +薍 +憝 +榠 +湆 +皵 +鎞 +菆 +糇 +矉 +搤 +紃 +峿 +磹 +甒 +琭 +𩥇 +菢 +禡 +渹 +刅 +迒 +敂 +蹜 +磓 +傪 +縿 +㕮 +涏 +䰀 +㡛 +韛 +犠 +餦 +圝 +焫 +㝢 +潬 +馵 +澟 +鱏 +譾 +㪍 +煼 +鍜 +窽 +紾 +堨 +䕸 +穅 +戅 +穄 +駴 +偫 +煗 +媠 +酘 +矬 +貆 +茞 +骩 +扠 +岞 +潓 +炧 +陊 +栭 +釱 +㡚 +篴 +耞 +鞉 +䋏 +𤫩 +椸 +儜 +痀 +謷 +潙 +寠 +牐 +嫭 +慅 +獧 +鈒 +欿 +薳 +蟂 +郲 +軨 +斨 +訦 +𠴲 +剺 +駪 +贙 +禫 +噣 +茢 +茙 +鄼 +揷 +魌 +䫻 +嗋 +噐 +侲 +諵 +𠺕 +挍 +䑳 +㨷 +槸 +靘 +㩧 +虣 +瑿 +衱 +襹 +餭 +㗶 +枑 +悋 +纑 +嶫 +儓 +髵 +甗 +榝 +㗭 +贗 +熸 +嬃 +礌 +偭 +樠 +栮 +鷼 +鵀 +澬 +眂 +牿 +骴 +呞 +爕 +牎 +巹 +帉 +砠 +梴 +䛏 +攃 +餁 +哿 +蹝 +崺 +閌 +醝 +臡 +麖 +駼 +賵 +夘 +骻 +愡 +俔 +諐 +觩 +莂 +饈 +殣 +溠 +冱 +埓 +厫 +虥 +芄 +慽 +竃 +埿 +仭 +褼 +倛 +韸 +牗 +幖 +禈 +穧 +蜧 +諞 +脞 +蝃 +飃 +煁 +涒 +谾 +覢 +赮 +鼘 +艗 +䶉 +鴥 +轒 +睅 +傔 +惵 +唈 +懆 +磣 +膢 +堶 +囈 +瘕 +誷 +瑘 +絝 +鬈 +嘽 +鷅 +梜 +喎 +鼟 +㟧 +劻 +眑 +剴 +痎 +餟 +庌 +菷 +梐 +吺 +躘 +慞 +罼 +穨 +摏 +釄 +莋 +呺 +砅 +鴽 +㘭 +㟅 +艴 +犉 +籕 +跐 +惏 +陗 +刋 +襘 +醹 +紽 +痌 +㗀 +撋 +陼 +駷 +艼 +踼 +癏 +慠 +趒 +邍 +姞 +䂬 +堲 +苙 +椌 +嗃 +挶 +岯 +禗 +嵔 +觡 +豜 +睩 +㒿 +塠 +燂 +扤 +恟 +鬝 +鬇 +鬡 +揳 +霠 +㗫 +苐 +蒀 +圌 +戭 +䖃 +𥈭 +勮 +耝 +轞 +胮 +墯 +枮 +罿 +浺 +綪 +爓 +蘃 +襍 +轜 +閠 +畽 +鄊 +嶆 +籭 +蠯 +陑 +瘽 +迆 +賷 +䍡 +韂 +躃 +禴 +簄 +瓟 +碐 +躨 +侜 +岍 +䃸 +趚 +髐 +榅 +粣 +屝 +鴃 +圁 +蝜 +黫 +僽 +丗 +靣 +湏 +抏 +㟽 +跙 +餤 +朙 +㹞 +瞖 +繣 +㨫 +罙 +糒 +惉 +葽 +鼮 +蕳 +豏 +𥱼 +鵮 +獦 +悕 +𠴨 +闟 +惽 +慿 +隉 +椷 +𩅰 +艛 +眽 +凓 +儃 +奨 +埀 +瑫 +駚 +濇 +緶 +峉 +礨 +髢 +瞯 +壥 +姡 +㟯 +髬 +啀 +㶿 +歅 +殀 +縩 +疈 +鳸 +霳 +稬 +圊 +彚 +裠 +埳 +褋 +㔩 +矲 +剶 +硋 +聦 +峞 +浰 +窵 +嘂 +睘 +簵 +腒 +韘 +躣 +甈 +忲 +舽 +襂 +硠 +脃 +鐏 +奯 +脧 +矕 +䠞 +駹 +豶 +訑 +柸 +鰅 +瘨 +趿 +糦 +蟏 +饛 +尰 +諑 +汃 +毺 +鋃 +絚 +馧 +艬 +枍 +爊 +峗 +泙 +碖 +鵕 +尩 +閗 +𤧚 +幩 +塉 +箊 +覂 +玒 +橧 +謟 +庨 +籔 +欑 +厎 +尭 +氉 +蠈 +䓞 +矙 +梡 +瀩 +溔 +煴 +蔲 +僬 +嵢 +梩 +弝 +𣙙 +鞟 +敉 +鮚 +湠 +鐐 +爣 +裻 +䶎 +𦨴 +謿 +垾 +蝂 +睂 +癙 +韽 +㟳 +桒 +鳿 +樏 +峛 +瑉 +僄 +顣 +衺 +殗 +肦 +圑 +朒 +喌 +犦 +㰅 +疁 +氃 +吰 +陻 +盰 +娀 +魶 +㖃 +曒 +娿 +獱 +孏 +酅 +蝡 +齰 +莬 +鄀 +逥 +挿 +觵 +縆 +㟝 +繍 +碙 +㑂 +䎳 +兾 +壸 +賝 +桯 +跁 +跒 +蔍 +舼 +忀 +懭 +媌 +罭 +菵 +狔 +靿 +拪 +㲉 +䔲 +嬀 +鵽 +涳 +朾 +𡸣 +𢫫 +虈 +㜮 +顑 +櫋 +蔪 +旝 +湡 +蹛 +稆 +唽 +㟏 +熂 +龡 +煟 +韅 +韐 +慂 +剳 +掫 +兠 +摋 +羫 +璊 +鵻 +駓 +佌 +蜹 +晲 +矒 +玅 +剰 +斶 +紖 +懴 +駜 +羢 +麳 +㳷 +馞 +爥 +鍚 +鑢 +螵 +嗺 +鏨 +𠙶 +疪 +鷔 +鮧 +轊 +栘 +鼜 +睗 +蟘 +枓 +䖟 +剠 +瞤 +圛 +椳 +籸 +䪌 +鯹 +湌 +丳 +賧 +縭 +檾 +𦨻 +撆 +䩫 +磢 +惥 +譀 +罤 +鞸 +鉎 +㶏 +膁 +甋 +瓀 +懹 +槢 +硊 +弆 +琫 +嵠 +駻 +湢 +杮 +䌨 +訹 +藇 +穯 +蠉 +曭 +蹎 +詄 +毷 +𩃎 +熁 +灜 +蜫 +蜳 +昈 +帩 +鈋 +䐹 +顖 +鄹 +匶 +毾 +礜 +堭 +婞 +鷿 +㙞 +詀 +瘮 +䫜 +㾪 +捘 +屫 +誧 +䲔 +閍 +蒳 +㬋 +遟 +嶀 +葐 +蜼 +㻱 +曡 +䃜 +濴 +䦱 +霫 +譆 +霋 +蕰 +襓 +氋 +鴷 +魦 +㩻 +㡠 +灉 +贑 +燑 +峝 +輷 +烻 +耼 +螉 +跜 +豩 +㑃 +藙 +鋂 +胐 +𣔻 +紒 +瓓 +塯 +辴 +趷 +堛 +㒟 +㗲 +㬊 +䄡 +卄 +姧 +猓 +躗 +覤 +醊 +兎 +罯 +痯 +覸 +詉 +癿 +岋 +歝 +茟 +㘆 +㮰 +淜 +𥉌 +㫰 +鈌 +毵 +狉 +贜 +峬 +汻 +誖 +烓 +睋 +潎 +䲺 +㠓 +歖 +𠜱 +槵 +熚 +萷 +磤 +絸 +鷞 +聻 +屷 +㝵 +諕 +瘂 +㺷 +蚰 +柦 +䍐 +泿 +礰 +摎 +㜕 +㻞 +洓 +喍 +囌 +囐 +䙱 +腨 +妉 +鄛 +鄥 +㵝 +輧 +鱄 +騟 +鈚 +廜 +𨗨 +㶼 +膞 +崯 +硞 +萆 +眒 +譩 +揬 +藑 +匌 +㠾 +㥏 +㢮 +䕢 +帣 +酭 +枦 +孅 +鞙 +丷 +鍭 +䤴 +餂 +愗 +冘 +埛 +㒇 +郕 +蔯 +簰 +刔 +蠩 +耏 +鞹 +𧑅 +觹 +䐑 +磶 +蹵 +鵃 +耛 +蓤 +臄 +轙 +庤 +㒩 +翐 +榥 +晀 +輣 +蟚 +拲 +皠 +穱 +䃔 +䃧 +窡 +絍 +礿 +鑞 +栯 +㾓 +掿 +厞 +淂 +撶 +伹 +鹻 +軓 +岹 +蚷 +榸 +刾 +艂 +㤝 +塕 +蚔 +藾 +攓 +鏬 +珫 +黪 +蟧 +猭 +漑 +粺 +驆 +撘 +亾 +㼌 +蝑 +澓 +揞 +欱 +愶 +泲 +醷 +螴 +芚 +絻 +轃 +漮 +唪 +岉 +鬀 +䱹 +齖 +䂓 +趢 +荓 +覶 +鯾 +諿 +槥 +嚆 +爢 +瓬 +笐 +篢 +舝 +襵 +鎒 +𤝞 +肭 +瘇 +笓 +餑 +豋 +湗 +緎 +肐 +胲 +掤 +潫 +䖴 +𠎝 +𨺗 +諢 +毈 +寱 +唲 +䃭 +峮 +狘 +韊 +䬝 +呰 +㹱 +碞 +畞 +㠌 +黭 +蚘 +豵 +穥 +尯 +㳇 +隵 +灇 +壜 +楰 +彲 +甤 +綹 +旞 +𡏟 +曁 +喩 +𥲤 +郈 +塺 +訧 +絿 +掔 +蠮 +𡱰 +䃺 +宻 +灎 +羵 +𨠵 +糚 +摉 +壷 +勴 +瑃 +鎝 +𥜥 +婥 +鬺 +扢 +肣 +溰 +磩 +耇 +宎 +㔇 +霱 +敚 +汳 +鏄 +儹 +隥 +㿉 +膆 +崏 +𦭵 +郔 +扂 +垗 +㳂 +礛 +缻 +垜 +晱 +訩 +蘪 +珇 +怮 +垝 +㔢 +憛 +痝 +蟨 +鞁 +鶤 +肎 +傝 +䢆 +䰄 +𥊚 +㖀 +㠭 +壵 +墋 +㠔 +橜 +怓 +蚹 +塛 +憪 +鋝 +腶 +嶾 +翍 +溓 +齼 +蔂 +䃂 +鉺 +攑 +瓐 +泎 +眤 +邘 +崝 +稡 +愸 +髥 +輹 +詨 +髆 +麃 +虤 +洐 +婐 +挏 +峑 +嶣 +篬 +葄 +瑎 +瓉 +㳅 +葼 +姙 +䪜 +𩇕 +焭 +剚 +濪 +霵 +僒 + +羭 diff --git a/modules/onnx_ocr_module/src/ppocr/utils/dict/pu_dict.txt b/modules/onnx_ocr_module/src/ppocr/utils/dict/pu_dict.txt new file mode 100644 index 0000000..9500fae --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/utils/dict/pu_dict.txt @@ -0,0 +1,130 @@ +p +u +_ +i +m +g +/ +8 +I +L +S +V +R +C +2 +0 +1 +v +a +l +6 +7 +4 +5 +. +j + +q +e +s +t +ã +o +x +9 +c +n +r +z +ç +õ +3 +A +U +d +º +ô +­ +, +E +; +ó +á +b +D +? +ú +ê +- +h +P +f +à +N +í +O +M +G +É +é +â +F +: +T +Á +" +Q +) +W +J +B +H +( +ö +% +Ö +« +w +K +y +! +k +] +' +Z ++ +Ç +Õ +Y +À +X +µ +» +ª +Í +ü +ä +´ +è +ñ +ß +ï +Ú +ë +Ô +Ï +Ó +[ +Ì +< + +ò +§ +³ +ø +å +# +$ +& +@ diff --git a/modules/onnx_ocr_module/src/ppocr/utils/dict/rs_dict.txt b/modules/onnx_ocr_module/src/ppocr/utils/dict/rs_dict.txt new file mode 100644 index 0000000..d1ce46d --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/utils/dict/rs_dict.txt @@ -0,0 +1,91 @@ +r +s +_ +i +m +g +/ +1 +I +L +S +V +R +C +2 +0 +v +a +l +7 +5 +8 +6 +. +j +p + +t +d +9 +3 +e +š +4 +k +u +ć +c +n +đ +o +z +č +b +ž +f +Z +T +h +M +F +O +Š +B +H +A +E +Đ +Ž +D +P +G +Č +K +U +N +J +Ć +w +y +W +x +Y +X +q +Q +# +& +$ +, +- +% +' +@ +! +: +? +( +É +é ++ diff --git a/modules/onnx_ocr_module/src/ppocr/utils/dict/rsc_dict.txt b/modules/onnx_ocr_module/src/ppocr/utils/dict/rsc_dict.txt new file mode 100644 index 0000000..95dd463 --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/utils/dict/rsc_dict.txt @@ -0,0 +1,134 @@ +r +s +c +_ +i +m +g +/ +5 +I +L +S +V +R +C +2 +0 +1 +v +a +l +9 +7 +8 +. +j +p +м +а +с +и +р +ћ +е +ш +3 +4 +о +г +н +з +в +л +6 +т +ж +у +к +п +њ +д +ч +С +ј +ф +ц +љ +х +О +И +А +б +Ш +К +ђ +џ +М +В +З +Д +Р +У +Н +Т +Б +? +П +Х +Ј +Ц +Г +Љ +Л +Ф +e +n +w +E +F +A +N +f +o +b +M +G +t +y +W +k +P +u +H +B +T +z +h +O +Y +d +U +K +D +x +X +J +Z +Q +q +' +- +@ +é +# +! +, +% +$ +: +& ++ +( +É + diff --git a/modules/onnx_ocr_module/src/ppocr/utils/dict/ru_dict.txt b/modules/onnx_ocr_module/src/ppocr/utils/dict/ru_dict.txt new file mode 100644 index 0000000..3b0cf3a --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/utils/dict/ru_dict.txt @@ -0,0 +1,125 @@ +к +в +а +з +и +у +р +о +н +я +х +п +л +ы +г +е +т +м +д +ж +ш +ь +с +ё +б +й +ч +ю +ц +щ +М +э +ф +А +ъ +С +Ф +Ю +В +К +Т +Н +О +Э +У +И +Г +Л +Р +Д +Б +Ш +П +З +Х +Е +Ж +Я +Ц +Ч +Й +Щ +0 +1 +2 +3 +4 +5 +6 +7 +8 +9 +a +b +c +d +e +f +g +h +i +j +k +l +m +n +o +p +q +r +s +t +u +v +w +x +y +z +A +B +C +D +E +F +G +H +I +J +K +L +M +N +O +P +Q +R +S +T +U +V +W +X +Y +Z + diff --git a/modules/onnx_ocr_module/src/ppocr/utils/dict/samaritan_dict.txt b/modules/onnx_ocr_module/src/ppocr/utils/dict/samaritan_dict.txt new file mode 100644 index 0000000..1fe9187 --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/utils/dict/samaritan_dict.txt @@ -0,0 +1,222 @@ +! +# +$ +% +& +' +( ++ +, +- +. +/ +0 +1 +2 +3 +4 +5 +6 +7 +8 +9 +: +? +@ +A +B +C +D +E +F +G +H +I +J +K +L +M +N +O +P +Q +R +S +T +U +V +W +X +Y +Z +_ +a +b +c +d +e +f +g +h +i +j +k +l +m +n +o +p +q +r +s +t +u +v +w +x +y +z +É +é +ء +آ +أ +ؤ +إ +ئ +ا +ب +ة +ت +ث +ج +ح +خ +د +ذ +ر +ز +س +ش +ص +ض +ط +ظ +ع +غ +ف +ق +ك +ل +م +ن +ه +و +ى +ي +ً +ٌ +ٍ +َ +ُ +ِ +ّ +ْ +ٓ +ٔ +ٰ +ٱ +ٹ +پ +چ +ڈ +ڑ +ژ +ک +ڭ +گ +ں +ھ +ۀ +ہ +ۂ +ۃ +ۆ +ۇ +ۈ +ۋ +ی +ې +ے +ۓ +ە +١ +٢ +٣ +٤ +٥ +٦ +٧ +٨ +٩ +ࠀ +ࠁ +ࠂ +ࠃ +ࠄ +ࠅ +ࠆ +ࠇ +ࠈ +ࠉ +ࠊ +ࠋ +ࠌ +ࠍ +ࠎ +ࠏ +ࠐ +ࠑ +ࠒ +ࠓ +ࠔ +ࠕ +ࠖ +ࠗ +࠘ +࠙ +ࠚ +ࠛ +ࠜ +ࠝ +ࠞ +ࠟ +ࠠ +ࠡ +ࠢ +ࠣ +ࠤ +ࠥ +ࠦ +ࠧ +ࠨ +ࠩ +ࠪ +ࠫ +ࠬ +࠭ +࠰ +࠱ +࠲ +࠳ +࠴ +࠵ +࠶ +࠷ +࠸ +࠹ +࠺ +࠻ +࠼ +࠽ +࠾ diff --git a/modules/onnx_ocr_module/src/ppocr/utils/dict/spin_dict.txt b/modules/onnx_ocr_module/src/ppocr/utils/dict/spin_dict.txt new file mode 100644 index 0000000..b6c16c8 --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/utils/dict/spin_dict.txt @@ -0,0 +1,68 @@ +0 +1 +2 +3 +4 +5 +6 +7 +8 +9 +a +b +c +d +e +f +g +h +i +j +k +l +m +n +o +p +q +r +s +t +u +v +w +x +y +z +: +( +' +- +, +% +> +. +[ +? +) +" += +_ +* +] +; +& ++ +$ +@ +/ +| +! +< +# +` +{ +~ +\ +} +^ diff --git a/modules/onnx_ocr_module/src/ppocr/utils/dict/syriac_dict.txt b/modules/onnx_ocr_module/src/ppocr/utils/dict/syriac_dict.txt new file mode 100644 index 0000000..4417f4a --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/utils/dict/syriac_dict.txt @@ -0,0 +1,157 @@ +! +# +$ +% +& +' +( ++ +, +- +. +/ +0 +1 +2 +3 +4 +5 +6 +7 +8 +9 +: +? +@ +A +B +C +D +E +F +G +H +I +J +K +L +M +N +O +P +Q +R +S +T +U +V +W +X +Y +Z +_ +a +b +c +d +e +f +g +h +i +j +k +l +m +n +o +p +q +r +s +t +u +v +w +x +y +z +É +é +܀ +܁ +܂ +܃ +܄ +܅ +܆ +܇ +܈ +܉ +܊ +܋ +܌ +܍ +܏ +ܐ +ܑ +ܒ +ܓ +ܔ +ܕ +ܖ +ܗ +ܘ +ܙ +ܚ +ܛ +ܜ +ܝ +ܞ +ܟ +ܠ +ܡ +ܢ +ܣ +ܤ +ܥ +ܦ +ܧ +ܨ +ܩ +ܪ +ܫ +ܬ +ܭ +ܮ +ܯ +ܰ +ܱ +ܲ +ܳ +ܴ +ܵ +ܶ +ܷ +ܸ +ܹ +ܺ +ܻ +ܼ +ܽ +ܾ +ܿ +݀ +݁ +݂ +݃ +݄ +݅ +݆ +݇ +݈ +݉ +݊ +ݍ +ݎ +ݏ diff --git a/modules/onnx_ocr_module/src/ppocr/utils/dict/ta_dict.txt b/modules/onnx_ocr_module/src/ppocr/utils/dict/ta_dict.txt new file mode 100644 index 0000000..19d8189 --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/utils/dict/ta_dict.txt @@ -0,0 +1,128 @@ +t +a +_ +i +m +g +/ +3 +I +L +S +V +R +C +2 +0 +1 +v +l +9 +7 +8 +. +j +p +ப +ூ +த +ம +ி +வ +ர +் +ந +ோ +ன +6 +ஆ +ற +ல +5 +ள +ா +ொ +ழ +ு +4 +ெ +ண +க +ட +ை +ே +ச +ய +ஒ +இ +அ +ங +உ +ீ +ஞ +எ +ஓ +ஃ +ஜ +ஷ +ஸ +ஏ +ஊ +ஹ +ஈ +ஐ +ௌ +ஔ +s +c +e +n +w +F +T +O +P +K +A +N +G +Y +E +M +H +U +B +o +b +D +d +r +W +u +y +f +X +k +q +h +J +z +Z +Q +x +- +' +$ +, +% +@ +é +! +# ++ +É +& +: +( +? + diff --git a/modules/onnx_ocr_module/src/ppocr/utils/dict/table_dict.txt b/modules/onnx_ocr_module/src/ppocr/utils/dict/table_dict.txt new file mode 100644 index 0000000..2ef028c --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/utils/dict/table_dict.txt @@ -0,0 +1,277 @@ +← + +☆ +─ +α + + +⋅ +$ +ω +ψ +χ +( +υ +≥ +σ +, +ρ +ε +0 +■ +4 +8 +✗ +b +< +✓ +Ψ +Ω +€ +D +3 +Π +H +║ + +L +Φ +Χ +θ +P +κ +λ +μ +T +ξ +X +β +γ +δ +\ +ζ +η +` +d + +h +f +l +Θ +p +√ +t + +x +Β +Γ +Δ +| +ǂ +ɛ +j +̧ +➢ +⁡ +̌ +′ +« +△ +▲ +# + +' +Ι ++ +¶ +/ +▼ +⇑ +□ +· +7 +▪ +; +? +➔ +∩ +C +÷ +G +⇒ +K + +O +S +С +W +Α +[ +○ +_ +● +‡ +c +z +g + +o + +〈 +〉 +s +⩽ +w +φ +ʹ +{ +» +∣ +̆ +e +ˆ +∈ +τ +◆ +ι +∅ +∆ +∙ +∘ +Ø +ß +✔ +∞ +∑ +− +× +◊ +∗ +∖ +˃ +˂ +∫ +" +i +& +π +↔ +* +∥ +æ +∧ +. +⁄ +ø +Q +∼ +6 +⁎ +: +★ +> +a +B +≈ +F +J +̄ +N +♯ +R +V + +― +Z +♣ +^ +¤ +¥ +§ + +¢ +£ +≦ +­ +≤ +‖ +Λ +© +n +↓ +→ +↑ +r +° +± +v + +♂ +k +♀ +~ +ᅟ +̇ +@ +” +♦ +ł +® +⊕ +„ +! + +% +⇓ +) +- +1 +5 +9 += +А +A +‰ +⋆ +Σ +E +◦ +I +※ +M +m +̨ +⩾ +† + +• +U +Y +
 +] +̸ +2 +‐ +– +‒ +̂ +— +̀ +́ +’ +‘ +⋮ +⋯ +̊ +“ +̈ +≧ +q +u +ı +y + +​ +̃ +} +ν diff --git a/modules/onnx_ocr_module/src/ppocr/utils/dict/table_master_structure_dict.txt b/modules/onnx_ocr_module/src/ppocr/utils/dict/table_master_structure_dict.txt new file mode 100644 index 0000000..95ab253 --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/utils/dict/table_master_structure_dict.txt @@ -0,0 +1,39 @@ + + + + + + + + + + + colspan="2" + colspan="3" + + + rowspan="2" + colspan="4" + colspan="6" + rowspan="3" + colspan="9" + colspan="10" + colspan="7" + rowspan="4" + rowspan="5" + rowspan="9" + colspan="8" + rowspan="8" + rowspan="6" + rowspan="7" + rowspan="10" + + + + + + + + diff --git a/modules/onnx_ocr_module/src/ppocr/utils/dict/table_structure_dict.txt b/modules/onnx_ocr_module/src/ppocr/utils/dict/table_structure_dict.txt new file mode 100644 index 0000000..fec6f7d --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/utils/dict/table_structure_dict.txt @@ -0,0 +1,28 @@ + + + + + + + + + + colspan="2" + colspan="3" + rowspan="2" + colspan="4" + colspan="6" + rowspan="3" + colspan="9" + colspan="10" + colspan="7" + rowspan="4" + rowspan="5" + rowspan="9" + colspan="8" + rowspan="8" + rowspan="6" + rowspan="7" + rowspan="10" diff --git a/modules/onnx_ocr_module/src/ppocr/utils/dict/table_structure_dict_ch.txt b/modules/onnx_ocr_module/src/ppocr/utils/dict/table_structure_dict_ch.txt new file mode 100644 index 0000000..0c59c0e --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/utils/dict/table_structure_dict_ch.txt @@ -0,0 +1,48 @@ + + + + + + + + + + colspan="2" + colspan="3" + colspan="4" + colspan="5" + colspan="6" + colspan="7" + colspan="8" + colspan="9" + colspan="10" + colspan="11" + colspan="12" + colspan="13" + colspan="14" + colspan="15" + colspan="16" + colspan="17" + colspan="18" + colspan="19" + colspan="20" + rowspan="2" + rowspan="3" + rowspan="4" + rowspan="5" + rowspan="6" + rowspan="7" + rowspan="8" + rowspan="9" + rowspan="10" + rowspan="11" + rowspan="12" + rowspan="13" + rowspan="14" + rowspan="15" + rowspan="16" + rowspan="17" + rowspan="18" + rowspan="19" + rowspan="20" diff --git a/modules/onnx_ocr_module/src/ppocr/utils/dict/te_dict.txt b/modules/onnx_ocr_module/src/ppocr/utils/dict/te_dict.txt new file mode 100644 index 0000000..83d74cc --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/utils/dict/te_dict.txt @@ -0,0 +1,151 @@ +t +e +_ +i +m +g +/ +5 +I +L +S +V +R +C +2 +0 +1 +v +a +l +3 +4 +8 +9 +. +j +p +త +ె +ర +క +్ +ి +ం +చ +ే +ద +ు +7 +6 +ఉ +ా +మ +ట +ో +వ +ప +ల +శ +ఆ +య +ై +భ +' +ీ +గ +ూ +డ +ధ +హ +న +జ +స +[ +‌ +ష +అ +ణ +ఫ +బ +ఎ +; +ళ +థ +ొ +ఠ +ృ +ఒ +ఇ +ః +ఊ +ఖ +- +ఐ +ఘ +ౌ +ఏ +ఈ +ఛ +, +ఓ +ఞ +| +? +: +ఢ +" +( +” +! ++ +) +* += +& +“ +€ +] +£ +$ +s +c +n +w +k +J +G +u +d +r +E +o +h +y +b +f +B +M +O +T +N +D +P +A +F +x +W +Y +U +H +K +X +z +Z +Q +q +É +% +# +@ +é diff --git a/modules/onnx_ocr_module/src/ppocr/utils/dict/th_dict.txt b/modules/onnx_ocr_module/src/ppocr/utils/dict/th_dict.txt new file mode 100644 index 0000000..83a5b54 --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/utils/dict/th_dict.txt @@ -0,0 +1,81 @@ +ก +ข +ฃ +ค +ฅ +ฆ +ง +จ +ฉ +ช +ซ +ฌ +ญ +ฎ +ฏ +ฐ +ฑ +ฒ +ณ +ด +ต +ถ +ท +ธ +น +บ +ป +ผ +ฝ +พ +ฟ +ภ +ม +ย +ร +ล +ว +ศ +ษ +ส +ห +ฬ +อ +ฮ +ะ +ั +า +ำ +ิ +ี +ึ +ื +ุ +ู +เ +แ +โ +ใ +ไ +็ +่ +้ +๊ +๋ +์ +๐ +๑ +๒ +๓ +๔ +๕ +๖ +๗ +๘ +๙ +ฯ +ๆ +ฤ +ฤา +ฦ +ฦา diff --git a/modules/onnx_ocr_module/src/ppocr/utils/dict/ug_dict.txt b/modules/onnx_ocr_module/src/ppocr/utils/dict/ug_dict.txt new file mode 100644 index 0000000..8fc0db7 --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/utils/dict/ug_dict.txt @@ -0,0 +1,131 @@ +` +~ += +| + +_ +- +, +; +: +! +? +/ +. +' +" +( +) +[ +] +{ +} +@ +$ +* +\ +& +# +% ++ +0 +1 +2 +3 +4 +5 +6 +7 +8 +9 +a +A +b +B +c +C +d +D +e +E +f +F +g +G +h +H +i +I +j +J +k +K +l +L +m +M +n +N +o +O +p +P +q +Q +r +R +s +S +t +T +u +U +v +V +w +W +x +X +y +Y +z +Z +Ö +ö +Ü +ü +Ë +ë +ا +ە +ب +پ +ت +ج +چ +خ +د +ر +ز +ژ +س +ش +غ +ف +ق +ك +گ +ڭ +ل +م +ن +ھ +و +ۇ +ۆ +ۈ +ۋ +ې +ى +ي +ئ diff --git a/modules/onnx_ocr_module/src/ppocr/utils/dict/uk_dict.txt b/modules/onnx_ocr_module/src/ppocr/utils/dict/uk_dict.txt new file mode 100644 index 0000000..c5ffc0a --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/utils/dict/uk_dict.txt @@ -0,0 +1,142 @@ +u +k +_ +i +m +g +/ +1 +6 +I +L +S +V +R +C +2 +0 +v +a +l +7 +9 +. +j +p +в +і +д +п +о +н +с +т +ю +4 +5 +3 +а +и +м +е +р +ч +у +Б +з +л +к +8 +А +В +г +є +б +ь +х +ґ +ш +ц +ф +я +щ +ж +Г +Х +У +Т +Е +І +Н +П +З +Л +Ю +С +Д +М +К +Р +Ф +О +Ц +И +Я +Ч +Ш +Ж +Є +Ґ +Ь +s +c +e +n +w +A +P +r +E +t +o +h +d +y +M +G +N +F +B +T +D +U +O +W +Z +f +H +Y +b +K +z +x +Q +X +q +J +$ +- +' +# +& +% +? +: +! +, ++ +@ +( +é +É + diff --git a/modules/onnx_ocr_module/src/ppocr/utils/dict/unimernet_tokenizer/tokenizer.json b/modules/onnx_ocr_module/src/ppocr/utils/dict/unimernet_tokenizer/tokenizer.json new file mode 100644 index 0000000..55a7760 --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/utils/dict/unimernet_tokenizer/tokenizer.json @@ -0,0 +1,100067 @@ +{ + "version": "1.0", + "truncation": { + "direction": "Right", + "max_length": 4096, + "strategy": "LongestFirst", + "stride": 0 + }, + "padding": { + "strategy": { + "Fixed": 4096 + }, + "direction": "Right", + "pad_to_multiple_of": null, + "pad_id": 1, + "pad_type_id": 0, + "pad_token": "" + }, + "added_tokens": [ + { + "id": 0, + "content": "", + "single_word": false, + "lstrip": false, + "rstrip": false, + "normalized": false, + "special": true + }, + { + "id": 1, + "content": "", + "single_word": false, + "lstrip": false, + "rstrip": false, + "normalized": false, + "special": true + }, + { + "id": 2, + "content": "", + "single_word": false, + "lstrip": false, + "rstrip": false, + "normalized": false, + "special": true + }, + { + "id": 3, + "content": "", + "single_word": false, + "lstrip": false, + "rstrip": false, + "normalized": false, + "special": true + }, + { + "id": 4, + "content": "[START_REF]", + "single_word": false, + "lstrip": false, + "rstrip": false, + "normalized": false, + "special": true + }, + { + "id": 5, + "content": "[END_REF]", + "single_word": false, + "lstrip": false, + "rstrip": false, + "normalized": false, + "special": true + }, + { + "id": 6, + "content": "[IMAGE]", + "single_word": false, + "lstrip": false, + "rstrip": false, + "normalized": false, + "special": true + }, + { + "id": 7, + "content": "", + "single_word": false, + "lstrip": false, + "rstrip": false, + "normalized": false, + "special": true + }, + { + "id": 8, + "content": "", + "single_word": false, + "lstrip": false, + "rstrip": false, + "normalized": false, + "special": true + }, + { + "id": 9, + "content": "", + "single_word": false, + "lstrip": false, + "rstrip": false, + "normalized": false, + "special": true + }, + { + "id": 10, + "content": "", + "single_word": false, + "lstrip": false, + "rstrip": false, + "normalized": false, + "special": true + }, + { + "id": 11, + "content": "[START_SUP]", + "single_word": false, + "lstrip": false, + "rstrip": false, + "normalized": false, + "special": true + }, + { + "id": 12, + "content": "[END_SUP]", + "single_word": false, + "lstrip": false, + "rstrip": false, + "normalized": false, + "special": true + }, + { + "id": 13, + "content": "[START_SUB]", + "single_word": false, + "lstrip": false, + "rstrip": false, + "normalized": false, + "special": true + }, + { + "id": 14, + "content": "[END_SUB]", + "single_word": false, + "lstrip": false, + "rstrip": false, + "normalized": false, + "special": true + }, + { + "id": 15, + "content": "[START_DNA]", + "single_word": false, + "lstrip": false, + "rstrip": false, + "normalized": false, + "special": true + }, + { + "id": 16, + "content": "[END_DNA]", + "single_word": false, + "lstrip": false, + "rstrip": false, + "normalized": false, + "special": true + }, + { + "id": 17, + "content": "[START_AMINO]", + "single_word": false, + "lstrip": false, + "rstrip": false, + "normalized": false, + "special": true + }, + { + "id": 18, + "content": "[END_AMINO]", + "single_word": false, + "lstrip": false, + "rstrip": false, + "normalized": false, + "special": true + }, + { + "id": 19, + "content": "[START_SMILES]", + "single_word": false, + "lstrip": false, + "rstrip": false, + "normalized": false, + "special": true + }, + { + "id": 20, + "content": "[END_SMILES]", + "single_word": false, + "lstrip": false, + "rstrip": false, + "normalized": false, + "special": true + }, + { + "id": 21, + "content": "[START_I_SMILES]", + "single_word": false, + "lstrip": false, + "rstrip": false, + "normalized": false, + "special": true + }, + { + "id": 22, + "content": "[END_I_SMILES]", + "single_word": false, + "lstrip": false, + "rstrip": false, + "normalized": false, + "special": true + } + ], + "normalizer": { + "type": "NFKC" + }, + "pre_tokenizer": { + "type": "Sequence", + "pretokenizers": [ + { + "type": "Split", + "pattern": { + "String": "SPL1T-TH1S-Pl3A5E" + }, + "behavior": "Removed", + "invert": false + }, + { + "type": "Digits", + "individual_digits": true + }, + { + "type": "Split", + "pattern": { + "Regex": "[\\(\\)\\[\\]\\{\\}]|([!\"\\#\\$%\\&'\\*\\+,\\-\\./:;<=>\\?\\\\\\^_`\\|\\~])\\1*" + }, + "behavior": "Isolated", + "invert": false + }, + { + "type": "Split", + "pattern": { + "String": "\n" + }, + "behavior": "Isolated", + "invert": false + }, + { + "type": "ByteLevel", + "add_prefix_space": false, + "trim_offsets": true, + "use_regex": true + } + ] + }, + "post_processor": { + "type": "TemplateProcessing", + "single": [ + { + "SpecialToken": { + "id": "", + "type_id": 0 + } + }, + { + "Sequence": { + "id": "A", + "type_id": 0 + } + }, + { + "SpecialToken": { + "id": "", + "type_id": 0 + } + } + ], + "pair": [ + { + "Sequence": { + "id": "A", + "type_id": 0 + } + }, + { + "Sequence": { + "id": "B", + "type_id": 1 + } + } + ], + "special_tokens": { + "": { + "id": "", + "ids": [ + 2 + ], + "tokens": [ + "" + ] + }, + "": { + "id": "", + "ids": [ + 0 + ], + "tokens": [ + "" + ] + } + } + }, + "decoder": { + "type": "ByteLevel", + "add_prefix_space": true, + "trim_offsets": true, + "use_regex": true + }, + "model": { + "type": "BPE", + "dropout": null, + "unk_token": null, + "continuing_subword_prefix": null, + "end_of_word_suffix": null, + "fuse_unk": false, + "byte_fallback": false, + "vocab": { + "": 0, + "": 1, + "": 2, + "": 3, + "[START_REF]": 4, + "[END_REF]": 5, + "[IMAGE]": 6, + "": 7, + "": 8, + "": 9, + "": 10, + "[START_SUP]": 11, + "[END_SUP]": 12, + "[START_SUB]": 13, + "[END_SUB]": 14, + "[START_DNA]": 15, + "[END_DNA]": 16, + "[START_AMINO]": 17, + "[END_AMINO]": 18, + "[START_SMILES]": 19, + "[END_SMILES]": 20, + "[START_I_SMILES]": 21, + "[END_I_SMILES]": 22, + "!": 23, + "\"": 24, + "#": 25, + "$": 26, + "%": 27, + "&": 28, + "'": 29, + "(": 30, + ")": 31, + "*": 32, + "+": 33, + ",": 34, + "-": 35, + ".": 36, + "/": 37, + "0": 38, + "1": 39, + "2": 40, + "3": 41, + "4": 42, + "5": 43, + "6": 44, + "7": 45, + "8": 46, + "9": 47, + ":": 48, + ";": 49, + "<": 50, + "=": 51, + ">": 52, + "?": 53, + "@": 54, + "A": 55, + "B": 56, + "C": 57, + "D": 58, + "E": 59, + "F": 60, + "G": 61, + "H": 62, + "I": 63, + "J": 64, + "K": 65, + "L": 66, + "M": 67, + "N": 68, + "O": 69, + "P": 70, + "Q": 71, + "R": 72, + "S": 73, + "T": 74, + "U": 75, + "V": 76, + "W": 77, + "X": 78, + "Y": 79, + "Z": 80, + "[": 81, + "\\": 82, + "]": 83, + "^": 84, + "_": 85, + "`": 86, + "a": 87, + "b": 88, + "c": 89, + "d": 90, + "e": 91, + "f": 92, + "g": 93, + "h": 94, + "i": 95, + "j": 96, + "k": 97, + "l": 98, + "m": 99, + "n": 100, + "o": 101, + "p": 102, + "q": 103, + "r": 104, + "s": 105, + "t": 106, + "u": 107, + "v": 108, + "w": 109, + "x": 110, + "y": 111, + "z": 112, + "{": 113, + "|": 114, + "}": 115, + "~": 116, + "¡": 117, + "¢": 118, + "£": 119, + "¤": 120, + "¥": 121, + "¦": 122, + "§": 123, + "¨": 124, + "©": 125, + "ª": 126, + "«": 127, + "¬": 128, + "®": 129, + "¯": 130, + "°": 131, + "±": 132, + "²": 133, + "³": 134, + "´": 135, + "µ": 136, + "¶": 137, + "·": 138, + "¸": 139, + "¹": 140, + "º": 141, + "»": 142, + "¼": 143, + "½": 144, + "¾": 145, + "¿": 146, + "À": 147, + "Á": 148, + "Â": 149, + "Ã": 150, + "Ä": 151, + "Å": 152, + "Æ": 153, + "Ç": 154, + "È": 155, + "É": 156, + "Ê": 157, + "Ë": 158, + "Ì": 159, + "Í": 160, + "Î": 161, + "Ï": 162, + "Ð": 163, + "Ñ": 164, + "Ò": 165, + "Ó": 166, + "Ô": 167, + "Õ": 168, + "Ö": 169, + "×": 170, + "Ø": 171, + "Ù": 172, + "Ú": 173, + "Û": 174, + "Ü": 175, + "Ý": 176, + "Þ": 177, + "ß": 178, + "à": 179, + "á": 180, + "â": 181, + "ã": 182, + "ä": 183, + "å": 184, + "æ": 185, + "ç": 186, + "è": 187, + "é": 188, + "ê": 189, + "ë": 190, + "ì": 191, + "í": 192, + "î": 193, + "ï": 194, + "ð": 195, + "ñ": 196, + "ò": 197, + "ó": 198, + "ô": 199, + "õ": 200, + "ö": 201, + "÷": 202, + "ø": 203, + "ù": 204, + "ú": 205, + "û": 206, + "ü": 207, + "ý": 208, + "þ": 209, + "ÿ": 210, + "Ā": 211, + "ā": 212, + "Ă": 213, + "ă": 214, + "Ą": 215, + "ą": 216, + "Ć": 217, + "ć": 218, + "Ĉ": 219, + "ĉ": 220, + "Ċ": 221, + "ċ": 222, + "Č": 223, + "č": 224, + "Ď": 225, + "ď": 226, + "Đ": 227, + "đ": 228, + "Ē": 229, + "ē": 230, + "Ĕ": 231, + "ĕ": 232, + "Ė": 233, + "ė": 234, + "Ę": 235, + "ę": 236, + "Ě": 237, + "ě": 238, + "Ĝ": 239, + "ĝ": 240, + "Ğ": 241, + "ğ": 242, + "Ġ": 243, + "ġ": 244, + "Ģ": 245, + "ģ": 246, + "Ĥ": 247, + "ĥ": 248, + "Ħ": 249, + "ħ": 250, + "Ĩ": 251, + "ĩ": 252, + "Ī": 253, + "ī": 254, + "Ĭ": 255, + "ĭ": 256, + "Į": 257, + "į": 258, + "İ": 259, + "ı": 260, + "IJ": 261, + "ij": 262, + "Ĵ": 263, + "ĵ": 264, + "Ķ": 265, + "ķ": 266, + "ĸ": 267, + "Ĺ": 268, + "ĺ": 269, + "Ļ": 270, + "ļ": 271, + "Ľ": 272, + "ľ": 273, + "Ŀ": 274, + "ŀ": 275, + "Ł": 276, + "ł": 277, + "Ń": 278, + "Ġt": 279, + "in": 280, + "Ġa": 281, + "he": 282, + "on": 283, + "re": 284, + "at": 285, + "Ġthe": 286, + "er": 287, + "Ġs": 288, + "Ġo": 289, + "en": 290, + "al": 291, + "Ġc": 292, + "ti": 293, + "or": 294, + "ed": 295, + "es": 296, + "is": 297, + "Ġp": 298, + "Ġof": 299, + "nd": 300, + "Ġin": 301, + "Ġf": 302, + "Ġw": 303, + "ĠĠ": 304, + "it": 305, + "an": 306, + "ro": 307, + "ar": 308, + "Ġd": 309, + "Ġm": 310, + "Ġb": 311, + "Ġand": 312, + "ic": 313, + "le": 314, + "ing": 315, + "ion": 316, + "as": 317, + "Ġe": 318, + "Ġre": 319, + "ation": 320, + "Ġto": 321, + "el": 322, + "ent": 323, + "ac": 324, + "et": 325, + "ec": 326, + "tion": 327, + "om": 328, + "st": 329, + "ĠT": 330, + "Ġn": 331, + "Ġth": 332, + "ol": 333, + "ul": 334, + "im": 335, + "RE": 336, + "ig": 337, + "us": 338, + "REF": 339, + "Ġl": 340, + "Ġh": 341, + "ur": 342, + "Ġis": 343, + "ĠĠĠĠ": 344, + "Ġfor": 345, + "id": 346, + "am": 347, + "ĠS": 348, + "ve": 349, + "il": 350, + "ĠA": 351, + "ĠC": 352, + "Ġg": 353, + "ot": 354, + "ith": 355, + "ly": 356, + "ce": 357, + "Ġcon": 358, + "ow": 359, + "Ġst": 360, + "ut": 361, + "os": 362, + "Ġwith": 363, + "od": 364, + "ra": 365, + "Ġv": 366, + "Ġpro": 367, + "um": 368, + "ĠI": 369, + "if": 370, + "uc": 371, + "ter": 372, + "un": 373, + "AR": 374, + "ST": 375, + "res": 376, + "Ġon": 377, + "EN": 378, + "ere": 379, + "ĠP": 380, + "ĠThe": 381, + "ĠM": 382, + "Ġas": 383, + "ART": 384, + "Ġan": 385, + "END": 386, + "START": 387, + "Ġthat": 388, + "qu": 389, + "em": 390, + "Ġbe": 391, + "Ġex": 392, + "ri": 393, + "ab": 394, + "ity": 395, + "tic": 396, + "ver": 397, + "Ġal": 398, + "pl": 399, + "ts": 400, + "ĠF": 401, + "Ġâ": 402, + "ure": 403, + "Ġby": 404, + "ate": 405, + "ag": 406, + "ir": 407, + "oc": 408, + "per": 409, + "ĠB": 410, + "ay": 411, + "ĠD": 412, + "Ġcom": 413, + "ĠH": 414, + "ated": 415, + "ĠR": 416, + "Ġare": 417, + "rom": 418, + "ĠE": 419, + "op": 420, + "ad": 421, + "se": 422, + "ĠL": 423, + "igh": 424, + "ĠN": 425, + "ment": 426, + "her": 427, + "og": 428, + "ain": 429, + "ect": 430, + "ud": 431, + "Ġde": 432, + "Ġr": 433, + "Ġat": 434, + "Ġwas": 435, + "Ġus": 436, + "Ġres": 437, + "ell": 438, + "iz": 439, + "ine": 440, + "ph": 441, + "Ġac": 442, + "ess": 443, + "ore": 444, + "ical": 445, + "th": 446, + "und": 447, + "rac": 448, + "Ġwe": 449, + "ath": 450, + "ĠG": 451, + "Ġfrom": 452, + "ati": 453, + "up": 454, + "ist": 455, + "ant": 456, + "Ġor": 457, + "ff": 458, + "Ġcomp": 459, + "Ġwh": 460, + "ĠW": 461, + "ch": 462, + "ers": 463, + "Ġsp": 464, + "orm": 465, + "Ġch": 466, + "ations": 467, + "ran": 468, + "ub": 469, + "te": 470, + "di": 471, + "Ġsh": 472, + "ge": 473, + "ase": 474, + "Ġwere": 475, + "ĠĠĠĠĠĠĠĠ": 476, + "ĠÎ": 477, + "ap": 478, + "ĠIn": 479, + "and": 480, + "Ġse": 481, + "vel": 482, + "Ġim": 483, + "ĠâĪ": 484, + "ens": 485, + "ies": 486, + "ich": 487, + "ight": 488, + "duc": 489, + "ĠO": 490, + "Ġit": 491, + "tions": 492, + "end": 493, + "Ġco": 494, + "Ġthis": 495, + "Ġcan": 496, + "Ġk": 497, + "âĢ": 498, + "lec": 499, + "ted": 500, + "Ġmod": 501, + "math": 502, + "Ġcont": 503, + "Ġne": 504, + "Ġpar": 505, + "ib": 506, + "ĠĠĠ": 507, + "Ġle": 508, + "iv": 509, + "ug": 510, + "ence": 511, + "ign": 512, + "ous": 513, + "ents": 514, + "ys": 515, + "ave": 516, + "red": 517, + "ress": 518, + "able": 519, + "por": 520, + "all": 521, + "iff": 522, + "est": 523, + "Ġap": 524, + "Ġinc": 525, + "nt": 526, + "ary": 527, + "iti": 528, + "Ġwhich": 529, + "Ġnot": 530, + "form": 531, + "Ġsy": 532, + "Ġad": 533, + "low": 534, + "ak": 535, + "Ġper": 536, + "Ġhe": 537, + "pro": 538, + "ance": 539, + "ial": 540, + "ue": 541, + "Ġen": 542, + "Ġcl": 543, + "ass": 544, + "ip": 545, + "rans": 546, + "Ġob": 547, + "Ġgen": 548, + "tim": 549, + "Ġdis": 550, + "unc": 551, + "Ġint": 552, + "ep": 553, + "etw": 554, + "Ġdiff": 555, + "ach": 556, + "ther": 557, + "ime": 558, + "age": 559, + "ple": 560, + "ill": 561, + "yp": 562, + "ĠK": 563, + "act": 564, + "ari": 565, + "Ġmet": 566, + "ors": 567, + "Ġhave": 568, + "Ġstud": 569, + "ong": 570, + "ĠU": 571, + "Ġpl": 572, + "ide": 573, + "ma": 574, + "hen": 575, + "ific": 576, + "ome": 577, + "Ġi": 578, + "ular": 579, + "ĠV": 580, + "ally": 581, + "Ġshow": 582, + "rib": 583, + "ia": 584, + "enti": 585, + "Ġass": 586, + "ond": 587, + "ft": 588, + "Ġab": 589, + "Ġinter": 590, + "ĠTh": 591, + "The": 592, + "str": 593, + "Ġcell": 594, + "cal": 595, + "Ġmodel": 596, + "ata": 597, + "ast": 598, + "Ġeff": 599, + "Ġtrans": 600, + "ates": 601, + "ased": 602, + "ost": 603, + "vi": 604, + "ang": 605, + "our": 606, + "Ġme": 607, + "ard": 608, + "Ġdiffere": 609, + "Ġpre": 610, + "Ġdi": 611, + "ĠâĪĴ": 612, + "olog": 613, + "ution": 614, + "ound": 615, + "ace": 616, + "Ġresul": 617, + "erm": 618, + "pos": 619, + "here": 620, + "tive": 621, + "ord": 622, + "so": 623, + "stem": 624, + "yl": 625, + "Ġph": 626, + "Ġy": 627, + "ame": 628, + "ork": 629, + "ative": 630, + "Ġqu": 631, + "ric": 632, + "SU": 633, + "wo": 634, + "Ġun": 635, + "Ġev": 636, + "are": 637, + "##": 638, + "de": 639, + "een": 640, + "tiv": 641, + "Ġgro": 642, + "ory": 643, + "Ġcons": 644, + "Ġsub": 645, + "ta": 646, + "--": 647, + "Ġstr": 648, + "ber": 649, + "erv": 650, + "etween": 651, + "enc": 652, + "Ġanal": 653, + "int": 654, + "Ġhas": 655, + "uch": 656, + "Ġreg": 657, + "Ġbetween": 658, + "Ġdet": 659, + "Ġall": 660, + "cess": 661, + "Ġexp": 662, + "ection": 663, + "ĠâĢ": 664, + "ind": 665, + "ater": 666, + "Ġsign": 667, + "pt": 668, + "ugh": 669, + "ite": 670, + "ility": 671, + "Ġusing": 672, + "Ġval": 673, + "Ġro": 674, + "ree": 675, + "Ġrel": 676, + "out": 677, + "Ġfunc": 678, + "ition": 679, + "Ġcor": 680, + "Ġalso": 681, + "Ġtwo": 682, + "ne": 683, + "ĠJ": 684, + "Ġsystem": 685, + "cl": 686, + "uct": 687, + "Ġsim": 688, + "tain": 689, + "ust": 690, + "ied": 691, + "port": 692, + "Ġrec": 693, + "Ġresp": 694, + "Ġdata": 695, + "rm": 696, + "resent": 697, + "uld": 698, + "xt": 699, + "Ġj": 700, + "ry": 701, + "ack": 702, + "Ġra": 703, + "par": 704, + "Ġform": 705, + "Ġsc": 706, + "frac": 707, + "ĠWe": 708, + "ating": 709, + "ech": 710, + "hod": 711, + "Ġfol": 712, + "ined": 713, + "ĠSt": 714, + "ual": 715, + "Ġused": 716, + "Ġone": 717, + "Ġdes": 718, + "ĠÏ": 719, + "Ġvari": 720, + "Ġdist": 721, + "Ġnum": 722, + "ym": 723, + "ew": 724, + "rec": 725, + "ob": 726, + "Ġinf": 727, + "Ġar": 728, + "lect": 729, + "ll": 730, + "ons": 731, + "ĠThis": 732, + "ose": 733, + "ile": 734, + "play": 735, + "ear": 736, + "ox": 737, + "ures": 738, + "one": 739, + "Ġstudy": 740, + "ysis": 741, + "Ġfollow": 742, + "yle": 743, + "ract": 744, + "dis": 745, + "Ġpos": 746, + "right": 747, + "Ġthan": 748, + "ros": 749, + "av": 750, + "Fig": 751, + "Ġtime": 752, + "ization": 753, + "ulation": 754, + "ized": 755, + "Ġsur": 756, + "oth": 757, + "Ġout": 758, + "Ġcol": 759, + "ature": 760, + "ive": 761, + "Ġsol": 762, + "Ġx": 763, + "eld": 764, + "Ġother": 765, + "plic": 766, + "Ġdef": 767, + "erg": 768, + "Ġgener": 769, + "ely": 770, + "Ġbeen": 771, + "Ġincre": 772, + "Ġthese": 773, + "Ġno": 774, + "ax": 775, + "style": 776, + "arg": 777, + "ian": 778, + "Ġind": 779, + "Ġsuch": 780, + "Ġfunction": 781, + "ting": 782, + "Ġequ": 783, + "aus": 784, + "Ġund": 785, + "mathb": 786, + "tical": 787, + "Ġhigh": 788, + "rain": 789, + "Ġam": 790, + "ield": 791, + "oun": 792, + "ression": 793, + "Ġspec": 794, + "Ġop": 795, + "Ġdec": 796, + "Ġover": 797, + "Ġmethod": 798, + "Ġset": 799, + "âĪ": 800, + "Ġif": 801, + "dition": 802, + "ues": 803, + "ects": 804, + "display": 805, + "hem": 806, + "Ġpati": 807, + "Ġresults": 808, + "old": 809, + "anc": 810, + "displaystyle": 811, + "Ġeach": 812, + "Ġmore": 813, + "les": 814, + "pr": 815, + "acter": 816, + "Ġtheir": 817, + "Ġacc": 818, + "Ġappro": 819, + "iss": 820, + "ize": 821, + "Ġinv": 822, + "ases": 823, + "Ġcells": 824, + "irst": 825, + "lu": 826, + "ail": 827, + "Ġmeas": 828, + "Ġlow": 829, + "ov": 830, + "the": 831, + "ik": 832, + "**": 833, + "ef": 834, + "Ġbut": 835, + "hes": 836, + "fter": 837, + "Ġdifferent": 838, + "vely": 839, + "Ġext": 840, + "Ġthere": 841, + "oci": 842, + "Ġprob": 843, + "Ġits": 844, + "ron": 845, + "ments": 846, + "Ġag": 847, + "NA": 848, + "Ġpo": 849, + "ice": 850, + "ype": 851, + "Ġgroup": 852, + "âĢĵ": 853, + "ever": 854, + "ult": 855, + "ism": 856, + "tern": 857, + "ability": 858, + "ions": 859, + "ark": 860, + "Ġnon": 861, + "to": 862, + "ĠĠĠĠĠĠĠ": 863, + "Ġobs": 864, + "Ġtre": 865, + "als": 866, + "left": 867, + "ĠPro": 868, + "Ġonly": 869, + "Ġman": 870, + "der": 871, + "Ġpol": 872, + "uring": 873, + "amet": 874, + "rol": 875, + "In": 876, + "yn": 877, + "Ġunder": 878, + "ĠCh": 879, + "Ġwhere": 880, + "ood": 881, + "ĠX": 882, + "nce": 883, + "Ġpartic": 884, + "ected": 885, + "ĠFig": 886, + "Ġem": 887, + "Ġfact": 888, + "ĠAn": 889, + "Ġperform": 890, + "Ġso": 891, + "Ġanalysis": 892, + "stract": 893, + "hed": 894, + "Ġmay": 895, + "atic": 896, + "Ġrep": 897, + "tein": 898, + "duced": 899, + "Ġup": 900, + "Ġinto": 901, + "Ġnumber": 902, + "Ġour": 903, + "Ġet": 904, + "eg": 905, + "itle": 906, + "over": 907, + "ix": 908, + "ator": 909, + "ulti": 910, + "Ġincl": 911, + "ould": 912, + "ici": 913, + "bstract": 914, + "Ġcomple": 915, + "Ġpatients": 916, + "Ġdo": 917, + "Ġexper": 918, + "vid": 919, + "ange": 920, + "Ġlevel": 921, + "Ġprocess": 922, + "mathcal": 923, + "ps": 924, + "Ġsignific": 925, + "Ġsam": 926, + "Title": 927, + "Ġbl": 928, + "Ġstruct": 929, + "eta": 930, + "Ġobserv": 931, + "raph": 932, + "gr": 933, + "Ġactiv": 934, + "Ġfirst": 935, + "velop": 936, + "gen": 937, + "ible": 938, + "Ġsm": 939, + "Ġwill": 940, + "ĠQ": 941, + "Ġmeasure": 942, + "put": 943, + "Ġloc": 944, + "Ġmo": 945, + "vers": 946, + "of": 947, + "tal": 948, + "ered": 949, + "own": 950, + "Ġmat": 951, + "ities": 952, + "til": 953, + "inal": 954, + "Ġcar": 955, + "pha": 956, + "Ġboth": 957, + "Ġcur": 958, + "SUB": 959, + "its": 960, + "rel": 961, + "Ġwhen": 962, + "Ġz": 963, + "Ġchar": 964, + "Ġbi": 965, + "cent": 966, + "Ġthen": 967, + "ise": 968, + "owever": 969, + "Ġmin": 970, + "ĠFor": 971, + "ĠY": 972, + "ption": 973, + "Ġes": 974, + "mun": 975, + "Ġinclud": 976, + "istic": 977, + "con": 978, + "Ġobtain": 979, + "ared": 980, + "duction": 981, + "Ġsignificant": 982, + "ĠZ": 983, + "Ġpresent": 984, + "ann": 985, + "Ġid": 986, + "ency": 987, + "Ġver": 988, + "val": 989, + "yd": 990, + "rough": 991, + "SUP": 992, + "fore": 993, + "Ġsome": 994, + "ĠAs": 995, + "Ġsup": 996, + "Ġafter": 997, + "ological": 998, + "entif": 999, + "Ġcase": 1000, + "Ġsec": 1001, + "elf": 1002, + "Ġdep": 1003, + "ks": 1004, + "Ġcal": 1005, + "ved": 1006, + "Ġtem": 1007, + "Ġuse": 1008, + "ĠCom": 1009, + "lam": 1010, + "ines": 1011, + "ays": 1012, + "Ġgiv": 1013, + "Ġconsid": 1014, + "Ġelect": 1015, + "ational": 1016, + "ĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ": 1017, + "iqu": 1018, + "ties": 1019, + "Ġline": 1020, + "Ġsu": 1021, + "Abstract": 1022, + "ount": 1023, + "Ġdevelop": 1024, + "ĠCon": 1025, + "ology": 1026, + "alpha": 1027, + "ans": 1028, + "prime": 1029, + "cc": 1030, + "ogen": 1031, + "Ġwork": 1032, + "ven": 1033, + "ium": 1034, + "ective": 1035, + "Ġpa": 1036, + "ten": 1037, + "ĠAl": 1038, + "Ġï": 1039, + "Ġfe": 1040, + "âĢĻ": 1041, + "ential": 1042, + "line": 1043, + "Ġparamet": 1044, + "Ġprotein": 1045, + "Ġdisc": 1046, + "face": 1047, + "ces": 1048, + "Ġwell": 1049, + "ural": 1050, + "eng": 1051, + "Ġduring": 1052, + "row": 1053, + "ants": 1054, + "Ġrem": 1055, + "formation": 1056, + "Ġexam": 1057, + "Ġmic": 1058, + "âĪĴ": 1059, + "lem": 1060, + "ergy": 1061, + "Ġassoci": 1062, + "ĠÃ": 1063, + "rop": 1064, + "Ġfield": 1065, + "ty": 1066, + "Ġclass": 1067, + "Ġu": 1068, + "ie": 1069, + "Ġbec": 1070, + "Ġexperim": 1071, + "sp": 1072, + "Ġpr": 1073, + "ilar": 1074, + "tial": 1075, + "Ġconst": 1076, + "ĠIt": 1077, + "Ġcontrol": 1078, + "da": 1079, + "Ġmulti": 1080, + "itive": 1081, + "ics": 1082, + "urn": 1083, + "Ġindic": 1084, + "Ġfound": 1085, + "text": 1086, + "Ġnew": 1087, + "Ġref": 1088, + "gor": 1089, + "rap": 1090, + "Ġdesc": 1091, + "Ġsame": 1092, + "Ġfollowing": 1093, + "Ġdistrib": 1094, + "Figure": 1095, + "ild": 1096, + "Ġanti": 1097, + "etwork": 1098, + "ove": 1099, + "Ġthrough": 1100, + "Ġmost": 1101, + "cer": 1102, + "Ġdeterm": 1103, + "ha": 1104, + "elta": 1105, + "arge": 1106, + "Ġshown": 1107, + "ince": 1108, + "Ġany": 1109, + "ren": 1110, + "dot": 1111, + "ral": 1112, + "ration": 1113, + "amma": 1114, + "oid": 1115, + "Ġmed": 1116, + "ension": 1117, + "art": 1118, + "Ġpred": 1119, + "met": 1120, + "mathbb": 1121, + "ake": 1122, + "Ġcalc": 1123, + "Ġhig": 1124, + "Ġthree": 1125, + "Ġbased": 1126, + "mon": 1127, + "arch": 1128, + "----": 1129, + "ples": 1130, + "ages": 1131, + "ause": 1132, + "ish": 1133, + "tively": 1134, + "qui": 1135, + "resp": 1136, + "Ġcharacter": 1137, + "ock": 1138, + "Ġtreat": 1139, + "Ġproper": 1140, + "ex": 1141, + "Ġsmall": 1142, + "Ġterm": 1143, + "bda": 1144, + "Ġkn": 1145, + "ode": 1146, + "ings": 1147, + "Ġexpression": 1148, + "Ġmon": 1149, + "emb": 1150, + "ute": 1151, + "echn": 1152, + "hib": 1153, + "Ġdirec": 1154, + "ination": 1155, + "ithm": 1156, + "ulated": 1157, + "Ġcy": 1158, + "Ġpot": 1159, + "Ġorder": 1160, + "ote": 1161, + "ically": 1162, + "Ġvalues": 1163, + "ort": 1164, + "urther": 1165, + "cept": 1166, + "ynam": 1167, + "ough": 1168, + "echan": 1169, + "Ġâī": 1170, + "ok": 1171, + "ement": 1172, + "Ġμ": 1173, + "Ġestim": 1174, + "Ġeffect": 1175, + "Ġpath": 1176, + "Ġconf": 1177, + "Ġapp": 1178, + "Ġgiven": 1179, + "Ġend": 1180, + "set": 1181, + "Ġgl": 1182, + "Ġthey": 1183, + "ning": 1184, + "Ġtest": 1185, + "Ġtemper": 1186, + "ves": 1187, + "Ġvalue": 1188, + "ited": 1189, + "ality": 1190, + "Ġlim": 1191, + "Ġspect": 1192, + "ently": 1193, + "tit": 1194, + "Ġsequ": 1195, + "Ġidentif": 1196, + "//": 1197, + "igma": 1198, + "Ġenergy": 1199, + "inc": 1200, + "ness": 1201, + "ensity": 1202, + "Ġproblem": 1203, + "ydro": 1204, + "agn": 1205, + "ane": 1206, + "rent": 1207, + "com": 1208, + "ject": 1209, + "Ġimport": 1210, + "ĉĉ": 1211, + "Ġoper": 1212, + "olution": 1213, + "Ġaut": 1214, + "ectively": 1215, + "ĠHowever": 1216, + "ho": 1217, + "ental": 1218, + "Ġsing": 1219, + "ey": 1220, + "mu": 1221, + "ross": 1222, + "action": 1223, + "epend": 1224, + "ĠEx": 1225, + "vious": 1226, + "Ġstudies": 1227, + "sc": 1228, + "ormal": 1229, + "Ġhad": 1230, + "Ġmain": 1231, + "alth": 1232, + "gorithm": 1233, + "Ġfl": 1234, + "omet": 1235, + "ĠÂ": 1236, + "..": 1237, + "err": 1238, + "Ġposs": 1239, + "Ġdifferen": 1240, + "Ġobserved": 1241, + "ray": 1242, + "Ġpredic": 1243, + "Ġgene": 1244, + "Ġstate": 1245, + "We": 1246, + "Ġstructure": 1247, + "Ġret": 1248, + "respond": 1249, + "requ": 1250, + "ily": 1251, + "ĠâĪĪ": 1252, + "Ġser": 1253, + "Ġbound": 1254, + "Ġrepresent": 1255, + "phi": 1256, + "Ġtreatment": 1257, + "hat": 1258, + "Ġrequi": 1259, + "app": 1260, + "uman": 1261, + "Ġhigher": 1262, + "Ġlarge": 1263, + "Ġtra": 1264, + "ward": 1265, + "Ġobtained": 1266, + "Ġcould": 1267, + "tig": 1268, + "ĠUn": 1269, + "Ġdescrib": 1270, + "Ġsimilar": 1271, + "ported": 1272, + "ins": 1273, + "Ġaddition": 1274, + "osis": 1275, + "Ġnetwork": 1276, + "Ġele": 1277, + "pi": 1278, + "rix": 1279, + "Ġrate": 1280, + "gan": 1281, + "ugg": 1282, + "uss": 1283, + "Ġmechan": 1284, + "Ġdise": 1285, + "Ġeffects": 1286, + "Ġmodels": 1287, + "orph": 1288, + "ike": 1289, + "Ġsecond": 1290, + "mathbf": 1291, + "Ġdue": 1292, + "Ġq": 1293, + "Ġpres": 1294, + "Ġtechn": 1295, + "els": 1296, + "Ġcorrespond": 1297, + "Ġassociated": 1298, + "posed": 1299, + "Ġmass": 1300, + "round": 1301, + "view": 1302, + "Ġins": 1303, + "ĠâĢ¢": 1304, + "ditions": 1305, + "Ġwhile": 1306, + "ole": 1307, + "Ġlong": 1308, + "alu": 1309, + "Ġcap": 1310, + "Ġsurface": 1311, + "Ġcomplex": 1312, + "Ġcent": 1313, + "Ġcompared": 1314, + "Ġfind": 1315, + "arget": 1316, + "atory": 1317, + "fer": 1318, + "Ġsize": 1319, + "Ġcontain": 1320, + "usion": 1321, + "utions": 1322, + "Ġdem": 1323, + "ES": 1324, + "Ġdepend": 1325, + "atis": 1326, + "sum": 1327, + "ffici": 1328, + "Ġbas": 1329, + "lambda": 1330, + "ier": 1331, + "AT": 1332, + "Ġmax": 1333, + "Ġimp": 1334, + "Ġevalu": 1335, + "Ġtemperature": 1336, + "ink": 1337, + "ector": 1338, + "Ġscal": 1339, + "Ġgrow": 1340, + "ower": 1341, + "Ġrespectively": 1342, + "lear": 1343, + "sh": 1344, + "ick": 1345, + "Ġfil": 1346, + "irc": 1347, + "ilon": 1348, + "ram": 1349, + "Ġα": 1350, + "ification": 1351, + "Ġocc": 1352, + "Ġyear": 1353, + "Ġsugg": 1354, + "Ġradi": 1355, + "ified": 1356, + "havi": 1357, + "Ġwithin": 1358, + "Ġsens": 1359, + "Ġinte": 1360, + "Ġwould": 1361, + "Ġconcent": 1362, + "Ġmicro": 1363, + "Ġsingle": 1364, + "ĠSp": 1365, + "ou": 1366, + "Ġatt": 1367, + "Ġself": 1368, + "Ġabout": 1369, + "ength": 1370, + "Ġel": 1371, + "ĠRe": 1372, + "xim": 1373, + "Ġconditions": 1374, + "ude": 1375, + "ĠAt": 1376, + "where": 1377, + "med": 1378, + "Ġneed": 1379, + "iron": 1380, + "Ġpop": 1381, + "Ġresult": 1382, + "Ġpoint": 1383, + "Ġlo": 1384, + "Ġalgorithm": 1385, + "Ġactivity": 1386, + "leq": 1387, + "plement": 1388, + "ĠRes": 1389, + "Ġsym": 1390, + "onstr": 1391, + "atures": 1392, + "Ġimpro": 1393, + "for": 1394, + "Ġgeneral": 1395, + "iter": 1396, + "Ġexpl": 1397, + "###": 1398, + "Ġdom": 1399, + "Ġtri": 1400, + "min": 1401, + "Ġdistribution": 1402, + "Ġtr": 1403, + "ĠThere": 1404, + "oss": 1405, + "uce": 1406, + "mathrm": 1407, + "ull": 1408, + "ER": 1409, + "reg": 1410, + "Ġpe": 1411, + "Ġtotal": 1412, + "Ġlead": 1413, + "==": 1414, + "iod": 1415, + "Ġassum": 1416, + "Ġchang": 1417, + "Ġgra": 1418, + "MI": 1419, + "Ġcomput": 1420, + "Ġcomb": 1421, + "Ġinformation": 1422, + "Ġdesign": 1423, + "Ġiniti": 1424, + "Ġfrequ": 1425, + "imension": 1426, + "cop": 1427, + "Ġproperties": 1428, + "Ġconsider": 1429, + "Ġlevels": 1430, + "ene": 1431, + "Ġtype": 1432, + "ived": 1433, + "ĠHe": 1434, + "ependent": 1435, + "Ġapplic": 1436, + "Ġinves": 1437, + "Ġprevious": 1438, + "aw": 1439, + "Ġspace": 1440, + "Ġprovid": 1441, + "hyl": 1442, + "Ġinvestig": 1443, + "Ġapproach": 1444, + "aterial": 1445, + "onse": 1446, + "lecular": 1447, + "Ġparameters": 1448, + "Ġphase": 1449, + "ulations": 1450, + "ubl": 1451, + "beta": 1452, + "Ġav": 1453, + "Ġflu": 1454, + "Ġpotential": 1455, + "ĠThese": 1456, + "sigma": 1457, + "lo": 1458, + "times": 1459, + "Ġoptim": 1460, + "ision": 1461, + "Ġaff": 1462, + "Ġmean": 1463, + "Ġbehavi": 1464, + "Ġvol": 1465, + "orem": 1466, + "agne": 1467, + "Ġdecre": 1468, + "tional": 1469, + "Ġsolution": 1470, + "Ġhuman": 1471, + "ger": 1472, + "Ġpaper": 1473, + "Ġcompar": 1474, + "Ġlower": 1475, + "andard": 1476, + "Ġcorrel": 1477, + "cri": 1478, + "Ġcurrent": 1479, + "Ġder": 1480, + "ission": 1481, + "ĠFigure": 1482, + "Ġproduc": 1483, + "Ġwater": 1484, + "ĠTo": 1485, + "Ġthose": 1486, + "Ġacid": 1487, + "Ġcancer": 1488, + "Ġlocal": 1489, + "ton": 1490, + "Ġflow": 1491, + "Ġregion": 1492, + "Ġhealth": 1493, + "Ġimportant": 1494, + "ograph": 1495, + "abl": 1496, + "Ġselec": 1497, + "Ġgre": 1498, + "Ġindi": 1499, + "ade": 1500, + "rid": 1501, + "Ġshould": 1502, + "based": 1503, + "Ġabove": 1504, + "ld": 1505, + "Ġsystems": 1506, + "ication": 1507, + "Ġed": 1508, + "Ġtyp": 1509, + "Ġphys": 1510, + "oper": 1511, + "Ġcompon": 1512, + "ON": 1513, + "Ġsuper": 1514, + "ga": 1515, + "hemical": 1516, + "isk": 1517, + "oph": 1518, + "Ġhy": 1519, + "Ġanaly": 1520, + "inu": 1521, + "Ġtarget": 1522, + "ĠAd": 1523, + "Ġpat": 1524, + "gamma": 1525, + "Ġsamples": 1526, + "Ġsl": 1527, + "Ġpart": 1528, + "olds": 1529, + "Ġbel": 1530, + "imum": 1531, + "ĠIm": 1532, + "Ġdisease": 1533, + "II": 1534, + "ists": 1535, + "iver": 1536, + "Ġperformance": 1537, + "ĠĠĠĠĠĠĠĠĠĠĠ": 1538, + "gle": 1539, + "Ġox": 1540, + "ndom": 1541, + "ĠĠĠĠĠ": 1542, + "Ġbecause": 1543, + "ayer": 1544, + "Ġrange": 1545, + "Ġcoun": 1546, + "Ġincreased": 1547, + "och": 1548, + "onal": 1549, + "Ġvery": 1550, + "Ġdynam": 1551, + "anti": 1552, + "Ġadd": 1553, + "Ġinhib": 1554, + "Ġmethods": 1555, + "idence": 1556, + "inical": 1557, + "erence": 1558, + "ival": 1559, + "ule": 1560, + "Ġfactor": 1561, + "Ġfin": 1562, + "ints": 1563, + "viron": 1564, + "Ġsour": 1565, + "verage": 1566, + "equ": 1567, + "Ġear": 1568, + "Ġshowed": 1569, + "ites": 1570, + "Ġperformed": 1571, + "Ġrese": 1572, + "ĠEn": 1573, + "Ġspecies": 1574, + "AC": 1575, + "ĠCl": 1576, + "hip": 1577, + "tilde": 1578, + "io": 1579, + "ately": 1580, + "Th": 1581, + "ody": 1582, + "Ġincrease": 1583, + "ĠPh": 1584, + "âĢĿ": 1585, + "Ġshows": 1586, + "ĠAc": 1587, + "Ġpost": 1588, + "ording": 1589, + "ences": 1590, + "oy": 1591, + "ner": 1592, + "Ġresponse": 1593, + "Ġoccur": 1594, + "rho": 1595, + "Ġperiod": 1596, + "ars": 1597, + "Ġred": 1598, + "ĠOn": 1599, + "Ġdensity": 1600, + "Ġexample": 1601, + "get": 1602, + "Ġreal": 1603, + "ĠCount": 1604, + "acy": 1605, + "Ġpower": 1606, + "Ġabs": 1607, + "ital": 1608, + "Ġprim": 1609, + "âĢIJ": 1610, + "Ġdefined": 1611, + "Ġnormal": 1612, + "aj": 1613, + "Ġinst": 1614, + "Ġallow": 1615, + "Ġpossible": 1616, + "Ġvis": 1617, + "Ġreported": 1618, + "Ġsignal": 1619, + "theta": 1620, + "Ġden": 1621, + "ables": 1622, + "Ġdeg": 1623, + "Ġindivid": 1624, + "agnetic": 1625, + "Ġgroups": 1626, + "ae": 1627, + "arrow": 1628, + "Ġstat": 1629, + "Ġmechanism": 1630, + "osp": 1631, + "mer": 1632, + "other": 1633, + "Ġprot": 1634, + "Ġcases": 1635, + "Ġcr": 1636, + "Ġte": 1637, + "Ġintegr": 1638, + "ets": 1639, + "Ġdevelopment": 1640, + "Ġrandom": 1641, + "Ġinvol": 1642, + "Ġincluding": 1643, + "Ġerr": 1644, + "gram": 1645, + "Ġparticular": 1646, + "eps": 1647, + "Ġstandard": 1648, + "position": 1649, + "Ġcontrib": 1650, + "sequ": 1651, + "Ġmany": 1652, + "Ġfurther": 1653, + "Ġsignificantly": 1654, + "ators": 1655, + "urb": 1656, + "Ġagain": 1657, + "bar": 1658, + "Ġwithout": 1659, + "Ġsever": 1660, + "Ġtop": 1661, + "ret": 1662, + "led": 1663, + "Ġmatrix": 1664, + "Ġspecific": 1665, + "ateg": 1666, + "ĨĴ": 1667, + "Ġdirect": 1668, + "Ġsample": 1669, + "Ġthem": 1670, + "SA": 1671, + "oint": 1672, + "Ġrole": 1673, + "Ġchanges": 1674, + "raction": 1675, + "Ġsum": 1676, + "Ġindividual": 1677, + "IN": 1678, + "Ġimmun": 1679, + "ced": 1680, + "oh": 1681, + "Ġstrong": 1682, + "Ġep": 1683, + "Ġlinear": 1684, + "ually": 1685, + "delta": 1686, + "way": 1687, + "asing": 1688, + "Ġtim": 1689, + "Ġvi": 1690, + "ison": 1691, + "Ġfunctions": 1692, + "Ġamong": 1693, + "Ġsee": 1694, + "erest": 1695, + "Ġgrowth": 1696, + "Ġrati": 1697, + "ĠSc": 1698, + "ixed": 1699, + "RNA": 1700, + "eed": 1701, + "tau": 1702, + "Ġent": 1703, + "Ġdr": 1704, + "ores": 1705, + "Ġapproxim": 1706, + "ful": 1707, + "Ġrele": 1708, + "Ġfactors": 1709, + "Ġdiscuss": 1710, + "Ġphot": 1711, + "Ġproposed": 1712, + "ero": 1713, + "omega": 1714, + "Ġfour": 1715, + "astic": 1716, + "Ġyears": 1717, + "hesis": 1718, + "ique": 1719, + "Ġmaterial": 1720, + "Ġbre": 1721, + "Ġprof": 1722, + "ĠAp": 1723, + "Ġneg": 1724, + "Ġbu": 1725, + "Ġassess": 1726, + "ĠâĢľ": 1727, + "Ġvir": 1728, + "atter": 1729, + "Ġdescribed": 1730, + "istics": 1731, + "Ġcompos": 1732, + "az": 1733, + "struc": 1734, + "Ġtum": 1735, + "partial": 1736, + "af": 1737, + "Ġwho": 1738, + "atal": 1739, + "Ġdemonstr": 1740, + "ances": 1741, + "yt": 1742, + "Ġremain": 1743, + "Ġless": 1744, + "Ġpositive": 1745, + "omic": 1746, + "Ġsince": 1747, + "ogn": 1748, + "Ġcondition": 1749, + "::": 1750, + "Ġdoes": 1751, + "tice": 1752, + "osph": 1753, + "Ġprov": 1754, + "ĠCO": 1755, + "Ġrat": 1756, + "Ġterms": 1757, + "box": 1758, + "Ġtak": 1759, + "Ġpattern": 1760, + "ale": 1761, + "Ġnan": 1762, + "ules": 1763, + "Ġmut": 1764, + "ished": 1765, + "Ġrelated": 1766, + "Ġtheory": 1767, + "bol": 1768, + "cdot": 1769, + "vironment": 1770, + "air": 1771, + "ivers": 1772, + "ĠAr": 1773, + "Ġï£": 1774, + "ressed": 1775, + "Ġâī¤": 1776, + "ĠMet": 1777, + "ID": 1778, + "ults": 1779, + "Ġβ": 1780, + "Ġdat": 1781, + "pose": 1782, + "Ġorig": 1783, + "Ġreturn": 1784, + "Ġchange": 1785, + "Ġlarg": 1786, + "au": 1787, + "aces": 1788, + "Ġarea": 1789, + "Ġgenes": 1790, + "AS": 1791, + "Ġhydro": 1792, + "Ġconsist": 1793, + "man": 1794, + "Ġresearch": 1795, + "ĠDe": 1796, + "Ġorgan": 1797, + "ask": 1798, + "Ġback": 1799, + "Ġfollows": 1800, + "ung": 1801, + "roll": 1802, + "Ġequation": 1803, + "plied": 1804, + "tr": 1805, + "Ġcorresponding": 1806, + "odes": 1807, + "ested": 1808, + "Ġrelations": 1809, + "nal": 1810, + "Ġfr": 1811, + "Ġlimit": 1812, + "mit": 1813, + "Ġoff": 1814, + "uted": 1815, + "Ġrisk": 1816, + "read": 1817, + "Ġknown": 1818, + "plit": 1819, + "tivity": 1820, + "Ġsequence": 1821, + "Ġconsidered": 1822, + "xi": 1823, + "ĠMod": 1824, + "vity": 1825, + "Ġnuc": 1826, + "cle": 1827, + "ices": 1828, + "Ġlength": 1829, + "Ġseveral": 1830, + "sing": 1831, + "oot": 1832, + "not": 1833, + "Ġstress": 1834, + "ĠIf": 1835, + "CT": 1836, + "roph": 1837, + "Ġcommun": 1838, + "Ġclust": 1839, + "ĠLe": 1840, + "me": 1841, + "antum": 1842, + "Ġmemb": 1843, + "Ġlab": 1844, + "Ġeven": 1845, + "Ġinflu": 1846, + "ck": 1847, + "ĠÃĹ": 1848, + "Ġlog": 1849, + "ving": 1850, + "ests": 1851, + "Ġhis": 1852, + "ank": 1853, + "ĠInd": 1854, + "actions": 1855, + "fty": 1856, + "mod": 1857, + "Ġreview": 1858, + "though": 1859, + "Ġeffici": 1860, + "Ġmap": 1861, + "infty": 1862, + "Ġbeing": 1863, + "land": 1864, + "Ġclinical": 1865, + "Ġmeasured": 1866, + "ering": 1867, + "ĠTable": 1868, + "Ġshe": 1869, + "see": 1870, + "Ġsection": 1871, + "Ġavail": 1872, + "omen": 1873, + "Ġvers": 1874, + "Ġdel": 1875, + "ither": 1876, + "eration": 1877, + "Ġhand": 1878, + "Ġcontinu": 1879, + "Ġconn": 1880, + "hors": 1881, + "rad": 1882, + "Ġfam": 1883, + "Ġlear": 1884, + "Ġinitial": 1885, + "ystem": 1886, + "Ġge": 1887, + "Ġâ̲": 1888, + "Ġcirc": 1889, + "Ġpubl": 1890, + "ĠIs": 1891, + "Ġvia": 1892, + "Ġcommon": 1893, + "ife": 1894, + "Ġmark": 1895, + "Ġever": 1896, + "arc": 1897, + "big": 1898, + "ertain": 1899, + "\\\\": 1900, + "var": 1901, + "As": 1902, + "roscop": 1903, + "Ġage": 1904, + "Ġhow": 1905, + "ĠLet": 1906, + "struct": 1907, + "Ġaverage": 1908, + "vant": 1909, + "ĠSh": 1910, + "imensional": 1911, + "SC": 1912, + "ape": 1913, + "nu": 1914, + "Ġloss": 1915, + "ason": 1916, + "ides": 1917, + "Ġpopulation": 1918, + "Ġdomain": 1919, + "inding": 1920, + "we": 1921, + "AL": 1922, + "Ġaccur": 1923, + "ety": 1924, + "Ġcaus": 1925, + "Delta": 1926, + "rapy": 1927, + "Ġprom": 1928, + "time": 1929, + "Ġintro": 1930, + "Ġmultiple": 1931, + "Ġconstant": 1932, + "pling": 1933, + "ino": 1934, + "ajor": 1935, + "ior": 1936, + "abol": 1937, + "def": 1938, + "Ġpoints": 1939, + "verse": 1940, + "name": 1941, + "ĠSe": 1942, + "itor": 1943, + "Pro": 1944, + "arm": 1945, + "Ġtiss": 1946, + "Ġfib": 1947, + "Ġgraph": 1948, + "Ġcall": 1949, + "atisf": 1950, + "Ġconduc": 1951, + "dex": 1952, + "ĠNe": 1953, + "Ġpers": 1954, + "ern": 1955, + "CR": 1956, + "angle": 1957, + "Ġfrequency": 1958, + "AP": 1959, + "Ġpresented": 1960, + "amp": 1961, + "Ġbefore": 1962, + "ords": 1963, + "Ġinput": 1964, + "ĠâĨĴ": 1965, + "Ġparticip": 1966, + "OR": 1967, + "Ġchild": 1968, + "Ġcre": 1969, + "fficient": 1970, + "Ġsepar": 1971, + "uration": 1972, + "α": 1973, + "Ġexist": 1974, + "ised": 1975, + "Ġlight": 1976, + "imal": 1977, + "****": 1978, + "ĠDNA": 1979, + "hel": 1980, + "Ġinterest": 1981, + "bf": 1982, + "ke": 1983, + "Ġcollec": 1984, + "Ġtrain": 1985, + "ai": 1986, + "ĠPl": 1987, + "Ġλ": 1988, + "ĠCo": 1989, + "Ġimage": 1990, + "Ġhyp": 1991, + "oma": 1992, + "Ġweight": 1993, + "Ġcross": 1994, + "rt": 1995, + "Ġdifference": 1996, + "Ġfeatures": 1997, + "medi": 1998, + "type": 1999, + "Ġpress": 2000, + "IC": 2001, + "Ġtherm": 2002, + "Ġstates": 2003, + "ustr": 2004, + "till": 2005, + "Ġhist": 2006, + "Ġratio": 2007, + "aging": 2008, + "ĠAll": 2009, + "Ġhel": 2010, + "bon": 2011, + "Ġbehavior": 2012, + "Ġpri": 2013, + "Ġsynt": 2014, + "ended": 2015, + "ĠInt": 2016, + "tt": 2017, + "Ġvarious": 2018, + "rect": 2019, + "Ġprec": 2020, + "Ġtimes": 2021, + "MS": 2022, + "Ġanalyz": 2023, + "Ġcare": 2024, + "mat": 2025, + "Ġalong": 2026, + "Ġpur": 2027, + "atively": 2028, + "Ġstar": 2029, + "jects": 2030, + "ii": 2031, + "istance": 2032, + "ĠThen": 2033, + "AN": 2034, + "Ġparameter": 2035, + "ulate": 2036, + "Ġevery": 2037, + "Ġsatisf": 2038, + "Ġdetermined": 2039, + "ina": 2040, + "rane": 2041, + "Ġpair": 2042, + "ool": 2043, + "Table": 2044, + "Ġthus": 2045, + "ogene": 2046, + "ĠÏĨ": 2047, + "Ġprogram": 2048, + "asc": 2049, + "Ġenvironment": 2050, + "MP": 2051, + "Ġread": 2052, + "Ġach": 2053, + "Ġpresence": 2054, + "Ġmice": 2055, + "For": 2056, + "Ġproduction": 2057, + "Ġdifferences": 2058, + "Ġprovide": 2059, + "ste": 2060, + "ames": 2061, + "ĉĠ": 2062, + "Ġ±": 2063, + "roup": 2064, + "Ġelectron": 2065, + "Ġhyper": 2066, + "bit": 2067, + "ĠRec": 2068, + "Ġvector": 2069, + "uble": 2070, + "rangle": 2071, + "Ġwr": 2072, + "wide": 2073, + "ĠâĬ": 2074, + "rack": 2075, + "ryst": 2076, + "Ġinj": 2077, + "ega": 2078, + "Ġwhe": 2079, + "psilon": 2080, + "Ġagainst": 2081, + "Ġdiagn": 2082, + "Ġhom": 2083, + "Ġachie": 2084, + "ns": 2085, + "Ġrece": 2086, + "--------": 2087, + "Ġavailable": 2088, + "inf": 2089, + "Ġsuc": 2090, + "Ġgu": 2091, + "Ġmajor": 2092, + "ĠThus": 2093, + "ware": 2094, + "Ġsupport": 2095, + "lor": 2096, + "Ġexperimental": 2097, + "ĠMo": 2098, + "Ġconcentration": 2099, + "tics": 2100, + "Ġnec": 2101, + "Ġphen": 2102, + "sq": 2103, + "Ġclos": 2104, + "sub": 2105, + "Ġknow": 2106, + "Ġformation": 2107, + "Ġdid": 2108, + "ouse": 2109, + "inary": 2110, + "ict": 2111, + "ĠCD": 2112, + "This": 2113, + "less": 2114, + "Ġnear": 2115, + "Ġimprove": 2116, + "abil": 2117, + "Ġreve": 2118, + "Ġexperiments": 2119, + "ience": 2120, + "ula": 2121, + "ored": 2122, + "Ġunc": 2123, + "__": 2124, + "Ġapplied": 2125, + "Ġreduced": 2126, + "Ġdetail": 2127, + "stand": 2128, + "Ġcho": 2129, + "omy": 2130, + "Ġcalculated": 2131, + "Ġenh": 2132, + "LES": 2133, + "itro": 2134, + "Ġrespons": 2135, + "Ġest": 2136, + "Ġmi": 2137, + "Ġcoe": 2138, + "ĠTherefore": 2139, + "ĠMore": 2140, + "bl": 2141, + "anced": 2142, + "ume": 2143, + "Ġband": 2144, + "Ġact": 2145, + "Ġeither": 2146, + "omes": 2147, + "ĠGen": 2148, + "vare": 2149, + "ET": 2150, + "reen": 2151, + "ĠPar": 2152, + "ĠSim": 2153, + "Ġidentified": 2154, + "Ġinteraction": 2155, + "Ġmade": 2156, + "Ġsource": 2157, + "tis": 2158, + "ots": 2159, + "mega": 2160, + "Ġserv": 2161, + "ms": 2162, + "alysis": 2163, + "vent": 2164, + "ense": 2165, + "gl": 2166, + "Ġlines": 2167, + "Ġappear": 2168, + "tif": 2169, + "Ġfree": 2170, + "oms": 2171, + "ining": 2172, + "eren": 2173, + "Ġchann": 2174, + "varepsilon": 2175, + "sim": 2176, + "Ġcou": 2177, + "°": 2178, + "Ġerror": 2179, + "Ġquanti": 2180, + "ĠEq": 2181, + "by": 2182, + "ĠII": 2183, + "tex": 2184, + "ĠSch": 2185, + "sqrt": 2186, + "ocus": 2187, + "Ġdev": 2188, + "quad": 2189, + "ters": 2190, + "Ġrelationship": 2191, + "oll": 2192, + "Ġgo": 2193, + "Ġwave": 2194, + "Ġleft": 2195, + "ways": 2196, + "hi": 2197, + "Ġright": 2198, + "obal": 2199, + "Ġdown": 2200, + "uk": 2201, + "Ġcoll": 2202, + "Ġmagnetic": 2203, + "Ġprog": 2204, + "dots": 2205, + "Ġstrateg": 2206, + "bs": 2207, + "unction": 2208, + "Ġenc": 2209, + "Ġclear": 2210, + "Ġcost": 2211, + "geb": 2212, + "etter": 2213, + "MILES": 2214, + "lamm": 2215, + "Ġmust": 2216, + "Ġeffective": 2217, + "Ġexc": 2218, + "Ġplas": 2219, + "Ġsuggest": 2220, + "itions": 2221, + "Ġleast": 2222, + "ying": 2223, + "lying": 2224, + "Ġlik": 2225, + "Omega": 2226, + "aking": 2227, + "Ġmaximum": 2228, + "Ġrelative": 2229, + "é": 2230, + "Ġaccording": 2231, + "ient": 2232, + "Ġway": 2233, + "Ġsem": 2234, + "atural": 2235, + "like": 2236, + "resh": 2237, + "ĠMe": 2238, + "Ps": 2239, + "ĠTrans": 2240, + "isc": 2241, + "Ġprac": 2242, + "Ġrun": 2243, + "Ġconver": 2244, + "Ġsk": 2245, + "Ġyield": 2246, + "geq": 2247, + "ably": 2248, + "Ġantib": 2249, + "izing": 2250, + "β": 2251, + "mission": 2252, + "Ġnow": 2253, + "Ġdetection": 2254, + "eloc": 2255, + "Ġget": 2256, + "ert": 2257, + "Ġvariables": 2258, + "Ġopen": 2259, + "Ġpressure": 2260, + "Ġstrain": 2261, + "ument": 2262, + "ĠFurther": 2263, + "Ġquantum": 2264, + "Ġimplement": 2265, + "Ġearly": 2266, + "Ġframe": 2267, + "Ġshort": 2268, + "Ġdrug": 2269, + "Ġrequired": 2270, + "PS": 2271, + "Ġmy": 2272, + "Ġmuch": 2273, + "Ġmem": 2274, + "CC": 2275, + "Ġquality": 2276, + "Ġproteins": 2277, + "Ġlayer": 2278, + "Ġques": 2279, + "Ġrecept": 2280, + "Ġhere": 2281, + "Ġproced": 2282, + "ured": 2283, + "Ġdeveloped": 2284, + "Ġposition": 2285, + "rum": 2286, + "Ġlat": 2287, + "Ġincreasing": 2288, + "EM": 2289, + "Ġmeasurements": 2290, + "Ġben": 2291, + "Ġisol": 2292, + "wh": 2293, + "To": 2294, + "Ġvalid": 2295, + "Ġfunctional": 2296, + "emma": 2297, + "...": 2298, + "orld": 2299, + "ries": 2300, + "Ġprobability": 2301, + "ĠNew": 2302, + "Ġmm": 2303, + "OS": 2304, + "AD": 2305, + "Ġδ": 2306, + "Ġscale": 2307, + "ĠFe": 2308, + "ĠTheorem": 2309, + "ĠQu": 2310, + "Ġcomponents": 2311, + "Ġblood": 2312, + "ĠÏĥ": 2313, + "acc": 2314, + "Ġbetter": 2315, + "Ġstep": 2316, + "Ġγ": 2317, + "Ġfac": 2318, + "aneous": 2319, + "Ġload": 2320, + "Ġmetabol": 2321, + "Ġevolution": 2322, + "son": 2323, + "ream": 2324, + "Ġeas": 2325, + "ird": 2326, + "dimensional": 2327, + "bor": 2328, + "Ġmus": 2329, + "Ġequations": 2330, + "psi": 2331, + "order": 2332, + "olar": 2333, + "Ġnumer": 2334, + "Ġkey": 2335, + "orth": 2336, + "Ġsimple": 2337, + "ift": 2338, + "cale": 2339, + "Ġindex": 2340, + "ĠâĢĵ": 2341, + "Ġconcentr": 2342, + "ges": 2343, + "Ġnegative": 2344, + "Ġveloc": 2345, + "Ġax": 2346, + "ĠEff": 2347, + "Ġfinite": 2348, + "Ġill": 2349, + "ching": 2350, + "Ġpatient": 2351, + "epsilon": 2352, + "Ġmen": 2353, + "Ġcri": 2354, + "IS": 2355, + "Cl": 2356, + "Ġconcl": 2357, + "Ġθ": 2358, + "ibility": 2359, + "Ġsymmet": 2360, + "enter": 2361, + "Ġdistance": 2362, + "Ġpolym": 2363, + "ights": 2364, + "Ġcult": 2365, + "Ġpeak": 2366, + "Ġacross": 2367, + "inition": 2368, + "Ġlet": 2369, + "Ġconstruc": 2370, + "Ġincluded": 2371, + "Ġhowever": 2372, + "Ġregions": 2373, + "Ġlearning": 2374, + "Ġevidence": 2375, + "inally": 2376, + "Ġneut": 2377, + "itation": 2378, + "Ġwhether": 2379, + "Ġoutput": 2380, + "ĠSection": 2381, + "Ġgood": 2382, + "IT": 2383, + "uation": 2384, + "Ġtypes": 2385, + "bm": 2386, + "cos": 2387, + "with": 2388, + "lim": 2389, + "otic": 2390, + "Ġstill": 2391, + "Ġdays": 2392, + "Ġstudied": 2393, + "Ġimages": 2394, + "ble": 2395, + "Ġarg": 2396, + "linear": 2397, + "Ġprocesses": 2398, + "Ġwid": 2399, + "Ġtraining": 2400, + "Ġindependent": 2401, + "plac": 2402, + "Ġresid": 2403, + "Ġsuccess": 2404, + "Ġnucle": 2405, + "GF": 2406, + "let": 2407, + "ploy": 2408, + "Ġtumor": 2409, + "Gamma": 2410, + "Ġtherefore": 2411, + "rast": 2412, + "Ġfocus": 2413, + "ash": 2414, + "Ġbelow": 2415, + "ially": 2416, + "Ġcomparison": 2417, + "Ġadj": 2418, + "Ġlike": 2419, + "Ġmolecular": 2420, + "ried": 2421, + "Ġfit": 2422, + "ĠDi": 2423, + "log": 2424, + "Ġplay": 2425, + "work": 2426, + "ections": 2427, + "Ġelectro": 2428, + "uit": 2429, + "more": 2430, + "Ġmight": 2431, + "Ġanalys": 2432, + "Ġmeans": 2433, + "Ġcorrelation": 2434, + "kn": 2435, + "Ġcontroll": 2436, + "IV": 2437, + "Ch": 2438, + "pec": 2439, + "rag": 2440, + "Ġmagn": 2441, + "Ġphysical": 2442, + "ION": 2443, + "Ġreveal": 2444, + "Ġphosph": 2445, + "Ġrates": 2446, + "Ġlarger": 2447, + "Ġstim": 2448, + "Ġsoft": 2449, + "Ġcompound": 2450, + "be": 2451, + "chi": 2452, + "ĠNo": 2453, + "Ġimpact": 2454, + "tor": 2455, + "Ġprimary": 2456, + "ocial": 2457, + "Ġapplication": 2458, + "Ġsolutions": 2459, + "duce": 2460, + "Ġcharacteristics": 2461, + "Ġelements": 2462, + "Ġview": 2463, + "Ġlater": 2464, + "uture": 2465, + "Ġfamily": 2466, + "rial": 2467, + "Ġtranscri": 2468, + "orption": 2469, + "Ġsw": 2470, + "CD": 2471, + "ED": 2472, + "Ġemb": 2473, + "Ġzero": 2474, + "ols": 2475, + "Ġlife": 2476, + "cep": 2477, + "ĠLi": 2478, + "ths": 2479, + "Ġseries": 2480, + "Ġaround": 2481, + "Ġtransition": 2482, + "ĠCor": 2483, + "ĠâĪĤ": 2484, + "Ġdatas": 2485, + "Ġher": 2486, + "ĠBy": 2487, + "AM": 2488, + "spec": 2489, + "oles": 2490, + "ography": 2491, + "tle": 2492, + "ĠCar": 2493, + "alle": 2494, + "Ġestabl": 2495, + "agement": 2496, + "Ġschem": 2497, + "ground": 2498, + "Ġfail": 2499, + "Ġexpected": 2500, + "Ġrequire": 2501, + "array": 2502, + "Ġexperiment": 2503, + "Ġelement": 2504, + "Ġneu": 2505, + "Ġgenerated": 2506, + "Ġsite": 2507, + "ĠCont": 2508, + "ĠRNA": 2509, + "eral": 2510, + "Ġcontent": 2511, + "Ġbacter": 2512, + "ler": 2513, + "Ġtransfer": 2514, + "ulf": 2515, + "rightarrow": 2516, + "any": 2517, + "ĠSince": 2518, + "induced": 2519, + "Ġreaction": 2520, + "heck": 2521, + "Ġstructures": 2522, + "Ġcount": 2523, + "Ġdetermine": 2524, + "zym": 2525, + "ĠBl": 2526, + "Ġunderstand": 2527, + "ocal": 2528, + "Ġsyn": 2529, + "Ġpoly": 2530, + "ury": 2531, + "Ġbest": 2532, + "Ġfixed": 2533, + "reng": 2534, + "Ġchemical": 2535, + "Ġtissue": 2536, + "Ġpul": 2537, + "Ġboundary": 2538, + "ising": 2539, + "Ġbro": 2540, + "atistical": 2541, + "icity": 2542, + "sk": 2543, + "ring": 2544, + "Ġlast": 2545, + "Ġchildren": 2546, + "rim": 2547, + "Ġreduction": 2548, + "Ġspin": 2549, + "Ġbody": 2550, + "operator": 2551, + "vari": 2552, + "Ġdiv": 2553, + "ymbol": 2554, + "Ġmal": 2555, + "Ġspati": 2556, + "ah": 2557, + "ĠBi": 2558, + "back": 2559, + "sy": 2560, + "Ġseen": 2561, + "ĠWith": 2562, + "ids": 2563, + "plications": 2564, + "Ġnecess": 2565, + "Ġside": 2566, + "Ġbrain": 2567, + "Ġfew": 2568, + "Ġapplications": 2569, + "utes": 2570, + "aches": 2571, + "Ġactive": 2572, + "varphi": 2573, + "term": 2574, + "Ġmom": 2575, + "iversity": 2576, + "Ġfinal": 2577, + "ledge": 2578, + "Ġdynamics": 2579, + "aving": 2580, + "erc": 2581, + "orphism": 2582, + "ones": 2583, + "off": 2584, + "pm": 2585, + "Ġaction": 2586, + "Ġnatural": 2587, + "ĠGe": 2588, + "Ġyou": 2589, + "lex": 2590, + "ĠĠĠĠĠĠ": 2591, + "stit": 2592, + "Ġgas": 2593, + "Ġmake": 2594, + "Ġinduced": 2595, + "ĠAfter": 2596, + "ĠWh": 2597, + "Ġcomponent": 2598, + "Ġinfection": 2599, + "ĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ": 2600, + "Ġconfir": 2601, + "igen": 2602, + "ĠSystem": 2603, + "ticle": 2604, + "Ġprovided": 2605, + "ternal": 2606, + "bers": 2607, + "OD": 2608, + "ĠInter": 2609, + "ott": 2610, + "aves": 2611, + "ĠStud": 2612, + "py": 2613, + "Ġresistance": 2614, + "ĠSur": 2615, + "atch": 2616, + "Ġdim": 2617, + "Ġinterp": 2618, + "Ġcycl": 2619, + "ont": 2620, + "iting": 2621, + "AG": 2622, + "Ġequival": 2623, + "otype": 2624, + "Ġpreviously": 2625, + "Ġadditional": 2626, + "outh": 2627, + "Ġimpl": 2628, + "Ġion": 2629, + "Ġir": 2630, + "Ġcop": 2631, + "Ġhal": 2632, + "Ġactivation": 2633, + "langle": 2634, + "Ġfull": 2635, + "SS": 2636, + "ĠOp": 2637, + "idd": 2638, + "Ġproof": 2639, + "Ġproblems": 2640, + "Ġtransform": 2641, + "Ġinteractions": 2642, + "Ġsupp": 2643, + "des": 2644, + "ĠReg": 2645, + "operatorname": 2646, + "egin": 2647, + "Ġcryst": 2648, + "Ġincreases": 2649, + "ronic": 2650, + "Ġadap": 2651, + "inant": 2652, + "Ġvelocity": 2653, + "ĠAss": 2654, + "iques": 2655, + "Ġcontinuous": 2656, + "ĠComp": 2657, + "ĠProper": 2658, + "Ġprior": 2659, + "orb": 2660, + "Ġnovel": 2661, + "Ġblock": 2662, + "Ġvolume": 2663, + "Ġregard": 2664, + "ometry": 2665, + "EC": 2666, + "Ġresulting": 2667, + "ĠOr": 2668, + "Ġcarbon": 2669, + "arent": 2670, + "Ġbinding": 2671, + "ij": 2672, + "Ġaccess": 2673, + "Ġweak": 2674, + "Ġunit": 2675, + "Ġide": 2676, + "\"\"": 2677, + "Ġcm": 2678, + "Ġcritical": 2679, + "Ġrespect": 2680, + "trans": 2681, + "Ġâī¥": 2682, + "Ġsal": 2683, + "ead": 2684, + "Ġsimulation": 2685, + "Ġcapac": 2686, + "itivity": 2687, + "Ġrecord": 2688, + "rak": 2689, + "Ġneur": 2690, + "onic": 2691, + "ople": 2692, + "Ġmg": 2693, + "Ġstreng": 2694, + "erve": 2695, + "Ġreduc": 2696, + "Ġpass": 2697, + "ordin": 2698, + "exp": 2699, + "jective": 2700, + "ensor": 2701, + "Ġparticles": 2702, + "Ġair": 2703, + "Ġlink": 2704, + "ĠÏĦ": 2705, + "Ġlist": 2706, + "cin": 2707, + "ĠOur": 2708, + "pri": 2709, + "vere": 2710, + "ibr": 2711, + "iform": 2712, + "Ġexplain": 2713, + "Ġfem": 2714, + "Ġutil": 2715, + "St": 2716, + "overline": 2717, + "Ġoften": 2718, + "ery": 2719, + "ope": 2720, + "ĠUsing": 2721, + "begin": 2722, + "Ġdifferenti": 2723, + "pers": 2724, + "self": 2725, + "izes": 2726, + "Ġconcentrations": 2727, + "IR": 2728, + "ĠSup": 2729, + "Ġbasis": 2730, + "Ġinclude": 2731, + "ĠBond": 2732, + "Ġextrac": 2733, + "ĠMethod": 2734, + "ĠData": 2735, + "ĠDef": 2736, + "wn": 2737, + "Ġnetworks": 2738, + "igned": 2739, + "âĢ¢": 2740, + "Ġexpressed": 2741, + "Ġcontrast": 2742, + "esis": 2743, + "col": 2744, + "inter": 2745, + "pid": 2746, + "Ġdri": 2747, + "Ġdefine": 2748, + "Ġinfluence": 2749, + "Ġselected": 2750, + "EL": 2751, + "Ġcontaining": 2752, + "Ġsil": 2753, + "gebra": 2754, + "reat": 2755, + "bolds": 2756, + "Ġinvestigated": 2757, + "ĠCol": 2758, + "ymmet": 2759, + "ytes": 2760, + "Ġmolec": 2761, + "Ġinvolved": 2762, + "Ġday": 2763, + "Ġchain": 2764, + "ĠMoreover": 2765, + "Ġdiag": 2766, + "Ġang": 2767, + "Ġlikely": 2768, + "Ġspectrum": 2769, + "Ġderiv": 2770, + "boldsymbol": 2771, + "Ġhelp": 2772, + "ĠAm": 2773, + "Ġtreated": 2774, + "Ġvariable": 2775, + "ellular": 2776, + "ĠDes": 2777, + "aps": 2778, + "Ġnm": 2779, + "ĠÏģ": 2780, + "ĠWhen": 2781, + "Ġhighly": 2782, + "amin": 2783, + "Ġwhat": 2784, + "related": 2785, + "Ġchrom": 2786, + "Ġsurv": 2787, + "ĠAnalysis": 2788, + "Ġsit": 2789, + "fact": 2790, + "oding": 2791, + "Ġproduct": 2792, + "Ġevents": 2793, + "ras": 2794, + "ĠPer": 2795, + "max": 2796, + "ĠAg": 2797, + "cont": 2798, + "icro": 2799, + "Ġadv": 2800, + "Ġcalled": 2801, + "Ġdegree": 2802, + "AB": 2803, + "TR": 2804, + "Ġseg": 2805, + "ĠCan": 2806, + "Ġdemonstrated": 2807, + "wise": 2808, + "Ġve": 2809, + "ĠCa": 2810, + "Ġdetected": 2811, + "co": 2812, + "Ġderived": 2813, + "Ġexhib": 2814, + "Ġglobal": 2815, + "alax": 2816, + "ulating": 2817, + "Al": 2818, + "angu": 2819, + "bo": 2820, + "Ġrecom": 2821, + "Ġfeature": 2822, + "dependent": 2823, + "Ġrot": 2824, + "vention": 2825, + "Ġremov": 2826, + "Ġwind": 2827, + "Ġaccuracy": 2828, + "size": 2829, + "Ġsumm": 2830, + "Ġmeasurement": 2831, + "Ġfields": 2832, + "wards": 2833, + "Ġliter": 2834, + "ataly": 2835, + "ĠStr": 2836, + "Ġreport": 2837, + "Ġcentral": 2838, + "Ġsqu": 2839, + "Ġtherapy": 2840, + "hest": 2841, + "Ġfeed": 2842, + "SMILES": 2843, + "ĠAN": 2844, + "Ġsites": 2845, + "â̲": 2846, + "ours": 2847, + "omal": 2848, + "Ġlip": 2849, + "Ġanalyzed": 2850, + "Ġ°": 2851, + "Ġwee": 2852, + "tem": 2853, + "Ġanother": 2854, + "iles": 2855, + "Ġcomplete": 2856, + "Ġnext": 2857, + "ĠOne": 2858, + "bi": 2859, + "rip": 2860, + "state": 2861, + "ĠModel": 2862, + "Ġfindings": 2863, + "ĠPre": 2864, + "Ġrecent": 2865, + "ascular": 2866, + "Ġestimate": 2867, + "Ġmechanisms": 2868, + "ĠResults": 2869, + "Ġparticipants": 2870, + "Ġeng": 2871, + "most": 2872, + "ometric": 2873, + "Ġequal": 2874, + "Ġrob": 2875, + "Ġpolar": 2876, + "Ġgenetic": 2877, + "Ġbo": 2878, + "Ġrest": 2879, + "ĠÏĢ": 2880, + "Ġrelation": 2881, + "Ġquestion": 2882, + "epti": 2883, + "Ġdiffic": 2884, + "ems": 2885, + "Ġfuture": 2886, + "ify": 2887, + "Ġmode": 2888, + "Ġmembrane": 2889, + "Ġheat": 2890, + "Aut": 2891, + "ding": 2892, + "Ġoxid": 2893, + "Ġconfig": 2894, + "plication": 2895, + "ĠMon": 2896, + "allel": 2897, + "ided": 2898, + "Ġdirection": 2899, + "pled": 2900, + "Ġprovides": 2901, + "Ġindicate": 2902, + "Ġsets": 2903, + "Ġtechnique": 2904, + "Ġmac": 2905, + "Ġhypot": 2906, + "Ġatten": 2907, + "Ġevent": 2908, + "Ġstage": 2909, + "Ġnode": 2910, + "Ġreference": 2911, + "Ġupper": 2912, + "Ġtechniques": 2913, + "Ġgreater": 2914, + "Ġdirectly": 2915, + "Ġareas": 2916, + "Ġdiss": 2917, + "hor": 2918, + "ĠPol": 2919, + "Ġevaluation": 2920, + "Ġpatterns": 2921, + "ĠAbstract": 2922, + "Ġvirus": 2923, + "vey": 2924, + "PC": 2925, + "Ġwomen": 2926, + "rient": 2927, + "Ġplasma": 2928, + "Ġproduced": 2929, + "Ġε": 2930, + "Ġanalyses": 2931, + "ĠSub": 2932, + "Ġsetting": 2933, + "Ġmoment": 2934, + "Ġthermal": 2935, + "Ġoptimal": 2936, + "Ġtaken": 2937, + "Ġrecogn": 2938, + "Ġvariation": 2939, + "ĠLemma": 2940, + "Ġsus": 2941, + "frak": 2942, + "ĠIL": 2943, + "Ġprocedure": 2944, + "hood": 2945, + "Ġaim": 2946, + "aries": 2947, + "mathfrak": 2948, + "Ġplant": 2949, + "brid": 2950, + "elect": 2951, + "Ġvisual": 2952, + "urs": 2953, + "cence": 2954, + "Ġfive": 2955, + "Ġspatial": 2956, + "Ġreceptor": 2957, + "Ġindicated": 2958, + "Ġess": 2959, + "Ġconsistent": 2960, + "Ġturn": 2961, + "tices": 2962, + "Ġexists": 2963, + "ectors": 2964, + "Ġenzym": 2965, + "meric": 2966, + "Ġnoise": 2967, + "Ġground": 2968, + "Ġestimated": 2969, + "eline": 2970, + "Ġchannel": 2971, + "tition": 2972, + "Ġdiscussed": 2973, + "omer": 2974, + "otes": 2975, + "Ġexact": 2976, + "ĠSec": 2977, + "Ġtake": 2978, + "Ġknowledge": 2979, + "Ġprop": 2980, + "Ġinflamm": 2981, + "Ġdouble": 2982, + "It": 2983, + "Ġcontext": 2984, + "ĠMed": 2985, + "MA": 2986, + "Ġfat": 2987, + "ams": 2988, + "data": 2989, + "ands": 2990, + "Ġcardi": 2991, + "ĠFurthermore": 2992, + "ocy": 2993, + "Ġobservations": 2994, + "apping": 2995, + "ĠInf": 2996, + "omial": 2997, + "Ġpublic": 2998, + "Ġemploy": 2999, + "Ġreason": 3000, + "ygen": 3001, + "Ġfollowed": 3002, + "Ġamount": 3003, + "Ġcertain": 3004, + "which": 3005, + "otyp": 3006, + "ĠCell": 3007, + "Ġchall": 3008, + "Ġparticle": 3009, + "ambda": 3010, + "Ġens": 3011, + "Ġpeople": 3012, + "ault": 3013, + "ĠUnd": 3014, + "ĠBe": 3015, + "umin": 3016, + "roscopy": 3017, + "MR": 3018, + "lation": 3019, + "Ġrepe": 3020, + "Ġable": 3021, + "ĠSo": 3022, + "ĠâĪŀ": 3023, + "Ġenti": 3024, + "Ġmove": 3025, + "Ġtrac": 3026, + "CO": 3027, + "Ġheter": 3028, + "Ġspeed": 3029, + "Ġefficiency": 3030, + "Ġoptical": 3031, + "Ġcombination": 3032, + "eness": 3033, + "Ġchem": 3034, + "LE": 3035, + "appa": 3036, + "Ġdecrease": 3037, + "μ": 3038, + "ped": 3039, + "note": 3040, + "ĠMulti": 3041, + "Ġaltern": 3042, + "Ġassume": 3043, + "ĠForm": 3044, + "stric": 3045, + "que": 3046, + "Ġiss": 3047, + "urrent": 3048, + "Ġprinc": 3049, + "Ġtask": 3050, + "ops": 3051, + "Ġwhereas": 3052, + "CH": 3053, + "Ġrevealed": 3054, + "Ġcannot": 3055, + "active": 3056, + "enz": 3057, + "Ġfore": 3058, + "Ġoperator": 3059, + "Ġcolum": 3060, + "atin": 3061, + "Ġoriginal": 3062, + "Ġsmaller": 3063, + "Ġmaterials": 3064, + "hydro": 3065, + "Ġcurve": 3066, + "Ġselection": 3067, + "akes": 3068, + "Ġexpos": 3069, + "ats": 3070, + "ĠÏī": 3071, + "Ġpack": 3072, + "Ġstability": 3073, + "Ġoverall": 3074, + "Ġmorph": 3075, + "Ġmetric": 3076, + "Ġol": 3077, + "Ġbar": 3078, + "ĠIN": 3079, + "IM": 3080, + "cy": 3081, + "ethyl": 3082, + "SP": 3083, + "Ġresponses": 3084, + "ancy": 3085, + "Ġlay": 3086, + "specific": 3087, + "Ġvs": 3088, + "aged": 3089, + "Ġsocial": 3090, + "Ġcut": 3091, + "IP": 3092, + "Ġlimited": 3093, + "encies": 3094, + "Ġprotoc": 3095, + "Ġcomposition": 3096, + "ĠThey": 3097, + "Ġnumbers": 3098, + "mbox": 3099, + "Ġdecreased": 3100, + "vec": 3101, + "RO": 3102, + "Authors": 3103, + "Ġthick": 3104, + "Ġcoordin": 3105, + "Ġmes": 3106, + "Ġaffect": 3107, + "Ġclose": 3108, + "Ġtransport": 3109, + "CA": 3110, + "rete": 3111, + "come": 3112, + "Ġcollected": 3113, + "ĠFrom": 3114, + "Ġcontains": 3115, + "chit": 3116, + "ĠDet": 3117, + "Ġflux": 3118, + "overy": 3119, + "eu": 3120, + "aff": 3121, + "Ġconducted": 3122, + "Ġcriter": 3123, + "Ġliterature": 3124, + "Ġmemory": 3125, + "Ġsequences": 3126, + "Ġpan": 3127, + "plicit": 3128, + "Ġtrue": 3129, + "Ġmedium": 3130, + "Ġdam": 3131, + "ire": 3132, + "cell": 3133, + "Let": 3134, + "eful": 3135, + "ĠAmeric": 3136, + "Ġnodes": 3137, + "gether": 3138, + "Ġtogether": 3139, + "TP": 3140, + "Ġrather": 3141, + "Ġauthors": 3142, + "Ġsch": 3143, + "Ġprocessing": 3144, + "Ġspectra": 3145, + "Ġevaluated": 3146, + "alk": 3147, + "Ġreduce": 3148, + "ĠHigh": 3149, + "ĠCons": 3150, + "Ġcycle": 3151, + "orn": 3152, + "iers": 3153, + "Ġpropor": 3154, + "ories": 3155, + "rate": 3156, + "Ġhost": 3157, + "ooth": 3158, + "ynt": 3159, + "Ġsources": 3160, + "Ġindividuals": 3161, + "Ġaccount": 3162, + "ĠAlthough": 3163, + "Ġcorrec": 3164, + "Ġplan": 3165, + "entially": 3166, + "Ġdistinc": 3167, + "Ġsoil": 3168, + "Ġsearch": 3169, + "Ġmanagement": 3170, + "Ġversion": 3171, + "âĢĶ": 3172, + "Ġfig": 3173, + "ĠNote": 3174, + "Ġhead": 3175, + "ditional": 3176, + "Ġbuild": 3177, + "ĠGl": 3178, + "asis": 3179, + "group": 3180, + "Ġdisplay": 3181, + "ĠUniversity": 3182, + "ootnote": 3183, + "ameter": 3184, + "minist": 3185, + "opl": 3186, + "ymph": 3187, + "Lambda": 3188, + "Ġidentify": 3189, + "ĠStere": 3190, + "ĠïĢ": 3191, + "Ġprol": 3192, + "ource": 3193, + "icial": 3194, + "Ġsimulations": 3195, + "Ġthresh": 3196, + "point": 3197, + "earch": 3198, + "elling": 3199, + "ĠAcc": 3200, + "Ġframework": 3201, + "Ġstrength": 3202, + "ĠAb": 3203, + "ticles": 3204, + "Ġcos": 3205, + "Footnote": 3206, + "ru": 3207, + "ospital": 3208, + "Ġstable": 3209, + "Ġmotion": 3210, + "Ġtested": 3211, + "Ġtests": 3212, + "aster": 3213, + "ldots": 3214, + "CL": 3215, + "inite": 3216, + "Ġspecial": 3217, + "====": 3218, + "Ġapproaches": 3219, + "ping": 3220, + "Ġconsum": 3221, + "SD": 3222, + "Ġjust": 3223, + "kappa": 3224, + "Ġthough": 3225, + "faces": 3226, + "Ġrapid": 3227, + "ensive": 3228, + "Ġnecessary": 3229, + "Ġtub": 3230, + "Ġforce": 3231, + "Ġblack": 3232, + "volution": 3233, + "ĠAtom": 3234, + "ĠHere": 3235, + "itude": 3236, + "ensions": 3237, + "ffer": 3238, + "rich": 3239, + "Ġgives": 3240, + "Ġshape": 3241, + "Ġhard": 3242, + "omp": 3243, + "Ġrepresentation": 3244, + "ling": 3245, + "ĠDec": 3246, + "Ġnumerical": 3247, + "Ġplace": 3248, + "Ġleading": 3249, + "Ġbenef": 3250, + "Ġregular": 3251, + "Ġcluster": 3252, + "Ġrelatively": 3253, + "Ġpercent": 3254, + "Ġautom": 3255, + "Ġsympt": 3256, + "ibri": 3257, + "ches": 3258, + "henyl": 3259, + "car": 3260, + "Ġillustr": 3261, + "ports": 3262, + "emic": 3263, + "Ġgive": 3264, + "Ġconven": 3265, + "lection": 3266, + "ĠĠĠĠĠĠĠĠĠĠĠĠ": 3267, + "ĠAnd": 3268, + "Ġfood": 3269, + "mic": 3270, + "ographic": 3271, + "Ġcheck": 3272, + "Ġability": 3273, + "iquid": 3274, + "Ġsubstr": 3275, + "ĠâĪĨ": 3276, + "Ġedge": 3277, + "ĠPD": 3278, + "Ġclassification": 3279, + "Ġsurvival": 3280, + "ĠCal": 3281, + "erate": 3282, + "Ġuseful": 3283, + "Ġcarried": 3284, + "Ġintensity": 3285, + "HE": 3286, + "ocenter": 3287, + "Ġpathway": 3288, + "Ġdefinition": 3289, + "Ġscheme": 3290, + "Ġsubsequ": 3291, + "ĠFirst": 3292, + "Ġconsequ": 3293, + "ĠDiff": 3294, + "Ġinhibit": 3295, + "Ġamplit": 3296, + "aser": 3297, + "ĠNetwork": 3298, + "normal": 3299, + "ĠST": 3300, + "Ġsolid": 3301, + "perim": 3302, + "comes": 3303, + "Ġcyt": 3304, + "odies": 3305, + "IF": 3306, + "radi": 3307, + "Ġmor": 3308, + "Ġcore": 3309, + "BS": 3310, + "********": 3311, + "Ġsoftware": 3312, + "ĠGu": 3313, + "ired": 3314, + "ident": 3315, + "Ġdifficult": 3316, + "use": 3317, + "Ġadded": 3318, + "ley": 3319, + "Ġcaused": 3320, + "gence": 3321, + "Ġbase": 3322, + "####": 3323, + "ogenic": 3324, + "from": 3325, + "Ġstatus": 3326, + "Ġassociation": 3327, + "ĠStereocenter": 3328, + "Ġgalax": 3329, + "NO": 3330, + "anguage": 3331, + "Ġdimension": 3332, + "ogenesis": 3333, + "Ġemission": 3334, + "Ġdeath": 3335, + "ulin": 3336, + "Ġagre": 3337, + "turb": 3338, + "nabl": 3339, + "poral": 3340, + "Ġpor": 3341, + "Ġcombined": 3342, + "Ġalgorithms": 3343, + "Cs": 3344, + "Ġsensitivity": 3345, + "Ġallows": 3346, + "Ġcapacity": 3347, + "version": 3348, + "Ġrestric": 3349, + "rome": 3350, + "Ġexposure": 3351, + "hy": 3352, + "anning": 3353, + "Ġobject": 3354, + "Ġcode": 3355, + "fl": 3356, + "roduction": 3357, + "resents": 3358, + "rup": 3359, + "Ġtext": 3360, + "ĠMat": 3361, + "Ġleads": 3362, + "Ġreson": 3363, + "Ġproducts": 3364, + "Ġwhole": 3365, + "Ġmatter": 3366, + "Phi": 3367, + "opt": 3368, + "encing": 3369, + "fficients": 3370, + "na": 3371, + "pecially": 3372, + "Ġhaving": 3373, + "ropy": 3374, + "Ġuncertain": 3375, + "enari": 3376, + "rical": 3377, + "Ġminim": 3378, + "Ġorigin": 3379, + "uper": 3380, + "ĠNon": 3381, + "Ġevaluate": 3382, + "Proof": 3383, + "cap": 3384, + "Ġsignaling": 3385, + "Ġpolymer": 3386, + "tically": 3387, + "itten": 3388, + "antit": 3389, + "Ġuser": 3390, + "level": 3391, + "Ġmeasures": 3392, + "Ġdynamic": 3393, + "Ġmonths": 3394, + "oti": 3395, + "rand": 3396, + "Ġuntil": 3397, + "Ġdenote": 3398, + "Ġnote": 3399, + "Ġmaintain": 3400, + "Ġkin": 3401, + "scill": 3402, + "Ġimaging": 3403, + "Ġpain": 3404, + "avy": 3405, + "Ġmit": 3406, + "othe": 3407, + "Ġregul": 3408, + "known": 3409, + "Ġplot": 3410, + "nabla": 3411, + "Ġfraction": 3412, + "wer": 3413, + "Ġstrategy": 3414, + "Ġgreat": 3415, + "Ġdataset": 3416, + "Ġunique": 3417, + "CM": 3418, + "Ġtw": 3419, + "han": 3420, + "ĠEu": 3421, + "andid": 3422, + "Ġbackground": 3423, + "Ġbroad": 3424, + "ilt": 3425, + "Ġimproved": 3426, + "Ġdiagnosis": 3427, + "ious": 3428, + "Ġdig": 3429, + "rem": 3430, + "era": 3431, + "Ġexcl": 3432, + "Ġmetal": 3433, + "Ġsix": 3434, + "Ġminimum": 3435, + "usions": 3436, + "ee": 3437, + "Ġcompounds": 3438, + "Ġasp": 3439, + "Ġeth": 3440, + "Ġdetect": 3441, + "ference": 3442, + "Ġη": 3443, + "Ġstatistical": 3444, + "atives": 3445, + "ris": 3446, + "Ġtheorem": 3447, + "ĠOF": 3448, + "ww": 3449, + "arily": 3450, + "ception": 3451, + "iving": 3452, + "Ġtesting": 3453, + "Ġdiagnos": 3454, + "Ġrepresents": 3455, + "Sigma": 3456, + "onical": 3457, + "Ġequivalent": 3458, + "Ġbiom": 3459, + "Ġsubst": 3460, + "raints": 3461, + "ĠRef": 3462, + "Ġscore": 3463, + "Ġdoc": 3464, + "Ġimplies": 3465, + "eter": 3466, + "Ġsynthesis": 3467, + "ilibri": 3468, + "attering": 3469, + "CS": 3470, + "alse": 3471, + "Ġneuro": 3472, + "Ġalthough": 3473, + "irus": 3474, + "methyl": 3475, + "Ġtranscription": 3476, + "ÏĢ": 3477, + "ĠMolecular": 3478, + "Ġcause": 3479, + "mut": 3480, + "ĠId": 3481, + "λ": 3482, + "add": 3483, + "Ġplac": 3484, + "Ġagg": 3485, + "ture": 3486, + "Ġlack": 3487, + "Ġprediction": 3488, + "raw": 3489, + "An": 3490, + "Ġult": 3491, + "ynomial": 3492, + "Ġimmune": 3493, + "ili": 3494, + "Ġprep": 3495, + "γ": 3496, + "class": 3497, + "Ġmach": 3498, + "ample": 3499, + "Ġresolution": 3500, + "Ġcoupling": 3501, + "seud": 3502, + "Ġindicates": 3503, + "Ġgeneration": 3504, + "Ġhar": 3505, + "Ġfund": 3506, + "scale": 3507, + "Ġeigen": 3508, + "ĠRel": 3509, + "abor": 3510, + "ĠCH": 3511, + "ext": 3512, + "amm": 3513, + "Ġcorrect": 3514, + "Ġscreen": 3515, + "Ġstructural": 3516, + "ĠpH": 3517, + "Ġrelevant": 3518, + "Ġangle": 3519, + "IG": 3520, + "Ġalgebra": 3521, + "helial": 3522, + "Ġworld": 3523, + "Ġcurves": 3524, + "ĠIntroduction": 3525, + "Ġthird": 3526, + "Ġintroduced": 3527, + "Big": 3528, + "no": 3529, + "auss": 3530, + "subset": 3531, + "Ġtransmission": 3532, + "Ġprofile": 3533, + "Ġν": 3534, + "Ġespecially": 3535, + "Ġattrib": 3536, + "uction": 3537, + "Ġcoefficients": 3538, + "Ġremains": 3539, + "Ġneigh": 3540, + "osen": 3541, + "Ġreli": 3542, + "Ġhighest": 3543, + "Ġuniform": 3544, + "Ġfar": 3545, + "chitect": 3546, + "||": 3547, + "Ġappropri": 3548, + "plex": 3549, + "ĠMass": 3550, + "ogeneous": 3551, + "ales": 3552, + "Ġrefer": 3553, + "Ġneeded": 3554, + "Ġdifferential": 3555, + "ceed": 3556, + "$$": 3557, + "ynamic": 3558, + "Ġsex": 3559, + "Ġspectral": 3560, + "char": 3561, + "PE": 3562, + "TS": 3563, + "Ġapproximately": 3564, + "value": 3565, + "Ġhalf": 3566, + "ending": 3567, + "Ġgradi": 3568, + "Ġcoefficient": 3569, + "ĠPhys": 3570, + "Ġconcer": 3571, + "Ġlabel": 3572, + "iral": 3573, + "Ġcharge": 3574, + "Ġoxygen": 3575, + "Ġdevi": 3576, + "Ġinternal": 3577, + "Ġexpans": 3578, + "load": 3579, + "ĠSm": 3580, + "rang": 3581, + "Con": 3582, + "ĠNa": 3583, + "Ġke": 3584, + "Ġdiab": 3585, + "ached": 3586, + "Ġlocation": 3587, + "Ġvolt": 3588, + "ĠDisc": 3589, + "---": 3590, + "ocytes": 3591, + "oretical": 3592, + "Ġgain": 3593, + "Ġmedi": 3594, + "ympt": 3595, + "oted": 3596, + "ĠVal": 3597, + "Ġcommunity": 3598, + "plementary": 3599, + "Ġtree": 3600, + "ĠTwo": 3601, + "Ġwhose": 3602, + "Ġdone": 3603, + "amine": 3604, + "Ġbiological": 3605, + "inks": 3606, + "Ġalmost": 3607, + "Ġslight": 3608, + "Ġrepro": 3609, + "ģĦ": 3610, + "Ġtherap": 3611, + "ocation": 3612, + "Ġgly": 3613, + "ĠEqu": 3614, + "Ġcolor": 3615, + "Ġnam": 3616, + "section": 3617, + "ĠEm": 3618, + "ready": 3619, + "Hz": 3620, + "PD": 3621, + "function": 3622, + "change": 3623, + "Ġprincip": 3624, + "Ġbecome": 3625, + "ĠâĢĺ": 3626, + "Ġcour": 3627, + "Ġlocated": 3628, + "Ġrang": 3629, + "inity": 3630, + "Ġinterval": 3631, + "gin": 3632, + "Ġinvestigate": 3633, + "free": 3634, + "Ġvitro": 3635, + "Ġsubset": 3636, + "Ġmov": 3637, + "Ġprove": 3638, + "Ġliver": 3639, + "ategor": 3640, + "etes": 3641, + "Ġlymph": 3642, + "dom": 3643, + "ĠElect": 3644, + "Ġserum": 3645, + "Ġscenari": 3646, + "ends": 3647, + "ĠFinally": 3648, + "Ġfilter": 3649, + "IL": 3650, + "Ġabund": 3651, + "mentation": 3652, + "imals": 3653, + "num": 3654, + "enced": 3655, + "Ġproperty": 3656, + "matrix": 3657, + "ĠCompar": 3658, + "Ġland": 3659, + "ĠChar": 3660, + "ressive": 3661, + "ulus": 3662, + "Ġbone": 3663, + "Ex": 3664, + "Ġradiation": 3665, + "Ġsuggested": 3666, + "ĠComput": 3667, + "Ġthreshold": 3668, + "ĠAD": 3669, + "Ġhor": 3670, + "Ġinduc": 3671, + "Ġapproximation": 3672, + "Ġadminist": 3673, + "Ġord": 3674, + "Ġlung": 3675, + "Ġreceived": 3676, + "Ġnorm": 3677, + "Ġestimates": 3678, + "Ġlaw": 3679, + "Ġoutcomes": 3680, + "ĠPr": 3681, + "Ġdepth": 3682, + "Ġelse": 3683, + "Ġcontribution": 3684, + "hetic": 3685, + "Ġconserv": 3686, + "Ġupon": 3687, + "Ġdeep": 3688, + "MD": 3689, + "Ġmel": 3690, + "Ġfilm": 3691, + "ilibrium": 3692, + "Ġoscill": 3693, + "olved": 3694, + "Ġbreast": 3695, + "CP": 3696, + "ĠDist": 3697, + "rices": 3698, + "inated": 3699, + "Ġoptimization": 3700, + "Ġpredicted": 3701, + "sf": 3702, + "dim": 3703, + "ĠSN": 3704, + "Ġavoid": 3705, + "Ġneural": 3706, + "Ġwa": 3707, + "rope": 3708, + "Ġdistributions": 3709, + "oxid": 3710, + "Ġsmooth": 3711, + "path": 3712, + "Ġfluid": 3713, + "Ġsaf": 3714, + "Ġchoice": 3715, + "AA": 3716, + "Ġmolecules": 3717, + "US": 3718, + "Ġalways": 3719, + "ivo": 3720, + "Ġregression": 3721, + "Ġsuccessful": 3722, + "Ġwall": 3723, + "oung": 3724, + "Ġactivities": 3725, + "Ġdependence": 3726, + "Ġrequires": 3727, + "Ġplane": 3728, + "Ġdesigned": 3729, + "PI": 3730, + "down": 3731, + "Ġpopulations": 3732, + "cor": 3733, + "mediate": 3734, + "Ġdose": 3735, + "Ġbond": 3736, + "Co": 3737, + "ĠMan": 3738, + "Ġdiagram": 3739, + "gs": 3740, + "Ġtool": 3741, + "Ġisolated": 3742, + "Ġversus": 3743, + "ney": 3744, + "Ġemerg": 3745, + "ĠAut": 3746, + "aim": 3747, + "field": 3748, + "Ġexamined": 3749, + "Ġsat": 3750, + "SM": 3751, + "ĠSpec": 3752, + "Ġparallel": 3753, + "isation": 3754, + "Ġdistinct": 3755, + "Ġpredict": 3756, + "Ġfer": 3757, + "Ġunderstanding": 3758, + "ĠSimilar": 3759, + "ĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ": 3760, + "udes": 3761, + "Ġorient": 3762, + "hic": 3763, + "uz": 3764, + "Ġmodified": 3765, + "Ġâμ": 3766, + "FF": 3767, + "There": 3768, + "Ġtrial": 3769, + "xy": 3770, + "gery": 3771, + "Ġalready": 3772, + "define": 3773, + "ming": 3774, + "ĠSD": 3775, + "Ġmonitor": 3776, + "Ġpsy": 3777, + "Ġbecomes": 3778, + "istry": 3779, + "ĠÎĵ": 3780, + "Ġhum": 3781, + "rier": 3782, + "ession": 3783, + "Ġhistory": 3784, + "ö": 3785, + "Ġξ": 3786, + "Ġestablished": 3787, + "Ġachieved": 3788, + "estern": 3789, + "ÏĨ": 3790, + "ĠHence": 3791, + "Ġassessment": 3792, + "otor": 3793, + "Ġdescribe": 3794, + "ochond": 3795, + "ylation": 3796, + "sts": 3797, + "space": 3798, + "Ġdiseases": 3799, + "jection": 3800, + "Ġslow": 3801, + "Ġnonlinear": 3802, + "ply": 3803, + "ml": 3804, + "Ġembed": 3805, + "comp": 3806, + "Ġefficient": 3807, + "Ġoperation": 3808, + "Ġcontact": 3809, + "oz": 3810, + "Ġinvari": 3811, + "Ġcenter": 3812, + "Ġconc": 3813, + "widetilde": 3814, + "Ġbeam": 3815, + "Ġclosed": 3816, + "ĠMethods": 3817, + "Ġchronic": 3818, + "aling": 3819, + "Ġsevere": 3820, + "Ġforms": 3821, + "ilit": 3822, + "side": 3823, + "pen": 3824, + "Ġbran": 3825, + "oud": 3826, + "tality": 3827, + "Ġmaps": 3828, + "acts": 3829, + "OL": 3830, + "PR": 3831, + "ĠÍ": 3832, + "sl": 3833, + "Ġinstance": 3834, + "ully": 3835, + "Ġestimation": 3836, + "Ġplate": 3837, + "Ġdevice": 3838, + "ĠIII": 3839, + "sin": 3840, + "Ġplants": 3841, + "ittle": 3842, + "Ġproduce": 3843, + "Ġhence": 3844, + "Ġnature": 3845, + "Ġrelease": 3846, + "ĠMin": 3847, + "rict": 3848, + "Ġconnected": 3849, + "ottom": 3850, + "ellar": 3851, + "Ġformed": 3852, + "Ġmob": 3853, + "Ġcomputed": 3854, + "ĠRE": 3855, + "Ġpolynomial": 3856, + "Ġliquid": 3857, + "gn": 3858, + "Ġassay": 3859, + "Ġmanif": 3860, + "ĠSi": 3861, + "rence": 3862, + "Ġaxis": 3863, + "VID": 3864, + "Ġsignals": 3865, + "θ": 3866, + "tok": 3867, + "ds": 3868, + "Ġrats": 3869, + "Ġtor": 3870, + "olecular": 3871, + "ched": 3872, + "Ġdescri": 3873, + "Ġexpon": 3874, + "Ġperturb": 3875, + "Ġgluc": 3876, + "Ġcolumn": 3877, + "UL": 3878, + "Ġmainly": 3879, + "Ġmul": 3880, + "ider": 3881, + "ĠCR": 3882, + "Ġcataly": 3883, + "Ġlaser": 3884, + "tioned": 3885, + "den": 3886, + "Ġsuggests": 3887, + "fig": 3888, + "Ġpropag": 3889, + "org": 3890, + "rep": 3891, + "Ġcharacterized": 3892, + "ologies": 3893, + "Ġaccum": 3894, + "Ġvary": 3895, + "Ġcontrolled": 3896, + "Ġupd": 3897, + "ĠBr": 3898, + "Ġentire": 3899, + "Ġ@": 3900, + "âģĦ": 3901, + "ĠÌ": 3902, + "Ġdatab": 3903, + "ano": 3904, + "amil": 3905, + "Ġadjust": 3906, + "ye": 3907, + "pression": 3908, + "erences": 3909, + "Ġessential": 3910, + "ĠHydro": 3911, + "ĠTr": 3912, + "Ġappropriate": 3913, + "Ġformula": 3914, + "Ġlattice": 3915, + "Ġacute": 3916, + "Ġusually": 3917, + "itable": 3918, + "Ġmar": 3919, + "Ġμm": 3920, + "ĠUSA": 3921, + "Ġincub": 3922, + "ocks": 3923, + "Ġpepti": 3924, + "iddle": 3925, + "Ġdecom": 3926, + "Ġdamage": 3927, + "Ġgenome": 3928, + "Ġmouse": 3929, + "circ": 3930, + "Ġlayers": 3931, + "Ġtrack": 3932, + "Ġtox": 3933, + "Ġreplac": 3934, + "Ġadvant": 3935, + "izon": 3936, + "Ġrecorded": 3937, + "Ġstart": 3938, + "Ġrank": 3939, + "ser": 3940, + "ĠGene": 3941, + "aussian": 3942, + "ingu": 3943, + "Ġconstraints": 3944, + "flow": 3945, + "Ġmig": 3946, + "PL": 3947, + "Ġincor": 3948, + "appro": 3949, + "Ġfast": 3950, + "Ġmuscle": 3951, + "Ġhome": 3952, + "eq": 3953, + "ĠÏĪ": 3954, + "Ġstrongly": 3955, + "ĠEurope": 3956, + "Ġsubjects": 3957, + "Ġobjects": 3958, + "test": 3959, + "tered": 3960, + "ĠWhile": 3961, + "Ġsymmetry": 3962, + "Ġquantif": 3963, + "``": 3964, + "Ġbreak": 3965, + "ĠExperim": 3966, + "Ġmixt": 3967, + "<<": 3968, + "ĠChina": 3969, + "ĠIdentif": 3970, + "Ġaffected": 3971, + "Ġsecondary": 3972, + "Ġinequ": 3973, + "incl": 3974, + "EG": 3975, + "FT": 3976, + "Ġfailure": 3977, + "ectiv": 3978, + "Ġkm": 3979, + "Ġsampling": 3980, + "Ġexpansion": 3981, + "Ġpractice": 3982, + "uations": 3983, + "ognitive": 3984, + "Ġdiet": 3985, + "Ġtemperatures": 3986, + "Ġcontrols": 3987, + "Ġchosen": 3988, + "Ġgenerally": 3989, + "ancer": 3990, + "Ġdegrad": 3991, + "uli": 3992, + "sm": 3993, + "otherapy": 3994, + "Ġtowards": 3995, + "ĠProperties": 3996, + "Ġclusters": 3997, + "Ġdelay": 3998, + "Ġhep": 3999, + "PA": 4000, + "ĠStudy": 4001, + "antitative": 4002, + "Ġclassical": 4003, + "ĠZh": 4004, + "ĠΩ": 4005, + "ĠBo": 4006, + "Ġseed": 4007, + "ĠStruct": 4008, + "Ġtrend": 4009, + "iological": 4010, + "Ġconfirmed": 4011, + "Ġdistributed": 4012, + "bial": 4013, + "Ġname": 4014, + "CN": 4015, + "valence": 4016, + "erior": 4017, + "iven": 4018, + "ned": 4019, + "Ġbehaviour": 4020, + "asks": 4021, + "gra": 4022, + "mark": 4023, + "Ġerrors": 4024, + "ĠRep": 4025, + "light": 4026, + "cript": 4027, + "If": 4028, + "Ġcandid": 4029, + "Ġdepends": 4030, + "ĠNational": 4031, + "Ġholds": 4032, + "Ġprotocol": 4033, + "ĠUnited": 4034, + "Ġinterface": 4035, + "Ġexpect": 4036, + "Ġïģ": 4037, + "ĠHIV": 4038, + "Ġroot": 4039, + "Ġscattering": 4040, + "words": 4041, + "Ġobservation": 4042, + "otop": 4043, + "Ġoccurs": 4044, + "ources": 4045, + "pite": 4046, + "ĠSte": 4047, + "Ġorth": 4048, + "Ġstain": 4049, + "Ġsteps": 4050, + "Ġcompare": 4051, + "Ġbasic": 4052, + "Ġinhibition": 4053, + "Ġsymptoms": 4054, + "ĠHealth": 4055, + "Ġpublished": 4056, + "fold": 4057, + "Ġtun": 4058, + "Ġvivo": 4059, + "Ġreconstr": 4060, + "ĠmRNA": 4061, + "icy": 4062, + "Ġhybrid": 4063, + "yr": 4064, + "Ġmixed": 4065, + "vis": 4066, + "ChI": 4067, + "Ġmedical": 4068, + "Ġfrag": 4069, + "Ġanimals": 4070, + "Ġimportance": 4071, + "Ġengine": 4072, + "ĠCT": 4073, + "Ġpairs": 4074, + "Ġbal": 4075, + "ĠEar": 4076, + "hers": 4077, + "Ġsynd": 4078, + "Ġarchitect": 4079, + "Ġidentification": 4080, + "Ġstrategies": 4081, + "Ġregulation": 4082, + "ĠLa": 4083, + "ror": 4084, + "Ġfluores": 4085, + "urity": 4086, + "Ġconcept": 4087, + "Ġattention": 4088, + "Ġtransformation": 4089, + "ucle": 4090, + "ĠResearch": 4091, + "Ġsimpl": 4092, + "Ġculture": 4093, + "aring": 4094, + "ifically": 4095, + "pir": 4096, + "ze": 4097, + "PT": 4098, + "mosp": 4099, + "Ġswit": 4100, + "Ġnor": 4101, + "Ġenhance": 4102, + "Ġenvironmental": 4103, + "rary": 4104, + "ĠMicro": 4105, + "Ġwide": 4106, + "opath": 4107, + "auge": 4108, + "zeta": 4109, + "Ġste": 4110, + "ĠEl": 4111, + "Ġwords": 4112, + "Ġnuclear": 4113, + "Ġlanguage": 4114, + "Ġdetails": 4115, + "opar": 4116, + "ĠRed": 4117, + "water": 4118, + "Ġcategor": 4119, + "Ġfile": 4120, + "Ġcover": 4121, + "Ġachieve": 4122, + "á": 4123, + "umm": 4124, + "Ġlig": 4125, + "Ġsurvey": 4126, + "Ġextended": 4127, + "lab": 4128, + "ĠInc": 4129, + "Ġdispers": 4130, + "Ġrecomm": 4131, + "ĠBased": 4132, + "Ġabsence": 4133, + "Ġconstruction": 4134, + "Ġpoor": 4135, + "Ġvoltage": 4136, + "Ġcellular": 4137, + "Ġmortality": 4138, + "Ġshowing": 4139, + "Ġprolif": 4140, + "mp": 4141, + "Ġneurons": 4142, + "Ġsupported": 4143, + "Ġprevent": 4144, + "eli": 4145, + "oxy": 4146, + "ica": 4147, + "Ġfully": 4148, + "Ġenough": 4149, + "otimes": 4150, + "ĠMR": 4151, + "Ġbul": 4152, + "Ġphenomen": 4153, + "FA": 4154, + "Ġdecision": 4155, + "Ġdual": 4156, + "Ġdecay": 4157, + "Ġown": 4158, + "Ġuses": 4159, + "Ġchalleng": 4160, + "Ġaddress": 4161, + "OC": 4162, + "tivation": 4163, + "Ġmill": 4164, + "Ġmodes": 4165, + "atus": 4166, + "iction": 4167, + "Ġabsorption": 4168, + "Ġepit": 4169, + "Ġconstra": 4170, + "Ġagreement": 4171, + "ĠAf": 4172, + "Ġbias": 4173, + "uded": 4174, + "Ġparts": 4175, + "Ġvan": 4176, + "Ġcolon": 4177, + "Ġexternal": 4178, + "Ġtheoretical": 4179, + "asi": 4180, + "Ġles": 4181, + "abilities": 4182, + "LA": 4183, + "ttps": 4184, + "Ġinstead": 4185, + "Ġmembers": 4186, + "++": 4187, + "Ġrecently": 4188, + "Ġprepared": 4189, + "Ġarticle": 4190, + "day": 4191, + "Ġextract": 4192, + "Ġâİ": 4193, + "Ġpathways": 4194, + "ÏĦ": 4195, + "mid": 4196, + "orage": 4197, + "Ġcommunication": 4198, + "Ġaccel": 4199, + "Ġunits": 4200, + "itis": 4201, + "ynthesis": 4202, + "Ġamplitude": 4203, + "rie": 4204, + "ultaneous": 4205, + "ĠLear": 4206, + "ecause": 4207, + "do": 4208, + "eff": 4209, + "Ġexplicit": 4210, + "Ġcriteria": 4211, + "bre": 4212, + "Ġexec": 4213, + "Ġmechanical": 4214, + "eros": 4215, + "ĠConcl": 4216, + "ĠExt": 4217, + "Ġclasses": 4218, + "Ġlonger": 4219, + "Ġcalculations": 4220, + "eutic": 4221, + "ociated": 4222, + "ardi": 4223, + "Ġcourse": 4224, + "Ġpartial": 4225, + "Ġsensor": 4226, + "Ïĥ": 4227, + "Ġoperators": 4228, + "ĠAmerican": 4229, + "ĠmM": 4230, + "Ġvacc": 4231, + "occ": 4232, + "icon": 4233, + "Ġoutcome": 4234, + "Ġanalog": 4235, + "Ġthickness": 4236, + "Ġreach": 4237, + "Ġassumed": 4238, + "ender": 4239, + "Ġmale": 4240, + "SE": 4241, + "Ġintra": 4242, + "Ġimplementation": 4243, + "emia": 4244, + "Ġenhanced": 4245, + "bility": 4246, + "Ġeasily": 4247, + "ump": 4248, + "Ġcarcin": 4249, + "osa": 4250, + "Ġcorresponds": 4251, + "neg": 4252, + "Ġmagnitude": 4253, + "const": 4254, + "Ġlatter": 4255, + "Ġrepresented": 4256, + "Ġsed": 4257, + "Ġparticularly": 4258, + "Ġwritten": 4259, + "part": 4260, + "Ġoil": 4261, + "berg": 4262, + "ĠBar": 4263, + "Ġdys": 4264, + "ĠSome": 4265, + "ĠMar": 4266, + "Ġalternative": 4267, + "ĠGerm": 4268, + "Ġgenerate": 4269, + "Ġconstruct": 4270, + "ians": 4271, + "stream": 4272, + "Ġec": 4273, + "ochemical": 4274, + "ibration": 4275, + "operative": 4276, + "ister": 4277, + "Ġrobust": 4278, + "tre": 4279, + "Ġmodeling": 4280, + "oring": 4281, + "ese": 4282, + "ded": 4283, + "ideo": 4284, + "Ġhydrogen": 4285, + "uments": 4286, + "Ġdemonstrate": 4287, + "Ġcorrelated": 4288, + "Ġsystematic": 4289, + "Ġsurgery": 4290, + "Ġindicating": 4291, + "Ġhypothesis": 4292, + "year": 4293, + "mitted": 4294, + "Ġstars": 4295, + "Ġprofiles": 4296, + "Ġconsists": 4297, + "tri": 4298, + "Ġdependent": 4299, + "ishing": 4300, + "top": 4301, + "Ġheart": 4302, + "atically": 4303, + "Ġinjury": 4304, + "Ġquad": 4305, + "Ġweeks": 4306, + "uting": 4307, + "ĠTe": 4308, + "Ġidenti": 4309, + "Ġgradient": 4310, + "Ġcalculation": 4311, + "Ġur": 4312, + "RT": 4313, + "zation": 4314, + "Ġeduc": 4315, + "ening": 4316, + "PP": 4317, + "zed": 4318, + "ush": 4319, + "Ġcharacteristic": 4320, + "Ġstrains": 4321, + "eth": 4322, + "Ġdivers": 4323, + "âĪĪ": 4324, + "oids": 4325, + "olic": 4326, + "Ġinterpret": 4327, + "Key": 4328, + "Ġattack": 4329, + "pective": 4330, + "Ġlabor": 4331, + "Ġmetast": 4332, + "NF": 4333, + "Ġtissues": 4334, + "Ġradius": 4335, + "ĠEach": 4336, + "Ġcat": 4337, + "Ġdon": 4338, + "Ġelev": 4339, + "Ġassemb": 4340, + "rons": 4341, + "Ġarbit": 4342, + "Ġpanel": 4343, + "Ġgrid": 4344, + "Ġtable": 4345, + "roscopic": 4346, + "Ġcle": 4347, + "ĠIntern": 4348, + "obacter": 4349, + "Ġassumption": 4350, + "ĠCOVID": 4351, + "Ġbounded": 4352, + "Ġothers": 4353, + "Ġschool": 4354, + "Ġhospital": 4355, + "lected": 4356, + "ĠCu": 4357, + "ÃĹ": 4358, + "Ġcomplet": 4359, + "Ġwidth": 4360, + "Ġlinks": 4361, + "po": 4362, + "ollow": 4363, + "Ġnut": 4364, + "Ġappears": 4365, + "rown": 4366, + "aro": 4367, + "Ġusers": 4368, + "Ġclim": 4369, + "Ġslightly": 4370, + "Ġblue": 4371, + "rab": 4372, + "ĠSer": 4373, + "Ġfigure": 4374, + "ĠRad": 4375, + "Ġelectric": 4376, + "mm": 4377, + "ochastic": 4378, + "rief": 4379, + "Ġcollection": 4380, + "Ġstem": 4381, + "Ġgover": 4382, + "Ġbur": 4383, + "Ġtypical": 4384, + "sup": 4385, + "Ġaggreg": 4386, + "raz": 4387, + "ĉĉĉ": 4388, + "Ġstation": 4389, + "Ġarter": 4390, + "ively": 4391, + "itrogen": 4392, + "Ġconstit": 4393, + "empt": 4394, + "ĠEffect": 4395, + "Ġdescription": 4396, + "Ġscores": 4397, + "Ġmethyl": 4398, + "ĠOb": 4399, + "ĠStates": 4400, + "Ġsplit": 4401, + "ĠVari": 4402, + "ĠWang": 4403, + "Ġcere": 4404, + "ĠFran": 4405, + "Ġneeds": 4406, + "ĠFour": 4407, + "Ġproject": 4408, + "Ġdevices": 4409, + "Ġintegral": 4410, + "ĠEs": 4411, + "ymmetric": 4412, + "Ġmess": 4413, + "Ġplays": 4414, + "ĠLearning": 4415, + "Ġoverl": 4416, + "Here": 4417, + "ignment": 4418, + "Ġdeliver": 4419, + "apan": 4420, + "CE": 4421, + "Ġgauge": 4422, + "ĠJoh": 4423, + "----------------": 4424, + "Ġunderlying": 4425, + "Ġthin": 4426, + "Ġassessed": 4427, + "Ġdiffusion": 4428, + "Ġheight": 4429, + "ĠSw": 4430, + "Ġdark": 4431, + "print": 4432, + "range": 4433, + "ĠCI": 4434, + "ises": 4435, + "lier": 4436, + "rant": 4437, + "omorphism": 4438, + "Ġcompact": 4439, + "ips": 4440, + "ĠName": 4441, + "Ġtechnology": 4442, + "agen": 4443, + "Ġconfiguration": 4444, + "Ġduration": 4445, + "ĠClass": 4446, + "Ġput": 4447, + "Ġmaking": 4448, + "Ġasympt": 4449, + "aid": 4450, + "Ġcoh": 4451, + "Ġcomplexity": 4452, + "Ġsections": 4453, + "ĠMD": 4454, + "ĠĠĠĠĠĠĠĠĠ": 4455, + "Ġrad": 4456, + "Ġsubstrate": 4457, + "dd": 4458, + "Ġann": 4459, + "Ġorganic": 4460, + "Ġtaking": 4461, + "Ġincludes": 4462, + "Ġkine": 4463, + "ares": 4464, + "Ġrow": 4465, + "ategory": 4466, + "Ġmitochond": 4467, + "UT": 4468, + "Ġsyndrome": 4469, + "ĠProb": 4470, + "retion": 4471, + "Ġfluct": 4472, + "ĠDis": 4473, + "Ġtransl": 4474, + "plas": 4475, + "Ġpsych": 4476, + "Ġsurfaces": 4477, + "Ġdetailed": 4478, + "amilton": 4479, + "Ġhold": 4480, + "ĠâĬĹ": 4481, + "ĠCN": 4482, + "Ġdil": 4483, + "ĠOver": 4484, + "atform": 4485, + "Ġvertical": 4486, + "Ġcomputation": 4487, + "Ġpure": 4488, + "Ġmakes": 4489, + "Ġexisting": 4490, + "Ġexamples": 4491, + "SO": 4492, + "orders": 4493, + "Ġmix": 4494, + "Ġincorpor": 4495, + "Ġrequ": 4496, + "antic": 4497, + "DNA": 4498, + "δ": 4499, + "Ġcloud": 4500, + "ĠTechn": 4501, + "Ġïĥ": 4502, + "ements": 4503, + "Ġbaseline": 4504, + "stein": 4505, + "Ġbelong": 4506, + "Ġtrials": 4507, + "Ġhorizon": 4508, + "Ġphosphor": 4509, + "Ġans": 4510, + "dix": 4511, + "roid": 4512, + "Ġapply": 4513, + "ued": 4514, + "ernel": 4515, + "Ġfemale": 4516, + "icacy": 4517, + "Ġvectors": 4518, + "Ġmatrices": 4519, + "atric": 4520, + "ĠMc": 4521, + "Ġpy": 4522, + "Ġchlor": 4523, + "len": 4524, + "Ġclearly": 4525, + "static": 4526, + "ref": 4527, + "ĠSouth": 4528, + "Ġmedia": 4529, + "ĠShe": 4530, + "ĠBay": 4531, + "Ġagents": 4532, + "By": 4533, + "Ġdifferentiation": 4534, + "istant": 4535, + "orphic": 4536, + "Ġvariety": 4537, + "Ġservice": 4538, + "Ġmapping": 4539, + "velength": 4540, + "Ġchannels": 4541, + "Ġcompute": 4542, + "Ġstream": 4543, + "uls": 4544, + "amide": 4545, + "oking": 4546, + "vit": 4547, + "Ġyields": 4548, + "omb": 4549, + "ĠGaussian": 4550, + "Ġpen": 4551, + "une": 4552, + "Ġexperience": 4553, + "band": 4554, + "ĠDo": 4555, + "mathsf": 4556, + "Ġallowed": 4557, + "Ar": 4558, + "RA": 4559, + "Ġbacterial": 4560, + "Ġmiss": 4561, + "Ġbacteria": 4562, + "Ġmomentum": 4563, + "Ġhours": 4564, + "uck": 4565, + "ĠProposition": 4566, + "bert": 4567, + "otrop": 4568, + "Ġvariance": 4569, + "Ġtrig": 4570, + "Ġshift": 4571, + "Ġequilibrium": 4572, + "bu": 4573, + "ING": 4574, + "Ġwhite": 4575, + "Ġkind": 4576, + "Ġjoint": 4577, + "Ġtemporal": 4578, + "ĠIV": 4579, + "ĠAfric": 4580, + "Ġsubject": 4581, + "ĠPo": 4582, + "head": 4583, + "idel": 4584, + "Ġantibody": 4585, + "ĠEffects": 4586, + "Ġspe": 4587, + "Ġsufficient": 4588, + "jected": 4589, + "rees": 4590, + "ĠTop": 4591, + "Ġmutations": 4592, + "isions": 4593, + "BC": 4594, + "Ġinduction": 4595, + "Ġinteresting": 4596, + "ella": 4597, + "can": 4598, + "Ġsusp": 4599, + "ĠGroup": 4600, + "Ġextracted": 4601, + "istically": 4602, + "coh": 4603, + "map": 4604, + "Ġaccurate": 4605, + "Ġtoo": 4606, + "Ġdimensions": 4607, + "tegr": 4608, + "Ġgreen": 4609, + "ĠRo": 4610, + "Ġwild": 4611, + "Ġloop": 4612, + "Ġmeta": 4613, + "Ġsubstit": 4614, + "osome": 4615, + "Ġsuggesting": 4616, + "Ġspecim": 4617, + "amental": 4618, + "iment": 4619, + "Ġij": 4620, + "Ġclaim": 4621, + "Ġauthor": 4622, + "Ġfilms": 4623, + "Ġcounter": 4624, + "Ġconventional": 4625, + "rin": 4626, + "otypes": 4627, + "Ġpast": 4628, + "Since": 4629, + "mediated": 4630, + "reatment": 4631, + "Ġextension": 4632, + "Ġbio": 4633, + "Ġsent": 4634, + "hal": 4635, + "Ġobjective": 4636, + "Ġarray": 4637, + "Ġsuitable": 4638, + "ĠBut": 4639, + "ĠHuman": 4640, + "organ": 4641, + "but": 4642, + "model": 4643, + "SI": 4644, + "Ġhealthy": 4645, + "Ġvac": 4646, + "Ġlate": 4647, + "Ġring": 4648, + "Ġlittle": 4649, + "MT": 4650, + "Ġsquare": 4651, + "Ġgeometry": 4652, + "ĠTHE": 4653, + "ĠSing": 4654, + "jug": 4655, + "Ġstudents": 4656, + ",,": 4657, + "Ġadult": 4658, + "Ġcharacterization": 4659, + "Ġatmosp": 4660, + "Ġmonitoring": 4661, + "ani": 4662, + "net": 4663, + "ĠPa": 4664, + "optosis": 4665, + "Ġcontin": 4666, + "ĠSol": 4667, + "Ġdatabase": 4668, + "import": 4669, + "mann": 4670, + "ĠProcess": 4671, + "ĠChen": 4672, + "Ġgap": 4673, + "Ġenzyme": 4674, + "OT": 4675, + "Ġsimultaneous": 4676, + "Ġexistence": 4677, + "BP": 4678, + "ĠJapan": 4679, + "ounts": 4680, + "Ġturb": 4681, + "Ġspaces": 4682, + "ĠWeight": 4683, + "ophil": 4684, + "Ġast": 4685, + "Ġwrite": 4686, + "Ġdiabetes": 4687, + "ĠCA": 4688, + "Ġneutral": 4689, + "Ġvariations": 4690, + "axon": 4691, + "Ġbegin": 4692, + "under": 4693, + "Ġextraction": 4694, + "ĠPati": 4695, + "Ġfron": 4696, + "efined": 4697, + "Ġacids": 4698, + "Ġservices": 4699, + "Ġsense": 4700, + "Ġagent": 4701, + "hens": 4702, + "electric": 4703, + "values": 4704, + "Ġimprovement": 4705, + "herent": 4706, + "actic": 4707, + "Ġacet": 4708, + "cdots": 4709, + "Ġamino": 4710, + "Ġroom": 4711, + "Ġexpress": 4712, + "Ġexcept": 4713, + "Ġold": 4714, + "plant": 4715, + "cepti": 4716, + "ĠPCR": 4717, + "ĠER": 4718, + "ĠBoth": 4719, + "vex": 4720, + "Ġadults": 4721, + "Ġpseud": 4722, + "Ġalle": 4723, + "Ġworks": 4724, + "Ġconsumption": 4725, + "ipher": 4726, + "cm": 4727, + "cast": 4728, + "Ġnanopar": 4729, + "Ïī": 4730, + "Ġecon": 4731, + "ynamics": 4732, + "Ġalter": 4733, + "Ġskin": 4734, + "Ġdiameter": 4735, + "GC": 4736, + "ĠSign": 4737, + "vial": 4738, + "Ġglucose": 4739, + "ĠNorth": 4740, + "otox": 4741, + "Ġprote": 4742, + "dx": 4743, + "ĠCr": 4744, + "Ġfract": 4745, + "Ġinside": 4746, + "Ġstatic": 4747, + "wid": 4748, + "Ġstorage": 4749, + "ĠAL": 4750, + "ĠMark": 4751, + "ĠAT": 4752, + "Ġsensitive": 4753, + "Ġads": 4754, + "Ġedges": 4755, + "ana": 4756, + "Re": 4757, + "Ġsummar": 4758, + "ĠAND": 4759, + "Ġremaining": 4760, + "ditionally": 4761, + "Ġmid": 4762, + "ĠTheory": 4763, + "MC": 4764, + "Ġflex": 4765, + "oly": 4766, + "Ġdegradation": 4767, + "Ġintr": 4768, + "ota": 4769, + "isms": 4770, + "Ġampl": 4771, + "ĠAre": 4772, + "Ġworking": 4773, + "Ġdiversity": 4774, + "Ġtensor": 4775, + "Ġbinary": 4776, + "\"\"\"": 4777, + "vals": 4778, + "Ġhem": 4779, + "ML": 4780, + "Ġμg": 4781, + "neq": 4782, + "ensities": 4783, + "Ġtakes": 4784, + "Ġcharg": 4785, + "Ġintervention": 4786, + "Ġalb": 4787, + "Ġqual": 4788, + "Ġmentioned": 4789, + "Ġones": 4790, + "ĠAccording": 4791, + "illed": 4792, + "OH": 4793, + "Sup": 4794, + "Ġgalaxies": 4795, + "aily": 4796, + "Ġrule": 4797, + "Ġcognitive": 4798, + "hern": 4799, + "Ġrecognition": 4800, + "Ġbuffer": 4801, + "Ġmarg": 4802, + "ĠNi": 4803, + "ĠâĪļ": 4804, + "Ġclin": 4805, + "Ġintegration": 4806, + "Ġsin": 4807, + "ĠAlso": 4808, + "Ġmachine": 4809, + "wr": 4810, + "idity": 4811, + "Ġsubsequent": 4812, + "Fe": 4813, + "Ġnames": 4814, + "ather": 4815, + "ĠCy": 4816, + "Ġmetabolism": 4817, + "Ġreactions": 4818, + "Ġiter": 4819, + "Ġnoted": 4820, + "Ġcauses": 4821, + "ĠHamilton": 4822, + "go": 4823, + "Ġrare": 4824, + "VA": 4825, + "ĠMy": 4826, + "vol": 4827, + "asure": 4828, + "Ġsignificance": 4829, + "ĠNone": 4830, + "Ġvehic": 4831, + "SR": 4832, + "Ġvariability": 4833, + "ĠDevelop": 4834, + "aren": 4835, + "Ġpromot": 4836, + "ards": 4837, + "Ġcomputational": 4838, + "Ġshall": 4839, + "izations": 4840, + "ĠHydrogen": 4841, + "Ġproliferation": 4842, + "Ġcoupled": 4843, + "chron": 4844, + "Ġconvergence": 4845, + "Ġgast": 4846, + "Ġcalculate": 4847, + "raft": 4848, + "paration": 4849, + "heric": 4850, + "ĠPC": 4851, + "plate": 4852, + "ptions": 4853, + "ĠAlgorithm": 4854, + "Ġresulted": 4855, + "DE": 4856, + "Ġinvestigation": 4857, + "ĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ": 4858, + "olation": 4859, + "Ġtasks": 4860, + "Ġleg": 4861, + "iness": 4862, + "Ġemployed": 4863, + "On": 4864, + "Ġexperi": 4865, + "Ġtraject": 4866, + "GA": 4867, + "Ġpurpose": 4868, + "ĠNum": 4869, + "Ġcompletely": 4870, + "that": 4871, + "ĠOptim": 4872, + "Ġformal": 4873, + "eck": 4874, + "ĠProtein": 4875, + "Ġgoal": 4876, + "Ġthroughout": 4877, + "Ġconsidering": 4878, + "Ġreflect": 4879, + "treated": 4880, + "oration": 4881, + "ribution": 4882, + "Ġtherapeutic": 4883, + "Ġfinding": 4884, + "UN": 4885, + "Then": 4886, + "ilities": 4887, + "Ġunknown": 4888, + "overed": 4889, + "Ġvertex": 4890, + "Ġexchange": 4891, + "Ġdrugs": 4892, + "ĠCP": 4893, + "Ġinstr": 4894, + "Ġsymmetric": 4895, + "ĠDep": 4896, + "Ġconstructed": 4897, + "Ġprevalence": 4898, + "Ġdecreases": 4899, + "ĠmiR": 4900, + "Ġyet": 4901, + "Ġbox": 4902, + "graph": 4903, + "widehat": 4904, + "alian": 4905, + "ufact": 4906, + "LR": 4907, + "cription": 4908, + "Ġnp": 4909, + "ĠCharacter": 4910, + "Ġepid": 4911, + "ν": 4912, + "Ġstages": 4913, + "Ġsay": 4914, + "ĠDuring": 4915, + "atur": 4916, + "ientif": 4917, + "abric": 4918, + "ü": 4919, + "ament": 4920, + "inations": 4921, + "Ġsolar": 4922, + "Ġdiscrete": 4923, + "ĠEr": 4924, + "ĠGeneral": 4925, + "bal": 4926, + "ĠCent": 4927, + "uel": 4928, + "Ġmixture": 4929, + "Ġwidely": 4930, + "ĠSecond": 4931, + "Ġresources": 4932, + "ĠAppro": 4933, + "ĠIR": 4934, + "Ġstring": 4935, + "opro": 4936, + "Ġinner": 4937, + "ĠComplex": 4938, + "OP": 4939, + "Ġatoms": 4940, + "Ġphases": 4941, + "Ġdomains": 4942, + "ada": 4943, + "Ġcountries": 4944, + "acet": 4945, + "ociation": 4946, + "izer": 4947, + "Ġitself": 4948, + "Ġminimal": 4949, + "ĠControl": 4950, + "ttp": 4951, + "Ġbottom": 4952, + "ball": 4953, + "ĠMay": 4954, + "dev": 4955, + "now": 4956, + "ember": 4957, + "Ġpercentage": 4958, + "ĠOther": 4959, + "omas": 4960, + "Ġled": 4961, + "Res": 4962, + "ĠEng": 4963, + "kg": 4964, + "Ġfrequencies": 4965, + "kin": 4966, + "Ġincidence": 4967, + "Ġanimal": 4968, + "Ġadop": 4969, + "Ġidentity": 4970, + "ĠRT": 4971, + "Ġyoung": 4972, + "istent": 4973, + "weight": 4974, + "gu": 4975, + "Ġseason": 4976, + "Ġexplained": 4977, + "ĠUnder": 4978, + "iotic": 4979, + "well": 4980, + "Ġmetabolic": 4981, + "gical": 4982, + "±": 4983, + "Theorem": 4984, + "ades": 4985, + "plicated": 4986, + "Ġcontained": 4987, + "Ġsulf": 4988, + "Ġcool": 4989, + "Ġperson": 4990, + "Ïģ": 4991, + "Ġpix": 4992, + "ĠSal": 4993, + "link": 4994, + "ini": 4995, + "tual": 4996, + "SH": 4997, + "ged": 4998, + "ky": 4999, + "asts": 5000, + "ercise": 5001, + "ĠHar": 5002, + "Ġrelax": 5003, + "equiv": 5004, + "Ġyour": 5005, + "Ġunderg": 5006, + "Ġrecovery": 5007, + "Ġcomm": 5008, + "Ġdenotes": 5009, + "formed": 5010, + "aria": 5011, + "etic": 5012, + "Ġtumors": 5013, + "ĠHy": 5014, + "Ġmarkers": 5015, + "Ġplaced": 5016, + "olute": 5017, + "Ġwaves": 5018, + "Ġuncertainty": 5019, + "Ġcontribute": 5020, + "ĠHist": 5021, + "Ġaver": 5022, + "Ġfav": 5023, + "Ġpow": 5024, + "ĠSee": 5025, + "Ġteam": 5026, + "Ġscales": 5027, + "ientific": 5028, + "ierarch": 5029, + "Ġearlier": 5030, + "Ġsatisfies": 5031, + "Ġcrystal": 5032, + "Ġpregn": 5033, + "Ġobserve": 5034, + "Ġonline": 5035, + "Ġcontributions": 5036, + "ogram": 5037, + "ĠMa": 5038, + "Ġfrac": 5039, + "Ġspread": 5040, + "Ġonce": 5041, + "det": 5042, + "Ġrespond": 5043, + "Ġplatform": 5044, + "Ġinflammatory": 5045, + "utive": 5046, + "ĠSumm": 5047, + "place": 5048, + "Ġions": 5049, + "Ġwindow": 5050, + "axis": 5051, + "estinal": 5052, + "Ġdepending": 5053, + "Ġseparation": 5054, + "Ġforward": 5055, + "ĠTi": 5056, + "Ġglass": 5057, + "Ġaccept": 5058, + "Ġfeedback": 5059, + "Ġonto": 5060, + "ME": 5061, + "merc": 5062, + "unctional": 5063, + "Ġapoptosis": 5064, + "ĠProperty": 5065, + "Ġintegrated": 5066, + "Ġorb": 5067, + "Ġdeviation": 5068, + "Ġantibodies": 5069, + "Ġremoved": 5070, + "Ġlipid": 5071, + "armac": 5072, + "Ġarbitrary": 5073, + "agger": 5074, + "Ġembry": 5075, + "Ġgrain": 5076, + "Ġdrop": 5077, + "Ġstarting": 5078, + "Ġrelationships": 5079, + "ĠÏĩ": 5080, + "SF": 5081, + "Ġsimply": 5082, + "Ġfacilit": 5083, + "Ġzone": 5084, + "ils": 5085, + "Psi": 5086, + "Ġinequality": 5087, + "Keywords": 5088, + "Ġtoler": 5089, + "edge": 5090, + "Ġeasy": 5091, + "Ġalpha": 5092, + "Ġperf": 5093, + "width": 5094, + "init": 5095, + "Ġimplemented": 5096, + "CF": 5097, + "osity": 5098, + "ocyte": 5099, + "Ġproportion": 5100, + "rest": 5101, + "ĠSuper": 5102, + "Ġpref": 5103, + "Ġword": 5104, + "ev": 5105, + "Ġextent": 5106, + "Ġinjection": 5107, + "alled": 5108, + "ĠAnti": 5109, + "Ġbeta": 5110, + "ĠJan": 5111, + "ĠGa": 5112, + "ĠZhang": 5113, + "Ġiron": 5114, + "Ġquantitative": 5115, + "roc": 5116, + "Ġfall": 5117, + "Ġregarding": 5118, + "Ġfix": 5119, + "Ġdatasets": 5120, + "Ġtend": 5121, + "Ġscalar": 5122, + "Ġresidual": 5123, + "Ġratios": 5124, + "ĠΦ": 5125, + "king": 5126, + "Ġinflammation": 5127, + "Ġsingular": 5128, + "ĠPark": 5129, + "omatic": 5130, + "unctions": 5131, + "Ġwar": 5132, + "ÍĴ": 5133, + "hemat": 5134, + "Ġface": 5135, + "ĠHu": 5136, + "Ġfundamental": 5137, + "Ġwavelength": 5138, + "eling": 5139, + "ĠSuch": 5140, + "RNAs": 5141, + "ct": 5142, + "Ġiden": 5143, + "cean": 5144, + "new": 5145, + "Type": 5146, + "ĠFormula": 5147, + "Ġmedic": 5148, + "ussion": 5149, + "Ġdistingu": 5150, + "Ġresonance": 5151, + "ATION": 5152, + "inear": 5153, + "Ġhyd": 5154, + "ln": 5155, + "âĨĴ": 5156, + "ĠUp": 5157, + "Ġactual": 5158, + "Ġadapt": 5159, + "hene": 5160, + "Ġmotor": 5161, + "list": 5162, + "abit": 5163, + "Ind": 5164, + "otal": 5165, + "Ġneighbor": 5166, + "ĠPT": 5167, + "gener": 5168, + "Ġpossibility": 5169, + "ergies": 5170, + "Ġseems": 5171, + "ĠUS": 5172, + "Ġimm": 5173, + "Ġtypically": 5174, + "Ġsimulated": 5175, + "ĠSystems": 5176, + "ectiveness": 5177, + "rying": 5178, + "Ġkinase": 5179, + "Ġdecomposition": 5180, + "ateral": 5181, + "Ġrotation": 5182, + "pendix": 5183, + "enn": 5184, + "att": 5185, + "vate": 5186, + "Ġtargets": 5187, + "Ġsituation": 5188, + "Ġinvolve": 5189, + "Ġcreated": 5190, + "hesized": 5191, + "Ġalone": 5192, + "ci": 5193, + "ĠmL": 5194, + "Ġdivided": 5195, + "Ġbulk": 5196, + "oin": 5197, + "HC": 5198, + "Ġarm": 5199, + "LO": 5200, + "ills": 5201, + "Ġmedian": 5202, + "ham": 5203, + "imer": 5204, + "flu": 5205, + "Ġfiber": 5206, + "ĠSU": 5207, + "file": 5208, + "tivated": 5209, + "Ġradio": 5210, + "ĠNames": 5211, + "pe": 5212, + "Ġoste": 5213, + "Ġelim": 5214, + "Ġsuscepti": 5215, + "rehens": 5216, + "Ġdiscussion": 5217, + "ĠSep": 5218, + "Ġarchitecture": 5219, + "Ġdest": 5220, + "typ": 5221, + "rame": 5222, + "Ġpartition": 5223, + "Ġoccurred": 5224, + "Ġsizes": 5225, + "cles": 5226, + "Ġsched": 5227, + "Molecular": 5228, + "Ġκ": 5229, + "Ġinvas": 5230, + "cup": 5231, + "PCR": 5232, + "ĠSMILES": 5233, + "tially": 5234, + "oxide": 5235, + "ĠEd": 5236, + "Ġmanufact": 5237, + "ĠMaterial": 5238, + "Ġflat": 5239, + "Ġmutation": 5240, + "Ġintroduce": 5241, + "bound": 5242, + "Ġdisorders": 5243, + "regulated": 5244, + "ĠMor": 5245, + "Ġfalse": 5246, + "inger": 5247, + "ĠTR": 5248, + "Ġextrem": 5249, + "war": 5250, + "Ġsymbol": 5251, + "Ġanomal": 5252, + "ĠAR": 5253, + "Ġissues": 5254, + "Ġcoordinates": 5255, + "Ġreceptors": 5256, + "Ġprogression": 5257, + "ĠFl": 5258, + "ublic": 5259, + "Ġelectronic": 5260, + "Ġaspects": 5261, + "Ġdocument": 5262, + "flo": 5263, + "ĠPred": 5264, + "Ġgraphs": 5265, + "Ġtraditional": 5266, + "DM": 5267, + "Ġsafety": 5268, + "ĠDr": 5269, + "ĠSequ": 5270, + "Ġcomposite": 5271, + "ĠÎĽ": 5272, + "Ġresponsible": 5273, + "Ġgran": 5274, + "Ġintermediate": 5275, + "odium": 5276, + "posite": 5277, + "phase": 5278, + "dt": 5279, + "Ġweek": 5280, + "Ġdos": 5281, + "Ġstabil": 5282, + "LC": 5283, + "ĠKey": 5284, + "Ġvertices": 5285, + "Ġcomputer": 5286, + "ĠCanonical": 5287, + "Ġinvariant": 5288, + "emark": 5289, + "benz": 5290, + "Ġice": 5291, + "tile": 5292, + "zy": 5293, + "ĠOut": 5294, + "Ġmovement": 5295, + "Ġshif": 5296, + "leep": 5297, + "Ġdaily": 5298, + "Ġpositions": 5299, + "Ġhim": 5300, + "Ġcreate": 5301, + "Our": 5302, + "Ġresearc": 5303, + "Ġprogn": 5304, + "duct": 5305, + "Ġscreening": 5306, + "Ġchoose": 5307, + "process": 5308, + "mal": 5309, + "Ġlaboratory": 5310, + "Ġoperations": 5311, + "Ġtools": 5312, + "ologic": 5313, + "qquad": 5314, + "Ġcommonly": 5315, + "Ġvoid": 5316, + "Ġoccup": 5317, + "associated": 5318, + "Ġcorrelations": 5319, + "Ġcarcinoma": 5320, + "lin": 5321, + "Ġvideo": 5322, + "Ġheavy": 5323, + "Ġlargest": 5324, + "Ġmiddle": 5325, + "ĉĉĉĉ": 5326, + "ĠBas": 5327, + "asons": 5328, + "iding": 5329, + "Ġetc": 5330, + "ache": 5331, + "ĠEval": 5332, + "ira": 5333, + "romagnetic": 5334, + "Ġcovari": 5335, + "LI": 5336, + "Ġdele": 5337, + "Ġstra": 5338, + "amples": 5339, + "oder": 5340, + "Ġcategory": 5341, + "ĠInstit": 5342, + "Ġpolicy": 5343, + "Based": 5344, + "ibly": 5345, + "Ġdetermination": 5346, + "Ġrespir": 5347, + "otropic": 5348, + "Ġolder": 5349, + "ĠMal": 5350, + "Ġcytok": 5351, + "Ġdegrees": 5352, + "aut": 5353, + "illing": 5354, + "eting": 5355, + "Ġreduces": 5356, + "Ġideal": 5357, + "binding": 5358, + "ĠSpect": 5359, + "unit": 5360, + "Ġdiver": 5361, + "ĠWorld": 5362, + "Ġmarked": 5363, + "aly": 5364, + "Ġcomplexes": 5365, + "ĠSummary": 5366, + "Ġpropose": 5367, + "ĠAustr": 5368, + "Ġmaxim": 5369, + "Ġround": 5370, + "Ġinhibitor": 5371, + "Ġefficacy": 5372, + "actor": 5373, + "bur": 5374, + "Ġtransf": 5375, + "ĠGal": 5376, + "Ġproved": 5377, + "ĠDefined": 5378, + "At": 5379, + "Ġselect": 5380, + "Ġnanoparticles": 5381, + "Wh": 5382, + "ken": 5383, + "ĠSP": 5384, + "enge": 5385, + "Ġdelivery": 5386, + "Ġdisorder": 5387, + "ĠInChI": 5388, + "ĠComparison": 5389, + "ifying": 5390, + "ĠMechan": 5391, + "Ġconclude": 5392, + "Ġrepeated": 5393, + "ellow": 5394, + "ĠÃĢ": 5395, + "CI": 5396, + "ĠHz": 5397, + "analysis": 5398, + "Tr": 5399, + "ÃŃ": 5400, + "elihood": 5401, + "Ġexpand": 5402, + "ĠDevelopment": 5403, + "ĠState": 5404, + "Ġtet": 5405, + "ffic": 5406, + "Ġparent": 5407, + "Ġscenario": 5408, + "rs": 5409, + "ĠWhat": 5410, + "âī": 5411, + "Ġstimulation": 5412, + "ĠObs": 5413, + "zero": 5414, + "Ġmanner": 5415, + "ashed": 5416, + "ĠLog": 5417, + "Ġoxide": 5418, + "phosph": 5419, + "Ġmigration": 5420, + "Ġsubgroup": 5421, + "rosis": 5422, + "ipp": 5423, + "DR": 5424, + "dec": 5425, + "osomal": 5426, + "Ġsegment": 5427, + "ogenous": 5428, + "FP": 5429, + "hand": 5430, + "ĠSurface": 5431, + "itz": 5432, + "Ġcrystall": 5433, + "this": 5434, + "Ġbuilding": 5435, + "tag": 5436, + "Ġreducing": 5437, + "Ġuns": 5438, + "Ġrecomb": 5439, + "Ġcam": 5440, + "Ġlimits": 5441, + "ocardi": 5442, + "&&": 5443, + "Ġseparate": 5444, + "Ġsupplement": 5445, + "kele": 5446, + "Ġgrad": 5447, + "Ġissue": 5448, + "ĠQuantum": 5449, + "Ġcurrently": 5450, + "Ġquite": 5451, + "EP": 5452, + "Ġrules": 5453, + "Ġweights": 5454, + "uary": 5455, + "illi": 5456, + "Ġbecame": 5457, + "ó": 5458, + "Ġnormalized": 5459, + "ĠNetworks": 5460, + "erved": 5461, + "Ġstatistics": 5462, + "ĠTime": 5463, + "ĠUV": 5464, + "Ġcav": 5465, + "used": 5466, + "Ġfish": 5467, + "Ġmajority": 5468, + "ĠPe": 5469, + "Ġcohort": 5470, + "Ġsemi": 5471, + "Ġgame": 5472, + "monary": 5473, + "MM": 5474, + "oded": 5475, + "Ġvent": 5476, + "Ġauto": 5477, + "Ġabundance": 5478, + "nov": 5479, + "Ġasymptotic": 5480, + "Ġtreatments": 5481, + "uly": 5482, + "Ġconstraint": 5483, + "Ġbey": 5484, + "ĠSO": 5485, + "Ġstd": 5486, + "Ġdeveloping": 5487, + "ĠNot": 5488, + "Lemma": 5489, + "Ġapparent": 5490, + "Ġcircuit": 5491, + "From": 5492, + "ĠEuropean": 5493, + "Ġsolve": 5494, + "ĠÍij": 5495, + "ux": 5496, + "Ġbeyond": 5497, + "ept": 5498, + "Ġappe": 5499, + "requency": 5500, + "Ġvacu": 5501, + "ĠIndeed": 5502, + "ĠChemical": 5503, + "ĠUndefined": 5504, + "Note": 5505, + "Ġnull": 5506, + "Ġinverse": 5507, + "Ġnamely": 5508, + "Ġshear": 5509, + "mL": 5510, + "All": 5511, + "Rec": 5512, + "Ġgeneralized": 5513, + "ranes": 5514, + "ĠTest": 5515, + "iling": 5516, + "Ġfluorescence": 5517, + "ĠΣ": 5518, + "Ġindepend": 5519, + "diff": 5520, + "Ġproviding": 5521, + "phenyl": 5522, + "hing": 5523, + "Ġviral": 5524, + "ĠBecause": 5525, + "Ġintrac": 5526, + "ĠHig": 5527, + "Ġwant": 5528, + "Ġprinciple": 5529, + "anol": 5530, + "Ġha": 5531, + "ovascular": 5532, + "Ġformer": 5533, + "Ġestablish": 5534, + "Ġadvantage": 5535, + "III": 5536, + "Ġsequencing": 5537, + "Ġprocedures": 5538, + "tra": 5539, + "index": 5540, + "fe": 5541, + "Ġpi": 5542, + "Ġobvious": 5543, + "Ġregime": 5544, + "sur": 5545, + "Ġpresents": 5546, + "Ġdisplac": 5547, + "Ġdecl": 5548, + "ĠAppendix": 5549, + "Ġinteract": 5550, + "lands": 5551, + "inate": 5552, + "omorphic": 5553, + "Ġlowest": 5554, + "Ġartif": 5555, + "Ġinvolving": 5556, + "Ġcommerc": 5557, + "Ġdop": 5558, + "Ġconform": 5559, + "ĠIg": 5560, + "rolog": 5561, + "vised": 5562, + "Ġflo": 5563, + "Ġcardiac": 5564, + "pts": 5565, + "rig": 5566, + "Ġensure": 5567, + "Ġaccumulation": 5568, + "Ġentropy": 5569, + "Ġidea": 5570, + "perature": 5571, + "Ġquestions": 5572, + "ĠPR": 5573, + "Ġstatistically": 5574, + "dagger": 5575, + "Ġnitrogen": 5576, + "scr": 5577, + "ĠDiscussion": 5578, + "Ġreports": 5579, + "Ġpulse": 5580, + "Ġrequirements": 5581, + "Ġcomparing": 5582, + "quired": 5583, + "layer": 5584, + "Ġspectroscopy": 5585, + "vironments": 5586, + "Ġscaling": 5587, + "Ġexposed": 5588, + "MB": 5589, + "ξ": 5590, + "Ġhole": 5591, + "Ġá": 5592, + "Ġsimilarity": 5593, + "Ġvariants": 5594, + "body": 5595, + "Ġkeep": 5596, + "ĠCancer": 5597, + "edi": 5598, + "osomes": 5599, + "Ç«": 5600, + "Ad": 5601, + "âĪŀ": 5602, + "monic": 5603, + "ging": 5604, + "split": 5605, + "know": 5606, + "Ġrough": 5607, + "hematical": 5608, + "vision": 5609, + "Ġded": 5610, + "Ġcycles": 5611, + "Ġfamil": 5612, + "Ġadministration": 5613, + "etal": 5614, + "Ġcoron": 5615, + "Ġinfections": 5616, + "Ġmacroph": 5617, + "atics": 5618, + "Ġpredictions": 5619, + "isher": 5620, + "erent": 5621, + "reted": 5622, + "include": 5623, + "Ġclimate": 5624, + "sec": 5625, + "========": 5626, + "ĠMS": 5627, + "Ġcompe": 5628, + "ratic": 5629, + "lig": 5630, + "poses": 5631, + "Ġpolarization": 5632, + "llip": 5633, + "derived": 5634, + "Ġreleased": 5635, + "Ġconnection": 5636, + "lic": 5637, + "Ġcoli": 5638, + "Ġoutside": 5639, + "Ġabsolute": 5640, + "esian": 5641, + "ĠEnd": 5642, + "ĠOf": 5643, + "Ġidentical": 5644, + "Ġmodule": 5645, + "Ġmitochondrial": 5646, + "Ġadvanced": 5647, + "ingly": 5648, + "formance": 5649, + "Ġtoward": 5650, + "uding": 5651, + "ek": 5652, + "Ġmeaning": 5653, + "crib": 5654, + "ulator": 5655, + "FN": 5656, + "key": 5657, + "cons": 5658, + "Ġapplying": 5659, + "ishes": 5660, + "Ġmamm": 5661, + "Ġderivatives": 5662, + "Ġorientation": 5663, + "Ġstochastic": 5664, + "ĠAug": 5665, + "Ġrenal": 5666, + "ĠGreen": 5667, + "Ġcomplement": 5668, + "obl": 5669, + "pirical": 5670, + "orts": 5671, + "BM": 5672, + "Ġexcess": 5673, + "Ġmorphology": 5674, + "Ġsound": 5675, + "ifier": 5676, + "Ġimplications": 5677, + "ĠDesign": 5678, + "approx": 5679, + "prop": 5680, + "Ġcandidate": 5681, + "Ġdepos": 5682, + "Ġequip": 5683, + "ustain": 5684, + "inese": 5685, + "etry": 5686, + "Ġpotentially": 5687, + "Ġstraight": 5688, + "Ġcruc": 5689, + "iology": 5690, + "Ġkernel": 5691, + "Ġalcoh": 5692, + "idden": 5693, + "return": 5694, + "Ġcorrection": 5695, + "rot": 5696, + "Ġmicroscopy": 5697, + "Ġfoot": 5698, + "GL": 5699, + "ĠCells": 5700, + "irth": 5701, + "yg": 5702, + "ĠPath": 5703, + "outhern": 5704, + "ĠLong": 5705, + "Ġrevers": 5706, + "ε": 5707, + "arse": 5708, + "Ġcereb": 5709, + "isted": 5710, + "Ġpuls": 5711, + "Ġdisk": 5712, + "itud": 5713, + "Ġdu": 5714, + "Ġangular": 5715, + "chem": 5716, + "length": 5717, + "Ġexactly": 5718, + "roke": 5719, + "uth": 5720, + "Ġcond": 5721, + "insic": 5722, + "Ġrise": 5723, + "take": 5724, + "Ġtopological": 5725, + "Ġremark": 5726, + "ollary": 5727, + "Ġcer": 5728, + "TE": 5729, + "nment": 5730, + "Ġbuilt": 5731, + "Ġfre": 5732, + "Ġenergies": 5733, + "ecting": 5734, + "ĠTem": 5735, + "rared": 5736, + "ĠNow": 5737, + "charge": 5738, + "Ġlocations": 5739, + "Ġbalance": 5740, + "Ġla": 5741, + "Ġreached": 5742, + "lammatory": 5743, + "Ġfabric": 5744, + "ifications": 5745, + "Ġdiagnostic": 5746, + "Ġmutant": 5747, + "ĠNO": 5748, + "HD": 5749, + "ĠAB": 5750, + "Ġdiscrim": 5751, + "Ġprecip": 5752, + "ĠThree": 5753, + "Ġinser": 5754, + "Ġinfected": 5755, + "Ġconstants": 5756, + "Ω": 5757, + "negative": 5758, + "Ġconfidence": 5759, + "ĠPatients": 5760, + "ollowing": 5761, + "ads": 5762, + "Ġhypert": 5763, + "ĠInternational": 5764, + "Def": 5765, + "ariate": 5766, + "Ġintervals": 5767, + "Ġexercise": 5768, + "Ġeducation": 5769, + "Ġremoval": 5770, + "thern": 5771, + "ster": 5772, + "Ġinteger": 5773, + "ĠPA": 5774, + "Ġkid": 5775, + "Ġcategories": 5776, + "ĠGiven": 5777, + "Ġvascular": 5778, + "herence": 5779, + "mathscr": 5780, + "ĠRet": 5781, + "Ġinsulin": 5782, + "ticip": 5783, + "ĠCF": 5784, + "Ġlook": 5785, + "ymmetry": 5786, + "Ġforces": 5787, + "ĠPhysical": 5788, + "LS": 5789, + "care": 5790, + "Ġhouse": 5791, + "Ġinduce": 5792, + "Ġbelie": 5793, + "ria": 5794, + "ĠAssum": 5795, + "Ġcomputing": 5796, + "Ġbus": 5797, + "âĪİ": 5798, + "Ġpractical": 5799, + "train": 5800, + "TT": 5801, + "Ġplastic": 5802, + "ĠNor": 5803, + "Ġfeas": 5804, + "ĠHamiltonian": 5805, + "Ġtail": 5806, + "ĠZn": 5807, + "Ġinterpretation": 5808, + "ducing": 5809, + "Is": 5810, + "Ġexamine": 5811, + "ulates": 5812, + "Ġmatch": 5813, + "ĠÄ": 5814, + "ives": 5815, + "ameters": 5816, + "ĠμM": 5817, + "Ġexhibit": 5818, + "Ġnit": 5819, + "oto": 5820, + "ĠClinical": 5821, + "ervation": 5822, + "ĠAdditionally": 5823, + "arant": 5824, + "Ġelastic": 5825, + "DA": 5826, + "otopic": 5827, + "Ġactivated": 5828, + "Ġter": 5829, + "Ġconsequence": 5830, + "Ġendot": 5831, + "ophag": 5832, + "Ġcomparable": 5833, + "Ġdominant": 5834, + "η": 5835, + "Ġvalidation": 5836, + "Im": 5837, + "ĠÅ": 5838, + "Ġleaf": 5839, + "Ġfung": 5840, + "taining": 5841, + "Ġunivers": 5842, + "Ġphyl": 5843, + "Ġlibr": 5844, + "Ġextra": 5845, + "Ġprint": 5846, + "mediately": 5847, + "Ġmaximal": 5848, + "idae": 5849, + "Ġoral": 5850, + "bin": 5851, + "Ġpeptide": 5852, + "ĠMax": 5853, + "arp": 5854, + "Ġconclusion": 5855, + "Ġsatisfy": 5856, + "Ġanalyze": 5857, + "ois": 5858, + "Ġinfer": 5859, + "Ġdraw": 5860, + "Ġdepression": 5861, + "Ġmetall": 5862, + "Ġposterior": 5863, + "Ġpeaks": 5864, + "sol": 5865, + "Ġhorizontal": 5866, + "Ġlateral": 5867, + "ĠOR": 5868, + "NN": 5869, + "Ġemo": 5870, + "PV": 5871, + "TA": 5872, + "Ġincubated": 5873, + "Ġretrie": 5874, + "Ġhumans": 5875, + "Ġri": 5876, + "Ġsoci": 5877, + "onia": 5878, + "Ġinterven": 5879, + "Ġvarying": 5880, + "Ġsti": 5881, + "ĠImmun": 5882, + "Ġonset": 5883, + "Ġleaves": 5884, + "Ġotherwise": 5885, + "Ġblocks": 5886, + "Ġassigned": 5887, + "SCs": 5888, + "Ġbios": 5889, + "Ġmixing": 5890, + "ara": 5891, + "li": 5892, + "Ġdeformation": 5893, + "Ġcosts": 5894, + "Ġperipher": 5895, + "ĠTra": 5896, + "Ġatomic": 5897, + "Ġrandomly": 5898, + "Ġargument": 5899, + "Ġitems": 5900, + "Ġsuff": 5901, + "Ġprobably": 5902, + "ners": 5903, + "Ġinhibitors": 5904, + "Ġbeh": 5905, + "ĠDeep": 5906, + "Ġpig": 5907, + "ĠType": 5908, + "ĠMost": 5909, + "ura": 5910, + "itudinal": 5911, + "Ġderivative": 5912, + "Ġexplore": 5913, + "ĠInformation": 5914, + "Ġgrap": 5915, + "ĠÎĶ": 5916, + "Ġprogress": 5917, + "****************": 5918, + "Ġul": 5919, + "ARS": 5920, + "oral": 5921, + "ostic": 5922, + "Com": 5923, + "ĠExternal": 5924, + "ĠStatistical": 5925, + "ĠRam": 5926, + "ĠLo": 5927, + "Ġelectrical": 5928, + "long": 5929, + "Net": 5930, + "ENT": 5931, + "va": 5932, + "ä": 5933, + "urations": 5934, + "Ġdesired": 5935, + "iring": 5936, + "Ġphysics": 5937, + "Ġmasses": 5938, + "ki": 5939, + "Ġbands": 5940, + "Ġalk": 5941, + "ĠSimilarly": 5942, + "Ġsurround": 5943, + "Ġconvex": 5944, + "oster": 5945, + "Ġlinked": 5946, + "Ġfocused": 5947, + "Ġhot": 5948, + "Ġmatching": 5949, + "Ġoxidation": 5950, + "Ġanten": 5951, + "miss": 5952, + "Ġmental": 5953, + "ille": 5954, + "iciency": 5955, + "ĠLiu": 5956, + "Ġprobe": 5957, + "ĠEstim": 5958, + "Ġindices": 5959, + "che": 5960, + "ĠRob": 5961, + "Ġconv": 5962, + "ĠVer": 5963, + "apse": 5964, + "Si": 5965, + "phal": 5966, + "Ġlesions": 5967, + "Ġmolecule": 5968, + "Ġadi": 5969, + "Ġdate": 5970, + "Ġcomposed": 5971, + "Ġaud": 5972, + "structure": 5973, + "oton": 5974, + "infor": 5975, + "Ġclustering": 5976, + "acent": 5977, + "star": 5978, + "PO": 5979, + "ĠChinese": 5980, + "Ġspecifically": 5981, + "erential": 5982, + "Ġcapture": 5983, + "ĠLow": 5984, + "Ġfine": 5985, + "Ġfemales": 5986, + "ĠHow": 5987, + "Ġaer": 5988, + "vector": 5989, + "portun": 5990, + "forms": 5991, + "zo": 5992, + "Ġprecision": 5993, + "ypt": 5994, + "Ġminutes": 5995, + "κ": 5996, + "Ġoxidative": 5997, + "conn": 5998, + "ensus": 5999, + "Ġtrace": 6000, + "Ġconjug": 6001, + "Ġhighlight": 6002, + "ss": 6003, + "ĠExperimental": 6004, + "ĠThat": 6005, + "artment": 6006, + "MO": 6007, + "''": 6008, + "ometer": 6009, + "Ġstop": 6010, + "Ġrib": 6011, + "Ġouter": 6012, + "rh": 6013, + "ript": 6014, + "Ġfluctuations": 6015, + "obs": 6016, + "non": 6017, + "Ġquark": 6018, + "Ġð": 6019, + "ĠMac": 6020, + "Ġperiods": 6021, + "rolled": 6022, + "AV": 6023, + "ĠOc": 6024, + "ĠImage": 6025, + "ĠBel": 6026, + "Ġpropagation": 6027, + "ĠDon": 6028, + "www": 6029, + "glish": 6030, + "Ġexhibited": 6031, + "ogeneity": 6032, + "ĠBack": 6033, + "Ġactions": 6034, + "ski": 6035, + "ĠAmong": 6036, + "Ġbrief": 6037, + "riers": 6038, + "ĠNF": 6039, + "positive": 6040, + "sequently": 6041, + "ulence": 6042, + "Ġenvironments": 6043, + "Ġcurv": 6044, + "omics": 6045, + "Ġbit": 6046, + "Ġgel": 6047, + "Ġrepresentations": 6048, + "Ġaway": 6049, + "ĠField": 6050, + "obic": 6051, + "CG": 6052, + "Ġcomprehens": 6053, + "Ġhierarch": 6054, + "Ġinduces": 6055, + "BD": 6056, + "Ġhapp": 6057, + "Ġeight": 6058, + "Ġgravity": 6059, + "Ġadaptive": 6060, + "BL": 6061, + "genic": 6062, + "Ġinstruc": 6063, + "Ġanalytical": 6064, + "ĠOx": 6065, + "ĠCON": 6066, + "Ġsurgical": 6067, + "Ġdip": 6068, + "ato": 6069, + "Ġrandomized": 6070, + "Ġroles": 6071, + "dep": 6072, + "ĠâĪĩ": 6073, + "chang": 6074, + "Ġdispersion": 6075, + "Ġseparated": 6076, + "ĠOrgan": 6077, + "ĠVi": 6078, + "ĠJohn": 6079, + "Ġannot": 6080, + "Ġresource": 6081, + "energy": 6082, + "relation": 6083, + "mean": 6084, + "ĠBen": 6085, + "Ġconfirm": 6086, + "With": 6087, + "Ġinfinite": 6088, + "ĠScience": 6089, + "Ġsuccessfully": 6090, + "Ġlocalization": 6091, + "mode": 6092, + "https": 6093, + "gebras": 6094, + "idelines": 6095, + "Ġeffectiveness": 6096, + "hyd": 6097, + "Ġsaid": 6098, + "ico": 6099, + "Ġtransitions": 6100, + "eding": 6101, + "Ġprograms": 6102, + "Ġmobile": 6103, + "Ġimmediately": 6104, + "ectivity": 6105, + "ĠTherm": 6106, + "ogenetic": 6107, + "Ġseven": 6108, + "Ġemph": 6109, + "GE": 6110, + "neum": 6111, + "Ġfusion": 6112, + "limits": 6113, + "Ġcalcium": 6114, + "raf": 6115, + "minus": 6116, + "Ġtrap": 6117, + "Ġspecimens": 6118, + "ancing": 6119, + "ĠMarch": 6120, + "Ġten": 6121, + "Ġfamilies": 6122, + "ĠHD": 6123, + "isons": 6124, + "Ġpreparation": 6125, + "hold": 6126, + "ether": 6127, + "ĠVol": 6128, + "ĠDise": 6129, + "Ġrunning": 6130, + "Ġqualit": 6131, + "Ġeffectively": 6132, + "fficiently": 6133, + "BI": 6134, + "Ġdenoted": 6135, + "ĠEquation": 6136, + "Ġdemand": 6137, + "itory": 6138, + "aching": 6139, + "Ġsodium": 6140, + "Ġreproduc": 6141, + "cho": 6142, + "Ġbil": 6143, + "Pi": 6144, + "umb": 6145, + "Ġreconstruction": 6146, + "forward": 6147, + "One": 6148, + "Ġconversion": 6149, + "Ġformulation": 6150, + "Ġnearly": 6151, + "ĠLag": 6152, + "Str": 6153, + "terior": 6154, + "Ġoperating": 6155, + "andom": 6156, + "Ġmoving": 6157, + "ĠReview": 6158, + "////": 6159, + "nai": 6160, + "pp": 6161, + "otide": 6162, + "label": 6163, + "ococc": 6164, + "Ġnever": 6165, + "aker": 6166, + "Ġdigital": 6167, + "Bl": 6168, + "Un": 6169, + "Ġmember": 6170, + "sel": 6171, + "Ġpotenti": 6172, + "Ġcopy": 6173, + "Ġelectrons": 6174, + "chlor": 6175, + "annel": 6176, + "ylind": 6177, + "Ġmis": 6178, + "ĠSet": 6179, + "Ġnutri": 6180, + "Ġdescribes": 6181, + "Ġassumptions": 6182, + "Ġvirtual": 6183, + "Ġcoordinate": 6184, + "Ġvor": 6185, + "ĠArab": 6186, + "ĠImp": 6187, + "Ġdeposition": 6188, + "Ġinstit": 6189, + "Ġrepresentative": 6190, + "everal": 6191, + "Ġmillion": 6192, + "ĠMA": 6193, + "Ġmales": 6194, + "Ġcrucial": 6195, + "Ġcold": 6196, + "Ġloading": 6197, + "Ġtranslation": 6198, + "Ġstead": 6199, + "rays": 6200, + "Ġchallenge": 6201, + "activity": 6202, + "idal": 6203, + "uff": 6204, + "Ġseem": 6205, + "Ġnational": 6206, + "Ġfa": 6207, + "Ġminor": 6208, + "Ġundergo": 6209, + "cr": 6210, + "Ġcapt": 6211, + "ele": 6212, + "uple": 6213, + "ĠMg": 6214, + "lege": 6215, + "GR": 6216, + "Ġrig": 6217, + "Ġarri": 6218, + "Ġdetector": 6219, + "Ġstrict": 6220, + "Ġadhes": 6221, + "Ġsea": 6222, + "theless": 6223, + "Ġsleep": 6224, + "ĠCommun": 6225, + "Ġantioxid": 6226, + "Ġmarker": 6227, + "Ġflows": 6228, + "ancre": 6229, + "ĠJanuary": 6230, + "input": 6231, + "UP": 6232, + "Ġstored": 6233, + "ading": 6234, + "itively": 6235, + "Ġslope": 6236, + "Ġshell": 6237, + "Ġelevated": 6238, + "ilk": 6239, + "Ġfrequently": 6240, + "Ġball": 6241, + "urban": 6242, + "Ġml": 6243, + "usive": 6244, + "ĠAnt": 6245, + "amino": 6246, + "Sim": 6247, + "Ġphysiological": 6248, + "regulation": 6249, + "esity": 6250, + "Ġexplan": 6251, + "Ġaden": 6252, + "reme": 6253, + "Ġdiffer": 6254, + "Ġmodification": 6255, + "Ġirradi": 6256, + "He": 6257, + "acial": 6258, + "Ġsuppress": 6259, + "quis": 6260, + "Ġdry": 6261, + "erated": 6262, + "Ġprojection": 6263, + "Ġpool": 6264, + "plete": 6265, + "Ġdirections": 6266, + "Ġchanged": 6267, + "ĠIts": 6268, + "Ġster": 6269, + "Ġradial": 6270, + "Ġgr": 6271, + "Ġperiodic": 6272, + "Ġbin": 6273, + "Ġpip": 6274, + "men": 6275, + "then": 6276, + "pc": 6277, + "amily": 6278, + "ĠDM": 6279, + "Ġsediment": 6280, + "mi": 6281, + "Ġclosely": 6282, + "Ġrepair": 6283, + "Ġrespiratory": 6284, + "Ġhorm": 6285, + "Ans": 6286, + "dr": 6287, + "ls": 6288, + "Ġhomogeneous": 6289, + "etric": 6290, + "DS": 6291, + "Ġresidues": 6292, + "ĠValue": 6293, + "Fs": 6294, + "Ġwhy": 6295, + "Sp": 6296, + "Ġca": 6297, + "Ġnarrow": 6298, + "gent": 6299, + "Ġbr": 6300, + "Ġquasi": 6301, + "Ġpict": 6302, + "mo": 6303, + "Ġatom": 6304, + "Ġhabit": 6305, + "Ġlimitations": 6306, + "conduc": 6307, + "Ġshock": 6308, + "ceptor": 6309, + "ĠDetection": 6310, + "Sh": 6311, + "ube": 6312, + "Ġellip": 6313, + "UR": 6314, + "Ġstaining": 6315, + "Ġrapidly": 6316, + "ĠBur": 6317, + "ĠBro": 6318, + "Ġuptake": 6319, + "Ġchallenges": 6320, + "SN": 6321, + "Ġanis": 6322, + "Ġbounds": 6323, + "step": 6324, + "omeric": 6325, + "tention": 6326, + "ĠEvaluation": 6327, + "Ġrecommend": 6328, + "Me": 6329, + "Ġmoderate": 6330, + "elled": 6331, + "Ġtit": 6332, + "ĠYang": 6333, + "Ġpharmac": 6334, + "inflammatory": 6335, + "ĠJune": 6336, + "Ġsensors": 6337, + "aired": 6338, + "Ġapproximate": 6339, + "SV": 6340, + "Ġbund": 6341, + "rc": 6342, + "oman": 6343, + "Ġvisible": 6344, + "Ġmeasuring": 6345, + "ogonal": 6346, + "ĠFourier": 6347, + "Ġtheories": 6348, + "Ġprofession": 6349, + "tained": 6350, + "atas": 6351, + "ĠInterest": 6352, + "param": 6353, + "ĠStructure": 6354, + "Ġliving": 6355, + "Data": 6356, + "ĠSM": 6357, + "Ġnet": 6358, + "Ġsimultaneously": 6359, + "continu": 6360, + "Ġshor": 6361, + "########": 6362, + "Ġdecreasing": 6363, + "Ġreferred": 6364, + "gg": 6365, + "Thus": 6366, + "Ġdro": 6367, + "pril": 6368, + "ĠPers": 6369, + "Ġencoding": 6370, + "Ġarc": 6371, + "Ġregulatory": 6372, + "Ġtrained": 6373, + "cepts": 6374, + "Ġrout": 6375, + "lys": 6376, + "Par": 6377, + "ĠUl": 6378, + "ĠGraph": 6379, + "âĪĤ": 6380, + "Ġirre": 6381, + "oidal": 6382, + "Ġexceed": 6383, + "Ġmostly": 6384, + "ĠPat": 6385, + "aternal": 6386, + "Ġer": 6387, + "Ġcoverage": 6388, + "FS": 6389, + "ĠRot": 6390, + "Ġclassified": 6391, + "Ġexcitation": 6392, + "Ġconductivity": 6393, + "Ġcommercial": 6394, + "ĠDel": 6395, + "ĠPolar": 6396, + "HR": 6397, + "Ġtraffic": 6398, + "zing": 6399, + "Ġsettings": 6400, + "Ġinclusion": 6401, + "Answer": 6402, + "Ġvit": 6403, + "vitational": 6404, + "Ġbind": 6405, + "Ġoc": 6406, + "ĠWestern": 6407, + "Ġprosp": 6408, + "Ġnorth": 6409, + "itch": 6410, + "ĠRiver": 6411, + "Ġvehicle": 6412, + "Ġlikelihood": 6413, + "LD": 6414, + "Ġinsp": 6415, + "âĪĨ": 6416, + "Ġleuk": 6417, + "ĠBre": 6418, + "Ġsynthetic": 6419, + "ĠGermany": 6420, + "ĠTheir": 6421, + "target": 6422, + "ĠEnglish": 6423, + "Ġnotation": 6424, + "ĠATP": 6425, + "ĠModels": 6426, + "Ġabnormal": 6427, + "ĠConclusions": 6428, + "Ġoccurrence": 6429, + "Ġmicrobi": 6430, + "ĠWar": 6431, + "tember": 6432, + "Ġlocally": 6433, + "born": 6434, + "Ġbarrier": 6435, + "Ġexpressions": 6436, + "oval": 6437, + "Ġflav": 6438, + "emble": 6439, + "Ġdynamical": 6440, + "Ġphoton": 6441, + "apped": 6442, + "Ġglut": 6443, + "Ġkinetic": 6444, + "Ġalcohol": 6445, + "Ġtransplant": 6446, + "LP": 6447, + "Ġdefault": 6448, + "Ġopportun": 6449, + "args": 6450, + "ĠDav": 6451, + "Ġfront": 6452, + "hom": 6453, + "Ġways": 6454, + "ĠAssociation": 6455, + "Ġkidney": 6456, + "Ġproportional": 6457, + "When": 6458, + "Ġepithelial": 6459, + "Ġfresh": 6460, + "Ġrecall": 6461, + "Ġenzymes": 6462, + "br": 6463, + "Ġmalign": 6464, + "textrm": 6465, + "ĠUse": 6466, + "Now": 6467, + "ĠLie": 6468, + "Ġimpair": 6469, + "Ġguarant": 6470, + "Ġinver": 6471, + "Ġtranscript": 6472, + "Ġsustain": 6473, + "Ġactually": 6474, + "alities": 6475, + "ĠMic": 6476, + "ĠIC": 6477, + "ĠMeasure": 6478, + "Ġ": 6479, + "Ġdensities": 6480, + "Ġgalaxy": 6481, + "Ġsufficiently": 6482, + "Ġorbit": 6483, + "ford": 6484, + "Ġpartially": 6485, + "ĠPy": 6486, + "Ġreverse": 6487, + "Ġsurve": 6488, + "ĠWork": 6489, + "Ġask": 6490, + "However": 6491, + "Ġsitu": 6492, + "Ġvacuum": 6493, + "tober": 6494, + "Ġspac": 6495, + "anth": 6496, + "Or": 6497, + "ags": 6498, + "Ġbig": 6499, + "herical": 6500, + "erge": 6501, + "ellite": 6502, + "Ġinvolves": 6503, + "ĠVis": 6504, + "Ġsummary": 6505, + "ĠSupplementary": 6506, + "ĠColl": 6507, + "Ġadjacent": 6508, + "ontaneous": 6509, + "abs": 6510, + "Ġresearchers": 6511, + "ka": 6512, + "Ġintern": 6513, + "Ġmonth": 6514, + "ĠNeural": 6515, + "apor": 6516, + "ĠNan": 6517, + "Ġstri": 6518, + "EE": 6519, + "Ġconsisting": 6520, + "Ġupdate": 6521, + "Ġphoto": 6522, + "Val": 6523, + "sens": 6524, + "Ġveget": 6525, + "BR": 6526, + "Ġcoinc": 6527, + "ĠJuly": 6528, + "tility": 6529, + "ĠExpression": 6530, + "Ġtopology": 6531, + "Ġgrowing": 6532, + "aptic": 6533, + "uced": 6534, + "Ġperipheral": 6535, + "enes": 6536, + "Ġplots": 6537, + "Ġexplo": 6538, + "Ġwor": 6539, + "ba": 6540, + "atitis": 6541, + "ief": 6542, + "wave": 6543, + "Ġprotection": 6544, + "Ġdefects": 6545, + "Ġadsorption": 6546, + "Ġshared": 6547, + "Ġstellar": 6548, + "ĠBa": 6549, + "ĠEnergy": 6550, + "queous": 6551, + "ĠAugust": 6552, + "Ġlys": 6553, + "Ġplus": 6554, + "irel": 6555, + "ĠGP": 6556, + "ĠNeu": 6557, + "dist": 6558, + "gers": 6559, + "ifer": 6560, + "isp": 6561, + "Ġstrat": 6562, + "ione": 6563, + "ĠMaterials": 6564, + "Ġln": 6565, + "Ġpulmonary": 6566, + "ened": 6567, + "plan": 6568, + "Mod": 6569, + "Ġorganization": 6570, + "Ġrelaxation": 6571, + "Ġcortex": 6572, + "Ġmodulation": 6573, + "ogl": 6574, + "shift": 6575, + "Ġsecurity": 6576, + "Ġfatty": 6577, + "Ġms": 6578, + "local": 6579, + "ergic": 6580, + "Ġinterference": 6581, + "inson": 6582, + "cf": 6583, + "Ġreasons": 6584, + "pred": 6585, + "Ġinterventions": 6586, + "Ġjo": 6587, + "ĠID": 6588, + "ĠArea": 6589, + "ĠHa": 6590, + "uits": 6591, + "output": 6592, + "Le": 6593, + "ycl": 6594, + "inted": 6595, + "Ġnano": 6596, + "NC": 6597, + "ĠCap": 6598, + "Ġchanging": 6599, + "Ġcust": 6600, + "Ġappeared": 6601, + "Ġgrown": 6602, + "ĠUK": 6603, + "Ġradical": 6604, + "ĠPot": 6605, + "ĠProgram": 6606, + "ĠSR": 6607, + "Ġshap": 6608, + "oscop": 6609, + "ĠChang": 6610, + "Ġquantity": 6611, + "ĠTaxon": 6612, + "idation": 6613, + "Ġadding": 6614, + "ĠLee": 6615, + "Ġamounts": 6616, + "Ġdespite": 6617, + "Ġremained": 6618, + "Ġscenarios": 6619, + "lets": 6620, + "oming": 6621, + "Ġcurvature": 6622, + "Ġdimensional": 6623, + "Ġpromising": 6624, + "ĠFil": 6625, + "string": 6626, + "Ġattributed": 6627, + "ymer": 6628, + "Ġneighb": 6629, + "Ġinputs": 6630, + "Ġmagnet": 6631, + "Ġtrees": 6632, + "Ġenter": 6633, + "ruit": 6634, + "stable": 6635, + "toplas": 6636, + "Ġmessage": 6637, + "rophic": 6638, + "Ġisolates": 6639, + "tz": 6640, + "Ġdisplayed": 6641, + "HA": 6642, + "ocl": 6643, + "Ġderive": 6644, + "Ġsynchron": 6645, + "QU": 6646, + "Ãŀ": 6647, + "Ġexamination": 6648, + "Ġdeb": 6649, + "Ġdefin": 6650, + "Ġfault": 6651, + "Ġsteady": 6652, + "Ġphenotype": 6653, + "Ġperspective": 6654, + "Ġstatement": 6655, + "df": 6656, + "void": 6657, + "Ġpromote": 6658, + "illary": 6659, + "ĠEth": 6660, + "Ġwalk": 6661, + "Ġrepresenting": 6662, + "Ġgenomic": 6663, + "ĠGr": 6664, + "shape": 6665, + "ĠPet": 6666, + "ĠLocal": 6667, + "plicity": 6668, + "ĠProblem": 6669, + "GS": 6670, + "Ġcompleted": 6671, + "inking": 6672, + "Ġreads": 6673, + "Ġinde": 6674, + "ceived": 6675, + "ĠPL": 6676, + "ĠMean": 6677, + "ĠSchool": 6678, + "Ġbiomark": 6679, + "ireless": 6680, + "cut": 6681, + "osing": 6682, + "nel": 6683, + "ĠApril": 6684, + "ĠBal": 6685, + "Ġadopted": 6686, + "Ġcomplications": 6687, + "Ġassembly": 6688, + "fort": 6689, + "har": 6690, + "Ġadoles": 6691, + "Ġanswer": 6692, + "Ġcommunities": 6693, + "ĠInstitute": 6694, + "Ġvariant": 6695, + "Finally": 6696, + "mitte": 6697, + "Ġrestricted": 6698, + "Ġmanip": 6699, + "aters": 6700, + "EX": 6701, + "Ġdust": 6702, + "Ġsupply": 6703, + "Ġperme": 6704, + "Ġreliable": 6705, + "ĠResp": 6706, + "Ġsubt": 6707, + "oks": 6708, + "Ġpoll": 6709, + "Ġcanc": 6710, + "ĠUnit": 6711, + "Ġendothelial": 6712, + "dy": 6713, + "ĠBlack": 6714, + "Ġempirical": 6715, + "Ġport": 6716, + "opy": 6717, + "Ġinitially": 6718, + "Ġcondens": 6719, + "Ġeye": 6720, + "Ġlisted": 6721, + "urrence": 6722, + "Ġreplaced": 6723, + "Ġselective": 6724, + "Ġdistances": 6725, + "Ġparas": 6726, + "ĠPost": 6727, + "ĠSeptember": 6728, + "Ġmissing": 6729, + "verex": 6730, + "Er": 6731, + "Ġthought": 6732, + "thal": 6733, + "Ġchromat": 6734, + "Ġbenefit": 6735, + "rames": 6736, + "ĠSuppose": 6737, + "Ġsubs": 6738, + "Ġangi": 6739, + "ori": 6740, + "Ġreplic": 6741, + "Ġschemes": 6742, + "pre": 6743, + "plane": 6744, + "Ġsouth": 6745, + "ager": 6746, + "Ġbeginning": 6747, + "vents": 6748, + "onent": 6749, + "iples": 6750, + "ĠHer": 6751, + "Ġspectrom": 6752, + "Ġdense": 6753, + "Ġtook": 6754, + "iverse": 6755, + "Ġdisturb": 6756, + "pass": 6757, + "Ġillustrated": 6758, + "Ġreveals": 6759, + "ama": 6760, + "Ġreflec": 6761, + "Ġallowing": 6762, + "Ġexponential": 6763, + "oustic": 6764, + "subseteq": 6765, + "Ġsn": 6766, + "Ġurban": 6767, + "Ġextend": 6768, + "Ġassays": 6769, + "rice": 6770, + "CoV": 6771, + "quisition": 6772, + "rine": 6773, + "ĠIntegr": 6774, + "fil": 6775, + "VD": 6776, + "Ġfibro": 6777, + "Ġcompens": 6778, + "ĠImpro": 6779, + "ĠĠĠĠĠĠĠĠĠĠ": 6780, + "ĠGR": 6781, + "ÏĪ": 6782, + "Ġbasal": 6783, + "Ġolig": 6784, + "HT": 6785, + "Ġvess": 6786, + "uzzy": 6787, + "Ġpossibly": 6788, + "Ġtolerance": 6789, + "Theta": 6790, + "Ġviol": 6791, + "uclear": 6792, + "ĠLim": 6793, + "gel": 6794, + "Ġmetrics": 6795, + "ĠMus": 6796, + "amination": 6797, + "Ġelectrode": 6798, + "Ġpersonal": 6799, + "Ġcooling": 6800, + "Ġacquired": 6801, + "ĠFunction": 6802, + "ows": 6803, + "olester": 6804, + "DP": 6805, + "Ġreliability": 6806, + "Ġmuc": 6807, + "ĠOctober": 6808, + "Ġgold": 6809, + "ca": 6810, + "Ġcul": 6811, + "fit": 6812, + "Ġlem": 6813, + "Ġexcit": 6814, + "Ġnucleus": 6815, + "iation": 6816, + "Ġpregnancy": 6817, + "Ġsynthesized": 6818, + "hemistry": 6819, + "Ġmembranes": 6820, + "vert": 6821, + "ĠKim": 6822, + "tenance": 6823, + "Ġquantities": 6824, + "Ġeconomic": 6825, + "Ġbenefits": 6826, + "Ġcylind": 6827, + "pler": 6828, + "ĠLarge": 6829, + "Ġengineering": 6830, + "ĠEp": 6831, + "Ġcoating": 6832, + "ativ": 6833, + "Ġconduct": 6834, + "Ġabsorb": 6835, + "ĠDecember": 6836, + "Ġopposite": 6837, + "ĠGlobal": 6838, + "Ġlif": 6839, + "ĠDue": 6840, + "Ġintake": 6841, + "odynamic": 6842, + "TM": 6843, + "Ġfed": 6844, + "Ġspecified": 6845, + "Ġgeometric": 6846, + "Ġrespective": 6847, + "Ġbirth": 6848, + "ĠCompound": 6849, + "Ġstarted": 6850, + "Ġmother": 6851, + "arr": 6852, + "Ġprimarily": 6853, + "Ġparen": 6854, + "Ġtube": 6855, + "Ġinters": 6856, + "Ġgraphene": 6857, + "itial": 6858, + "ously": 6859, + "Ġcardiovascular": 6860, + "ĠeV": 6861, + "Ġheating": 6862, + "Ġmathematical": 6863, + "Ġindependently": 6864, + "BA": 6865, + "Ġaffects": 6866, + "umor": 6867, + "ĠMP": 6868, + "ĠDem": 6869, + "ĠWest": 6870, + "ĠDom": 6871, + "itter": 6872, + "Ġdisrup": 6873, + "oped": 6874, + "Ġphenomenon": 6875, + "Ġlumin": 6876, + "Ac": 6877, + "Ġprefer": 6878, + "omers": 6879, + "Ġgender": 6880, + "ĠGL": 6881, + "FC": 6882, + "Ġindeed": 6883, + "Ġrational": 6884, + "ĠSC": 6885, + "Ġprincipal": 6886, + "Ġperfect": 6887, + "Ġintroduction": 6888, + "tes": 6889, + "Ġpiec": 6890, + "Ġcity": 6891, + "Ġpopular": 6892, + "Ġcoding": 6893, + "cler": 6894, + "ague": 6895, + "ĠHR": 6896, + "Ġtracking": 6897, + "ker": 6898, + "Ġphosphorylation": 6899, + "Ġpaths": 6900, + "Ġsolving": 6901, + "Ġdy": 6902, + "Ġplayed": 6903, + "Ġprecise": 6904, + "ĠSl": 6905, + "ĠSem": 6906, + "Ġgenerating": 6907, + "ĠSun": 6908, + "Ġcriterion": 6909, + "Ġbranch": 6910, + "Ġζ": 6911, + "tish": 6912, + "Se": 6913, + "Ġantigen": 6914, + "Ġcalibration": 6915, + "Es": 6916, + "ĠItal": 6917, + "Ġmassive": 6918, + "En": 6919, + "No": 6920, + "YP": 6921, + "ya": 6922, + "Ġsatisfying": 6923, + "Ġquick": 6924, + "HO": 6925, + "Ġbehaviors": 6926, + "icrobial": 6927, + "Ġamb": 6928, + "Ġproton": 6929, + "SL": 6930, + "Ġusual": 6931, + "rows": 6932, + "ench": 6933, + "UC": 6934, + "Ġweighted": 6935, + "Ġrecords": 6936, + "ĠAC": 6937, + "GT": 6938, + "inn": 6939, + "Ġeq": 6940, + "ĠWil": 6941, + "yroid": 6942, + "Ġsetup": 6943, + "IA": 6944, + "press": 6945, + "isely": 6946, + "Ġentry": 6947, + "%%": 6948, + "ĠSil": 6949, + "east": 6950, + "ĠEvolution": 6951, + "ĠRandom": 6952, + "Ġcavity": 6953, + "Ġnamed": 6954, + "knowled": 6955, + "mber": 6956, + "uestion": 6957, + "ĠâĪ©": 6958, + "gi": 6959, + "Ġdetermining": 6960, + "tin": 6961, + "Ġgenus": 6962, + "Ġtoxicity": 6963, + "ocyt": 6964, + "Ġperturbation": 6965, + "rought": 6966, + "ĠBri": 6967, + "Ġcarb": 6968, + "ĠGra": 6969, + "ĠFlu": 6970, + "uns": 6971, + "Ġdriven": 6972, + "Ġbatch": 6973, + "rif": 6974, + "Pl": 6975, + "Ġdisplacement": 6976, + "ĠCL": 6977, + "Ġdepic": 6978, + "Ġpredictive": 6979, + "Int": 6980, + "hydroxy": 6981, + "tid": 6982, + "dri": 6983, + "Ġpancre": 6984, + "Ġdiagonal": 6985, + "Ġseverity": 6986, + "Ġlongitudinal": 6987, + "ĠED": 6988, + "atible": 6989, + "dir": 6990, + "ĠAnother": 6991, + "ĠHel": 6992, + "van": 6993, + "Ġpneum": 6994, + "Ġspecificity": 6995, + "squ": 6996, + "Ġign": 6997, + "Ġbed": 6998, + "ĠWT": 6999, + "awa": 7000, + "ester": 7001, + "Ġkg": 7002, + "Ġcompression": 7003, + "evertheless": 7004, + "Ġmask": 7005, + "-----------": 7006, + "Ġtens": 7007, + "rowth": 7008, + "ĠGo": 7009, + "Ġfaster": 7010, + "Ġcanonical": 7011, + "Ġdetermin": 7012, + "ustrial": 7013, + "ĠEarth": 7014, + "while": 7015, + "ournal": 7016, + "Ġcountry": 7017, + "Ġferm": 7018, + "rist": 7019, + "Ġproxim": 7020, + "Ġmicrobial": 7021, + "Ġextensive": 7022, + "Ġcham": 7023, + "Ġ§": 7024, + "such": 7025, + "went": 7026, + "Ġlar": 7027, + "Using": 7028, + "ĠPM": 7029, + "Ġoffset": 7030, + "ĠPI": 7031, + "ĠBayesian": 7032, + "HS": 7033, + "ĠAfrica": 7034, + "Ġsusceptibility": 7035, + "ĠâĬĤ": 7036, + "ococcus": 7037, + "ĠDir": 7038, + "Ġbos": 7039, + "Ġdysfunction": 7040, + "ovember": 7041, + "Ġunderst": 7042, + "Ġlargely": 7043, + "ĠCM": 7044, + "Ġmaintained": 7045, + "Ġpossess": 7046, + "Ġexcluded": 7047, + "ensis": 7048, + "ĠDC": 7049, + "opsis": 7050, + "Ġtorch": 7051, + "idine": 7052, + "Ġforest": 7053, + "ĠExact": 7054, + "ĠStudies": 7055, + "iffiff": 7056, + "ĠCam": 7057, + "angular": 7058, + "Ġremove": 7059, + "oir": 7060, + "ava": 7061, + "ida": 7062, + "Ġmant": 7063, + "Log": 7064, + "Ġranging": 7065, + "rog": 7066, + "Ġchains": 7067, + "ĠÇ«": 7068, + "ĠCase": 7069, + "ĠAP": 7070, + "points": 7071, + "Ġtargeting": 7072, + "Ġscience": 7073, + "Ġepis": 7074, + "ĠSoci": 7075, + "Ġphysic": 7076, + "Ġpromoter": 7077, + "ĠEarly": 7078, + "estic": 7079, + "tives": 7080, + "Ġassuming": 7081, + "ĠMi": 7082, + "Ġlemma": 7083, + "Ġconfigurations": 7084, + "alia": 7085, + "Ġpay": 7086, + "rino": 7087, + "eb": 7088, + "Ġvaried": 7089, + "ounted": 7090, + "Ġinterview": 7091, + "ĠGeV": 7092, + "OM": 7093, + "ognition": 7094, + "Ġenhancement": 7095, + "ĠMach": 7096, + "plies": 7097, + "Ob": 7098, + "setminus": 7099, + "Ġintrinsic": 7100, + "Ġcomparisons": 7101, + "bold": 7102, + "xiety": 7103, + "Ġstroke": 7104, + "GB": 7105, + "ancial": 7106, + "stead": 7107, + "Ġrock": 7108, + "thon": 7109, + "ĠCurrent": 7110, + "cat": 7111, + "Ġguidelines": 7112, + "cycl": 7113, + "Ġintracellular": 7114, + "oney": 7115, + "ko": 7116, + "Ġdirected": 7117, + "ripts": 7118, + "Ġtravel": 7119, + "Ġlens": 7120, + "idi": 7121, + "ĠAssess": 7122, + "Ġdx": 7123, + "ĠPos": 7124, + "Ġmethodology": 7125, + "Ġpredom": 7126, + "defined": 7127, + "ĠPop": 7128, + "Ġgovernment": 7129, + "ellig": 7130, + "phyl": 7131, + "oli": 7132, + "ropical": 7133, + "Ġembedded": 7134, + "edom": 7135, + "cribed": 7136, + "ĠDisease": 7137, + "Ġmediated": 7138, + "Ġcircular": 7139, + "ĠTopological": 7140, + "Ġearth": 7141, + "ritis": 7142, + "gal": 7143, + "mass": 7144, + "Ġcomprehensive": 7145, + "ĠAir": 7146, + "Ġnerve": 7147, + "Ġimplant": 7148, + "Ġextremely": 7149, + "ĠSE": 7150, + "Ġmarket": 7151, + "Ġconserved": 7152, + "embrane": 7153, + "Ġschedul": 7154, + "Ġruns": 7155, + "Ph": 7156, + "Ġtechnical": 7157, + "TL": 7158, + "Ġregional": 7159, + "Ġgerm": 7160, + "ĠProt": 7161, + "Ġbright": 7162, + "Ġartery": 7163, + "Ġmacrophages": 7164, + "mittee": 7165, + "ĠSingle": 7166, + "Ġcome": 7167, + "wa": 7168, + "acchar": 7169, + "plet": 7170, + "Ġsensing": 7171, + "rosp": 7172, + "atom": 7173, + "Ġcompr": 7174, + "ĠLu": 7175, + "Ġavailability": 7176, + "prot": 7177, + "Ġfitting": 7178, + "selves": 7179, + "ĠPrim": 7180, + "rew": 7181, + "Ġwaste": 7182, + "ĠKing": 7183, + "pot": 7184, + "Ġinstrument": 7185, + "ĠYork": 7186, + "AF": 7187, + "antial": 7188, + "standing": 7189, + "Ġplanning": 7190, + "uster": 7191, + "ĠâĨ": 7192, + "NT": 7193, + "icular": 7194, + "Ġmelan": 7195, + "Ġexcell": 7196, + "iller": 7197, + "ĠLD": 7198, + "info": 7199, + "Ġshare": 7200, + "vas": 7201, + "Ġlum": 7202, + "Ġaqueous": 7203, + "Ġquery": 7204, + "Ġmag": 7205, + "ulture": 7206, + "ĠBer": 7207, + "Ġoffer": 7208, + "ĠNMR": 7209, + "aceae": 7210, + "Ġmodern": 7211, + "Ġcircum": 7212, + "Ġcultures": 7213, + "Ġdog": 7214, + "Ġcir": 7215, + "Ġpoli": 7216, + "Ġchemotherapy": 7217, + "Ġplates": 7218, + "Ġrestriction": 7219, + "stack": 7220, + "ĠFlow": 7221, + "ĠBu": 7222, + "ĠCenter": 7223, + "Ġproceed": 7224, + "timicrobial": 7225, + "she": 7226, + "Ġthereby": 7227, + "Ġknock": 7228, + "Ġdiverse": 7229, + "ustry": 7230, + "Ġstated": 7231, + "ĠHol": 7232, + "More": 7233, + "Ġconservation": 7234, + "Ġprevention": 7235, + "norm": 7236, + "Ġpal": 7237, + "ĠCalc": 7238, + "Ġclean": 7239, + "ĠPlas": 7240, + "```": 7241, + "perp": 7242, + "prod": 7243, + "Ġâī¡": 7244, + "porter": 7245, + "Ġtransient": 7246, + "asp": 7247, + "Ġtargeted": 7248, + "ĠPri": 7249, + "Supplementary": 7250, + "ĠTreatment": 7251, + "zen": 7252, + "ĠMart": 7253, + "ĠFerm": 7254, + "uscript": 7255, + "ĠSynthesis": 7256, + "Ġcombinations": 7257, + "ULL": 7258, + "Ġweb": 7259, + "Ġthrom": 7260, + "Ġexplicitly": 7261, + "anks": 7262, + "Ġadaptation": 7263, + "ĠSequence": 7264, + "Ġacts": 7265, + "Ġranges": 7266, + "fs": 7267, + "bru": 7268, + "Ġsystemic": 7269, + "Ġsteel": 7270, + "Ġprivate": 7271, + "Ġobesity": 7272, + "ĠPart": 7273, + "mented": 7274, + "break": 7275, + "ERT": 7276, + "Ġfibers": 7277, + "Ġiso": 7278, + "Ġtransverse": 7279, + "CTION": 7280, + "ĠRi": 7281, + "itin": 7282, + "ĠRepresent": 7283, + "ophys": 7284, + "Ġcoast": 7285, + "Ġalignment": 7286, + "ACT": 7287, + "esides": 7288, + "open": 7289, + "gly": 7290, + "Ġsalt": 7291, + "unced": 7292, + "iaz": 7293, + "Ġcosm": 7294, + "Ġangles": 7295, + "ĠâĢł": 7296, + "ĠIdentification": 7297, + "hex": 7298, + "ĠHall": 7299, + "Ġhepat": 7300, + "Ġsegments": 7301, + "ĠPhase": 7302, + "ĠLand": 7303, + "forming": 7304, + "hbox": 7305, + "ications": 7306, + "Ġsubsequently": 7307, + "ĠCur": 7308, + "Ġlabels": 7309, + "vidence": 7310, + "uality": 7311, + "Ġheld": 7312, + "emann": 7313, + "Ġcamera": 7314, + "cing": 7315, + "ubic": 7316, + "ĠSARS": 7317, + "ulatory": 7318, + "keletal": 7319, + "ĠInflu": 7320, + "ĠIndia": 7321, + "amic": 7322, + "Ġsand": 7323, + "Ġcomes": 7324, + "Ġassociations": 7325, + "Ġcharged": 7326, + "Ġsper": 7327, + "oprotein": 7328, + "iii": 7329, + "odal": 7330, + "Ġboundaries": 7331, + "tization": 7332, + "ĠHeavy": 7333, + "ĠReal": 7334, + "ĠAF": 7335, + "Ġcontroller": 7336, + "Ġantioxidant": 7337, + "Ġbars": 7338, + "Ġwet": 7339, + "ener": 7340, + "ĠComplexity": 7341, + "Ġstack": 7342, + "Therefore": 7343, + "Ġreplication": 7344, + "Ġappearance": 7345, + "Ġtrajectory": 7346, + "Ġunderstood": 7347, + "Ġdot": 7348, + "Ġimag": 7349, + "Ġscanning": 7350, + "Ti": 7351, + "ruct": 7352, + "ĠLy": 7353, + "Ġspontaneous": 7354, + "lat": 7355, + "omon": 7356, + "Ġroots": 7357, + "Ġlive": 7358, + "Ġfinally": 7359, + "¿½": 7360, + "Ġapproved": 7361, + "ĠApplications": 7362, + "ĠPan": 7363, + "Ġlost": 7364, + "Ġsatisfied": 7365, + "Ġgamma": 7366, + "ional": 7367, + "Ġimproving": 7368, + "Ġmanifold": 7369, + "Ġcodes": 7370, + "bb": 7371, + "ĠNovember": 7372, + "Ġrich": 7373, + "NP": 7374, + "ĠEle": 7375, + "SB": 7376, + "Ġdeal": 7377, + "Ġoptions": 7378, + "Ġcultured": 7379, + "Ġvul": 7380, + ">>": 7381, + "arithm": 7382, + "oys": 7383, + "These": 7384, + "ĠDeterm": 7385, + "Ġquadratic": 7386, + "ĠComb": 7387, + "isson": 7388, + "ĠPerformance": 7389, + "Ġexception": 7390, + "Ġnuclei": 7391, + "Ġadverse": 7392, + "ket": 7393, + "ĠPal": 7394, + "ĠMany": 7395, + "Ġdiffraction": 7396, + "Ġtransmit": 7397, + "Ġphosphate": 7398, + "olesterol": 7399, + "Ġquestionnai": 7400, + "ĠSea": 7401, + "bruary": 7402, + "Ġmodelling": 7403, + "ĠDR": 7404, + "olin": 7405, + "chmark": 7406, + "Ġprecisely": 7407, + "gans": 7408, + "vin": 7409, + "ridge": 7410, + "ĠIncre": 7411, + "Ġneuronal": 7412, + "ĠâīĪ": 7413, + "Ġexcellent": 7414, + "etary": 7415, + "Ġoverlap": 7416, + "Ġstronger": 7417, + "Ġfracture": 7418, + "ĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ": 7419, + "Ġclinic": 7420, + "ĠList": 7421, + "Ġhistor": 7422, + "generation": 7423, + "riched": 7424, + "illus": 7425, + "ĠÃħ": 7426, + "ĠRole": 7427, + "Ġlabeled": 7428, + "Ġorthogonal": 7429, + "Ġischem": 7430, + "Ġinstability": 7431, + "loop": 7432, + "Ġplotted": 7433, + "ĠProcessing": 7434, + "ĠTa": 7435, + "ĠConclusion": 7436, + "Ġmagne": 7437, + "Ġuniversal": 7438, + "Ġjet": 7439, + "Ġregim": 7440, + "float": 7441, + "Ġcod": 7442, + "adj": 7443, + "boldmath": 7444, + "Ġarrang": 7445, + "Ġtrends": 7446, + "Ġprecipitation": 7447, + "frequency": 7448, + "Ġcontrad": 7449, + "Ġtransferred": 7450, + "Ġmaintenance": 7451, + "ÎĶ": 7452, + "np": 7453, + "istence": 7454, + "heres": 7455, + "lective": 7456, + "ĠSurvey": 7457, + "ĠÐ": 7458, + "Ġstand": 7459, + "Ġdiscovery": 7460, + "ains": 7461, + "versely": 7462, + "Ġnumerous": 7463, + "ylated": 7464, + "Ġembedding": 7465, + "Ġcollabor": 7466, + "ename": 7467, + "immun": 7468, + "Ġadjusted": 7469, + "ires": 7470, + "cur": 7471, + "Ġvaccine": 7472, + "Ġtraits": 7473, + "Ġmorphological": 7474, + "Ġprecurs": 7475, + "roscope": 7476, + "adi": 7477, + "ecutive": 7478, + "uan": 7479, + "Ġtract": 7480, + "ĠPres": 7481, + "Ġmyel": 7482, + "Ġadequ": 7483, + "Ġethanol": 7484, + "ih": 7485, + "Ġmeth": 7486, + "Ġcounts": 7487, + "Ġqualitative": 7488, + "Ġmusic": 7489, + "Ġreinfor": 7490, + "After": 7491, + "Ġacquisition": 7492, + "Ġhttps": 7493, + "alling": 7494, + "ita": 7495, + "icate": 7496, + "script": 7497, + "Ġoptimized": 7498, + "ĠHo": 7499, + "Ġmild": 7500, + "oplas": 7501, + "Ġoverex": 7502, + "ĠâΧ": 7503, + "Ġcollect": 7504, + "ĠMain": 7505, + "Ġextracellular": 7506, + "Ġanc": 7507, + "rawn": 7508, + "Ġexplored": 7509, + "Ġreserv": 7510, + "ĠApplication": 7511, + "case": 7512, + "Ġmarine": 7513, + "ĠĠĠĠĠĠĠĠĠĠĠĠĠ": 7514, + "iled": 7515, + "Ġmesh": 7516, + "ĠMonte": 7517, + "clos": 7518, + "Ġperforming": 7519, + "Ag": 7520, + "regular": 7521, + "Ġcatal": 7522, + "Ġpotentials": 7523, + "antly": 7524, + "URE": 7525, + "Ġaccomp": 7526, + "Ġreasonable": 7527, + "Ġpresentation": 7528, + "abolic": 7529, + "ĠOnly": 7530, + "anned": 7531, + "Ġsubstantial": 7532, + "Ġdietary": 7533, + "Ġsubstrates": 7534, + "apter": 7535, + "Ġintestinal": 7536, + "Ġproduces": 7537, + "Proposition": 7538, + "rogen": 7539, + "ĠStat": 7540, + "burg": 7541, + "rench": 7542, + "textbf": 7543, + "ystems": 7544, + "atable": 7545, + "ĠVir": 7546, + "Ġsolved": 7547, + "icense": 7548, + "Ġsong": 7549, + "Ġextreme": 7550, + "pty": 7551, + "ĠCity": 7552, + "vered": 7553, + "ĠMRI": 7554, + "Ġtwice": 7555, + "ĠMn": 7556, + "Ġmerg": 7557, + "activation": 7558, + "Ġng": 7559, + "Ġodd": 7560, + "Ġattrac": 7561, + "Ġattempt": 7562, + "Ġseparately": 7563, + "Ġrobot": 7564, + "ĠMultiple": 7565, + "Ġscientific": 7566, + "ĠPP": 7567, + "Ġmineral": 7568, + "Ġprotocols": 7569, + "Ġsuperior": 7570, + "ocamp": 7571, + "boxyl": 7572, + "Ġuniformly": 7573, + "ĠSeveral": 7574, + "Ġmol": 7575, + "Cor": 7576, + "underline": 7577, + "Ġinfluenced": 7578, + "Ġcurren": 7579, + "using": 7580, + "race": 7581, + "ĠNevertheless": 7582, + "Ġaccom": 7583, + "Ġgravitational": 7584, + "Ġindirect": 7585, + "Ġcapable": 7586, + "Ġanalysed": 7587, + "Ġdischarge": 7588, + "Ġves": 7589, + "Ġligand": 7590, + "lik": 7591, + "Ġsi": 7592, + "Ġaged": 7593, + "Ġcrystals": 7594, + "Ġspeech": 7595, + "Ġcopper": 7596, + "ĠSan": 7597, + "ĠArm": 7598, + "Ġmanuscript": 7599, + "Ġsecretion": 7600, + "wedge": 7601, + "·": 7602, + "Ġraw": 7603, + "Ġaimed": 7604, + "Ġevolutionary": 7605, + "Ġconsequences": 7606, + "Ġitem": 7607, + "Ġwestern": 7608, + "Ġsolvent": 7609, + "Ġstimuli": 7610, + "Ġrequirement": 7611, + "http": 7612, + "efore": 7613, + "ĠAtl": 7614, + "Ġatmospheric": 7615, + "Ġpackage": 7616, + "Ġmyocardi": 7617, + "Ġdashed": 7618, + "Ġverify": 7619, + "ativistic": 7620, + "Ġtom": 7621, + "avirus": 7622, + "aken": 7623, + "ĠNumer": 7624, + "Ġadvantages": 7625, + "FR": 7626, + "ĠSelf": 7627, + "rected": 7628, + "config": 7629, + "Ġiteration": 7630, + "Ġeigenvalues": 7631, + "Ġprobabilities": 7632, + "FIG": 7633, + "ĠWater": 7634, + "ĠAu": 7635, + "Ġgave": 7636, + "Ġvar": 7637, + "ricular": 7638, + "opathy": 7639, + "Ġrh": 7640, + "ordance": 7641, + "Ġwin": 7642, + "ĠScale": 7643, + "Ġannual": 7644, + "ataset": 7645, + "Ġpel": 7646, + "ĠâĪª": 7647, + "ĠCC": 7648, + "itors": 7649, + "Ġlith": 7650, + "Ġchromosome": 7651, + "Ġfuel": 7652, + "Ġmultiv": 7653, + "Ġmanufacture": 7654, + "la": 7655, + "ĠSa": 7656, + "umes": 7657, + "igm": 7658, + "Ġnanoc": 7659, + "EGF": 7660, + "Ġsignature": 7661, + "NS": 7662, + "Ġmeet": 7663, + "Ġfair": 7664, + "meth": 7665, + "Ġlocalized": 7666, + "ĠCentral": 7667, + "deg": 7668, + "Ġsurrounding": 7669, + "Ġnone": 7670, + "ĠMO": 7671, + "ĠInterestingly": 7672, + "Ġmultic": 7673, + "ĠKe": 7674, + "Ġinhibited": 7675, + "ĠCare": 7676, + "ĠOpen": 7677, + "Ġglob": 7678, + "EA": 7679, + "ĠFound": 7680, + "Ġpixel": 7681, + "oke": 7682, + "RD": 7683, + "loc": 7684, + "tious": 7685, + "Ġdistinguish": 7686, + "Ġanterior": 7687, + "urch": 7688, + "Ġjud": 7689, + "ĠPower": 7690, + "Ġswitch": 7691, + "ĠSyn": 7692, + "Ġinvolvement": 7693, + "ucl": 7694, + "Ġlibrary": 7695, + "ĠConst": 7696, + "Ġspherical": 7697, + "ĠTNF": 7698, + "Ġaltered": 7699, + "vance": 7700, + "transfer": 7701, + "Ms": 7702, + "ĠOper": 7703, + "inement": 7704, + "seq": 7705, + "Cons": 7706, + "hole": 7707, + "ĠPhot": 7708, + "Ġgut": 7709, + "acterial": 7710, + "ĠIP": 7711, + "unt": 7712, + "Ġnom": 7713, + "has": 7714, + "ĠFebruary": 7715, + "Ġprostate": 7716, + "ĠML": 7717, + "high": 7718, + "ĠBackground": 7719, + "ulent": 7720, + "Ġocean": 7721, + "after": 7722, + "ĠOff": 7723, + "loss": 7724, + "Ġfavor": 7725, + "Ġworkers": 7726, + "Ġhidden": 7727, + "Ġextracts": 7728, + "razil": 7729, + "sign": 7730, + "None": 7731, + "Ġcolumns": 7732, + "Ġfractions": 7733, + "Ġcovered": 7734, + "ĠServ": 7735, + "Ġinform": 7736, + "bed": 7737, + "Ġattem": 7738, + "raining": 7739, + "Ġneutron": 7740, + "Ġrice": 7741, + "Ġmotif": 7742, + "Ġartificial": 7743, + "Ġinhibitory": 7744, + "Ġdt": 7745, + "AGE": 7746, + "Ġsampled": 7747, + "Ġbatter": 7748, + "Ġsubjected": 7749, + "Ġgeneric": 7750, + "ĠNH": 7751, + "Ġcontinue": 7752, + "utional": 7753, + "Ġaug": 7754, + "ius": 7755, + "Ġexecution": 7756, + "ĠWilli": 7757, + "ĠDespite": 7758, + "AMI": 7759, + "Ġcontents": 7760, + "ĠSens": 7761, + "ogens": 7762, + "Col": 7763, + "Ġfo": 7764, + "Ġaddi": 7765, + "uated": 7766, + "Ġrecommended": 7767, + "ĠSW": 7768, + "Ġarch": 7769, + "ĠYes": 7770, + "Ġhol": 7771, + "aturally": 7772, + "titive": 7773, + "Ġche": 7774, + "Ġsector": 7775, + "ĠDefinition": 7776, + "Ġconcepts": 7777, + "orous": 7778, + "small": 7779, + "erson": 7780, + "inator": 7781, + "ĠMT": 7782, + "Ġhypertension": 7783, + "cks": 7784, + "Ġnative": 7785, + "Ġtax": 7786, + "ryl": 7787, + "Ġreactive": 7788, + "rb": 7789, + "ducible": 7790, + "omm": 7791, + "Ġdiagnosed": 7792, + "Ġdriving": 7793, + "Ġbiomass": 7794, + "uate": 7795, + "Ġpil": 7796, + "called": 7797, + "Ġserve": 7798, + "Ġinterfer": 7799, + "ippocamp": 7800, + "Ġalgebraic": 7801, + "Ġbegan": 7802, + "Ġpicture": 7803, + "independent": 7804, + "Ġutilized": 7805, + "going": 7806, + "ora": 7807, + "nm": 7808, + "Ġdownstream": 7809, + "Ġorbital": 7810, + "ountain": 7811, + "ĠHis": 7812, + "Ġresol": 7813, + "Ġcorrections": 7814, + "onym": 7815, + "scripts": 7816, + "Ġsilicon": 7817, + "Ġcum": 7818, + "ĠTri": 7819, + "Ġpeptides": 7820, + "Ġreceiving": 7821, + "Ġstationary": 7822, + "ĠμL": 7823, + "clerosis": 7824, + "Ġmodules": 7825, + "ema": 7826, + "ĠAfrican": 7827, + "struction": 7828, + "Ġfarm": 7829, + "Ġlearn": 7830, + "node": 7831, + "®": 7832, + "Ġsuperconduc": 7833, + "ĠLinear": 7834, + "Ġtechnologies": 7835, + "Ġnecessarily": 7836, + "Ġcoronary": 7837, + "ĠEast": 7838, + "Ġframes": 7839, + "Ġsegmentation": 7840, + "Vs": 7841, + "Ġbehavioral": 7842, + "Îĵ": 7843, + "Ġlogic": 7844, + "Ġaccompan": 7845, + "tified": 7846, + "hanol": 7847, + "ĠInhib": 7848, + "ilation": 7849, + "ander": 7850, + "Ġeffort": 7851, + "ĠDen": 7852, + "DI": 7853, + "optim": 7854, + "terminal": 7855, + "Ġmobility": 7856, + "Ġconsideration": 7857, + "OVA": 7858, + "Ġparad": 7859, + "oxo": 7860, + "Ġdeficiency": 7861, + "ultural": 7862, + "Ġvalidity": 7863, + "Ġorders": 7864, + "Ġlocus": 7865, + "Ġarth": 7866, + "emat": 7867, + "Ġfeeding": 7868, + "Ġprogramming": 7869, + "Ġtemplate": 7870, + "elian": 7871, + "Ġoption": 7872, + "ĠFollowing": 7873, + "Ġenable": 7874, + "Ġassign": 7875, + "Ġformul": 7876, + "pu": 7877, + "Ġatmosphere": 7878, + "slant": 7879, + "ĠRuss": 7880, + "ĠEvidence": 7881, + "Ġsimilarly": 7882, + "Ġcamp": 7883, + "Ġwound": 7884, + "ĠCharacterization": 7885, + "ĠPBS": 7886, + "ees": 7887, + "ĠDirect": 7888, + "ĠSL": 7889, + "Ġfruit": 7890, + "Ġgate": 7891, + "ito": 7892, + "Chem": 7893, + "Ġcollision": 7894, + "ortic": 7895, + "Ġpolymorphism": 7896, + "enza": 7897, + "what": 7898, + "Ġexperimentally": 7899, + "Ġultra": 7900, + "ez": 7901, + "Ġnerv": 7902, + "Ġessentially": 7903, + "ĠAustralia": 7904, + "ĠStandard": 7905, + "Ġmedicine": 7906, + "adian": 7907, + "ĠHiggs": 7908, + "uge": 7909, + "Ġsupports": 7910, + "uma": 7911, + "Ġcomplicated": 7912, + "date": 7913, + "ophagy": 7914, + "ĠMarkov": 7915, + "Ġoccurring": 7916, + "oplus": 7917, + "Pub": 7918, + "prob": 7919, + "urable": 7920, + "Ġkept": 7921, + "Ġisolation": 7922, + "Ġevol": 7923, + "iliary": 7924, + "Ġregist": 7925, + "Ġholes": 7926, + "Ġclar": 7927, + "ipar": 7928, + "Ġenrich": 7929, + "Ġroute": 7930, + "ayers": 7931, + "ediatric": 7932, + "Ġpolynomials": 7933, + "Ġtrivial": 7934, + "ĠSam": 7935, + "variant": 7936, + "Ġfreedom": 7937, + "poss": 7938, + "Ġinference": 7939, + "ola": 7940, + "Ġinterpreted": 7941, + "Ca": 7942, + "emory": 7943, + "Ġcentury": 7944, + "ĠRem": 7945, + "ĠWu": 7946, + "Ġsuppression": 7947, + "Ġgenerator": 7948, + "ĠHom": 7949, + "Ġviscos": 7950, + "Ġpseudo": 7951, + "ĠChild": 7952, + "ĠSA": 7953, + "iber": 7954, + "Ġequivalence": 7955, + "ifies": 7956, + "ĠConsider": 7957, + "oline": 7958, + "âī¤": 7959, + "Ġdeple": 7960, + "Ġaveraged": 7961, + "Ġsouthern": 7962, + "Ġordered": 7963, + "ĠBrown": 7964, + "Ġmethylation": 7965, + "ĠAdap": 7966, + "Ġmaternal": 7967, + "onded": 7968, + "ĠBehavi": 7969, + "Ġidentifiers": 7970, + "Ġprocessed": 7971, + "GG": 7972, + "VI": 7973, + "Ġcha": 7974, + "unk": 7975, + "ĠFunctional": 7976, + "Ġhydroph": 7977, + "Ġfinancial": 7978, + "econd": 7979, + "ĠΨ": 7980, + "Ġemphas": 7981, + "Ġdefect": 7982, + "mar": 7983, + "Ġnorthern": 7984, + "core": 7985, + "Ġadhesion": 7986, + "Ġtele": 7987, + "Ġwarm": 7988, + "rifug": 7989, + "rangian": 7990, + "resolution": 7991, + "Ġhex": 7992, + "hbar": 7993, + "Ġharmonic": 7994, + "Ġcontrac": 7995, + "Ġreading": 7996, + "Ġefforts": 7997, + "ĠOl": 7998, + "Ġanxiety": 7999, + "bul": 8000, + "TC": 8001, + "ipid": 8002, + "Remark": 8003, + "Ġforming": 8004, + "ilbert": 8005, + "amond": 8006, + "Ġanalytic": 8007, + "orec": 8008, + "cha": 8009, + "ĠConsequently": 8010, + "ĠSu": 8011, + "forall": 8012, + "ĠÃŀ": 8013, + "Ġaspect": 8014, + "Ġinsights": 8015, + "ativity": 8016, + "iotics": 8017, + "heimer": 8018, + "ĠLabor": 8019, + "Ġaware": 8020, + "ĠBritish": 8021, + "chemical": 8022, + "Ġâĭ": 8023, + "clusion": 8024, + "ĠMich": 8025, + "Ġgrade": 8026, + "ĠSEM": 8027, + "ĠCirc": 8028, + "heses": 8029, + "WL": 8030, + "Ġenabl": 8031, + "Ġdend": 8032, + "Ġindustry": 8033, + "Ġimproves": 8034, + "tet": 8035, + "Ġtel": 8036, + "Ġwashed": 8037, + "Ġshorter": 8038, + "Ġincident": 8039, + "ĠActivity": 8040, + "Ġdoses": 8041, + "ĠBrazil": 8042, + "Ġtransformations": 8043, + "Ġformat": 8044, + "ĠProof": 8045, + "Ġlen": 8046, + "ulative": 8047, + "Ġcyclic": 8048, + "Ġrecruit": 8049, + "ptr": 8050, + "TH": 8051, + "Ġreceive": 8052, + "ĠNext": 8053, + "ĠExp": 8054, + "iant": 8055, + "instein": 8056, + "Set": 8057, + "rene": 8058, + "Ġgeomet": 8059, + "Ġconsiderable": 8060, + "So": 8061, + "ught": 8062, + "Ġpapers": 8063, + "ĠCS": 8064, + "za": 8065, + "Ġisomorphism": 8066, + "hou": 8067, + "Ġmutants": 8068, + "Ġportion": 8069, + "Ġþ": 8070, + "Ġcontinuum": 8071, + "Cu": 8072, + "ĠComputed": 8073, + "Ġcombining": 8074, + "ova": 8075, + "ĠNP": 8076, + "Ġcrack": 8077, + "Ġsometimes": 8078, + "Ġcontinued": 8079, + "Definition": 8080, + "arcin": 8081, + "ĠCd": 8082, + "ĠMedical": 8083, + "iences": 8084, + "ĠCross": 8085, + "Ġtranscriptional": 8086, + "ĠZe": 8087, + "std": 8088, + "iforn": 8089, + "Ġfailed": 8090, + "Ġidentifying": 8091, + "Ġmir": 8092, + "Ġmetastasis": 8093, + "OF": 8094, + "nn": 8095, + "ĠCID": 8096, + "Ġoscillations": 8097, + "ancies": 8098, + "write": 8099, + "Ġbandwidth": 8100, + "Ġtrade": 8101, + "Ġaging": 8102, + "ĠModeling": 8103, + "Ġassert": 8104, + "Ġcurrents": 8105, + "Ġfire": 8106, + "ubiqu": 8107, + "Ġalbum": 8108, + "Ġfrequent": 8109, + "Name": 8110, + "Ġpurch": 8111, + "Ġplayer": 8112, + "ĠEsc": 8113, + "Ġnotion": 8114, + "Ġinternational": 8115, + "ulum": 8116, + "oic": 8117, + "Ġincubation": 8118, + "Ġphenomena": 8119, + "Ġserver": 8120, + "uter": 8121, + "Ġven": 8122, + "quin": 8123, + "Ġhypox": 8124, + "ĠRF": 8125, + "iton": 8126, + "Error": 8127, + "Ġhemat": 8128, + "Ġthemselves": 8129, + "Ġperp": 8130, + "idual": 8131, + "Ġpurposes": 8132, + "mes": 8133, + "wing": 8134, + "rov": 8135, + "Ġemiss": 8136, + "Ġexperienced": 8137, + "ques": 8138, + "ĠLC": 8139, + "ĠRecent": 8140, + "book": 8141, + "Ġalkal": 8142, + "idx": 8143, + "hyth": 8144, + "Ġconcrete": 8145, + "Ġswitching": 8146, + "Ġexplanation": 8147, + "irds": 8148, + "Ġsigns": 8149, + "Ġobj": 8150, + "Ġcytokines": 8151, + "ubble": 8152, + "adder": 8153, + "Ġuncertainties": 8154, + "Ġpromotes": 8155, + "Ġcompl": 8156, + "Ġscan": 8157, + "Ġprime": 8158, + "PH": 8159, + "Ġheterogeneous": 8160, + "ĠYou": 8161, + "Although": 8162, + "Ġserious": 8163, + "Ġdrive": 8164, + "Ġheterogeneity": 8165, + "rystall": 8166, + "Ġod": 8167, + "Ġconvolution": 8168, + "ĠâĬĨ": 8169, + "ĠSpace": 8170, + "Ġgastric": 8171, + "ĠStre": 8172, + "ĠPV": 8173, + "base": 8174, + "Met": 8175, + "Ġlosses": 8176, + "Ġcytotox": 8177, + "Ġcontrolling": 8178, + "lease": 8179, + "Ġregulated": 8180, + "ĠEngine": 8181, + "ĠHospital": 8182, + "Br": 8183, + "onom": 8184, + "hyde": 8185, + "stage": 8186, + "Ġgiving": 8187, + "ĠPen": 8188, + "ĠSociety": 8189, + "driven": 8190, + "iang": 8191, + "Ġmodifications": 8192, + "BV": 8193, + "Ġacceleration": 8194, + "Ġmilk": 8195, + "onomic": 8196, + "Ġthink": 8197, + "oglob": 8198, + "Ġfeasible": 8199, + "nam": 8200, + "Ġreflection": 8201, + "ĠPoly": 8202, + "Ġsummarized": 8203, + "FL": 8204, + "Ġrect": 8205, + "Ġpredominant": 8206, + "Ġblot": 8207, + "dehyde": 8208, + "Ġtransformed": 8209, + "Ġfacilitate": 8210, + "ĠCarlo": 8211, + "Ġgreatly": 8212, + "ĠSocial": 8213, + "Ġparents": 8214, + "bigg": 8215, + "rospective": 8216, + "Ġprognosis": 8217, + "Ġcharacterize": 8218, + "Ġconnectivity": 8219, + "Ġtrajectories": 8220, + "ĠSH": 8221, + "Ġlies": 8222, + "Ġcandidates": 8223, + "romy": 8224, + "Ġsor": 8225, + "ĠIns": 8226, + "Ġthor": 8227, + "Ġmetals": 8228, + "ĠSV": 8229, + "Ġtiming": 8230, + "Ġutility": 8231, + "Ġnewly": 8232, + "ĠIFN": 8233, + "Ġaffecting": 8234, + "cement": 8235, + "ĠMel": 8236, + "ĠÌģ": 8237, + "types": 8238, + "lysis": 8239, + "ercul": 8240, + "Ġdistor": 8241, + "actors": 8242, + "psy": 8243, + "Ġbook": 8244, + "ĠEven": 8245, + "temperature": 8246, + "Ġinvasion": 8247, + "Ġrecognized": 8248, + "factor": 8249, + "Ne": 8250, + "Ġintersection": 8251, + "Ġcortical": 8252, + "ng": 8253, + "Ġdeploy": 8254, + "Ġamplitudes": 8255, + "Ġda": 8256, + "ĠGC": 8257, + "Ġchallenging": 8258, + "Ġprelim": 8259, + "GM": 8260, + "Acc": 8261, + "Ġfourth": 8262, + "alc": 8263, + "ĠPS": 8264, + "ĠGenetic": 8265, + "lock": 8266, + "error": 8267, + "skip": 8268, + "sime": 8269, + "Ġana": 8270, + "simeq": 8271, + "Ġcerebral": 8272, + "ĠEX": 8273, + "aved": 8274, + "rophy": 8275, + "idopsis": 8276, + "Ġbehind": 8277, + "Ġenables": 8278, + "Ġindustrial": 8279, + "ĠPac": 8280, + "Ġdefinitions": 8281, + "Ġcatalytic": 8282, + "Ġdissip": 8283, + "ervical": 8284, + "Ġcommut": 8285, + "Ġrepeat": 8286, + "Ġchiral": 8287, + "Ġpron": 8288, + "pol": 8289, + "Ġgoing": 8290, + "Ġmicroscope": 8291, + "Ġhealthcare": 8292, + "ĠClassification": 8293, + "titude": 8294, + "ĠFermi": 8295, + "Ġhttp": 8296, + "arest": 8297, + "Ġsupporting": 8298, + "Ġwood": 8299, + "night": 8300, + "Ġkinetics": 8301, + "Ġsubsets": 8302, + "Ġsubunit": 8303, + "ĠCanada": 8304, + "aton": 8305, + "Ġaccurately": 8306, + "Ġresistant": 8307, + "Ġï̽": 8308, + "riction": 8309, + "Ġchamber": 8310, + "igue": 8311, + "ĠPhil": 8312, + "Ġrecover": 8313, + "cs": 8314, + "Ġsphere": 8315, + "ĠSpecifically": 8316, + "Ġanne": 8317, + "Ġinitiation": 8318, + "ĠTH": 8319, + "Ġbud": 8320, + "ordered": 8321, + "Ġdielectric": 8322, + "ĠCollege": 8323, + "Ġproducing": 8324, + "Ġantenna": 8325, + "Bs": 8326, + "ĠFrench": 8327, + "OX": 8328, + "ĠAmerica": 8329, + "ĠâĢĶ": 8330, + "ounting": 8331, + "fully": 8332, + "Ġserved": 8333, + "Ġresidue": 8334, + "Ġarguments": 8335, + "Ġpand": 8336, + "Ġcompany": 8337, + "Ġconditional": 8338, + "mia": 8339, + "ĠQCD": 8340, + "Ġviscosity": 8341, + "Ġprospective": 8342, + "asonal": 8343, + "Ġdominated": 8344, + "Ġpenet": 8345, + "opo": 8346, + "Ġnine": 8347, + "ĠIll": 8348, + "ĠVisual": 8349, + "Ġfiles": 8350, + "Ġyeast": 8351, + "Ġthank": 8352, + "GN": 8353, + "real": 8354, + "Ġverified": 8355, + "ĠIndian": 8356, + "Ġstiff": 8357, + "rological": 8358, + "Ġdram": 8359, + "Ġtight": 8360, + "ĠGerman": 8361, + "ĠTechnology": 8362, + "ĠApproach": 8363, + "romatic": 8364, + "Ġacoustic": 8365, + "tian": 8366, + "osin": 8367, + "ĠDepartment": 8368, + "otropy": 8369, + "Ġempty": 8370, + "trivial": 8371, + "ofil": 8372, + "Ġalgebras": 8373, + "texts": 8374, + "Ġwebs": 8375, + "Ġpore": 8376, + "Ġpacket": 8377, + "Time": 8378, + "img": 8379, + "ony": 8380, + "ritic": 8381, + "Ġvelocities": 8382, + "ĠDynamics": 8383, + "Ġcancers": 8384, + "Ġtrunc": 8385, + "ĠFormation": 8386, + "ĠDonor": 8387, + "ĠMit": 8388, + "IST": 8389, + "Ġconcluded": 8390, + "Ġantag": 8391, + "ĠSoft": 8392, + "append": 8393, + "Ġfragments": 8394, + "ĠProf": 8395, + "Ġfluor": 8396, + "ĠJac": 8397, + "ĠSn": 8398, + "Ġlept": 8399, + "Ġsplitting": 8400, + "Ġsexual": 8401, + "ĠFore": 8402, + "ĠGener": 8403, + "Ġneighborhood": 8404, + "Ġbenchmark": 8405, + "ĠRA": 8406, + "Ġdivision": 8407, + "ifornia": 8408, + "True": 8409, + "Ġfuzzy": 8410, + "Ġtro": 8411, + "cents": 8412, + "Ġconstitu": 8413, + "atial": 8414, + "astern": 8415, + "ĠTim": 8416, + "Ġperception": 8417, + "Ġsubstanti": 8418, + "Ġmacro": 8419, + "Ġoutl": 8420, + "ĠObserv": 8421, + "prising": 8422, + "oked": 8423, + "orectal": 8424, + "ĠCho": 8425, + "ĠDifferent": 8426, + "Ġinvestigations": 8427, + "Ġconsistency": 8428, + "ients": 8429, + "ĠFOR": 8430, + "ASS": 8431, + "ĠVan": 8432, + "Ġsituations": 8433, + "ĠBR": 8434, + "Ġinfrared": 8435, + "ymal": 8436, + "Ġpixels": 8437, + "Ġcarrier": 8438, + "sen": 8439, + "INT": 8440, + "Ġefficiently": 8441, + "DT": 8442, + "ĠExpl": 8443, + "ionic": 8444, + "Ġnaturally": 8445, + "Ġpropos": 8446, + "Ġguide": 8447, + "Ġconclusions": 8448, + "oon": 8449, + "Ġgrant": 8450, + "Ġinstances": 8451, + "Ġreviewed": 8452, + "Ġelectromagnetic": 8453, + "Ġthreat": 8454, + "edia": 8455, + "ĠOptimization": 8456, + "ĠBio": 8457, + "Ġtrigger": 8458, + "icient": 8459, + "otypic": 8460, + "Ġstret": 8461, + "Ġantic": 8462, + "Ġtoxic": 8463, + "Ġspinal": 8464, + "UPAC": 8465, + "Ġoverview": 8466, + "otion": 8467, + "Ġstraightforward": 8468, + "Ġpositively": 8469, + "aste": 8470, + "Ġreferences": 8471, + "ulose": 8472, + "ĠGre": 8473, + "Ġantagon": 8474, + "Ġshifts": 8475, + "Ġdrawn": 8476, + "ĠWhite": 8477, + "Ġfractional": 8478, + "Ġbundle": 8479, + "Ġexhibits": 8480, + "Ġreservoir": 8481, + "ĠAlex": 8482, + "Ġaggregation": 8483, + "Ġcircle": 8484, + "Ġpractices": 8485, + "ĠCoval": 8486, + "ĠDistribution": 8487, + "Ġtang": 8488, + "ĠMut": 8489, + "Ġregulate": 8490, + "osphere": 8491, + "iro": 8492, + "AMINO": 8493, + "vest": 8494, + "Ġphotos": 8495, + "Ġevident": 8496, + "Ġbusiness": 8497, + "control": 8498, + "Ġworth": 8499, + "ĠPoisson": 8500, + "ĠArabidopsis": 8501, + "ĠTarget": 8502, + "Ġregulates": 8503, + "ĠIr": 8504, + "ĠAdv": 8505, + "Ġensemble": 8506, + "pring": 8507, + "Ġprice": 8508, + "ĠFL": 8509, + "ĠImpact": 8510, + "Ġeventually": 8511, + "inating": 8512, + "Ġcentrifug": 8513, + "frame": 8514, + "Ġdiagrams": 8515, + "Ġtag": 8516, + "Ġtry": 8517, + "surface": 8518, + "ĠIdentifiers": 8519, + "rained": 8520, + "Ġsides": 8521, + "Ġinn": 8522, + "Ġflexible": 8523, + "Ġsatellite": 8524, + "Ġaffinity": 8525, + "Ġsummer": 8526, + "GP": 8527, + "amb": 8528, + "Ġaqu": 8529, + "String": 8530, + "treatment": 8531, + "ĠDynamic": 8532, + "mathop": 8533, + "Ġnotice": 8534, + "nes": 8535, + "rowave": 8536, + "vestig": 8537, + "Ġoutputs": 8538, + "Ġcoherent": 8539, + "Ġillustrate": 8540, + "Ġvalidated": 8541, + "ĠSchem": 8542, + "Ġasked": 8543, + "batch": 8544, + "Ġpurified": 8545, + "Ġminimize": 8546, + "ĠDE": 8547, + "UM": 8548, + "check": 8549, + "varian": 8550, + "ĠGold": 8551, + "ylene": 8552, + "IO": 8553, + "Ġcholesterol": 8554, + "PubChem": 8555, + "ĠKore": 8556, + "ĠCounty": 8557, + "Ġii": 8558, + "ĠMAP": 8559, + "ectomy": 8560, + "Ġsemantic": 8561, + "Ġcollagen": 8562, + "Ġperceived": 8563, + "ichia": 8564, + "Ġadministered": 8565, + "containing": 8566, + "rank": 8567, + "InChI": 8568, + "Ġirradiation": 8569, + "Ġlogarithm": 8570, + "Ġgames": 8571, + "Ġinjected": 8572, + "ĠMHz": 8573, + "Ġdors": 8574, + "Ġevaluating": 8575, + "ĠHyper": 8576, + "Ġchromatography": 8577, + "phen": 8578, + "ĠKar": 8579, + "Ġantimicrobial": 8580, + "riend": 8581, + "Ġdescribing": 8582, + "Ġwt": 8583, + "Ġhormone": 8584, + "AK": 8585, + "ĠIUPAC": 8586, + "Ga": 8587, + "Ġvitamin": 8588, + "Ġconnections": 8589, + "uous": 8590, + "ĠLine": 8591, + "Ġbeneficial": 8592, + "cases": 8593, + "icated": 8594, + "isks": 8595, + "parent": 8596, + "Id": 8597, + "eries": 8598, + "run": 8599, + "Ġmind": 8600, + "itt": 8601, + "sulf": 8602, + "zheimer": 8603, + "Ġinterf": 8604, + "Vert": 8605, + "Ġanth": 8606, + "ologous": 8607, + "ĠLife": 8608, + "Ġmur": 8609, + "Ġpermut": 8610, + "oting": 8611, + "Ġneutrino": 8612, + "Ġborn": 8613, + "pmatrix": 8614, + "ĠCalifornia": 8615, + "agent": 8616, + "Ġcollisions": 8617, + "ĠNS": 8618, + "Ġhippocamp": 8619, + "Ġpowder": 8620, + "Ġvaries": 8621, + "Ġepidem": 8622, + "ĠWeb": 8623, + "uler": 8624, + "Ġinterested": 8625, + "Ġdevelopmental": 8626, + "Ġlengths": 8627, + "Ġcolour": 8628, + "Ġquas": 8629, + "ĠRich": 8630, + "Eq": 8631, + "Ġinfants": 8632, + "ĠPH": 8633, + "ophila": 8634, + "Ġcausing": 8635, + "Ge": 8636, + "module": 8637, + "IB": 8638, + "Ġcontributed": 8639, + "rose": 8640, + "Ġcytoplas": 8641, + "--------------------------------": 8642, + "Ġroad": 8643, + "symmetric": 8644, + "Us": 8645, + "Ġweakly": 8646, + "tite": 8647, + "Ġdefines": 8648, + "ĠPE": 8649, + "Ġmetabolites": 8650, + "Ġlob": 8651, + "Ġterminal": 8652, + "Ġdemonstrates": 8653, + "ĠAcceptor": 8654, + "ĠClo": 8655, + "Ġinferred": 8656, + "Ġvill": 8657, + "First": 8658, + "Ġneglig": 8659, + "Ġwireless": 8660, + "Ab": 8661, + "particle": 8662, + "oisotopic": 8663, + "Ġexcited": 8664, + "PM": 8665, + "Ġconsecutive": 8666, + "ĠIsotype": 8667, + "Ġstimulus": 8668, + "ĠMC": 8669, + "timate": 8670, + "ĠCovalently": 8671, + "Bonded": 8672, + "Ġyellow": 8673, + "Ġalloy": 8674, + "density": 8675, + "Ġfilters": 8676, + "Ġamplification": 8677, + "Ġwon": 8678, + "ht": 8679, + "Ġimpacts": 8680, + "Ġstaff": 8681, + "ĠâĪĢ": 8682, + "ĠIsomeric": 8683, + "Ġsmoking": 8684, + "Qu": 8685, + "Ġcaptured": 8686, + "haps": 8687, + "ĠNULL": 8688, + "Ġriver": 8689, + "count": 8690, + "Ġmanifest": 8691, + "Ġdiabetic": 8692, + "Ġalterations": 8693, + "ĠRotatable": 8694, + "ĠPRO": 8695, + "ĠMonoisotopic": 8696, + "ĠïĤ": 8697, + "spect": 8698, + "Ġcatalyst": 8699, + "Ġmodeled": 8700, + "Ġpage": 8701, + "ĠROS": 8702, + "ĠCanonicalized": 8703, + "ĠTw": 8704, + "Ġaux": 8705, + "avage": 8706, + "ĠRaman": 8707, + "sto": 8708, + "perf": 8709, + "Ġreplacement": 8710, + "ĠEnvironment": 8711, + "Ġacting": 8712, + "pati": 8713, + "ificant": 8714, + "through": 8715, + "Ġsaturation": 8716, + "Ġtip": 8717, + "Ġrecurrence": 8718, + "ĠHistory": 8719, + "Ġprotective": 8720, + "Ġburden": 8721, + "ado": 8722, + "yes": 8723, + "inst": 8724, + "Ap": 8725, + "ĠSy": 8726, + "Ġphon": 8727, + "ĠâĪij": 8728, + "Ġgenotype": 8729, + "Ġcovariance": 8730, + "Ġquickly": 8731, + "ĠDu": 8732, + "Ġsug": 8733, + "Ġdecline": 8734, + "ĠTB": 8735, + "Ġstrictly": 8736, + "Ġmoist": 8737, + "undred": 8738, + "ĠCB": 8739, + "atile": 8740, + "ĠHF": 8741, + "Ġarticles": 8742, + "Ġps": 8743, + "ĠEnh": 8744, + "isting": 8745, + "Ġbiology": 8746, + "Ġbodies": 8747, + "ĠAk": 8748, + "ĠNumerical": 8749, + "ĠLagrangian": 8750, + "Ġdiscovered": 8751, + "Ġvic": 8752, + "opes": 8753, + "Ġfragment": 8754, + "Ġty": 8755, + "ismic": 8756, + "Ġhepatic": 8757, + "Ġenriched": 8758, + "pan": 8759, + "Ġinfluences": 8760, + "ĠLake": 8761, + "color": 8762, + "Ġenrichment": 8763, + "ochemistry": 8764, + "Ġunstable": 8765, + "ĠIgG": 8766, + "derly": 8767, + "Ġecos": 8768, + "Ġconcerning": 8769, + "ĠRisk": 8770, + "Ġmargin": 8771, + "Ġpathogenesis": 8772, + "Ġpump": 8773, + "Ġpreliminary": 8774, + "Ġtumour": 8775, + "Further": 8776, + "azole": 8777, + "Ġelectrodes": 8778, + "Ġdial": 8779, + "ubes": 8780, + "ĠNatural": 8781, + "ĠMul": 8782, + "ĠïĢŃ": 8783, + "Ġnic": 8784, + "Ġimped": 8785, + "only": 8786, + "Ġcomparative": 8787, + "rection": 8788, + "aki": 8789, + "Ġrend": 8790, + "Ġsparse": 8791, + "Ġindicator": 8792, + "location": 8793, + "tism": 8794, + "activated": 8795, + "ĠPb": 8796, + "eptide": 8797, + "Ġendogenous": 8798, + "Ġcenters": 8799, + "ao": 8800, + "sw": 8801, + "Ġconsensus": 8802, + "Ġattributes": 8803, + "Ġsafe": 8804, + "Ġbelieve": 8805, + "ovirus": 8806, + "Ġimmunity": 8807, + "Ġfitted": 8808, + "Ġcontributes": 8809, + "iable": 8810, + "Ġviruses": 8811, + "Ġinsight": 8812, + "ĠNovel": 8813, + "ĠAlzheimer": 8814, + "cepted": 8815, + "ĠPt": 8816, + "Ġcentre": 8817, + "nat": 8818, + "Ġbiosynthesis": 8819, + "mits": 8820, + "Ġchemistry": 8821, + "Ġjus": 8822, + "anish": 8823, + "Ġrefrac": 8824, + "ĠTor": 8825, + "Ġpanels": 8826, + "Ġimply": 8827, + "Ġmatched": 8828, + "usc": 8829, + "word": 8830, + "vae": 8831, + "ĠStar": 8832, + "syn": 8833, + "Mat": 8834, + "Ġapplicable": 8835, + "ĠPseud": 8836, + "ampions": 8837, + "ĠRen": 8838, + "Ġusage": 8839, + "ĠLight": 8840, + "prec": 8841, + "Ġfibrosis": 8842, + "Ġreconstruc": 8843, + "ĠON": 8844, + "ĠGHz": 8845, + "GD": 8846, + "algebra": 8847, + "iger": 8848, + "Ġdecisions": 8849, + "infected": 8850, + "knowledg": 8851, + "Ġexpressing": 8852, + "Ġmyocardial": 8853, + "ordination": 8854, + "Ġprognostic": 8855, + "Ġfibrobl": 8856, + "Ġacceler": 8857, + "ĠAssessment": 8858, + "Ġconstrained": 8859, + "Ġallele": 8860, + "ride": 8861, + "Ġrequest": 8862, + "abilistic": 8863, + "teb": 8864, + "Ġga": 8865, + "Ġrecovered": 8866, + "Ġpromin": 8867, + "urses": 8868, + "ĠHC": 8869, + "ĠMur": 8870, + "ĠEqs": 8871, + "Ġdefining": 8872, + "Ġmer": 8873, + "image": 8874, + "Ġorganisms": 8875, + "grad": 8876, + "Ġreflected": 8877, + "elastic": 8878, + "eties": 8879, + "dimethyl": 8880, + "ELO": 8881, + "random": 8882, + "ĠDiagn": 8883, + "erculosis": 8884, + "rob": 8885, + "Ġmoments": 8886, + "ĠEC": 8887, + "Ġexperiences": 8888, + "erving": 8889, + "ĠNC": 8890, + "Ġvortex": 8891, + "gre": 8892, + "structures": 8893, + "elt": 8894, + "Ġcarry": 8895, + "ĠThrough": 8896, + "Ġpreced": 8897, + "rastruct": 8898, + "itus": 8899, + "Ġpsychological": 8900, + "Ġlimiting": 8901, + "two": 8902, + "ĠBound": 8903, + "ĠCre": 8904, + "ĠSmith": 8905, + "Ġcast": 8906, + "Ġcompetition": 8907, + "sch": 8908, + "Ġcapability": 8909, + "achment": 8910, + "Ġinhibits": 8911, + "ð": 8912, + "ĠDifferential": 8913, + "Ġautomatically": 8914, + "Ġgest": 8915, + "Ġwaters": 8916, + "Ġuniqu": 8917, + "zer": 8918, + "Equ": 8919, + "Ġstudying": 8920, + "Ġdied": 8921, + "Ġos": 8922, + "Ġrecombination": 8923, + "uncil": 8924, + "Ġpathogen": 8925, + "GFR": 8926, + "UV": 8927, + "eneration": 8928, + "ĠSta": 8929, + "Ġinstant": 8930, + "Ġproven": 8931, + "Ġds": 8932, + "Ġdamp": 8933, + "Next": 8934, + "ĠYoung": 8935, + "Ġpowerful": 8936, + "Ġwriting": 8937, + "kl": 8938, + "Ġcareer": 8939, + "ĠCorollary": 8940, + "Ns": 8941, + "Ġ�": 8942, + "ĠMil": 8943, + "Ġburn": 8944, + "ticular": 8945, + "ondon": 8946, + "Pr": 8947, + "ĠLin": 8948, + "ĠJapanese": 8949, + "ĠLab": 8950, + "Ġstrip": 8951, + "protein": 8952, + "Ġhour": 8953, + "anglement": 8954, + "anguages": 8955, + "rd": 8956, + "parse": 8957, + "Ġemissions": 8958, + "Hence": 8959, + "ĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ": 8960, + "Ġjob": 8961, + "ĠAS": 8962, + "Ġaxial": 8963, + "ĠTur": 8964, + "carbon": 8965, + "MF": 8966, + "ĠNE": 8967, + "Ġarise": 8968, + "Ġlinearly": 8969, + "Ġprolong": 8970, + "Ġleak": 8971, + "ĠĠĠĠĠĠĠĠĠĠĠĠĠĠ": 8972, + "Ġmoved": 8973, + "orbidity": 8974, + "Ġprofessional": 8975, + "code": 8976, + "osine": 8977, + "Ġpolic": 8978, + "Ġbonds": 8979, + "mask": 8980, + "Ġconverted": 8981, + "ville": 8982, + "ectious": 8983, + "parallel": 8984, + "ĠHal": 8985, + "ĠTGF": 8986, + "mental": 8987, + "Ġreader": 8988, + "Ġstandards": 8989, + "ago": 8990, + "ĠEN": 8991, + "Ġstations": 8992, + "Ġnormalization": 8993, + "ĠÎĺ": 8994, + "chain": 8995, + "What": 8996, + "Ġtomography": 8997, + "Ġentries": 8998, + "blue": 8999, + "ĠPrevious": 9000, + "ias": 9001, + "Ġquestionnaire": 9002, + "Ġhaz": 9003, + "Ġhomology": 9004, + "very": 9005, + "Ġnucleotide": 9006, + "ĠGenome": 9007, + "Ġμl": 9008, + "Ġutilization": 9009, + "Ġpolymers": 9010, + "rote": 9011, + "Ġsmallest": 9012, + "calc": 9013, + "Ġspl": 9014, + "Ġtension": 9015, + "Ġdiscontinu": 9016, + "ala": 9017, + "hol": 9018, + "Ġdetermines": 9019, + "Ġproj": 9020, + "ĠOverall": 9021, + "Ġble": 9022, + "fo": 9023, + "Ġprinciples": 9024, + "Ġinteracting": 9025, + "Ġhardware": 9026, + "life": 9027, + "ails": 9028, + "Ġdifficulty": 9029, + "Ġchoices": 9030, + "Ġcard": 9031, + "Ġlact": 9032, + "Ġroll": 9033, + "Ġquantified": 9034, + "ĠScientific": 9035, + "Ġlandsc": 9036, + "aligned": 9037, + "Ġcomposites": 9038, + "herichia": 9039, + "Ġenvelop": 9040, + "itig": 9041, + "Ste": 9042, + "Ġcompet": 9043, + "Ġimpairment": 9044, + "Ġclosure": 9045, + "Ġreturned": 9046, + "Ġreceiver": 9047, + "Ġpeer": 9048, + "Ġconsent": 9049, + "Ġultras": 9050, + "Ġphotons": 9051, + "Ġsuppose": 9052, + "Ġpredicting": 9053, + "ĠâĬķ": 9054, + "Ġcompan": 9055, + "Ġnegligible": 9056, + "current": 9057, + "umber": 9058, + "Ġcompatible": 9059, + "iop": 9060, + "ĠStructural": 9061, + "Ref": 9062, + "Ġson": 9063, + "Ġequality": 9064, + "Ġconsisted": 9065, + "Ġvibr": 9066, + "oupling": 9067, + "vation": 9068, + "Ġovercome": 9069, + "super": 9070, + "lict": 9071, + "Ġpancreatic": 9072, + "Gs": 9073, + "aped": 9074, + "asal": 9075, + "wan": 9076, + "Ġlatent": 9077, + "Ġcovering": 9078, + "Ġlesion": 9079, + "iance": 9080, + "ĠFT": 9081, + "wood": 9082, + "jecture": 9083, + "ĠBC": 9084, + "linked": 9085, + "ĠLaw": 9086, + "Ġemit": 9087, + "Ġunclear": 9088, + "Ġprem": 9089, + "acted": 9090, + "polar": 9091, + "cre": 9092, + "Ġmodulus": 9093, + "ropath": 9094, + "Sub": 9095, + "ami": 9096, + "Ġpick": 9097, + "ERR": 9098, + "Ġmovements": 9099, + "Ni": 9100, + "Ġmechanics": 9101, + "odic": 9102, + "Ġgal": 9103, + "ĠManagement": 9104, + "host": 9105, + "ewise": 9106, + "ĠTotal": 9107, + "ĠInfluence": 9108, + "Ġubiqu": 9109, + "rophys": 9110, + "Ġcaps": 9111, + "Ġparticipant": 9112, + "Ġpolyp": 9113, + "td": 9114, + "Ġiterations": 9115, + "dominal": 9116, + "BB": 9117, + "Ġcharacters": 9118, + "Ġdeviations": 9119, + "resistant": 9120, + "Ġmalaria": 9121, + "Ġremote": 9122, + "hskip": 9123, + "Ġunderwent": 9124, + "util": 9125, + "block": 9126, + "uclide": 9127, + "Φ": 9128, + "electron": 9129, + "Ġsensory": 9130, + "ĠSimulation": 9131, + "Ġreward": 9132, + "Ġpandemic": 9133, + "Ġbor": 9134, + "ynthetic": 9135, + "Ġinvasive": 9136, + "RF": 9137, + "ĠSmall": 9138, + "ĠFisher": 9139, + "valent": 9140, + "ĠMI": 9141, + "rocytes": 9142, + "ĠTE": 9143, + "Ġstre": 9144, + "Ġperturbations": 9145, + "Ġsimplicity": 9146, + "ĠGrowth": 9147, + "ĠÎł": 9148, + "Ġinoc": 9149, + "arding": 9150, + "atum": 9151, + "multi": 9152, + "ĠDiv": 9153, + "anes": 9154, + "acillus": 9155, + "Ġlifetime": 9156, + "ĠHep": 9157, + "Ġaz": 9158, + "usp": 9159, + "ĠAssume": 9160, + "Ġbreaking": 9161, + "ĠAtt": 9162, + "ticipants": 9163, + "Ġluminosity": 9164, + "Ġdonor": 9165, + "params": 9166, + "ohyd": 9167, + "Ġprogen": 9168, + "ĠPO": 9169, + "GO": 9170, + "ĠLeg": 9171, + "Ġbiomarkers": 9172, + "Ġrural": 9173, + "Ġneon": 9174, + "gluc": 9175, + "ĠPB": 9176, + "Ġguid": 9177, + "Ġcervical": 9178, + "pace": 9179, + "Ġcord": 9180, + "umn": 9181, + "Ġsubspace": 9182, + "Ġattached": 9183, + "Ġdeposited": 9184, + "Ġindicators": 9185, + "ĠSF": 9186, + "quire": 9187, + "Ġdissolved": 9188, + "rite": 9189, + "ĠNA": 9190, + "Ġju": 9191, + "Ġaddressed": 9192, + "Ġsuppressed": 9193, + "Ġpneumonia": 9194, + "Ġsession": 9195, + "ĠChe": 9196, + "ĠFer": 9197, + "Ġaccordance": 9198, + "Des": 9199, + "Ġquar": 9200, + "Ġfitness": 9201, + "Ġviability": 9202, + "osh": 9203, + "Ġphylogenetic": 9204, + "ectin": 9205, + "pat": 9206, + "ĠFrance": 9207, + "Ġmessages": 9208, + "Ġloci": 9209, + "Ġconflict": 9210, + "Ġrelevance": 9211, + "Ġinstructions": 9212, + "Ġsomewhat": 9213, + "changed": 9214, + "Ġcorrectly": 9215, + "ozyg": 9216, + "avig": 9217, + "ĠLat": 9218, + "Ġovarian": 9219, + "ĠRemark": 9220, + "joint": 9221, + "aint": 9222, + "west": 9223, + "sample": 9224, + "Ġdivergence": 9225, + "Ġhair": 9226, + "agonal": 9227, + "Ġmim": 9228, + "Ġimmediate": 9229, + "ĠPort": 9230, + "Ġoffers": 9231, + "Ġdepicted": 9232, + "Ġhydrox": 9233, + "ĠTow": 9234, + "Ġemerging": 9235, + "oupled": 9236, + "Ġhundred": 9237, + "Ġadapted": 9238, + "eller": 9239, + "ĠRelations": 9240, + "ette": 9241, + "Ġgastro": 9242, + "Ġmorphism": 9243, + "Ġequipment": 9244, + "pop": 9245, + "unately": 9246, + "Ġtransplantation": 9247, + "ifiers": 9248, + "Ġelderly": 9249, + "onucle": 9250, + "Ġrefers": 9251, + "arial": 9252, + "ĠCommittee": 9253, + "Ġmalignant": 9254, + "omonas": 9255, + "Ġallocation": 9256, + "ogether": 9257, + "Ġnanot": 9258, + "plot": 9259, + "ĠMes": 9260, + "Ġplanar": 9261, + "ells": 9262, + "source": 9263, + "owski": 9264, + "Ġna": 9265, + "Ġclock": 9266, + "Ġambient": 9267, + "ocene": 9268, + "Ġfluorescent": 9269, + "Ġvalu": 9270, + "ĠMagnetic": 9271, + "Ġdepart": 9272, + "phosphate": 9273, + "Ġroughly": 9274, + "Ġneither": 9275, + "ĠAltern": 9276, + "Ġstay": 9277, + "Ġspot": 9278, + "ĠEnt": 9279, + "Ġseconds": 9280, + "hard": 9281, + "Ġrecurrent": 9282, + "Ġpatch": 9283, + "Ġlimitation": 9284, + "ĠDer": 9285, + "Ġsharp": 9286, + "Ġexpectation": 9287, + "ĠLore": 9288, + "dict": 9289, + "Reg": 9290, + "Ġneutroph": 9291, + "Ġnur": 9292, + "Ġstarts": 9293, + "ostasis": 9294, + "Ġorganized": 9295, + "ĠcDNA": 9296, + "orient": 9297, + "ĠExample": 9298, + "ĠFund": 9299, + "aylor": 9300, + "idering": 9301, + "Ġtriple": 9302, + "nic": 9303, + "Ġattacks": 9304, + "ĠDros": 9305, + "è": 9306, + "ĠEM": 9307, + "Ġoptimum": 9308, + "Ġpull": 9309, + "Ġce": 9310, + "eryth": 9311, + "Ġrating": 9312, + "Ġreproductive": 9313, + "Ġdecades": 9314, + "Ġreplace": 9315, + "List": 9316, + "ĠFast": 9317, + "Ġredshift": 9318, + "opsy": 9319, + "illa": 9320, + "double": 9321, + "tera": 9322, + "Ġgoals": 9323, + "ĠSk": 9324, + "INE": 9325, + "Ġbiochemical": 9326, + "uint": 9327, + "Ġfetal": 9328, + "ĠRiemann": 9329, + "uries": 9330, + "Ġpp": 9331, + "Ġsymbols": 9332, + "ĠKa": 9333, + "Di": 9334, + "ĠGalax": 9335, + "ĠCompared": 9336, + "Ġcasc": 9337, + "Ġbits": 9338, + "Ġscaff": 9339, + "Ġestimator": 9340, + "ĠAdditional": 9341, + "Ġimprovements": 9342, + "ectives": 9343, + "Ġhous": 9344, + "ĠMagn": 9345, + "Ġmultivariate": 9346, + "Ġagric": 9347, + "vo": 9348, + "utter": 9349, + "ĠAcknowledg": 9350, + "su": 9351, + "Ġammon": 9352, + "Ġaims": 9353, + "Ġzinc": 9354, + "Ġelong": 9355, + "ĠGO": 9356, + "Question": 9357, + "including": 9358, + "LogP": 9359, + "Ġintellig": 9360, + "Ġcone": 9361, + "ĠFoundation": 9362, + "Ġimpaired": 9363, + "Ġillness": 9364, + "ĠEscherichia": 9365, + "Ġabundant": 9366, + "scal": 9367, + "ensively": 9368, + "Ġnegatively": 9369, + "parameter": 9370, + "Ġpermeability": 9371, + "domain": 9372, + "rated": 9373, + "Ġepoch": 9374, + "Ġadolescents": 9375, + "Ġdefic": 9376, + "ĠEstimation": 9377, + "Ġroutine": 9378, + "Per": 9379, + "tol": 9380, + "Ġelliptic": 9381, + "ĠHE": 9382, + "oblast": 9383, + "Ġreaches": 9384, + "Ġfluxes": 9385, + "Ġsun": 9386, + "ĠAnaly": 9387, + "âĢľ": 9388, + "ĠXLogP": 9389, + "Ġfiltering": 9390, + "rian": 9391, + "ĠScal": 9392, + "Ġpin": 9393, + "ĠTiO": 9394, + "iments": 9395, + "Ġmarginal": 9396, + "Ġrecombinant": 9397, + "Ġencour": 9398, + "Ġalumin": 9399, + "Ġtf": 9400, + "atalytic": 9401, + "Ġobservational": 9402, + "Ġgeneralization": 9403, + "Ġ": 9404, + "Ġantibiotic": 9405, + "Ġgenerates": 9406, + "ĠdB": 9407, + "Spec": 9408, + "rically": 9409, + "Ġvaluable": 9410, + "Ġtopic": 9411, + "Ġtermin": 9412, + "Ġsemicon": 9413, + "Ġquantification": 9414, + "ubb": 9415, + "Ġkinem": 9416, + "erring": 9417, + "Ġaeros": 9418, + "pack": 9419, + "Ġfewer": 9420, + "Ġfatigue": 9421, + "Ġgoes": 9422, + "Ġnight": 9423, + "ĠUs": 9424, + "â̬": 9425, + "ĠPrinc": 9426, + "Ġspring": 9427, + "Ġconcerns": 9428, + "Ġsmart": 9429, + "Ġsecret": 9430, + "Ġmmol": 9431, + "Ġbelief": 9432, + "DC": 9433, + "Ġsubstantially": 9434, + "âĪĩ": 9435, + "Ġsubstitution": 9436, + "mapsto": 9437, + "sky": 9438, + "illance": 9439, + "Ġstudent": 9440, + "okine": 9441, + "Ġinterior": 9442, + "Ġeigenvalue": 9443, + "my": 9444, + "Ġcloser": 9445, + "erenti": 9446, + "Ġecological": 9447, + "ĠFigures": 9448, + "olytic": 9449, + "Ġarrays": 9450, + "ĠCas": 9451, + "Ġloops": 9452, + "Ġcorrected": 9453, + "Ġrhe": 9454, + "Ġinversion": 9455, + "Ġpreferred": 9456, + "umab": 9457, + "ĠDI": 9458, + "Ġadequate": 9459, + "irm": 9460, + "Ġimplicit": 9461, + "ship": 9462, + "Ġplayers": 9463, + "Ġdelayed": 9464, + "Ġwinter": 9465, + "Ġvulner": 9466, + "Ġshapes": 9467, + "Ġstained": 9468, + "ĠMajor": 9469, + "Ġhierarchical": 9470, + "ĠDig": 9471, + "ersion": 9472, + "ĠEfficient": 9473, + "Ġwalls": 9474, + "dfrac": 9475, + "Ġclassifier": 9476, + "Ġmonol": 9477, + "Ġupdated": 9478, + "Ġmature": 9479, + "ĠLI": 9480, + "earing": 9481, + "Ġfinger": 9482, + "ounter": 9483, + "ankton": 9484, + "While": 9485, + "Ġrealistic": 9486, + "ĠCamp": 9487, + "Ġfilled": 9488, + "Ġdead": 9489, + "ĠPacific": 9490, + "Ïĩ": 9491, + "ĠDavid": 9492, + "Ġadditive": 9493, + "enchymal": 9494, + "Ġobser": 9495, + "Ġstere": 9496, + "Ġultrasound": 9497, + "ĠPredic": 9498, + "Ġends": 9499, + "sectional": 9500, + "mas": 9501, + "omat": 9502, + "ivity": 9503, + "Ġhandle": 9504, + "Ġmetastatic": 9505, + "olet": 9506, + "ryp": 9507, + "ACE": 9508, + "Ġporous": 9509, + "Ġconcern": 9510, + "itored": 9511, + "Ġcircles": 9512, + "Ġemotional": 9513, + "gered": 9514, + "Ġfriction": 9515, + "first": 9516, + "ophy": 9517, + "escop": 9518, + "aded": 9519, + "Ġresolved": 9520, + "ERS": 9521, + "Ġpathogens": 9522, + "Ġgradually": 9523, + "ĠBrain": 9524, + "xf": 9525, + "anium": 9526, + "ael": 9527, + "New": 9528, + "Ġcytokine": 9529, + "ĠBP": 9530, + "Ġspecimen": 9531, + "olean": 9532, + "Ġtaxon": 9533, + "Ġsequential": 9534, + "κB": 9535, + "ademic": 9536, + "plings": 9537, + "~~": 9538, + "ermal": 9539, + "tree": 9540, + "Ġcausal": 9541, + "arian": 9542, + "Ġcrop": 9543, + "opol": 9544, + "channel": 9545, + "ĠMex": 9546, + "Ġclon": 9547, + "ĠRecently": 9548, + "ĠInvestig": 9549, + "Ġrecommendations": 9550, + "format": 9551, + "ĠMET": 9552, + "Ġsentence": 9553, + "Ġbp": 9554, + "ĠGW": 9555, + "Ġrecording": 9556, + "Ġple": 9557, + "totic": 9558, + "Ġ": 9559, + "Ġranged": 9560, + "ention": 9561, + "obacteria": 9562, + "ceptions": 9563, + "ĠImport": 9564, + "dynamic": 9565, + "porary": 9566, + "Given": 9567, + "Ġturbulence": 9568, + "Ġgram": 9569, + "Ġequally": 9570, + "cd": 9571, + "ĠOs": 9572, + "Ġturns": 9573, + "Ġdetecting": 9574, + "atio": 9575, + "generate": 9576, + "grade": 9577, + "Ġcirculation": 9578, + "Ġmanufacturer": 9579, + "La": 9580, + "ĠHilbert": 9581, + "Ts": 9582, + "integr": 9583, + "Ġbelongs": 9584, + "ĠInternet": 9585, + "angl": 9586, + "ĠâĬ¥": 9587, + "ĠDrosophila": 9588, + "uclidean": 9589, + "tan": 9590, + "Ġextends": 9591, + "Ġexpanded": 9592, + "illin": 9593, + "square": 9594, + "ysacchar": 9595, + "Ġquantify": 9596, + "Ġpulses": 9597, + "Ġvesic": 9598, + "ĠNK": 9599, + "orescence": 9600, + "ĠPhosph": 9601, + "Ġvision": 9602, + "ĠHuang": 9603, + "ĠResponse": 9604, + "house": 9605, + "ears": 9606, + "Ġeg": 9607, + "Ġaccepted": 9608, + "ĠTM": 9609, + "ametric": 9610, + "Ġpathological": 9611, + "Ġrecruitment": 9612, + "ATA": 9613, + "Ġfigures": 9614, + "ĠPress": 9615, + "Ġaligned": 9616, + "Ġpostoperative": 9617, + "ĠMeV": 9618, + "Ġconsiderably": 9619, + "Ġconformal": 9620, + "ĠIsland": 9621, + "number": 9622, + "Ġautomatic": 9623, + "Ġsplic": 9624, + "Ġcytos": 9625, + "Ġdescrip": 9626, + "ĠSant": 9627, + "lies": 9628, + "uity": 9629, + "itone": 9630, + "ECT": 9631, + "ĠBon": 9632, + "Ġdisapp": 9633, + "board": 9634, + "orrh": 9635, + "Ġcalculating": 9636, + "nee": 9637, + "ĠMeas": 9638, + "Ġgenomes": 9639, + "Ġphotoc": 9640, + "Ġreadily": 9641, + "ovine": 9642, + "ĠDev": 9643, + "Ġsatur": 9644, + "Ġkinds": 9645, + "ĠPK": 9646, + "Ġrod": 9647, + "Ġjunction": 9648, + "ĠHA": 9649, + "Ġdesigns": 9650, + "hn": 9651, + "Ġordering": 9652, + "Ġcosmological": 9653, + "Ġpilot": 9654, + "Ġcolorectal": 9655, + "ĠLondon": 9656, + "ĠDirac": 9657, + "Cont": 9658, + "ĠWind": 9659, + "ĠTre": 9660, + "idin": 9661, + "ĠïĢ«": 9662, + "iltration": 9663, + "Moreover": 9664, + "Ġretention": 9665, + "timately": 9666, + "hydrogen": 9667, + "del": 9668, + "bolic": 9669, + "ĠQuanti": 9670, + "period": 9671, + "Ġretrieval": 9672, + "atase": 9673, + "endicular": 9674, + "ulties": 9675, + "RS": 9676, + "NH": 9677, + "Ġinformed": 9678, + "Ġfiltered": 9679, + "membrane": 9680, + "Ġstiffness": 9681, + "ĠOcean": 9682, + "ĠSY": 9683, + "Ġlot": 9684, + "ĠFigs": 9685, + "Ġansw": 9686, + "ĠEngland": 9687, + "ĠAtlantic": 9688, + "processing": 9689, + "Ġdogs": 9690, + "Ġlie": 9691, + "Ġunion": 9692, + "ĠTan": 9693, + "Ġhalo": 9694, + "Ġcontinuously": 9695, + "Bu": 9696, + "AMP": 9697, + "ĠApp": 9698, + "Ġmoisture": 9699, + "Ġthyroid": 9700, + "Ġaccompanied": 9701, + "Ġfold": 9702, + "Ġoriginally": 9703, + "Ġspan": 9704, + "ĠFA": 9705, + "connected": 9706, + "Ġrecurs": 9707, + "vian": 9708, + "ĠEquations": 9709, + "ena": 9710, + "arcinoma": 9711, + "....": 9712, + "Ġdiscrep": 9713, + "UH": 9714, + "о": 9715, + "anger": 9716, + "Ġmonitored": 9717, + "Ġinfluenza": 9718, + "Ġsure": 9719, + "black": 9720, + "oe": 9721, + "Ġalloc": 9722, + "Ġhabitat": 9723, + "ophenyl": 9724, + "Ġventricular": 9725, + "Ġpolicies": 9726, + "amate": 9727, + "Ġreporting": 9728, + "Ġsoluble": 9729, + "================": 9730, + "Ġdipole": 9731, + "Ġirreducible": 9732, + "ĠPrec": 9733, + "acetyl": 9734, + "Ġthread": 9735, + "ĠApproxim": 9736, + "Ġmapped": 9737, + "ipro": 9738, + "Ġtropical": 9739, + "Sch": 9740, + "ĠANOVA": 9741, + "Ġlanguages": 9742, + "icine": 9743, + "ĠFamily": 9744, + "functions": 9745, + "EF": 9746, + "Ġnutrient": 9747, + "Ġanalyzing": 9748, + "inescence": 9749, + "Ġthromb": 9750, + "Ġkit": 9751, + "Ġmammalian": 9752, + "optotic": 9753, + "Ġequipped": 9754, + "ona": 9755, + "Ġque": 9756, + "Ġcame": 9757, + "Ġsimplified": 9758, + "Ġdecays": 9759, + "Ġpassive": 9760, + "Ġdeletion": 9761, + "Ġobtaining": 9762, + "Ġmixtures": 9763, + "Ġprimers": 9764, + "ĠPsy": 9765, + "osc": 9766, + "oment": 9767, + "Ġchloride": 9768, + "ĠPaul": 9769, + "start": 9770, + "intestinal": 9771, + "helium": 9772, + "arth": 9773, + "odot": 9774, + "Ġfits": 9775, + "Ġsquares": 9776, + "ĠCardi": 9777, + "aka": 9778, + "ributed": 9779, + "Ġinequalities": 9780, + "omething": 9781, + "hedral": 9782, + "ĠFuture": 9783, + "Ġgli": 9784, + "Ġmetallic": 9785, + "Ġfacilities": 9786, + "Ġobst": 9787, + "possible": 9788, + "Ġzones": 9789, + "ucid": 9790, + "Ġdrift": 9791, + "depend": 9792, + "valued": 9793, + "Ġnons": 9794, + "Ġworldwide": 9795, + "Ġtrust": 9796, + "Ġsole": 9797, + "ĠLevel": 9798, + "ĠSha": 9799, + "Ġregardless": 9800, + "Ġspectrometry": 9801, + "ductor": 9802, + "leuk": 9803, + "Ġskills": 9804, + "Ġincorporated": 9805, + "Ġlearned": 9806, + "Ġure": 9807, + "Ġextinc": 9808, + "ODU": 9809, + "Ġgrains": 9810, + "atern": 9811, + "ĠIndex": 9812, + "comput": 9813, + "ua": 9814, + "Ġcontamination": 9815, + "ĠAff": 9816, + "uning": 9817, + "Ġasymmetric": 9818, + "Ġopening": 9819, + "Ġbat": 9820, + "Ġagree": 9821, + "ITY": 9822, + "ĠChanges": 9823, + "organic": 9824, + "ĠRay": 9825, + "ĠHand": 9826, + "ni": 9827, + "inic": 9828, + "Ġrisks": 9829, + "Ġstock": 9830, + "Ġneck": 9831, + "Ġvolumes": 9832, + "ĠPrac": 9833, + "Ġincreasingly": 9834, + "Sc": 9835, + "oses": 9836, + "GFP": 9837, + "Ġassignment": 9838, + "ĠFed": 9839, + "ospit": 9840, + "Ġoverexpression": 9841, + "Ġmaster": 9842, + "Ġopt": 9843, + "iler": 9844, + "invariant": 9845, + "Ġconverges": 9846, + "Similar": 9847, + "ny": 9848, + "Ġstore": 9849, + "Ġelevation": 9850, + "Ġcoal": 9851, + "het": 9852, + "item": 9853, + "PLC": 9854, + "ohist": 9855, + "Gen": 9856, + "ĠChem": 9857, + "ĠCost": 9858, + "pair": 9859, + "Ġnumerically": 9860, + "Ġpreference": 9861, + "ĠNucle": 9862, + "ĠBD": 9863, + "TI": 9864, + "ĠHyp": 9865, + "roy": 9866, + "Te": 9867, + "ĠFin": 9868, + "Ġclaims": 9869, + "ibilities": 9870, + "Ġlarvae": 9871, + "ima": 9872, + "embly": 9873, + "Ġcit": 9874, + "LL": 9875, + "Ġsilica": 9876, + "ĠVI": 9877, + "Ġreaching": 9878, + "Of": 9879, + "ĠAustralian": 9880, + "tub": 9881, + "world": 9882, + "oni": 9883, + "ĠFP": 9884, + "Ġbriefly": 9885, + "ĠDescription": 9886, + "ζ": 9887, + "charg": 9888, + "Ġcis": 9889, + "ĠCat": 9890, + "Ġrecip": 9891, + "Ġemergency": 9892, + "Ġstrand": 9893, + "Ġrealized": 9894, + "posing": 9895, + "otope": 9896, + "Ġmaintaining": 9897, + "ĠChrist": 9898, + "Ġcreating": 9899, + "Ġembryos": 9900, + "Ġskeletal": 9901, + "Ġages": 9902, + "represent": 9903, + "Cr": 9904, + "Ġestimating": 9905, + "Ġrear": 9906, + "ĠYu": 9907, + "ĠPi": 9908, + "mg": 9909, + "Ġfloat": 9910, + "ĠRoy": 9911, + "pus": 9912, + "Ġchick": 9913, + "Ġmicrobiota": 9914, + "vasive": 9915, + "ĠBern": 9916, + "ĠPattern": 9917, + "lines": 9918, + "Ġflood": 9919, + "ĠLou": 9920, + "ilitary": 9921, + "rosion": 9922, + "Ġsurveys": 9923, + "FI": 9924, + "iae": 9925, + "Ġsearc": 9926, + "mol": 9927, + "Ġtitle": 9928, + "ĠMachine": 9929, + "Ġcircuits": 9930, + "ĠNumber": 9931, + "zi": 9932, + "ĠBMI": 9933, + "Ġautomated": 9934, + "plicate": 9935, + "ĠLPS": 9936, + "Ġelectrochemical": 9937, + "Ġwebsite": 9938, + "Ġanisotropy": 9939, + "Ġrings": 9940, + "Ġinnov": 9941, + "bits": 9942, + "win": 9943, + "ĠNAD": 9944, + "According": 9945, + "ĠConn": 9946, + "ureus": 9947, + "ĠFeature": 9948, + "ĠInstead": 9949, + "Comp": 9950, + "itudes": 9951, + "Mo": 9952, + "Ġscope": 9953, + "tification": 9954, + "ĠIS": 9955, + "ĠNeut": 9956, + "Ġregulating": 9957, + "coding": 9958, + "Ġrows": 9959, + "hl": 9960, + "ĠKn": 9961, + "istor": 9962, + "ampionship": 9963, + "Ġprominent": 9964, + "Ġrs": 9965, + "umatic": 9966, + "Am": 9967, + "Ġdifferentially": 9968, + "ugin": 9969, + "Ġadvance": 9970, + "phys": 9971, + "Ġsharing": 9972, + "Ġart": 9973, + "vacy": 9974, + "titions": 9975, + "Ġstyle": 9976, + "Figures": 9977, + "Ġglu": 9978, + "Ġvaccination": 9979, + "ĠOptical": 9980, + "fluid": 9981, + "ĠFre": 9982, + "Ġgradients": 9983, + "ophyl": 9984, + "ĠPubl": 9985, + "Ġaccretion": 9986, + "Ġâ̲â̲": 9987, + "ressing": 9988, + "Ġtransmitted": 9989, + "Ġnervous": 9990, + "umar": 9991, + "Ġreviews": 9992, + "Ġgenotypes": 9993, + "lower": 9994, + "ĠEV": 9995, + "Ġcontract": 9996, + "atibility": 9997, + "Ġchildhood": 9998, + "Ġonc": 9999, + "Ġbiofil": 10000, + "Ġautophagy": 10001, + "Ġadsorb": 10002, + "ĠSupport": 10003, + "Ġligands": 10004, + "power": 10005, + "rectional": 10006, + "ĠRap": 10007, + "similar": 10008, + "Ġinfarc": 10009, + "Ġelectroly": 10010, + "Ġincome": 10011, + "arity": 10012, + "ĠAv": 10013, + "eric": 10014, + "Ġclinically": 10015, + "unch": 10016, + "Ġattribute": 10017, + "Ġcommand": 10018, + "ributions": 10019, + "Ġglyc": 10020, + "Ġtranscripts": 10021, + "ograms": 10022, + "Ġassessing": 10023, + "FO": 10024, + "scriptstyle": 10025, + "ji": 10026, + "rick": 10027, + "environment": 10028, + "Ġlaws": 10029, + "Ġnormally": 10030, + "Ġdepletion": 10031, + "ĠRO": 10032, + "Ġencoded": 10033, + "hma": 10034, + "Ġbranches": 10035, + "Ġargs": 10036, + "ounger": 10037, + "orge": 10038, + "umps": 10039, + "Ġviewed": 10040, + "Ġultr": 10041, + "RR": 10042, + "ulsion": 10043, + "ĠHor": 10044, + "Ġfro": 10045, + "ĠMeasurement": 10046, + "xx": 10047, + "erman": 10048, + "ĠOnce": 10049, + "Ġoriented": 10050, + "ĠPoint": 10051, + "Ġtown": 10052, + "Ġformulas": 10053, + "SY": 10054, + "ĠAM": 10055, + "Ġconsiderations": 10056, + "ĠTC": 10057, + "ĠKit": 10058, + "Ġactin": 10059, + "Ġplasmid": 10060, + "Ġhistorical": 10061, + "Ġdye": 10062, + "Ġheur": 10063, + "ĠLeague": 10064, + "ĠMad": 10065, + "Ġgraft": 10066, + "Ġsilver": 10067, + "Over": 10068, + "ĠCos": 10069, + "ographical": 10070, + "Ġprecursor": 10071, + "rus": 10072, + "Ġregarded": 10073, + "ĠHam": 10074, + "functional": 10075, + "iveness": 10076, + "fficiency": 10077, + "igene": 10078, + "ocol": 10079, + "Ġcumulative": 10080, + "Ġseasonal": 10081, + "Ġmu": 10082, + "ĠBan": 10083, + "omycin": 10084, + "Ġbool": 10085, + "ĠMag": 10086, + "ĠAnal": 10087, + "entia": 10088, + "aign": 10089, + "Ġfootball": 10090, + "acting": 10091, + "Ġreturns": 10092, + "ĠTom": 10093, + "shaped": 10094, + "itance": 10095, + "ĠExperiment": 10096, + "ĠOS": 10097, + "Ġabsent": 10098, + "ranial": 10099, + "Ġtherapies": 10100, + "Op": 10101, + "ounced": 10102, + "ATE": 10103, + "Value": 10104, + "green": 10105, + "Ġvegetation": 10106, + "Ds": 10107, + "Ġincom": 10108, + "ç": 10109, + "Ġmarrow": 10110, + "ĠCouncil": 10111, + "Ġinvest": 10112, + "Ġclub": 10113, + "Trans": 10114, + "device": 10115, + "Ġvibration": 10116, + "ĠXu": 10117, + "////////": 10118, + "ĠHen": 10119, + "vier": 10120, + "Ġanalogous": 10121, + "Ġdelta": 10122, + "Ġsaline": 10123, + "Ġrequiring": 10124, + "Ġneuron": 10125, + "oo": 10126, + "ĠQuality": 10127, + "Ġteac": 10128, + "ĠEc": 10129, + "Li": 10130, + "Ġpublication": 10131, + "ĠPhysics": 10132, + "Ġppm": 10133, + "thase": 10134, + "Ġcreation": 10135, + "ĠAge": 10136, + "Ġbelonging": 10137, + "Ġionic": 10138, + "ĠSI": 10139, + "uating": 10140, + "endif": 10141, + "ĠCour": 10142, + "а": 10143, + "Ġdots": 10144, + "Ġeast": 10145, + "arcom": 10146, + "Ġâĩ": 10147, + "Ġrights": 10148, + "essions": 10149, + "Ġversions": 10150, + "ĠFree": 10151, + "ĠStress": 10152, + "Ġsediments": 10153, + "Ġmitig": 10154, + "Ġbow": 10155, + "ĠAct": 10156, + "ĠCarbon": 10157, + "there": 10158, + "teen": 10159, + "Ġphenotypes": 10160, + "Ġnearest": 10161, + "ĠPotential": 10162, + "Ġdeform": 10163, + "Ġreflects": 10164, + "Ġpartners": 10165, + "Ġanest": 10166, + "Ġadvers": 10167, + "ĠFactor": 10168, + "Ġconvenient": 10169, + "ulos": 10170, + "ĠPur": 10171, + "ĠMer": 10172, + "Ġflag": 10173, + "Ġtriang": 10174, + "Ġseeds": 10175, + "Ġfif": 10176, + "obil": 10177, + "ĠCK": 10178, + "mentioned": 10179, + "Ġvapor": 10180, + "ogue": 10181, + "Ġpredictor": 10182, + "Out": 10183, + "Ġcompletion": 10184, + "ĠSeg": 10185, + "Ġdiffuse": 10186, + "Ġraised": 10187, + "Ġcoordination": 10188, + "Ġsynaptic": 10189, + "ĠBor": 10190, + "ĠBol": 10191, + "Ġpolymerase": 10192, + "Ġwheat": 10193, + "Ġinsertion": 10194, + "Ġesc": 10195, + "ĠWal": 10196, + "Ġdistal": 10197, + "transferase": 10198, + "Ġinterfaces": 10199, + "Ġinsu": 10200, + "Ġpoorly": 10201, + "Ġaureus": 10202, + "Ġbenz": 10203, + "Ġuniverse": 10204, + "ĠInteraction": 10205, + "ĠFrame": 10206, + "ĠImaging": 10207, + "Ġexploration": 10208, + "ĠEngineering": 10209, + "ĠBesides": 10210, + "tia": 10211, + "Ġenum": 10212, + "anine": 10213, + "Ġtot": 10214, + "ĠEduc": 10215, + "Ġderivation": 10216, + "Array": 10217, + "yloid": 10218, + "ĠArch": 10219, + "isen": 10220, + "acity": 10221, + "akers": 10222, + "Ġsheet": 10223, + "ĠEst": 10224, + "Ġwear": 10225, + "Ġeryth": 10226, + "ECK": 10227, + "hematics": 10228, + "Ġarterial": 10229, + "criptstyle": 10230, + "scriptscriptstyle": 10231, + "echanical": 10232, + "Ġparticipation": 10233, + "cher": 10234, + "urance": 10235, + "ĠFR": 10236, + "ĠCV": 10237, + "Ġcomplementary": 10238, + "aine": 10239, + "empty": 10240, + "Ġdiges": 10241, + "Ġexponent": 10242, + "Ġsimulate": 10243, + "UE": 10244, + "Ġantibiotics": 10245, + "ĠUnivers": 10246, + "Ġpathology": 10247, + "thermal": 10248, + "pa": 10249, + "Ġstresses": 10250, + "ĠLaboratory": 10251, + "Node": 10252, + "Ġleave": 10253, + "ashing": 10254, + "Ġdiscre": 10255, + "Ġsuspension": 10256, + "reek": 10257, + "Ġscheduling": 10258, + "ĠDA": 10259, + "aryn": 10260, + "ĠNaCl": 10261, + "strain": 10262, + "STR": 10263, + "ĠCong": 10264, + "olf": 10265, + "Ġcalibr": 10266, + "ĠOptimal": 10267, + "Ġó": 10268, + "Gl": 10269, + "ĠRh": 10270, + "Ġdifficulties": 10271, + "Ġvessels": 10272, + "Ġasymmetry": 10273, + "Ġcoherence": 10274, + "ĠTaxonomy": 10275, + "Ġped": 10276, + "ĠHouse": 10277, + "titudes": 10278, + "ĠFar": 10279, + "OY": 10280, + "Ġconcentrated": 10281, + "Ġsignalling": 10282, + "Ġfungal": 10283, + "Ġconsistently": 10284, + "Ġenhances": 10285, + "Ġforecast": 10286, + "Ġcubic": 10287, + "ĠEP": 10288, + "Ġparticipate": 10289, + "ĠPlant": 10290, + "risk": 10291, + "And": 10292, + "adic": 10293, + "oflu": 10294, + "Ġsperm": 10295, + "ĠChris": 10296, + "ND": 10297, + "colon": 10298, + "Ġfaces": 10299, + "Ġtuberculosis": 10300, + "rystal": 10301, + "floor": 10302, + "ups": 10303, + "Ġgray": 10304, + "ĠPublic": 10305, + "tensor": 10306, + "Ġrigid": 10307, + "Ġeastern": 10308, + "ĠItaly": 10309, + "Ġsignatures": 10310, + "Ġshallow": 10311, + "ón": 10312, + "ĠCe": 10313, + "Ġprojects": 10314, + "Ġrouting": 10315, + "Ġpredicts": 10316, + "ĠFeatures": 10317, + "ĠDistrict": 10318, + "Ġcarrying": 10319, + "ĉĠĠĠĠ": 10320, + "ĠTO": 10321, + "HM": 10322, + "dings": 10323, + "Ġrenormal": 10324, + "Ġbring": 10325, + "pin": 10326, + "aled": 10327, + "Ġclouds": 10328, + "names": 10329, + "oxin": 10330, + "Ġperpendicular": 10331, + "WT": 10332, + "ership": 10333, + "Ġrecon": 10334, + "Ġworked": 10335, + "ĠâĢ«": 10336, + "rastructure": 10337, + "Ġpointed": 10338, + "EV": 10339, + "ĠTaylor": 10340, + "Ġhepatitis": 10341, + "Ġorbits": 10342, + "ĠFactors": 10343, + "cellular": 10344, + "Ġfocal": 10345, + "Ġboost": 10346, + "Ġmicrowave": 10347, + "ĠProject": 10348, + "BF": 10349, + "Ġpolitical": 10350, + "Ġsupplemented": 10351, + "Ġillustrates": 10352, + "Ġideas": 10353, + "ĠDrug": 10354, + "obile": 10355, + "ĠHO": 10356, + "Ġrobustness": 10357, + "rosine": 10358, + "ĠNormal": 10359, + "Ġstimulated": 10360, + "Ġimpedance": 10361, + "fortunately": 10362, + "zyme": 10363, + "Ġbarriers": 10364, + "actory": 10365, + "learly": 10366, + "Ġpreprint": 10367, + "sensitive": 10368, + "Ġturbulent": 10369, + "thing": 10370, + "Ġboard": 10371, + "Ġpit": 10372, + "Ġintegrity": 10373, + "Ġrotating": 10374, + "uda": 10375, + "Ġventi": 10376, + "ĠSNPs": 10377, + "Ġcorrespondence": 10378, + "Ġvisualization": 10379, + "avail": 10380, + "Ġbeams": 10381, + "ĠContinu": 10382, + "Ġpersistent": 10383, + "Ġbath": 10384, + "ĠmiRNAs": 10385, + "Ġcustom": 10386, + "Ġordinary": 10387, + "Ġgenerators": 10388, + "Ġbridge": 10389, + "Ġdomin": 10390, + "amy": 10391, + "Ġlooking": 10392, + "table": 10393, + "False": 10394, + "Ġsoils": 10395, + "Ġmatches": 10396, + "Ġprogressive": 10397, + "states": 10398, + "ĠShort": 10399, + "Ġcores": 10400, + "Ġintroducing": 10401, + "Ġarrest": 10402, + "Ġtexture": 10403, + "Ġdorsal": 10404, + "Ġdrain": 10405, + "izoph": 10406, + "ĠQue": 10407, + "ñ": 10408, + "disc": 10409, + "Index": 10410, + "Ġextensively": 10411, + "Ġplasticity": 10412, + "Ġreally": 10413, + "ĠError": 10414, + "Ġsugges": 10415, + "Ġconsequently": 10416, + "Ġperforms": 10417, + "likely": 10418, + "ivered": 10419, + "Ġthermodynamic": 10420, + "Ġker": 10421, + "Ġacetate": 10422, + "Ġgets": 10423, + "leqslant": 10424, + "Ġpredictors": 10425, + "ĠSwed": 10426, + "nan": 10427, + "heter": 10428, + "Ġanomaly": 10429, + "Ġoperational": 10430, + "Ġretrospective": 10431, + "Ġtends": 10432, + "aden": 10433, + "Ġborder": 10434, + "Ġmethanol": 10435, + "ĠEnter": 10436, + "Ġcollapse": 10437, + "Ġpurchased": 10438, + "Da": 10439, + "ĠHT": 10440, + "Ġfulf": 10441, + "Ġcrust": 10442, + "stone": 10443, + "Ġpenal": 10444, + "Ġtunn": 10445, + "ĠTemperature": 10446, + "Ġpotent": 10447, + "lecule": 10448, + "Ġcovers": 10449, + "Ġbattery": 10450, + "Ġbeg": 10451, + "Ġorgans": 10452, + "ĠThomas": 10453, + "Ġsolub": 10454, + "ocrine": 10455, + "ĠSpin": 10456, + "Ġinterests": 10457, + "doc": 10458, + "Ġundergoing": 10459, + "ui": 10460, + "Ġinherent": 10461, + "Ġintegrals": 10462, + "irable": 10463, + "ashi": 10464, + "Ġregeneration": 10465, + "Ġinflation": 10466, + "manif": 10467, + "ĠRecognition": 10468, + "Ġdisplays": 10469, + "Another": 10470, + "Ġcontamin": 10471, + "junction": 10472, + "Ġcopies": 10473, + "MRI": 10474, + "Ġvehicles": 10475, + "Get": 10476, + "Ġperhaps": 10477, + "Ġwest": 10478, + "Ġintensive": 10479, + "Ġsomething": 10480, + "Ġhypoxia": 10481, + "Ġcouplings": 10482, + "Ġfeasibility": 10483, + "azine": 10484, + "unic": 10485, + "iner": 10486, + "ĠIT": 10487, + "Ġdistrict": 10488, + "ĠJames": 10489, + "eval": 10490, + "Ġplacebo": 10491, + "aque": 10492, + "Ġelucid": 10493, + "ĠJacob": 10494, + "Ġcounting": 10495, + "Ġflexibility": 10496, + "Ġperman": 10497, + "Ġadvances": 10498, + "ulph": 10499, + "Ġentanglement": 10500, + "Ġintegers": 10501, + "Ġfocusing": 10502, + "kov": 10503, + "Ġhospit": 10504, + "Ġapplies": 10505, + "Ġcot": 10506, + "Sm": 10507, + "assium": 10508, + "Ġdocumented": 10509, + "Ġloaded": 10510, + "Ġrely": 10511, + "Ġinfectious": 10512, + "Ġprobes": 10513, + "Ġhighlighted": 10514, + "Ġpediatric": 10515, + "Ġweather": 10516, + "Ġmanual": 10517, + "Ġcation": 10518, + "Ġinterpolation": 10519, + "ĠStep": 10520, + "ĠKal": 10521, + "DH": 10522, + "db": 10523, + "izophren": 10524, + "ader": 10525, + "carb": 10526, + "Ġagon": 10527, + "orphous": 10528, + "tors": 10529, + "atz": 10530, + "Ġbif": 10531, + "Ġcharges": 10532, + "ĠAgain": 10533, + "Ġbron": 10534, + "ĠGover": 10535, + "Ġmining": 10536, + "aver": 10537, + "Ġearthqu": 10538, + "Ġviews": 10539, + "Ġscene": 10540, + "parameters": 10541, + "Ġbroken": 10542, + "Test": 10543, + "ĠSum": 10544, + "ĠProm": 10545, + "ÎĽ": 10546, + "Ġcutoff": 10547, + "Ġbirds": 10548, + "Ġarising": 10549, + "ĠAI": 10550, + "ĠCE": 10551, + "Ġpronounced": 10552, + "aspase": 10553, + "Ġintended": 10554, + "Ġaffine": 10555, + "Ġurine": 10556, + "Ġbelieved": 10557, + "ĠPrimary": 10558, + "ĠConf": 10559, + "Ġabdominal": 10560, + "spin": 10561, + "uniform": 10562, + "ĠStochastic": 10563, + "ĠProv": 10564, + "ĠmiRNA": 10565, + "ĠBell": 10566, + "BO": 10567, + "ĠSoftware": 10568, + "ĠTs": 10569, + "utri": 10570, + "icking": 10571, + "ien": 10572, + "Ġmicros": 10573, + "ĠNR": 10574, + "Ġleukemia": 10575, + "Ġsupernat": 10576, + "family": 10577, + "Ġalloys": 10578, + "ĠPET": 10579, + "ĠAbs": 10580, + "ĠGA": 10581, + "ĠQuantitative": 10582, + "Lo": 10583, + "Ġisland": 10584, + "second": 10585, + "pectives": 10586, + "Ġlatency": 10587, + "angi": 10588, + "Ġflight": 10589, + "ĠEuclidean": 10590, + "emy": 10591, + "ĠBlood": 10592, + "leukin": 10593, + "LT": 10594, + "enh": 10595, + "Ġswe": 10596, + "Ġunitary": 10597, + "ĠRepublic": 10598, + "Ġstructured": 10599, + "ĠSen": 10600, + "Mn": 10601, + "centric": 10602, + "Ġtransgenic": 10603, + "Ġhelpful": 10604, + "pyx": 10605, + "Ġhomeostasis": 10606, + "Na": 10607, + "Ġpassed": 10608, + "Ġeyes": 10609, + "Ġabstract": 10610, + "ulse": 10611, + "Ġmirror": 10612, + "Ġregulator": 10613, + "Ġmurine": 10614, + "loaded": 10615, + "Ġmodular": 10616, + "Ġlandscape": 10617, + "icks": 10618, + "Ġsnow": 10619, + "Ġbovine": 10620, + "elli": 10621, + "Ġdatabases": 10622, + "Ġoutbreak": 10623, + "larg": 10624, + "ĠRun": 10625, + "BE": 10626, + "Ġsurprising": 10627, + "Ġacceptable": 10628, + "Ġrotational": 10629, + "pg": 10630, + "FE": 10631, + "wik": 10632, + "Ġyounger": 10633, + "ashion": 10634, + "Ġmicroscopic": 10635, + "regation": 10636, + "Ġfibr": 10637, + "ĠPlan": 10638, + "Ġhapl": 10639, + "Ġmanifolds": 10640, + "Ġoutper": 10641, + "Ġchoosing": 10642, + "eper": 10643, + "ĠkeV": 10644, + "ĠTyp": 10645, + "pread": 10646, + "ntz": 10647, + "ĠReport": 10648, + "ĠMatrix": 10649, + "Ġintu": 10650, + "Ġproperly": 10651, + "ogly": 10652, + "oscopic": 10653, + "ĠAMP": 10654, + "ĠBM": 10655, + "Ġelementary": 10656, + "keleton": 10657, + "Ġsynthase": 10658, + "Ġionization": 10659, + "bes": 10660, + "ophage": 10661, + "duces": 10662, + "acco": 10663, + "Ġprotect": 10664, + "ĠCoul": 10665, + "Ġspent": 10666, + "Ġmand": 10667, + "Ġhind": 10668, + "fluor": 10669, + "ĠGood": 10670, + "Ġdoing": 10671, + "Object": 10672, + "ducts": 10673, + "oyl": 10674, + "chiatric": 10675, + "Ġov": 10676, + "cel": 10677, + "Ġbases": 10678, + "Ġmitochondria": 10679, + "pted": 10680, + "artz": 10681, + "Ġbrown": 10682, + "Ġequals": 10683, + "tible": 10684, + "Ġopportunity": 10685, + "azol": 10686, + "Ġofficial": 10687, + "ailed": 10688, + "Ġurinary": 10689, + "ĠHan": 10690, + "Be": 10691, + "result": 10692, + "units": 10693, + "Ġbad": 10694, + "ĠString": 10695, + "izable": 10696, + "condition": 10697, + "ĠElectron": 10698, + "immune": 10699, + "ĠME": 10700, + "hao": 10701, + "Σ": 10702, + "ĠMAT": 10703, + "Ġadopt": 10704, + "Ġelic": 10705, + "Ġshr": 10706, + "Ġproximal": 10707, + "FD": 10708, + "ĠSS": 10709, + "Ġentirely": 10710, + "esium": 10711, + "ĠEEG": 10712, + "Ġpaired": 10713, + "ĠTP": 10714, + "ĠDO": 10715, + "NAL": 10716, + "idespread": 10717, + "Ġmoves": 10718, + "site": 10719, + "Ġrain": 10720, + "Ġlap": 10721, + "ĠFu": 10722, + "ĠMeta": 10723, + "ircraft": 10724, + "Ġmagnetization": 10725, + "operation": 10726, + "Ġprost": 10727, + "Step": 10728, + "Ġsubgroups": 10729, + "ĠSouthern": 10730, + "Ġathe": 10731, + "luor": 10732, + "ĠTaxonomic": 10733, + "ĠEinstein": 10734, + "Ġrace": 10735, + "ĠKen": 10736, + "Ġattempts": 10737, + "Ġcosmic": 10738, + "ĠDop": 10739, + "Ġfixation": 10740, + "Ġremoving": 10741, + "BT": 10742, + "Ġlimb": 10743, + "Ġalign": 10744, + "Ġdried": 10745, + "du": 10746, + "Ġputative": 10747, + "uccess": 10748, + "pert": 10749, + "Ġslowly": 10750, + "also": 10751, + "olip": 10752, + "Ġclient": 10753, + "Ġbasin": 10754, + "Ġsusceptible": 10755, + "Ġcoming": 10756, + "nson": 10757, + "ĠNGC": 10758, + "assert": 10759, + "Ġtensile": 10760, + "Ġarises": 10761, + "cutaneous": 10762, + "Ġcaro": 10763, + "Bi": 10764, + "Ġdiscussions": 10765, + "Ġabnormalities": 10766, + "Ġpollution": 10767, + "ĠAx": 10768, + "Ġloads": 10769, + "Do": 10770, + "iao": 10771, + "Ġmedication": 10772, + "Ġintact": 10773, + "ĠCX": 10774, + "Ġbreeding": 10775, + "ĠUnion": 10776, + "ĠBat": 10777, + "ĠParticipants": 10778, + "ĠRegulation": 10779, + "Ġcontradiction": 10780, + "Ġintensities": 10781, + "encephal": 10782, + "rile": 10783, + "ĠTLR": 10784, + "Ġredund": 10785, + "Ġpersons": 10786, + "ĠArc": 10787, + "solid": 10788, + "law": 10789, + "Results": 10790, + "ilic": 10791, + "zone": 10792, + "ocytosis": 10793, + "Ġtriangle": 10794, + "STM": 10795, + "ĠVirus": 10796, + "Ġaid": 10797, + "soft": 10798, + "Ġsoon": 10799, + "expected": 10800, + "Ġanch": 10801, + "ĠMu": 10802, + "ĠSr": 10803, + "ĠLO": 10804, + "Ġcry": 10805, + "Ġupstream": 10806, + "oxic": 10807, + "mathit": 10808, + "ĠKle": 10809, + "Ġisotropic": 10810, + "Ġspatially": 10811, + "ĠHard": 10812, + "Ġextr": 10813, + "bas": 10814, + "eor": 10815, + "ivil": 10816, + "yan": 10817, + "Ġshifted": 10818, + "Ġbiopsy": 10819, + "Ġfeel": 10820, + "glut": 10821, + "Size": 10822, + "Ġerg": 10823, + "ĠTer": 10824, + "Ġdeaths": 10825, + "borne": 10826, + "Ġrelativistic": 10827, + "ĠVEGF": 10828, + "atab": 10829, + "spring": 10830, + "restim": 10831, + "ĠSearch": 10832, + "yphenyl": 10833, + "ecal": 10834, + "urc": 10835, + "Ġlamin": 10836, + "Ġserial": 10837, + "las": 10838, + "ĠProduction": 10839, + "Ġsocio": 10840, + "Ġmodify": 10841, + "ĠService": 10842, + "Ġbary": 10843, + "Ġradiative": 10844, + "bigl": 10845, + "Ġparadigm": 10846, + "patient": 10847, + "Ġspp": 10848, + "phone": 10849, + "Ġî": 10850, + "Ġrocks": 10851, + "ĠMartin": 10852, + "mn": 10853, + "Ġfluids": 10854, + "ĠINTR": 10855, + "ods": 10856, + "Ġdivis": 10857, + "Consider": 10858, + "component": 10859, + "Ġanomalies": 10860, + "Ġknee": 10861, + "ĠRelationship": 10862, + "aud": 10863, + "Ġovernight": 10864, + "Ġrainf": 10865, + "Ġannealing": 10866, + "Ġtreating": 10867, + "Ġcoarse": 10868, + "Model": 10869, + "Ġpose": 10870, + "Ġoccas": 10871, + "ĠWilliam": 10872, + "oor": 10873, + "Ġadjustment": 10874, + "ĠFunctions": 10875, + "imeter": 10876, + "Ġdetectors": 10877, + "Ġinstitutional": 10878, + "Ġthroughput": 10879, + "ividual": 10880, + "Ġentities": 10881, + "Ġprolonged": 10882, + "Ġship": 10883, + "Ġpreserved": 10884, + "ODUCTION": 10885, + "Ġlogistic": 10886, + "ĠPrediction": 10887, + "tized": 10888, + "ĠOrig": 10889, + "ĠHem": 10890, + "onomous": 10891, + "################": 10892, + "ĠGeneration": 10893, + "bottom": 10894, + "ĠKnow": 10895, + "clinical": 10896, + "Ġtrauma": 10897, + "Ġiterative": 10898, + "Ġfacility": 10899, + "ront": 10900, + "ĠBus": 10901, + "Ġretinal": 10902, + "Ġconduction": 10903, + "Ġchecked": 10904, + "Ġcalls": 10905, + "ologists": 10906, + "CON": 10907, + "ĠSciences": 10908, + "Ġnonzero": 10909, + "Ġbrack": 10910, + "Ġmelting": 10911, + "Ġasc": 10912, + "Ġmention": 10913, + "ĠBL": 10914, + "Ġverification": 10915, + "ukary": 10916, + "ĠSpatial": 10917, + "ĠGram": 10918, + "Ġplaces": 10919, + "Ġnecrosis": 10920, + "ĠChildren": 10921, + "Ġdelivered": 10922, + "Ġresection": 10923, + "Ġdeterministic": 10924, + "Section": 10925, + "Ġmultim": 10926, + "DF": 10927, + "Ġhypotheses": 10928, + "Ġraise": 10929, + "Ġseismic": 10930, + "Ġlam": 10931, + "ĠHCC": 10932, + "bigr": 10933, + "Ġhealing": 10934, + "isy": 10935, + "Ġoptimize": 10936, + "obacterium": 10937, + "edy": 10938, + "Ġtruth": 10939, + "Ġspacetime": 10940, + "Ġchromatin": 10941, + "Ġdomestic": 10942, + "Ġrecru": 10943, + "ĠJose": 10944, + "ĠThermal": 10945, + "Ġenvelope": 10946, + "vable": 10947, + "Ġincons": 10948, + "Ġnod": 10949, + "и": 10950, + "Ġcontributing": 10951, + "Ġguarantee": 10952, + "ĠPhen": 10953, + "Ġrab": 10954, + "Man": 10955, + "Ġsurveillance": 10956, + "Ġthings": 10957, + "Ġprev": 10958, + "ĠNonlinear": 10959, + "Ġgaps": 10960, + "aya": 10961, + "ĠCri": 10962, + "Ġcrystalline": 10963, + "strict": 10964, + "Ġcomputations": 10965, + "Ġunable": 10966, + "habil": 10967, + "umina": 10968, + "Ġpromoting": 10969, + "egrad": 10970, + "Ġregister": 10971, + "Ġcrossing": 10972, + "ulators": 10973, + "ĠLanguage": 10974, + "ĠAA": 10975, + "Ġiner": 10976, + "ĠLV": 10977, + "osan": 10978, + "Ġcoastal": 10979, + "Ġbiod": 10980, + "ĠMOD": 10981, + "Ġneighbour": 10982, + "Ġpredominantly": 10983, + "ĠNewton": 10984, + "ĠStrateg": 10985, + "being": 10986, + "Ġì": 10987, + "Ġcapabilities": 10988, + "Ġunless": 10989, + "formal": 10990, + "Ġvessel": 10991, + "bmatrix": 10992, + "ESS": 10993, + "Ġrainfall": 10994, + "ã": 10995, + "Ġprepar": 10996, + "axial": 10997, + "Ġdental": 10998, + "ĠProte": 10999, + "Ġworse": 11000, + "doped": 11001, + "hentic": 11002, + "Ġvalidate": 11003, + "Zn": 11004, + "Ġspecification": 11005, + "si": 11006, + "ĠAng": 11007, + "Ġtubes": 11008, + "ulic": 11009, + "ĠAny": 11010, + "ĠMap": 11011, + "Ġfabricated": 11012, + "Ġforced": 11013, + "ĠWilson": 11014, + "olysis": 11015, + "ĠWave": 11016, + "ĠCast": 11017, + "Ġasthma": 11018, + "Ġperi": 11019, + "ĠCyt": 11020, + "asty": 11021, + "Ġsky": 11022, + "rupt": 11023, + "Dec": 11024, + "Ġmelanoma": 11025, + "PER": 11026, + "Ġcontinuity": 11027, + "Box": 11028, + "system": 11029, + "Ġnavig": 11030, + "Ġcirculating": 11031, + "Ġcolony": 11032, + "lesssim": 11033, + "adium": 11034, + "Ġtetra": 11035, + "Ġaccounts": 11036, + "Ġpresenting": 11037, + "ĠLik": 11038, + "Ġresis": 11039, + "Ġdamping": 11040, + "ĠGly": 11041, + "ĠNeuro": 11042, + "user": 11043, + "Ġcapital": 11044, + "urate": 11045, + "ĠMW": 11046, + "Ġcorrelates": 11047, + "ĠGib": 11048, + "Ġhappens": 11049, + "Ġgall": 11050, + "ĠWithin": 11051, + "Ġcombine": 11052, + "Ġsinus": 11053, + "ĠKin": 11054, + "********************************": 11055, + "Map": 11056, + "Ġmaturation": 11057, + "Ġblocking": 11058, + "ĠCloud": 11059, + "Ġcontacts": 11060, + "Ġsac": 11061, + "ALL": 11062, + "ĠRab": 11063, + "zz": 11064, + "utch": 11065, + "Ġcarriers": 11066, + "ĠSNR": 11067, + "erb": 11068, + "Ġprotected": 11069, + "racking": 11070, + "radient": 11071, + "Ġattractive": 11072, + "Ġlag": 11073, + "Ġopin": 11074, + "ĠGi": 11075, + "Ġdefense": 11076, + "Ġtuning": 11077, + "Ġelectroph": 11078, + "Ġgreatest": 11079, + "Ġreconstructed": 11080, + "ĠPopulation": 11081, + "MAP": 11082, + "Ġwrote": 11083, + "AND": 11084, + "economic": 11085, + "ĠMichael": 11086, + "ĠBlock": 11087, + "Ġvo": 11088, + "oprop": 11089, + "Ġprofiling": 11090, + "ootst": 11091, + "ĠAsian": 11092, + "Ġoscillation": 11093, + "ĠâĨIJ": 11094, + "UD": 11095, + "Ġsigned": 11096, + "ĠEuler": 11097, + "ĠComparative": 11098, + "ĠWhere": 11099, + "ĠJack": 11100, + "Ġpassing": 11101, + "Ġvillage": 11102, + "Ġau": 11103, + "ĠNorthern": 11104, + "essage": 11105, + "matic": 11106, + "Ġaffili": 11107, + "ĠFac": 11108, + "Ġoverlapping": 11109, + "shell": 11110, + "Ġobstac": 11111, + "Ġbecoming": 11112, + "entive": 11113, + "Ġeasier": 11114, + "initely": 11115, + "Ġcentered": 11116, + "Ġacademic": 11117, + "annels": 11118, + "Ġirregular": 11119, + "Ġprojections": 11120, + "Ġproposition": 11121, + "Ġdiscrimination": 11122, + "Ġremod": 11123, + "Ġshoot": 11124, + "month": 11125, + "essor": 11126, + "Ġdiffers": 11127, + "ĠTV": 11128, + "ĠZhou": 11129, + "Ġinher": 11130, + "Ġmachines": 11131, + "Ġmell": 11132, + "Ġconjugate": 11133, + "Ġcoc": 11134, + "una": 11135, + "anyl": 11136, + "Ġoffic": 11137, + "Ġopportunities": 11138, + "Ġvein": 11139, + "ĠCharacteristics": 11140, + "Ġpathogenic": 11141, + "OYSA": 11142, + "ĠParkinson": 11143, + "ĠGalactic": 11144, + "FFFA": 11145, + "yses": 11146, + "UHFFFA": 11147, + "UHFFFAOYSA": 11148, + "actin": 11149, + "Ġunus": 11150, + "hesia": 11151, + "aceu": 11152, + "adow": 11153, + "oside": 11154, + "Ġglycos": 11155, + "Ġdiluted": 11156, + "ĠSource": 11157, + "olated": 11158, + "armaceu": 11159, + "antom": 11160, + "Ġmusc": 11161, + "Ġaveraging": 11162, + "Ġvisit": 11163, + "Ġcatch": 11164, + "Ġsatisfaction": 11165, + "Ġvon": 11166, + "valid": 11167, + "Ġyielded": 11168, + "Ġpackets": 11169, + "Ġresonant": 11170, + "pret": 11171, + "ĠGFP": 11172, + "Ġcutting": 11173, + "Ġreplacing": 11174, + "aze": 11175, + "Pa": 11176, + "Ġtoday": 11177, + "Ġdecided": 11178, + "ilateral": 11179, + "imate": 11180, + "lings": 11181, + "ĠRobust": 11182, + "ĠAst": 11183, + "odynamics": 11184, + "Ġlacking": 11185, + "izophrenia": 11186, + "Ġcontraction": 11187, + "umann": 11188, + "ĠSample": 11189, + "Ġdiamond": 11190, + "method": 11191, + "TOR": 11192, + "Ġcomments": 11193, + "sey": 11194, + "Ġmanufacturing": 11195, + "ĠDa": 11196, + "NR": 11197, + "Ġoperated": 11198, + "rates": 11199, + "Ġextinction": 11200, + "uvant": 11201, + "ĠFinite": 11202, + "Ġlymphocytes": 11203, + "bro": 11204, + "omology": 11205, + "Ġinstruments": 11206, + "bec": 11207, + "ogle": 11208, + "Ġquoti": 11209, + "Ġhyperbolic": 11210, + "Ġtrim": 11211, + "Ġpap": 11212, + "aturated": 11213, + "haus": 11214, + "Ġsessions": 11215, + "Ġcampaign": 11216, + "Ġvarieties": 11217, + "Ġprojected": 11218, + "Ġrid": 11219, + "bone": 11220, + "Ġancest": 11221, + "ĠET": 11222, + "mail": 11223, + "ĠTransport": 11224, + "///": 11225, + "ĠAnn": 11226, + "Ġcompositions": 11227, + "ĠINTRODUCTION": 11228, + "ĠâĪĴâĨĴ": 11229, + "Ġwhenever": 11230, + "ĠLip": 11231, + "parts": 11232, + "Ġisomorphic": 11233, + "Ġsulfate": 11234, + "Ġhop": 11235, + "Ġgon": 11236, + "ĠObject": 11237, + "Ġpipeline": 11238, + "Ġma": 11239, + "ĠGas": 11240, + "Ġtendency": 11241, + "object": 11242, + "Ġparametric": 11243, + "ĠReturn": 11244, + "Ġdwar": 11245, + "Ġpressures": 11246, + "ĠBios": 11247, + "Ġmultiplication": 11248, + "Ġdimin": 11249, + "Ġcolors": 11250, + "ĠTrue": 11251, + "Max": 11252, + "ĠDepend": 11253, + "Ġpairwise": 11254, + "Ġlake": 11255, + "Ġhierarchy": 11256, + "Ġthresholds": 11257, + "ĠAdaptive": 11258, + "making": 11259, + "Ġcatalysts": 11260, + "ipal": 11261, + "Ġeggs": 11262, + "Ġwire": 11263, + "ophyll": 11264, + "ictor": 11265, + "labeled": 11266, + "Ġmuscles": 11267, + "ĠUnderstanding": 11268, + "Ġfibre": 11269, + "controlled": 11270, + "Ġinvariance": 11271, + "Ġcache": 11272, + "Ġboson": 11273, + "Ġnearby": 11274, + "ĠWomen": 11275, + "ĠInitial": 11276, + "Ġprobabilistic": 11277, + "Ġembryonic": 11278, + "ĠBetween": 11279, + "Ġconjecture": 11280, + "ienti": 11281, + "tx": 11282, + "gens": 11283, + "anck": 11284, + "Ġgir": 11285, + "ĠLower": 11286, + "Ġhospitals": 11287, + "bridge": 11288, + "Method": 11289, + "Ġtheta": 11290, + "ja": 11291, + "Ġconceptual": 11292, + "Ġcolle": 11293, + "ĠSaf": 11294, + "dic": 11295, + "Ġpet": 11296, + "Ġprimer": 11297, + "ĠOh": 11298, + "Ġuntreated": 11299, + "longrightarrow": 11300, + "Ġlicense": 11301, + "Ġhelps": 11302, + "Ġcleavage": 11303, + "Ġamplified": 11304, + "е": 11305, + "Ġaccessible": 11306, + "ĠSelection": 11307, + "ĠLorentz": 11308, + "Py": 11309, + "Ġpolarized": 11310, + "ĠSTAT": 11311, + "mitt": 11312, + "Up": 11313, + "Ġongoing": 11314, + "Ġneph": 11315, + "efficient": 11316, + "activ": 11317, + "ĠRR": 11318, + "Ġfunctioning": 11319, + "otin": 11320, + "Ġlists": 11321, + "Ġformalism": 11322, + "Ġoscillator": 11323, + "Ġgastrointestinal": 11324, + "ootstrap": 11325, + "ĠAsia": 11326, + "ĠDay": 11327, + "Ġcompeting": 11328, + "ivalent": 11329, + "Ġbladder": 11330, + "Ġhit": 11331, + "Ġapproximations": 11332, + "ĠEg": 11333, + "ĠClust": 11334, + "Ġrelies": 11335, + "NE": 11336, + "copro": 11337, + "Ġbank": 11338, + "Ġintegrating": 11339, + "ĠHear": 11340, + "Ġinitiated": 11341, + "acryl": 11342, + "ĠBH": 11343, + "racted": 11344, + "yc": 11345, + "ĠRa": 11346, + "Ġremarkable": 11347, + "ĠË": 11348, + "teness": 11349, + "Ġemploying": 11350, + "steine": 11351, + "Ġï£Ń": 11352, + "Ġtransfected": 11353, + "Ġinjuries": 11354, + "ĠBrief": 11355, + "Ġwidespread": 11356, + "ĠAK": 11357, + "IVE": 11358, + "Ġharm": 11359, + "Ġpole": 11360, + "Ġanisotropic": 11361, + "aten": 11362, + "gene": 11363, + "ivariate": 11364, + "Inter": 11365, + "ductors": 11366, + "Ġaccompl": 11367, + "oglobin": 11368, + "cong": 11369, + "Ġqueries": 11370, + "escope": 11371, + "ĠHop": 11372, + "Ġentity": 11373, + "Ġoffered": 11374, + "State": 11375, + "ĠExperiments": 11376, + "anner": 11377, + "ĠWood": 11378, + "arded": 11379, + "agon": 11380, + "Ġfibroblasts": 11381, + "Ġnanos": 11382, + "Ġperoxid": 11383, + "Ġevid": 11384, + "Ġ": 11385, + "Ġretained": 11386, + "osqu": 11387, + "Ġleaving": 11388, + "Ġfashion": 11389, + "ĠnM": 11390, + "Ġmutual": 11391, + "approxim": 11392, + "Ġwalking": 11393, + "Ġimpossible": 11394, + "Ġdemonstrating": 11395, + "Ġdegener": 11396, + "ĠAV": 11397, + "Ġcontrary": 11398, + "ustion": 11399, + "oclonal": 11400, + "Anal": 11401, + "Ġperformances": 11402, + "Ġcomprom": 11403, + "orms": 11404, + "Ġbudget": 11405, + "ĠHaw": 11406, + "Ġarthritis": 11407, + "obj": 11408, + "noise": 11409, + "TiO": 11410, + "ochrome": 11411, + "Ġgeodes": 11412, + "bean": 11413, + "Ġselectivity": 11414, + "ĠFood": 11415, + "ughter": 11416, + "Ġpermutation": 11417, + "ĠRP": 11418, + "osal": 11419, + "Ġadip": 11420, + "armaceutical": 11421, + "when": 11422, + "ĠText": 11423, + "week": 11424, + "Ġbonding": 11425, + "arb": 11426, + "ocor": 11427, + "Ġvoc": 11428, + "Ġupregulated": 11429, + "Ġneighbors": 11430, + "Ġtrait": 11431, + "Ġtheore": 11432, + "Ġcf": 11433, + "ĠBerg": 11434, + "ĠLA": 11435, + "Ġlas": 11436, + "unte": 11437, + "ceptual": 11438, + "ASE": 11439, + "Ġischemic": 11440, + "Ġbending": 11441, + "dataset": 11442, + "Ġkeeping": 11443, + "Ġarrows": 11444, + "Ġsubstances": 11445, + "Ġns": 11446, + "Ġextending": 11447, + "ĠRu": 11448, + "Ġsupplementation": 11449, + "critical": 11450, + "ĠTraining": 11451, + "bullet": 11452, + "Ġpara": 11453, + "tail": 11454, + "ĠReference": 11455, + "Ġ": 11456, + "Ġdissipation": 11457, + "Ġauxiliary": 11458, + "ĠCycl": 11459, + "stim": 11460, + "Ġdilution": 11461, + "buf": 11462, + "ĠMiss": 11463, + "Ġultimately": 11464, + "Ġpowers": 11465, + "Ġstands": 11466, + "usted": 11467, + "ĠOH": 11468, + "habilitation": 11469, + "analy": 11470, + "ĠBra": 11471, + "adding": 11472, + "Corollary": 11473, + "Ġdrought": 11474, + "quality": 11475, + "Ġstandardized": 11476, + "ĠJe": 11477, + "ĠAcid": 11478, + "Ġmism": 11479, + "ĠChrom": 11480, + "draw": 11481, + "ĠBiom": 11482, + "ĠStability": 11483, + "Furthermore": 11484, + "last": 11485, + "vic": 11486, + "Ġabst": 11487, + "Ġbis": 11488, + "Ġemergence": 11489, + "Ġgiant": 11490, + "De": 11491, + "ĠSamples": 11492, + "ABA": 11493, + "nas": 11494, + "Ġont": 11495, + "Ġevap": 11496, + "levant": 11497, + "main": 11498, + "ĠRod": 11499, + "Ġcros": 11500, + "itary": 11501, + "Ġdoub": 11502, + "rö": 11503, + "igenetic": 11504, + "Ġincomplete": 11505, + "depth": 11506, + "ïģ": 11507, + "Ġsaturated": 11508, + "Ġaerosol": 11509, + "Assum": 11510, + "Ġimmunos": 11511, + "Ġlipids": 11512, + "itoneal": 11513, + "Ġbearing": 11514, + "ĠImplications": 11515, + "Ġsustained": 11516, + "Ġcompetitive": 11517, + "Ġmotivation": 11518, + "Ġdisturbance": 11519, + "rystalline": 11520, + "Ġtaxa": 11521, + "Ġdementia": 11522, + "Ġconcerned": 11523, + "PIO": 11524, + "homogeneous": 11525, + "ĠEv": 11526, + "ĠGeorge": 11527, + "ĠAlgorithms": 11528, + "ickel": 11529, + "usively": 11530, + "Ġcorner": 11531, + "ĠRest": 11532, + "Ġinfinity": 11533, + "ĠTransform": 11534, + "heng": 11535, + "Ġneurode": 11536, + "olim": 11537, + "Íij": 11538, + "Ġskew": 11539, + "ĠBS": 11540, + "score": 11541, + "YPE": 11542, + "eman": 11543, + "elle": 11544, + "ĠCorrelation": 11545, + "Ġcultural": 11546, + "ophosph": 11547, + "Ġattenuation": 11548, + "Ġaggregate": 11549, + "Ġambig": 11550, + "Ġanomalous": 11551, + "Ġtors": 11552, + "Ġplanet": 11553, + "ĠNPs": 11554, + "hr": 11555, + "ĠDivision": 11556, + "ĠEducation": 11557, + "lectic": 11558, + "Ġbrought": 11559, + "ĠMorph": 11560, + "Ġplanes": 11561, + "Ġsugar": 11562, + "Ġdendritic": 11563, + "Ġcontour": 11564, + "Ġcylinder": 11565, + "post": 11566, + "Ġwent": 11567, + "RL": 11568, + "Ġadmission": 11569, + "MSE": 11570, + "IX": 11571, + "Ġdisjoint": 11572, + "Ġannotation": 11573, + "Ġisotope": 11574, + "Ġμν": 11575, + "Ġeliminate": 11576, + "Ġreactor": 11577, + "onents": 11578, + "Ġreasoning": 11579, + "Ġmorbidity": 11580, + "Ġcorrosion": 11581, + "othermal": 11582, + "arctic": 11583, + "ĠMB": 11584, + "ĠZhao": 11585, + "Ġhistological": 11586, + "Ġsuperconducting": 11587, + "attered": 11588, + "Ġhousehold": 11589, + "ĠProp": 11590, + "Ġasser": 11591, + "hered": 11592, + "Ġteams": 11593, + "Ġvanishes": 11594, + "Pre": 11595, + "aments": 11596, + "Ġamorphous": 11597, + "ĠDetermination": 11598, + "missions": 11599, + "Ġoverhead": 11600, + "determ": 11601, + "Ġutilizing": 11602, + "fa": 11603, + "ipolar": 11604, + "Ġformulated": 11605, + "Ġextrap": 11606, + "grid": 11607, + "Ġhumidity": 11608, + "uber": 11609, + "tumor": 11610, + "rous": 11611, + "Ġdistortion": 11612, + "dynamics": 11613, + "ĠLoss": 11614, + "Ġscaled": 11615, + "Ġischemia": 11616, + "Ġaxes": 11617, + "Ġquantit": 11618, + "nit": 11619, + "ĠRegion": 11620, + "ained": 11621, + "Ġfill": 11622, + "Ġbranching": 11623, + "ĠTiss": 11624, + "cross": 11625, + "Ġplatelet": 11626, + "iffiffiffiff": 11627, + "rops": 11628, + "lux": 11629, + "join": 11630, + "uracy": 11631, + "icide": 11632, + "ĠLouis": 11633, + "Ġ": 11634, + "Ġstrings": 11635, + "yset": 11636, + "Ġfacial": 11637, + "ĠMMP": 11638, + "RES": 11639, + "Ġhydrolysis": 11640, + "ĠCanadian": 11641, + "Ġprojective": 11642, + "Ġscatter": 11643, + "uron": 11644, + "ĠPsych": 11645, + "complex": 11646, + "ĠNam": 11647, + "Ġconcurrent": 11648, + "IONS": 11649, + "Ġthous": 11650, + "Ġchance": 11651, + "Ġplacement": 11652, + "Ġawareness": 11653, + "Ġtrib": 11654, + "ĠTex": 11655, + "ĠThird": 11656, + "Ġlabeling": 11657, + "cerol": 11658, + "Ġsaw": 11659, + "ĠBand": 11660, + "ĠPear": 11661, + "Ġpregnant": 11662, + "ĠDown": 11663, + "platin": 11664, + "Seq": 11665, + "xe": 11666, + "ethylene": 11667, + "ĠHigher": 11668, + "Ġreality": 11669, + "uris": 11670, + "ĠPAR": 11671, + "lb": 11672, + "dose": 11673, + "shif": 11674, + "iliar": 11675, + "total": 11676, + "SW": 11677, + "Ġvalve": 11678, + "nder": 11679, + "н": 11680, + "amous": 11681, + "Ġendomet": 11682, + "LISA": 11683, + "Ġfractures": 11684, + "Ġfilt": 11685, + "role": 11686, + "Ġmicrostructure": 11687, + "ĠSNP": 11688, + "TER": 11689, + "ĠZnO": 11690, + "oving": 11691, + "ali": 11692, + "ĠGM": 11693, + "unct": 11694, + "Ġextensions": 11695, + "expression": 11696, + "Ġescape": 11697, + "ĠMas": 11698, + "ĠSpanish": 11699, + "Ġfloor": 11700, + "ĠCommon": 11701, + "otopy": 11702, + "plementation": 11703, + "Ġrhyth": 11704, + "Ġserves": 11705, + "yto": 11706, + "Ġwavelengths": 11707, + "emptyset": 11708, + "ĠHill": 11709, + "nor": 11710, + "ĠElectro": 11711, + "Ġdehydrogen": 11712, + "Ġwhom": 11713, + "imetric": 11714, + "ĠRoman": 11715, + "ĠVe": 11716, + "âī¥": 11717, + "ĠKu": 11718, + "ĠTransfer": 11719, + "Äĩ": 11720, + "ĠTF": 11721, + "brain": 11722, + "coprotein": 11723, + "ĠGreat": 11724, + "aven": 11725, + "ĠIndividual": 11726, + "uri": 11727, + "Ġfungi": 11728, + "Ġparam": 11729, + "pton": 11730, + "symmetry": 11731, + "Ġlock": 11732, + "meas": 11733, + "Ġhaem": 11734, + "Ġhip": 11735, + "Ass": 11736, + "enger": 11737, + "Ġpotassium": 11738, + "anal": 11739, + "ibrary": 11740, + "Ġschools": 11741, + "natal": 11742, + "Ġalleles": 11743, + "ĠHLA": 11744, + "oxygen": 11745, + "ĠCup": 11746, + "Ġpurely": 11747, + "DO": 11748, + "Ġchip": 11749, + "ôı": 11750, + "Car": 11751, + "sil": 11752, + "Ġunlikely": 11753, + "correspond": 11754, + "ĠDP": 11755, + "Ġintense": 11756, + "Ġforcing": 11757, + "ĠJournal": 11758, + "Ġarrow": 11759, + "ocyan": 11760, + "Ġcultiv": 11761, + "Ġblind": 11762, + "Ġselecting": 11763, + "ocarcinoma": 11764, + "rance": 11765, + "Ġhydrophobic": 11766, + "closed": 11767, + "Ġensures": 11768, + "Ġpromoted": 11769, + "Ġdetectable": 11770, + "ranean": 11771, + "Ġschedule": 11772, + "Ġpartly": 11773, + "Ġgland": 11774, + "Ġcouple": 11775, + "ĠEmerg": 11776, + "Ġtraces": 11777, + "poly": 11778, + "Ġprotease": 11779, + "ystic": 11780, + "Ġdocuments": 11781, + "positions": 11782, + "Ġdriver": 11783, + "tium": 11784, + "ĠCYP": 11785, + "close": 11786, + "ĠRecep": 11787, + "Ġpermit": 11788, + "Ġblocked": 11789, + "Ġinvestigating": 11790, + "ĠTumor": 11791, + "ĠBig": 11792, + "Ġwavegu": 11793, + "Ġsubstance": 11794, + "Ġweaker": 11795, + "ĠMont": 11796, + "rovers": 11797, + "ĠMexico": 11798, + "pres": 11799, + "ĠAcute": 11800, + "Ġmicrogl": 11801, + "ĠES": 11802, + "itoring": 11803, + "ĠSeries": 11804, + "lights": 11805, + "Ġhypothesized": 11806, + "Ġconstructs": 11807, + "Ġfiltration": 11808, + "Black": 11809, + "Ġunchanged": 11810, + "Ġobservable": 11811, + "Ġray": 11812, + "between": 11813, + "Ġ": 11814, + "ĠPosition": 11815, + "Ġthi": 11816, + "ĠSystematic": 11817, + "Class": 11818, + "km": 11819, + "ĠTak": 11820, + "Ġrespondents": 11821, + "Ġinnate": 11822, + "Ġant": 11823, + "Ġconnecting": 11824, + "Rel": 11825, + "Ġmanipulation": 11826, + "ĠNeg": 11827, + "NPs": 11828, + "ĠDiab": 11829, + "ĠActive": 11830, + "ĠGall": 11831, + "ĠCoulomb": 11832, + "Ġspacing": 11833, + "ĠFlor": 11834, + "Ġconductance": 11835, + "Ġtracks": 11836, + "ĠZhu": 11837, + "weighted": 11838, + "rocy": 11839, + "Ġfather": 11840, + "idium": 11841, + "structured": 11842, + "ĠTel": 11843, + "Ġstrom": 11844, + "ithub": 11845, + "certain": 11846, + "But": 11847, + "ĠAccess": 11848, + "Ġpreventing": 11849, + "restrial": 11850, + "ĠConsidering": 11851, + "true": 11852, + "Ġhosts": 11853, + "Ġworst": 11854, + "ĠPd": 11855, + "gredi": 11856, + "Ġglycol": 11857, + "Ġstory": 11858, + "osquito": 11859, + "paratus": 11860, + "Ġmeeting": 11861, + "Ġepisode": 11862, + "nc": 11863, + "ĠSand": 11864, + "Ġuint": 11865, + "ynamical": 11866, + "urt": 11867, + "Ġeducational": 11868, + "Ġfocuses": 11869, + "gt": 11870, + "ĠHS": 11871, + "Ġdeterminant": 11872, + "Ġlithium": 11873, + "ĠDigital": 11874, + "Ġguidance": 11875, + "Ġpriority": 11876, + "Ġparty": 11877, + "orial": 11878, + "Two": 11879, + "ĠProblems": 11880, + "Ġseman": 11881, + "ĠCNN": 11882, + "ĠEpid": 11883, + "Ġplaying": 11884, + "Ġelimination": 11885, + "ĠSat": 11886, + "Ġobjectives": 11887, + "plectic": 11888, + "Ġcircumst": 11889, + "ĠGS": 11890, + "ocellular": 11891, + "otrans": 11892, + "Ġfinds": 11893, + "Ġaromatic": 11894, + "izers": 11895, + "Ġfavorable": 11896, + "standard": 11897, + "ichlor": 11898, + "models": 11899, + "otyping": 11900, + "Ġstabilization": 11901, + "Ġhandling": 11902, + "Ġcoated": 11903, + "even": 11904, + "Ġletter": 11905, + "ZE": 11906, + "Ġultrason": 11907, + "Ġfriend": 11908, + "Ġsensiti": 11909, + "Ġattachment": 11910, + "Ġapart": 11911, + "Ġgrey": 11912, + "Ġaircraft": 11913, + "ĠrRNA": 11914, + "Ġenabled": 11915, + "Ġbuff": 11916, + "Ġredox": 11917, + "assisted": 11918, + "Ġgenerality": 11919, + "PSS": 11920, + "Ġelection": 11921, + "response": 11922, + "Ġdedicated": 11923, + "Ġdemographic": 11924, + "Ġimposed": 11925, + "ĠKir": 11926, + "ĠRadio": 11927, + "ĠELISA": 11928, + "gae": 11929, + "Ġresc": 11930, + "ĠRic": 11931, + "raphic": 11932, + "Ġrail": 11933, + "Ġjournal": 11934, + "oler": 11935, + "WS": 11936, + "Ġincorporation": 11937, + "wind": 11938, + "Ġauditory": 11939, + "AE": 11940, + "task": 11941, + "Ġpc": 11942, + "wall": 11943, + "Ġapprec": 11944, + "aterials": 11945, + "Ġpartner": 11946, + "Ġcollective": 11947, + "Ġscoring": 11948, + "ĠFrank": 11949, + "Ġpermanent": 11950, + "ĠIran": 11951, + "umination": 11952, + "Med": 11953, + "ĠHybrid": 11954, + "Ġphenotypic": 11955, + "Ġdisruption": 11956, + "violet": 11957, + "ospheric": 11958, + "Ġregimes": 11959, + "ĠColor": 11960, + "ĠPatient": 11961, + "Ġfever": 11962, + "Ġnn": 11963, + "Ġvariational": 11964, + "keys": 11965, + "Ġdistill": 11966, + "Ġspectroscopic": 11967, + "ĠArchitect": 11968, + "acing": 11969, + "Ġproves": 11970, + "Ġverteb": 11971, + "ĠComputer": 11972, + "Ġexpensive": 11973, + "Ġfrozen": 11974, + "arcoma": 11975, + "NK": 11976, + "Ġhistone": 11977, + "Ġpolymerization": 11978, + "Ġtob": 11979, + "Ġturned": 11980, + "effective": 11981, + "ĠAuthor": 11982, + "API": 11983, + "Ġdecade": 11984, + "ĠRobert": 11985, + "Example": 11986, + "overset": 11987, + "ABLE": 11988, + "ĠBehavior": 11989, + "feed": 11990, + "ĠTai": 11991, + "Ġ": 11992, + "Ġegg": 11993, + "Ġcath": 11994, + "aux": 11995, + "ĠJohnson": 11996, + "Ġtorque": 11997, + "Ġpurification": 11998, + "White": 11999, + "cious": 12000, + "ĠSong": 12001, + "Ġprecipit": 12002, + "reshold": 12003, + "Ġmilitary": 12004, + "Ġconvection": 12005, + "ĠMiddle": 12006, + "ĠWhe": 12007, + "Ġôı": 12008, + "aland": 12009, + "aration": 12010, + "figure": 12011, + "Ġdeduce": 12012, + "chloro": 12013, + "cost": 12014, + "ithmetic": 12015, + "ĠItalian": 12016, + "missible": 12017, + "ĠCommunity": 12018, + "ĠNature": 12019, + "Ġdioxide": 12020, + "Ġbalanced": 12021, + "ett": 12022, + "STAT": 12023, + "ilding": 12024, + "Ġevolved": 12025, + "Ġmonot": 12026, + "pur": 12027, + "Ġpreferences": 12028, + "dinger": 12029, + "Ġargue": 12030, + "Ġmotions": 12031, + "Ġinfant": 12032, + "Ġaccelerated": 12033, + "Ġobserver": 12034, + "Ġfabrication": 12035, + "ĠMechanisms": 12036, + "Ġfunctor": 12037, + "Ġharves": 12038, + "rase": 12039, + "ĠSpecial": 12040, + "Ġdeposits": 12041, + "Ġrub": 12042, + "à¸": 12043, + "ĠCPU": 12044, + "ĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ": 12045, + "atomical": 12046, + "Ġfinit": 12047, + "Ġsecure": 12048, + "Ġnutritional": 12049, + "renal": 12050, + "ĠFalse": 12051, + "Ġshel": 12052, + "Ġrecruited": 12053, + "ambig": 12054, + "ĠSignaling": 12055, + "KO": 12056, + "organisms": 12057, + "ĠLT": 12058, + "elen": 12059, + "ĠMarc": 12060, + "abatic": 12061, + "Ġtables": 12062, + "Ġconfined": 12063, + "ĠAz": 12064, + "Ġproductivity": 12065, + "Ġadherence": 12066, + "Ġreplicates": 12067, + "Ġvirt": 12068, + "fin": 12069, + "Ġagricultural": 12070, + "ĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ": 12071, + "ĠChampionship": 12072, + "anda": 12073, + "ĠChurch": 12074, + "During": 12075, + "Ġinserted": 12076, + "ighter": 12077, + "Ġxen": 12078, + "Ġsave": 12079, + "Ġtangent": 12080, + "venous": 12081, + "Ġconverge": 12082, + "Ġdistinguished": 12083, + "Ġexplos": 12084, + "Ġaortic": 12085, + "Ġjump": 12086, + "Ġneonatal": 12087, + "udden": 12088, + "Ġslower": 12089, + "Ġinfarction": 12090, + "Ġprevents": 12091, + "uer": 12092, + "Ġeros": 12093, + "RP": 12094, + "Ġcontinues": 12095, + "ORT": 12096, + "Ġconsiders": 12097, + "ĠNuclear": 12098, + "lymp": 12099, + "Ġaccounted": 12100, + "oresis": 12101, + "Ġneighboring": 12102, + "ĠRichard": 12103, + "Ġenfor": 12104, + "ĠChronic": 12105, + "Ġdiscover": 12106, + "ĠHong": 12107, + "cells": 12108, + "ĠChall": 12109, + "Ġhomogen": 12110, + "Ġatheros": 12111, + "Ġisolate": 12112, + "ĠPlasma": 12113, + "ĠDL": 12114, + "parametric": 12115, + "ĠUpper": 12116, + "HP": 12117, + "Ġintroduces": 12118, + "Ġmothers": 12119, + "Ġattract": 12120, + "Ġexclusion": 12121, + "gravity": 12122, + "ĠKr": 12123, + "Ġspike": 12124, + "ĠHeat": 12125, + "vival": 12126, + "ĠRNAs": 12127, + "bach": 12128, + "atorial": 12129, + "ĠLtd": 12130, + "onomy": 12131, + "invasive": 12132, + "lass": 12133, + "Ġwells": 12134, + "Ġimaginary": 12135, + "Ġcarbohyd": 12136, + "oda": 12137, + "Ġactivate": 12138, + "µĦ": 12139, + "Ġenzymatic": 12140, + "pes": 12141, + "Ġstatements": 12142, + "Ġapproximated": 12143, + "ĠSalmon": 12144, + "ophageal": 12145, + "ĠHPV": 12146, + "conf": 12147, + "umat": 12148, + "Ġsulfur": 12149, + "ĠRecall": 12150, + "Ġchond": 12151, + "Ġviable": 12152, + "poration": 12153, + "Ġcarefully": 12154, + "tetra": 12155, + "Ġlymphoma": 12156, + "stat": 12157, + "Ġconservative": 12158, + "atabase": 12159, + "mand": 12160, + "Ġscored": 12161, + "Ġvas": 12162, + "Ġprivacy": 12163, + "onymous": 12164, + "Ġlogarithmic": 12165, + "ĠEcon": 12166, + "Ġachieves": 12167, + "Ġabundances": 12168, + "cam": 12169, + "Ġcyan": 12170, + "ĠEL": 12171, + "idelity": 12172, + "jo": 12173, + "Ġanticip": 12174, + "reported": 12175, + "Ġarrangement": 12176, + "iterranean": 12177, + "psis": 12178, + "ichi": 12179, + "Ġta": 12180, + "umping": 12181, + "ĠActivation": 12182, + "Ġmelt": 12183, + "Ġanno": 12184, + "oge": 12185, + "ĠDam": 12186, + "optimal": 12187, + "Ġneurological": 12188, + "sa": 12189, + "ĠParameters": 12190, + "offset": 12191, + "Ġcement": 12192, + "Ġinhibiting": 12193, + "Ġchose": 12194, + "itzer": 12195, + "attr": 12196, + "Ġmoder": 12197, + "atories": 12198, + "Ġteaching": 12199, + "ĠCore": 12200, + "phthal": 12201, + "ĠLuc": 12202, + "Ġingredi": 12203, + "Ġclearance": 12204, + "Ġachieving": 12205, + "tage": 12206, + "Ġburst": 12207, + "vie": 12208, + "ĠSpain": 12209, + "pto": 12210, + "Ġtransmembrane": 12211, + "Ġsupplementary": 12212, + "Ġtoken": 12213, + "Ġobviously": 12214, + "ĠVector": 12215, + "Ġdestr": 12216, + "HOD": 12217, + "Ġassumes": 12218, + "Ġpenetration": 12219, + "Ġsubjective": 12220, + "holds": 12221, + "ão": 12222, + "Ġmotiv": 12223, + "Ġproviders": 12224, + "vascular": 12225, + "Ġdepartment": 12226, + "ocket": 12227, + "File": 12228, + "Ġbreath": 12229, + "ĠBest": 12230, + "grable": 12231, + "Ġliqu": 12232, + "ĠArg": 12233, + "ĠBob": 12234, + "Ġfragmentation": 12235, + "ectic": 12236, + "Ġvital": 12237, + "since": 12238, + "alloc": 12239, + "oxyphenyl": 12240, + "Ġradiotherapy": 12241, + "ĠSDS": 12242, + "Ġcytometry": 12243, + "nucle": 12244, + "ĠIM": 12245, + "ĠTeV": 12246, + "rafish": 12247, + "ĠKorea": 12248, + "Ġstrengthen": 12249, + "Ġbare": 12250, + "Ġwoman": 12251, + "Ġradar": 12252, + "Ġplatforms": 12253, + "ozygous": 12254, + "ĠAh": 12255, + "Ġsubtypes": 12256, + "pyrid": 12257, + "ĠTranscription": 12258, + "Ġáº": 12259, + "ĠMeasurements": 12260, + "Ġsurviv": 12261, + "ĠNear": 12262, + "Ġcascade": 12263, + "outhe": 12264, + "BU": 12265, + "Ġexponentially": 12266, + "Ġhazard": 12267, + "ĠsiRNA": 12268, + "Ġcellulose": 12269, + "Figs": 12270, + "Ġdifferentiated": 12271, + "Ġimplicated": 12272, + "metric": 12273, + "Ġcorrelate": 12274, + "Ġmission": 12275, + "Ġmantle": 12276, + "ĠPhyl": 12277, + "ĠHart": 12278, + "Ġgases": 12279, + "Ġunity": 12280, + "Ġexpert": 12281, + "Ġchart": 12282, + "Ġdict": 12283, + "Ġepile": 12284, + "Ġoffspring": 12285, + "Ġemerged": 12286, + "Ġdemands": 12287, + "Ġpresum": 12288, + "orbid": 12289, + "ĠMedicine": 12290, + "Ġstreams": 12291, + "ticed": 12292, + "ĠNic": 12293, + "Ġfilling": 12294, + "ĠCro": 12295, + "Ġrestrictions": 12296, + "See": 12297, + "ĠMill": 12298, + "Ġparental": 12299, + "Ġdeterminants": 12300, + "Ġecosystem": 12301, + "ĠWall": 12302, + "ĠMemory": 12303, + "plets": 12304, + "Ġaggregates": 12305, + "perturb": 12306, + "Ġresidents": 12307, + "ACK": 12308, + "vectors": 12309, + "Ġmanually": 12310, + "Ġïĺ": 12311, + "ĠFramework": 12312, + "Ġvag": 12313, + "ebrafish": 12314, + "lib": 12315, + "ĠHeart": 12316, + "ĠAnimal": 12317, + "Ġwider": 12318, + "Gene": 12319, + "ĠRos": 12320, + "Ġoperate": 12321, + "Ġpossibilities": 12322, + "ĠStrong": 12323, + "Ġpyro": 12324, + "respectively": 12325, + "Ġhybridization": 12326, + "ipedia": 12327, + "xin": 12328, + "Ġstom": 12329, + "fish": 12330, + "ĠForce": 12331, + "Ġdimer": 12332, + "SUL": 12333, + "else": 12334, + "Ġunde": 12335, + "gar": 12336, + "conv": 12337, + "Ġarrival": 12338, + "Ġmonoclonal": 12339, + "IAL": 12340, + "Ġly": 12341, + "Ġsymmetries": 12342, + "Ġnursing": 12343, + "rach": 12344, + "ĠóµĦ": 12345, + "Ġbiased": 12346, + "Ġcues": 12347, + "Ġbiomarker": 12348, + "ders": 12349, + "Ġcrow": 12350, + "ernels": 12351, + "Ġbilateral": 12352, + "Ġphysically": 12353, + "Ġpatches": 12354, + "Ġuncon": 12355, + "ĠBefore": 12356, + "default": 12357, + "estyle": 12358, + "tfrac": 12359, + "ĠCox": 12360, + "Ġinfiltration": 12361, + "Ġconvert": 12362, + "Ġstrengths": 12363, + "ĠSar": 12364, + "igible": 12365, + "ocomp": 12366, + "Ġstir": 12367, + "Ġschizophrenia": 12368, + "was": 12369, + "Ġow": 12370, + "eterm": 12371, + "ĠOrder": 12372, + "Ġfoss": 12373, + "Ġlineage": 12374, + "Ġrabbit": 12375, + "Ġregularization": 12376, + "ranch": 12377, + "oplastic": 12378, + "TO": 12379, + "Ġmeasurable": 12380, + "Ġmang": 12381, + "initial": 12382, + "Ġbuildings": 12383, + "Ġsystematically": 12384, + "Ġfermions": 12385, + "Ġlibraries": 12386, + "Ġablation": 12387, + "ideos": 12388, + "ĠWi": 12389, + "photon": 12390, + "ĠTesting": 12391, + "ĠComputing": 12392, + "tier": 12393, + "inet": 12394, + "Ġprimitive": 12395, + "Ġcapillary": 12396, + "Ġslip": 12397, + "vergence": 12398, + "rapeutic": 12399, + "ĠBlue": 12400, + "ĠAcad": 12401, + "hai": 12402, + "ĠLew": 12403, + "Ġtriangular": 12404, + "MSO": 12405, + "Ġsalinity": 12406, + "Ġnanocom": 12407, + "oa": 12408, + "Ġhomomorphism": 12409, + "ĠMM": 12410, + "Ġresin": 12411, + "DB": 12412, + "uminescence": 12413, + "dashed": 12414, + "ĠKh": 12415, + "quark": 12416, + "embles": 12417, + "Ġidentifies": 12418, + "Ġfollic": 12419, + "Ġmetam": 12420, + "ĠHerm": 12421, + "Ġtobacco": 12422, + "Ġrealization": 12423, + "hydrox": 12424, + "ĠBet": 12425, + "Because": 12426, + "Ġpieces": 12427, + "Ġtalk": 12428, + "Ġopened": 12429, + "asome": 12430, + "Ġsurge": 12431, + "Ġfluctuation": 12432, + "github": 12433, + "ĠBacter": 12434, + "Ġbinds": 12435, + "ĠRapid": 12436, + "auer": 12437, + "pH": 12438, + "embed": 12439, + "ĠDoc": 12440, + "uchi": 12441, + "ĠCandid": 12442, + "Ġrarely": 12443, + "Ġmountain": 12444, + "ĠFat": 12445, + "Ġsend": 12446, + "ovsk": 12447, + "ĠOrganization": 12448, + "ĠFranc": 12449, + "ĠOP": 12450, + "âμ": 12451, + "okes": 12452, + "ece": 12453, + "deficient": 12454, + "Ġlinkage": 12455, + "odon": 12456, + "Ġfly": 12457, + "Ġtidal": 12458, + "ĠExamples": 12459, + "ĠRout": 12460, + "Ġaccommod": 12461, + "Suppose": 12462, + "adap": 12463, + "Ġdie": 12464, + "root": 12465, + "Ġhon": 12466, + "Ġminimizing": 12467, + "Ġroughness": 12468, + "Ġgrass": 12469, + "enta": 12470, + "ĠLang": 12471, + "edu": 12472, + "ĠSimple": 12473, + "enic": 12474, + "Ġinducing": 12475, + "tf": 12476, + "Ġcontexts": 12477, + "ĠGeneralized": 12478, + "ĠWnt": 12479, + "Pb": 12480, + "atomic": 12481, + "dem": 12482, + "ĠPreparation": 12483, + "Ġinsufficient": 12484, + "sam": 12485, + "ĠSpecies": 12486, + "ĠSolar": 12487, + "Ġunsigned": 12488, + "ĠHER": 12489, + "âĬ": 12490, + "Ġparity": 12491, + "Ġnitrate": 12492, + "ĠCer": 12493, + "ptic": 12494, + "identif": 12495, + "geal": 12496, + "Ġemotion": 12497, + "ĠLP": 12498, + "Ġenhancing": 12499, + "Ġmeaningful": 12500, + "station": 12501, + "Ġrelig": 12502, + "yo": 12503, + "Ġperspectives": 12504, + "Ġscans": 12505, + "uginosa": 12506, + "Ġsummarize": 12507, + "relations": 12508, + "Ġdistant": 12509, + "Ġfunctionality": 12510, + "Ġdeeper": 12511, + "olate": 12512, + "ĠPor": 12513, + "graphs": 12514, + "ĠWa": 12515, + "ophilic": 12516, + "CLUS": 12517, + "ropathy": 12518, + "Ġcred": 12519, + "Ġuniversity": 12520, + "seg": 12521, + "vee": 12522, + "OG": 12523, + "ĠMen": 12524, + "ĠCritical": 12525, + "ãģ": 12526, + "Ġexit": 12527, + "vartheta": 12528, + "ĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ": 12529, + "Ġunf": 12530, + "Ġproposal": 12531, + "Ġtyrosine": 12532, + "otides": 12533, + "Ġproximity": 12534, + "Ġboxes": 12535, + "caten": 12536, + "ĠEnvironmental": 12537, + "bounded": 12538, + "downarrow": 12539, + "Ġfalls": 12540, + "Ġfertil": 12541, + "Ġcomprised": 12542, + "Ġmellitus": 12543, + "Ġleakage": 12544, + "uty": 12545, + "Ġchromosomes": 12546, + "ĠStatistics": 12547, + "%%%%": 12548, + "Ġcombinator": 12549, + "Ġket": 12550, + "advant": 12551, + "Ther": 12552, + "Ġtopics": 12553, + "flat": 12554, + "nia": 12555, + "ĠSpectral": 12556, + "Ġsynchronization": 12557, + "varrho": 12558, + "Ġcolonies": 12559, + "ĠFive": 12560, + "agues": 12561, + "ĠFC": 12562, + "IDS": 12563, + "Ġaward": 12564, + "Ġyielding": 12565, + "Ġarchitectures": 12566, + "ashington": 12567, + "chitz": 12568, + "perty": 12569, + "Ġmoduli": 12570, + "moment": 12571, + "speed": 12572, + "Ġmesenchymal": 12573, + "optera": 12574, + "Ġincomp": 12575, + "Cell": 12576, + "ĠMice": 12577, + "Ġgot": 12578, + "teger": 12579, + "Ġtau": 12580, + "ĠAdS": 12581, + "Ġbill": 12582, + "Ġdrinking": 12583, + "ulsive": 12584, + "Ġknockdown": 12585, + "Ġarms": 12586, + "ĠAutom": 12587, + "ĠIncreased": 12588, + "HF": 12589, + "Ġglobally": 12590, + "Ġdoping": 12591, + "Ġath": 12592, + "ĠCop": 12593, + "Ġsuccessive": 12594, + "ULT": 12595, + "eless": 12596, + "Ġbleeding": 12597, + "Ġfoods": 12598, + "Ġimmunohist": 12599, + "Ġdefinite": 12600, + "ĠJones": 12601, + "ĠTS": 12602, + "Ġjoined": 12603, + "ĠTowards": 12604, + "ĠCs": 12605, + "Ġunlike": 12606, + "Ġvalence": 12607, + "dor": 12608, + "oS": 12609, + "Ġpush": 12610, + "Ġoffice": 12611, + "Ġaluminum": 12612, + "idyl": 12613, + "idirectional": 12614, + "written": 12615, + "Ġbubble": 12616, + "HI": 12617, + "Ġmarkedly": 12618, + "ĠTok": 12619, + "Ġvesicles": 12620, + "Ġquotient": 12621, + "Ġreproduce": 12622, + "Ġelsewhere": 12623, + "ĠMyc": 12624, + "Ġinfrastructure": 12625, + "Ġgained": 12626, + "abel": 12627, + "ĠSex": 12628, + "ĠTables": 12629, + "etin": 12630, + "Ġhomolog": 12631, + "Ġlegal": 12632, + "hea": 12633, + "Ġsociety": 12634, + "Ġmanaged": 12635, + "idase": 12636, + "ĠInhibition": 12637, + "Ġparasite": 12638, + "Ġvolunte": 12639, + "ATP": 12640, + "ios": 12641, + "Ġsepsis": 12642, + "Ġribosomal": 12643, + "Ġconfound": 12644, + "ĠStaphyl": 12645, + "aryngeal": 12646, + "ïĢ": 12647, + "comb": 12648, + "ĠObjective": 12649, + "SULTS": 12650, + "Ġthorough": 12651, + "mt": 12652, + "Ġchest": 12653, + "Vector": 12654, + "element": 12655, + "Ġvirulence": 12656, + "Ġhemisp": 12657, + "Ġsought": 12658, + "ĠKo": 12659, + "Ġnutrition": 12660, + "uling": 12661, + "iana": 12662, + "Ġprototype": 12663, + "ĠOnt": 12664, + "cine": 12665, + "Ġdotted": 12666, + "Ġobese": 12667, + "ountered": 12668, + "Ġphysicians": 12669, + "Ġmini": 12670, + "Ľľ": 12671, + "spaces": 12672, + "Ġexclusively": 12673, + "ĠConvolution": 12674, + "Ġcaspase": 12675, + "ĠLink": 12676, + "div": 12677, + "ĠRoyal": 12678, + "hist": 12679, + "itness": 12680, + "Ġester": 12681, + "Ġconducting": 12682, + "Ġparticipated": 12683, + "Ġairway": 12684, + "Ġaeruginosa": 12685, + "Ext": 12686, + "argument": 12687, + "ocking": 12688, + "Ġintegrate": 12689, + "Ġcontrovers": 12690, + "apes": 12691, + "training": 12692, + "ĠPrevalence": 12693, + "temp": 12694, + "both": 12695, + "Ġreactivity": 12696, + "Ġranking": 12697, + "Ġtunneling": 12698, + "ODE": 12699, + "ĠMediterranean": 12700, + "Ġresonances": 12701, + "Mg": 12702, + "Ġlib": 12703, + "ĠHeter": 12704, + "Ġnothing": 12705, + "Ġindication": 12706, + "ĠHM": 12707, + "ocytic": 12708, + "strand": 12709, + "Ġcollaboration": 12710, + "Ġelectrostatic": 12711, + "Ġindependence": 12712, + "hab": 12713, + "Ġconflic": 12714, + "Ġiod": 12715, + "inus": 12716, + "Ġdependency": 12717, + "ĠLam": 12718, + "Ġexamining": 12719, + "Ġoccupied": 12720, + "Ġqueue": 12721, + "ĠBul": 12722, + "Ġregistered": 12723, + "Ġindividually": 12724, + "Rx": 12725, + "ausal": 12726, + "VE": 12727, + "Ġbrightness": 12728, + "respons": 12729, + "balance": 12730, + "Ġcytotoxic": 12731, + "fall": 12732, + "commut": 12733, + "ICAL": 12734, + "uran": 12735, + "aining": 12736, + "raulic": 12737, + "results": 12738, + "Ġepisodes": 12739, + "YS": 12740, + "ĠGar": 12741, + "Ġsurfact": 12742, + "drug": 12743, + "Ġcities": 12744, + "ĠChange": 12745, + "osition": 12746, + "Ġtriggered": 12747, + "Ġcytoplasmic": 12748, + "erves": 12749, + "Ġlex": 12750, + "Ġasymptotically": 12751, + "phy": 12752, + "Ġfrontal": 12753, + "ĠDensity": 12754, + "Ġsynerg": 12755, + "cycle": 12756, + "ĠImproved": 12757, + "ø": 12758, + "Ġmono": 12759, + "Ġaccumulated": 12760, + "oriented": 12761, + "bour": 12762, + "Ġtunnel": 12763, + "coming": 12764, + "Ġapparatus": 12765, + "Ġencountered": 12766, + "Cre": 12767, + "Ġletters": 12768, + "etch": 12769, + "Ġexcessive": 12770, + "Ġbiofilm": 12771, + "Ġrearrang": 12772, + "Ġpolymorphisms": 12773, + "erobic": 12774, + "Ġconnect": 12775, + "resolved": 12776, + "ĠNN": 12777, + "Ġretro": 12778, + "ĠIniti": 12779, + "ĠQuantif": 12780, + "Ġpup": 12781, + "Tensor": 12782, + "Ġsentences": 12783, + "lay": 12784, + "rants": 12785, + "ploid": 12786, + "ĠAnderson": 12787, + "Ġdesirable": 12788, + "stud": 12789, + "iability": 12790, + "Ġdrying": 12791, + "ecess": 12792, + "Ġdens": 12793, + "Ġdescript": 12794, + "ĠËĨ": 12795, + "Ġclones": 12796, + "Ġjuven": 12797, + "bp": 12798, + "Ġkil": 12799, + "HL": 12800, + "Ġhemorrh": 12801, + "ĠKi": 12802, + "How": 12803, + "Ġenerge": 12804, + "Ġsubsection": 12805, + "ĠSac": 12806, + "dial": 12807, + "Ġcardiomy": 12808, + "Ġtouch": 12809, + "dm": 12810, + "Ġscienti": 12811, + "oides": 12812, + "ĠÃĤ": 12813, + "ysaccharide": 12814, + "Ġsclerosis": 12815, + "ĠZealand": 12816, + "inine": 12817, + "Ġunusual": 12818, + "ĠBA": 12819, + "ipschitz": 12820, + "gap": 12821, + "ĠDifferences": 12822, + "Ġduality": 12823, + "edical": 12824, + "Ġlign": 12825, + "Ġfails": 12826, + "Ġlect": 12827, + "Ġrelate": 12828, + "Ġincorrect": 12829, + "Ġspecify": 12830, + "Ġcylindrical": 12831, + "ĠPF": 12832, + "ĠLind": 12833, + "Ġdeterior": 12834, + "Ġherb": 12835, + "dz": 12836, + "Ġweld": 12837, + "Ġnominal": 12838, + "copy": 12839, + "Ġacetyl": 12840, + "html": 12841, + "Ġrecognize": 12842, + "***": 12843, + "itian": 12844, + "WA": 12845, + "ĠMN": 12846, + "ĠFind": 12847, + "Ġauthentic": 12848, + "perture": 12849, + "Ġcytotoxicity": 12850, + "ofl": 12851, + "ĠGet": 12852, + "Ġcohomology": 12853, + "Ġremainder": 12854, + "Ġexpanding": 12855, + "Ġheav": 12856, + "osterone": 12857, + "Right": 12858, + "Ġcopol": 12859, + "Ġshed": 12860, + "Ġcompliance": 12861, + "Ġacidic": 12862, + "oric": 12863, + "Ġamyloid": 12864, + "Ġevaporation": 12865, + "dl": 12866, + "Ġdelays": 12867, + "Po": 12868, + "ĠCHECK": 12869, + "tains": 12870, + "Ġreversed": 12871, + "ĠMPa": 12872, + "Ġprocessor": 12873, + "Ġhall": 12874, + "ĠLast": 12875, + "Ġplasm": 12876, + "ĠAssociated": 12877, + "ĠBasic": 12878, + "inos": 12879, + "Ġsymptom": 12880, + "ãĢ": 12881, + "Ġanthrop": 12882, + "Ġjudg": 12883, + "Ġeti": 12884, + "kle": 12885, + "Ġwrong": 12886, + "room": 12887, + "Ġdevelopments": 12888, + "ĠMaximum": 12889, + "Ġcoatings": 12890, + "Ġheuristic": 12891, + "rontal": 12892, + "Some": 12893, + "Ġutilize": 12894, + "ĠâĪħ": 12895, + "coll": 12896, + "ĠRelated": 12897, + "Ġdegeneration": 12898, + "template": 12899, + "Ġmodulated": 12900, + "Ġparametri": 12901, + "Ġsaliv": 12902, + "ĠPseudomonas": 12903, + "Ġantigens": 12904, + "Ġharmon": 12905, + "ĠLHC": 12906, + "doi": 12907, + "ensitive": 12908, + "ĠNotice": 12909, + "ĠMoh": 12910, + "tilage": 12911, + "ACS": 12912, + "Ġdiscrepancy": 12913, + "Ġspik": 12914, + "Ġrestrict": 12915, + "itrile": 12916, + "leg": 12917, + "ĠBase": 12918, + "Ġconvolutional": 12919, + "ĠResistance": 12920, + "Ġappearing": 12921, + "ĠImages": 12922, + "ĠMann": 12923, + "Ġreact": 12924, + "Ġmacrophage": 12925, + "Ġwavelet": 12926, + "ochrom": 12927, + "Ġfairly": 12928, + "Ġpreceding": 12929, + "Ġspir": 12930, + "network": 12931, + "ĠNak": 12932, + "IFT": 12933, + "Ġago": 12934, + "Ġencryp": 12935, + "ald": 12936, + "ensin": 12937, + "Ġsulph": 12938, + "ĠPolymer": 12939, + "ĠArt": 12940, + "Ġsubunits": 12941, + "shot": 12942, + "Ġbegins": 12943, + "Ġexer": 12944, + "propto": 12945, + "Ġnurses": 12946, + "Ġsuffices": 12947, + "Ġgraded": 12948, + "ĠRock": 12949, + "Ġuniquely": 12950, + "itol": 12951, + "Ġspiral": 12952, + "Ġthanks": 12953, + "character": 12954, + "ĠDistributed": 12955, + "ĠCart": 12956, + "Form": 12957, + "Ġformulations": 12958, + "ictionary": 12959, + "Ġspreading": 12960, + "Ġsingularity": 12961, + "Ġpigs": 12962, + "itu": 12963, + "otrophic": 12964, + "ÑĢ": 12965, + "Ġsemiconductor": 12966, + "Ġdrag": 12967, + "next": 12968, + "maxim": 12969, + "unn": 12970, + "Ġargued": 12971, + "plastic": 12972, + "Ġdehydrogenase": 12973, + "Ġreinforcement": 12974, + "entral": 12975, + "ĠDS": 12976, + "Ġcompanies": 12977, + "Ġquantization": 12978, + "ĠDri": 12979, + "Ġsimpler": 12980, + "Ġradii": 12981, + "ĠEthics": 12982, + "ĠElectronic": 12983, + "taken": 12984, + "Ġpharmacological": 12985, + "pson": 12986, + "Ġpairing": 12987, + "Ġnest": 12988, + "ĠRS": 12989, + "Ġlic": 12990, + "ocon": 12991, + "Ġobserving": 12992, + "ĠFM": 12993, + "IES": 12994, + "Ġsubmitted": 12995, + "ĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ": 12996, + "Ġnoisy": 12997, + "Ġvanishing": 12998, + "ĠTechnologies": 12999, + "ilst": 13000, + "agic": 13001, + "Ġembeddings": 13002, + "Ġplans": 13003, + "reak": 13004, + "oct": 13005, + "Ġepithelium": 13006, + "Ġreversible": 13007, + "Ġrequests": 13008, + "Vi": 13009, + "ĠProg": 13010, + "methoxy": 13011, + "uria": 13012, + "Ġslice": 13013, + "Ġmetastases": 13014, + "ĠMary": 13015, + "Ġpriori": 13016, + "Ġexplains": 13017, + "ĠSigma": 13018, + "ĠArmy": 13019, + "Ġprey": 13020, + "KL": 13021, + "ĠPass": 13022, + "Ġreproduction": 13023, + "Ġfermentation": 13024, + "ulo": 13025, + "Ġproofs": 13026, + "ĠAccordingly": 13027, + "tist": 13028, + "ĠïĢ©": 13029, + "Ġmeat": 13030, + "Ġplanned": 13031, + "Ġangiogenesis": 13032, + "WR": 13033, + "ĠAust": 13034, + "Similarly": 13035, + "ĠWashington": 13036, + "Ġrefinement": 13037, + "Ġembryo": 13038, + "Ġdissociation": 13039, + "án": 13040, + "plasia": 13041, + "ĠGro": 13042, + "Ġsimilarities": 13043, + "Ġsolubility": 13044, + "Ġimmobil": 13045, + "ĠScot": 13046, + "ĠSubsequently": 13047, + "divid": 13048, + "Ġclosest": 13049, + "ĠWat": 13050, + "ĠâĮ": 13051, + "ĠAGN": 13052, + "Ġprescribed": 13053, + "Ġmosquito": 13054, + "Ġfirm": 13055, + "Ġdegenerate": 13056, + "Ġethyl": 13057, + "Ġharvest": 13058, + "ĠSpecific": 13059, + "Ġcompartment": 13060, + "public": 13061, + "ĠBiological": 13062, + "Ġpiece": 13063, + "Ġattitudes": 13064, + "Ġspray": 13065, + "ĠSix": 13066, + "Ġprofessionals": 13067, + "Ġslot": 13068, + "Ġretrieved": 13069, + "vement": 13070, + "Ġexecuted": 13071, + "seed": 13072, + "Ġoutflow": 13073, + "distance": 13074, + "ĠTerm": 13075, + "ady": 13076, + "ĠProvince": 13077, + "ĠCentre": 13078, + "ĠDFT": 13079, + "Ġsudden": 13080, + "Ġseiz": 13081, + "rat": 13082, + "romo": 13083, + "otechn": 13084, + "Ġhighlights": 13085, + "Ġelectrolyte": 13086, + "ĠAdvanced": 13087, + "allow": 13088, + "px": 13089, + "osed": 13090, + "subarray": 13091, + "racks": 13092, + "PRO": 13093, + "ogeny": 13094, + "Ġpooled": 13095, + "Ġdtype": 13096, + "Ġopposed": 13097, + "ĠGrand": 13098, + "Ġdesigning": 13099, + "bel": 13100, + "itability": 13101, + "Ġminimization": 13102, + "Ġdramatically": 13103, + "Ġsoy": 13104, + "agents": 13105, + "ĠMetal": 13106, + "ĠMV": 13107, + "ribute": 13108, + "DD": 13109, + "itan": 13110, + "Ġspeeds": 13111, + "Ġmarried": 13112, + "Ġevaluations": 13113, + "ĠKingdom": 13114, + "Ġclay": 13115, + "ĠTissue": 13116, + "leftarrow": 13117, + "Ġcompensation": 13118, + "child": 13119, + "pool": 13120, + "uparrow": 13121, + "ĠDomain": 13122, + "species": 13123, + "Ġmethane": 13124, + "ĠEGFR": 13125, + "Ġparser": 13126, + "have": 13127, + "Ġneglected": 13128, + "func": 13129, + "apsed": 13130, + "Ġsays": 13131, + "adata": 13132, + "binom": 13133, + "Case": 13134, + "Ġreporter": 13135, + "Sn": 13136, + "Ġmaximize": 13137, + "Ġbifurc": 13138, + "ĠCNS": 13139, + "ĠOlymp": 13140, + "Ġdeclare": 13141, + "Ġencoder": 13142, + "Ġabelian": 13143, + "Ġsingularities": 13144, + "Ġech": 13145, + "Ψ": 13146, + "Ġproto": 13147, + "Ġphag": 13148, + "Ġpolyg": 13149, + "Ġbott": 13150, + "Ġadipose": 13151, + "uing": 13152, + "jk": 13153, + "uchy": 13154, + "ĠStudent": 13155, + "Ġnanow": 13156, + "Ġthym": 13157, + "Ed": 13158, + "End": 13159, + "Ġtransforms": 13160, + "ĠPCA": 13161, + "kern": 13162, + "regn": 13163, + "Ġcomment": 13164, + "ĠLL": 13165, + "elles": 13166, + "Ġengagement": 13167, + "ĠPeter": 13168, + "ISPR": 13169, + "ĠChannel": 13170, + "iny": 13171, + "Ġbundles": 13172, + "Ald": 13173, + "Ġpublications": 13174, + "TG": 13175, + "stra": 13176, + "Ġfear": 13177, + "Ġretic": 13178, + "plements": 13179, + "Ġcorpus": 13180, + "ĠCluster": 13181, + "ĠRate": 13182, + "Ġsimplest": 13183, + "acic": 13184, + "rbrack": 13185, + "Ġblow": 13186, + "Ġcompress": 13187, + "ĠDark": 13188, + "Ġpsychiatric": 13189, + "ĠConversely": 13190, + "Ġowing": 13191, + "Ġabsor": 13192, + "ĠHP": 13193, + "Ġcrude": 13194, + "equal": 13195, + "ĠArray": 13196, + "ĠRelative": 13197, + "Ġcombustion": 13198, + "Red": 13199, + "kt": 13200, + "ĠmA": 13201, + "Ġtex": 13202, + "porters": 13203, + "Ġdiffered": 13204, + "Ġaudio": 13205, + "zon": 13206, + "odi": 13207, + "Ġmacroscopic": 13208, + "acin": 13209, + "Ġzeros": 13210, + "Ġforeign": 13211, + "Ġduct": 13212, + "bow": 13213, + "worth": 13214, + "ĠRoad": 13215, + "rey": 13216, + "aceous": 13217, + "Ġblast": 13218, + "Ġgranul": 13219, + "Ġwing": 13220, + "Ġannotated": 13221, + "ĠFull": 13222, + "Ġinfluencing": 13223, + "vy": 13224, + "iazol": 13225, + "Ġpitch": 13226, + "Ġrehabilitation": 13227, + "ĠPrior": 13228, + "comit": 13229, + "mathtt": 13230, + "dia": 13231, + "ĠIon": 13232, + "Ġabuse": 13233, + "Ġharvested": 13234, + "Ġepidemic": 13235, + "Ġfilament": 13236, + "Ġnucleation": 13237, + "ĠKnowledge": 13238, + "rinos": 13239, + "Ġbent": 13240, + "Ġsquared": 13241, + "Ġhippocampal": 13242, + "ĠTG": 13243, + "ANT": 13244, + "modified": 13245, + "ario": 13246, + "ĠFace": 13247, + "Ġgrows": 13248, + "Ġfaults": 13249, + "virus": 13250, + "Ġpartitioning": 13251, + "airs": 13252, + "Ġhearing": 13253, + "Ġcongen": 13254, + "Ġrip": 13255, + "ĠCollabor": 13256, + "Ġinterviews": 13257, + "Ġhuge": 13258, + "Ġbreakdown": 13259, + "Ġmonthly": 13260, + "ĠCONCLUS": 13261, + "Each": 13262, + "Diff": 13263, + "Ġrelay": 13264, + "ĠMuse": 13265, + "oscopy": 13266, + "Ġrenew": 13267, + "gb": 13268, + "Ġbrid": 13269, + "Ġoutlined": 13270, + "orig": 13271, + "eat": 13272, + "ĠWithout": 13273, + "Ġspor": 13274, + "ĠTN": 13275, + "ĠJo": 13276, + "ĠAU": 13277, + "Not": 13278, + "Ġretin": 13279, + "ĠAngel": 13280, + "Ġtried": 13281, + "eyond": 13282, + "je": 13283, + "ĠRussian": 13284, + "ĠUnfortunately": 13285, + "ĠMeanwhile": 13286, + "ographs": 13287, + "Ġaccounting": 13288, + "ĠAβ": 13289, + "mb": 13290, + "Ġdopamine": 13291, + "ĠBriefly": 13292, + "ĠFrequency": 13293, + "Matrix": 13294, + "ĠJoseph": 13295, + "Ġexperts": 13296, + "Ġdrops": 13297, + "ĠRESULTS": 13298, + "Ġrectangular": 13299, + "athione": 13300, + "center": 13301, + "ĠLeft": 13302, + "inform": 13303, + "kins": 13304, + "Ġmil": 13305, + "ĠMah": 13306, + "Ġmedial": 13307, + "ĠCompany": 13308, + "Ġpassage": 13309, + "Ġleader": 13310, + "Ġscreened": 13311, + "eri": 13312, + "posites": 13313, + "rarily": 13314, + "Ġphone": 13315, + "ietic": 13316, + "Ġexpectations": 13317, + "ĠParticle": 13318, + "ĠMountain": 13319, + "Ġinterleukin": 13320, + "Ġfifth": 13321, + "Ġvast": 13322, + "Ġlogical": 13323, + "Ġterr": 13324, + "Ġcreates": 13325, + "Ġfinitely": 13326, + "Ġswim": 13327, + "Ġsupernatant": 13328, + "opathological": 13329, + "ĠUltra": 13330, + "ĠTy": 13331, + "Ġgrand": 13332, + "Ġconstitute": 13333, + "ologist": 13334, + "ĠBroad": 13335, + "aware": 13336, + "Ġvicinity": 13337, + "agulation": 13338, + "unsigned": 13339, + "ĠSize": 13340, + "ĠCognitive": 13341, + "Ġsuspected": 13342, + "Ġupl": 13343, + "Ġautoimmune": 13344, + "ĠSK": 13345, + "CB": 13346, + "Ġslices": 13347, + "ĠChi": 13348, + "Ġobservables": 13349, + "Ġhippocampus": 13350, + "sover": 13351, + "Ġfunding": 13352, + "Ġconformation": 13353, + "ĠQuestion": 13354, + "ĠSqu": 13355, + "ĠWill": 13356, + "Ġscattered": 13357, + "irty": 13358, + "Ġplaus": 13359, + "correlation": 13360, + "Ġventilation": 13361, + "ĠGenes": 13362, + "Ġbenign": 13363, + "Ġhetero": 13364, + "Status": 13365, + "angled": 13366, + "Ġbootstrap": 13367, + "Ġvaccines": 13368, + "Ġmicroorganisms": 13369, + "Ġvisits": 13370, + "Ġtheorems": 13371, + "drop": 13372, + "ĠTA": 13373, + "Ġcycling": 13374, + "Ġspectrometer": 13375, + "Ġgroundwater": 13376, + "Ġnanotubes": 13377, + "Ġjoints": 13378, + "ĠEll": 13379, + "Ġconsult": 13380, + "Ġwindows": 13381, + "Ġdisability": 13382, + "Ġgains": 13383, + "Ġdischarg": 13384, + "Ġheated": 13385, + "Ġafore": 13386, + "arying": 13387, + "incre": 13388, + "Ġaggressive": 13389, + "Ġhemod": 13390, + "arium": 13391, + "ĠInst": 13392, + "vm": 13393, + "Ġdroplet": 13394, + "ptive": 13395, + "viously": 13396, + "Ġstarch": 13397, + "Ġdf": 13398, + "osyl": 13399, + "Ġdonors": 13400, + "ĠUnlike": 13401, + "Ġalkaline": 13402, + "Ġintelligence": 13403, + "aa": 13404, + "Ġacceptance": 13405, + "Ġsliding": 13406, + "apses": 13407, + "ĠDiss": 13408, + "istan": 13409, + "auc": 13410, + "Ġbins": 13411, + "Ġmodulate": 13412, + "Ġmanage": 13413, + "outs": 13414, + "Ġsenes": 13415, + "Ġdifferentiate": 13416, + "Ġcounted": 13417, + "ASK": 13418, + "Ġantibacterial": 13419, + "Ġentered": 13420, + "Ġdisadvant": 13421, + "ĠSalmonella": 13422, + "Ġisotopic": 13423, + "Ġannounced": 13424, + "ĠBoard": 13425, + "Ġrestoration": 13426, + "Ġallevi": 13427, + "Ġprogramme": 13428, + "Ġalbumin": 13429, + "Ġcatalog": 13430, + "estine": 13431, + "Ġdifferently": 13432, + "Ġmolar": 13433, + "rödinger": 13434, + "ĠEvent": 13435, + "ministration": 13436, + "ĠSerum": 13437, + "ROM": 13438, + "kw": 13439, + "bot": 13440, + "Ġjets": 13441, + "ĠDouble": 13442, + "eler": 13443, + "Ġinfusion": 13444, + "Ġconsumed": 13445, + "ĠIron": 13446, + "ĠProcesses": 13447, + "Ġadmits": 13448, + "Ġjuris": 13449, + "ĠPeriod": 13450, + "Ġremodeling": 13451, + "alley": 13452, + "Ġenabling": 13453, + "Ġbackward": 13454, + "ĠMid": 13455, + "brevi": 13456, + "Ġclassify": 13457, + "Ġcrypt": 13458, + "Ġhelix": 13459, + "ĠJiang": 13460, + "Ġhoney": 13461, + "gestion": 13462, + "xc": 13463, + "Ġcoincides": 13464, + "ĠDN": 13465, + "Ġapoptotic": 13466, + "Ġinstall": 13467, + "ĠRever": 13468, + "ĠDoppler": 13469, + "icago": 13470, + "erals": 13471, + "Ġpie": 13472, + "ĠMars": 13473, + "ĠStaphylococcus": 13474, + "Ġnoting": 13475, + "Ġgenera": 13476, + "ĠIo": 13477, + "Ġhope": 13478, + "Ġpreserve": 13479, + "MAX": 13480, + "ynchron": 13481, + "Ġrup": 13482, + "Ġcomprising": 13483, + "ĠWay": 13484, + "Ġviolation": 13485, + "QR": 13486, + "Ġreflecting": 13487, + "Ġregularity": 13488, + "ĠSiO": 13489, + "ĠJun": 13490, + "Ġcommunications": 13491, + "rating": 13492, + "Ġfamiliar": 13493, + "Ġinstantaneous": 13494, + "Ġcortic": 13495, + "Ġapparently": 13496, + "XX": 13497, + "Ġexcitations": 13498, + "ĠAward": 13499, + "Num": 13500, + "ĠUN": 13501, + "Ġqubit": 13502, + "ĠAction": 13503, + "ĠFried": 13504, + "Ġeliminated": 13505, + "Ġaspir": 13506, + "hler": 13507, + "Ġdecoding": 13508, + "unov": 13509, + "Ġanalogue": 13510, + "ulmonary": 13511, + "Ġgeographic": 13512, + "Ġsort": 13513, + "ĠCRC": 13514, + "Aldrich": 13515, + "ĠkDa": 13516, + "ĠND": 13517, + "Ġsettle": 13518, + "exists": 13519, + "Ġstatistic": 13520, + "ĠBow": 13521, + "ĠCG": 13522, + "Ġorganizations": 13523, + "ĠMobile": 13524, + "Ġinvent": 13525, + "Ġincorporate": 13526, + "ĠFib": 13527, + "ordan": 13528, + "Ġcolleagues": 13529, + "ĠStation": 13530, + "Ġsen": 13531, + "Ġencaps": 13532, + "ĠRH": 13533, + "relim": 13534, + "Ġcarbonate": 13535, + "ĠNether": 13536, + "mem": 13537, + "EEE": 13538, + "Ġaforementioned": 13539, + "Ġpent": 13540, + "ĠSignal": 13541, + "Ġsuspended": 13542, + "Color": 13543, + "Ġspins": 13544, + "Ġproportions": 13545, + "ulty": 13546, + "Ġenrolled": 13547, + "ĠTEM": 13548, + "ĠReceptor": 13549, + "Ġprevalent": 13550, + "large": 13551, + "vs": 13552, + "Ġtruncated": 13553, + "Ġâĭħ": 13554, + "lm": 13555, + "anil": 13556, + "Ġannih": 13557, + "ĠGalaxy": 13558, + "eras": 13559, + "Ġepigenetic": 13560, + "Ġtooth": 13561, + "Ġcondensation": 13562, + "ĠTensor": 13563, + "Ġinorganic": 13564, + "ymers": 13565, + "uf": 13566, + "anese": 13567, + "aret": 13568, + "Ġarithmetic": 13569, + "âĨ": 13570, + "Ġtrying": 13571, + "Ġimplementing": 13572, + "xd": 13573, + "Ġillumination": 13574, + "ela": 13575, + "Ġdeficits": 13576, + "Ġspots": 13577, + "Ġdoesn": 13578, + "Ġresting": 13579, + "trained": 13580, + "Ġerosion": 13581, + "Ġgranular": 13582, + "Ġscar": 13583, + "Ġpollen": 13584, + "lie": 13585, + "Ġconvers": 13586, + "Ġdisturbances": 13587, + "ĠGod": 13588, + "Ġenlarg": 13589, + "ĠLate": 13590, + "ylase": 13591, + "Ġfacts": 13592, + "enty": 13593, + "ĠStreet": 13594, + "sequence": 13595, + "Ġvenous": 13596, + "ĠCheck": 13597, + "agg": 13598, + "Ġabsorbed": 13599, + "Ġcommit": 13600, + "sets": 13601, + "Ġdestroy": 13602, + "Ġbowel": 13603, + "Ġfinished": 13604, + "ĠFeed": 13605, + "Ġdoped": 13606, + "ĠAlb": 13607, + "ĠMitochond": 13608, + "Ġtheoretically": 13609, + "RI": 13610, + "Ġmeteor": 13611, + "ĠMG": 13612, + "Ġnation": 13613, + "ĠBasin": 13614, + "nik": 13615, + "Ġdepths": 13616, + "ĠMechanism": 13617, + "Ġmotifs": 13618, + "ĠHay": 13619, + "Ġmotivated": 13620, + "ĠCopy": 13621, + "ĠEastern": 13622, + "Ġpersistence": 13623, + "Ġrays": 13624, + "FB": 13625, + "andem": 13626, + "layers": 13627, + "eyer": 13628, + "ĠStrept": 13629, + "Ġregistration": 13630, + "ĠAntarctic": 13631, + "CV": 13632, + "ĠPap": 13633, + "ĠSpe": 13634, + "Ġsplicing": 13635, + "performance": 13636, + "Ġsemantics": 13637, + "Ġlocom": 13638, + "oblastoma": 13639, + "Ġmoney": 13640, + "Ġtransparent": 13641, + "Ġhr": 13642, + "ĠInteractions": 13643, + "Ġsap": 13644, + "Ġbiases": 13645, + "Ġteeth": 13646, + "ynolds": 13647, + "omethyl": 13648, + "ĠmV": 13649, + "Ġsolely": 13650, + "Ġorange": 13651, + "blast": 13652, + "ATIONS": 13653, + "call": 13654, + "opoietic": 13655, + "sided": 13656, + "ĠFox": 13657, + "ĠVideo": 13658, + "Ġinspection": 13659, + "Ġbuck": 13660, + "hesize": 13661, + "present": 13662, + "ĠAntib": 13663, + "Ġham": 13664, + "alam": 13665, + "ĠPG": 13666, + "ĠAE": 13667, + "Ġjoin": 13668, + "Ġmonocytes": 13669, + "estiv": 13670, + "Ġrandomised": 13671, + "Ġtranslocation": 13672, + "Ġincorporating": 13673, + "Ġprolifer": 13674, + "Ġodds": 13675, + "ITH": 13676, + "Ġran": 13677, + "Ġinstruction": 13678, + "Ġresolve": 13679, + "Ġft": 13680, + "ĠHead": 13681, + "Ġreagent": 13682, + "Ġadmitted": 13683, + "human": 13684, + "posure": 13685, + "ĠCha": 13686, + "ĠFr": 13687, + "Ġbroadcast": 13688, + "Ġnutrients": 13689, + "nob": 13690, + "Ġnotable": 13691, + "ĠIGF": 13692, + "ĠClearly": 13693, + "Ġquarks": 13694, + "Ġeukary": 13695, + "ĠAdd": 13696, + "itosan": 13697, + "Ġinteractive": 13698, + "itting": 13699, + "ĠComputational": 13700, + "Ġdissolution": 13701, + "istribution": 13702, + "product": 13703, + "ĠABC": 13704, + "olimits": 13705, + "biased": 13706, + "Ġtrapped": 13707, + "PK": 13708, + "ĠHPLC": 13709, + "rophot": 13710, + "zes": 13711, + "ourse": 13712, + "ĠHot": 13713, + "Ġrecipro": 13714, + "nolimits": 13715, + "ello": 13716, + "Ġassessments": 13717, + "ENTS": 13718, + "Ġalteration": 13719, + "tw": 13720, + "Ġchaotic": 13721, + "ĠLoc": 13722, + "Ġcattle": 13723, + "Ray": 13724, + "Ġformally": 13725, + "leave": 13726, + "textstyle": 13727, + "Ġventral": 13728, + "ĠWilliams": 13729, + "ĠPeople": 13730, + "ixing": 13731, + "ĠTherapy": 13732, + "Ġiii": 13733, + "ĠDT": 13734, + "Ġbic": 13735, + "Ġspheres": 13736, + "Ġvisc": 13737, + "Ġestablishment": 13738, + "Ġdescriptions": 13739, + "ĠAverage": 13740, + "Ġtour": 13741, + "ĠInfection": 13742, + "ĠLicense": 13743, + "Ġprepare": 13744, + "Hs": 13745, + "finite": 13746, + "rium": 13747, + "oreg": 13748, + "entry": 13749, + "Ġdisks": 13750, + "Ġelongation": 13751, + "cpu": 13752, + "ĠCharles": 13753, + "FIGURE": 13754, + "ston": 13755, + "ĠObservations": 13756, + "Add": 13757, + "ĠTask": 13758, + "atomy": 13759, + "igration": 13760, + "ĠDatabase": 13761, + "ĠTexas": 13762, + "Ġphyt": 13763, + "ller": 13764, + "conjug": 13765, + "onald": 13766, + "Ġheavily": 13767, + "Ġsple": 13768, + "Ġassist": 13769, + "ĠCp": 13770, + "Ġhappen": 13771, + "uv": 13772, + "ĠUniverse": 13773, + "ĠGPS": 13774, + "WE": 13775, + "Xi": 13776, + "Ġadministr": 13777, + "strong": 13778, + "Ġmagnitudes": 13779, + "Ġsimplify": 13780, + "Ġelegans": 13781, + "esh": 13782, + "ĠBody": 13783, + "ĠNetherlands": 13784, + "ï": 13785, + "ometers": 13786, + "Bo": 13787, + "FM": 13788, + "ĠNiger": 13789, + "plus": 13790, + "instance": 13791, + "Ġdistress": 13792, + "Organ": 13793, + "Cas": 13794, + "Ġsymplectic": 13795, + "Ġbreaks": 13796, + "ÑĤ": 13797, + "Ġfermion": 13798, + "emporal": 13799, + "Ġsomatic": 13800, + "event": 13801, + "neut": 13802, + "lammation": 13803, + "ĠLibrary": 13804, + "Ġmultiplic": 13805, + "ĠInstr": 13806, + "ethel": 13807, + "urys": 13808, + "Ġhelped": 13809, + "Ġcollege": 13810, + "Ġcartilage": 13811, + "Ġrpm": 13812, + "western": 13813, + "resis": 13814, + "Ġlobe": 13815, + "QL": 13816, + "Input": 13817, + "Ġemphasis": 13818, + "best": 13819, + "Ġtotally": 13820, + "ĠMETHOD": 13821, + "ĠFa": 13822, + "ĠReduction": 13823, + "icious": 13824, + "Ġimplantation": 13825, + "potential": 13826, + "problem": 13827, + "Ġobtains": 13828, + "urons": 13829, + "Ġconstructing": 13830, + "ĠMusic": 13831, + "Ġcancell": 13832, + "Ġnews": 13833, + "ĠChapter": 13834, + "Ġlabelled": 13835, + "Ġzebrafish": 13836, + "ĠSolid": 13837, + "Ġglutamate": 13838, + "ĉĉĉĉĉ": 13839, + "Ġchapter": 13840, + "ĠPresident": 13841, + "Min": 13842, + "Ġatrial": 13843, + "cp": 13844, + "fi": 13845, + "final": 13846, + "Ġtok": 13847, + "Ġeffector": 13848, + "Ġspine": 13849, + "Ġidentities": 13850, + "isco": 13851, + "olis": 13852, + "ĠCle": 13853, + "Ġinvariants": 13854, + "Path": 13855, + "ĠGon": 13856, + "factory": 13857, + "Ġexogenous": 13858, + "ĠMAPK": 13859, + "Ġanswers": 13860, + "Ġgetting": 13861, + "Rs": 13862, + "IH": 13863, + "ĠDefine": 13864, + "ĠConvolutional": 13865, + "Ġgeometrical": 13866, + "ĠInput": 13867, + "Ġà": 13868, + "Ġattenuated": 13869, + "Ġradicals": 13870, + "ĠAcademy": 13871, + "ãĥ": 13872, + "ichlet": 13873, + "Ġtorus": 13874, + "ĠTheoretical": 13875, + "ĠTD": 13876, + "Ġantiv": 13877, + "onge": 13878, + "Ġintravenous": 13879, + "Ġhypoth": 13880, + "Ġwastewater": 13881, + "ĠFlo": 13882, + "Ġporosity": 13883, + "Ġpall": 13884, + "aci": 13885, + "Ġrecordings": 13886, + "Ġeating": 13887, + "ĠDW": 13888, + "unting": 13889, + "ĠDim": 13890, + "Ġemitted": 13891, + "ĠJoint": 13892, + "ofib": 13893, + "Ġearthquake": 13894, + "Ġmunic": 13895, + "Ġreductions": 13896, + "Ġconjunction": 13897, + "ĠLocation": 13898, + "Ġestablishing": 13899, + "ĠMathematical": 13900, + "ĠSolution": 13901, + "buffer": 13902, + "arin": 13903, + "iley": 13904, + "ĠCommission": 13905, + "ĠGABA": 13906, + "ĠMuseum": 13907, + "Ġverb": 13908, + "lecules": 13909, + "infection": 13910, + "Ġinsect": 13911, + "iser": 13912, + "Ġprovision": 13913, + "Ġagreed": 13914, + "Ġafford": 13915, + "theory": 13916, + "knowledge": 13917, + "Protein": 13918, + "Ġkernels": 13919, + "Ġderm": 13920, + "Ġwish": 13921, + "Ġvox": 13922, + "Scale": 13923, + "hu": 13924, + "Ġcounterparts": 13925, + "ĠRoss": 13926, + "Ġunp": 13927, + "ĠOnline": 13928, + "Ġtransporter": 13929, + "Graph": 13930, + "Ġuter": 13931, + "Ġminute": 13932, + "Ġautomorphism": 13933, + "iltr": 13934, + "ĠRespons": 13935, + "ĠSym": 13936, + "Ġfactorization": 13937, + "sem": 13938, + "Ġmediates": 13939, + "Ġunexpected": 13940, + "Ġorganism": 13941, + "Ġattempted": 13942, + "aran": 13943, + "venue": 13944, + "etheless": 13945, + "Ġnoticed": 13946, + "ĠInvestigation": 13947, + "Ġcareg": 13948, + "Ġgrouped": 13949, + "orbit": 13950, + "Ġshortest": 13951, + "Ġbroader": 13952, + "ĠMIM": 13953, + "rises": 13954, + "veloper": 13955, + "ĠHi": 13956, + "ĠkHz": 13957, + "Ġbeads": 13958, + "Ġphyto": 13959, + "ĠDoes": 13960, + "Ġmammals": 13961, + "Ġrefined": 13962, + "volume": 13963, + "Ser": 13964, + "Ġresistivity": 13965, + "Ġterrestrial": 13966, + "Ġaxi": 13967, + "ifluor": 13968, + "Ġ£": 13969, + "Ġvice": 13970, + "ĠKel": 13971, + "VM": 13972, + "ĠTown": 13973, + "adm": 13974, + "plates": 13975, + "Ġholomorphic": 13976, + "ĠRib": 13977, + "ĠSB": 13978, + "ĠTemporal": 13979, + "src": 13980, + "Ġupdates": 13981, + "Ġseek": 13982, + "endix": 13983, + "oretic": 13984, + "warz": 13985, + "Ġroutes": 13986, + "Ġstanding": 13987, + "ĠÃģ": 13988, + "Ġclassic": 13989, + "Ġpale": 13990, + "lections": 13991, + "Ġclassifiers": 13992, + "Ġpathophys": 13993, + "Ġmounted": 13994, + "Ġdesignated": 13995, + "Ġvideos": 13996, + "Ġincoming": 13997, + "Ġguarantees": 13998, + "Ġparasites": 13999, + "ĠBacillus": 14000, + "four": 14001, + "Ġâ΍": 14002, + "Ġcommutative": 14003, + "stackrel": 14004, + "ĠBanach": 14005, + "Ġdealing": 14006, + "emporary": 14007, + "Multi": 14008, + "otomy": 14009, + "reting": 14010, + "Ġnond": 14011, + "ĠConference": 14012, + "tzmann": 14013, + "Ġphosphorus": 14014, + "Ġchemicals": 14015, + "Ġdispar": 14016, + "degree": 14017, + "Ġarbitrarily": 14018, + "rocyte": 14019, + "Ġparabolic": 14020, + "Ġdimensionless": 14021, + "Ġosm": 14022, + "Ġphonon": 14023, + "tiary": 14024, + "ĠSect": 14025, + "ophysical": 14026, + "ĠMapping": 14027, + "bis": 14028, + "ĠCommunication": 14029, + "Ġmimic": 14030, + "Ġregulators": 14031, + "Ġneutrophils": 14032, + "fn": 14033, + "ĠImportantly": 14034, + "Ġmere": 14035, + "Ġconfirms": 14036, + "agram": 14037, + "Ġattend": 14038, + "ungal": 14039, + "ĠGroups": 14040, + "Ġzo": 14041, + "Ġmouth": 14042, + "Ġsteep": 14043, + "Ġprevented": 14044, + "Ġdepressive": 14045, + "acies": 14046, + "ĠLS": 14047, + "Ġnitric": 14048, + "Ġvisualized": 14049, + "Ġtranscriptome": 14050, + "Ġgait": 14051, + "ercury": 14052, + "Ġshot": 14053, + "ĠVen": 14054, + "Ġexchang": 14055, + "Ġintention": 14056, + "ĠTang": 14057, + "Ġfavour": 14058, + "veolar": 14059, + "Ġpermission": 14060, + "Ġhabitats": 14061, + "Ġmaize": 14062, + "inct": 14063, + "Ġtelevision": 14064, + "rystals": 14065, + "ĠRadi": 14066, + "Ġflavon": 14067, + "Ġcann": 14068, + "iota": 14069, + "ĠOT": 14070, + "pic": 14071, + "Rad": 14072, + "titial": 14073, + "ĠOrth": 14074, + "stellar": 14075, + "ĠKine": 14076, + "Ġnavigation": 14077, + "fast": 14078, + "ĠCRISPR": 14079, + "Ġkinematic": 14080, + "Ġsearching": 14081, + "Ġmicrom": 14082, + "Ġinstalled": 14083, + "ĠTaiwan": 14084, + "ila": 14085, + "rf": 14086, + "riage": 14087, + "plinary": 14088, + "Ġecho": 14089, + "rav": 14090, + "ĠLes": 14091, + "create": 14092, + "Ġubiquit": 14093, + "Ġprecursors": 14094, + "KE": 14095, + "Ġdivide": 14096, + "Ġlnc": 14097, + "ĠConstruction": 14098, + "anic": 14099, + "estim": 14100, + "isters": 14101, + "Ġfeet": 14102, + "ariant": 14103, + "ĠSchw": 14104, + "Ġexclude": 14105, + "Ġvolcan": 14106, + "ĠOverview": 14107, + "Ġyr": 14108, + "olk": 14109, + "Ġ©": 14110, + "ĠFE": 14111, + "Ġspermat": 14112, + "Ġcapacitance": 14113, + "ĠSchrödinger": 14114, + "ĠGE": 14115, + "Ġcalibrated": 14116, + "SEM": 14117, + "Ġlattices": 14118, + "plier": 14119, + "Arg": 14120, + "ĠNT": 14121, + "ĠEnhanced": 14122, + "Ġbrom": 14123, + "Ġmultip": 14124, + "Ġcertified": 14125, + "Ġislands": 14126, + "Ġcyst": 14127, + "Ġaltitude": 14128, + "edef": 14129, + "Ġconstrain": 14130, + "Ġsatisfactory": 14131, + "Ġspecialized": 14132, + "Ġjunctions": 14133, + "Ġcoronavirus": 14134, + "udge": 14135, + "exc": 14136, + "Ġalt": 14137, + "ĠBacterial": 14138, + "Ġseasons": 14139, + "ĠLM": 14140, + "Ġhistogram": 14141, + "Ġsolvents": 14142, + "average": 14143, + "Ġcardinal": 14144, + "chrom": 14145, + "python": 14146, + "dered": 14147, + "enia": 14148, + "ĠGH": 14149, + "ĠEss": 14150, + "____": 14151, + "ĠPak": 14152, + "sized": 14153, + "ĠHg": 14154, + "Ġelif": 14155, + "ĠSchematic": 14156, + "Ġcytoplasm": 14157, + "ĠFort": 14158, + "ania": 14159, + "Ġcareful": 14160, + "ĠDual": 14161, + "Ġtranslated": 14162, + "Ġnasal": 14163, + "Inv": 14164, + "Ġdaughter": 14165, + "Ġemphasize": 14166, + "modules": 14167, + "Ġlives": 14168, + "Ġhomotopy": 14169, + "Ġbot": 14170, + "Ġdisordered": 14171, + "mato": 14172, + "Second": 14173, + "Ġclaimed": 14174, + "addle": 14175, + "Ġinterfacial": 14176, + "Ġviscous": 14177, + "Ġdestination": 14178, + "ĠPlanck": 14179, + "Ġabsorbance": 14180, + "Ġvolatile": 14181, + "Ġstorm": 14182, + "Ġcarboxyl": 14183, + "ĠBank": 14184, + "ĠPack": 14185, + "Ġscaffold": 14186, + "tebr": 14187, + "ipot": 14188, + "Ġtumours": 14189, + "ĠGol": 14190, + "Ġelectrophoresis": 14191, + "Ġrealize": 14192, + "Ġconstituents": 14193, + "Sol": 14194, + "ĠEvery": 14195, + "Ġmediate": 14196, + "Ġcoincide": 14197, + "Ġexploit": 14198, + "Ġmonoton": 14199, + "measure": 14200, + "Ġsupplied": 14201, + "racellular": 14202, + "Ġferro": 14203, + "Ġpurs": 14204, + "erentially": 14205, + "trast": 14206, + "ĠRB": 14207, + "Ġdissem": 14208, + "asy": 14209, + "Ġrelating": 14210, + "null": 14211, + "uates": 14212, + "constant": 14213, + "ĠContinuous": 14214, + "Ġgeometries": 14215, + "rust": 14216, + "ĠSTR": 14217, + "cluster": 14218, + "Ġprogenitor": 14219, + "ĠCSF": 14220, + "ĠYam": 14221, + "ĠReynolds": 14222, + "ĠMY": 14223, + "ĠKO": 14224, + "ĠWalk": 14225, + "ariable": 14226, + "inder": 14227, + "ĠRight": 14228, + "ĠAlgebra": 14229, + "ĠWik": 14230, + "Ġinactivation": 14231, + "tmp": 14232, + "access": 14233, + "ĠLater": 14234, + "Ġmicrobiome": 14235, + "Ġgeodesic": 14236, + "Ġrejection": 14237, + "uses": 14238, + "Ġhardness": 14239, + "Ġhydrodynamic": 14240, + "Ġvanish": 14241, + "Ġpollut": 14242, + "amycin": 14243, + "ĠÏŃ": 14244, + "ipitation": 14245, + "Ġaugmented": 14246, + "ĠTT": 14247, + "aval": 14248, + "Ġencode": 14249, + "Ġtoxin": 14250, + "eto": 14251, + "ighbor": 14252, + "addr": 14253, + "Ġdamaged": 14254, + "oi": 14255, + "Ġtransduction": 14256, + "Ġinteracts": 14257, + "ÃŃa": 14258, + "ĠCall": 14259, + "riends": 14260, + "ĠMonitoring": 14261, + "ĠVariation": 14262, + "Ġôı¼": 14263, + "Ġdich": 14264, + "Ġspars": 14265, + "align": 14266, + "Ġanatomical": 14267, + "Ġcentrifuged": 14268, + "urally": 14269, + "ĠZr": 14270, + "ĠCarl": 14271, + "Recall": 14272, + "Ġopinion": 14273, + "Ġera": 14274, + "Ġdrainage": 14275, + "Ġmicroarray": 14276, + "status": 14277, + "umental": 14278, + "Ġcomprises": 14279, + "pressure": 14280, + "Ġpractition": 14281, + "mac": 14282, + "Ġcongr": 14283, + "urnal": 14284, + "ĠAPI": 14285, + "ĠLR": 14286, + "Ġtransfection": 14287, + "Ġslopes": 14288, + "ĠCode": 14289, + "Ġphil": 14290, + "bool": 14291, + "Ws": 14292, + "ĠâĻ": 14293, + "Ġassociate": 14294, + "otoxicity": 14295, + "rade": 14296, + "ĠMiller": 14297, + "ĠϪ": 14298, + "Ġshorten": 14299, + "Ġadditionally": 14300, + "ĠEffective": 14301, + "Ġsupervised": 14302, + "Ġelabor": 14303, + "ĠCellular": 14304, + "Ġtell": 14305, + "ĠRC": 14306, + "save": 14307, + "imid": 14308, + "Ġratings": 14309, + "ĠTaking": 14310, + "Ġapproval": 14311, + "Ġpenalty": 14312, + "KK": 14313, + "context": 14314, + "aks": 14315, + "pecific": 14316, + "Ġtempor": 14317, + "Ġupregulation": 14318, + "VAL": 14319, + "Ġencodes": 14320, + "inin": 14321, + "Ġnotes": 14322, + "ĠForest": 14323, + "Ġcombinatorial": 14324, + "ymptotic": 14325, + "Ġsquamous": 14326, + "ĠAsh": 14327, + "ourn": 14328, + "Ġmyeloid": 14329, + "elines": 14330, + "Bio": 14331, + "Ġbreed": 14332, + "ĠRub": 14333, + "uzz": 14334, + "Ġsinglet": 14335, + "enna": 14336, + "Ġcritically": 14337, + "dig": 14338, + "disci": 14339, + "Ġdropped": 14340, + "Ġlipoprotein": 14341, + "ĠEt": 14342, + "Ġnov": 14343, + "ophen": 14344, + "Ġancient": 14345, + "Base": 14346, + "Ġsmoothing": 14347, + "itives": 14348, + "pine": 14349, + "Ġsolver": 14350, + "perm": 14351, + "ĠHome": 14352, + "Ġazim": 14353, + "lVert": 14354, + "Ġtransportation": 14355, + "Ġdex": 14356, + "ĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ": 14357, + "opathic": 14358, + "experim": 14359, + "âĢ¢âĢ¢": 14360, + "perfusion": 14361, + "Ġdoi": 14362, + "ĠLact": 14363, + "Ġhepatocellular": 14364, + "Ġmismatch": 14365, + "Ġadenocarcinoma": 14366, + "ĠPain": 14367, + "Ġspr": 14368, + "Ġconfinement": 14369, + "Ġexceeds": 14370, + "Ġhash": 14371, + "ĠComparing": 14372, + "ĠSensor": 14373, + "Ġfiring": 14374, + "kes": 14375, + "vir": 14376, + "inea": 14377, + "affected": 14378, + "Ġmodelled": 14379, + "Ġether": 14380, + "Ġsuffer": 14381, + "â̲â̲": 14382, + "оÐ": 14383, + "ĠBir": 14384, + "Äģ": 14385, + "Ġsecreted": 14386, + "Ġcatheter": 14387, + "Ġyouth": 14388, + "expl": 14389, + "ĠDar": 14390, + "ĠWHO": 14391, + "Ġfoundation": 14392, + "Ġhydraulic": 14393, + "ĠCarol": 14394, + "SSION": 14395, + "Ġá¹": 14396, + "feld": 14397, + "avor": 14398, + "Ġpasses": 14399, + "visiae": 14400, + "Ġapplicability": 14401, + "Ġnested": 14402, + "Fl": 14403, + "ĠCatal": 14404, + "Ġmicroenvironment": 14405, + "labels": 14406, + "Ġcrystallization": 14407, + "Info": 14408, + "Ġpositioning": 14409, + "Ġtriangles": 14410, + "Ġtryp": 14411, + "ĠTransition": 14412, + "Ġsett": 14413, + "Ġneurot": 14414, + "Mon": 14415, + "Ġdroplets": 14416, + "ĠART": 14417, + "Ġcorne": 14418, + "Ġmultiplicity": 14419, + "Ġeccentric": 14420, + "Ġiv": 14421, + "ĠMatter": 14422, + "learning": 14423, + "electro": 14424, + "ĠWeyl": 14425, + "Ġdecide": 14426, + "ĠWr": 14427, + "ĠHierarch": 14428, + "Ġapical": 14429, + "Ġfailures": 14430, + "Ġdigestion": 14431, + "MIC": 14432, + "Ġgeographical": 14433, + "ĠElement": 14434, + "ĠThough": 14435, + "Ġchron": 14436, + "limited": 14437, + "ĠDISC": 14438, + "ĠArchitecture": 14439, + "Ġvibrational": 14440, + "ĠVarious": 14441, + "Ġdynamically": 14442, + "aked": 14443, + "Ġconvenience": 14444, + "ĠIsra": 14445, + "ĠMDA": 14446, + "itic": 14447, + "Au": 14448, + "Ġassistance": 14449, + "ventional": 14450, + "midt": 14451, + "ospor": 14452, + "Following": 14453, + "Ġinferior": 14454, + "Ġnickel": 14455, + "raine": 14456, + "paren": 14457, + "Ġtitanium": 14458, + "Field": 14459, + "Ġhoc": 14460, + "ĠCauchy": 14461, + "ĠMcC": 14462, + "ĠScreen": 14463, + "Ġneglect": 14464, + "classes": 14465, + "ĠIF": 14466, + "Ġstratified": 14467, + "enses": 14468, + "ĠPlate": 14469, + "ozoic": 14470, + "Ġinstitutions": 14471, + "ĠThose": 14472, + "Ġgenerations": 14473, + "transform": 14474, + "Ġpartitions": 14475, + "Rxiv": 14476, + "enth": 14477, + "Ġstic": 14478, + "olith": 14479, + "ĠFem": 14480, + "Ġagar": 14481, + "beam": 14482, + "Ġprotons": 14483, + "LU": 14484, + "Ġworkload": 14485, + "Ġminerals": 14486, + "Ġmt": 14487, + "lla": 14488, + "ĠPharmac": 14489, + "Ġconverter": 14490, + "ĠMechanical": 14491, + "Ġflavor": 14492, + "Ġphosphatase": 14493, + "Ġsums": 14494, + "PCs": 14495, + "Ġisoforms": 14496, + "igroup": 14497, + "pyr": 14498, + "features": 14499, + "Ġperc": 14500, + "Ġcompleteness": 14501, + "Ġforests": 14502, + "Ġdividing": 14503, + "ĠLipschitz": 14504, + "periodic": 14505, + "Ġrecycl": 14506, + "ĠNag": 14507, + "Ġtwin": 14508, + "eptides": 14509, + "Ġcohor": 14510, + "Ġsearches": 14511, + "eated": 14512, + "Hg": 14513, + "ĠPU": 14514, + "ĠTree": 14515, + "allic": 14516, + "PF": 14517, + "Ġappendix": 14518, + "ĠCov": 14519, + "Ġchecking": 14520, + "Ġbackbone": 14521, + "Thermo": 14522, + "Ġactivating": 14523, + "ĠVictor": 14524, + "Ġcritic": 14525, + "ĠLem": 14526, + "groups": 14527, + "REG": 14528, + "ĠOcc": 14529, + "SCC": 14530, + "ĠXRD": 14531, + "ĠValues": 14532, + "Ġsubtype": 14533, + "Ġstretching": 14534, + "ORM": 14535, + "some": 14536, + "Ġflip": 14537, + "Ġphenolic": 14538, + "Ġkilled": 14539, + "Ġsequenced": 14540, + "uscular": 14541, + "abin": 14542, + "Ġquadr": 14543, + "Ġtranslational": 14544, + "Ġsolids": 14545, + "direct": 14546, + "Ġpromotion": 14547, + "Ġcohorts": 14548, + "ĠClimate": 14549, + "ĠOld": 14550, + "ĠSir": 14551, + "gue": 14552, + "strate": 14553, + "ĠPoss": 14554, + "Ġreceives": 14555, + "ĠValidation": 14556, + "uctive": 14557, + "Ġcerevisiae": 14558, + "Gu": 14559, + "isis": 14560, + "ceil": 14561, + "ĠPearson": 14562, + "ĠPrelim": 14563, + "ĠGran": 14564, + "CSF": 14565, + "Ġsterile": 14566, + "ofluorescence": 14567, + "bad": 14568, + "Ġcolored": 14569, + "compass": 14570, + "equation": 14571, + "jan": 14572, + "Ġconditioning": 14573, + "Ġvoice": 14574, + "Ġmening": 14575, + "Ġgranted": 14576, + "Ġrenormalization": 14577, + "ĠLimit": 14578, + "thi": 14579, + "Ġaperture": 14580, + "Ġdosage": 14581, + "directed": 14582, + "ĠBreast": 14583, + "ocular": 14584, + "bearing": 14585, + "sal": 14586, + "ascul": 14587, + "upervised": 14588, + "Ġmonolayer": 14589, + "Ġmembership": 14590, + "ĠWireless": 14591, + "show": 14592, + "ĠMedia": 14593, + "ĠVL": 14594, + "essel": 14595, + "Ġdecoder": 14596, + "ĠMF": 14597, + "ĠComposition": 14598, + "ĠClark": 14599, + "Point": 14600, + "ĠNano": 14601, + "ĠDeg": 14602, + "NL": 14603, + "ĠBox": 14604, + "Ġexploring": 14605, + "molecular": 14606, + "Other": 14607, + "ĠDiabetes": 14608, + "height": 14609, + "Ġkinases": 14610, + "Ġadjusting": 14611, + "Ġsports": 14612, + "offs": 14613, + "ĠIEEE": 14614, + "Ġtil": 14615, + "ĠIntra": 14616, + "Ġplanets": 14617, + "ĠEpidem": 14618, + "Ġtomato": 14619, + "Ġscaffolds": 14620, + "ĠMetabol": 14621, + "ĠGeometry": 14622, + "imetry": 14623, + "ĠTen": 14624, + "thread": 14625, + "ohex": 14626, + "Ġproposes": 14627, + "prim": 14628, + "ĠParty": 14629, + "Ġquarter": 14630, + "ĠShi": 14631, + "Ġaberr": 14632, + "ĠIntr": 14633, + "Ġdirector": 14634, + "affe": 14635, + "ĠSus": 14636, + "ensors": 14637, + "Ele": 14638, + "Ġpoles": 14639, + "Additional": 14640, + "Ġbypass": 14641, + "catenin": 14642, + "Ġundertaken": 14643, + "imation": 14644, + "opor": 14645, + "Ġpreserving": 14646, + "Ġmultiplex": 14647, + "ĠRepresentative": 14648, + "sis": 14649, + "ĠAG": 14650, + "achy": 14651, + "Ġfruits": 14652, + "Ġreconstruct": 14653, + "ensen": 14654, + "Ġstrongest": 14655, + "Ġscav": 14656, + "ĠCheng": 14657, + "ĠCoron": 14658, + "ĠObservation": 14659, + "ĠAch": 14660, + "ĠGeorg": 14661, + "ĠSVM": 14662, + "ĠChern": 14663, + "Ġreversal": 14664, + "via": 14665, + "imp": 14666, + "Ġdeployment": 14667, + "ĠHad": 14668, + "Ġcircumstances": 14669, + "obi": 14670, + "Ġcurved": 14671, + "Induced": 14672, + "ĠPositive": 14673, + "imb": 14674, + "ĠParis": 14675, + "ĠStein": 14676, + "icz": 14677, + "ĠCath": 14678, + "Ġdrawing": 14679, + "tory": 14680, + "Ġcontinental": 14681, + "Ġquantitatively": 14682, + "acerb": 14683, + "Ġnorms": 14684, + "ĠBE": 14685, + "Several": 14686, + "door": 14687, + "Ġplateau": 14688, + "Gal": 14689, + "Ġcivil": 14690, + "ĠFix": 14691, + "LAB": 14692, + "occal": 14693, + "Ġsorted": 14694, + "ĠâĢĿ": 14695, + "Ġediting": 14696, + "ĠChristian": 14697, + "Ġclarify": 14698, + "Ġwaveguide": 14699, + "bell": 14700, + "Ġdeduced": 14701, + "odec": 14702, + "utrition": 14703, + "Ġcompressive": 14704, + "ĠEU": 14705, + "ĠRegression": 14706, + "Ġranked": 14707, + "Ġestimators": 14708, + "Ġabilities": 14709, + "Ġbeliefs": 14710, + "three": 14711, + "ĠâĩĴ": 14712, + "rology": 14713, + "Ġautonomous": 14714, + "ĠSz": 14715, + "schem": 14716, + "ĠALT": 14717, + "ĠPatterns": 14718, + "Ġexon": 14719, + "Ġlifestyle": 14720, + "fill": 14721, + "ĠCAR": 14722, + "ĠDomains": 14723, + "Ġpaid": 14724, + "Ġtab": 14725, + "ĠCohen": 14726, + "airy": 14727, + "Ġsheep": 14728, + "Ġseaw": 14729, + "ĠKong": 14730, + "gas": 14731, + "Ġreserved": 14732, + "Ġresil": 14733, + "Ġobl": 14734, + "carbox": 14735, + "ĠGovernment": 14736, + "upper": 14737, + "racting": 14738, + "Ġgangl": 14739, + "ĠRV": 14740, + "Ġbronch": 14741, + "Methods": 14742, + "ĠLiver": 14743, + "Ġguess": 14744, + "charomy": 14745, + "ICE": 14746, + "Ġcongenital": 14747, + "Ġka": 14748, + "Ġspanning": 14749, + "ĠRecomm": 14750, + "ea": 14751, + "Ġconvention": 14752, + "Ġsheets": 14753, + "Ġthermo": 14754, + "Ġqualitatively": 14755, + "Ġoxides": 14756, + "Ġcongru": 14757, + "ĠJer": 14758, + "Ġpreservation": 14759, + "ĠBT": 14760, + "ĠDMSO": 14761, + "Ġcomplication": 14762, + "Ġsurvivors": 14763, + "Ġreduct": 14764, + "Ġdescent": 14765, + "Ġsucrose": 14766, + "ĠCourt": 14767, + "Ġmetabolite": 14768, + "ĠMath": 14769, + "ĠSecurity": 14770, + "ĠNotably": 14771, + "ĠStem": 14772, + "Ġdwarf": 14773, + "bc": 14774, + "Ġrevis": 14775, + "ĠKl": 14776, + "ĠGh": 14777, + "Ġmanager": 14778, + "Ġinvestment": 14779, + "Ġmotility": 14780, + "Em": 14781, + "ĠMr": 14782, + "asic": 14783, + "ĠBos": 14784, + "Ġinspired": 14785, + "placian": 14786, + "Ġease": 14787, + "Ġtorsion": 14788, + "ĠDirichlet": 14789, + "Ġspleen": 14790, + "agation": 14791, + "onate": 14792, + "ĠTrial": 14793, + "Ġturnover": 14794, + "Ġselectively": 14795, + "ĠÍĴ": 14796, + "iano": 14797, + "Ġnontrivial": 14798, + "iasis": 14799, + "Ñģ": 14800, + "ĠGuo": 14801, + "Ġaddresses": 14802, + "Ġuniqueness": 14803, + "Ġwithdraw": 14804, + "riz": 14805, + "Ġcomputationally": 14806, + "Ġpersonality": 14807, + "AX": 14808, + "wenty": 14809, + "Ġgovern": 14810, + "berts": 14811, + "Ġrobots": 14812, + "Ġready": 14813, + "Ġdiets": 14814, + "lit": 14815, + "My": 14816, + "ĠReve": 14817, + "ĠLos": 14818, + "infrared": 14819, + "Ġintram": 14820, + "lated": 14821, + "plankton": 14822, + "ĠGrant": 14823, + "piper": 14824, + "Ġantennas": 14825, + "Ġbol": 14826, + "fp": 14827, + "ĠVit": 14828, + "Compar": 14829, + "oken": 14830, + "Ġkeys": 14831, + "ĠClub": 14832, + "inery": 14833, + "ĠFoot": 14834, + "Ġwarming": 14835, + "mond": 14836, + "Ġmiles": 14837, + "Ġspeaking": 14838, + "ĠIv": 14839, + "Ġconformational": 14840, + "ĠOk": 14841, + "Ġunified": 14842, + "Ġassembled": 14843, + "Ġinverted": 14844, + "Ġfelt": 14845, + "corresponding": 14846, + "ĠECM": 14847, + "ĠNSC": 14848, + "Ġindoor": 14849, + "gov": 14850, + "Ġantagonist": 14851, + "unched": 14852, + "ĠJava": 14853, + "ĠCombined": 14854, + "tivities": 14855, + "Ġalternating": 14856, + "ãĤ": 14857, + "ĠDiagnosis": 14858, + "Ġdistinction": 14859, + "leigh": 14860, + "ĠTogether": 14861, + "Ġparticipating": 14862, + "Ġglomer": 14863, + "oche": 14864, + "Ġcopyright": 14865, + "ĠGTP": 14866, + "ĠVar": 14867, + "Ġammonium": 14868, + "Ġfacilitates": 14869, + "Ġperfusion": 14870, + "ĠLB": 14871, + "full": 14872, + "Ġreti": 14873, + "iferase": 14874, + "Ġimmunosup": 14875, + "ĠImplementation": 14876, + "Ġpores": 14877, + "ĠBB": 14878, + "ĠBud": 14879, + "ĠVO": 14880, + "ĠVo": 14881, + "Ġphysician": 14882, + "ĠAUC": 14883, + "Ġcertainly": 14884, + "μm": 14885, + "ĠKol": 14886, + "Ġwrap": 14887, + "middle": 14888, + "Ġsilencing": 14889, + "Ġfreshwater": 14890, + "igan": 14891, + "area": 14892, + "AI": 14893, + "Ġmicrotub": 14894, + "Ġarranged": 14895, + "structive": 14896, + "ĠRegular": 14897, + "ĠFile": 14898, + "alks": 14899, + "Ġplain": 14900, + "Ġintegrable": 14901, + "ĠMembrane": 14902, + "istors": 14903, + "Ġaquatic": 14904, + "Ġworkflow": 14905, + "ĠGer": 14906, + "ulant": 14907, + "Ġactivates": 14908, + "Term": 14909, + "ĠUpon": 14910, + "ĠPut": 14911, + "Var": 14912, + "ĠOD": 14913, + "half": 14914, + "Ġulcer": 14915, + "ĠBO": 14916, + "ĠGy": 14917, + "rences": 14918, + "Ġpurity": 14919, + "Ġarrive": 14920, + "ĠSignificant": 14921, + "ĠMAC": 14922, + "ĠOtherwise": 14923, + "oured": 14924, + "Ġtan": 14925, + "ĠRL": 14926, + "ĠQTL": 14927, + "Ġammonia": 14928, + "vmode": 14929, + "Ġmagnesium": 14930, + "Ġacknowled": 14931, + "Ġalternatives": 14932, + "idents": 14933, + "rVert": 14934, + "ĠComplete": 14935, + "ĠBone": 14936, + "yer": 14937, + "ĠBab": 14938, + "Ġeut": 14939, + "Ġnovo": 14940, + "disciplinary": 14941, + "Ġseverely": 14942, + "uki": 14943, + "ĠPN": 14944, + "leavevmode": 14945, + "clip": 14946, + "ĠSynd": 14947, + "ĠMIMO": 14948, + "adequ": 14949, + "ĠArctic": 14950, + "lycer": 14951, + "RET": 14952, + "ensed": 14953, + "coated": 14954, + "VP": 14955, + "Ġlakes": 14956, + "Ġchurch": 14957, + "Ġhomologous": 14958, + "Ġoxidase": 14959, + "ĠAud": 14960, + "Ġincrement": 14961, + "Ġneutrinos": 14962, + "arbon": 14963, + "TYPE": 14964, + "izumab": 14965, + "utable": 14966, + "Ġimplying": 14967, + "ĠMotion": 14968, + "Ġâīĥ": 14969, + "Ġpages": 14970, + "Ġplausible": 14971, + "ĠNL": 14972, + "Ġisotop": 14973, + "ĠHyd": 14974, + "Att": 14975, + "lattice": 14976, + "shore": 14977, + "Ġsucceed": 14978, + "Ġsupposed": 14979, + "ĠTransmission": 14980, + "Dimensional": 14981, + "inguistic": 14982, + "Ġcontours": 14983, + "Ġconcomit": 14984, + "Ġagrees": 14985, + "ĠDani": 14986, + "quar": 14987, + "Ġshield": 14988, + "Ġozone": 14989, + "ĠTet": 14990, + "lbrack": 14991, + "Ġwat": 14992, + "Ġcytochrome": 14993, + "tailed": 14994, + "pix": 14995, + "Ġcoex": 14996, + "ĠView": 14997, + "odef": 14998, + "ĠWild": 14999, + "ĠLE": 15000, + "hop": 15001, + "Ġpointing": 15002, + "uncture": 15003, + "Ġecology": 15004, + "Ġbab": 15005, + "rea": 15006, + "ego": 15007, + "Ġviolence": 15008, + "ĠtRNA": 15009, + "ĠRN": 15010, + "pent": 15011, + "orel": 15012, + "ĠParallel": 15013, + "Ġdrives": 15014, + "nobreak": 15015, + "Ġholog": 15016, + "Ġprobable": 15017, + "Ġentering": 15018, + "Ġsink": 15019, + "Ġswelling": 15020, + "producing": 15021, + "âĨĴâĪŀ": 15022, + "ĠSafety": 15023, + "Ġanalyse": 15024, + "series": 15025, + "Ġdrivers": 15026, + "KS": 15027, + "ĠRMS": 15028, + "Ġgenetics": 15029, + "ĠFred": 15030, + "Ġsubm": 15031, + "Ġscientists": 15032, + "ĠFD": 15033, + "ĠSolutions": 15034, + "ĠFab": 15035, + "Ġencompass": 15036, + "commutative": 15037, + "Ġadiabatic": 15038, + "butyl": 15039, + "PEG": 15040, + "Ġαβ": 15041, + "ĠStan": 15042, + "Ġclustered": 15043, + "Ġholding": 15044, + "ĠBeck": 15045, + "ĠYan": 15046, + "Ġaster": 15047, + "Ġeconom": 15048, + "Ġignored": 15049, + "uro": 15050, + "yles": 15051, + "ubbles": 15052, + "Ġfate": 15053, + "Ġperceptions": 15054, + "Ġlin": 15055, + "én": 15056, + "Ġactu": 15057, + "Ġarsen": 15058, + "Ġba": 15059, + "epoch": 15060, + "ĠStim": 15061, + "Ġmedications": 15062, + "ECs": 15063, + "ĠMinistry": 15064, + "ĠPublisher": 15065, + "Ġdepri": 15066, + "Ġobstruction": 15067, + "ĠmRNAs": 15068, + "Ġbrother": 15069, + "Ġcrossover": 15070, + "ĠTurb": 15071, + "tation": 15072, + "Ġtank": 15073, + "ĠMem": 15074, + "Ġintestine": 15075, + "Ġmicroglia": 15076, + "ĠMaxwell": 15077, + "Ġjurisdic": 15078, + "Ġphenyl": 15079, + "hyper": 15080, + "ums": 15081, + "ĠHIF": 15082, + "ĠShen": 15083, + "Ġcheckpoint": 15084, + "ĠBrownian": 15085, + "ĠâĭĨ": 15086, + "ĠStrain": 15087, + "ĠExtraction": 15088, + "Ġbatteries": 15089, + "ĠPle": 15090, + "ĠConditions": 15091, + "Ġinconsistent": 15092, + "ĠHost": 15093, + "ypical": 15094, + "Ġcrops": 15095, + "alg": 15096, + "ĠFI": 15097, + "anta": 15098, + "Ġfounded": 15099, + "Ġmarks": 15100, + "distribution": 15101, + "Ġι": 15102, + "Ġhors": 15103, + "Ġsnap": 15104, + "WM": 15105, + "Ġmanifestations": 15106, + "empl": 15107, + "Ġproving": 15108, + "leading": 15109, + "ĠACE": 15110, + "ĠLED": 15111, + "channels": 15112, + "Ġlift": 15113, + "Function": 15114, + "inase": 15115, + "supervised": 15116, + "ĠUser": 15117, + "Ġphysiology": 15118, + "Ġlinking": 15119, + "pressed": 15120, + "Ġiff": 15121, + "ĠJim": 15122, + "Ġglutathione": 15123, + "ĠTI": 15124, + "Ġane": 15125, + "enosis": 15126, + "Ġcollections": 15127, + "Ġgenetically": 15128, + "ĠFilter": 15129, + "ĠChicago": 15130, + "ĠServices": 15131, + "Ġsupersymmetric": 15132, + "Ġstriking": 15133, + "Ġirrig": 15134, + "ococcal": 15135, + "Ġfibres": 15136, + "Ġecosystems": 15137, + "uming": 15138, + "fly": 15139, + "Ġlungs": 15140, + "Ġcovariates": 15141, + "Ġlayout": 15142, + "ĠRaj": 15143, + "Ġsummation": 15144, + "abled": 15145, + "Ġfreely": 15146, + "Ġrevised": 15147, + "Ġcuts": 15148, + "ĠIntegrated": 15149, + "Ġpharmaceutical": 15150, + "Ġrespiration": 15151, + "ĠBill": 15152, + "Ġestrogen": 15153, + "raint": 15154, + "Ġpercentages": 15155, + "ĠPf": 15156, + "ĠGF": 15157, + "methylene": 15158, + "Ġorigins": 15159, + "trim": 15160, + "match": 15161, + "itney": 15162, + "ĠYe": 15163, + "Ġallocated": 15164, + "manifold": 15165, + "ĠTris": 15166, + "ĠLys": 15167, + "Ġcompressed": 15168, + "orer": 15169, + "Ġhimself": 15170, + "Ġquin": 15171, + "ĠAssembly": 15172, + "single": 15173, + "temporal": 15174, + "Ġsoph": 15175, + "Ġepidemiological": 15176, + "Ġknockout": 15177, + "Ġcompares": 15178, + "ĠSensitivity": 15179, + "Ġgirls": 15180, + "ĠValley": 15181, + "alid": 15182, + "ĠScheme": 15183, + "ĠCOMP": 15184, + "Ġrefractive": 15185, + "ĠOffice": 15186, + "Ġlatest": 15187, + "Ġprices": 15188, + "carboxyl": 15189, + "Ġeconomy": 15190, + "Ġbooks": 15191, + "ĠDD": 15192, + "Ġneoplas": 15193, + "appings": 15194, + "Ġfolding": 15195, + "momentum": 15196, + "potent": 15197, + "Ġprefix": 15198, + "ĠRiemannian": 15199, + "ĠERK": 15200, + "ĠPathway": 15201, + "Ġlarval": 15202, + "olor": 15203, + "Ġattitude": 15204, + "geqslant": 15205, + "Ġgates": 15206, + "Ġagonist": 15207, + "Ġï̍": 15208, + "ĠMCF": 15209, + "ostatic": 15210, + "micro": 15211, + "Ġdoubl": 15212, + "ĠParameter": 15213, + "Ġequivalently": 15214, + "Ġsrc": 15215, + "Most": 15216, + "ĉĠĠĠ": 15217, + "Ġrheumat": 15218, + "ĠHum": 15219, + "region": 15220, + "Ġwinds": 15221, + "Ġquadrup": 15222, + "cales": 15223, + "ulfide": 15224, + "balanced": 15225, + "Under": 15226, + "generated": 15227, + "oplasmic": 15228, + "Ġweighting": 15229, + "ĠNov": 15230, + "veloc": 15231, + "utils": 15232, + "ĠACT": 15233, + "Ġvulnerable": 15234, + "dc": 15235, + "Ġstromal": 15236, + "Ġexacerb": 15237, + "HV": 15238, + "Ġperfectly": 15239, + "txt": 15240, + "direction": 15241, + "ogon": 15242, + "Ġbim": 15243, + "ĠMarg": 15244, + "itons": 15245, + "Ġtermination": 15246, + "eda": 15247, + "Ġpretreatment": 15248, + "Ġimportantly": 15249, + "Ġduc": 15250, + "Ġartifacts": 15251, + "Stud": 15252, + "otensin": 15253, + "reland": 15254, + "ahn": 15255, + "Ġdeployed": 15256, + "ĠEF": 15257, + "ensing": 15258, + "ĠCard": 15259, + "ĠJordan": 15260, + "apunov": 15261, + "Ġanesthesia": 15262, + "Ġatherosclerosis": 15263, + "inner": 15264, + "structural": 15265, + "ĠAsp": 15266, + "throughput": 15267, + "urities": 15268, + "Ġinset": 15269, + "without": 15270, + "Ġacquire": 15271, + "Ġcombines": 15272, + "ĠShar": 15273, + "MASK": 15274, + "ĠLiter": 15275, + "Ġconscious": 15276, + "iscell": 15277, + "consistent": 15278, + "yst": 15279, + "Ġfilaments": 15280, + "ĠAlice": 15281, + "ĠGround": 15282, + "ĠmTOR": 15283, + "versal": 15284, + "Ġlineages": 15285, + "particles": 15286, + "aroscopic": 15287, + "ĠProced": 15288, + "Ġorientations": 15289, + "ĠMouse": 15290, + "Ġaccordingly": 15291, + "Ġsuppressor": 15292, + "Ġdestruction": 15293, + "OV": 15294, + "ĠProteins": 15295, + "PECT": 15296, + "Ġcup": 15297, + "Ġmonomer": 15298, + "plemental": 15299, + "Ġneutrophil": 15300, + "Ġerup": 15301, + "Ġtac": 15302, + "Ġasymptomatic": 15303, + "ĠEmbed": 15304, + "ĠRadiation": 15305, + "ĠGame": 15306, + "Ġneedle": 15307, + "Ġreuse": 15308, + "ĠDutch": 15309, + "Ġjuvenile": 15310, + "Ġmomenta": 15311, + "ĠBose": 15312, + "Ġdeveloper": 15313, + "Ġresiduals": 15314, + "Å¡": 15315, + "Ġcognition": 15316, + "ĠRegional": 15317, + "You": 15318, + "ĠConcent": 15319, + "ocin": 15320, + "ĠPartial": 15321, + "Ġcompletes": 15322, + "ĠSingh": 15323, + "ĠExc": 15324, + "ĠIsolation": 15325, + "ĠStructures": 15326, + "Ġintermitt": 15327, + "Exception": 15328, + "Ġanalytically": 15329, + "Ġelectricity": 15330, + "âĭ": 15331, + "Äį": 15332, + "Ġproteome": 15333, + "Ġic": 15334, + "kal": 15335, + "inux": 15336, + "ĠBeyond": 15337, + "Ġimplied": 15338, + "ASH": 15339, + "Ġclone": 15340, + "ĠRussia": 15341, + "ĠHod": 15342, + "tebrates": 15343, + "Ġproxy": 15344, + "holder": 15345, + "elve": 15346, + "Ġvalley": 15347, + "utely": 15348, + "Ġjobs": 15349, + "ruption": 15350, + "roids": 15351, + "ĠWhy": 15352, + "eping": 15353, + "ĠYet": 15354, + "Ġpyl": 15355, + "Ġbra": 15356, + "ilization": 15357, + "eters": 15358, + "Ġadver": 15359, + "Ġove": 15360, + "kernel": 15361, + "samples": 15362, + "ordinate": 15363, + "ĠAssuming": 15364, + "Ġcontaminated": 15365, + "Ġbipolar": 15366, + "Ġlac": 15367, + "Ġluc": 15368, + "Ġcentrifugation": 15369, + "Both": 15370, + "Ġnd": 15371, + "Ġtib": 15372, + "Before": 15373, + "ĠImmune": 15374, + "Ġash": 15375, + "Ġconditioned": 15376, + "ĠRank": 15377, + "NOS": 15378, + "Ġnanoparticle": 15379, + "Ġdependencies": 15380, + "Ġhouseholds": 15381, + "agers": 15382, + "Ġspectrophot": 15383, + "Ġbile": 15384, + "ĠHans": 15385, + "ĠAcknowledgements": 15386, + "ratio": 15387, + "ĠSecondary": 15388, + "Ġdownregulated": 15389, + "fixed": 15390, + "Obs": 15391, + "ĠHL": 15392, + "Ġsends": 15393, + "tings": 15394, + "Ġfi": 15395, + "ĠPaper": 15396, + "Ġultraviolet": 15397, + "ĠBall": 15398, + "Ġdrastic": 15399, + "ailure": 15400, + "oil": 15401, + "exchange": 15402, + "ĠDan": 15403, + "ĠAuto": 15404, + "Ġarchae": 15405, + "ĠCollection": 15406, + "Ġantiviral": 15407, + "ĠChemistry": 15408, + "Ġferr": 15409, + "choice": 15410, + "vac": 15411, + "olipid": 15412, + "Ġdanger": 15413, + "ĠLittle": 15414, + "Ġdehyd": 15415, + "Ġoccasion": 15416, + "opropyl": 15417, + "abe": 15418, + "Ġinterferon": 15419, + "Ġexport": 15420, + "onitrile": 15421, + "pd": 15422, + "ĠContext": 15423, + "ruz": 15424, + "ĠDys": 15425, + "Ġassembl": 15426, + "Ġoils": 15427, + "Image": 15428, + "rowing": 15429, + "Ġaneurys": 15430, + "Ġliquids": 15431, + "Ġactively": 15432, + "Ġevapor": 15433, + "ĠPresent": 15434, + "Ġconstitutive": 15435, + "ĠSite": 15436, + "Ġscript": 15437, + "Ġrepeats": 15438, + "ĠSIR": 15439, + "ĠFilm": 15440, + "ĠSanta": 15441, + "ĠRepresentation": 15442, + "ĠAma": 15443, + "ordon": 15444, + "ĠMolecule": 15445, + "Ġgoverning": 15446, + "ĠSoil": 15447, + "Ver": 15448, + "Ġphotonic": 15449, + "tify": 15450, + "ĠLewis": 15451, + "athered": 15452, + "Ġcategorical": 15453, + "iscellaneous": 15454, + "update": 15455, + "Ġdeficit": 15456, + "Ġadjuvant": 15457, + "ĠHenry": 15458, + "Group": 15459, + "istency": 15460, + "agraph": 15461, + "ĠImproving": 15462, + "El": 15463, + "Ġflame": 15464, + "rogate": 15465, + "omorph": 15466, + "Ġqubits": 15467, + "Ġillustration": 15468, + "ĠFlorida": 15469, + "ĠDG": 15470, + "bigcup": 15471, + "Ġprovince": 15472, + "egradation": 15473, + "ĠLandau": 15474, + "Ġgrating": 15475, + "Ġinsects": 15476, + "Ġdraft": 15477, + "ĠHb": 15478, + "Ġss": 15479, + "ĠRas": 15480, + "Ġmucosa": 15481, + "Ġhydroxyl": 15482, + "Ġmodest": 15483, + "Ġconfirming": 15484, + "ĠGalaxies": 15485, + "Gaussian": 15486, + "ĠRetrie": 15487, + "Ġrestored": 15488, + "memory": 15489, + "Ġreinforced": 15490, + "rific": 15491, + "Ġassisted": 15492, + "Ġaffiliations": 15493, + "RC": 15494, + "ducer": 15495, + "ĠIntellig": 15496, + "ĠASD": 15497, + "modium": 15498, + "Ġomitted": 15499, + "okers": 15500, + "Ġguided": 15501, + "Ġgraphical": 15502, + "ĠQual": 15503, + "Due": 15504, + "Ġnemat": 15505, + "variable": 15506, + "Ġsenescence": 15507, + "Ġpipe": 15508, + "Ġsustainable": 15509, + "Ġteacher": 15510, + "Ġthing": 15511, + "ĠGPU": 15512, + "TB": 15513, + "Ġreform": 15514, + "Ġreflex": 15515, + "Ġindicative": 15516, + "about": 15517, + "Ġopi": 15518, + "effect": 15519, + "Ġdispersed": 15520, + "kh": 15521, + "ithelial": 15522, + "ĠTreg": 15523, + "ipl": 15524, + "ĠAutomatic": 15525, + "Ġnitro": 15526, + "complete": 15527, + "Ġbosons": 15528, + "Ġpac": 15529, + "Ġavoiding": 15530, + "isl": 15531, + "plasty": 15532, + "responsive": 15533, + "dest": 15534, + "ĠBrad": 15535, + "ĠDecision": 15536, + "ĠDiscovery": 15537, + "Ġchicken": 15538, + "mus": 15539, + "ĠWITH": 15540, + "Ġtric": 15541, + "Ġquartz": 15542, + "onstruction": 15543, + "ĠFields": 15544, + "Ġassim": 15545, + "oprot": 15546, + "Ġguaranteed": 15547, + "fat": 15548, + "icts": 15549, + "Ġchol": 15550, + "ido": 15551, + "ĠKL": 15552, + "Ġchitosan": 15553, + "ĠNd": 15554, + "ĠOscill": 15555, + "Ġevolve": 15556, + "cu": 15557, + "Ġmast": 15558, + "Ġamph": 15559, + "torch": 15560, + "Vis": 15561, + "entity": 15562, + "ĠAdam": 15563, + "Ġdevoted": 15564, + "Ġethical": 15565, + "Ġpremature": 15566, + "Ġconsumer": 15567, + "Ġrecursive": 15568, + "Ġgluon": 15569, + "Ġmoderately": 15570, + "Ġmodalities": 15571, + "Ġcanal": 15572, + "force": 15573, + "ĠChlor": 15574, + "slash": 15575, + "sten": 15576, + "Ġcommercially": 15577, + "ongs": 15578, + "Ġstimulate": 15579, + "atinum": 15580, + "ĠRail": 15581, + "Ġconvective": 15582, + "Ġarteries": 15583, + "inv": 15584, + "ĠWol": 15585, + "ĠLung": 15586, + "letes": 15587, + "raphy": 15588, + "ĠHI": 15589, + "Ġgraphite": 15590, + "Ġhousing": 15591, + "each": 15592, + "Ġcalor": 15593, + "acetamide": 15594, + "rochemical": 15595, + "Ġhands": 15596, + "Ġelucidate": 15597, + "ĠChand": 15598, + "road": 15599, + "nova": 15600, + "ĠLineage": 15601, + "Ġram": 15602, + "Ġfight": 15603, + "Ġrecommendation": 15604, + "Ġamongst": 15605, + "Ġswitches": 15606, + "berry": 15607, + "Ġtherein": 15608, + "algebras": 15609, + "ĠTaken": 15610, + "azz": 15611, + "Ġfurn": 15612, + "Ġamel": 15613, + "Ġteachers": 15614, + "arn": 15615, + "Ġavoided": 15616, + "Ġaverages": 15617, + "amer": 15618, + "ĠCondition": 15619, + "Ġdislocation": 15620, + "ircon": 15621, + "Ġadolescent": 15622, + "Ġtur": 15623, + "env": 15624, + "Ġze": 15625, + "DL": 15626, + "loading": 15627, + "icidal": 15628, + "category": 15629, + "ĠDB": 15630, + "Ġmucosal": 15631, + "ĠRG": 15632, + "Ġtaxonomic": 15633, + "Ġmutagen": 15634, + "ĠStage": 15635, + "necess": 15636, + "ĠPerm": 15637, + "Ġocclusion": 15638, + "Ġexploited": 15639, + "Ġanaerobic": 15640, + "uled": 15641, + "Ġwanted": 15642, + "ĠCombining": 15643, + "Ġsubcutaneous": 15644, + "Recomm": 15645, + "Ġdiscusses": 15646, + "Ġcounterpart": 15647, + "ĠFB": 15648, + "Ġadsorbed": 15649, + "don": 15650, + "Many": 15651, + "ĠSweden": 15652, + "ĠAndrew": 15653, + "enhanced": 15654, + "Ġdoctor": 15655, + "ĠKorean": 15656, + "ĠSAR": 15657, + "Ġmating": 15658, + "aturation": 15659, + "ĠLatin": 15660, + "Ġsorting": 15661, + "Ġskip": 15662, + "Os": 15663, + "Ġwife": 15664, + "Ġcommittee": 15665, + "lvert": 15666, + "ĠACC": 15667, + "ĠComm": 15668, + "Ġsubtle": 15669, + "ĠSurvival": 15670, + "because": 15671, + "Ġfeat": 15672, + "ĠPortug": 15673, + "ARY": 15674, + "ĠISB": 15675, + "itron": 15676, + "Ġsectors": 15677, + "Ġadjoint": 15678, + "ĠAlexander": 15679, + "Ġimpurity": 15680, + "ĠMarine": 15681, + "lact": 15682, + "Ġtrapping": 15683, + "Ġgeneralize": 15684, + "filter": 15685, + "Ġpolarity": 15686, + "Also": 15687, + "Ġstabilized": 15688, + "ĠVirgin": 15689, + "Ġstores": 15690, + "PAGE": 15691, + "Ġdrawback": 15692, + "Ġâݪ": 15693, + "jet": 15694, + "Ġsubstituted": 15695, + "LINE": 15696, + "Ġoutperforms": 15697, + "Ġtermed": 15698, + "Ġweekly": 15699, + "Ġpolyc": 15700, + "Ġfused": 15701, + "Ġferromagnetic": 15702, + "lr": 15703, + "ellites": 15704, + "ĠTurn": 15705, + "ĠCulture": 15706, + "prise": 15707, + "ÅĤ": 15708, + "omposition": 15709, + "elfare": 15710, + "ĠGoogle": 15711, + "oarth": 15712, + "Ġë": 15713, + "Ġmist": 15714, + "ĠMathematics": 15715, + "SET": 15716, + "Ġepochs": 15717, + "Ġcontras": 15718, + "ishment": 15719, + "ĠFirstly": 15720, + "Ġdeclared": 15721, + "aur": 15722, + "ĠPed": 15723, + "Ġreplicate": 15724, + "Ġeligible": 15725, + "Ġconcaten": 15726, + "Ġcig": 15727, + "Ġtriplet": 15728, + "found": 15729, + "ĠCz": 15730, + "Ġaccomplished": 15731, + "Ġgoverned": 15732, + "onuclear": 15733, + "ĠNY": 15734, + "ĠEthiop": 15735, + "Ġinject": 15736, + "Ġeosin": 15737, + "annon": 15738, + "olo": 15739, + "ĠMHC": 15740, + "Ġpreoperative": 15741, + "Ġdates": 15742, + "Ġsigma": 15743, + "Long": 15744, + "ĠReson": 15745, + "Ġsymptomatic": 15746, + "Ġvolunteers": 15747, + "Ġcooperation": 15748, + "Ġarr": 15749, + "Ġcloned": 15750, + "Ġdent": 15751, + "ĠSob": 15752, + "Ġcathode": 15753, + "ctx": 15754, + "Ġencephal": 15755, + "Ġpiv": 15756, + "vive": 15757, + "umetric": 15758, + "ĠFF": 15759, + "Ġunderestim": 15760, + "Ġcoded": 15761, + "Ġanalges": 15762, + "spectral": 15763, + "Ġattracted": 15764, + "Ġtwenty": 15765, + "Ġinactive": 15766, + "Ġvictim": 15767, + "Ġholder": 15768, + "ogenes": 15769, + "Ġsuffering": 15770, + "rex": 15771, + "Ġprophyl": 15772, + "ĠUniversal": 15773, + "Ġdenom": 15774, + "stolic": 15775, + "ansion": 15776, + "SIZE": 15777, + "ĠHCV": 15778, + "Ġtechnological": 15779, + "CNN": 15780, + "enching": 15781, + "Ġdebris": 15782, + "ĠBoundary": 15783, + "linking": 15784, + "Ġstopped": 15785, + "ĠDie": 15786, + "ĠCosm": 15787, + "Ġturning": 15788, + "Ġglycoprotein": 15789, + "ĠKumar": 15790, + "Ġpg": 15791, + "ĠBY": 15792, + "Ġrising": 15793, + "ĠROC": 15794, + "Despite": 15795, + "ĠBoolean": 15796, + "ilder": 15797, + "Ġexponents": 15798, + "inters": 15799, + "printf": 15800, + "Ġlit": 15801, + "track": 15802, + "Ġfidelity": 15803, + "Ġsmoke": 15804, + "otemporal": 15805, + "Ġadmissible": 15806, + "ĠBoltzmann": 15807, + "TF": 15808, + "olite": 15809, + "liament": 15810, + "Ġcalculus": 15811, + "itized": 15812, + "Ġdivergent": 15813, + "Ġcolonization": 15814, + "Ġconvergent": 15815, + "ĠHas": 15816, + "Ġconsumers": 15817, + "Ġmyc": 15818, + "Ġcontig": 15819, + "Ġepidemiology": 15820, + "és": 15821, + "ĠAssoci": 15822, + "given": 15823, + "Ġwhilst": 15824, + "ĠKur": 15825, + "Ġreasonably": 15826, + "Ġaerobic": 15827, + "separ": 15828, + "Ġchecks": 15829, + "ĠSemantic": 15830, + "Ġserving": 15831, + "ĠAtmosp": 15832, + "Ġoxidized": 15833, + "coupled": 15834, + "ĠbioRxiv": 15835, + "Ġtuned": 15836, + "uspended": 15837, + "Ġindirectly": 15838, + "ĠCAD": 15839, + "ĠCurrently": 15840, + "Ġbehaviours": 15841, + "ĠPPAR": 15842, + "rors": 15843, + "ereb": 15844, + "Ġwidths": 15845, + "diagonal": 15846, + "ervice": 15847, + "Ġole": 15848, + "means": 15849, + "IME": 15850, + "ĠTracking": 15851, + "Ġacknowledge": 15852, + "ĠHon": 15853, + "ĠTechniques": 15854, + "ĠOxid": 15855, + "blind": 15856, + "Ġdiast": 15857, + "named": 15858, + "asitic": 15859, + "Ġpreparations": 15860, + "ĠArth": 15861, + "Ġpreserves": 15862, + "Ġfasc": 15863, + "Ġwaveform": 15864, + "ĠCrystal": 15865, + "Ġuncom": 15866, + "Ġelast": 15867, + "Ġfunctionally": 15868, + "Hom": 15869, + "ĠCoast": 15870, + "Ġoptic": 15871, + "ĠAlternatively": 15872, + "onyl": 15873, + "ĠLig": 15874, + "aldehyde": 15875, + "Ġsimulator": 15876, + "Ġdramatic": 15877, + "ifera": 15878, + "Ġexhibiting": 15879, + "Ġbehavioural": 15880, + "thick": 15881, + "xture": 15882, + "Ġexecutive": 15883, + "Ġcondensate": 15884, + "ĠOutcomes": 15885, + "Text": 15886, + "ointed": 15887, + "ĠCopyright": 15888, + "Ġdc": 15889, + "odd": 15890, + "ĠDiversity": 15891, + "chip": 15892, + "ĠBuilding": 15893, + "Ġpulsed": 15894, + "harmonic": 15895, + "Ġclinicians": 15896, + "dp": 15897, + "ĠqPCR": 15898, + "marks": 15899, + "Ġappreci": 15900, + "ĠLaser": 15901, + "Ġsizeof": 15902, + "yrene": 15903, + "Ġcooperative": 15904, + "generative": 15905, + "ĠLib": 15906, + "Ġdispersal": 15907, + "Ġevolving": 15908, + "ĠStatus": 15909, + "Ġsupercon": 15910, + "ĠMamm": 15911, + "Ġinterstitial": 15912, + "isenberg": 15913, + "Ġâľ": 15914, + "Ġconfocal": 15915, + "Ġmodulates": 15916, + "hour": 15917, + "Ġperoxide": 15918, + "dependence": 15919, + "Ġperturbed": 15920, + "illation": 15921, + "Ġplaque": 15922, + "ĠNeumann": 15923, + "Ġtriggers": 15924, + "omain": 15925, + "ĠAdministration": 15926, + "olia": 15927, + "ĠMIC": 15928, + "osaic": 15929, + "ĠGB": 15930, + "textnormal": 15931, + "Ġdominance": 15932, + "ĠExper": 15933, + "CAM": 15934, + "ĠAbout": 15935, + "ĠGarc": 15936, + "Ġsummarizes": 15937, + "App": 15938, + "charomyces": 15939, + "tificial": 15940, + "Ġglycerol": 15941, + "ĠAssumption": 15942, + "Ġtect": 15943, + "ĠFW": 15944, + "Ġcotton": 15945, + "general": 15946, + "ĠFern": 15947, + "Pt": 15948, + "Ġworker": 15949, + "Ġanion": 15950, + "grams": 15951, + "req": 15952, + "Ġlooks": 15953, + "Ġimplementations": 15954, + "ĠColumb": 15955, + "agi": 15956, + "ĠAttention": 15957, + "ĠTeam": 15958, + "oning": 15959, + "onential": 15960, + "tiny": 15961, + "ĠHighly": 15962, + "textup": 15963, + "Ġinvertible": 15964, + "ocortic": 15965, + "Inf": 15966, + "ĠOfficial": 15967, + "ĠModelling": 15968, + "Ġinclusions": 15969, + "Ġblank": 15970, + "Ġsight": 15971, + "ĠGamma": 15972, + "Ġlepton": 15973, + "Ġpneumoniae": 15974, + "Ġrotor": 15975, + "Ġethnic": 15976, + "Ġretain": 15977, + "varying": 15978, + "ĠEB": 15979, + "Ġastrocytes": 15980, + "ĠNorm": 15981, + "Ġnanom": 15982, + "classical": 15983, + "Ġshadow": 15984, + "ĠReferences": 15985, + "ĠFS": 15986, + "Ġnonnegative": 15987, + "bond": 15988, + "ĠCoh": 15989, + "Ġnumpy": 15990, + "Ġoct": 15991, + "span": 15992, + "racts": 15993, + "Ġnotably": 15994, + "Ġsophistic": 15995, + "PAR": 15996, + "Ġhormones": 15997, + "Ġtensors": 15998, + "ĠÌĦ": 15999, + "ĠConstraints": 16000, + "ĠâIJ": 16001, + "Ġtransit": 16002, + "Ġruntime": 16003, + "author": 16004, + "Ġprompt": 16005, + "ĠSG": 16006, + "Ġgrate": 16007, + "cemia": 16008, + "ĠLyapunov": 16009, + "convex": 16010, + "Ġforecasting": 16011, + "push": 16012, + "Ġjurisdictional": 16013, + "ÃĢ": 16014, + "Ġbiomedical": 16015, + "Ġepilepsy": 16016, + "feature": 16017, + "wiki": 16018, + "View": 16019, + "Ġlesser": 16020, + "Ġconjugated": 16021, + "Ġwaiting": 16022, + "ĠWord": 16023, + "IZ": 16024, + "Ġhydroxy": 16025, + "Ġdisp": 16026, + "Ġseeded": 16027, + "fitting": 16028, + "Ġstratification": 16029, + "Ġendpoint": 16030, + "Ġmediators": 16031, + "ductive": 16032, + "Ġinjections": 16033, + "ĠMicrobi": 16034, + "Ġinsert": 16035, + "ĠEmb": 16036, + "Ġstopping": 16037, + "welling": 16038, + "Ġirradiated": 16039, + "Ġmetallicity": 16040, + "vinyl": 16041, + "Ġplasmids": 16042, + "Rep": 16043, + "ĠDifferenti": 16044, + "ĠSmart": 16045, + "ĠIdentifier": 16046, + "ĠBF": 16047, + "ropic": 16048, + "Ġkinematics": 16049, + "Ġinoculated": 16050, + "CK": 16051, + "auses": 16052, + "ĠReturns": 16053, + "reement": 16054, + "Ġanticancer": 16055, + "Ġspecifications": 16056, + "Ġadds": 16057, + "Ġstake": 16058, + "Ġwheel": 16059, + "üller": 16060, + "ĠSon": 16061, + "Ġrupture": 16062, + "Ġsold": 16063, + "than": 16064, + "Ġintermedi": 16065, + "ĠNik": 16066, + "Ġtuple": 16067, + "establ": 16068, + "Ġnorthe": 16069, + "Ġsuppresses": 16070, + "Ġfet": 16071, + "Ġwashing": 16072, + "Ġinterplay": 16073, + "Ġregularly": 16074, + "EXT": 16075, + "Ġemployees": 16076, + "yz": 16077, + "rupted": 16078, + "etts": 16079, + "ĠUAV": 16080, + "Ġdifferentiable": 16081, + "inge": 16082, + "MDA": 16083, + "Ġho": 16084, + "Ġtags": 16085, + "Ġcompatibility": 16086, + "ĠÃĥ": 16087, + "bus": 16088, + "ĠUC": 16089, + "Ġtokens": 16090, + "Ġclients": 16091, + "Ġprescription": 16092, + "ĠÌĪ": 16093, + "ĠReaction": 16094, + "velocity": 16095, + "ĠNLR": 16096, + "ĠGast": 16097, + "ĠPlasmodium": 16098, + "ĠCut": 16099, + "Ġnas": 16100, + "grained": 16101, + "Ġchromosomal": 16102, + "Ġpossesses": 16103, + "Ġmath": 16104, + "Ġelected": 16105, + "placement": 16106, + "Ġcollecting": 16107, + "Ġgels": 16108, + "aire": 16109, + "Ġdeformations": 16110, + "raise": 16111, + "Ġflank": 16112, + "sulfanyl": 16113, + "zens": 16114, + "priate": 16115, + "Ġchlorophyll": 16116, + "abi": 16117, + "available": 16118, + "ا": 16119, + "Ġtack": 16120, + "fields": 16121, + "Ġrichness": 16122, + "Ġimplants": 16123, + "obenz": 16124, + "idential": 16125, + "Ġbillion": 16126, + "utor": 16127, + "ĠISBN": 16128, + "Ġinsurance": 16129, + "NET": 16130, + "Ġinadequ": 16131, + "Ġmerged": 16132, + "ĠRange": 16133, + "Ġavoidance": 16134, + "ĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ": 16135, + "rics": 16136, + "Ġexclusive": 16137, + "LV": 16138, + "Ġ": 16139, + "Ġcategorized": 16140, + "Ġultrasonic": 16141, + "ipe": 16142, + "icans": 16143, + "ĠAPP": 16144, + "Ġtraumatic": 16145, + "Ba": 16146, + "ĠAssay": 16147, + "ĠGrid": 16148, + "ĠClassical": 16149, + "ĠDES": 16150, + "Ġsoybean": 16151, + "Ġtopography": 16152, + "ĠControll": 16153, + "Ġemotions": 16154, + "Ġcarbohydrate": 16155, + "Ġconsol": 16156, + "oxyl": 16157, + "Ġbifurcation": 16158, + "Ġcoil": 16159, + "find": 16160, + "Ġwitness": 16161, + "ĠLF": 16162, + "threshold": 16163, + "Ġaddressing": 16164, + "Ġscrew": 16165, + "Ġactor": 16166, + "ĠWell": 16167, + "Ġ": 16168, + "ïĺ": 16169, + "ĠDF": 16170, + "ĠCorporation": 16171, + "ĠMitochondrial": 16172, + "Ġkpc": 16173, + "unders": 16174, + "Ġfibrin": 16175, + "axel": 16176, + "Ġpolyt": 16177, + "Ġshaped": 16178, + "rez": 16179, + "steresis": 16180, + "ĠComprehens": 16181, + "Ġ": 16182, + "dh": 16183, + "Ġsemic": 16184, + "Ġmot": 16185, + "ĠDavis": 16186, + "ska": 16187, + "ĠLH": 16188, + "Ġexpansions": 16189, + "acks": 16190, + "Ġoptimizing": 16191, + "eak": 16192, + "ĠQi": 16193, + "mul": 16194, + "ograft": 16195, + "Ġsuicide": 16196, + "calar": 16197, + "ĠScott": 16198, + "Ġthinking": 16199, + "Ġdirectional": 16200, + "Ġsurfactant": 16201, + "Ġdegraded": 16202, + "Ġregimen": 16203, + "itative": 16204, + "ĠVersion": 16205, + "ĠMaster": 16206, + "ĠSimulations": 16207, + "NCBI": 16208, + "lip": 16209, + "Ġreagents": 16210, + "Ġposted": 16211, + "osus": 16212, + "Ġlayered": 16213, + "ĠSpectrum": 16214, + "ĠGraphs": 16215, + "burst": 16216, + "Ġlived": 16217, + "Ġelemental": 16218, + "Ġ": 16219, + "ĠDiscrete": 16220, + "Ġexcluding": 16221, + "Ġoriginating": 16222, + "ĠGames": 16223, + "continuous": 16224, + "ATED": 16225, + "Ġpyram": 16226, + "luent": 16227, + "Ġtwisted": 16228, + "ĠNb": 16229, + "oxicity": 16230, + "Ġscr": 16231, + "Ġfun": 16232, + "ĠSegmentation": 16233, + "Ġphenol": 16234, + "Ġmeters": 16235, + "ĠEigen": 16236, + "ĠWeak": 16237, + "Ġschematic": 16238, + "rone": 16239, + "Ġphilos": 16240, + "titis": 16241, + "ĠIreland": 16242, + "Ġgy": 16243, + "ĠPTM": 16244, + "Ġpacking": 16245, + "ilinear": 16246, + "zeros": 16247, + "Ġubiquitin": 16248, + "ĠPressure": 16249, + "Ġinfiltr": 16250, + "ENS": 16251, + "validation": 16252, + "Ġprone": 16253, + "Ġoutline": 16254, + "hs": 16255, + "rength": 16256, + "Ġattain": 16257, + "Ġtwe": 16258, + "Ġtandem": 16259, + "Can": 16260, + "Ġlatitude": 16261, + "uitary": 16262, + "Ġvoltages": 16263, + "ĠGao": 16264, + "Ġpharmacokine": 16265, + "Ġcontextual": 16266, + "Ġxyl": 16267, + "elson": 16268, + "ĠMetabolic": 16269, + "oden": 16270, + "tiles": 16271, + "fficking": 16272, + "Ġdistilled": 16273, + "Ġalph": 16274, + "Ġpiezo": 16275, + "growth": 16276, + "Ġbore": 16277, + "Ġredundant": 16278, + "Ġdemonstration": 16279, + "Ġik": 16280, + "Ġrounds": 16281, + "ĠSri": 16282, + "figuration": 16283, + "ĠRayleigh": 16284, + "Line": 16285, + "ovol": 16286, + "Ġobstacle": 16287, + "cn": 16288, + "Ġbioactive": 16289, + "ĠOA": 16290, + "physical": 16291, + "atidyl": 16292, + "ACC": 16293, + "how": 16294, + "Ġresultant": 16295, + "ĠHubble": 16296, + "ĠVor": 16297, + "Ġensuring": 16298, + "Ġannotations": 16299, + "acyl": 16300, + "stituted": 16301, + "ĠAmb": 16302, + "feeding": 16303, + "Ġpresumably": 16304, + "Ġblockade": 16305, + "Ġsoc": 16306, + "ĠUrb": 16307, + "Ġmultiplied": 16308, + "Ġdiffe": 16309, + "Ġreflectance": 16310, + "ĠKeywords": 16311, + "ĠBayes": 16312, + "odeficiency": 16313, + "ĠBinding": 16314, + "inely": 16315, + "except": 16316, + "ĠUltr": 16317, + "ĠBrazilian": 16318, + "Number": 16319, + "Ġmassless": 16320, + "ĠConsistent": 16321, + "Ġcrisis": 16322, + "ogs": 16323, + "Ġresidence": 16324, + "Ġimper": 16325, + "fts": 16326, + "Ġcaptures": 16327, + "ĠSyndrome": 16328, + "Ġdimensionality": 16329, + "jun": 16330, + "Ġexhaus": 16331, + "ĠModern": 16332, + "Ġpercenti": 16333, + "Level": 16334, + "ĠResponses": 16335, + "Ġlaunched": 16336, + "Ġrepos": 16337, + "ĠKam": 16338, + "atility": 16339, + "Ġcarotid": 16340, + "rotic": 16341, + "ĠMand": 16342, + "UB": 16343, + "ĠMixed": 16344, + "Ġindexes": 16345, + "Ġcisplatin": 16346, + "ican": 16347, + "ionine": 16348, + "Ġhab": 16349, + "ĠIce": 16350, + "ĠGT": 16351, + "ĠAgg": 16352, + "ĠLDL": 16353, + "Ġvolcanic": 16354, + "dB": 16355, + "ĠElectric": 16356, + "Ġtmp": 16357, + "Ġgrids": 16358, + "liquid": 16359, + "prom": 16360, + "ĠGAL": 16361, + "Ġpestic": 16362, + "Ġhelium": 16363, + "Ġ": 16364, + "ĠDong": 16365, + "Ġmagnification": 16366, + "kip": 16367, + "ĠGrad": 16368, + "ĠWei": 16369, + "ĠPDF": 16370, + "ĠGluc": 16371, + "Pol": 16372, + "Ġtumorigen": 16373, + "yrin": 16374, + "Ġshelf": 16375, + "adher": 16376, + "entials": 16377, + "sn": 16378, + "Ġcultivars": 16379, + "Ġorbitals": 16380, + "ĠPEG": 16381, + "ĠAnne": 16382, + "eno": 16383, + "Ġattended": 16384, + "ophore": 16385, + "ishop": 16386, + "Ġfriends": 16387, + "posable": 16388, + "Ġimpose": 16389, + "Ġendemic": 16390, + "Ġsick": 16391, + "shifts": 16392, + "ĠOutput": 16393, + "LM": 16394, + "ĠMiscellaneous": 16395, + "Ġthousands": 16396, + "ĠDataset": 16397, + "Ġperturbative": 16398, + "oprec": 16399, + "Ġbene": 16400, + "Ġreef": 16401, + "Ġfossil": 16402, + "Ġcited": 16403, + "plicates": 16404, + "Ġrelates": 16405, + "ĠVII": 16406, + "Ġantifer": 16407, + "Ġglasses": 16408, + "closure": 16409, + "Ġrubber": 16410, + "Ġbird": 16411, + "Ġsupersymmetry": 16412, + "Ġmeson": 16413, + "hell": 16414, + "Ġparties": 16415, + "kar": 16416, + "ĠHur": 16417, + "ĠEA": 16418, + "ĠStars": 16419, + "othing": 16420, + "hot": 16421, + "illar": 16422, + "ASP": 16423, + "hev": 16424, + "ïĥ": 16425, + "aques": 16426, + "Ġcoordinated": 16427, + "ĠIslands": 16428, + "enable": 16429, + "SiO": 16430, + "Ġexceptional": 16431, + "Comb": 16432, + "ĠLike": 16433, + "Ġbroadly": 16434, + "ĠBac": 16435, + "Ġnil": 16436, + "ipartite": 16437, + "rations": 16438, + "Ġrewrite": 16439, + "Ġsalts": 16440, + "dimension": 16441, + "ĠVehic": 16442, + "Ġhundreds": 16443, + "ĠUr": 16444, + "Ġendpoints": 16445, + "ĠMODEL": 16446, + "ĠHBV": 16447, + "ĠVirtual": 16448, + "ĠConfl": 16449, + "ĠPractice": 16450, + "ĠAFM": 16451, + "Ġadversarial": 16452, + "Ġdiameters": 16453, + "Ġtransported": 16454, + "REM": 16455, + "ĠBart": 16456, + "Ġedition": 16457, + "Ġturbine": 16458, + "Ġminus": 16459, + "otechnology": 16460, + "Ig": 16461, + "Ġbigger": 16462, + "abul": 16463, + "Ġperoxidase": 16464, + "white": 16465, + "ĠSed": 16466, + "dihydro": 16467, + "Ġsegregation": 16468, + "Ġreductase": 16469, + "Ġhoriz": 16470, + "Ġinfinitely": 16471, + "availability": 16472, + "Ġactivator": 16473, + "Ġcensus": 16474, + "pressing": 16475, + "Ġspirit": 16476, + "conver": 16477, + "ĠQuantification": 16478, + "omerase": 16479, + "Ġrelapse": 16480, + "ĠFinal": 16481, + "Ġoverweight": 16482, + "aper": 16483, + "Ġformulae": 16484, + "rr": 16485, + "Ġfemoral": 16486, + "Ġfoam": 16487, + "otics": 16488, + "Ġprovider": 16489, + "Ġinstrumental": 16490, + "Ġadvice": 16491, + "Ġoccupation": 16492, + "assembly": 16493, + "bias": 16494, + "ĠNOT": 16495, + "restric": 16496, + "ĠProtocol": 16497, + "ĠCandida": 16498, + "ĠRhod": 16499, + "arden": 16500, + "funder": 16501, + "osens": 16502, + "Ġparams": 16503, + "front": 16504, + "Ġexerc": 16505, + "Ġgalactic": 16506, + "rvert": 16507, + "Ġimbalance": 16508, + "Ġkilling": 16509, + "ĠGenomic": 16510, + "Ġip": 16511, + "Ġcave": 16512, + "Ġfalc": 16513, + "ĠRM": 16514, + "Ġcarries": 16515, + "global": 16516, + "Ġcube": 16517, + "Ġrigorous": 16518, + "Ġcomputes": 16519, + "QP": 16520, + "Ġexposures": 16521, + "cover": 16522, + "ologically": 16523, + "Oper": 16524, + "Ġpec": 16525, + "Ġinhomogeneous": 16526, + "Ġservers": 16527, + "aliana": 16528, + "nb": 16529, + "Ġexplaining": 16530, + "Ġshrink": 16531, + "Ġcomorbid": 16532, + "ethoxy": 16533, + "outheast": 16534, + "Ġcourses": 16535, + "ĠNM": 16536, + "ĠShape": 16537, + "Ġflies": 16538, + "ĠMir": 16539, + "Ġpublicly": 16540, + "Ġphotometric": 16541, + "versible": 16542, + "olev": 16543, + "Ġvulnerability": 16544, + "Ġcations": 16545, + "Ġseeking": 16546, + "UTR": 16547, + "Ġdecomposed": 16548, + "Ġhus": 16549, + "Ġdisappear": 16550, + "Ġencounter": 16551, + "Ġtransforming": 16552, + "Ġpolymeric": 16553, + "Ġdiscretization": 16554, + "otoxic": 16555, + "ĠIter": 16556, + "ĠMari": 16557, + "Ġunfold": 16558, + "ĠAdult": 16559, + "obacillus": 16560, + "metal": 16561, + "berger": 16562, + "raphene": 16563, + "respective": 16564, + "Ġsurvive": 16565, + "ovich": 16566, + "Ġprotects": 16567, + "ĠRog": 16568, + "Ġimmunotherapy": 16569, + "ĠDSM": 16570, + "Ġanalogy": 16571, + "ĠPER": 16572, + "ĠPython": 16573, + "hum": 16574, + "ĠAdj": 16575, + "ĠLikewise": 16576, + "Ġ": 16577, + "Ġstomach": 16578, + "Ġinit": 16579, + "Ġwires": 16580, + "Ġingredients": 16581, + "Ġperceptual": 16582, + "Hand": 16583, + "Back": 16584, + "Ġmood": 16585, + "Ġdeformed": 16586, + "ĠRead": 16587, + "Ġrhiz": 16588, + "ĠOrganism": 16589, + "ĠIndones": 16590, + "annot": 16591, + "ictory": 16592, + "Ġtended": 16593, + "ĠSound": 16594, + "iax": 16595, + "Sr": 16596, + "ĠTab": 16597, + "ĠLaplacian": 16598, + "oluminescence": 16599, + "backslash": 16600, + "iologic": 16601, + "Ġtypename": 16602, + "ĠYear": 16603, + "Dependent": 16604, + "Ġslides": 16605, + "Ġsacrific": 16606, + "Ġconcomitant": 16607, + "opsies": 16608, + "Bigg": 16609, + "peak": 16610, + "ĠApplying": 16611, + "Ġcodon": 16612, + "ĠSimultaneous": 16613, + "tise": 16614, + "Ġtertiary": 16615, + "ĠPoll": 16616, + "Ġrevision": 16617, + "RAF": 16618, + "xmm": 16619, + "Ġsuited": 16620, + "ĠRecommend": 16621, + "ĠRy": 16622, + "Ġsake": 16623, + "Ġstretch": 16624, + "ĠSampling": 16625, + "Ġtubular": 16626, + "Ġpark": 16627, + "Ġultimate": 16628, + "Ġlands": 16629, + "ĠCriter": 16630, + "assay": 16631, + "mor": 16632, + "Ġdocking": 16633, + "Ġgradual": 16634, + "Ġeditor": 16635, + "Ġpolice": 16636, + "affin": 16637, + "ĠDeath": 16638, + "Ġpromoters": 16639, + "assic": 16640, + "Ġwriter": 16641, + "ĠVolume": 16642, + "iso": 16643, + "Ġdisag": 16644, + "token": 16645, + "Ġsteroid": 16646, + "Non": 16647, + "ĠMethyl": 16648, + "Americ": 16649, + "due": 16650, + "ĠLess": 16651, + "Ġdyst": 16652, + "ĠStatement": 16653, + "ĠTwenty": 16654, + "Ġaccessed": 16655, + "Ġblotting": 16656, + "ĠCOPD": 16657, + "Ġsteam": 16658, + "Ġdescriptive": 16659, + "ĠVery": 16660, + "Ġcapacities": 16661, + "ĠPersonal": 16662, + "acid": 16663, + "ähler": 16664, + "estival": 16665, + "Context": 16666, + "Ġastr": 16667, + "Analysis": 16668, + "Ġsept": 16669, + "Ġprinted": 16670, + "dual": 16671, + "aman": 16672, + "erer": 16673, + "Ġweakness": 16674, + "ìĿ": 16675, + "ĠTranslation": 16676, + "Ġpropagating": 16677, + "ĠSections": 16678, + "aca": 16679, + "Ġconfusion": 16680, + "IK": 16681, + "Ġframeworks": 16682, + "Ġsituated": 16683, + "Ġstays": 16684, + "nodes": 16685, + "chen": 16686, + "artments": 16687, + "Ġfreezing": 16688, + "ws": 16689, + "nett": 16690, + "Ġcontrollers": 16691, + "Ġsilic": 16692, + "LAST": 16693, + "foot": 16694, + "ĠDISCU": 16695, + "RH": 16696, + "ridine": 16697, + "ĠRev": 16698, + "perg": 16699, + "pyrim": 16700, + "flags": 16701, + "ĠGuide": 16702, + "Ġspeaker": 16703, + "tisol": 16704, + "rell": 16705, + "ĠDEG": 16706, + "Ġfu": 16707, + "ĠGut": 16708, + "Ġshar": 16709, + "Ġgross": 16710, + "Ġcrosses": 16711, + "wavelength": 16712, + "ĠApplied": 16713, + "ïve": 16714, + "ĠHB": 16715, + "ĠEdge": 16716, + "Ġinertial": 16717, + "Ġvocal": 16718, + "production": 16719, + "pathetic": 16720, + "Ġplanetary": 16721, + "Ġsister": 16722, + "Ġminima": 16723, + "Ġlongest": 16724, + "Ġflash": 16725, + "Ġperiodon": 16726, + "Ġepidermal": 16727, + "Ġfloating": 16728, + "GET": 16729, + "ĠTake": 16730, + "pdf": 16731, + "ĠLiquid": 16732, + "Ġremarkably": 16733, + "Sign": 16734, + "Ġshells": 16735, + "oglobulin": 16736, + "quilibrium": 16737, + "ĠMoore": 16738, + "ĠAdvers": 16739, + "ĠMycobacterium": 16740, + "Invitrogen": 16741, + "Ġthaliana": 16742, + "BY": 16743, + "ĠBit": 16744, + "Ġts": 16745, + "Ġsynchronous": 16746, + "yx": 16747, + "Ġpropagator": 16748, + "ĠIncreasing": 16749, + "iparum": 16750, + "Ġfreeze": 16751, + "ĠSelective": 16752, + "afe": 16753, + "Ġstrept": 16754, + "phantom": 16755, + "ĠGenerally": 16756, + "Ġalternate": 16757, + "ĠConvergence": 16758, + "////////////////": 16759, + "enging": 16760, + "ĠRandomized": 16761, + "develop": 16762, + "predict": 16763, + "ressor": 16764, + "Ġmathematics": 16765, + "fr": 16766, + "ĠComputation": 16767, + "ĠMalays": 16768, + "Ġbreathing": 16769, + "Through": 16770, + "ĠSIM": 16771, + "Ġanode": 16772, + "oad": 16773, + "ĠATCC": 16774, + "Ġconstituent": 16775, + "ĠMeasuring": 16776, + "ĠfMRI": 16777, + "Ġanemia": 16778, + "liest": 16779, + "Ġhemisphere": 16780, + "Ġmaxima": 16781, + "Ġtemporary": 16782, + "Ġdz": 16783, + "otoxin": 16784, + "Count": 16785, + "oned": 16786, + "ú": 16787, + "Ġcollaborative": 16788, + "Ġkb": 16789, + "Ġversa": 16790, + "ĠSwedish": 16791, + "ika": 16792, + "Ġdialysis": 16793, + "Ġperovsk": 16794, + "Ġwilling": 16795, + "ĠGreek": 16796, + "Output": 16797, + "Ġsemigroup": 16798, + "Ġbottlen": 16799, + "ĠGibbs": 16800, + "dark": 16801, + "Ġrheumatoid": 16802, + "urring": 16803, + "matched": 16804, + "Ġsophisticated": 16805, + "Ġcustomer": 16806, + "tetrahydro": 16807, + "XY": 16808, + "bug": 16809, + "Ġmorning": 16810, + "ĠCVD": 16811, + "Ġmappings": 16812, + "ĠMSCs": 16813, + "ĠDH": 16814, + "Ġquatern": 16815, + "health": 16816, + "ı": 16817, + "Ġtemp": 16818, + "ĠJew": 16819, + "ĠIl": 16820, + "Ġvortices": 16821, + "Ġserine": 16822, + "ĠOxygen": 16823, + "weg": 16824, + "Ġexplanations": 16825, + "PG": 16826, + "Ġciti": 16827, + "Ġlocality": 16828, + "===": 16829, + "ĠThom": 16830, + "Ġdairy": 16831, + "Block": 16832, + "ordial": 16833, + "akov": 16834, + "Ġglioma": 16835, + "Ġtransaction": 16836, + "Ġincremental": 16837, + "anche": 16838, + "Ret": 16839, + "magnetic": 16840, + "pyrrol": 16841, + "ĠPic": 16842, + "Ġamelior": 16843, + "oxidant": 16844, + "roviral": 16845, + "oratory": 16846, + "Ġsav": 16847, + "ĠStream": 16848, + "Ġsuperf": 16849, + "ĠICU": 16850, + "Ġevidenced": 16851, + "Ġrepeatedly": 16852, + "Ġrated": 16853, + "ĠPit": 16854, + "FAULT": 16855, + "Ġhat": 16856, + "ĠContent": 16857, + "Ġisoform": 16858, + "VER": 16859, + "Ġnodal": 16860, + "Ġscheduled": 16861, + "Ġshoulder": 16862, + "Ġtap": 16863, + "Ġportal": 16864, + "Ġtraps": 16865, + "aev": 16866, + "ĠSOD": 16867, + "ematic": 16868, + "Ġenj": 16869, + "Ġreticulum": 16870, + "ĠMinister": 16871, + "ĠSel": 16872, + "Ġfalling": 16873, + "rost": 16874, + "NG": 16875, + "fd": 16876, + "nitro": 16877, + "ĠMove": 16878, + "relativistic": 16879, + "enges": 16880, + "ĠSST": 16881, + "ĠInv": 16882, + "Ġfinish": 16883, + "ĠPoland": 16884, + "osecond": 16885, + "ĠBAL": 16886, + "oarthritis": 16887, + "Ġoptics": 16888, + "ĠSky": 16889, + "Ġadvoc": 16890, + "Ġhemorrhage": 16891, + "Ġmodulating": 16892, + "nis": 16893, + "Ġmachinery": 16894, + "Ġupdating": 16895, + "Ġcharacterizing": 16896, + "ishman": 16897, + "Ġtemplates": 16898, + "ĠLaplace": 16899, + "ĠEns": 16900, + "Recently": 16901, + "orus": 16902, + "arts": 16903, + "diffusion": 16904, + "ĠLevels": 16905, + "aga": 16906, + "ĠInj": 16907, + "ĠLayer": 16908, + "Ġremn": 16909, + "Ġelasticity": 16910, + "Ġmerely": 16911, + "Ġfission": 16912, + "engue": 16913, + "make": 16914, + "Ġmonop": 16915, + "Ġurea": 16916, + "ĠSimon": 16917, + "miR": 16918, + "ĠSecondly": 16919, + "uric": 16920, + "ĠVariable": 16921, + "ilis": 16922, + "Ġmultiplicative": 16923, + "ĠNoise": 16924, + "Ġswitched": 16925, + "Ġnicot": 16926, + "Ġefficiencies": 16927, + "hema": 16928, + "Ġappointed": 16929, + "guided": 16930, + "Ġwinning": 16931, + "ĠMechanics": 16932, + "Ġneo": 16933, + "ĠBRCA": 16934, + "udi": 16935, + "Ġcontainer": 16936, + "shop": 16937, + "Ġsuggestions": 16938, + "KB": 16939, + "Ġsubstitute": 16940, + "Ox": 16941, + "VC": 16942, + "Ġstone": 16943, + "anna": 16944, + "ĠDepression": 16945, + "Ġcontemporary": 16946, + "Ġoutliers": 16947, + "quet": 16948, + "ĠZheng": 16949, + "Ġoccl": 16950, + "Ġalveolar": 16951, + "expressing": 16952, + "Ġcomfort": 16953, + "Ġignore": 16954, + "Among": 16955, + "ĠKlein": 16956, + "Ġrhythm": 16957, + "Ġimmers": 16958, + "Ġfaith": 16959, + "bling": 16960, + "Ġaugmentation": 16961, + "ĠPrevention": 16962, + "Ġhepar": 16963, + "Ġnotations": 16964, + "Ġhematopoietic": 16965, + "perfect": 16966, + "Ġshares": 16967, + "notin": 16968, + "Ġpictures": 16969, + "ĠAcknowledgments": 16970, + "Ġtick": 16971, + "Ġunrelated": 16972, + "ĠTool": 16973, + "Ġmas": 16974, + "osocial": 16975, + "gest": 16976, + "ushed": 16977, + "Ġphosphorylated": 16978, + "Ġceramic": 16979, + "cool": 16980, + "orylation": 16981, + "Ġdeficient": 16982, + "Ġrelaxed": 16983, + "ĠAnalyses": 16984, + "ecraft": 16985, + "Ġretina": 16986, + "ĠInternal": 16987, + "Ġspite": 16988, + "Ġrecipients": 16989, + "Ġshut": 16990, + "Ġethylene": 16991, + "ĠGulf": 16992, + "Ġunaffected": 16993, + "ĠResource": 16994, + "ĠNet": 16995, + "Ġperpet": 16996, + "Ġslab": 16997, + "report": 16998, + "Ġμmol": 16999, + "Ġidx": 17000, + "Ġskill": 17001, + "ĠInduction": 17002, + "Ġmalignancy": 17003, + "Ġcv": 17004, + "Ġdiffering": 17005, + "Ġappropriately": 17006, + "ijing": 17007, + "Ġwarrant": 17008, + "rally": 17009, + "Ġalgae": 17010, + "weights": 17011, + "casts": 17012, + "Ġocular": 17013, + "racycl": 17014, + "Ġdominates": 17015, + "Ġleuc": 17016, + "Where": 17017, + "phon": 17018, + "Ġsocioeconomic": 17019, + "itzerland": 17020, + "Ġresilience": 17021, + "Ġneighbourhood": 17022, + "Ġtone": 17023, + "psych": 17024, + "ĠOrganic": 17025, + "Ġgather": 17026, + "Ġfalciparum": 17027, + "Ġengineered": 17028, + "ĠAvail": 17029, + "intering": 17030, + "Ġclimatic": 17031, + "ĠEvolutionary": 17032, + "NMR": 17033, + "Ġrev": 17034, + "central": 17035, + "ĠSin": 17036, + "Ġdeclined": 17037, + "opausal": 17038, + "Ġalarm": 17039, + "Rightarrow": 17040, + "sex": 17041, + "Ġenergetic": 17042, + "ïĤ": 17043, + "Ġdiscs": 17044, + "Ġolfactory": 17045, + "uripot": 17046, + "spectrum": 17047, + "spot": 17048, + "Ġhemoglobin": 17049, + "Mark": 17050, + "cov": 17051, + "arboxyl": 17052, + "Ġindications": 17053, + "Ġsalmon": 17054, + "Ġsearched": 17055, + "Ġended": 17056, + "rologic": 17057, + "rfloor": 17058, + "Ġautism": 17059, + "Ġselen": 17060, + "ĠHung": 17061, + "ĠInference": 17062, + "Ġmammary": 17063, + "lfloor": 17064, + "Ġseroton": 17065, + "Ġfunded": 17066, + "ĠViet": 17067, + "Ġrivers": 17068, + "ĠReinfor": 17069, + "urg": 17070, + "Ġalbicans": 17071, + "ĠThermo": 17072, + "ERROR": 17073, + "Ġmutually": 17074, + "Ġirr": 17075, + "ĠRat": 17076, + "Ġimg": 17077, + "Ġlymphocyte": 17078, + "ĠRefs": 17079, + "ĠSparse": 17080, + "holders": 17081, + "Free": 17082, + "RED": 17083, + "ĠGauss": 17084, + "Ġcircadian": 17085, + "ĠJin": 17086, + "Ġconstitutes": 17087, + "Ġwors": 17088, + "Ġfeatured": 17089, + "ocent": 17090, + "lete": 17091, + "Ġontology": 17092, + "Ġbilayer": 17093, + "ĠCambridge": 17094, + "Ġencryption": 17095, + "rotron": 17096, + "etti": 17097, + "ĠAer": 17098, + "Ġcouples": 17099, + "rail": 17100, + "Ġtwist": 17101, + "Ġridge": 17102, + "GAN": 17103, + "iders": 17104, + "SHIFT": 17105, + "Ġdiffus": 17106, + "Ġmeant": 17107, + "ĠSchwarz": 17108, + "Sb": 17109, + "Ġarcs": 17110, + "Notice": 17111, + "iy": 17112, + "Ġemerge": 17113, + "kwargs": 17114, + "Eff": 17115, + "Ent": 17116, + "ionization": 17117, + "choline": 17118, + "ustries": 17119, + "acher": 17120, + "spl": 17121, + "population": 17122, + "fol": 17123, + "Ġquestionnaires": 17124, + "Ġallergic": 17125, + "wich": 17126, + "ĠVacc": 17127, + "Ġattained": 17128, + "ĠAnimals": 17129, + "amics": 17130, + "ĠRegarding": 17131, + "ĠSemi": 17132, + "Ġglac": 17133, + "ĠEfficacy": 17134, + "Ġsynergistic": 17135, + "ISH": 17136, + "Ġmaintains": 17137, + "Ġsongs": 17138, + "ĠNegative": 17139, + "amoto": 17140, + "ĠModified": 17141, + "Ġseparable": 17142, + "Ġbinaries": 17143, + "Ġaccessibility": 17144, + "Iter": 17145, + "din": 17146, + "ĠBinary": 17147, + "equilibrium": 17148, + "Ġcue": 17149, + "magn": 17150, + "Ġedema": 17151, + "�": 17152, + "Ġpositioned": 17153, + "Ġcharging": 17154, + "Ġunivariate": 17155, + "hep": 17156, + "Ġclade": 17157, + "Ġcysteine": 17158, + "racle": 17159, + "Ġrescue": 17160, + "habit": 17161, + "ĠDISCUSSION": 17162, + "Ġdepicts": 17163, + "pole": 17164, + "Ġstenosis": 17165, + "Ġveter": 17166, + "pringer": 17167, + "ĠPow": 17168, + "Ġcovariant": 17169, + "Ġmodifying": 17170, + "Algorithm": 17171, + "averaged": 17172, + "alo": 17173, + "reson": 17174, + "Ġcharacterised": 17175, + "Ġni": 17176, + "Ġseemed": 17177, + "ĠRom": 17178, + "short": 17179, + "NV": 17180, + "Ġfertility": 17181, + "ĠMemb": 17182, + "Ġlying": 17183, + "Ġinstitution": 17184, + "images": 17185, + "ĠBorel": 17186, + "fsys": 17187, + "cataly": 17188, + "Ġseparating": 17189, + "biotic": 17190, + "mel": 17191, + "pgfsys": 17192, + "ĠJackson": 17193, + "Ġbag": 17194, + "ograp": 17195, + "propyl": 17196, + "ĠProgramming": 17197, + "ocratic": 17198, + "Ġpion": 17199, + "ĠGradient": 17200, + "Ġsphe": 17201, + "Ġinline": 17202, + "Ġdominate": 17203, + "Ġsuffered": 17204, + "ĠDiseases": 17205, + "igenous": 17206, + "will": 17207, + "Ġamin": 17208, + "adherin": 17209, + "ĠTro": 17210, + "adjusted": 17211, + "EW": 17212, + "Ġdebut": 17213, + "nea": 17214, + "ĠDun": 17215, + "Ġdictionary": 17216, + "operatively": 17217, + "KA": 17218, + "beit": 17219, + "Ġpersonnel": 17220, + "ĠŽ": 17221, + "review": 17222, + "into": 17223, + "ĠTokyo": 17224, + "Ġtrop": 17225, + "Ġventric": 17226, + "ĠMETHODS": 17227, + "Ġimplication": 17228, + "akis": 17229, + "ĠCMB": 17230, + "Ġtransmitter": 17231, + "oichi": 17232, + "ĠNigeria": 17233, + "ĠKon": 17234, + "Ġbear": 17235, + "ĠKan": 17236, + "ĠPlot": 17237, + "ĠSPSS": 17238, + "ĠBiology": 17239, + "Ġbaryon": 17240, + "ĠmicroRNA": 17241, + "Ġreproducibility": 17242, + "Ġlactate": 17243, + "Ġpolyphen": 17244, + "ĠMt": 17245, + "ĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ": 17246, + "endit": 17247, + "Ġhydrothermal": 17248, + "Ġwealth": 17249, + "Ġhadron": 17250, + "Ġwhereby": 17251, + "ellum": 17252, + "ĠDiffusion": 17253, + "ĠOrigin": 17254, + "Ġnonlinearity": 17255, + "Ġinformative": 17256, + "Ġvisited": 17257, + "Ġvirtually": 17258, + "ĠTun": 17259, + "Ġreset": 17260, + "ĠElectrical": 17261, + "ĠGlu": 17262, + "ĠSAM": 17263, + "ĠIsing": 17264, + "ĠStra": 17265, + "onder": 17266, + "Ġdies": 17267, + "Ġreciprocal": 17268, + "Check": 17269, + "ĠGuidelines": 17270, + "hester": 17271, + "Ġproblematic": 17272, + "ĠAtomic": 17273, + "Ġconcentrate": 17274, + "steps": 17275, + "json": 17276, + "Recommended": 17277, + "ĠScreening": 17278, + "Ġnaive": 17279, + "Ġpractitioners": 17280, + "Ġfasting": 17281, + "Ġmechanistic": 17282, + "options": 17283, + "Ptr": 17284, + "ITE": 17285, + "Work": 17286, + "âĢĺ": 17287, + "rafts": 17288, + "Ġunw": 17289, + "Ġannihilation": 17290, + "objective": 17291, + "ĠDynamical": 17292, + "adec": 17293, + "ĠLith": 17294, + "Ġextracting": 17295, + "Ġcoral": 17296, + "ĠStable": 17297, + "Ġbackgrounds": 17298, + "omorphisms": 17299, + "ĠâĪ«": 17300, + "Ġgrew": 17301, + "Inst": 17302, + "gels": 17303, + "Ġinhal": 17304, + "dam": 17305, + "heim": 17306, + "benzyl": 17307, + "Ġpelvic": 17308, + "Ġdiarr": 17309, + "Ġdiode": 17310, + "Ġempir": 17311, + "ĠAlf": 17312, + "ĠUncertain": 17313, + "ĠHCl": 17314, + "Ġjointly": 17315, + "Ġdepar": 17316, + "Ġmerging": 17317, + "Ġchi": 17318, + "apt": 17319, + "Ġplt": 17320, + "Ġidi": 17321, + "Ġperfor": 17322, + "stituting": 17323, + "page": 17324, + "aré": 17325, + "indices": 17326, + "putation": 17327, + "different": 17328, + "burn": 17329, + "Ġsurrounded": 17330, + "ĠTL": 17331, + "untary": 17332, + "strip": 17333, + "lan": 17334, + "Ġcow": 17335, + "ĠSab": 17336, + "ĠGaAs": 17337, + "pf": 17338, + "Ġesophageal": 17339, + "ĠAlt": 17340, + "Ġhospitalization": 17341, + "ĠApproximation": 17342, + "Organism": 17343, + "ĠFair": 17344, + "Ġtracing": 17345, + "Ġpreferentially": 17346, + "Ġlowering": 17347, + "uliar": 17348, + "ĠDeriv": 17349, + "Ġphytoplankton": 17350, + "omyc": 17351, + "That": 17352, + "ĠIsrael": 17353, + "Ġminimized": 17354, + "Ġanything": 17355, + "rule": 17356, + "pow": 17357, + "Ġfamous": 17358, + "ĠAccuracy": 17359, + "Ġphotocatalytic": 17360, + "ĠNonetheless": 17361, + "Ġdivisor": 17362, + "vb": 17363, + "Ġcameras": 17364, + "ĠWales": 17365, + "ĠContributions": 17366, + "Ġdisplacements": 17367, + "ĠTam": 17368, + "Ġvolumetric": 17369, + "essional": 17370, + "Ġcompensate": 17371, + "Ġace": 17372, + "triangle": 17373, + "buff": 17374, + "Ġnamespace": 17375, + "Ġbounding": 17376, + "ynchronous": 17377, + "md": 17378, + "Ġimagery": 17379, + "itated": 17380, + "Ġoriginated": 17381, + "ĠBelg": 17382, + "ĠECG": 17383, + "existing": 17384, + "ĠStokes": 17385, + "sensitivity": 17386, + "tidine": 17387, + "ĠWM": 17388, + "Ġmonotone": 17389, + "Ġproceeds": 17390, + "ĠClustering": 17391, + "ĠIoT": 17392, + "ernary": 17393, + "alamic": 17394, + "ĠCollaboration": 17395, + "ĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ": 17396, + "OLD": 17397, + "Îĺ": 17398, + "ĠNanopar": 17399, + "ĠMultiv": 17400, + "Ġcystic": 17401, + "pire": 17402, + "Ġoperates": 17403, + "Ġmediating": 17404, + "Ġbeneath": 17405, + "obe": 17406, + "gate": 17407, + "Ġoocytes": 17408, + "Ġmargins": 17409, + "ymmetries": 17410, + "Ġreligious": 17411, + "ĠNit": 17412, + "Ġcutaneous": 17413, + "ANS": 17414, + "Ġdevelops": 17415, + "asia": 17416, + "ĠRoberts": 17417, + "avier": 17418, + "Ġsimplic": 17419, + "Ġrevealing": 17420, + "UND": 17421, + "Ġtea": 17422, + "Ġlysis": 17423, + "Ġaggregated": 17424, + "ĠRGB": 17425, + "Ġcorro": 17426, + "Ġbir": 17427, + "inae": 17428, + "vd": 17429, + "Ġcourt": 17430, + "Ġcontroversial": 17431, + "Ġtow": 17432, + "Ġhysteresis": 17433, + "enberg": 17434, + "Ġenters": 17435, + "png": 17436, + "ĠFlex": 17437, + "Assume": 17438, + "ĠBad": 17439, + "ĠSimilarities": 17440, + "Experim": 17441, + "ATH": 17442, + "Ġut": 17443, + "terms": 17444, + "ĠMol": 17445, + "Ġvisually": 17446, + "Ġadoption": 17447, + "Ġprinting": 17448, + "Ġequiv": 17449, + "ĠPert": 17450, + "Ġpercol": 17451, + "Ġsomeone": 17452, + "abulary": 17453, + "Ġlever": 17454, + "ĠHaus": 17455, + "icillin": 17456, + "itar": 17457, + "Ġtourn": 17458, + "Altern": 17459, + "Exp": 17460, + "~~~~": 17461, + "ĠFo": 17462, + "Ġabol": 17463, + "median": 17464, + "Ġrolling": 17465, + "hm": 17466, + "Ġtelescope": 17467, + "ĠCav": 17468, + "Ġseedlings": 17469, + "inhib": 17470, + "Ġdin": 17471, + "Ġimpurities": 17472, + "Ġamplifier": 17473, + "ĠKer": 17474, + "Ġdiminished": 17475, + "PB": 17476, + "fib": 17477, + "rock": 17478, + "ĠBin": 17479, + "Ġphotosynthetic": 17480, + "ĠCrypt": 17481, + "Ġpreterm": 17482, + "Ġhits": 17483, + "Ġfractal": 17484, + "Ġdiscarded": 17485, + "Ġendocrine": 17486, + "oshi": 17487, + "Ġmodulo": 17488, + "wt": 17489, + "Ġquenching": 17490, + "Ġsounds": 17491, + "ĠEDTA": 17492, + "reactive": 17493, + "Ġresist": 17494, + "anghai": 17495, + "Ġnarr": 17496, + "Ġinitiate": 17497, + "ĠSaint": 17498, + "XR": 17499, + "GeV": 17500, + "ĠIndependent": 17501, + "Ġinjective": 17502, + "upus": 17503, + "Ġlinguistic": 17504, + "Ġanalogues": 17505, + "Ġdissection": 17506, + "Ġlasers": 17507, + "diab": 17508, + "ĠTele": 17509, + "Ġcracks": 17510, + "Ġbrane": 17511, + "VO": 17512, + "ĠExtended": 17513, + "Ġtells": 17514, + "Ġremarks": 17515, + "ulting": 17516, + "ĠBurn": 17517, + "dL": 17518, + "ressible": 17519, + "ĠChap": 17520, + "Ġsq": 17521, + "Ġreproduced": 17522, + "ĠBcl": 17523, + "Ġswarm": 17524, + "opathology": 17525, + "chrotron": 17526, + "Ġmine": 17527, + "Ġhadronic": 17528, + "ĠLocalization": 17529, + "ĠMotor": 17530, + "Ġvisualize": 17531, + "Ġcats": 17532, + "Ġbalancing": 17533, + "ĠSched": 17534, + "CoA": 17535, + "Ġthermodynamics": 17536, + "ĠDiagnostic": 17537, + "Ġrelief": 17538, + "Ġpositivity": 17539, + "Ġhub": 17540, + "ĠInfrared": 17541, + "Sur": 17542, + "omed": 17543, + "Ġoptically": 17544, + "Ġvascul": 17545, + "isations": 17546, + "encoder": 17547, + "Ġcopolymer": 17548, + "Ġrestore": 17549, + "Ġinertia": 17550, + "ubicin": 17551, + "Ġetiology": 17552, + "ĠSecret": 17553, + "ĠCW": 17554, + "Const": 17555, + "ĠBrit": 17556, + "ĠConstant": 17557, + "ĠDIS": 17558, + "Ġdiscipl": 17559, + "bra": 17560, + "ĠOral": 17561, + "ĠUL": 17562, + "Ġdeline": 17563, + "Ġnucleon": 17564, + "Ġemployment": 17565, + "ĠRD": 17566, + "qq": 17567, + "ĠCarolina": 17568, + "ĠGab": 17569, + "Ġassertion": 17570, + "CMC": 17571, + "rgb": 17572, + "Frame": 17573, + "ĠJust": 17574, + "Ġinoculation": 17575, + "cluding": 17576, + "Ġoscillatory": 17577, + "Ġcancel": 17578, + "ĠPoinc": 17579, + "pora": 17580, + "ĠJul": 17581, + "ruvate": 17582, + "Ġpolitic": 17583, + "urus": 17584, + "ĠAdvances": 17585, + "ĠRoot": 17586, + "thood": 17587, + "oxygenase": 17588, + "msg": 17589, + "ĠkV": 17590, + "Ġadmit": 17591, + "Ġrefractory": 17592, + "Ġcloning": 17593, + "Ġfatal": 17594, + "plantation": 17595, + "ĠGir": 17596, + "Ġtes": 17597, + "ĠRho": 17598, + "ohn": 17599, + "Ġinnovation": 17600, + "Ġsending": 17601, + "Ġcable": 17602, + "Ġniche": 17603, + "Ġreserve": 17604, + "Ġatrophy": 17605, + "athan": 17606, + "ĠÃij": 17607, + "itization": 17608, + "Ġfan": 17609, + "Ġbubbles": 17610, + "ĠTheorems": 17611, + "ĠSwitzerland": 17612, + "ĠHeisenberg": 17613, + "ĠReduced": 17614, + "Ra": 17615, + "Zr": 17616, + "ĠPossible": 17617, + "Upsilon": 17618, + "ĠAgric": 17619, + "ellect": 17620, + "nds": 17621, + "mathds": 17622, + "atre": 17623, + "Ġforaging": 17624, + "Ġupward": 17625, + "idene": 17626, + "Ġglands": 17627, + "fed": 17628, + "uccessful": 17629, + "ĠWolf": 17630, + "Ġusefulness": 17631, + "oporous": 17632, + "Ġpunct": 17633, + "ardo": 17634, + "Ġsystolic": 17635, + "ĠTargeting": 17636, + "Ġillumin": 17637, + "Ġpigment": 17638, + "Ġsimulating": 17639, + "Ġportions": 17640, + "ĠPrinciples": 17641, + "ĠHopf": 17642, + "lipid": 17643, + "ĠLU": 17644, + "ubation": 17645, + "ĠArtificial": 17646, + "Ġprison": 17647, + "aning": 17648, + "ĠGN": 17649, + "ĠStrategies": 17650, + "ĠPas": 17651, + "Ta": 17652, + "ĠProbability": 17653, + "orum": 17654, + "Ġskeleton": 17655, + "Ġcompartments": 17656, + "Read": 17657, + "Ġcoach": 17658, + "Ġmodality": 17659, + "ĠRegister": 17660, + "Ġje": 17661, + "Ġheights": 17662, + "inyl": 17663, + "Ġsubspaces": 17664, + "tip": 17665, + "Ġá¸": 17666, + "ĠGI": 17667, + "Char": 17668, + "rogenic": 17669, + "rett": 17670, + "eutics": 17671, + "Ġadhesive": 17672, + "ĠPier": 17673, + "Left": 17674, + "idental": 17675, + "NAc": 17676, + "Ġconjugation": 17677, + "orov": 17678, + "idge": 17679, + "imaging": 17680, + "ĠTW": 17681, + "Ġpresident": 17682, + "ĠOste": 17683, + "assemb": 17684, + "Ġinternet": 17685, + "Ġdeals": 17686, + "ĠGAP": 17687, + "Ġformulate": 17688, + "ĠUpdate": 17689, + "ĠRNAi": 17690, + "clero": 17691, + "Ġpermutations": 17692, + "Ġisotopes": 17693, + "opic": 17694, + "ĠQU": 17695, + "romes": 17696, + "ĠPolicy": 17697, + "ĠCreek": 17698, + "ĠWindows": 17699, + "Ġmerge": 17700, + "Ġaccident": 17701, + "Ġsuperposition": 17702, + "Ġdebate": 17703, + "Ġdocumentation": 17704, + "Ġeigenvectors": 17705, + "sor": 17706, + "ĠPhoto": 17707, + "Ġdeposit": 17708, + "Ġgermination": 17709, + "Ġsubgraph": 17710, + "ĠRecords": 17711, + "Ġchemically": 17712, + "ĠPredicting": 17713, + "ĠKy": 17714, + "selective": 17715, + "ynman": 17716, + "dispers": 17717, + "Ġlumbar": 17718, + "Ġmusical": 17719, + "inates": 17720, + "Ġinherited": 17721, + "ju": 17722, + "Ġtracer": 17723, + "Ġending": 17724, + "Ġengaged": 17725, + "handed": 17726, + "Ġproducer": 17727, + "Ġentangled": 17728, + "ĠDelta": 17729, + "Ġpiecewise": 17730, + "NAME": 17731, + "stop": 17732, + "Ġmutated": 17733, + "Ġrecess": 17734, + "Ġimmuno": 17735, + "cancer": 17736, + "ĠAkt": 17737, + "iters": 17738, + "ĠBMP": 17739, + "Ġcompanion": 17740, + "Ġcommunicate": 17741, + "Ġhollow": 17742, + "Ġpad": 17743, + "Ġsph": 17744, + "omod": 17745, + "Ġparton": 17746, + "Ġspontaneously": 17747, + "eared": 17748, + "Ġrotations": 17749, + "Ġcosmology": 17750, + "Ġmoreover": 17751, + "princ": 17752, + "Ġeverywhere": 17753, + "brane": 17754, + "lational": 17755, + "eme": 17756, + "Ġbehave": 17757, + "umen": 17758, + "oston": 17759, + "oves": 17760, + "Ġgar": 17761, + "Ġadrenal": 17762, + "ĠEstimating": 17763, + "Nb": 17764, + "Ġechocardi": 17765, + "Ġemphasized": 17766, + "Ġengines": 17767, + "Ġbrackets": 17768, + "Ġleaders": 17769, + "Ġdistinctive": 17770, + "ĠLymph": 17771, + "Ġexert": 17772, + "Ġinnovative": 17773, + "coupling": 17774, + "ĠSignific": 17775, + "sheet": 17776, + "ĠCover": 17777, + "ĠCCD": 17778, + "ĠFall": 17779, + "stimulated": 17780, + "Ġsuperoxide": 17781, + "Ġpollutants": 17782, + "Ġbytes": 17783, + "ĠLipid": 17784, + "Ġtrafficking": 17785, + "Ġleadership": 17786, + "informatics": 17787, + "Ġbiodiversity": 17788, + "ador": 17789, + "Ġinterconn": 17790, + "Ġharmonics": 17791, + "Ġseawater": 17792, + "ĠIllumina": 17793, + "necessary": 17794, + "ĠAnton": 17795, + "Ġprocessors": 17796, + "typename": 17797, + "Det": 17798, + "proton": 17799, + "Ġsubtraction": 17800, + "Ġshifting": 17801, + "Ġcustomers": 17802, + "Ke": 17803, + "ĠOB": 17804, + "atonin": 17805, + "atellite": 17806, + "ĠSUS": 17807, + "ĠColon": 17808, + "ĠTimes": 17809, + "TV": 17810, + "ĠMink": 17811, + "ĠIntegration": 17812, + "Ġprofound": 17813, + "ITC": 17814, + "Ġgras": 17815, + "ĠNASA": 17816, + "ĠACK": 17817, + "radiol": 17818, + "ĠMale": 17819, + "ĠWorking": 17820, + "ticity": 17821, + "ilibria": 17822, + "boundary": 17823, + "ĠRI": 17824, + "ĠAli": 17825, + "cardi": 17826, + "ĠFGF": 17827, + "branes": 17828, + "Ġbeet": 17829, + "Ġmissed": 17830, + "Source": 17831, + "ĠBot": 17832, + "ieve": 17833, + "Ġisother": 17834, + "neys": 17835, + "nl": 17836, + "ortion": 17837, + "Ġcooled": 17838, + "MV": 17839, + "Ġomit": 17840, + "Ġverbal": 17841, + "arette": 17842, + "Ġconference": 17843, + "Ġtransformer": 17844, + "Ġrejected": 17845, + "Ġprogressively": 17846, + "ĠTurkey": 17847, + "Ġathletes": 17848, + "Ġanatomy": 17849, + "EQ": 17850, + "Ġdeterioration": 17851, + "ĠDietary": 17852, + "Ġcorn": 17853, + "Ġcapsule": 17854, + "Ġvibrations": 17855, + "Ġoccupational": 17856, + "Ġexosomes": 17857, + "Ġrewritten": 17858, + "Ġlignin": 17859, + "Ġbiopsies": 17860, + "ĠAdversarial": 17861, + "Ġmercury": 17862, + "Ġplatinum": 17863, + "Ġirrelevant": 17864, + "Ġkeratin": 17865, + "ĠEmission": 17866, + "Ġeukaryotic": 17867, + "Ġinteg": 17868, + "Ġknot": 17869, + "Ġsera": 17870, + "Ġcavities": 17871, + "ĠMedi": 17872, + "Indeed": 17873, + "Eu": 17874, + "ĠâŁ": 17875, + "Ġscenes": 17876, + "Ġlaparoscopic": 17877, + "Ġsenior": 17878, + "ĠDistance": 17879, + "predic": 17880, + "Ġearliest": 17881, + "Ġorg": 17882, + "ĠThor": 17883, + "bury": 17884, + "oblasts": 17885, + "Ġpumping": 17886, + "targeted": 17887, + "Ġrap": 17888, + "ĠPil": 17889, + "Îł": 17890, + "Ġneurom": 17891, + "oft": 17892, + "ostat": 17893, + "Ġpadding": 17894, + "Ġconflicts": 17895, + "Ġstems": 17896, + "ĠSaccharomyces": 17897, + "engine": 17898, + "Ġalkyl": 17899, + "Ġtill": 17900, + "ĠQuad": 17901, + "good": 17902, + "rox": 17903, + "ĠFuzzy": 17904, + "Ġrobotic": 17905, + "ĠDenote": 17906, + "ĠNIR": 17907, + "ĠYuk": 17908, + "parency": 17909, + "Ġlegs": 17910, + "ylvan": 17911, + "Ġtightly": 17912, + "Ġdecor": 17913, + "ĠVP": 17914, + "ĠMun": 17915, + "atoms": 17916, + "ĠSilver": 17917, + "Ġneurodegenerative": 17918, + "Ġresponded": 17919, + "Ġrecons": 17920, + "GEN": 17921, + "ĠFine": 17922, + "fc": 17923, + "Ġparagraph": 17924, + "Ġintens": 17925, + "Ġalongside": 17926, + "Ġbrand": 17927, + "monium": 17928, + "Ġpm": 17929, + "Ġsimplex": 17930, + "ĠPreliminary": 17931, + "Ġdownregulation": 17932, + "Ġxy": 17933, + "ĠMak": 17934, + "opter": 17935, + "ushing": 17936, + "ĠBog": 17937, + "oxia": 17938, + "================================": 17939, + "common": 17940, + "ĠASS": 17941, + "ĠHDL": 17942, + "alamus": 17943, + "Ġirrigation": 17944, + "NM": 17945, + "Ġfading": 17946, + "Ġpreventive": 17947, + "Ġreliably": 17948, + "ĠEthiopia": 17949, + "othesis": 17950, + "izability": 17951, + "OB": 17952, + "Ġtriglycer": 17953, + "Ġgestational": 17954, + "Ġbesides": 17955, + "ĠIii": 17956, + "ĠZone": 17957, + "Ġcoping": 17958, + "Ġminority": 17959, + "Ġdeprivation": 17960, + "Ġhexagonal": 17961, + "chlorophenyl": 17962, + "ĠóµĦ¨": 17963, + "Ġgyr": 17964, + "Ġviewing": 17965, + "Newton": 17966, + "ĠHierarchical": 17967, + "oL": 17968, + "eces": 17969, + "Ġconcludes": 17970, + "Ġfungus": 17971, + "Ġpylori": 17972, + "Ġobstacles": 17973, + "thiazol": 17974, + "conjugated": 17975, + "rass": 17976, + "Ġlose": 17977, + "Ġforth": 17978, + "ĠAllen": 17979, + "oplast": 17980, + "ĠProtection": 17981, + "Ġintermittent": 17982, + "Ġluciferase": 17983, + "ĠMK": 17984, + "Ġgaug": 17985, + "ĠFan": 17986, + "Ġmodal": 17987, + "ĠExercise": 17988, + "scattering": 17989, + "ĠShim": 17990, + "Ġexcretion": 17991, + "Ġatypical": 17992, + "Ġmalignancies": 17993, + "anglades": 17994, + "ĠSpectroscopy": 17995, + "Ġadenosine": 17996, + "lif": 17997, + "Ġnucleic": 17998, + "Ġinclination": 17999, + "ĠCass": 18000, + "Ġethn": 18001, + "Ġexempl": 18002, + "ĠDy": 18003, + "Ġlambda": 18004, + "Ġjac": 18005, + "ĠPRE": 18006, + "Ġrailway": 18007, + "Ġfle": 18008, + "Ġreflections": 18009, + "Ġnanostructures": 18010, + "tists": 18011, + "prints": 18012, + "ĠCAT": 18013, + "Ġsib": 18014, + "Ġchloro": 18015, + "Ġrecipient": 18016, + "optic": 18017, + "Ġcounty": 18018, + "Ġnucleotides": 18019, + "Ġzircon": 18020, + "Ġhorses": 18021, + "ĠMental": 18022, + "inline": 18023, + "ĠNorway": 18024, + "They": 18025, + "Ġmuscular": 18026, + "acetic": 18027, + "ĠJu": 18028, + "Ġcommunic": 18029, + "files": 18030, + "filled": 18031, + "HB": 18032, + "Ġregulations": 18033, + "Ġaccumulate": 18034, + "ĠPanel": 18035, + "Cy": 18036, + "öl": 18037, + "ĠPakistan": 18038, + "Ġthoracic": 18039, + "ĠMPI": 18040, + "portion": 18041, + "Ġinductive": 18042, + "ĠCongress": 18043, + "Ġfibroblast": 18044, + "clust": 18045, + "Ġcentres": 18046, + "adel": 18047, + "Ġsubstitutions": 18048, + "Ġtruncation": 18049, + "rification": 18050, + "oka": 18051, + "Flow": 18052, + "ĠReduc": 18053, + "polarized": 18054, + "ibular": 18055, + "Pe": 18056, + "ĠAML": 18057, + "ĠAgency": 18058, + "Ġtilt": 18059, + "ublished": 18060, + "Ġdepolar": 18061, + "Ġbelt": 18062, + "Ġoptimizer": 18063, + "ELL": 18064, + "ĠHandbook": 18065, + "ĠVirginia": 18066, + "sense": 18067, + "ĠDur": 18068, + "Ġpiezoelectric": 18069, + "Ġawarded": 18070, + "ailing": 18071, + "Pos": 18072, + "pref": 18073, + "ĠSummer": 18074, + "edo": 18075, + "ĠIde": 18076, + "ĠBSA": 18077, + "Ġmonomers": 18078, + "Ġcoagulation": 18079, + "Ġgam": 18080, + "Ġhomes": 18081, + "Ġheads": 18082, + "admium": 18083, + "ĠOC": 18084, + "Ġoccupancy": 18085, + "ĠEmpirical": 18086, + "ĠIi": 18087, + "Ġchir": 18088, + "Ġdegeneracy": 18089, + "Ġflowers": 18090, + "Ġsuperconductivity": 18091, + "Ġinversely": 18092, + "optical": 18093, + "were": 18094, + "ĠAsymptotic": 18095, + "Sec": 18096, + "title": 18097, + "posal": 18098, + "ĠProgn": 18099, + "Ġposes": 18100, + "ĠBorn": 18101, + "Ġcontinuation": 18102, + "Ġcultivated": 18103, + "entiment": 18104, + "Ġmanaging": 18105, + "Ġthrombosis": 18106, + "aug": 18107, + "CNT": 18108, + "urea": 18109, + "Ġspind": 18110, + "ĠWhereas": 18111, + "ĠPerson": 18112, + "Ġbipartite": 18113, + "Ġrescal": 18114, + "Ġmarkets": 18115, + "phan": 18116, + "perties": 18117, + "Ġfermionic": 18118, + "Ġmunicip": 18119, + "Ġachievable": 18120, + "tab": 18121, + "Åį": 18122, + "ĠRelation": 18123, + "Total": 18124, + "xia": 18125, + "Ġintelligent": 18126, + "ĠUT": 18127, + "ĠDal": 18128, + "Ġmedicinal": 18129, + "Ġinadequate": 18130, + "iently": 18131, + "ersen": 18132, + "Ġprecondition": 18133, + "Ġmethodological": 18134, + "Ġcanopy": 18135, + "Ġbacterium": 18136, + "column": 18137, + "Cal": 18138, + "ĠDiego": 18139, + "ĠSak": 18140, + "ĠComprehensive": 18141, + "Ġantitumor": 18142, + "Ġflower": 18143, + "ĠKhan": 18144, + "Ġmetadata": 18145, + "Ġphotore": 18146, + "ogenicity": 18147, + "Ġleague": 18148, + "olating": 18149, + "Ġpromise": 18150, + "ĠPere": 18151, + "Ġpermits": 18152, + "Ġthreads": 18153, + "ĠDCs": 18154, + "ĠCham": 18155, + "razol": 18156, + "Bank": 18157, + "Ġwithdrawal": 18158, + "Ġappend": 18159, + "othelial": 18160, + "ĠMeasures": 18161, + "Ġguideline": 18162, + "Ġmitigate": 18163, + "adjoint": 18164, + "Ġbracket": 18165, + "Pad": 18166, + "Mills": 18167, + "Buffer": 18168, + "Ġcass": 18169, + "hoc": 18170, + "manifolds": 18171, + "herry": 18172, + "Ġfacilitated": 18173, + "Event": 18174, + "ĠÈ": 18175, + "ĠCruz": 18176, + "ĠBrand": 18177, + "Ġnecessity": 18178, + "burgh": 18179, + "ĠmeV": 18180, + "ĠcAMP": 18181, + "Off": 18182, + "selected": 18183, + "Ġengage": 18184, + "Ġredundancy": 18185, + "Ġnanocomposites": 18186, + "solution": 18187, + "onset": 18188, + "ĠExposure": 18189, + "Ġrepetitive": 18190, + "Ãł": 18191, + "ĠRAD": 18192, + "ĠTurk": 18193, + "Ġcorneal": 18194, + "Ġexploiting": 18195, + "Ġobstructive": 18196, + "gramming": 18197, + "ĠMED": 18198, + "Ġmathem": 18199, + "Ġconductive": 18200, + "Ġphotosynthesis": 18201, + "Einstein": 18202, + "ĠPeng": 18203, + "MW": 18204, + "ĠSchmidt": 18205, + "Ġrepetition": 18206, + "identified": 18207, + "Ġinjured": 18208, + "Ġdefective": 18209, + "ĠPel": 18210, + "Ġcultivation": 18211, + "Ġfirstly": 18212, + "Ġanalyzer": 18213, + "Ġstainless": 18214, + "Ġjoining": 18215, + "ĠOxidative": 18216, + "Ġphage": 18217, + "Ġexpendit": 18218, + "Ġhomogeneity": 18219, + "iple": 18220, + "ovic": 18221, + "Ġcrossed": 18222, + "ĠTrust": 18223, + "ĠFract": 18224, + "rophysiological": 18225, + "Ġbasically": 18226, + "Ġcoales": 18227, + "Ġgravit": 18228, + "fulness": 18229, + "cano": 18230, + "Ġcolitis": 18231, + "Ġchaos": 18232, + "carbons": 18233, + "Once": 18234, + "ĠToward": 18235, + "orf": 18236, + "topic": 18237, + "ĠPlay": 18238, + "ĠCorrespond": 18239, + "ĠSleep": 18240, + "ticularly": 18241, + "cumin": 18242, + "vdots": 18243, + "ĠRhe": 18244, + "Ġultraf": 18245, + "Ġtimescale": 18246, + "ĠDetails": 18247, + "angles": 18248, + "Ġsurrogate": 18249, + "ĠFluid": 18250, + "cz": 18251, + "Ġinitialization": 18252, + "ĠTelescope": 18253, + "rases": 18254, + "ĠStock": 18255, + "ĠCond": 18256, + "Ġimmunodeficiency": 18257, + "Bel": 18258, + "oser": 18259, + "shown": 18260, + "Ġkcal": 18261, + "Equation": 18262, + "protective": 18263, + "Ġcalling": 18264, + "Ġanticipated": 18265, + "Ġambiguity": 18266, + "ĠNode": 18267, + "ĠGD": 18268, + "Ġinlet": 18269, + "Ġbread": 18270, + "Ġexceeded": 18271, + "Ġimmunization": 18272, + "Ġprohib": 18273, + "ytic": 18274, + "Ġboys": 18275, + "tu": 18276, + "Ġtower": 18277, + "Like": 18278, + "ĠAnomal": 18279, + "âĮ": 18280, + "ĠShow": 18281, + "Ġimaged": 18282, + "Ġequil": 18283, + "Ġrendering": 18284, + "obility": 18285, + "Ġgeological": 18286, + "friend": 18287, + "ör": 18288, + "carboxamide": 18289, + "ovolta": 18290, + "Current": 18291, + "ĠSti": 18292, + "ĠMU": 18293, + "Ġvalued": 18294, + "Ġpoison": 18295, + "Ġpractically": 18296, + "Ġrequested": 18297, + "Code": 18298, + "Ġbrings": 18299, + "Ġdimethyl": 18300, + "hyp": 18301, + "cemic": 18302, + "Vol": 18303, + "quanti": 18304, + "Ġexha": 18305, + "Ġresponsibility": 18306, + "ĠControlled": 18307, + "Ġfur": 18308, + "Ġresemb": 18309, + "ĠKaw": 18310, + "Ġevoked": 18311, + "Ġuterine": 18312, + "л": 18313, + "Ġanonymous": 18314, + "ĠChallenges": 18315, + "Ġanchor": 18316, + "ĠAbd": 18317, + "Der": 18318, + "Ġthermally": 18319, + "ĠCAP": 18320, + "oblot": 18321, + "ĠFire": 18322, + "Ġdiagnostics": 18323, + "Ġexecute": 18324, + "alis": 18325, + "roni": 18326, + "ĠHarris": 18327, + "ĠGonz": 18328, + "Ġvig": 18329, + "ĠProfessor": 18330, + "Ġinventory": 18331, + "intensity": 18332, + "ĠNSCLC": 18333, + "Ġinterfere": 18334, + "ysaccharides": 18335, + "Ġregener": 18336, + "ĠAuthors": 18337, + "Ġtranslate": 18338, + "ĠTests": 18339, + "ĠLove": 18340, + "ĠInduced": 18341, + "ennis": 18342, + "ĠGEN": 18343, + "Ġoligonucle": 18344, + "Ġmeter": 18345, + "satisf": 18346, + "hesion": 18347, + "Ġtransporters": 18348, + "BIT": 18349, + "ĠConc": 18350, + "Ġglauc": 18351, + "scores": 18352, + "Ġmerger": 18353, + "GH": 18354, + "Ġstoichi": 18355, + "ĠXia": 18356, + "effects": 18357, + "ĠExploring": 18358, + "dorff": 18359, + "Ġcardinality": 18360, + "ĠKaz": 18361, + "false": 18362, + "ĠHSP": 18363, + "Ġunsupervised": 18364, + "inguish": 18365, + "ischer": 18366, + "Ġrelativity": 18367, + "onormal": 18368, + "oothed": 18369, + "edges": 18370, + "ĠIMP": 18371, + "Ġimpulse": 18372, + "ĠColumbia": 18373, + "Ġparticulate": 18374, + "ĠSupporting": 18375, + "ĠSDSS": 18376, + "voltage": 18377, + "ĠAmazon": 18378, + "Ġepoxy": 18379, + "Call": 18380, + "Bigl": 18381, + "Ġmeets": 18382, + "Ġequatorial": 18383, + "Ġneuros": 18384, + "Ġperitoneal": 18385, + "desc": 18386, + "inputs": 18387, + "Ġexterior": 18388, + "aco": 18389, + "Ġmeal": 18390, + "ĠDaniel": 18391, + "Ġintuitive": 18392, + "Ġcouns": 18393, + "depress": 18394, + "inis": 18395, + "phot": 18396, + "ĠAmin": 18397, + "Ġreservoirs": 18398, + "ĠWhole": 18399, + "Ġcaud": 18400, + "Ġbosonic": 18401, + "Ġreaders": 18402, + "Ġcrim": 18403, + "Ġpathophysiology": 18404, + "argo": 18405, + "these": 18406, + "income": 18407, + "Ġissued": 18408, + "Ġhepatocytes": 18409, + "ĠCi": 18410, + "deriv": 18411, + "upta": 18412, + "tuple": 18413, + "ĠChan": 18414, + "Ġauthentication": 18415, + "ygd": 18416, + "Ġinfin": 18417, + "Ġaccelerate": 18418, + "eptive": 18419, + "Ġhydrogel": 18420, + "aska": 18421, + "ONE": 18422, + "Ġfederal": 18423, + "ographics": 18424, + "Ġmuon": 18425, + "Ġslide": 18426, + "Ġelliptical": 18427, + "atite": 18428, + "Ġcc": 18429, + "ETs": 18430, + "Ġclarity": 18431, + "ocycl": 18432, + "isal": 18433, + "rections": 18434, + "ayan": 18435, + "roweak": 18436, + "ĠSOC": 18437, + "oderm": 18438, + "tun": 18439, + "asm": 18440, + "ĠHir": 18441, + "likelihood": 18442, + "Ġadul": 18443, + "tl": 18444, + "High": 18445, + "Ġalters": 18446, + "plitude": 18447, + "ĠRelease": 18448, + "Ġharmful": 18449, + "late": 18450, + "ounds": 18451, + "ĠFederal": 18452, + "ĠEconomic": 18453, + "Ġrabb": 18454, + "Ġaccommodate": 18455, + "emission": 18456, + "ĠBah": 18457, + "cox": 18458, + "ĠModulation": 18459, + "Ġconstructions": 18460, + "igner": 18461, + "ĠUrban": 18462, + "Ġwake": 18463, + "Ġadversary": 18464, + "wikipedia": 18465, + "Ġsuite": 18466, + "wick": 18467, + "expressed": 18468, + "rod": 18469, + "KD": 18470, + "Ġcomputers": 18471, + "ĠBanglades": 18472, + "Ġpersist": 18473, + "Ġburning": 18474, + "Ġadministrative": 18475, + "Ġplug": 18476, + "ĠRepresentations": 18477, + "ĠScattering": 18478, + "Ġendometrial": 18479, + "Ġdescriptors": 18480, + "Ġcommission": 18481, + "Bar": 18482, + "ighth": 18483, + "ĠMarsh": 18484, + "sampling": 18485, + "Ġhull": 18486, + "icin": 18487, + "Prob": 18488, + "Ġnurse": 18489, + "Ġsham": 18490, + "ĠKerr": 18491, + "Ġprefrontal": 18492, + "Ġfixing": 18493, + "OK": 18494, + "Ġbold": 18495, + "Ġcorollary": 18496, + "cfg": 18497, + "ĠOxford": 18498, + "Ġboron": 18499, + "RB": 18500, + "ĠCab": 18501, + "Bigr": 18502, + "ĠPredict": 18503, + "Ġpeculiar": 18504, + "hidden": 18505, + "isa": 18506, + "iden": 18507, + "appropriate": 18508, + "orh": 18509, + "ellectual": 18510, + "Ġseizures": 18511, + "asser": 18512, + "tilis": 18513, + "handle": 18514, + "iaxial": 18515, + "sym": 18516, + "Ġcarcinomas": 18517, + "sea": 18518, + "spired": 18519, + "Ġabrupt": 18520, + "tests": 18521, + "Ġwelfare": 18522, + "ĠOil": 18523, + "ĠLoad": 18524, + "FLAG": 18525, + "uthal": 18526, + "Ġfacing": 18527, + "American": 18528, + "LAS": 18529, + "Ġirrespective": 18530, + "Ġroutinely": 18531, + "wal": 18532, + "Ġsettlement": 18533, + "ĠAqu": 18534, + "Ġelectronics": 18535, + "Ġhandled": 18536, + "Ġbiologically": 18537, + "smooth": 18538, + "ĠBelongs": 18539, + "tib": 18540, + "Ġtrav": 18541, + "pressive": 18542, + "ournals": 18543, + "к": 18544, + "filename": 18545, + "Ġhelical": 18546, + "Ġbacteri": 18547, + "Ġsatellites": 18548, + "BH": 18549, + "ented": 18550, + "ĠFootball": 18551, + "Ġ": 18552, + "ĠHV": 18553, + "Ġtrip": 18554, + "ĠCKD": 18555, + "rani": 18556, + "Ġcleaning": 18557, + "limit": 18558, + "ĠTCP": 18559, + "Ġscin": 18560, + "Ġsludge": 18561, + "Ġsymbolic": 18562, + "ĠSequencing": 18563, + "adal": 18564, + "ĠPhilipp": 18565, + "ICS": 18566, + "Ġvaginal": 18567, + "Ġcommitment": 18568, + "ĠAwards": 18569, + "trig": 18570, + "Ġguitar": 18571, + "acetate": 18572, + "Ġbet": 18573, + "ClN": 18574, + "Ġagriculture": 18575, + "Ġchief": 18576, + "Ġembol": 18577, + "build": 18578, + "Ġtexts": 18579, + "ĠCooper": 18580, + "lived": 18581, + "ĠDelay": 18582, + "ĠMode": 18583, + "yal": 18584, + "BN": 18585, + "Ġindexed": 18586, + "expr": 18587, + "ERN": 18588, + "vens": 18589, + "Ġpointer": 18590, + "cv": 18591, + "acon": 18592, + "tance": 18593, + "ĠâĪĿ": 18594, + "Ġlowered": 18595, + "Ġmitotic": 18596, + "rhosis": 18597, + "ĠPage": 18598, + "ür": 18599, + "imm": 18600, + "ĠTherapeutic": 18601, + "Ġosteopor": 18602, + "Ġbilinear": 18603, + "ĠCatholic": 18604, + "ĠAlternative": 18605, + "oxidation": 18606, + "Ġinitio": 18607, + "benzo": 18608, + "ĠAdi": 18609, + "person": 18610, + "peritoneal": 18611, + "ĉĉĠ": 18612, + "Ġattraction": 18613, + "Ġdiarrhea": 18614, + "Ġren": 18615, + "ĠISO": 18616, + "imir": 18617, + "Ġterminology": 18618, + "ukey": 18619, + "Ġresonator": 18620, + "Ġsubstituting": 18621, + "Ġharbor": 18622, + "provid": 18623, + "decay": 18624, + "ĠHDAC": 18625, + "ĠAnalytical": 18626, + "Ġpostnatal": 18627, + "Ġundes": 18628, + "Specific": 18629, + "dichlor": 18630, + "ARI": 18631, + "tot": 18632, + "Ġdigit": 18633, + "oping": 18634, + "ĠZinc": 18635, + "Ġlethal": 18636, + "Whitney": 18637, + "Fi": 18638, + "quantum": 18639, + "ĠFailure": 18640, + "Ġsolves": 18641, + "ĠSpaces": 18642, + "earman": 18643, + "Ġgoat": 18644, + "Ġsynapses": 18645, + "Ġresuspended": 18646, + "Ġresident": 18647, + "Ġcompac": 18648, + "Ġcortisol": 18649, + "Ġphotometry": 18650, + "WP": 18651, + "select": 18652, + "Ġcele": 18653, + "orubicin": 18654, + "ĠMultic": 18655, + "ĠJean": 18656, + "Ġclip": 18657, + "Ġsa": 18658, + "oco": 18659, + "geometric": 18660, + "Ġhelic": 18661, + "Ġempirically": 18662, + "Ġmicrofluid": 18663, + "idis": 18664, + "Ġautocor": 18665, + "WF": 18666, + "ĠRespir": 18667, + "radiation": 18668, + "Ġthemes": 18669, + "Ġtaste": 18670, + "ricing": 18671, + "Ġexaminations": 18672, + "ĠSensing": 18673, + "same": 18674, + "DEFAULT": 18675, + "Ġphylogeny": 18676, + "hig": 18677, + "Ġplatelets": 18678, + "ĠHistor": 18679, + "aba": 18680, + "Ġresidential": 18681, + "Ġunbounded": 18682, + "anding": 18683, + "hedron": 18684, + "rys": 18685, + "ĠCCR": 18686, + "Ġconce": 18687, + "Ġparasitic": 18688, + "cb": 18689, + "ĠFeynman": 18690, + "ĠKepler": 18691, + "ô": 18692, + "ĠGil": 18693, + "ĠMATLAB": 18694, + "ben": 18695, + "scope": 18696, + "Ġdiscrimin": 18697, + "Ġjustified": 18698, + "plasma": 18699, + "ĠChoi": 18700, + "Ġroof": 18701, + "PCA": 18702, + "ĠTCR": 18703, + "Ġvoxel": 18704, + "ĠWard": 18705, + "Ġuncor": 18706, + "Stok": 18707, + "Ġspur": 18708, + "TRA": 18709, + "Ġdiagnoses": 18710, + "rophysical": 18711, + "ategories": 18712, + "Ġoverestim": 18713, + "Ġstreaming": 18714, + "ĠRecovery": 18715, + "Ġeverything": 18716, + "LOW": 18717, + "Gener": 18718, + "Ġunbiased": 18719, + "Ġvariances": 18720, + "compact": 18721, + "espan": 18722, + "inj": 18723, + "Ġendoscopic": 18724, + "Ġideals": 18725, + "ĠRice": 18726, + "ĠKaplan": 18727, + "Ġfecal": 18728, + "ferred": 18729, + "ĠCycle": 18730, + "Ġimplanted": 18731, + "Ġwine": 18732, + "PET": 18733, + "Ġassignments": 18734, + "Ġabsol": 18735, + "XT": 18736, + "Ġswimming": 18737, + "MN": 18738, + "ĠGeometric": 18739, + "ĠHealthcare": 18740, + "Ġpowders": 18741, + "ĠGel": 18742, + "Ġdownward": 18743, + "Ġexceeding": 18744, + "ĠHEK": 18745, + "lym": 18746, + "ĠBV": 18747, + "Ġvisco": 18748, + "iet": 18749, + "ĠCOX": 18750, + "ployment": 18751, + "inski": 18752, + "Ġoutdoor": 18753, + "ĠLiterature": 18754, + "anted": 18755, + "methoxyphenyl": 18756, + "ĠMedium": 18757, + "Ġdia": 18758, + "ailand": 18759, + "variance": 18760, + "ĠEvaluating": 18761, + "oxacin": 18762, + "Ġantif": 18763, + "Ġpulp": 18764, + "Ġcorrobor": 18765, + "ĠOt": 18766, + "Ġrabbits": 18767, + "Ru": 18768, + "Ġfunctionals": 18769, + "âĩ": 18770, + "Ġimmersion": 18771, + "Ġcreatin": 18772, + "ĠqRT": 18773, + "Ġcondensed": 18774, + "nr": 18775, + "ĠVA": 18776, + "had": 18777, + "Ġking": 18778, + "oble": 18779, + "Ġexisted": 18780, + "Ġthesis": 18781, + "ubbard": 18782, + "apoptotic": 18783, + "Ġflowering": 18784, + "ĠAdaptation": 18785, + "ĠKalman": 18786, + "trl": 18787, + "Ġment": 18788, + "utation": 18789, + "ĠConv": 18790, + "Ġhistories": 18791, + "Ġenanti": 18792, + "nell": 18793, + "onian": 18794, + "ĠFabric": 18795, + "Ġxx": 18796, + "Ġfell": 18797, + "Ġcytosolic": 18798, + "Ġmud": 18799, + "Ġsuspensions": 18800, + "ĠMicrobial": 18801, + "measured": 18802, + "Ġdownload": 18803, + "Ġinvalid": 18804, + "Ġcapturing": 18805, + "ĠHH": 18806, + "ĠGray": 18807, + "ĠAZ": 18808, + "ĠNash": 18809, + "viation": 18810, + "naire": 18811, + "ortium": 18812, + "ynch": 18813, + "aminergic": 18814, + "Ġwait": 18815, + "Schem": 18816, + "trace": 18817, + "ĠVill": 18818, + "Ġpools": 18819, + "Ġhypoxic": 18820, + "xp": 18821, + "Ġshaded": 18822, + "ORY": 18823, + "turn": 18824, + "interacting": 18825, + "Ġdestroyed": 18826, + "akh": 18827, + "ĠCpG": 18828, + "dotted": 18829, + "ĠTranscript": 18830, + "planar": 18831, + "Ġpreclinical": 18832, + "ĠRepro": 18833, + "ĠSurgery": 18834, + "Stokes": 18835, + "ifdef": 18836, + "Ġdiscriminate": 18837, + "ĠGross": 18838, + "Ġflags": 18839, + "iety": 18840, + "ummy": 18841, + "Ġtransfers": 18842, + "SG": 18843, + "ĠSci": 18844, + "Ġheader": 18845, + "ĠFunding": 18846, + "Ġdetrim": 18847, + "Ġinstabilities": 18848, + "ĠPhylogenetic": 18849, + "ymethyl": 18850, + "ĠAssessing": 18851, + "ROC": 18852, + "elsen": 18853, + "Equal": 18854, + "Ġcas": 18855, + "Ġvertically": 18856, + "Ġvisibility": 18857, + "ĠFTIR": 18858, + "scrib": 18859, + "Ġbursts": 18860, + "ĠDoug": 18861, + "ĠFrancisco": 18862, + "ĠMSC": 18863, + "Ġpredis": 18864, + "established": 18865, + "Ġfaced": 18866, + "ĠWI": 18867, + "Sl": 18868, + "Ġcharts": 18869, + "orthy": 18870, + "izontal": 18871, + "ialysis": 18872, + "Ġtunable": 18873, + "Ġexplosion": 18874, + "Sw": 18875, + "TNF": 18876, + "Ġdiscontinuous": 18877, + "ecture": 18878, + "ciences": 18879, + "mathbbm": 18880, + "look": 18881, + "Ġtachy": 18882, + "Ġbrow": 18883, + "observed": 18884, + "Ġanaest": 18885, + "Sal": 18886, + "qPCR": 18887, + "Ġsees": 18888, + "Ġspacecraft": 18889, + "Ġsales": 18890, + "ĠTrac": 18891, + "Tem": 18892, + "ivest": 18893, + "ĠFc": 18894, + "ĠNews": 18895, + "Ġharvesting": 18896, + "ĠEG": 18897, + "pad": 18898, + "Ġnanowires": 18899, + "Ġpotato": 18900, + "pliers": 18901, + "onin": 18902, + "Ġworm": 18903, + "sue": 18904, + "tie": 18905, + "Ġmasks": 18906, + "Ġthrow": 18907, + "!!": 18908, + "behavi": 18909, + "Ġpine": 18910, + "ogy": 18911, + "TEST": 18912, + "onto": 18913, + "Ġcreatinine": 18914, + "ĠBoston": 18915, + "Ġchair": 18916, + "ploys": 18917, + "oven": 18918, + "Ġentrance": 18919, + "Ġcoch": 18920, + "Ġdyes": 18921, + "Tor": 18922, + "ĠPDE": 18923, + "underset": 18924, + "atasets": 18925, + "Ġternary": 18926, + "choose": 18927, + "five": 18928, + "chloride": 18929, + "onium": 18930, + "Property": 18931, + "Ġtu": 18932, + "Ġadequately": 18933, + "romycin": 18934, + "Ġcooper": 18935, + "ïĽľ": 18936, + "Ġpapill": 18937, + "ĠStreptococcus": 18938, + "ĠCY": 18939, + "Ġgrouping": 18940, + "Ġbioc": 18941, + "ĠCardiac": 18942, + "ĠBook": 18943, + "reference": 18944, + "Ġconfirmation": 18945, + "ivery": 18946, + "Ġwarning": 18947, + "pretation": 18948, + "Ġlove": 18949, + "Ġoscillators": 18950, + "sed": 18951, + "ĠTX": 18952, + "ilent": 18953, + "ĠVas": 18954, + "Ġclamp": 18955, + "Ġahead": 18956, + "acs": 18957, + "Ġdepleted": 18958, + "Ġmethodologies": 18959, + "may": 18960, + "Ġcaffe": 18961, + "Ġsequentially": 18962, + "osacchar": 18963, + "Ġcomprise": 18964, + "Ġchel": 18965, + "Ġinacc": 18966, + "Ġtendon": 18967, + "Sequ": 18968, + "ought": 18969, + "server": 18970, + "ĠPerturb": 18971, + "Ġterrain": 18972, + "curve": 18973, + "ĠArgent": 18974, + "TABLE": 18975, + "Ġimplicitly": 18976, + "Ġenjoy": 18977, + "ĠSitter": 18978, + "Ġmicron": 18979, + "ĠEvans": 18980, + "nsylvan": 18981, + "Ġlooked": 18982, + "spe": 18983, + "volving": 18984, + "ĠLSTM": 18985, + "agnetism": 18986, + "ĠNotch": 18987, + "ĠTal": 18988, + "ĠDEGs": 18989, + "leman": 18990, + "Ġboolean": 18991, + "Ġobey": 18992, + "organization": 18993, + "seen": 18994, + "ĠEnc": 18995, + "schild": 18996, + "ĠOntario": 18997, + "Element": 18998, + "ĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ": 18999, + "mouse": 19000, + "Ġpolyethylene": 19001, + "Ġacetic": 19002, + "sections": 19003, + "uronal": 19004, + "ĠDick": 19005, + "Ġkill": 19006, + "Ġbroadening": 19007, + "Ġfluoride": 19008, + "Ġsaved": 19009, + "Ġdeem": 19010, + "Stream": 19011, + "aced": 19012, + "ĠJeff": 19013, + "QA": 19014, + "Ġscalable": 19015, + "ĠFif": 19016, + "ĠMini": 19017, + "Ġsupergravity": 19018, + "Ġcolloidal": 19019, + "LY": 19020, + "OA": 19021, + "Ġperic": 19022, + "Ġshortly": 19023, + "Ġvap": 19024, + "Ġsplits": 19025, + "move": 19026, + "Ġstimulating": 19027, + "ĠBeijing": 19028, + "Ġpyr": 19029, + "ÏŃ": 19030, + "Ġlexical": 19031, + "âĢł": 19032, + "ÅĦ": 19033, + "itories": 19034, + "olerance": 19035, + "Ġinsulator": 19036, + "ĠLeon": 19037, + "Ġpropagate": 19038, + "ĠElements": 19039, + "yen": 19040, + "Module": 19041, + "ĠWhether": 19042, + "Ġaph": 19043, + "ĠLaure": 19044, + "ĠMutations": 19045, + "Ġhypertrophy": 19046, + "Ġoceanic": 19047, + "ographically": 19048, + "patients": 19049, + "ĠAngeles": 19050, + "Ġphe": 19051, + "Ġsquee": 19052, + "Ġcaroten": 19053, + "fine": 19054, + "Ġsketch": 19055, + "Ġansatz": 19056, + "titution": 19057, + "ĠFus": 19058, + "ĠSug": 19059, + "obacterial": 19060, + "Ħĥ": 19061, + "Related": 19062, + "Ġartist": 19063, + "Ġacryl": 19064, + "lined": 19065, + "rafted": 19066, + "ĠQoS": 19067, + "ĠFeng": 19068, + "search": 19069, + "Ġnanotube": 19070, + "ĠVM": 19071, + "ahl": 19072, + "Ġstride": 19073, + "ĠTag": 19074, + "ĠLar": 19075, + "Ġdesorption": 19076, + "dtype": 19077, + "Ġbug": 19078, + "Ġcaregivers": 19079, + "ĠHun": 19080, + "ĠPractical": 19081, + "Ġoblig": 19082, + "rer": 19083, + "ĠKang": 19084, + "ĠProducts": 19085, + "ometh": 19086, + "ĠHeLa": 19087, + "Ġlaboratories": 19088, + "natural": 19089, + "Ġful": 19090, + "Ġmold": 19091, + "abine": 19092, + "ĠSpring": 19093, + "Ġcobal": 19094, + "Ġhighlighting": 19095, + "ĠPref": 19096, + "cyclic": 19097, + "ĠCONCLUSION": 19098, + "ĠSources": 19099, + "Ġapex": 19100, + "parser": 19101, + "ĠLogic": 19102, + "Ġpond": 19103, + "Ġtold": 19104, + "ĠShap": 19105, + "pergillus": 19106, + "Ġsaying": 19107, + "Ġmutagenesis": 19108, + "ĠmmHg": 19109, + "ĠPAN": 19110, + "Ġsmokers": 19111, + "oday": 19112, + "Ġherein": 19113, + "CMV": 19114, + "ĠPW": 19115, + "Ġredshifts": 19116, + "ĠMinim": 19117, + "yman": 19118, + "ulli": 19119, + "dense": 19120, + "Ġarsenic": 19121, + "ĠEMT": 19122, + "ogaster": 19123, + "carboxylate": 19124, + "sys": 19125, + "Ro": 19126, + "anch": 19127, + "ĠAlpha": 19128, + "ĠTechnical": 19129, + "sv": 19130, + "Ġbones": 19131, + "Ġacceptor": 19132, + "Ġnewborn": 19133, + "private": 19134, + "Ġnanor": 19135, + "ĠSwiss": 19136, + "around": 19137, + "Ġsyntax": 19138, + "ĠKähler": 19139, + "Ġaerial": 19140, + "ĠPale": 19141, + "typedef": 19142, + "namespace": 19143, + "Ġconfounding": 19144, + "viÄĩ": 19145, + "Ġretard": 19146, + "Ġzeta": 19147, + "ĠTum": 19148, + "isch": 19149, + "Ġsulfide": 19150, + "ĠTian": 19151, + "uy": 19152, + "Ġintuition": 19153, + "Ġphospholip": 19154, + "ĠSher": 19155, + "ricts": 19156, + "----------------------------------------------------------------": 19157, + "okines": 19158, + "glucose": 19159, + "toler": 19160, + "iferative": 19161, + "ĠFluor": 19162, + "Ġencourage": 19163, + "Ġresponsive": 19164, + "perturbative": 19165, + "Ġsaddle": 19166, + "lers": 19167, + "ndez": 19168, + "ĠZero": 19169, + "ĠDiet": 19170, + "Ġdevelopers": 19171, + "Syn": 19172, + "Ġconfer": 19173, + "Ġoriginate": 19174, + "ropol": 19175, + "haw": 19176, + "letion": 19177, + "mskip": 19178, + "Ġber": 19179, + "Ġpeat": 19180, + "vially": 19181, + "Ġgranules": 19182, + "ĠÌĥ": 19183, + "Ġpluripot": 19184, + "Ġassimilation": 19185, + "Ġdenominator": 19186, + "abilization": 19187, + "ĠEpidemiology": 19188, + "MIN": 19189, + "eeds": 19190, + "ĠVR": 19191, + "Eval": 19192, + "store": 19193, + "ĠBaseline": 19194, + "Ġcu": 19195, + "ĠSpectra": 19196, + "Ġfractionation": 19197, + "Ġplacing": 19198, + "Ġburied": 19199, + "eleration": 19200, + "Ġalkali": 19201, + "ĠIU": 19202, + "Calc": 19203, + "weak": 19204, + "Ġmorphisms": 19205, + "Ġligase": 19206, + "Ġfs": 19207, + "Ġutilizes": 19208, + "Comput": 19209, + "â": 19210, + "Ġstig": 19211, + "relative": 19212, + "Ġimmature": 19213, + "ĠFrac": 19214, + "api": 19215, + "Ġoutpatient": 19216, + "Ġachievement": 19217, + "Ġstacking": 19218, + "Ġnodules": 19219, + "IND": 19220, + "ĠGPa": 19221, + "Ġpercolation": 19222, + "mspace": 19223, + "Ġbrains": 19224, + "uffle": 19225, + "entropy": 19226, + "Lab": 19227, + "Ġstabilize": 19228, + "ĠRicci": 19229, + "ĠAntimicrobial": 19230, + "personal": 19231, + "Ġfarms": 19232, + "ĠPin": 19233, + "Ġporcine": 19234, + "Ġoccasionally": 19235, + "whe": 19236, + "Ġundergoes": 19237, + "Ġregimens": 19238, + "Ġblade": 19239, + "Ġlinearized": 19240, + "Ġdecon": 19241, + "Ġpacked": 19242, + "Ġfishes": 19243, + "ĠMend": 19244, + "Ġapproaching": 19245, + "Ġballs": 19246, + "Ġproinflammatory": 19247, + "imeric": 19248, + "ĠDirector": 19249, + "Ġsoliton": 19250, + "Ġmosaic": 19251, + "viet": 19252, + "Mean": 19253, + "ĠPad": 19254, + "Ġtriplicate": 19255, + "supported": 19256, + "Ġcart": 19257, + "<<<<": 19258, + "Ġremission": 19259, + "aseous": 19260, + "asticity": 19261, + "ĠMik": 19262, + "ĠStrategy": 19263, + "ramer": 19264, + "ĠPolish": 19265, + "Ġenthal": 19266, + "Ġheterozygous": 19267, + "ĠGravity": 19268, + "Ax": 19269, + "Ġorganizational": 19270, + "Ġmovie": 19271, + "Ġexploratory": 19272, + "WLED": 19273, + "Ġmoiety": 19274, + "decre": 19275, + "ĠStill": 19276, + "Ġ¡": 19277, + "Ġgreenhouse": 19278, + "Ġsuperconductors": 19279, + "enum": 19280, + "elin": 19281, + "Ġoffering": 19282, + "stad": 19283, + "ĠTrich": 19284, + "Ġrepl": 19285, + "Ġrecycling": 19286, + "phor": 19287, + "Ġinelastic": 19288, + "ockey": 19289, + "ĠâĢĻ": 19290, + "Ġsequel": 19291, + "EB": 19292, + "ĠChile": 19293, + "Ġfibrillation": 19294, + "Ġdisulfide": 19295, + "obtained": 19296, + "ubin": 19297, + "Ĥ¬": 19298, + "Ġfacilitating": 19299, + "Ġhopping": 19300, + "Ġmediator": 19301, + "Ġhydration": 19302, + "Ġsparsity": 19303, + "Ġsati": 19304, + "Ġisothermal": 19305, + "Ġreturning": 19306, + "Ġtraveling": 19307, + "Ġing": 19308, + "Ġstent": 19309, + "Ġcapacitor": 19310, + "Ġcompromise": 19311, + "ĠSud": 19312, + "ĠVision": 19313, + "Ġtopologies": 19314, + "opolysaccharide": 19315, + "ĠProfile": 19316, + "ĠRing": 19317, + "Ġdiscrepancies": 19318, + "Dis": 19319, + "ARD": 19320, + "cccc": 19321, + "Ġdirectory": 19322, + "ĠCMOS": 19323, + "owed": 19324, + "illo": 19325, + "ĠInsights": 19326, + "ĠTib": 19327, + "Ġaband": 19328, + "arose": 19329, + "Order": 19330, + "Ġ¬": 19331, + "Ġintracranial": 19332, + "Ġintermediates": 19333, + "Ġhabits": 19334, + "Ġcarp": 19335, + "property": 19336, + "IMAGE": 19337, + "ĠUk": 19338, + "Ġhydrophilic": 19339, + "Wid": 19340, + "Ġabiotic": 19341, + "Ġobservers": 19342, + "Ġchor": 19343, + "ĠConservation": 19344, + "ĠEnhance": 19345, + "ĠAutomated": 19346, + "ĠGlut": 19347, + "iratory": 19348, + "Ġspaw": 19349, + "ĠEfficiency": 19350, + "vast": 19351, + "initi": 19352, + "Ġoptional": 19353, + "ĠScaling": 19354, + "ifold": 19355, + "ĠmtDNA": 19356, + "ĠReconstruction": 19357, + "Ġcountable": 19358, + "ĠGrass": 19359, + "Den": 19360, + "ĠChain": 19361, + "enzyme": 19362, + "Ġwaveforms": 19363, + "Ġpancreas": 19364, + "ĠDetailed": 19365, + "cmd": 19366, + "Ġâİľ": 19367, + "Ġmagneto": 19368, + "ĠFPGA": 19369, + "Ġabsolutely": 19370, + "Ġstimulates": 19371, + "achus": 19372, + "ĠArn": 19373, + "message": 19374, + "ocompatibility": 19375, + "HCl": 19376, + "ĠFish": 19377, + "Ġphenomenological": 19378, + "Ġsalivary": 19379, + "ondo": 19380, + "Ġnotions": 19381, + "fur": 19382, + "UCT": 19383, + "Ġwww": 19384, + "abet": 19385, + "ĠSulf": 19386, + "Fil": 19387, + "dominated": 19388, + "arser": 19389, + "Ġpackages": 19390, + "Ġsplice": 19391, + "Flo": 19392, + "NOWLED": 19393, + "xa": 19394, + "ĠYuan": 19395, + "Ġacetone": 19396, + "ĠVitamin": 19397, + "ĠÎŀ": 19398, + "Ġobsc": 19399, + "Ġchaper": 19400, + "Ġmort": 19401, + "MAN": 19402, + "Ġsubtilis": 19403, + "Ġoptimality": 19404, + "Ġcontinuing": 19405, + "Ġduplication": 19406, + "Ġmultiplying": 19407, + "Ġimmunological": 19408, + "Ġcirrhosis": 19409, + "hospital": 19410, + "ĠProbabilistic": 19411, + "Ġdeletions": 19412, + "Ġcaution": 19413, + "Ġowner": 19414, + "oxorubicin": 19415, + "Ġlaunch": 19416, + "Ġcure": 19417, + "thus": 19418, + "ĠHermitian": 19419, + "canonical": 19420, + "Ġimmunore": 19421, + "formin": 19422, + "Ġbroadband": 19423, + "partum": 19424, + "ophe": 19425, + "ĠBeta": 19426, + "ĠBI": 19427, + "Ġïĺº": 19428, + "Ġjumps": 19429, + "Ġparadox": 19430, + "umped": 19431, + "Ġdoctors": 19432, + "Ġhospitalized": 19433, + "Ġwash": 19434, + "precision": 19435, + "Ġruled": 19436, + "Ġduplicate": 19437, + "ante": 19438, + "Ġneurotrans": 19439, + "Ġïĥ§": 19440, + "Ġtheme": 19441, + "Taking": 19442, + "ĠPlants": 19443, + "following": 19444, + "Ġageing": 19445, + "Ġcongestion": 19446, + "osarcoma": 19447, + "Ġrepository": 19448, + "ĠHess": 19449, + "ĠCatalytic": 19450, + "ĠDV": 19451, + "INK": 19452, + "priv": 19453, + "ĠAna": 19454, + "ĠSLE": 19455, + "ĠThailand": 19456, + "íķ": 19457, + "Ġduty": 19458, + "locations": 19459, + "oter": 19460, + "Ġlysine": 19461, + "Ġindist": 19462, + "Ġagonists": 19463, + "Ack": 19464, + "Ġminimally": 19465, + "Ġetching": 19466, + "ugging": 19467, + "cuda": 19468, + "ndef": 19469, + "Ġreferring": 19470, + "Ġlysates": 19471, + "Ġserotonin": 19472, + "cribing": 19473, + "ĠInterface": 19474, + "dV": 19475, + "Ġdurations": 19476, + "Ġphotod": 19477, + "Ġdating": 19478, + "Ġirreversible": 19479, + "osidase": 19480, + "ĠFROM": 19481, + "within": 19482, + "SNR": 19483, + "Ġarrhyth": 19484, + "ĠRatio": 19485, + "ĠThin": 19486, + "centered": 19487, + "Ġshocks": 19488, + "ĠVers": 19489, + "Ġnoticeable": 19490, + "Ġfoci": 19491, + "Ġorthonormal": 19492, + "ĠâİŁ": 19493, + "Ġluminescence": 19494, + "ĠSUSY": 19495, + "internal": 19496, + "ĠTour": 19497, + "Ġabbrevi": 19498, + "ĠMAL": 19499, + "vertex": 19500, + "Ġemploys": 19501, + "INS": 19502, + "Ġimmunohistochemistry": 19503, + "Ġheparin": 19504, + "Ġidiopathic": 19505, + "Ġimmobilized": 19506, + "ishe": 19507, + "phth": 19508, + "thin": 19509, + "ĠStorage": 19510, + "Ġperovskite": 19511, + "Prot": 19512, + "ĠDepending": 19513, + "Ġblends": 19514, + "Ġpredator": 19515, + "Ġdisplaying": 19516, + "Ġvesicle": 19517, + "ĠKra": 19518, + "Ġlane": 19519, + "Ġmultilayer": 19520, + "Ġhomozygous": 19521, + "cosh": 19522, + "Ġsuperficial": 19523, + "Ġil": 19524, + "ĠKR": 19525, + "ĠBrun": 19526, + "ĠEW": 19527, + "opa": 19528, + "ĠCartesian": 19529, + "ĠCytoplas": 19530, + "ĠPennsylvan": 19531, + "bands": 19532, + "Ġangiotensin": 19533, + "ĠLattice": 19534, + "GI": 19535, + "jee": 19536, + "Ġenlarged": 19537, + "enius": 19538, + "ĠIa": 19539, + "oux": 19540, + "Ġgent": 19541, + "Ġcarbonyl": 19542, + "chers": 19543, + "Ġhypothe": 19544, + "Ġmicrosp": 19545, + "Ġaffective": 19546, + "Ġaxons": 19547, + "ei": 19548, + "yptoph": 19549, + "ĠJon": 19550, + "queue": 19551, + "ĠGauge": 19552, + "menopausal": 19553, + "ĠDas": 19554, + "ĠEssential": 19555, + "ĠFault": 19556, + "ĠBil": 19557, + "Ġtestosterone": 19558, + "Ġchambers": 19559, + "dione": 19560, + "Ġelicited": 19561, + "IGN": 19562, + "Ġantioxidants": 19563, + "populations": 19564, + "Ġovary": 19565, + "Ġâĸ": 19566, + "Ġabstraction": 19567, + "Ġhydrocarbons": 19568, + "Ġrectal": 19569, + "Ġtriggering": 19570, + "Ġthoroughly": 19571, + "Run": 19572, + "acteria": 19573, + "information": 19574, + "ĠBed": 19575, + "Ġquenc": 19576, + "Ġunders": 19577, + "ĠScotland": 19578, + "Ġrevolution": 19579, + "Ġpituitary": 19580, + "Ġanthropogenic": 19581, + "focus": 19582, + "Ġmethan": 19583, + "Ġinflow": 19584, + "Ġdeflection": 19585, + "ĠCape": 19586, + "Ġmultidimensional": 19587, + "Ġarrived": 19588, + "ĠSpar": 19589, + "dv": 19590, + "Ġcows": 19591, + "ĠBh": 19592, + "Ġjk": 19593, + "tolyl": 19594, + "Ġeigenstates": 19595, + "Ġpreprocessing": 19596, + "ĠRain": 19597, + "ä¸": 19598, + "inz": 19599, + "Ġmn": 19600, + "REE": 19601, + "atrick": 19602, + "Dev": 19603, + "Ġfulfilled": 19604, + "Ġartic": 19605, + "Ġrealizations": 19606, + "ĠComponent": 19607, + "ĠWS": 19608, + "Ġinfo": 19609, + "printed": 19610, + "atosis": 19611, + "cache": 19612, + "anov": 19613, + "ĠTg": 19614, + "content": 19615, + "junc": 19616, + "ĠCDK": 19617, + "Ġbehaves": 19618, + "ĠKid": 19619, + "difference": 19620, + "ĠPs": 19621, + "ĠUg": 19622, + "Ġstructurally": 19623, + "erebral": 19624, + "ĠSurve": 19625, + "heal": 19626, + "onite": 19627, + "Ġdeleted": 19628, + "itim": 19629, + "Star": 19630, + "ĠSpeech": 19631, + "ĠAstr": 19632, + "gradient": 19633, + "Ġfellow": 19634, + "Ġsyring": 19635, + "NB": 19636, + "ĠNB": 19637, + "Ġcreep": 19638, + "Ġlogging": 19639, + "Ġinten": 19640, + "scalar": 19641, + "ĠAtmospheric": 19642, + "Ġlupus": 19643, + "Ġidentically": 19644, + "processed": 19645, + "signal": 19646, + "ĠClostr": 19647, + "ancers": 19648, + "Ġdb": 19649, + "Ġsubsystem": 19650, + "situ": 19651, + "Ġferroelectric": 19652, + "ĠïĽľ": 19653, + "Ġore": 19654, + "ĠRb": 19655, + "ĠMicrosoft": 19656, + "ĠCoch": 19657, + "ĠActin": 19658, + "Ġnerves": 19659, + "Ġexpertise": 19660, + "otive": 19661, + "ĠPoincaré": 19662, + "ĠRig": 19663, + "Ġpsychosocial": 19664, + "Ġprogenitors": 19665, + "ĠMyr": 19666, + "ĠHug": 19667, + "Ġbiogenesis": 19668, + "Ġincorporates": 19669, + "Ġnevertheless": 19670, + "ĠDecl": 19671, + "observ": 19672, + "Ġmultiplier": 19673, + "Ġresponding": 19674, + "hoff": 19675, + "Ġimpacted": 19676, + "Ġsyndromes": 19677, + "kel": 19678, + "ĠSynt": 19679, + "ĠConcer": 19680, + "ĠAmericans": 19681, + "Ġspaced": 19682, + "umption": 19683, + "ĠThompson": 19684, + "ĠJacobian": 19685, + "Tra": 19686, + "evolution": 19687, + "Ġdidn": 19688, + "Ġpercentile": 19689, + "Ġlid": 19690, + "equivalent": 19691, + "Ġantico": 19692, + "Ġmultiply": 19693, + "Ġpenicillin": 19694, + "Ġresponsiveness": 19695, + "Ġrunoff": 19696, + "alanine": 19697, + "squares": 19698, + "ĠInsulin": 19699, + "rele": 19700, + "ĠLif": 19701, + "ĠMinkowski": 19702, + "Ġblend": 19703, + "ĠPand": 19704, + "Ġtwelve": 19705, + "Ġhybrids": 19706, + "Ġbass": 19707, + "interaction": 19708, + "ĠBangladesh": 19709, + "Ġopens": 19710, + "ĠArts": 19711, + "Ġconcave": 19712, + "Ġpedest": 19713, + "Ġfist": 19714, + "ĠAdults": 19715, + "openia": 19716, + "ENCE": 19717, + "ĠFusion": 19718, + "Ġmicroc": 19719, + "ĠSurgical": 19720, + "ylate": 19721, + "Ġpackaging": 19722, + "OCK": 19723, + "QC": 19724, + "Tri": 19725, + "scan": 19726, + "Ġregards": 19727, + "Ġdiscriminant": 19728, + "Ġindustries": 19729, + "icus": 19730, + "ĠWalker": 19731, + "Ġpeers": 19732, + "synt": 19733, + "Ġhorse": 19734, + "Ġflowing": 19735, + "urred": 19736, + "ĠCRP": 19737, + "ĠCareer": 19738, + "iffiffiffiffiffiffiffiff": 19739, + "ĠMSE": 19740, + "hana": 19741, + "ĠMortality": 19742, + "Ġtumorigenesis": 19743, + "ĠIslam": 19744, + "Ġazimuthal": 19745, + "wen": 19746, + "Ġsys": 19747, + "azin": 19748, + "neighbor": 19749, + "Config": 19750, + "they": 19751, + "Ġsorption": 19752, + "Ġspanned": 19753, + "Ġviewpoint": 19754, + "MOD": 19755, + "Ġthrust": 19756, + "uplex": 19757, + "Ġhistograms": 19758, + "Ġprogrammed": 19759, + "Ġethics": 19760, + "ectable": 19761, + "representation": 19762, + "umns": 19763, + "Ġstreet": 19764, + "ĠSobolev": 19765, + "Ġexcision": 19766, + "ĠRud": 19767, + "quires": 19768, + "Ġowned": 19769, + "Ġthousand": 19770, + "Ġantagonists": 19771, + "UST": 19772, + "Ġdrastically": 19773, + "ĠóµĦ©": 19774, + "ĠDor": 19775, + "ĠMOS": 19776, + "pn": 19777, + "ĠDecre": 19778, + "Dep": 19779, + "Ġsintering": 19780, + "Ġpurple": 19781, + "ethanol": 19782, + "Ġhydrocarbon": 19783, + "ĠFO": 19784, + "leftrightarrow": 19785, + "Ġimmunofluorescence": 19786, + "ĠOM": 19787, + "Ġmaturity": 19788, + "Ġearthquakes": 19789, + "Ġaxon": 19790, + "Ġprobed": 19791, + "ORD": 19792, + "ĠADP": 19793, + "sg": 19794, + "omere": 19795, + "Ġtranscribed": 19796, + "Mar": 19797, + "ĠUtil": 19798, + "ĠIA": 19799, + "Ġcompiled": 19800, + "Ġsupervision": 19801, + "ĠXen": 19802, + "ĠJur": 19803, + "compar": 19804, + "Ġhypertensive": 19805, + "ilized": 19806, + "rae": 19807, + "Conclusion": 19808, + "'''": 19809, + "Double": 19810, + "ĠFas": 19811, + "Ġinsectic": 19812, + "ĠPrem": 19813, + "Pri": 19814, + "ĠCao": 19815, + "ĠQuestionnaire": 19816, + "Ġgathered": 19817, + "GW": 19818, + "ĠNV": 19819, + "ĠLactobacillus": 19820, + "Ġcyclin": 19821, + "Ġreject": 19822, + "Ġskull": 19823, + "Ġaw": 19824, + "ĠCold": 19825, + "Ġmesons": 19826, + "bd": 19827, + "Ġdetrimental": 19828, + "apore": 19829, + "nowled": 19830, + "ĠCXCL": 19831, + "Ġspikes": 19832, + "Ġtent": 19833, + "ĠLength": 19834, + "Ġdoor": 19835, + "Ġflour": 19836, + "ustration": 19837, + "Health": 19838, + "Ġtransparency": 19839, + "Ġdisrupted": 19840, + "Hy": 19841, + "overl": 19842, + "ĠReinforcement": 19843, + "ceptors": 19844, + "ĠKos": 19845, + "retroviral": 19846, + "ĠINT": 19847, + "ĠSor": 19848, + "Ġadopting": 19849, + "Ġendoplasmic": 19850, + "Ġsuit": 19851, + "Ġopioid": 19852, + "Ġintegrin": 19853, + "away": 19854, + "Ġtailored": 19855, + "ĠSoc": 19856, + "Ġquies": 19857, + "Ġhusband": 19858, + "Ġumb": 19859, + "ĠCai": 19860, + "ĠAspergillus": 19861, + "ĠGaN": 19862, + "Ġdistinguishing": 19863, + "Ġextrapolation": 19864, + "Ġcage": 19865, + "Ġscavenging": 19866, + "KF": 19867, + "Tree": 19868, + "ĠConflict": 19869, + "UNC": 19870, + "Ġmanganese": 19871, + "days": 19872, + "ÃŁ": 19873, + "ĠLive": 19874, + "sd": 19875, + "ractor": 19876, + "Ġlute": 19877, + "Ġdissimilar": 19878, + "Ġib": 19879, + "ĠVeg": 19880, + "Ġoccurrences": 19881, + "Ġbinomial": 19882, + "Scheme": 19883, + "Ġtape": 19884, + "ĠCant": 19885, + "Ġelectrosp": 19886, + "Cd": 19887, + "made": 19888, + "Ġsevent": 19889, + "shared": 19890, + "Ġaccession": 19891, + "orp": 19892, + "DATA": 19893, + "leted": 19894, + "Vari": 19895, + "Ġrose": 19896, + "tagged": 19897, + "ĠAth": 19898, + "Ġeddy": 19899, + "estone": 19900, + "Ġesters": 19901, + "Ġtyping": 19902, + "ĠStudents": 19903, + "yi": 19904, + "oresistance": 19905, + "inois": 19906, + "Ġglucocortic": 19907, + "iosis": 19908, + "Ġcoronal": 19909, + "Ġsheath": 19910, + "ĠTrack": 19911, + "Ġequilibria": 19912, + "amming": 19913, + "Ġpione": 19914, + "Ġsciences": 19915, + "Ġsuppressing": 19916, + "Ġdeco": 19917, + "ifndef": 19918, + "His": 19919, + "Ġpellet": 19920, + "Linear": 19921, + "orbent": 19922, + "Ġflatten": 19923, + "Ġstraw": 19924, + "Ġalbeit": 19925, + "ĠPredictive": 19926, + "Ġgaze": 19927, + "Ġhydroly": 19928, + "uther": 19929, + "oders": 19930, + "Ġflap": 19931, + "Ġsimplicial": 19932, + "System": 19933, + "Ġstressed": 19934, + "Ġimmunoglobulin": 19935, + "ilia": 19936, + "Ġconsuming": 19937, + "Ġé": 19938, + "galact": 19939, + "Ġadulthood": 19940, + "Ġvorticity": 19941, + "yclic": 19942, + "ovoltaic": 19943, + "ivestock": 19944, + "Ġbeds": 19945, + "ĠPlanning": 19946, + "Ġparameterized": 19947, + "Ġghost": 19948, + "maximum": 19949, + "Ġsuperim": 19950, + "Ġphysicochemical": 19951, + "gp": 19952, + "ongue": 19953, + "Ġprimordial": 19954, + "xff": 19955, + "insula": 19956, + "Mc": 19957, + "Ġminimizes": 19958, + "ĠGravitational": 19959, + "osoma": 19960, + "ignificant": 19961, + "Ġelucidated": 19962, + "Ġsubsurface": 19963, + "significant": 19964, + "Ġrelatives": 19965, + "ferroni": 19966, + "transf": 19967, + "Ġtails": 19968, + "beck": 19969, + "omagnetic": 19970, + "Ġunnecessary": 19971, + "Ġmonomial": 19972, + "delay": 19973, + "Ġsta": 19974, + "ĠSuz": 19975, + "Ġaltering": 19976, + "LOG": 19977, + "ĠLac": 19978, + "Ġranks": 19979, + "hw": 19980, + "ĠNep": 19981, + "Ġneuropath": 19982, + "ĠCompe": 19983, + "Gr": 19984, + "Pati": 19985, + "reduce": 19986, + "ĠMalaysia": 19987, + "ceral": 19988, + "Ġmicrobes": 19989, + "Ġlensing": 19990, + "ĠCalcium": 19991, + "ĠDetermin": 19992, + "ĠCosta": 19993, + "Ġkeeps": 19994, + "printing": 19995, + "ĉĉĉĉĉĉ": 19996, + "chin": 19997, + "exposed": 19998, + "Ġperiodically": 19999, + "Ġrender": 20000, + "ĠCardiovascular": 20001, + "entin": 20002, + "Ġbioavailability": 20003, + "Ġinterpretations": 20004, + "ĠCU": 20005, + "Ġnegoti": 20006, + "Ġantim": 20007, + "Ġdeemed": 20008, + "Ġae": 20009, + "Ġhalos": 20010, + "ĠMichigan": 20011, + "Ġosteoarthritis": 20012, + "diag": 20013, + "ĠBeng": 20014, + "Ġmetagen": 20015, + "Ġparameterization": 20016, + "diagn": 20017, + "ĠMatching": 20018, + "Ġcatalysis": 20019, + "uts": 20020, + "Ġdissemination": 20021, + "Ġoutlet": 20022, + "ĠMoon": 20023, + "ĠGST": 20024, + "sphere": 20025, + "Ġresearcher": 20026, + "ambiguation": 20027, + "Ġraises": 20028, + "Ġflavonoids": 20029, + "ĠMultivariate": 20030, + "Ġaccl": 20031, + "WI": 20032, + "Ġnu": 20033, + "Ġergodic": 20034, + "unique": 20035, + "atinib": 20036, + "Ġresolutions": 20037, + "Ġhouses": 20038, + "DEC": 20039, + "ighed": 20040, + "Ġsixth": 20041, + "Ġpolitician": 20042, + "apache": 20043, + "Ġsolute": 20044, + "Ġaugment": 20045, + "stress": 20046, + "HIV": 20047, + "ĠSets": 20048, + "Ġtransistors": 20049, + "qubit": 20050, + "amines": 20051, + "Ġfarmers": 20052, + "Ġnt": 20053, + "ĠLagrange": 20054, + "Ġvegetables": 20055, + "Ġpret": 20056, + "ĠSynthetic": 20057, + "Ġcones": 20058, + "Ġmedicines": 20059, + "Ġgenomics": 20060, + "Ġexperiencing": 20061, + "agland": 20062, + "Ġgenital": 20063, + "ĠObservatory": 20064, + "ĠSkin": 20065, + "ĠRosen": 20066, + "ĠBritain": 20067, + "genome": 20068, + "ĠEntropy": 20069, + "Ġrac": 20070, + "Go": 20071, + "Ġwalks": 20072, + "criptor": 20073, + "ĠBaker": 20074, + "oker": 20075, + "Ġpropensity": 20076, + "Ġpopularity": 20077, + "restricted": 20078, + "ĠBert": 20079, + "before": 20080, + "ĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ": 20081, + "auto": 20082, + "Rank": 20083, + "ĠRCT": 20084, + "Ġpocket": 20085, + "obut": 20086, + "Ġbenzene": 20087, + "ĠCNT": 20088, + "yptophan": 20089, + "allis": 20090, + "ĠResources": 20091, + "ĠBerlin": 20092, + "Ġscholar": 20093, + "glob": 20094, + "ĠSpeed": 20095, + "ĠXiao": 20096, + "biggl": 20097, + "ANCE": 20098, + "ĠPrime": 20099, + "Phys": 20100, + "idia": 20101, + "Ġmonoc": 20102, + "ĠCommunications": 20103, + "ĠPrecision": 20104, + "ĠPauli": 20105, + "Ġinvestigators": 20106, + "ĠLiang": 20107, + "Ġmeteorological": 20108, + "mog": 20109, + "reens": 20110, + "ubric": 20111, + "Ġrearrangement": 20112, + "orta": 20113, + "Elect": 20114, + "ĠTukey": 20115, + "ĠMis": 20116, + "Ġepiderm": 20117, + "ĠACKNOWLED": 20118, + "wart": 20119, + "Ġexciton": 20120, + "Ġassociative": 20121, + "styrene": 20122, + "Ġlosing": 20123, + "ĠOd": 20124, + "prep": 20125, + "essation": 20126, + "Ġattributable": 20127, + "ĠNavier": 20128, + "anz": 20129, + "Ġcorrectness": 20130, + "oints": 20131, + "ĠRather": 20132, + "Ġassemblies": 20133, + "Ġbridges": 20134, + "OSS": 20135, + "MET": 20136, + "Ġperm": 20137, + "Ġauthorities": 20138, + "Ġiodine": 20139, + "shire": 20140, + "interval": 20141, + "eptid": 20142, + "Ġpotency": 20143, + "Ġrenewable": 20144, + "vard": 20145, + "Ġsurjective": 20146, + "Ġsubsequence": 20147, + "ĠEVs": 20148, + "itching": 20149, + "Ġgenotyping": 20150, + "ĠAccurate": 20151, + "iophene": 20152, + "Gly": 20153, + "plified": 20154, + "ĠDistinct": 20155, + "ACH": 20156, + "Ġspeakers": 20157, + "holm": 20158, + "Ġpros": 20159, + "ĠDevice": 20160, + "mc": 20161, + "ĠDense": 20162, + "ĠVa": 20163, + "rison": 20164, + "Ġacyl": 20165, + "ĠPrincipal": 20166, + "ĠViral": 20167, + "Ġcosine": 20168, + "ĠResidual": 20169, + "Ġefflux": 20170, + "ĠSubjects": 20171, + "Ġrectangle": 20172, + "workers": 20173, + "Ġrotated": 20174, + "Ġbomb": 20175, + "ĠResolution": 20176, + "near": 20177, + "Ġ®": 20178, + "Ġestablishes": 20179, + "amed": 20180, + "Ġcompetence": 20181, + "Glu": 20182, + "ĠDend": 20183, + "ĠHsp": 20184, + "ensation": 20185, + "ĠLead": 20186, + "Ġlogger": 20187, + "sinh": 20188, + "Ġintellectual": 20189, + "former": 20190, + "Ce": 20191, + "Ġmonocyte": 20192, + "hores": 20193, + "Ġdiastolic": 20194, + "Ġlifespan": 20195, + "ĠSilva": 20196, + "arum": 20197, + "Ġtransducer": 20198, + "Ġoutgoing": 20199, + "entation": 20200, + "Ġabsorbing": 20201, + "itage": 20202, + "Ġsynthesize": 20203, + "Ġfeeling": 20204, + "asian": 20205, + "Ġceramics": 20206, + "iph": 20207, + "Ġnonlocal": 20208, + "Part": 20209, + "Ġimmersed": 20210, + "stationary": 20211, + "lecting": 20212, + "Ġwelding": 20213, + "Ġresembles": 20214, + "ĠKat": 20215, + "master": 20216, + "Ġintersect": 20217, + "ĠOlig": 20218, + "ĠTrends": 20219, + "agh": 20220, + "ĠNav": 20221, + "ĠTu": 20222, + "Ġepist": 20223, + "Ġclinics": 20224, + "Ġrepresentatives": 20225, + "Ġgrateful": 20226, + "GPIO": 20227, + "HH": 20228, + "Ġunambig": 20229, + "tuning": 20230, + "Ġnewsp": 20231, + "cohol": 20232, + "################################": 20233, + "%%%%%%%%": 20234, + "represented": 20235, + "ocic": 20236, + "ĠFuk": 20237, + "ĠSund": 20238, + "hasone": 20239, + "Mode": 20240, + "olone": 20241, + "ĠSb": 20242, + "Three": 20243, + "Link": 20244, + "cephal": 20245, + "ĠKap": 20246, + "Ġeliminating": 20247, + "Ġmelanogaster": 20248, + "âŁ": 20249, + "ĠBMD": 20250, + "ISE": 20251, + "ĠBattle": 20252, + "Ġshrinkage": 20253, + "ĠSeven": 20254, + "ĠGlass": 20255, + "romagn": 20256, + "Ġkl": 20257, + "ĠObviously": 20258, + "preserving": 20259, + "ĠPlatform": 20260, + "ĠÌĩ": 20261, + "omavirus": 20262, + "ĠEight": 20263, + "Ġallerg": 20264, + "ĠNanoparticles": 20265, + "aryl": 20266, + "Ġpriors": 20267, + "pattern": 20268, + "Ġlinearity": 20269, + "Ġtruly": 20270, + "Process": 20271, + "Ġdescending": 20272, + "ĠVictoria": 20273, + "cond": 20274, + "ĠICP": 20275, + "orescent": 20276, + "Ġauthority": 20277, + "Ġmock": 20278, + "igmoid": 20279, + "Ġcomorbidities": 20280, + "simple": 20281, + "Ġblo": 20282, + "ĠCompute": 20283, + "Ġgestation": 20284, + "achusetts": 20285, + "Ġphantom": 20286, + "ĠEdward": 20287, + "ĠFBS": 20288, + "factors": 20289, + "ĠEstimates": 20290, + "clear": 20291, + "WB": 20292, + "products": 20293, + "numpy": 20294, + "brief": 20295, + "Ġshop": 20296, + "ĠPoli": 20297, + "ĠRespiratory": 20298, + "Ġsurprisingly": 20299, + "Ġnanocomposite": 20300, + "dividual": 20301, + "Ġholographic": 20302, + "ygdala": 20303, + "roplasty": 20304, + "otactic": 20305, + "ĠPennsylvania": 20306, + "ĠScore": 20307, + "Obj": 20308, + "Ġstories": 20309, + "Ġmaximizing": 20310, + "Ġgelatin": 20311, + "rites": 20312, + "ĠTau": 20313, + "Ġtrypsin": 20314, + "Ġith": 20315, + "Ġfaint": 20316, + "Ġpriming": 20317, + "eworthy": 20318, + "ĠInverse": 20319, + "Ġknots": 20320, + "sharp": 20321, + "Ġtrains": 20322, + "Ġcredit": 20323, + "ĠBelow": 20324, + "pixel": 20325, + "Ġspindle": 20326, + "ĠPast": 20327, + "Ġenumerate": 20328, + "olateral": 20329, + "Ġattending": 20330, + "Ġquantized": 20331, + "Ġhaplotypes": 20332, + "encl": 20333, + "Ġwaven": 20334, + "Ġfurthermore": 20335, + "Ġchallenged": 20336, + "Ġmanufactured": 20337, + "ipheral": 20338, + "Ġinfinites": 20339, + "ĠRand": 20340, + "Ġstaging": 20341, + "agan": 20342, + "Ġperox": 20343, + "trifluor": 20344, + "ĠMcK": 20345, + "ĠFOX": 20346, + "ĠLank": 20347, + "ĠLuo": 20348, + "ĠAnth": 20349, + "ibrio": 20350, + "yel": 20351, + "ĠJi": 20352, + "ĠIO": 20353, + "ĠBridge": 20354, + "ĠRow": 20355, + "Ġcompensated": 20356, + "atsu": 20357, + "Ġhypothetical": 20358, + "Ġterminals": 20359, + "Ġcobalt": 20360, + "mers": 20361, + "ĠMang": 20362, + "NI": 20363, + "ĠRac": 20364, + "ALS": 20365, + "fen": 20366, + "ĠUb": 20367, + "Ġpredation": 20368, + "cadherin": 20369, + "ĠShanghai": 20370, + "Ġtries": 20371, + "Ġsport": 20372, + "acrylate": 20373, + "ĠAlgebraic": 20374, + "aints": 20375, + "Expr": 20376, + "Ġandrogen": 20377, + "Ġwedge": 20378, + "disp": 20379, + "Ġstirred": 20380, + "ĠAle": 20381, + "Ġcock": 20382, + "Four": 20383, + "Ġscanner": 20384, + "Ġplasmon": 20385, + "ĠGender": 20386, + "ĠRecord": 20387, + "ĠInjury": 20388, + "oblastic": 20389, + "ĠFluorescence": 20390, + "Ġantidepress": 20391, + "Ġdefinitive": 20392, + "Ġrepression": 20393, + "ordinates": 20394, + "Ġangiography": 20395, + "ĠHelical": 20396, + "Ġcancellation": 20397, + "release": 20398, + "Ġrelational": 20399, + "ĠAndre": 20400, + "molecule": 20401, + "Ġshaping": 20402, + "ĠDenmark": 20403, + "ĠALS": 20404, + "ĠNW": 20405, + "overrightarrow": 20406, + "Ġcombat": 20407, + "boxes": 20408, + "subject": 20409, + "Ġnanoscale": 20410, + "Ġcanine": 20411, + "Ġsaving": 20412, + "Ġstrategic": 20413, + "Stat": 20414, + "ĠDub": 20415, + "Ġpermitted": 20416, + "ĠTwitter": 20417, + "âĶ": 20418, + "Ġmemories": 20419, + "ĠBusiness": 20420, + "adays": 20421, + "Ġpooling": 20422, + "ĠClusters": 20423, + "imide": 20424, + "ounters": 20425, + "fraction": 20426, + "ĠCliff": 20427, + "Cam": 20428, + "Even": 20429, + "KY": 20430, + "kit": 20431, + "ibrated": 20432, + "Ġaccompanying": 20433, + "anus": 20434, + "Ġbuoy": 20435, + "Ġproliferative": 20436, + "Ġproc": 20437, + "Ġstabilizing": 20438, + "ĠNamely": 20439, + "posp": 20440, + "soon": 20441, + "Ġaberrant": 20442, + "Ġinterstellar": 20443, + "Overall": 20444, + "ĠGn": 20445, + "ĠFeedback": 20446, + "Ġoracle": 20447, + "Ġprenatal": 20448, + "commun": 20449, + "Ġoutbreaks": 20450, + "Ġfertilization": 20451, + "ĠMAG": 20452, + "Ġsinger": 20453, + "ĠMicrowave": 20454, + "ĠParliament": 20455, + "casting": 20456, + "General": 20457, + "algorithm": 20458, + "Ġphrase": 20459, + "Ġavian": 20460, + "ĠPLA": 20461, + "Ġhardly": 20462, + "approximately": 20463, + "ARCH": 20464, + "Ġtransc": 20465, + "Ġdecomp": 20466, + "contin": 20467, + "ĠMilky": 20468, + "Ġherpes": 20469, + "Range": 20470, + "OFF": 20471, + "prisingly": 20472, + "lx": 20473, + "ĠABA": 20474, + "Ġshore": 20475, + "Ġderiving": 20476, + "Ġpellets": 20477, + "nowledg": 20478, + "Item": 20479, + "stranded": 20480, + "built": 20481, + "Glc": 20482, + "quist": 20483, + "ĠSubstrate": 20484, + "Ġtraditionally": 20485, + "ĠMount": 20486, + "ivalence": 20487, + "axation": 20488, + "Ġlocate": 20489, + "Ġgun": 20490, + "Ġvocabulary": 20491, + "ĠPolym": 20492, + "Ġect": 20493, + "Ġmult": 20494, + "Ġsedimentary": 20495, + "Ġautocorrelation": 20496, + "ĠSympt": 20497, + "Ġterritory": 20498, + "Ġexcitatory": 20499, + "Ġvote": 20500, + "Ġhered": 20501, + "acea": 20502, + "ĠFocus": 20503, + "ampling": 20504, + "ffee": 20505, + "Ġprimes": 20506, + "ĠMaking": 20507, + "irs": 20508, + "MPs": 20509, + "Ġlitter": 20510, + "amethasone": 20511, + "ĠkJ": 20512, + "Ġsecretory": 20513, + "Ġcostly": 20514, + "Ġpartnership": 20515, + "ĠBacteria": 20516, + "Ġperoxidation": 20517, + "stroke": 20518, + "ĠSav": 20519, + "ĠBW": 20520, + "Ġconnects": 20521, + "Ġamine": 20522, + "ril": 20523, + "Ġbattle": 20524, + "ĠNotes": 20525, + "ĠProvid": 20526, + "ĠInstitutional": 20527, + "Ġpropri": 20528, + "fan": 20529, + "Ġpun": 20530, + "romb": 20531, + "vities": 20532, + "ĠCAM": 20533, + "ĠIsh": 20534, + "ĠHN": 20535, + "ĠRecomb": 20536, + "sche": 20537, + "Ġsynchrotron": 20538, + "rik": 20539, + "synaptic": 20540, + "ĠGeorgia": 20541, + "??": 20542, + "CY": 20543, + "Ġcorresponded": 20544, + "kinase": 20545, + "ĠITS": 20546, + "Ġproposals": 20547, + "Ġbioge": 20548, + "ĠESR": 20549, + "ĠWen": 20550, + "ĠJa": 20551, + "ĠSevere": 20552, + "ĠAden": 20553, + "ĠCCL": 20554, + "Ġseat": 20555, + "ĠKre": 20556, + "Ġhelping": 20557, + "Ġnets": 20558, + "ĠLep": 20559, + "hedra": 20560, + "opoulos": 20561, + "ĠBak": 20562, + "ansas": 20563, + "Ġrefrig": 20564, + "Ġubiquitous": 20565, + "Ġmatters": 20566, + "Ġsilicate": 20567, + "ĠLastly": 20568, + "ĠTheories": 20569, + "Ġagarose": 20570, + "biggr": 20571, + "transition": 20572, + "ĠDecomposition": 20573, + "bromo": 20574, + "Ġstakeholders": 20575, + "ĠEE": 20576, + "Only": 20577, + "ĠKenya": 20578, + "Ġargon": 20579, + "ĠIdentifying": 20580, + "Ġtournament": 20581, + "clock": 20582, + "ĠCFU": 20583, + "ĠBehavioral": 20584, + "Ġpod": 20585, + "Ġtaxonomy": 20586, + "ĠProduct": 20587, + "ĠAlong": 20588, + "Ġfamilial": 20589, + "Ġdescriptor": 20590, + "vated": 20591, + "ĠVariables": 20592, + "tp": 20593, + "Ġgoods": 20594, + "ĠAST": 20595, + "ĠAnis": 20596, + "Ġspinor": 20597, + "attention": 20598, + "Ġbasket": 20599, + "Struct": 20600, + "Ġimmunohistochemical": 20601, + "engers": 20602, + "CAT": 20603, + "Ġtangential": 20604, + "Cap": 20605, + "ĠPair": 20606, + "Ġviscoelastic": 20607, + "ĠAds": 20608, + "Ġglycosylation": 20609, + "Ġdur": 20610, + "ĠMinimum": 20611, + "Ġrigidity": 20612, + "stats": 20613, + "tillation": 20614, + "ĠDiscrim": 20615, + "ĠLegend": 20616, + "Previous": 20617, + "film": 20618, + "Ġaluminium": 20619, + "Micro": 20620, + "inia": 20621, + "egel": 20622, + "ĠSubcellular": 20623, + "Ġbottleneck": 20624, + "Ġsyll": 20625, + "icle": 20626, + "Ġsheaf": 20627, + "chell": 20628, + "example": 20629, + "ĠSelected": 20630, + "Ġpredators": 20631, + "Ġreper": 20632, + "Ġstrugg": 20633, + "ĠMaria": 20634, + "lyl": 20635, + "LF": 20636, + "Ġexercises": 20637, + "obium": 20638, + "ILITY": 20639, + "corrected": 20640, + "Ġbenchmarks": 20641, + "ĠTol": 20642, + "Ġintercept": 20643, + "ĠCalculation": 20644, + "ĠIndonesia": 20645, + "Ġglioblastoma": 20646, + "KM": 20647, + "ĠSupplemental": 20648, + "Ġcitizens": 20649, + "adren": 20650, + "Ġmultimodal": 20651, + "Ġmosquitoes": 20652, + "iva": 20653, + "ĠFindings": 20654, + "ĠPub": 20655, + "ĠMacroph": 20656, + "Acknowledg": 20657, + "Ġbasins": 20658, + "exact": 20659, + "Ġgrades": 20660, + "Ġfir": 20661, + "iga": 20662, + "ĠPolynomial": 20663, + "ĠLongitudinal": 20664, + "Ġsemiconductors": 20665, + "Top": 20666, + "iptera": 20667, + "Ġlacks": 20668, + "rograph": 20669, + "Ġselects": 20670, + "Ġsweet": 20671, + "Ġbac": 20672, + "Ġdownloaded": 20673, + "aponic": 20674, + "ijk": 20675, + "otonic": 20676, + "normalized": 20677, + "ĠVariability": 20678, + "division": 20679, + "ĠSupers": 20680, + "ilab": 20681, + "Human": 20682, + "Ġleptin": 20683, + "Ġosmotic": 20684, + "Ġhur": 20685, + "ĠSingapore": 20686, + "ĠOPT": 20687, + "ĠSoviet": 20688, + "litaxel": 20689, + "retaceous": 20690, + "ĠOnc": 20691, + "ĠIX": 20692, + "ulas": 20693, + "uent": 20694, + "Ġlymphoid": 20695, + "Tc": 20696, + "Ġrationale": 20697, + "Layer": 20698, + "osities": 20699, + "Ġdesire": 20700, + "ĠAnnual": 20701, + "uba": 20702, + "ĠCompounds": 20703, + "Ġantifungal": 20704, + "Ġcationic": 20705, + "items": 20706, + "acterium": 20707, + "amilies": 20708, + "Ġelongated": 20709, + "ĠMassachusetts": 20710, + "ĠIrish": 20711, + "asso": 20712, + "azo": 20713, + "ĠBurk": 20714, + "robenius": 20715, + "Ġisinstance": 20716, + "bion": 20717, + "Ġgreedy": 20718, + "Ġnicotine": 20719, + "Ġretrieve": 20720, + "Ġsympathetic": 20721, + "quee": 20722, + "Ġfoli": 20723, + "Ġsputter": 20724, + "Ġgrading": 20725, + "determined": 20726, + "Ġabnorm": 20727, + "Ġmanagers": 20728, + "Ġtopical": 20729, + "Ġimmig": 20730, + "ĠDNN": 20731, + "gtr": 20732, + "Ġdetections": 20733, + "ĠObesity": 20734, + "suc": 20735, + "ĠSche": 20736, + "Ġtrunk": 20737, + "Ġtough": 20738, + "ĠBN": 20739, + "Ġru": 20740, + "oxif": 20741, + "Ġaiming": 20742, + "ĠExtracellular": 20743, + "Ġhaplotype": 20744, + "Du": 20745, + "ĠDing": 20746, + "ĠDol": 20747, + "Ġhumid": 20748, + "brom": 20749, + "Ġoffline": 20750, + "Combining": 20751, + "Ġpulsar": 20752, + "Ġpari": 20753, + "partate": 20754, + "imated": 20755, + "Ġwatershed": 20756, + "acrylamide": 20757, + "exec": 20758, + "ĠComposite": 20759, + "Ġdispersive": 20760, + "Ġtons": 20761, + "rometry": 20762, + "ĠJud": 20763, + "aza": 20764, + "Ġchickens": 20765, + "register": 20766, + "nz": 20767, + "Util": 20768, + "ĠVes": 20769, + "eV": 20770, + "ĠRule": 20771, + "substituted": 20772, + "Conv": 20773, + "query": 20774, + "Mac": 20775, + "ĠTar": 20776, + "implies": 20777, + "ĠRates": 20778, + "Ġrins": 20779, + "Ġtimescales": 20780, + "ĠCzech": 20781, + "Such": 20782, + "restimate": 20783, + "ĠMb": 20784, + "ĠFuj": 20785, + "ĠIMD": 20786, + "cit": 20787, + "Ġraising": 20788, + "........": 20789, + "home": 20790, + "asted": 20791, + "Ġocta": 20792, + "Ġcadmium": 20793, + "Ġpsori": 20794, + "roleum": 20795, + "ĠStellar": 20796, + "ĠKinase": 20797, + "ĠGard": 20798, + "ieu": 20799, + "ĠMoS": 20800, + "MG": 20801, + "ĠGSH": 20802, + "Ġhazards": 20803, + "Ġnice": 20804, + "heating": 20805, + "Ġreproducible": 20806, + "genesis": 20807, + "ĠIgM": 20808, + "Ġbeat": 20809, + "onuclease": 20810, + "entralized": 20811, + "ĠLé": 20812, + "Ġdol": 20813, + "Ġdeeply": 20814, + "ractive": 20815, + "Ġglial": 20816, + "iella": 20817, + "Ġinitialized": 20818, + "ĠMethodology": 20819, + "Ġbenthic": 20820, + "omi": 20821, + "ĠAlter": 20822, + "Ordered": 20823, + "ĠLIN": 20824, + "Ġunilateral": 20825, + "Ġcorticoster": 20826, + "LEN": 20827, + "Ġdilute": 20828, + "Ġmetalloprotein": 20829, + "abeth": 20830, + "ampion": 20831, + "Ġmoral": 20832, + "ĠSiC": 20833, + "Ġquadrature": 20834, + "Ġsedimentation": 20835, + "ete": 20836, + "ĠFrag": 20837, + "Ġpeaked": 20838, + "Ġmitigation": 20839, + "Ġsoldi": 20840, + "Ġdoubly": 20841, + "Ġellipso": 20842, + "ĠlncRNAs": 20843, + "Ġâİ¢": 20844, + "ĠSame": 20845, + "ĠSustain": 20846, + "ĠCapacity": 20847, + "Ġsomat": 20848, + "Ġtransistor": 20849, + "Ġassayed": 20850, + "ĠNur": 20851, + "tools": 20852, + "Sing": 20853, + "Ġligament": 20854, + "atever": 20855, + "Ġperce": 20856, + "hence": 20857, + "UX": 20858, + "sent": 20859, + "EGG": 20860, + "third": 20861, + "enders": 20862, + "theoretic": 20863, + "Ġrewards": 20864, + "uto": 20865, + "Ġinstallation": 20866, + "ĠKinetic": 20867, + "ĠInnov": 20868, + "ĠSolving": 20869, + "ĠSymmetry": 20870, + "Ġramp": 20871, + "Ġneuropathy": 20872, + "omerization": 20873, + "Ġcatech": 20874, + "Pred": 20875, + "ĠBoh": 20876, + "EMENT": 20877, + "Ġarmy": 20878, + "ĠYukawa": 20879, + "Ġalignments": 20880, + "ĠDependence": 20881, + "Ġenv": 20882, + "ean": 20883, + "sr": 20884, + "Ġinterpreting": 20885, + "elocity": 20886, + "Ġpsychology": 20887, + "Ġbiofilms": 20888, + "Ġeccentricity": 20889, + "lot": 20890, + "analytic": 20891, + "Ġperiodicity": 20892, + "nings": 20893, + "ĠKent": 20894, + "flag": 20895, + "Ġmp": 20896, + "ĠNich": 20897, + "hire": 20898, + "Ġflare": 20899, + "Ġcitrate": 20900, + "Ġpaste": 20901, + "Ġdelete": 20902, + "zymes": 20903, + "orientation": 20904, + "ĠHY": 20905, + "Ġcommands": 20906, + "Ġstrike": 20907, + "symbol": 20908, + "ĠMind": 20909, + "Ġoptimisation": 20910, + "Ġosteoporosis": 20911, + "ĠInflammation": 20912, + "ĠIntelligence": 20913, + "eh": 20914, + "utum": 20915, + "Ġvec": 20916, + "ellation": 20917, + "ĠBloch": 20918, + "ĠMajorana": 20919, + "enor": 20920, + "ĠNgu": 20921, + "Ġdeuter": 20922, + "opedia": 20923, + "Ġutter": 20924, + "Ġribosome": 20925, + "Ġactors": 20926, + "electronic": 20927, + "ée": 20928, + "Ġfeaturing": 20929, + "agle": 20930, + "Ġperin": 20931, + "ĠCivil": 20932, + "Ġpredefined": 20933, + "lag": 20934, + "ĠJAK": 20935, + "jamin": 20936, + "individual": 20937, + "onc": 20938, + "Ġfishing": 20939, + "ditive": 20940, + "Norm": 20941, + "ĠScanning": 20942, + "vanishing": 20943, + "Ġcessation": 20944, + "ĠHole": 20945, + "ributes": 20946, + "IE": 20947, + "ĠMpc": 20948, + "wegian": 20949, + "Ma": 20950, + "Ġrevisited": 20951, + "ĠPlus": 20952, + "abilized": 20953, + "Ġscanned": 20954, + "ĠExchange": 20955, + "Ġbromide": 20956, + "Life": 20957, + "otroph": 20958, + "ADS": 20959, + "âĭħ": 20960, + "Ġoperative": 20961, + "ĠBERT": 20962, + "Ġplume": 20963, + "Ġpoorer": 20964, + "Ġtrout": 20965, + "Ġmicrotubule": 20966, + "Ġphosphatidyl": 20967, + "radius": 20968, + "ĠMuscle": 20969, + "Ġcarcinogenesis": 20970, + "Ġseeing": 20971, + "uclein": 20972, + "follow": 20973, + "Ġsupplements": 20974, + "olars": 20975, + "specially": 20976, + "Ġcompleting": 20977, + "Ġnaïve": 20978, + "ĠÏ©": 20979, + "clerotic": 20980, + "Disc": 20981, + "ĠFestival": 20982, + "Ġclick": 20983, + "clusive": 20984, + "Ġcatalogue": 20985, + "Ġapps": 20986, + "ĠSED": 20987, + "Ġstacked": 20988, + "Ġtune": 20989, + "ĠDMEM": 20990, + "Ġaerosols": 20991, + "Ġgear": 20992, + "antine": 20993, + "ĠStone": 20994, + "Ġpositives": 20995, + "triang": 20996, + "probability": 20997, + "Ġdecoupling": 20998, + "ĠÍĵ": 20999, + "ĠVin": 21000, + "Ġsurvived": 21001, + "Ġreplicated": 21002, + "utrient": 21003, + "Ġtemperate": 21004, + "Ġensembles": 21005, + "Ġmulticenter": 21006, + "Ġgaseous": 21007, + "idea": 21008, + "classification": 21009, + "ĠOutcome": 21010, + "clonal": 21011, + "Ġdiscontinuity": 21012, + "Ġadvantageous": 21013, + "Ġdistricts": 21014, + "ĠIBM": 21015, + "inguishable": 21016, + "Ġcars": 21017, + "cult": 21018, + "enriched": 21019, + "argin": 21020, + "novae": 21021, + "steady": 21022, + "Ġbuy": 21023, + "piration": 21024, + "Ġpartitioned": 21025, + "Ġinability": 21026, + "pq": 21027, + "Ġbull": 21028, + "odend": 21029, + "Ġassistant": 21030, + "Ġlumen": 21031, + "Ġconverting": 21032, + "PY": 21033, + "zol": 21034, + "utors": 21035, + "ĠNLRP": 21036, + "apply": 21037, + "ĠBonferroni": 21038, + "Ls": 21039, + "Ġtips": 21040, + "ĠLN": 21041, + "rolase": 21042, + "Ġadvis": 21043, + "ĠMetast": 21044, + "Ġsaliva": 21045, + "Ġinhabit": 21046, + "Ġrim": 21047, + "debug": 21048, + "Any": 21049, + "Ġforb": 21050, + "Ġversatile": 21051, + "ĠCompact": 21052, + "voc": 21053, + "ĠIso": 21054, + "ĠJus": 21055, + "bodies": 21056, + "ARM": 21057, + "ĠGWAS": 21058, + "hetized": 21059, + "Ġmicrofluidic": 21060, + "Ġacetonitrile": 21061, + "Ġinhom": 21062, + "Ġparench": 21063, + "Ġinsensitive": 21064, + "Ġagency": 21065, + "poor": 21066, + "ĠAngi": 21067, + "Ġapproached": 21068, + "Ġemulsion": 21069, + "Ġvoluntary": 21070, + "utt": 21071, + "ĠRecurrent": 21072, + "riculum": 21073, + "ê": 21074, + "Ġtall": 21075, + "ĠDepth": 21076, + "Ġff": 21077, + "ĠIncidence": 21078, + "Ġmanifestation": 21079, + "Ġcompromised": 21080, + "iaceae": 21081, + "ĠMIT": 21082, + "otransfer": 21083, + "ĠWD": 21084, + "mov": 21085, + "ĠManual": 21086, + "Medi": 21087, + "Ġinterfering": 21088, + "ĠJacobi": 21089, + "KT": 21090, + "Ġsarcoma": 21091, + "Ġkidneys": 21092, + "Ġodor": 21093, + "Ġti": 21094, + "yday": 21095, + "although": 21096, + "visible": 21097, + "Ġdengue": 21098, + "ĠCAL": 21099, + "strat": 21100, + "ĠVariations": 21101, + "inib": 21102, + "components": 21103, + "ĠTob": 21104, + "ĠAntioxidant": 21105, + "ÍĶ": 21106, + "Ġkiller": 21107, + "Ġsubtracted": 21108, + "ĠEvents": 21109, + "Ġimplements": 21110, + "ĠGAN": 21111, + "Ġprophylaxis": 21112, + "Ġnozz": 21113, + "Ġsmoothed": 21114, + "Ġdecaying": 21115, + "ĠInitially": 21116, + "Ġuncommon": 21117, + "Ġconductor": 21118, + "ĠWOR": 21119, + "avity": 21120, + "ĠXie": 21121, + "ĠAcet": 21122, + "Ġine": 21123, + "ĠBeam": 21124, + "opolymer": 21125, + "ĠXML": 21126, + "ĠWide": 21127, + "Ñĥ": 21128, + "Ġejection": 21129, + "BMI": 21130, + "tc": 21131, + "uez": 21132, + "Ġcerebellar": 21133, + "Ġcatchment": 21134, + "coxon": 21135, + "ĠShannon": 21136, + "Ġcentrality": 21137, + "Ġsafely": 21138, + "probe": 21139, + "ĠLaboratories": 21140, + "Ġnc": 21141, + "Ġspher": 21142, + "Ġprobing": 21143, + "ĠLev": 21144, + "Ġaf": 21145, + "ĠMig": 21146, + "ĠVascular": 21147, + "Ġprogrammes": 21148, + "Ġcontaminants": 21149, + "sequent": 21150, + "Ġbonded": 21151, + "integration": 21152, + "bos": 21153, + "ĠFew": 21154, + "ĠIllinois": 21155, + "She": 21156, + "WC": 21157, + "ĠGPIO": 21158, + "oC": 21159, + "ĠMaternal": 21160, + "ercetin": 21161, + "ĠMassive": 21162, + "Ġenorm": 21163, + "imgur": 21164, + "Ġbidirectional": 21165, + "ĠGraphene": 21166, + "insky": 21167, + "ĠObserve": 21168, + "Ġstops": 21169, + "bio": 21170, + "ĠLines": 21171, + "ĠGill": 21172, + "Ġeigenvector": 21173, + "Space": 21174, + "ĠMining": 21175, + "Ġmelatonin": 21176, + "ĠSET": 21177, + "onsequ": 21178, + "oscale": 21179, + "ĠRaw": 21180, + "Ġreviewers": 21181, + "Ġnanofib": 21182, + "taking": 21183, + "ammad": 21184, + "Ġrecursion": 21185, + "usal": 21186, + "Ġpositron": 21187, + "ĠNIH": 21188, + "ĠINTER": 21189, + "ĠDocument": 21190, + "Ġconstantly": 21191, + "Ġundergone": 21192, + "Ġelectroweak": 21193, + "Ġiteratively": 21194, + "folio": 21195, + "Ġsubfamily": 21196, + "Ġâİ¥": 21197, + "Page": 21198, + "ferm": 21199, + "avir": 21200, + "Ġagencies": 21201, + "Ġpolys": 21202, + "ĠSquare": 21203, + "ymm": 21204, + "Ġhydrogels": 21205, + "almost": 21206, + "arter": 21207, + "Ġankle": 21208, + "Ġrises": 21209, + "Ġmedull": 21210, + "gated": 21211, + "Ġmononuclear": 21212, + "Ġdiscussing": 21213, + "Ġprofessor": 21214, + "transformed": 21215, + "Ġcolours": 21216, + "ragg": 21217, + "emicon": 21218, + "Ġsymmetrical": 21219, + "Ġplacental": 21220, + "Ġli": 21221, + "Ġstudio": 21222, + "sequences": 21223, + "Ġtam": 21224, + "ĠLap": 21225, + "ĠCriteria": 21226, + "Ġhappened": 21227, + "Ġantiferromagnetic": 21228, + "ĠHausdorff": 21229, + "ĠCONCLUSIONS": 21230, + "HER": 21231, + "VR": 21232, + "ĠKor": 21233, + "ĠAPO": 21234, + "Ġprotecting": 21235, + "ĠSOL": 21236, + "ĠBuck": 21237, + "phia": 21238, + "ĠMultim": 21239, + "onine": 21240, + "ulsions": 21241, + "Ġgp": 21242, + "benzamide": 21243, + "ĠNADPH": 21244, + "ĠOhio": 21245, + "ĠMEG": 21246, + "COVID": 21247, + "Ġdisplaced": 21248, + "ĠAbb": 21249, + "Ġbranched": 21250, + "ĠNavy": 21251, + "ĠNrf": 21252, + "ĠODE": 21253, + "achi": 21254, + "ĠTransient": 21255, + "Ġcircumference": 21256, + "Ġbees": 21257, + "iration": 21258, + "Ġfaculty": 21259, + "IGHT": 21260, + "ĠMetabolism": 21261, + "MK": 21262, + "ĠInequ": 21263, + "ĠQualitative": 21264, + "PBS": 21265, + "terminus": 21266, + "kary": 21267, + "ovian": 21268, + "ĠTHz": 21269, + "ĠReliability": 21270, + "furan": 21271, + "Ġcorners": 21272, + "Ġattacker": 21273, + "Ġmarriage": 21274, + "oprecipitation": 21275, + "ĠCry": 21276, + "ĠâĬĻ": 21277, + "Ġevolves": 21278, + "Ġban": 21279, + "Ġdiurnal": 21280, + "ounce": 21281, + "Ġoverw": 21282, + "ĠHoff": 21283, + "Ġextrinsic": 21284, + "amps": 21285, + "ULAR": 21286, + "opher": 21287, + "Ġlighting": 21288, + "Ġarchitectural": 21289, + "hesive": 21290, + "Ġsavings": 21291, + "Ġglaucoma": 21292, + "ozoa": 21293, + "ĠOption": 21294, + "controll": 21295, + "ecker": 21296, + "Ġosteocl": 21297, + "Ġglycine": 21298, + "analyses": 21299, + "ĠAld": 21300, + "ĠSyd": 21301, + "ĠCx": 21302, + "Ġscalars": 21303, + "Ġknowing": 21304, + "Ġremember": 21305, + "ĠEmbry": 21306, + "TEM": 21307, + "ĠBran": 21308, + "FORM": 21309, + "Ġsurviving": 21310, + "Ġglobular": 21311, + "Ġinclusive": 21312, + "sched": 21313, + "UTION": 21314, + "Ġquadrupole": 21315, + "ĠHubbard": 21316, + "Ġaxonal": 21317, + "ĠCosmic": 21318, + "Ġslots": 21319, + "ĠProcedure": 21320, + "agin": 21321, + "ĠLoop": 21322, + "arer": 21323, + "Ġbutter": 21324, + "Ġhistopathological": 21325, + "fusion": 21326, + "ANOVA": 21327, + "Ġclosing": 21328, + "ĠLord": 21329, + "ĠBis": 21330, + "ĠRAM": 21331, + "IDE": 21332, + "Ġjournals": 21333, + "Ġmonkeys": 21334, + "Ġattenuates": 21335, + "Ġsegmented": 21336, + "TOF": 21337, + "otional": 21338, + "polymer": 21339, + "ĠShah": 21340, + "Akt": 21341, + "Wr": 21342, + "lov": 21343, + "Ġpolymorphic": 21344, + "Ġarrangements": 21345, + "UF": 21346, + "lon": 21347, + "Ġdepressed": 21348, + "NAT": 21349, + "ĠOperation": 21350, + "ι": 21351, + "ĠRan": 21352, + "âIJ": 21353, + "Ġthereafter": 21354, + "Ġmyeloma": 21355, + "jor": 21356, + "Ã¥": 21357, + "ĠWinter": 21358, + "ptosis": 21359, + "Dir": 21360, + "verty": 21361, + "ĠFinn": 21362, + "Ġortholog": 21363, + "Ġmonotonically": 21364, + "Ġtectonic": 21365, + "ĠGBM": 21366, + "ĠAO": 21367, + "Ġgenerative": 21368, + "Clearly": 21369, + "Ġtile": 21370, + "ĠRNN": 21371, + "Ġgrounds": 21372, + "GaAs": 21373, + "Ġbee": 21374, + "ĠBoy": 21375, + "ĠTranscriptional": 21376, + "urin": 21377, + "otom": 21378, + "Ġsinusoidal": 21379, + "ĠAy": 21380, + "ĠClinic": 21381, + "utorial": 21382, + "ĠADC": 21383, + "ERIAL": 21384, + "cation": 21385, + "ĠADHD": 21386, + "cyclohex": 21387, + "ĠHawai": 21388, + "astom": 21389, + "Ġmorphologies": 21390, + "Ġrodents": 21391, + "Ġscalability": 21392, + "ROS": 21393, + "aemia": 21394, + "Ġdecompose": 21395, + "Ġpivotal": 21396, + "Ġdiffusivity": 21397, + "Ġcovalent": 21398, + "ĠKD": 21399, + "atalyst": 21400, + "Ġoldest": 21401, + "Ġsuitability": 21402, + "Ġwants": 21403, + "ifts": 21404, + "ĠDistributions": 21405, + "ĠQueen": 21406, + "lich": 21407, + "Ġparse": 21408, + "ĠMHD": 21409, + "Ġrecre": 21410, + "Ġhydroxide": 21411, + "eum": 21412, + "Ġlev": 21413, + "Ġreferral": 21414, + "planes": 21415, + "ĠEgypt": 21416, + "Ġlenti": 21417, + "Ġtransactions": 21418, + "Ġexpense": 21419, + "Ġcysts": 21420, + "Ġabscess": 21421, + "ĠmicroRNAs": 21422, + "effectiveness": 21423, + "ĠDifferentiation": 21424, + "Ġcertif": 21425, + "cience": 21426, + "ĠREL": 21427, + "Ġreadout": 21428, + "ĠQuasi": 21429, + "Ġrounded": 21430, + "otti": 21431, + "efficients": 21432, + "Ġsynchronized": 21433, + "Ġsilico": 21434, + "Ġforecasts": 21435, + "Ġdμ": 21436, + "Ġexotic": 21437, + "ĠOCT": 21438, + "xb": 21439, + "Ġasynchronous": 21440, + "nez": 21441, + "chiat": 21442, + "Ġhaemat": 21443, + "Ġfulfill": 21444, + "ĠMix": 21445, + "ibli": 21446, + "fm": 21447, + "Ġjava": 21448, + "soluble": 21449, + "Ġincompressible": 21450, + "âĨij": 21451, + "CDM": 21452, + "Ġdilation": 21453, + "LYP": 21454, + "ashes": 21455, + "ĠSports": 21456, + "Ġfundament": 21457, + "ĠSaudi": 21458, + "Ġenroll": 21459, + "ĠNaOH": 21460, + "Ġcrustal": 21461, + "ĠInstruments": 21462, + "Ġïģ¡": 21463, + "Result": 21464, + "Ġpreferential": 21465, + "Ġsugars": 21466, + "Ġdimers": 21467, + "ĠEmerging": 21468, + "ère": 21469, + "diabetic": 21470, + "Ġstrengthening": 21471, + "epi": 21472, + "ĠMeg": 21473, + "ĠYour": 21474, + "ĠSetting": 21475, + "lez": 21476, + "ĠBou": 21477, + "Ġhistology": 21478, + "Ġolive": 21479, + "ĠDisorders": 21480, + "Ġdistorted": 21481, + "Ġcompete": 21482, + "cens": 21483, + "ĠAe": 21484, + "ĠGG": 21485, + "Ġquantifying": 21486, + "Ġaur": 21487, + "ĠWright": 21488, + "Ġsuperconductor": 21489, + "eds": 21490, + "stalk": 21491, + "concent": 21492, + "ĠLimited": 21493, + "Ġstyles": 21494, + "design": 21495, + "ĠEllip": 21496, + "PLA": 21497, + "mogorov": 21498, + "ĠRidge": 21499, + "Ġrandomization": 21500, + "aft": 21501, + "icially": 21502, + "ĠBiotechnology": 21503, + "Ġseizure": 21504, + "KI": 21505, + "AVE": 21506, + "receptor": 21507, + "Ġgrammar": 21508, + "Ġcrime": 21509, + "nection": 21510, + "inces": 21511, + "ĠCompton": 21512, + "Ġventricle": 21513, + "Ġredistribution": 21514, + "ynaptic": 21515, + "Parameter": 21516, + "Normal": 21517, + "Pack": 21518, + "ermann": 21519, + "ulants": 21520, + "degenerate": 21521, + "ĠNewtonian": 21522, + "Ġancestral": 21523, + "phrag": 21524, + "Ġimpression": 21525, + "Ġnormalize": 21526, + "Ġambiguous": 21527, + "Ġingredient": 21528, + "ĠClaim": 21529, + "Ġcleaved": 21530, + "ĠApproaches": 21531, + "ĠSPECT": 21532, + "csv": 21533, + "ĠReveals": 21534, + "ĠWaves": 21535, + "Ġdwarfs": 21536, + "ĠProgress": 21537, + "Ġaorta": 21538, + "Ġnig": 21539, + "ĠAdams": 21540, + "ĠMüller": 21541, + "ĠYellow": 21542, + "ĠCord": 21543, + "ĠPhill": 21544, + "ĠFormal": 21545, + "besgue": 21546, + "termin": 21547, + "rn": 21548, + "bn": 21549, + "kine": 21550, + "rit": 21551, + "qi": 21552, + "ĠRoute": 21553, + "enol": 21554, + "ĠASC": 21555, + "ĠPu": 21556, + "mill": 21557, + "umer": 21558, + "Ġsupernova": 21559, + "iative": 21560, + "differenti": 21561, + "Ġtolu": 21562, + "opus": 21563, + "RM": 21564, + "Ġpoverty": 21565, + "ĠXX": 21566, + "ĠïĤ¶": 21567, + "ultry": 21568, + "Optim": 21569, + "Ġglacial": 21570, + "ĠDispers": 21571, + "Ġdifferentiating": 21572, + "ández": 21573, + "project": 21574, + "ĠEliz": 21575, + "scaling": 21576, + "ĠToll": 21577, + "Ġnonempty": 21578, + "Ġpredicate": 21579, + "Ġgyrus": 21580, + "minute": 21581, + "âĸ": 21582, + "ĠHind": 21583, + "ĠLiving": 21584, + "VS": 21585, + "prior": 21586, + "ĠVertical": 21587, + "arks": 21588, + "ĠSFR": 21589, + "ĠVietnam": 21590, + "compare": 21591, + ">>>": 21592, + "Ġbanks": 21593, + "Ġseptic": 21594, + "ĠBif": 21595, + "ĠEPS": 21596, + "ĠIntel": 21597, + "ĠDisorder": 21598, + "PN": 21599, + "ĠNord": 21600, + "tiveness": 21601, + "Ġdrilling": 21602, + "ĠSubject": 21603, + "enario": 21604, + "Ġrms": 21605, + "phones": 21606, + "hang": 21607, + "ĠTechnique": 21608, + "Ġclot": 21609, + "Ġintersections": 21610, + "Ġanions": 21611, + "above": 21612, + "Ġclause": 21613, + "Ġgenu": 21614, + "ozo": 21615, + "rhiz": 21616, + "Ġlobes": 21617, + "ĠBian": 21618, + "Ġexerted": 21619, + "ureth": 21620, + "roma": 21621, + "ĠCharge": 21622, + "ĠSynchron": 21623, + "Ġconting": 21624, + "otherapeutic": 21625, + "gtrsim": 21626, + "ĠResonance": 21627, + "ĠFal": 21628, + "undle": 21629, + "Ġdropout": 21630, + "ĠPerspective": 21631, + "OLOG": 21632, + "atches": 21633, + "ĠSequences": 21634, + "Considering": 21635, + "Ġprospects": 21636, + "Ġaliqu": 21637, + "Ġstrata": 21638, + "Ġanalogs": 21639, + "Ġencouraged": 21640, + "ĠPulmonary": 21641, + "Ġchim": 21642, + "ĠCFT": 21643, + "unar": 21644, + "izz": 21645, + "endocrine": 21646, + "ĠCRE": 21647, + "ĠStroke": 21648, + "âĩĴ": 21649, + "upuncture": 21650, + "translational": 21651, + "ĠGriff": 21652, + "ĠSter": 21653, + "erged": 21654, + "phrine": 21655, + "Ġlivestock": 21656, + "ĠHash": 21657, + "Ġdosing": 21658, + "Ġplasmas": 21659, + "ĠComparisons": 21660, + "Ġencouraging": 21661, + "Ġcomparatively": 21662, + "Ġcharacterisation": 21663, + "Ġascending": 21664, + "ĠFixed": 21665, + "Ġvegetable": 21666, + "especially": 21667, + "ĠLange": 21668, + "ĠCoding": 21669, + "Ġvertebrate": 21670, + "FW": 21671, + "ĠORF": 21672, + "ĠTub": 21673, + "lee": 21674, + "Ġtimely": 21675, + "Ep": 21676, + "ĠâĪĴâĪŀ": 21677, + "Ġliposomes": 21678, + "Ġextremal": 21679, + "ropolitan": 21680, + "ĠCay": 21681, + "ĠBiod": 21682, + "oulli": 21683, + "Dri": 21684, + "ĠRats": 21685, + "Ġcentroid": 21686, + "ospin": 21687, + "rospinal": 21688, + "Ġsolitons": 21689, + "portive": 21690, + "ĠMcG": 21691, + "Bbb": 21692, + "Ġparaffin": 21693, + "lectively": 21694, + "Ġmetastable": 21695, + "Ġdissipative": 21696, + "Ġassemblages": 21697, + "Ġcolonic": 21698, + "Ġsized": 21699, + "Ġcryp": 21700, + "processor": 21701, + "ção": 21702, + "Ġacknowledged": 21703, + "ĠUncertainty": 21704, + "ĠIndustrial": 21705, + "Ġuncont": 21706, + "Ġrefere": 21707, + "ĠNitrogen": 21708, + "Ġlifting": 21709, + "Ġforget": 21710, + "Ġfeelings": 21711, + "Ġdigits": 21712, + "Ġstratig": 21713, + "ypes": 21714, + "Ġcompositional": 21715, + "Ġsupernatants": 21716, + "Ġconflicting": 21717, + "Ġdisadvantage": 21718, + "adelphia": 21719, + "Pd": 21720, + "ĠCoupling": 21721, + "Ġexpenditure": 21722, + "iki": 21723, + "described": 21724, + "ĠRNase": 21725, + "ĠConvex": 21726, + "ĠBax": 21727, + "ungsten": 21728, + "Ġboiling": 21729, + "Ġbasement": 21730, + "ocardial": 21731, + "history": 21732, + "inton": 21733, + "trimethyl": 21734, + "Ġgrafting": 21735, + "ĠHardy": 21736, + "ĠFemale": 21737, + "ĠFollow": 21738, + "ĠEST": 21739, + "tistic": 21740, + "Open": 21741, + "Ġreflux": 21742, + "elements": 21743, + "Ġpolysaccharide": 21744, + "dims": 21745, + "acency": 21746, + "Ġbiore": 21747, + "capac": 21748, + "Ġoverexpressed": 21749, + "either": 21750, + "Ġlaid": 21751, + "Ġincision": 21752, + "Ġassets": 21753, + "inflammation": 21754, + "Ġreconstructions": 21755, + "Ġglomerular": 21756, + "Ġconvey": 21757, + "ĠCXCR": 21758, + "oro": 21759, + "Ġclassifying": 21760, + "Ġcope": 21761, + "Ġpd": 21762, + "linic": 21763, + "Ġchord": 21764, + "ĠAging": 21765, + "Ġpalm": 21766, + "Ġpermittivity": 21767, + "ĠReverse": 21768, + "Ġoffshore": 21769, + "Ġdoubt": 21770, + "imoto": 21771, + "ĠColomb": 21772, + "Ġrodent": 21773, + "ĠElectrochemical": 21774, + "ĠImprovement": 21775, + "inescent": 21776, + "ĠTriton": 21777, + "Ġtransfusion": 21778, + "Ġlocomotion": 21779, + "Ġdangerous": 21780, + "Ġweighed": 21781, + "ĠHSV": 21782, + "techn": 21783, + "ĠDiagram": 21784, + "Ġparietal": 21785, + "six": 21786, + "Ġtitles": 21787, + "ylon": 21788, + "Ġheuristics": 21789, + "Ġjaponic": 21790, + "Ġtranslations": 21791, + "Ġtiters": 21792, + "Ġworms": 21793, + "ĠDPP": 21794, + "Ġcytoskeleton": 21795, + "Mediated": 21796, + "ariance": 21797, + "thel": 21798, + "Ãħ": 21799, + "ĠInflammatory": 21800, + "Ġoscillating": 21801, + "Ġavoids": 21802, + "Define": 21803, + "ĠOlympics": 21804, + "ogel": 21805, + "Ġheme": 21806, + "Ġmicrop": 21807, + "Ġthreats": 21808, + "QCD": 21809, + "XRD": 21810, + "ĠCoupled": 21811, + "Ġlm": 21812, + "ĠHelic": 21813, + "Ġdischarged": 21814, + "Ġrooted": 21815, + "Ġalleviate": 21816, + "Ġcausality": 21817, + "ĠCrow": 21818, + "ĠMack": 21819, + "ĠAirport": 21820, + "Ġchemokine": 21821, + "Ġll": 21822, + "ĠNar": 21823, + "omyces": 21824, + "ethoxyphenyl": 21825, + "ĠDaily": 21826, + "ĠFinland": 21827, + "Ġhorn": 21828, + "ĠOrient": 21829, + "Ġionized": 21830, + "ĠYears": 21831, + "Ġquasipar": 21832, + "Ġpercutaneous": 21833, + "Phase": 21834, + "Ġforeground": 21835, + "ĠANAL": 21836, + "Ġincrements": 21837, + "stan": 21838, + "Ġspeculate": 21839, + "TX": 21840, + "Ġpile": 21841, + "Ġdic": 21842, + "ipy": 21843, + "window": 21844, + "neutral": 21845, + "ĠAtlas": 21846, + "ĠMTT": 21847, + "ĠNy": 21848, + "ĠVIII": 21849, + "ĠFilms": 21850, + "singular": 21851, + "remove": 21852, + "Length": 21853, + "ĠRece": 21854, + "wait": 21855, + "Ġpurchase": 21856, + "ĠWikipedia": 21857, + "ĠLars": 21858, + "Ġsyntactic": 21859, + "Ġactuator": 21860, + "ĠAKT": 21861, + "ĠBry": 21862, + "ĠResult": 21863, + "ĠVariational": 21864, + "Ġjudgment": 21865, + "JECT": 21866, + "ximab": 21867, + "Ġtraced": 21868, + "Ġcardiomyopathy": 21869, + "WN": 21870, + "ĠRodrig": 21871, + "bt": 21872, + "Ġbid": 21873, + "acle": 21874, + "amura": 21875, + "Ġepic": 21876, + "Ġpuzz": 21877, + "ĠSox": 21878, + "Ġinflux": 21879, + "ÃŃn": 21880, + "uloskeletal": 21881, + "Dim": 21882, + "ĠSCC": 21883, + "ĠRAS": 21884, + "mr": 21885, + "UI": 21886, + "Ġjun": 21887, + "ĠSpearman": 21888, + "Ġfairness": 21889, + "etz": 21890, + "ĠPPI": 21891, + "inance": 21892, + "enko": 21893, + "Ġgalact": 21894, + "öm": 21895, + "Ġexceptions": 21896, + "ĠCretaceous": 21897, + "MY": 21898, + "Resp": 21899, + "Ġpep": 21900, + "ĠOrd": 21901, + "STE": 21902, + "Ġhelicity": 21903, + "Ġofficer": 21904, + "Target": 21905, + "ĠNorwegian": 21906, + "Ġdehydration": 21907, + "ĠSIRT": 21908, + "ĠRobinson": 21909, + "ĠBenchmark": 21910, + "viral": 21911, + "Real": 21912, + "Ġdoxorubicin": 21913, + "Ġestimations": 21914, + "ĠCauc": 21915, + "Ġadditives": 21916, + "modes": 21917, + "ĠHend": 21918, + "Ġaccelerating": 21919, + "ĠGordon": 21920, + "ĠMagnet": 21921, + "Ġgonad": 21922, + "Ġpyrolysis": 21923, + "coholic": 21924, + "ĠPKC": 21925, + "SAR": 21926, + "Ġwinding": 21927, + "terious": 21928, + "ĠMountains": 21929, + "ĠSymbol": 21930, + "ĠMatthe": 21931, + "ĠShin": 21932, + "Script": 21933, + "rug": 21934, + "ĠmW": 21935, + "ĠISM": 21936, + "ĠNg": 21937, + "Ġappoint": 21938, + "ĠAIDS": 21939, + "Ġports": 21940, + "differential": 21941, + "ĠJes": 21942, + "ĠNeed": 21943, + "Ġlenses": 21944, + "ĠAMPK": 21945, + "à¤": 21946, + "leaf": 21947, + "ĠBron": 21948, + "Ġprofit": 21949, + "Local": 21950, + "ĠExamination": 21951, + "ĠChief": 21952, + "Ġopinions": 21953, + "ĠRound": 21954, + "formations": 21955, + "Ġcollinear": 21956, + "Ġdigested": 21957, + "lassical": 21958, + "ervative": 21959, + "Ġcephal": 21960, + "Ġdisadvantages": 21961, + "Ġïĥ·": 21962, + "Ġsubtracting": 21963, + "Ġweigh": 21964, + "Bound": 21965, + "DG": 21966, + "Ġinfluential": 21967, + "Ġtoxins": 21968, + "ĠBenjamin": 21969, + "ĠNumbers": 21970, + "crystal": 21971, + "Ġstocks": 21972, + "ĠBour": 21973, + "ĠCompeting": 21974, + "Ġacqu": 21975, + "tRNA": 21976, + "ĠSeparation": 21977, + "Ġtagged": 21978, + "Ġconject": 21979, + "ĠPrince": 21980, + "Ġgrazing": 21981, + "Ġreleases": 21982, + "ĠChallenge": 21983, + "ATPase": 21984, + "Ġemail": 21985, + "insically": 21986, + "ĠRegulatory": 21987, + "Message": 21988, + "Ġslit": 21989, + "Ġpolygon": 21990, + "Ġdoubling": 21991, + "Ġreceivers": 21992, + "Ġtracked": 21993, + "Ġengineer": 21994, + "stained": 21995, + "ĠDanish": 21996, + "shock": 21997, + "ĠMaz": 21998, + "Ġcough": 21999, + "ĠImmunohist": 22000, + "Consequ": 22001, + "armacy": 22002, + "Ġchemo": 22003, + "ĠMH": 22004, + "Ġemerges": 22005, + "Ġannealed": 22006, + "Ġhypothesize": 22007, + "ĠTypically": 22008, + "ĠBang": 22009, + "ĠPuls": 22010, + "Ġgirl": 22011, + "Ġherbiv": 22012, + "ĠANN": 22013, + "Ġseism": 22014, + "ĠCytok": 22015, + "ĠThroughout": 22016, + "Ġadaptations": 22017, + "lang": 22018, + "Ġclonal": 22019, + "umulation": 22020, + "ĠUniform": 22021, + "Ġhi": 22022, + "opent": 22023, + "Ġbutton": 22024, + "tene": 22025, + "Ġproteasome": 22026, + "bred": 22027, + "ĠNelson": 22028, + "racycline": 22029, + "ĠDY": 22030, + "Ġimmunoblot": 22031, + "prol": 22032, + "Ġpic": 22033, + "Ġcompilation": 22034, + "ĠDevices": 22035, + "etermined": 22036, + "ĠFrancis": 22037, + "notation": 22038, + "writing": 22039, + "terase": 22040, + "ĠStephen": 22041, + "amel": 22042, + "ĠChu": 22043, + "alone": 22044, + "Ġexhaust": 22045, + "relevant": 22046, + "ĠStrat": 22047, + "Ġparametrization": 22048, + "ĠBull": 22049, + "ĠRemote": 22050, + "increasing": 22051, + "Ġdd": 22052, + "â̰": 22053, + "yroidism": 22054, + "ilin": 22055, + "ĠHip": 22056, + "ICA": 22057, + "ĠApoptosis": 22058, + "Ġmachining": 22059, + "LDL": 22060, + "Ġgem": 22061, + "ĠFFT": 22062, + "ĠGuang": 22063, + "Ġoriginates": 22064, + "dat": 22065, + "cone": 22066, + "ĠAdoles": 22067, + "ucci": 22068, + "avoid": 22069, + "ulpt": 22070, + "urium": 22071, + "Ġliteracy": 22072, + "Recent": 22073, + "avg": 22074, + "Ġinvited": 22075, + "ĠPeak": 22076, + "ĠEnterobacter": 22077, + "Ġaneurysm": 22078, + "ĠMorris": 22079, + "tida": 22080, + "ĠSER": 22081, + "ĠMichel": 22082, + "ĠIBD": 22083, + "ĠNG": 22084, + "Ġscarce": 22085, + "web": 22086, + "Ġexpresses": 22087, + "Ġschema": 22088, + "Ġlessons": 22089, + "Ġarginine": 22090, + "Ġphotographs": 22091, + "ĠNeurons": 22092, + "ĠATPase": 22093, + "Ġfiller": 22094, + "rapped": 22095, + "Ġrandomness": 22096, + "Ġveins": 22097, + "Ġwounds": 22098, + "ĠApart": 22099, + "Ġracial": 22100, + "Ġnoteworthy": 22101, + "Ġremoves": 22102, + "Ġganglion": 22103, + "Ġlaminar": 22104, + "ĠSSR": 22105, + "Ġpolysaccharides": 22106, + "Ġbuf": 22107, + "Ġendothelium": 22108, + "ĠCAS": 22109, + "ĠGolgi": 22110, + "Ġinheritance": 22111, + "isite": 22112, + "COMP": 22113, + "Ġpt": 22114, + "Ġmeshes": 22115, + "Ġtherapeutics": 22116, + "OST": 22117, + "olinergic": 22118, + "UG": 22119, + "squared": 22120, + "Ġdegrade": 22121, + "uum": 22122, + "Ġretrosp": 22123, + "Loc": 22124, + "ĠJNK": 22125, + "Options": 22126, + "Ġinsulating": 22127, + "Ġspecifies": 22128, + "Ġoven": 22129, + "yy": 22130, + "ĠConver": 22131, + "Ġdisappeared": 22132, + "ĠPrognostic": 22133, + "ĠNguyen": 22134, + "Ġperiphery": 22135, + "bank": 22136, + "Ġimid": 22137, + "Ġassigning": 22138, + "ĠMess": 22139, + "propan": 22140, + "ioma": 22141, + "olyb": 22142, + "Ġepitope": 22143, + "Ġemitting": 22144, + "DIR": 22145, + "ync": 22146, + "Ġimpairments": 22147, + "ĠMicroscopy": 22148, + "ĠFWHM": 22149, + "gray": 22150, + "Ġfing": 22151, + "ucial": 22152, + "plemented": 22153, + "eas": 22154, + "estamp": 22155, + "Ġcrest": 22156, + "ĠMos": 22157, + "Ġneutrons": 22158, + "Ġbroth": 22159, + "Ġheadache": 22160, + "ongevity": 22161, + "Ġreass": 22162, + "ĠPSF": 22163, + "ĠBuch": 22164, + "visor": 22165, + "Ġdenoting": 22166, + "integer": 22167, + "ouin": 22168, + "efficacy": 22169, + "Ġglutamine": 22170, + "Ġpicked": 22171, + "ĠCampbell": 22172, + "ĠKernel": 22173, + "Ġships": 22174, + "lt": 22175, + "ondyl": 22176, + "Ġcredi": 22177, + "Ġpeptid": 22178, + "ĠEstabl": 22179, + "bons": 22180, + "Ġaggl": 22181, + "USE": 22182, + "supp": 22183, + "upsilon": 22184, + "characterized": 22185, + "isheries": 22186, + "May": 22187, + "ARC": 22188, + "Ġroads": 22189, + "Ġdeparture": 22190, + "ĠMAX": 22191, + "ĠTRA": 22192, + "imod": 22193, + "ĠAlber": 22194, + "Ġterminated": 22195, + "ölder": 22196, + "Scalar": 22197, + "hash": 22198, + "ĠMSS": 22199, + "Ġsmoothness": 22200, + "Ġresemble": 22201, + "ĠEffectiveness": 22202, + "rx": 22203, + "ĠEye": 22204, + "Ġfaecal": 22205, + "þ": 22206, + "ĠClostridium": 22207, + "achine": 22208, + "ĠBDNF": 22209, + "Ġcab": 22210, + "ĠWong": 22211, + "ĠDouglas": 22212, + "Ġreperfusion": 22213, + "ĠXi": 22214, + "Ġconfused": 22215, + "ĠPhiladelphia": 22216, + "Ġapple": 22217, + "Ġile": 22218, + "Ġfavored": 22219, + "Ġplaques": 22220, + "Ġtrivially": 22221, + "ĠTypical": 22222, + "Ġcentralized": 22223, + "ĠFacebook": 22224, + "Ġnortheast": 22225, + "Ġnormality": 22226, + "ĠTb": 22227, + "Ġapt": 22228, + "Ġfacet": 22229, + "ĠRenal": 22230, + "clk": 22231, + "Ġligation": 22232, + "ifferenti": 22233, + "Ġputting": 22234, + "Ġintrig": 22235, + "walled": 22236, + "Et": 22237, + "ĠCow": 22238, + "ĠNations": 22239, + "Ġcampus": 22240, + "ĠKinetics": 22241, + "ĠMexican": 22242, + "ERK": 22243, + "Ġlatitudes": 22244, + "ĠRoll": 22245, + "ĠQD": 22246, + "adaptive": 22247, + "Ġquenched": 22248, + "Ġfram": 22249, + "Qi": 22250, + "Ġtongue": 22251, + "edes": 22252, + "Ġascorb": 22253, + "ĠGlucose": 22254, + "ouri": 22255, + "Ġdefeated": 22256, + "ophilus": 22257, + "ralateral": 22258, + "xrightarrow": 22259, + "ĠJup": 22260, + "axes": 22261, + "eger": 22262, + "MIT": 22263, + "ĠMember": 22264, + "ĠNu": 22265, + "Ġtransloc": 22266, + "ĠFlux": 22267, + "ĠColorado": 22268, + "Ġrelying": 22269, + "atrol": 22270, + "Ġcontrasts": 22271, + "centage": 22272, + "Ġleukocyte": 22273, + "Ġcoincidence": 22274, + "Ġcontractions": 22275, + "oga": 22276, + "ANN": 22277, + "ĠAbsorption": 22278, + "Return": 22279, + "reprene": 22280, + "baum": 22281, + "traumatic": 22282, + "incial": 22283, + "Ġautophag": 22284, + "Ġalgorithmic": 22285, + "rimp": 22286, + "Ġdivides": 22287, + "ĠRose": 22288, + "ĠEric": 22289, + "Ġaddiction": 22290, + "plification": 22291, + "Ġdiffusive": 22292, + "ĠVehicle": 22293, + "enerate": 22294, + "tising": 22295, + "Ġstarvation": 22296, + "absorption": 22297, + "ĠAra": 22298, + "Ġgrav": 22299, + "ĠSubunit": 22300, + "Ġamide": 22301, + "Ġenhancer": 22302, + "Ġmerid": 22303, + "ermost": 22304, + "Ġalgal": 22305, + "ĠQueens": 22306, + "ĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ": 22307, + "Ġjudge": 22308, + "ĠGreenland": 22309, + "brace": 22310, + "ĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ": 22311, + "Ġhypergly": 22312, + "Ġemergent": 22313, + "Fisher": 22314, + "ĠLas": 22315, + "Ġsexes": 22316, + "Sep": 22317, + "Ġphrases": 22318, + "ĠSequential": 22319, + "inki": 22320, + "Ġaxioms": 22321, + "study": 22322, + "Ġtiny": 22323, + "Ġcd": 22324, + "catalyzed": 22325, + "asaki": 22326, + "ĠWR": 22327, + "ĠMinimal": 22328, + "Ġsubcellular": 22329, + "Ġphospho": 22330, + "ESI": 22331, + "Ġvow": 22332, + "Ġsupplies": 22333, + "operand": 22334, + "Fix": 22335, + "anian": 22336, + "writer": 22337, + "âζ": 22338, + "Ġwinner": 22339, + "ĠPID": 22340, + "ĠLebesgue": 22341, + "Ġsimplification": 22342, + "ĠRelationships": 22343, + "Ġautomata": 22344, + "ĠContribution": 22345, + "Ġhereditary": 22346, + "errin": 22347, + "ĠBLAST": 22348, + "aea": 22349, + "yleth": 22350, + "ĠTc": 22351, + "adeh": 22352, + "adjuvant": 22353, + "Wave": 22354, + "counter": 22355, + "ĠGupta": 22356, + "ĠGhana": 22357, + "Cho": 22358, + "Ġourselves": 22359, + "Ġevenly": 22360, + "lymph": 22361, + "Ġcerebellum": 22362, + "Ġcopolymers": 22363, + "modular": 22364, + "Ġharder": 22365, + "Ġplease": 22366, + "ĠPSD": 22367, + "Ġlimbs": 22368, + "Ġexploitation": 22369, + "iry": 22370, + "Ġperiodontal": 22371, + "ATCH": 22372, + "Ġmalicious": 22373, + "ĠSlov": 22374, + "HY": 22375, + "Consequently": 22376, + "oren": 22377, + "ĠPare": 22378, + "agine": 22379, + "ĠROI": 22380, + "ĠWhich": 22381, + "ĠNative": 22382, + "amen": 22383, + "reshape": 22384, + "oplankton": 22385, + "Ġartifact": 22386, + "Ġrhin": 22387, + "gpu": 22388, + "Ġundet": 22389, + "Ġsporadic": 22390, + "Ġorally": 22391, + "Ġstepwise": 22392, + "ĠCohort": 22393, + "Ġrhod": 22394, + "cyt": 22395, + "Ġierr": 22396, + "Ġmotors": 22397, + "ĠIgE": 22398, + "calculated": 22399, + "ĠChampionships": 22400, + "pel": 22401, + "ĠFerr": 22402, + "Ġisometric": 22403, + "nutrition": 22404, + "Ġunsaturated": 22405, + "Ġdoll": 22406, + "ĠRMSE": 22407, + "Ġsolitary": 22408, + "approximation": 22409, + "Ġreperto": 22410, + "sight": 22411, + "Ġcranial": 22412, + "ilical": 22413, + "ĠOst": 22414, + "oul": 22415, + "Ġdg": 22416, + "ĠProceed": 22417, + "Ġmilling": 22418, + "sz": 22419, + "Ġmineralization": 22420, + "Ġcigarette": 22421, + "Ġporph": 22422, + "Ġspons": 22423, + "ĠGreece": 22424, + "ipore": 22425, + "accept": 22426, + "ĠPTSD": 22427, + "Å«": 22428, + "Ġcipher": 22429, + "Ġfunctionalized": 22430, + "Poly": 22431, + "Ġabd": 22432, + "flight": 22433, + "ĠSydney": 22434, + "Ġdisaster": 22435, + "ĠHaving": 22436, + "Ġdiesel": 22437, + "ĠGreg": 22438, + "Ġspans": 22439, + "ĠSeasonal": 22440, + "STEM": 22441, + "ierr": 22442, + "ĠIB": 22443, + "Ġlemm": 22444, + "anum": 22445, + "ĠBottom": 22446, + "Ġseal": 22447, + "boost": 22448, + "Ġlegend": 22449, + "bing": 22450, + "abis": 22451, + "Ġchitin": 22452, + "Ġmaximally": 22453, + "Ġimmunosuppressive": 22454, + "âĪĴâĪĴ": 22455, + "Ġabolished": 22456, + "ige": 22457, + "Ġesophag": 22458, + "Ġlasted": 22459, + "Ġcarbohydrates": 22460, + "Ġchips": 22461, + "ĠFernand": 22462, + "far": 22463, + "ĠPoints": 22464, + "calation": 22465, + "ĠRegions": 22466, + "CHK": 22467, + "veratrol": 22468, + "truth": 22469, + "Ġstrange": 22470, + "Interest": 22471, + "sho": 22472, + "ĠInduc": 22473, + "Ġmigraine": 22474, + "ĠVac": 22475, + "ophores": 22476, + "Ġerrone": 22477, + "scriptsize": 22478, + "ĠNeutron": 22479, + "Ġindistinguishable": 22480, + "istine": 22481, + "Ġhelper": 22482, + "specified": 22483, + "Ġjuice": 22484, + "oxal": 22485, + "ĠJung": 22486, + "Ġmagazine": 22487, + "Ġtelephone": 22488, + "ĠPhyt": 22489, + "Ġum": 22490, + "ĠAvailability": 22491, + "ĠTropical": 22492, + "ĠCases": 22493, + "Ġdescend": 22494, + "Har": 22495, + "âĪĹ": 22496, + "ĠâĨĵ": 22497, + "Ks": 22498, + "Ġê": 22499, + "oluble": 22500, + "Ġchampionship": 22501, + "ĠMovement": 22502, + "ĠXY": 22503, + "kappaB": 22504, + "years": 22505, + "memb": 22506, + "quine": 22507, + "Ġletting": 22508, + "Ġbiggest": 22509, + "Ġcards": 22510, + "Ġbiotin": 22511, + "ĠAur": 22512, + "modal": 22513, + "Ġvaccinated": 22514, + "Ġtranslates": 22515, + "ĠPAC": 22516, + "lli": 22517, + "reonine": 22518, + "Ġcurcumin": 22519, + "ĠConstruct": 22520, + "Ġconvinc": 22521, + "ĠNat": 22522, + "Ġamygdala": 22523, + "Ġprotr": 22524, + "ĠSingular": 22525, + "ĠContact": 22526, + "kind": 22527, + "ĠDaw": 22528, + "ogroup": 22529, + "ĠKCl": 22530, + "Ġhygi": 22531, + "erenced": 22532, + "Ġsurveyed": 22533, + "ĠMull": 22534, + "esthetic": 22535, + "Ġmsg": 22536, + "ĠRequire": 22537, + "Ġdistortions": 22538, + "Control": 22539, + "BERT": 22540, + "Ġautonomic": 22541, + "Ġhormonal": 22542, + "Ġstrips": 22543, + "Ġtrophic": 22544, + "ifting": 22545, + "opod": 22546, + "ĠSpontaneous": 22547, + "Ġlogs": 22548, + "OPT": 22549, + "ĠMot": 22550, + "ĠGmb": 22551, + "aharan": 22552, + "ĠPOL": 22553, + "Ġvisceral": 22554, + "blocks": 22555, + "Ġsitting": 22556, + "Ġsine": 22557, + "Ġoncogenic": 22558, + "ERRQ": 22559, + "quinone": 22560, + "Ġsmartphone": 22561, + "ĠTanz": 22562, + "lactam": 22563, + "ĠSignificance": 22564, + "Ġeu": 22565, + "ĠISS": 22566, + "ĠTrig": 22567, + "ĠMaj": 22568, + "tingale": 22569, + "Ġdilat": 22570, + "ennes": 22571, + "ĠBelgium": 22572, + "lev": 22573, + "ĠContr": 22574, + "ĠGalois": 22575, + "ĠCombination": 22576, + "ĠThi": 22577, + "ĠAustria": 22578, + "Prom": 22579, + "Ġelicit": 22580, + "biosis": 22581, + "Ġlymphatic": 22582, + "ĠMurray": 22583, + "ĠXPS": 22584, + "Ġcong": 22585, + "screen": 22586, + "tide": 22587, + "amoyl": 22588, + "ĠMcD": 22589, + "Ġretired": 22590, + "mixed": 22591, + "ELD": 22592, + "ĠMaps": 22593, + "ĠVE": 22594, + "cession": 22595, + "numer": 22596, + "idated": 22597, + "ĠBishop": 22598, + "Ġneonates": 22599, + "Ġlandsl": 22600, + "ĠFractional": 22601, + "Ġspecifying": 22602, + "ĠJr": 22603, + "Ġnanowire": 22604, + "Ġconsultation": 22605, + "language": 22606, + "Ġpricing": 22607, + "ĠLimitations": 22608, + "ĠPediatric": 22609, + "ĠDimension": 22610, + "Ġpreparing": 22611, + "Lag": 22612, + "segment": 22613, + "Ġspend": 22614, + "athe": 22615, + "Ġweap": 22616, + "ĠJos": 22617, + "textit": 22618, + "outputs": 22619, + "ordering": 22620, + "Ġplacenta": 22621, + "ationally": 22622, + "ĠKun": 22623, + "Ġoutstanding": 22624, + "Ġthicknesses": 22625, + "ĠChIP": 22626, + "deoxy": 22627, + "ĠZo": 22628, + "ĠDeveloping": 22629, + "Ġstringent": 22630, + "iency": 22631, + "perse": 22632, + "Ġpend": 22633, + "ĠDevelopmental": 22634, + "Ġextern": 22635, + "Ġinverter": 22636, + "ĠDAPI": 22637, + "lectivity": 22638, + "Ġtablets": 22639, + "Ġprogester": 22640, + "ĠïģŃ": 22641, + "Ġanswered": 22642, + "entary": 22643, + "ORS": 22644, + "Ġdir": 22645, + "Ġdeleterious": 22646, + "Ġdopaminergic": 22647, + "Random": 22648, + "diss": 22649, + "Ġmonolayers": 22650, + "Ġintegrand": 22651, + "ĠComponents": 22652, + "ĠPerc": 22653, + "agit": 22654, + "ARN": 22655, + "esophageal": 22656, + "ivan": 22657, + "neider": 22658, + "ĠStarting": 22659, + "PORT": 22660, + "yellow": 22661, + "Ġregisters": 22662, + "pairs": 22663, + "Ġethnicity": 22664, + "Ġboy": 22665, + "auti": 22666, + "Ġchromium": 22667, + "POS": 22668, + "vature": 22669, + "ayashi": 22670, + "Ġinappropriate": 22671, + "ĠSNA": 22672, + "Domain": 22673, + "ĠPrice": 22674, + "Ġmacular": 22675, + "Ġoverload": 22676, + "ĠUnified": 22677, + "Ġattach": 22678, + "ĠScottish": 22679, + "maps": 22680, + "agl": 22681, + "emi": 22682, + "Ġseam": 22683, + "ĠAnalog": 22684, + "dated": 22685, + "uo": 22686, + "Ġplated": 22687, + "Ġasset": 22688, + "Ġscreens": 22689, + "Ġspurious": 22690, + "Besides": 22691, + "Ġbaselines": 22692, + "heads": 22693, + "Ġcoat": 22694, + "ĠRemoval": 22695, + "Ġinfinitesimal": 22696, + "ĠTransformation": 22697, + "Ġcommens": 22698, + "Float": 22699, + "AUC": 22700, + "ĠLay": 22701, + "Ġintron": 22702, + "ĠDetecting": 22703, + "ĠHerein": 22704, + "ĠAssociations": 22705, + "Ġprogesterone": 22706, + "Bacteria": 22707, + "Ġsentiment": 22708, + "ĠPhenomen": 22709, + "matter": 22710, + "Ġcylinders": 22711, + "Ġtoluene": 22712, + "Ġspatiotemporal": 22713, + "Ġlanding": 22714, + "ĠCoronavirus": 22715, + "ĠBerry": 22716, + "ĠBragg": 22717, + "Ġregistry": 22718, + "Ġenthalpy": 22719, + "tica": 22720, + "razine": 22721, + "Ġcargo": 22722, + "otation": 22723, + "Ġcontradicts": 22724, + "Ġpesticides": 22725, + "ĠFischer": 22726, + "Ġmechanically": 22727, + "ĠInterfer": 22728, + "ĠCyp": 22729, + "ĠKas": 22730, + "Ġmetres": 22731, + "Ġantiretroviral": 22732, + "Ġtravers": 22733, + "selection": 22734, + "ĠWA": 22735, + "Ġdoublet": 22736, + "meta": 22737, + "ENTR": 22738, + "sonic": 22739, + "Ġmarking": 22740, + "ĠOverex": 22741, + "Ġpyruvate": 22742, + "Ġextrusion": 22743, + "Ġingestion": 22744, + "Ġcocaine": 22745, + "ĠFellow": 22746, + "CNTs": 22747, + "BG": 22748, + "ĠMorphological": 22749, + "Ġdefence": 22750, + "ĠYosh": 22751, + "mitter": 22752, + "rystallization": 22753, + "STRACT": 22754, + "Ġinflammasome": 22755, + "ĠGd": 22756, + "Ġshaft": 22757, + "Ġeruption": 22758, + "ĠOxide": 22759, + "ifolds": 22760, + "ĠGam": 22761, + "ĠGap": 22762, + "command": 22763, + "ĠIgA": 22764, + "Ġshortening": 22765, + "assembled": 22766, + "isopropyl": 22767, + "Ġalumina": 22768, + "ĠATM": 22769, + "Ġct": 22770, + "Ġspinning": 22771, + "ĠPetsc": 22772, + "prefix": 22773, + "Ġperpetuity": 22774, + "PRE": 22775, + "Ġfruct": 22776, + "GHz": 22777, + "elike": 22778, + "enyl": 22779, + "Ġwherein": 22780, + "UK": 22781, + "visual": 22782, + "lipidemia": 22783, + "reduction": 22784, + "anin": 22785, + "olas": 22786, + "Ġamplic": 22787, + "ĠSAT": 22788, + "Ġmodulator": 22789, + "forth": 22790, + "rl": 22791, + "Ġcrew": 22792, + "ĠiP": 22793, + "Ġxi": 22794, + "ADD": 22795, + "ĠAlexand": 22796, + "constrained": 22797, + "ratory": 22798, + "ĠkW": 22799, + "ĠMDR": 22800, + "ĠlncRNA": 22801, + "Mill": 22802, + "ĠMgO": 22803, + "circuit": 22804, + "Ġpersonalized": 22805, + "ĠOperator": 22806, + "stock": 22807, + "ĠPSA": 22808, + "ensable": 22809, + "Ġlean": 22810, + "yield": 22811, + "Ġopacity": 22812, + "ĠCommons": 22813, + "Ġsummed": 22814, + "ucker": 22815, + "ecke": 22816, + "epithelial": 22817, + "Ġasking": 22818, + "uese": 22819, + "ĠFlav": 22820, + "Ġlactic": 22821, + "Ġlubric": 22822, + "Ġisn": 22823, + "regions": 22824, + "support": 22825, + "Below": 22826, + "ĠNom": 22827, + "Ġhyal": 22828, + "ikh": 22829, + "ban": 22830, + "ĠBG": 22831, + "rometer": 22832, + "indic": 22833, + "opharyngeal": 22834, + "ITION": 22835, + "ĠPropagation": 22836, + "ĠPlace": 22837, + "ĠCircuit": 22838, + "ĠCOL": 22839, + "Green": 22840, + "Ir": 22841, + "lav": 22842, + "ĠdS": 22843, + "ĠMoment": 22844, + "Ġinducible": 22845, + "Ġdischarges": 22846, + "habdi": 22847, + "ĠExperience": 22848, + "Ġsg": 22849, + "Ġoutward": 22850, + "Ġportable": 22851, + "ĠOperators": 22852, + "Av": 22853, + "ĠDQ": 22854, + "ostatin": 22855, + "Ġeosinophil": 22856, + "Ġstriatum": 22857, + "ĠConsensus": 22858, + "Ġimperfect": 22859, + "NOT": 22860, + "ĠDemocratic": 22861, + ";;": 22862, + "Body": 22863, + "dii": 22864, + "Ho": 22865, + "ĠRailway": 22866, + "ĠUganda": 22867, + "Ġunpaired": 22868, + "friendly": 22869, + "Ġreprogramming": 22870, + "Alternative": 22871, + "RG": 22872, + "imet": 22873, + "enez": 22874, + "ĠHypothesis": 22875, + "Ġton": 22876, + "ĠCombin": 22877, + "ĠDelivery": 22878, + "Last": 22879, + "Ġowners": 22880, + "razole": 22881, + "ĠKob": 22882, + "Ġformats": 22883, + "Ġpolyclonal": 22884, + "Ġidentifier": 22885, + "ILL": 22886, + "Ġsurgeon": 22887, + "Ġpostp": 22888, + "ĠGenerative": 22889, + "ĠMall": 22890, + "abc": 22891, + "ĠHaz": 22892, + "Ġsmoothly": 22893, + "Ġcrystallographic": 22894, + "ĠFDA": 22895, + "Ġcoexistence": 22896, + "ionized": 22897, + "Ġcompiler": 22898, + "ĠArter": 22899, + "Ġappearances": 22900, + "amiltonian": 22901, + "Ġencapsulated": 22902, + "atia": 22903, + "wi": 22904, + "reb": 22905, + "Ġwafer": 22906, + "ubs": 22907, + "ĠUE": 22908, + "ĠGSK": 22909, + "Ġviv": 22910, + "Ġflooding": 22911, + "ĠGyr": 22912, + "Ġstably": 22913, + "Ġdislocations": 22914, + "Ġescap": 22915, + "ĠPhysiological": 22916, + "tidal": 22917, + "yme": 22918, + "ĠMaxim": 22919, + "iterator": 22920, + "ordant": 22921, + "Ġattentional": 22922, + "Ġcatalyzed": 22923, + "ĠTryp": 22924, + "PIN": 22925, + "ĠCorrelations": 22926, + "Ġhydrological": 22927, + "Ġnose": 22928, + "export": 22929, + "Ġdext": 22930, + "ĠBenef": 22931, + "ĠBiosystems": 22932, + "ĠPars": 22933, + "Ġreadings": 22934, + "Ġinstrumentation": 22935, + "ĠIQ": 22936, + "RIC": 22937, + "Ġgrafts": 22938, + "overs": 22939, + "ĠMedic": 22940, + "Ġmonod": 22941, + "Ġuniformity": 22942, + "ĠATLAS": 22943, + "Ġmasked": 22944, + "Ri": 22945, + "ĠPhysic": 22946, + "Ġimposing": 22947, + "ĠParad": 22948, + "imetic": 22949, + "Ġdemanding": 22950, + "unks": 22951, + "Ġfolds": 22952, + "ĠAnc": 22953, + "Ġvolatility": 22954, + "Ġbringing": 22955, + "acil": 22956, + "ĠNMDA": 22957, + "reduced": 22958, + "tii": 22959, + "Ġnorthwest": 22960, + "ĠBessel": 22961, + "ventions": 22962, + "Ġconsolidation": 22963, + "Meier": 22964, + "Ġmicrof": 22965, + "Ġqualified": 22966, + "Ġinsignificant": 22967, + "ĠMorphology": 22968, + "Ġpointwise": 22969, + "Ġlearns": 22970, + "Ġguard": 22971, + "CHECK": 22972, + "phonon": 22973, + "ĠEnhancement": 22974, + "Ġzonal": 22975, + "ERG": 22976, + "Start": 22977, + "Ġhistoric": 22978, + "ĠPure": 22979, + "ĠGmbH": 22980, + "glu": 22981, + "Ġpatterning": 22982, + "Ġstick": 22983, + "uminosity": 22984, + "Dataset": 22985, + "Ġoverride": 22986, + "ĠSteel": 22987, + "Ġfuels": 22988, + "mechanical": 22989, + "Ġautologous": 22990, + "Ġdepartments": 22991, + "ĠBlo": 22992, + "Ġimported": 22993, + "Ġrestrictive": 22994, + "eigen": 22995, + "ĠRome": 22996, + "ĠÌĬ": 22997, + "Ġepitopes": 22998, + "Ġlabelling": 22999, + "Ġownership": 23000, + "ĠEspecially": 23001, + "Ġcoffee": 23002, + "ĠGRB": 23003, + "Head": 23004, + "ĠVent": 23005, + "esare": 23006, + "ĠParticles": 23007, + "UNCTION": 23008, + "jj": 23009, + "uents": 23010, + "elic": 23011, + "ĠTat": 23012, + "ĠFle": 23013, + "Ġgating": 23014, + "Ġrefuge": 23015, + "Additionally": 23016, + "Ġrhs": 23017, + "Ġmaybe": 23018, + "ĠFang": 23019, + "Ġadvent": 23020, + "otransferase": 23021, + "should": 23022, + "Ġproteomic": 23023, + "Ġlegitim": 23024, + "PERIM": 23025, + "ĠGiant": 23026, + "Ġgraphics": 23027, + "onomical": 23028, + "scatter": 23029, + "Ġsuggestive": 23030, + "plots": 23031, + "Ġmultidrug": 23032, + "Ġabsorber": 23033, + "XS": 23034, + "consuming": 23035, + "Ġsustainability": 23036, + "opre": 23037, + "fix": 23038, + "Ġvolcano": 23039, + "ĠTypes": 23040, + "ĠCreate": 23041, + "Ġchooses": 23042, + "Ġstirring": 23043, + "Ġsurgeons": 23044, + "dS": 23045, + "Ġcharacterizes": 23046, + "Ġadjustments": 23047, + "texttt": 23048, + "etra": 23049, + "Ġclassifications": 23050, + "spots": 23051, + "Ġâϝ": 23052, + "erex": 23053, + "dehyd": 23054, + "ĠBrig": 23055, + "ĠSuperconduc": 23056, + "Ġgrants": 23057, + "ĠCen": 23058, + "ĠYin": 23059, + "ĠReactions": 23060, + "description": 23061, + "transcription": 23062, + "important": 23063, + "Ġhemodynamic": 23064, + "ĠYi": 23065, + "ĠGolden": 23066, + "kk": 23067, + "alb": 23068, + "Ġrooms": 23069, + "Ġsegreg": 23070, + "Ġsumming": 23071, + "Ġsuccession": 23072, + "Ġfollicular": 23073, + "Ġtackle": 23074, + "Down": 23075, + "Ġevaluates": 23076, + "atica": 23077, + "annual": 23078, + "ĠAlbert": 23079, + "Ġtal": 23080, + "orbital": 23081, + "fted": 23082, + "variables": 23083, + "Ġwetland": 23084, + "outheastern": 23085, + "MEM": 23086, + "ĠBrill": 23087, + "ĠSodium": 23088, + "ĠAlexa": 23089, + "umed": 23090, + "BUG": 23091, + "arine": 23092, + "Ġrevenue": 23093, + "habditis": 23094, + "Ġdissol": 23095, + "amplitude": 23096, + "Ġartists": 23097, + "Ġnormalised": 23098, + "Ġfluctuating": 23099, + "Ġaspar": 23100, + "ĠFi": 23101, + "olates": 23102, + "ispanic": 23103, + "Ġacetylation": 23104, + "ĠConcentration": 23105, + "Ġthro": 23106, + "shots": 23107, + "Ġnarrative": 23108, + "ĠWaals": 23109, + "ammonium": 23110, + "ureau": 23111, + "------------": 23112, + "Ġresearches": 23113, + "Ġbaby": 23114, + "Ġsharply": 23115, + "ÙĦ": 23116, + "ĠCel": 23117, + "CX": 23118, + "uminal": 23119, + "Ġgermline": 23120, + "ĠTransformer": 23121, + "pseud": 23122, + "HG": 23123, + "Ka": 23124, + "ĠSMC": 23125, + "ĠNutrition": 23126, + "Ġbarc": 23127, + "ĠWrite": 23128, + "Ġproteases": 23129, + "Ġsweep": 23130, + "ĠKolmogorov": 23131, + "morph": 23132, + "inducible": 23133, + "Ġexciting": 23134, + "lein": 23135, + "ĠHass": 23136, + "Ġproductive": 23137, + "mesh": 23138, + "ĠCMS": 23139, + "Ġheavier": 23140, + "Ġmeetings": 23141, + "ĠCopper": 23142, + "Ġvirtue": 23143, + "asant": 23144, + "ĠDEN": 23145, + "Ġinherently": 23146, + "rio": 23147, + "Ġhoused": 23148, + "Ġintraoperative": 23149, + "Ġcrown": 23150, + "conditions": 23151, + "ANG": 23152, + "YSIS": 23153, + "iman": 23154, + "Ġnmol": 23155, + "ĠRetrieval": 23156, + "algae": 23157, + "Ġkappa": 23158, + "deep": 23159, + "inence": 23160, + "ĠCarcinoma": 23161, + "Ġchromatographic": 23162, + "Ġascribed": 23163, + "Ġleverage": 23164, + "ĠKK": 23165, + "omyel": 23166, + "pet": 23167, + "ĠNJ": 23168, + "comm": 23169, + "Ġannually": 23170, + "gran": 23171, + "Ġaval": 23172, + "ĠNish": 23173, + "Ġevac": 23174, + "Ġmultif": 23175, + "Ġfunds": 23176, + "enny": 23177, + "ĠMong": 23178, + "ĠException": 23179, + "paths": 23180, + "ymen": 23181, + "hpp": 23182, + "Ġrestricting": 23183, + "saturated": 23184, + "âĻ": 23185, + "Ġlearners": 23186, + "ĠLanka": 23187, + "inities": 23188, + "ĠGDP": 23189, + "Ġspeciation": 23190, + "Ġensured": 23191, + "Ġneutralizing": 23192, + "Ġballoon": 23193, + "Comparison": 23194, + "ĠCalibration": 23195, + "ĠInfluenza": 23196, + "Ġvapour": 23197, + "XA": 23198, + "tracking": 23199, + "ĠICD": 23200, + "fluoro": 23201, + "ĠDamage": 23202, + "Ġpra": 23203, + "Ġconceived": 23204, + "ĠCosmological": 23205, + "Ġloose": 23206, + "inositol": 23207, + "ĠClifford": 23208, + "owa": 23209, + "Ġoffsets": 23210, + "document": 23211, + "Ġenormous": 23212, + "Ġphotoelectron": 23213, + "record": 23214, + "esticular": 23215, + "Ġvocals": 23216, + "Ġconsciousness": 23217, + "Ġtrem": 23218, + "Ġlandscapes": 23219, + "ĠFundamental": 23220, + "tebrate": 23221, + "Ġvertebral": 23222, + "Ġregenerative": 23223, + "Ġtroposp": 23224, + "Integr": 23225, + "Ġassociates": 23226, + "oved": 23227, + "ussed": 23228, + "aws": 23229, + "ĠSide": 23230, + "Ġinterconnected": 23231, + "Ġsuperfamily": 23232, + "ĠCook": 23233, + "loader": 23234, + "Ġpython": 23235, + "ĠCounter": 23236, + "books": 23237, + "Ġïģ²": 23238, + "breaking": 23239, + "gy": 23240, + "Ġcarbox": 23241, + "Ġedited": 23242, + "otyped": 23243, + "Ġduoden": 23244, + "anne": 23245, + "Ġanastom": 23246, + "ginate": 23247, + "ĠBiosciences": 23248, + "rage": 23249, + "ĠChiral": 23250, + "Ġsimplifies": 23251, + "Ġtestis": 23252, + "ström": 23253, + "ials": 23254, + "Ġmicelles": 23255, + "correct": 23256, + "ĠGenetics": 23257, + "along": 23258, + "Rem": 23259, + "resistance": 23260, + "Ġdrink": 23261, + "orbed": 23262, + "ĠTreat": 23263, + "ĠSho": 23264, + "shows": 23265, + "ér": 23266, + "Ġmimics": 23267, + "occup": 23268, + "eclam": 23269, + "ONG": 23270, + "Ġmarketing": 23271, + "ĠFinding": 23272, + "Ġendometri": 23273, + "âĶĢ": 23274, + "strained": 23275, + "ĠMuch": 23276, + "Ġexons": 23277, + "ĠHil": 23278, + "TD": 23279, + "ĠWW": 23280, + "ĠVic": 23281, + "enda": 23282, + "Ġfactory": 23283, + "ĠHepG": 23284, + "ĠStatic": 23285, + "blastoma": 23286, + "wd": 23287, + "raisal": 23288, + "ĠBasis": 23289, + "Ins": 23290, + "ĠUnsupervised": 23291, + "elo": 23292, + "oselective": 23293, + "Ġaccomplish": 23294, + "ĠProspective": 23295, + "Ġuncorrelated": 23296, + "ĠGate": 23297, + "icycl": 23298, + "Ġurgent": 23299, + "ĠPathways": 23300, + "Ġoblique": 23301, + "ĠIndividuals": 23302, + "Ġinitiative": 23303, + "Ġcatast": 23304, + "jections": 23305, + "Ġautosomal": 23306, + "ĠPhilip": 23307, + "Ġcomprehension": 23308, + "mM": 23309, + "pain": 23310, + "ĠmicroM": 23311, + "Ġencounters": 23312, + "goto": 23313, + "Ġladder": 23314, + "Ġoccupy": 23315, + "ĠSurfaces": 23316, + "Doc": 23317, + "ugby": 23318, + "Ġexamines": 23319, + "osynthesis": 23320, + "ĠKEGG": 23321, + "glass": 23322, + "slice": 23323, + "propagation": 23324, + "stry": 23325, + "Ġillustrating": 23326, + "imi": 23327, + "Ġspores": 23328, + "Ġastrophysical": 23329, + "Ġenclosed": 23330, + "Ġinferences": 23331, + "Ġbijection": 23332, + "Ġeveryday": 23333, + "Ġalternatively": 23334, + "reaction": 23335, + "iants": 23336, + "contact": 23337, + "Ġging": 23338, + "ĠBias": 23339, + "Ġautomaton": 23340, + "background": 23341, + "Ġneighbouring": 23342, + "Ġdetects": 23343, + "porate": 23344, + "ĠSharma": 23345, + "Hydro": 23346, + "Ġsacc": 23347, + "ĠFiber": 23348, + "ĠChlam": 23349, + "Ġbuffers": 23350, + "Applying": 23351, + "lceil": 23352, + "emph": 23353, + "ĠGSE": 23354, + "metry": 23355, + "Ġimmunost": 23356, + "ĠHistorical": 23357, + "ĠDrag": 23358, + "Ġtransplanted": 23359, + "Ġfrail": 23360, + "Ġanthocyan": 23361, + "inte": 23362, + "ĠBhat": 23363, + "ĠOg": 23364, + "Ġsteering": 23365, + "benzene": 23366, + "****************************************************************": 23367, + "Ġsynthet": 23368, + "Act": 23369, + "Ġcin": 23370, + "Ġherbal": 23371, + "Ġdyn": 23372, + "Ġhyperplasia": 23373, + "header": 23374, + "Ġcalculates": 23375, + "ĠDifference": 23376, + "Ġbats": 23377, + "ductivity": 23378, + "Ġconformations": 23379, + "city": 23380, + "Ġseparates": 23381, + "ĠCDC": 23382, + "ĠPrism": 23383, + "ĠBehaviour": 23384, + "ĠKelly": 23385, + "ĠSey": 23386, + "ĠÃł": 23387, + "LEX": 23388, + "gkin": 23389, + "strom": 23390, + "Ġvom": 23391, + "ĠWin": 23392, + "ĠWigner": 23393, + "Ġcontralateral": 23394, + "ĠMinor": 23395, + "Ġstereo": 23396, + "ĠApproximately": 23397, + "LED": 23398, + "say": 23399, + "ĠJS": 23400, + "Ġalcohols": 23401, + "Ġsan": 23402, + "Ġhardening": 23403, + "IFN": 23404, + "Ġretrospectively": 23405, + "Ġgeneralised": 23406, + "Ġtibial": 23407, + "ĠWeek": 23408, + "Ġaryl": 23409, + "ĠPeninsula": 23410, + "Ġdeterminations": 23411, + "Ġphotovoltaic": 23412, + "Ġsuggestion": 23413, + "Jac": 23414, + "ĠVitro": 23415, + "Ġcyclo": 23416, + "Ġfibrous": 23417, + "disambiguation": 23418, + "program": 23419, + "Ġguest": 23420, + "ĠDust": 23421, + "rceil": 23422, + "Ġpowered": 23423, + "Ġcardiomyocytes": 23424, + "heat": 23425, + "ylic": 23426, + "Ġpresentations": 23427, + "Ġtransmitting": 23428, + "WD": 23429, + "added": 23430, + "Initial": 23431, + "Del": 23432, + "ĠVelocity": 23433, + "Ġmole": 23434, + "Ġoval": 23435, + "Ġplankton": 23436, + "their": 23437, + "ĠQED": 23438, + "volutions": 23439, + "Ġmandatory": 23440, + "Ġrepulsive": 23441, + "ĉĠĠ": 23442, + "Ġpostulated": 23443, + "ĠCortex": 23444, + "ĠCarb": 23445, + "CHKERRQ": 23446, + "Ġoverlay": 23447, + "ĠFarm": 23448, + "enorhabditis": 23449, + "Ġposed": 23450, + "Ġinstanti": 23451, + "ZT": 23452, + "ĠVisualization": 23453, + "ĠGAPDH": 23454, + "lecom": 23455, + "ochron": 23456, + "ĠBj": 23457, + "ĠTrib": 23458, + "Ġbyte": 23459, + "Ġsuperimposed": 23460, + "Ġundi": 23461, + "Ġaccelerator": 23462, + "criptions": 23463, + "ĠSmooth": 23464, + "Ġzip": 23465, + "nesota": 23466, + "ĠEFF": 23467, + "ĠCole": 23468, + "ĠBru": 23469, + "rend": 23470, + "utz": 23471, + "Ġdiagnose": 23472, + "basis": 23473, + "diamond": 23474, + "ĠInn": 23475, + "ĠMedian": 23476, + "Ġmarginally": 23477, + "Ġlemmas": 23478, + "rectomy": 23479, + "Ġdialogue": 23480, + "ĠBrid": 23481, + "Ġå": 23482, + "oxane": 23483, + "aris": 23484, + "Ġmunicipality": 23485, + "Ġproducers": 23486, + "Regarding": 23487, + "ĠFV": 23488, + "ideal": 23489, + "exponential": 23490, + "Label": 23491, + "ĠFrobenius": 23492, + "Ġell": 23493, + "ĠLTE": 23494, + "Ġlipase": 23495, + "rp": 23496, + "Ġdm": 23497, + "otri": 23498, + "cloud": 23499, + "ĠAgent": 23500, + "MSCs": 23501, + "osom": 23502, + "hydropy": 23503, + "neurons": 23504, + "Ġsolvable": 23505, + "ducting": 23506, + "Ġrendered": 23507, + "Ġattractor": 23508, + "Ġbrac": 23509, + "Ãģ": 23510, + "Ġhosted": 23511, + "ĠOct": 23512, + "Ġguiding": 23513, + "Ġdigestive": 23514, + "js": 23515, + "Ġintent": 23516, + "flux": 23517, + "Ġbiosynthetic": 23518, + "Ġelections": 23519, + "ĠWilcoxon": 23520, + "Ġspectrophotometer": 23521, + "Ġimpairs": 23522, + "Ġabdomen": 23523, + "kb": 23524, + "ĠWho": 23525, + "ASSERT": 23526, + "Ġeluted": 23527, + "Ġmaximization": 23528, + "Ġcollector": 23529, + "ĠPreviously": 23530, + "aq": 23531, + "ambo": 23532, + "ĠOz": 23533, + "Cur": 23534, + "Ġcaffeine": 23535, + "Mass": 23536, + "pal": 23537, + "piece": 23538, + "ouville": 23539, + "ĠMeyer": 23540, + "uta": 23541, + "chan": 23542, + "ĠKS": 23543, + "omotor": 23544, + "ĠGPR": 23545, + "Ġeval": 23546, + "ĠCooperative": 23547, + "oglycan": 23548, + "Ġnozzle": 23549, + "ĠShel": 23550, + "Ġinterchange": 23551, + "Ġundergrad": 23552, + "Ġexplanatory": 23553, + "Ġphagocytosis": 23554, + "Ġctx": 23555, + "hess": 23556, + "Ġuniversality": 23557, + "ĠKilling": 23558, + "onsin": 23559, + "Ġlasting": 23560, + "ĠImm": 23561, + "Ġconcordance": 23562, + "yma": 23563, + "Ġautumn": 23564, + "Ġbarley": 23565, + "Ġconsequent": 23566, + "isi": 23567, + "Ġconjugates": 23568, + "Ġtaught": 23569, + "Ġcovariate": 23570, + "Ġadolescence": 23571, + "Ġvillages": 23572, + "Ġeigenfunctions": 23573, + "Ġtemporally": 23574, + "ĠMinnesota": 23575, + "yrate": 23576, + "iesis": 23577, + "definite": 23578, + "Ġalphabet": 23579, + "ĠYun": 23580, + "ĠMAR": 23581, + "Ġsealed": 23582, + "ronectin": 23583, + "ĠSepar": 23584, + "nx": 23585, + "CAA": 23586, + "Ġreception": 23587, + "ucky": 23588, + "ĠPTEN": 23589, + "ĠMorgan": 23590, + "Ġdiodes": 23591, + "Ġmetformin": 23592, + "Ġsynthes": 23593, + "ĠParticip": 23594, + "ĠJersey": 23595, + "Ġamphib": 23596, + "chel": 23597, + "Ġlamp": 23598, + "ĠHels": 23599, + "ĠFN": 23600, + "Ġexcav": 23601, + "isecond": 23602, + "intro": 23603, + "Ġnoncommutative": 23604, + "Ġsubsystems": 23605, + "summ": 23606, + "Ġcontrasting": 23607, + "ĠSilicon": 23608, + "ĠPartition": 23609, + "GlcNAc": 23610, + "Ġdiscern": 23611, + "ĠBounds": 23612, + "ĠRah": 23613, + "Ġapproximating": 23614, + "ĠHypert": 23615, + "ĠDil": 23616, + "Ġcompactness": 23617, + "Ġcaught": 23618, + "ĠImprove": 23619, + "ĠToronto": 23620, + "ĠBiomark": 23621, + "ĠBag": 23622, + "ĠInvent": 23623, + "Ġelaborate": 23624, + "ĠMott": 23625, + "ABC": 23626, + "ĠGraham": 23627, + "Ġpoultry": 23628, + "ĠConjecture": 23629, + "ĠAlgebras": 23630, + "ĠNLO": 23631, + "apsing": 23632, + "pathy": 23633, + "ĠElizabeth": 23634, + "ĠTit": 23635, + "ĠSCI": 23636, + "anton": 23637, + "Ġvoting": 23638, + "mathrel": 23639, + "ĠFord": 23640, + "igibility": 23641, + "Ġallergy": 23642, + "acoustic": 23643, + "ĠDyn": 23644, + "ĠDSC": 23645, + "ĠGRO": 23646, + "ĠThirty": 23647, + "Ġanalysing": 23648, + "ĠEmpire": 23649, + "fire": 23650, + "Ġpathologic": 23651, + "Ġpatent": 23652, + "Ġheard": 23653, + "ĠFront": 23654, + "isconsin": 23655, + "hypert": 23656, + "uzumab": 23657, + "ĠMutation": 23658, + "Ġbiliary": 23659, + "Ġsuperfluid": 23660, + "ĠWC": 23661, + "ustom": 23662, + "ĠActivities": 23663, + "Ġpolypeptide": 23664, + "heets": 23665, + "Ġborders": 23666, + "early": 23667, + "Ġorthogon": 23668, + "Ġbulge": 23669, + "ï£": 23670, + "Ġconical": 23671, + "ĠLept": 23672, + "Ġelectrolytes": 23673, + "Ġ«": 23674, + "regulating": 23675, + "Ġviolated": 23676, + "âĺ": 23677, + "ALT": 23678, + "ĠWorks": 23679, + "ĠHepat": 23680, + "urgical": 23681, + "obar": 23682, + "ĠReactive": 23683, + "possibly": 23684, + "ĠAdsorption": 23685, + "ĠRio": 23686, + "anoic": 23687, + "ĠâĨij": 23688, + "Ġintriguing": 23689, + "Ġom": 23690, + "hertz": 23691, + "ĠApproximate": 23692, + "ĠParent": 23693, + "Ġcoin": 23694, + "expand": 23695, + "в": 23696, + "Ġnonparametric": 23697, + "extern": 23698, + "aeus": 23699, + "glycerol": 23700, + "Ġcp": 23701, + "Ġbatches": 23702, + "Ġnanomaterials": 23703, + "Use": 23704, + "ĠVivo": 23705, + "Rh": 23706, + "Ġtiles": 23707, + "Ġdepict": 23708, + "Ġsouthwest": 23709, + "ĠCasimir": 23710, + "layered": 23711, + "ĠLeaf": 23712, + "fem": 23713, + "bered": 23714, + "Ġsubalgebra": 23715, + "Ġdetachment": 23716, + "ĠLeuk": 23717, + "olus": 23718, + "ĠRick": 23719, + "Ġabortion": 23720, + "Ġclarified": 23721, + "Ġganglia": 23722, + "QS": 23723, + "oising": 23724, + "ĠForward": 23725, + "ĠPeripheral": 23726, + "shifted": 23727, + "bula": 23728, + "ramolecular": 23729, + "ĠFEM": 23730, + "ĠProton": 23731, + "AME": 23732, + "Ġschedules": 23733, + "Ġaa": 23734, + "ĠUDP": 23735, + "stere": 23736, + "Ġmorphine": 23737, + "Ġspecialist": 23738, + "ĠAndroid": 23739, + "Identif": 23740, + "Ġunexpl": 23741, + "Ġheterozyg": 23742, + "Ġfid": 23743, + "pyridyl": 23744, + "ĠWy": 23745, + "phosphor": 23746, + "Ġfriendly": 23747, + "Ġmicrol": 23748, + "ĠSplit": 23749, + "agner": 23750, + "cribe": 23751, + "Ġmoth": 23752, + "ĠEuro": 23753, + "igs": 23754, + "ĠConditional": 23755, + "ĠStewart": 23756, + "properties": 23757, + "ASC": 23758, + "ĠTraditional": 23759, + "ĠPortugal": 23760, + "Ġearned": 23761, + "Ġcathe": 23762, + "Create": 23763, + "iciencies": 23764, + "Ġsphing": 23765, + "xml": 23766, + "Ġimmunomod": 23767, + "Ġcommute": 23768, + "Ġselenium": 23769, + "anges": 23770, + "hook": 23771, + "denoted": 23772, + "Ġjustify": 23773, + "ĠPool": 23774, + "Ġguinea": 23775, + "Ġcontra": 23776, + "Ġfolded": 23777, + "Ġlisting": 23778, + "ĠLG": 23779, + "ĠLane": 23780, + "Ġsurely": 23781, + "vet": 23782, + "fluorophenyl": 23783, + "Ġcorona": 23784, + "ĠAbund": 23785, + "ĠObjects": 23786, + "Ġtrough": 23787, + "cht": 23788, + "Ġdish": 23789, + "ithi": 23790, + "ĠMatlab": 23791, + "worm": 23792, + "Ġproteomics": 23793, + "Ġintermolecular": 23794, + "ĠPeters": 23795, + "Ġmirrors": 23796, + "quinoline": 23797, + "artens": 23798, + "ĠJewish": 23799, + "kB": 23800, + "ĠDegradation": 23801, + "Ġreleasing": 23802, + "VEGF": 23803, + "Ġsubpopulations": 23804, + "ĠTraffic": 23805, + "Ġproline": 23806, + "ĠHf": 23807, + "Ġadren": 23808, + "birth": 23809, + "Ġsender": 23810, + "Ġatlas": 23811, + "Ġworkplace": 23812, + "Ġreflectivity": 23813, + "ĠExistence": 23814, + "cls": 23815, + "Ġfiner": 23816, + "Ġbreastfeeding": 23817, + "onectin": 23818, + "Ġcogn": 23819, + "ellate": 23820, + "ĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ": 23821, + "byte": 23822, + "Ġsket": 23823, + "NULL": 23824, + "systems": 23825, + "ĠBranch": 23826, + "ĠProposed": 23827, + "learn": 23828, + "Ġtolerant": 23829, + "Ġvertebrates": 23830, + "Ġmultilevel": 23831, + "ĠPAH": 23832, + "Ġaudience": 23833, + "ĠWL": 23834, + "nitrop": 23835, + "ĠCt": 23836, + "Ġsativa": 23837, + "eight": 23838, + "Ġmeg": 23839, + "ocell": 23840, + "Ġstating": 23841, + "dominant": 23842, + "bytes": 23843, + "Ġpu": 23844, + "ĠBatter": 23845, + "otaxis": 23846, + "ĠEBV": 23847, + "Ġnanocrystals": 23848, + "Ġmonopole": 23849, + "Ġdiaphrag": 23850, + "ĠVel": 23851, + "Appendix": 23852, + "atten": 23853, + "impl": 23854, + "Ġlandmark": 23855, + "enclature": 23856, + "ĠSTAR": 23857, + "Ġprostagland": 23858, + "oprotective": 23859, + "Ġloadings": 23860, + "ĠPresence": 23861, + "ĠNSF": 23862, + "resses": 23863, + "FU": 23864, + "ilers": 23865, + "Ġerythrocytes": 23866, + "trac": 23867, + "islation": 23868, + "ĠNight": 23869, + "Ġsteroids": 23870, + "tiz": 23871, + "ĠDMA": 23872, + "Ġric": 23873, + "Ġsalient": 23874, + "ĠFur": 23875, + "special": 23876, + "Ġbioinformatics": 23877, + "ignant": 23878, + "ĠEXPERIM": 23879, + "avorable": 23880, + "disk": 23881, + "Ġcurriculum": 23882, + "imidazol": 23883, + "higher": 23884, + "Ġdesigner": 23885, + "ĠStrength": 23886, + "Ġcytosol": 23887, + "ĠChannels": 23888, + "Land": 23889, + "spar": 23890, + "Expression": 23891, + "Ġdaytime": 23892, + "mercial": 23893, + "vbox": 23894, + "inar": 23895, + "ieving": 23896, + "cein": 23897, + "ĠNCBI": 23898, + "RAN": 23899, + "¸Ģ": 23900, + "Hig": 23901, + "ĠDHA": 23902, + "Ġsubscript": 23903, + "Ġ¢": 23904, + "orange": 23905, + "Ġknows": 23906, + "ĠNAF": 23907, + "produced": 23908, + "epid": 23909, + "Ġdexamethasone": 23910, + "Ġformaldehyde": 23911, + "yll": 23912, + "Ġectopic": 23913, + "ĠVerification": 23914, + "activating": 23915, + "ĠIG": 23916, + "ĠPav": 23917, + "Ġtrading": 23918, + "Ġgraduate": 23919, + "ĠFIR": 23920, + "encil": 23921, + "every": 23922, + "Ġradiological": 23923, + "ĠMammalian": 23924, + "MES": 23925, + "inium": 23926, + "ĠSAS": 23927, + "ĠWH": 23928, + "Override": 23929, + "ĠScheduling": 23930, + "ĠBes": 23931, + "ĠYao": 23932, + "Ġglad": 23933, + "ĠStandards": 23934, + "Ġprovinces": 23935, + "eners": 23936, + "Ġnr": 23937, + "Ġtranspos": 23938, + "ĠCarib": 23939, + "Ġfauna": 23940, + "umi": 23941, + "reset": 23942, + "Ġsupra": 23943, + "Ġdivisions": 23944, + "Ġbiodegrad": 23945, + "metrics": 23946, + "ografts": 23947, + "Ġfunctors": 23948, + "Ġsupportive": 23949, + "Ġcaudal": 23950, + "Ġexerts": 23951, + "Ġcub": 23952, + "odimer": 23953, + "Ġairborne": 23954, + "Ġdelivering": 23955, + "Ġmultivariable": 23956, + "Ġfurnace": 23957, + "Ġremnant": 23958, + "Ġinco": 23959, + "ĠElectromagnetic": 23960, + "mapping": 23961, + "Ġdeclines": 23962, + "cold": 23963, + "ĠSeed": 23964, + "conversion": 23965, + "Ġglycogen": 23966, + "dT": 23967, + "awi": 23968, + "APP": 23969, + "Hol": 23970, + "atalysts": 23971, + "ĠSatellite": 23972, + "garis": 23973, + "card": 23974, + "ĠBreak": 23975, + "ĠAgainst": 23976, + "ddot": 23977, + "Ġpruning": 23978, + "ĠCaenorhabditis": 23979, + "Ġsucceeded": 23980, + "ubert": 23981, + "ĠÏħ": 23982, + "IDs": 23983, + "Ġasymptotics": 23984, + "Ġautoanti": 23985, + "ĠScalar": 23986, + "Ġnematode": 23987, + "hd": 23988, + "Ġgyn": 23989, + "istocene": 23990, + "Ġunderground": 23991, + "ĠEthical": 23992, + "Ġsial": 23993, + "ĠMigration": 23994, + "cope": 23995, + "Ġstigma": 23996, + "Ġeleven": 23997, + "Ġcoloring": 23998, + "initions": 23999, + "ĠJay": 24000, + "oba": 24001, + "ĠLDA": 24002, + "Ġbuilds": 24003, + "gences": 24004, + "ĠEcology": 24005, + "scheme": 24006, + "ĠUltras": 24007, + "Ġmediation": 24008, + "ĠTaq": 24009, + "Ġflying": 24010, + "ĠEquilibrium": 24011, + "ophosphate": 24012, + "ĠArgentina": 24013, + "psia": 24014, + "ttes": 24015, + "Ġdisparity": 24016, + "Ġadvertis": 24017, + "aggreg": 24018, + "ISA": 24019, + "odem": 24020, + "ĠRational": 24021, + "Ġsilent": 24022, + "divided": 24023, + "Pan": 24024, + "JA": 24025, + "claim": 24026, + "Ġradioactive": 24027, + "Ġpink": 24028, + "Ġconverse": 24029, + "ĠMell": 24030, + "enib": 24031, + "ruskal": 24032, + "slope": 24033, + "henol": 24034, + "ĠPon": 24035, + "partition": 24036, + "SMGR": 24037, + "titled": 24038, + "ĠInterference": 24039, + "tosecond": 24040, + "Ġseq": 24041, + "Ġtransitive": 24042, + "ĠWid": 24043, + "reviewed": 24044, + "×¥": 24045, + "ĠVC": 24046, + "recall": 24047, + "ogeneic": 24048, + "ĠOverexpression": 24049, + "Ġcommitted": 24050, + "Ġsynapse": 24051, + "Short": 24052, + "ĠNeutral": 24053, + "icles": 24054, + "ISM": 24055, + "Ġintrinsically": 24056, + "Ġmicrosatellite": 24057, + "RN": 24058, + "ĠâĪĥ": 24059, + "detection": 24060, + "Ġcodimension": 24061, + "Ġdrawbacks": 24062, + "ĠTurner": 24063, + "Ġsputtering": 24064, + "Ġdismut": 24065, + "Ġhypogly": 24066, + "Ġspeak": 24067, + "JD": 24068, + "Ġsul": 24069, + "Ġperinatal": 24070, + "Ġink": 24071, + "iest": 24072, + "Ġofficers": 24073, + "tick": 24074, + "Ġretaining": 24075, + "ĠNET": 24076, + "Ġexchanges": 24077, + "Ġanyone": 24078, + "ĠEndothelial": 24079, + "send": 24080, + "injection": 24081, + "ĠPeru": 24082, + "Ġclades": 24083, + "uctuations": 24084, + "Ġsulphate": 24085, + "pio": 24086, + "Ġphysi": 24087, + "ĠMiy": 24088, + "ĠBAS": 24089, + "arius": 24090, + "Ġlipopolysaccharide": 24091, + "Ġneurodegeneration": 24092, + "ĠTurkish": 24093, + "Ġophthal": 24094, + "Ġacted": 24095, + "entre": 24096, + "Ġshaking": 24097, + "Ġchloroplast": 24098, + "ĠSid": 24099, + "regnancy": 24100, + "asion": 24101, + "ĠHs": 24102, + "Ġinitiating": 24103, + "Ġflexural": 24104, + "Ϫ": 24105, + "Ġparac": 24106, + "Ġinterlayer": 24107, + "ĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ": 24108, + "cause": 24109, + "ractions": 24110, + "Ġvaluation": 24111, + "SYSMGR": 24112, + "ĠGarcia": 24113, + "arrays": 24114, + "Ġcasting": 24115, + "ĠPFN": 24116, + "ĠLanc": 24117, + "ĠGlob": 24118, + "Ġdenti": 24119, + "Ġportfolio": 24120, + "ĠHolocene": 24121, + "ĠMATERIAL": 24122, + "Ġsarc": 24123, + "Lear": 24124, + "Ġtin": 24125, + "ĠClear": 24126, + "below": 24127, + "Ġadvection": 24128, + "Ġoverlaps": 24129, + "Ġarthroplasty": 24130, + "compute": 24131, + "Ġglycolysis": 24132, + "hept": 24133, + "lora": 24134, + "frames": 24135, + "ĠHern": 24136, + "proto": 24137, + "Ġswine": 24138, + "Ġjejun": 24139, + "Ġrepeating": 24140, + "ancreatic": 24141, + "ĠCollins": 24142, + "ĠPrinciple": 24143, + "Ġnanof": 24144, + "Ġadjacency": 24145, + "Ġsynov": 24146, + "chet": 24147, + "ĠAlmost": 24148, + "Ġintrusion": 24149, + "Ġechocardiography": 24150, + "liferation": 24151, + "Ġquiescent": 24152, + "ĠMuk": 24153, + "Ġlifetimes": 24154, + "graded": 24155, + "Ġoverwhel": 24156, + "zel": 24157, + "Ġnitride": 24158, + "Ġdisturbed": 24159, + "Ġfastest": 24160, + "grability": 24161, + "Ġtolerated": 24162, + "frag": 24163, + "ĠExtension": 24164, + "anoate": 24165, + "iferous": 24166, + "Ġhydrodynamics": 24167, + "IONAL": 24168, + "ĠToday": 24169, + "ĠExpansion": 24170, + "Ġvenom": 24171, + "ĠHepatitis": 24172, + "ño": 24173, + "onation": 24174, + "synuclein": 24175, + "Ġbasketball": 24176, + "clusions": 24177, + "Ġsettled": 24178, + "IQR": 24179, + "ĠCra": 24180, + "Ġautomation": 24181, + "ĠHealthy": 24182, + "ĠPortuguese": 24183, + "ĠAbelian": 24184, + "Ġgad": 24185, + "ĠHG": 24186, + "ĠRoth": 24187, + "Ġconsume": 24188, + "FG": 24189, + "inals": 24190, + "ĠMCMC": 24191, + "Ġpregnancies": 24192, + "DES": 24193, + "portional": 24194, + "ĠBiochemical": 24195, + "Ġmissions": 24196, + "ĠAntibody": 24197, + "ĠBCG": 24198, + "ĠLAS": 24199, + "marine": 24200, + "DMA": 24201, + "Ġlongevity": 24202, + "ĠDry": 24203, + "ĠRao": 24204, + "Ġinterferometer": 24205, + "Ġdiscretized": 24206, + "osensory": 24207, + "sit": 24208, + "etta": 24209, + "tainer": 24210, + "otherwise": 24211, + "AKT": 24212, + "ĠFaculty": 24213, + "Ġascertain": 24214, + "ĠSimulated": 24215, + "Ġpayload": 24216, + "OUT": 24217, + "Ġsuffers": 24218, + "Ġtungsten": 24219, + "ĠAnxiety": 24220, + "ĠHeterogeneous": 24221, + "lingual": 24222, + "Ġpherom": 24223, + "bors": 24224, + "linux": 24225, + "Ġmonkey": 24226, + "£": 24227, + "url": 24228, + "ĠAcross": 24229, + "ĠAKI": 24230, + "Ġopp": 24231, + "ocalization": 24232, + "Ġmorphogenesis": 24233, + "gic": 24234, + "ĠPCM": 24235, + "Ġoligomers": 24236, + "Ġexhaustive": 24237, + "ĠGIS": 24238, + "Ġpristine": 24239, + "ĠActiv": 24240, + "ĠScilab": 24241, + "ĠAcoustic": 24242, + "ĠPick": 24243, + "integral": 24244, + "Ġphilosophy": 24245, + "ĠDeng": 24246, + "ĠHab": 24247, + "scape": 24248, + "ĠEmergency": 24249, + "Ġepi": 24250, + "ĠBET": 24251, + "ricket": 24252, + "Ġannulus": 24253, + "Ġlysosomal": 24254, + "Ġstrands": 24255, + "CAP": 24256, + "ĠAmino": 24257, + "ĠStri": 24258, + "ependence": 24259, + "Ġfootprint": 24260, + "ĠFatty": 24261, + "ĠNaz": 24262, + "nest": 24263, + "ĠExplicit": 24264, + "planetary": 24265, + "lead": 24266, + "Ġgrip": 24267, + "need": 24268, + "ATT": 24269, + "ERV": 24270, + "ĠTargeted": 24271, + "CRP": 24272, + "Ġparamagnetic": 24273, + "ĠTyr": 24274, + "ĠMicroRNA": 24275, + "hline": 24276, + "gh": 24277, + "pit": 24278, + "ĠIsolated": 24279, + "jectory": 24280, + "Ġcleaned": 24281, + "oste": 24282, + "Ġpathologies": 24283, + "propylene": 24284, + "ĠReason": 24285, + "ĠINFO": 24286, + "RAY": 24287, + "Values": 24288, + "Ġalive": 24289, + "Ġbiof": 24290, + "ewicz": 24291, + "Ġcracking": 24292, + "google": 24293, + "locked": 24294, + "crop": 24295, + "eca": 24296, + "urane": 24297, + "SVM": 24298, + "utta": 24299, + "ĠMetric": 24300, + "ĠEncycl": 24301, + "ĠModule": 24302, + "Ġwarranted": 24303, + "Ġmultidisciplinary": 24304, + "ĠElastic": 24305, + "labelled": 24306, + "ĠSchwarzschild": 24307, + "ĠPCC": 24308, + "major": 24309, + "video": 24310, + "Ġstoring": 24311, + "ĠMake": 24312, + "ako": 24313, + "ĠJia": 24314, + "Ġtoroidal": 24315, + "ĠHMM": 24316, + "Ġmasking": 24317, + "Again": 24318, + "Ġnephropathy": 24319, + "gf": 24320, + "Ġdominating": 24321, + "erkin": 24322, + "ĠFabrication": 24323, + "ĠFel": 24324, + "DEF": 24325, + "culture": 24326, + "ĠIra": 24327, + "ĠREG": 24328, + "ilingual": 24329, + "Ġmuss": 24330, + "plain": 24331, + "zh": 24332, + "iston": 24333, + "ĠÎ¥": 24334, + "minimal": 24335, + "cmp": 24336, + "GaN": 24337, + "Ġmonotonic": 24338, + "Ġinvolution": 24339, + "Ġwhatever": 24340, + "ĠInstrument": 24341, + "imple": 24342, + "ĠPCI": 24343, + "ĠNeuronal": 24344, + "Ġfacets": 24345, + "Ġhemodialysis": 24346, + "apatite": 24347, + "ĠKil": 24348, + "ontally": 24349, + "Ġinserting": 24350, + "ĠRIP": 24351, + "Ġconnective": 24352, + "ĠFederation": 24353, + "nut": 24354, + "ĠGun": 24355, + "inuous": 24356, + "Mor": 24357, + "ĠWisconsin": 24358, + "Ġmush": 24359, + "ITS": 24360, + "Ġeject": 24361, + "ĠBPS": 24362, + "ĠHorn": 24363, + "ĠEmbedding": 24364, + "Ġraces": 24365, + "ĠJam": 24366, + "Ġposture": 24367, + "ĠInvol": 24368, + "ĠIMDb": 24369, + "ĠPlease": 24370, + "proportion": 24371, + "ĠInterleukin": 24372, + "Ġarte": 24373, + "Ġsubsp": 24374, + "oderma": 24375, + "Find": 24376, + "imit": 24377, + "ĠClin": 24378, + "Hel": 24379, + "FILE": 24380, + "original": 24381, + "ervoir": 24382, + "Ġpleural": 24383, + "clipse": 24384, + "encer": 24385, + "inaries": 24386, + "Ġvictory": 24387, + "Ġinvestigates": 24388, + "ĠImportance": 24389, + "ĠMIN": 24390, + "Ġphonons": 24391, + "integrated": 24392, + "Ġexchanged": 24393, + "ystis": 24394, + "Ġmigrate": 24395, + "Rob": 24396, + "eland": 24397, + "proof": 24398, + "ĠIntegral": 24399, + "Ġmergers": 24400, + "Ġpolyphenols": 24401, + "ĠFully": 24402, + "Ġuro": 24403, + "Ġhomogenous": 24404, + "Ġrecognizing": 24405, + "ĠSignals": 24406, + "vat": 24407, + "igms": 24408, + "Ġaccuracies": 24409, + "Substituting": 24410, + "Ġpoisoning": 24411, + "Ġshrimp": 24412, + "ĠHölder": 24413, + "ĠTanzania": 24414, + "JS": 24415, + "MENT": 24416, + "ĠTopology": 24417, + "Ġinvers": 24418, + "ĠDU": 24419, + "Ġuniaxial": 24420, + "ĠSEC": 24421, + "party": 24422, + "Ġcontrollable": 24423, + "Ġfum": 24424, + "ostics": 24425, + "Ġmanifested": 24426, + "Ġpropagated": 24427, + "Ġsuffix": 24428, + "ĠCAN": 24429, + "ĠPret": 24430, + "keeping": 24431, + "Assuming": 24432, + "Ġsuture": 24433, + "Ġpest": 24434, + "Ġgamet": 24435, + "ĠAlignment": 24436, + "esarean": 24437, + "tum": 24438, + "Ġrefine": 24439, + "Ġpopulated": 24440, + "Ġestu": 24441, + "ĠDefense": 24442, + "ĠPrivacy": 24443, + "ĠWein": 24444, + "ĠSenate": 24445, + "Ġazimuth": 24446, + "ĠProfessional": 24447, + "Ġlabour": 24448, + "Ġseminal": 24449, + "ĠIntervention": 24450, + "ĠOlder": 24451, + "AU": 24452, + "Wind": 24453, + "dynamical": 24454, + "ĠVeter": 24455, + "ación": 24456, + "Ġcooking": 24457, + "Ġâīª": 24458, + "Ġbead": 24459, + "Ġdensely": 24460, + "Ġpalliative": 24461, + "mort": 24462, + "ĠAAV": 24463, + "ĠRyan": 24464, + "Prim": 24465, + "galax": 24466, + "muir": 24467, + "sters": 24468, + "ĠSalt": 24469, + "queeze": 24470, + "ĠPlateau": 24471, + "Ġí": 24472, + "Ġlighter": 24473, + "ordinary": 24474, + "formaldehyde": 24475, + "ĠWer": 24476, + "Ġbark": 24477, + "Ġhomogenized": 24478, + "Ġpyramidal": 24479, + "Ġinert": 24480, + "ĠAPC": 24481, + "ĠMicros": 24482, + "ĠProteobacteria": 24483, + "ĠPurification": 24484, + "Ġparametrized": 24485, + "Ġille": 24486, + "accuracy": 24487, + "embedding": 24488, + "Ġtoughness": 24489, + "Ġisometry": 24490, + "backs": 24491, + "ĠFIG": 24492, + "ĠRon": 24493, + "ĠESP": 24494, + "Ġmicroglial": 24495, + "interp": 24496, + "ĠIntegrating": 24497, + "ĠReducing": 24498, + "Ġhearts": 24499, + "Ġseriously": 24500, + "Ġspecially": 24501, + "CTRL": 24502, + "ĠSurprisingly": 24503, + "Ġhyperplane": 24504, + "polynomial": 24505, + "Ġreconc": 24506, + "Ġpharmacokinetic": 24507, + "Mart": 24508, + "ĠBright": 24509, + "mable": 24510, + "Ġionizing": 24511, + "Ġtrich": 24512, + "zymatic": 24513, + "Ġleptons": 24514, + "etting": 24515, + "ĠHex": 24516, + "Ġneurop": 24517, + "Ġadipocytes": 24518, + "Ġrods": 24519, + "Ġsupercritical": 24520, + "Ġsuccin": 24521, + "Ġanter": 24522, + "ĠNAC": 24523, + "ĠSubsequent": 24524, + "IGH": 24525, + "Ġsoutheast": 24526, + "Ġendowed": 24527, + "Ġconverging": 24528, + "Ġspatio": 24529, + "Ġcelebr": 24530, + "helix": 24531, + "Ġaccessions": 24532, + "Ġimmobilization": 24533, + "ĠEQ": 24534, + "spatial": 24535, + "Ġinformal": 24536, + "Ġdere": 24537, + "ĠEnzyme": 24538, + "ĠBBC": 24539, + "ĠEPR": 24540, + "Ġelectrically": 24541, + "Ġleukocytes": 24542, + "Ġalanine": 24543, + "Ġmitogen": 24544, + "Ġintramolecular": 24545, + "ĠNI": 24546, + "Ġprokary": 24547, + "ISO": 24548, + "Ġdodec": 24549, + "ĠTrade": 24550, + "ĠDai": 24551, + "ccc": 24552, + "ĠWalter": 24553, + "ĠNeither": 24554, + "Ġvulgaris": 24555, + "Ġlongitude": 24556, + "ĠIntro": 24557, + "option": 24558, + "ĠQC": 24559, + "ĠâĿ": 24560, + "protection": 24561, + "ĠIMF": 24562, + "aprote": 24563, + "Ġlinker": 24564, + "Ġfounder": 24565, + "Ġaspiration": 24566, + "clusters": 24567, + "ĠPay": 24568, + "ĠRoles": 24569, + "Ġacyclic": 24570, + "overing": 24571, + "Ġremind": 24572, + "ĠTong": 24573, + "ĠAtten": 24574, + "Ġengineers": 24575, + "Ġdysregulation": 24576, + "ĠFourth": 24577, + "Ġfilename": 24578, + "ĠCool": 24579, + "protected": 24580, + "Ġnilpotent": 24581, + "ĠHK": 24582, + "clone": 24583, + "ĠStadium": 24584, + "ais": 24585, + "osamine": 24586, + "ABILITY": 24587, + "rovascular": 24588, + "ĠAH": 24589, + "ĠConcept": 24590, + "Ġcerebrospinal": 24591, + "owitz": 24592, + "Ġresolving": 24593, + "Ġwings": 24594, + "ĠEGF": 24595, + "ĠCommand": 24596, + "iazep": 24597, + "Ġbeef": 24598, + "Ġspines": 24599, + "Ġpriorities": 24600, + "Ġattempting": 24601, + "Ġtelomere": 24602, + "BQU": 24603, + "Ġviolations": 24604, + "LB": 24605, + "omnia": 24606, + "osm": 24607, + "irq": 24608, + "Ġdiversification": 24609, + "alt": 24610, + "ĠBRAF": 24611, + "Ġorganisation": 24612, + "die": 24613, + "Ġautoreg": 24614, + "icked": 24615, + "ĠEcological": 24616, + "ĠTrain": 24617, + "ĠPY": 24618, + "Ġmusculoskeletal": 24619, + "Ġhorizons": 24620, + "Ġomega": 24621, + "Ġquasars": 24622, + "eption": 24623, + "Ġerad": 24624, + "Ġluminal": 24625, + "Interestingly": 24626, + "Ġpayment": 24627, + "cnt": 24628, + "Ġdipl": 24629, + "Ġrecognised": 24630, + "Cat": 24631, + "ĠChl": 24632, + "Ġmillions": 24633, + "Ġdisappearance": 24634, + "GAP": 24635, + "Ġradiographic": 24636, + "Ġpostpartum": 24637, + "developed": 24638, + "xual": 24639, + "Ġhed": 24640, + "idered": 24641, + "ĠCertain": 24642, + "Ġdysplasia": 24643, + "________": 24644, + "ĠHalf": 24645, + "Ġasymmetries": 24646, + "ĠAlcohol": 24647, + "Sum": 24648, + "Ġfm": 24649, + "Ġchap": 24650, + "Ġpretreated": 24651, + "ĠGallery": 24652, + "Ġoutperform": 24653, + "Ġbreeds": 24654, + "Ġtied": 24655, + "Ġdiffeomorphism": 24656, + "Ġcausative": 24657, + "Ġcollectively": 24658, + "Ġsuboptimal": 24659, + "Ġinsulation": 24660, + "Ġmanipulate": 24661, + "Ġkilomet": 24662, + "Ġrepulsion": 24663, + "Ġchloroform": 24664, + "Ġbean": 24665, + "Ġhero": 24666, + "rophysics": 24667, + "ĠPeptide": 24668, + "Ġoutlier": 24669, + "Derived": 24670, + "isser": 24671, + "ĠInfant": 24672, + "sulfonyl": 24673, + "Ġrecursively": 24674, + "Hu": 24675, + "ĠKoh": 24676, + "pyridine": 24677, + "Ġsquad": 24678, + "Ġthirty": 24679, + "Ġspoken": 24680, + "ĠZar": 24681, + "othermic": 24682, + "Ġcalcification": 24683, + "ĠHelsinki": 24684, + "Ġbeach": 24685, + "ĠFDR": 24686, + "Ġprobiotic": 24687, + "Ġfinishing": 24688, + "ymmetrical": 24689, + "Ġvacancy": 24690, + "Ġthrombo": 24691, + "Compared": 24692, + "AST": 24693, + "sted": 24694, + "otherap": 24695, + "Ġiodide": 24696, + "Ġtt": 24697, + "alignment": 24698, + "Ġmicrovascular": 24699, + "Ġinitialize": 24700, + "ĠANALYSIS": 24701, + "Ġtopographic": 24702, + "ĠReporting": 24703, + "Ġunderestimated": 24704, + "puted": 24705, + "Ġatherosclerotic": 24706, + "Qiagen": 24707, + "gut": 24708, + "ĠCortical": 24709, + "Ġdisrupt": 24710, + "este": 24711, + "Ġglue": 24712, + "Ġnarrower": 24713, + "Ġinpatient": 24714, + "Ġscholars": 24715, + "Ġbc": 24716, + "ĠPsychological": 24717, + "ĠHamiltonians": 24718, + "Ġhonor": 24719, + "tibular": 24720, + "Ġinsertions": 24721, + "oscope": 24722, + "Ġpharmacokinetics": 24723, + "Ġmathematically": 24724, + "Ġfork": 24725, + "ipital": 24726, + "ĠArgs": 24727, + "abolism": 24728, + "Ġâİł": 24729, + "ĠRobot": 24730, + "ĠCasc": 24731, + "Ġleaching": 24732, + "ĠLack": 24733, + "Ġendocytosis": 24734, + "Ġtris": 24735, + "Ġsensitivities": 24736, + "Ġlicensed": 24737, + "Ġsponge": 24738, + "carbonyl": 24739, + "feat": 24740, + "Ġprecl": 24741, + "Ġwaist": 24742, + "tifications": 24743, + "Ġoliv": 24744, + "binary": 24745, + "atri": 24746, + "ĠBiot": 24747, + "TZ": 24748, + "Ġfake": 24749, + "ĠMosc": 24750, + "ĠHPS": 24751, + "ĠVoltage": 24752, + "ĠâİĿ": 24753, + "ĠAhmed": 24754, + "ĠSexual": 24755, + "dehydes": 24756, + "ĠCot": 24757, + "Ġmagma": 24758, + "oxylin": 24759, + "ÐĪ": 24760, + "amethyl": 24761, + "ĠLOS": 24762, + "diphenyl": 24763, + "experimental": 24764, + "Ġpluripotent": 24765, + "agittal": 24766, + "walk": 24767, + "Ġplasmonic": 24768, + "Ġcontracts": 24769, + "Ġexped": 24770, + "ĠArabia": 24771, + "Ġshoots": 24772, + "ĠRAN": 24773, + "ustrated": 24774, + "Ġconvexity": 24775, + "ĠmJ": 24776, + "ĠAbsolute": 24777, + "ĠSEL": 24778, + "MIP": 24779, + "ĠActually": 24780, + "sole": 24781, + "QI": 24782, + "ĠTGFβ": 24783, + "Ġâİŀ": 24784, + "Ġrearrangements": 24785, + "Ġcuring": 24786, + "expensive": 24787, + "ceptibility": 24788, + "Ġours": 24789, + "ĠKidney": 24790, + "Ġassigns": 24791, + "Ġvoxels": 24792, + "oreal": 24793, + "Ġevening": 24794, + "hus": 24795, + "ĠãĢ": 24796, + "oradi": 24797, + "ĠCorrection": 24798, + "Ġnanofibers": 24799, + "Ġcantile": 24800, + "bigoplus": 24801, + "uminous": 24802, + "eclampsia": 24803, + "ĠCult": 24804, + "ECH": 24805, + "atology": 24806, + "Ġji": 24807, + "cryp": 24808, + "ĠAspects": 24809, + "eni": 24810, + "Ġsemis": 24811, + "IRS": 24812, + "ĠPho": 24813, + "encoding": 24814, + "ĠJustice": 24815, + "ococci": 24816, + "Ġhypothalamic": 24817, + "ractable": 24818, + "ĠOrb": 24819, + "Simons": 24820, + "Ġmanipulated": 24821, + "attribute": 24822, + "onov": 24823, + "orously": 24824, + "endar": 24825, + "uder": 24826, + "insert": 24827, + "Ġlysed": 24828, + "ĠHodge": 24829, + "Ġfootballer": 24830, + "Device": 24831, + "ĠLeast": 24832, + "Ġstratum": 24833, + "Ġmitral": 24834, + "Ġsell": 24835, + "ĠMuc": 24836, + "glycer": 24837, + "oj": 24838, + "Ġpathogenicity": 24839, + "ĠDeclaration": 24840, + "opause": 24841, + "ĠArticle": 24842, + "Ġrinsed": 24843, + "ĠLévy": 24844, + "rement": 24845, + "Ġants": 24846, + "ĠDic": 24847, + "ĠkPa": 24848, + "urry": 24849, + "motion": 24850, + "client": 24851, + "Ġaccessory": 24852, + "Ġdepolarization": 24853, + "namely": 24854, + "Ġdisparities": 24855, + "Ġfavourable": 24856, + "ĠTibet": 24857, + "Ġoocyte": 24858, + "istration": 24859, + "Ġunresolved": 24860, + "criptive": 24861, + "physics": 24862, + "Ġbenzo": 24863, + "Ġcrystallinity": 24864, + "Ġpayoff": 24865, + "Ġumbilical": 24866, + "osil": 24867, + "ĠSystemic": 24868, + "ĠSTM": 24869, + "Ġstabilizer": 24870, + "USA": 24871, + "ĠJensen": 24872, + "Aug": 24873, + "ĠHat": 24874, + "AGG": 24875, + "underbrace": 24876, + "Ġmanipulations": 24877, + "ĠManc": 24878, + "nedy": 24879, + "Ġscratch": 24880, + "Cherry": 24881, + "osaccharides": 24882, + "Ġprecipitate": 24883, + "quarters": 24884, + "icul": 24885, + "Ġoptimally": 24886, + "many": 24887, + "Ġneoplasms": 24888, + "Ġinward": 24889, + "aryng": 24890, + "Ġmoll": 24891, + "ĠWel": 24892, + "ĠWiley": 24893, + "Ġnewspaper": 24894, + "Ġinhabitants": 24895, + "ĠSuccess": 24896, + "Ġbridging": 24897, + "Ġdisconnected": 24898, + "Ġhygiene": 24899, + "Dist": 24900, + "Ġscripts": 24901, + "Ġmesoporous": 24902, + "Ġrestricts": 24903, + "actone": 24904, + "Ġaquifer": 24905, + "ĠïĤ·": 24906, + "Ġplex": 24907, + "Ġpresumed": 24908, + "Ġips": 24909, + "ĠMilitary": 24910, + "Ġjudged": 24911, + "Ġald": 24912, + "Ġsequest": 24913, + "compared": 24914, + "ULATION": 24915, + "adapted": 24916, + "Ġinstructed": 24917, + "pulse": 24918, + "Ġcusp": 24919, + "matching": 24920, + "carrier": 24921, + "Ġenforce": 24922, + "ĠInterview": 24923, + "ometrics": 24924, + "Ġnullptr": 24925, + "Ġflavour": 24926, + "ĠPareto": 24927, + "ĠBER": 24928, + "Ġuv": 24929, + "Ġcrash": 24930, + "ĠCann": 24931, + "ĠMineral": 24932, + "ĠOlympic": 24933, + "Ġpolycrystalline": 24934, + "lett": 24935, + "Tables": 24936, + "requent": 24937, + "Ġsedentary": 24938, + "unsaturated": 24939, + "ĠBernoulli": 24940, + "Ġadmissions": 24941, + "itorial": 24942, + "acute": 24943, + "Ġadditions": 24944, + "weet": 24945, + "ALE": 24946, + "ĠManip": 24947, + "tokens": 24948, + "preced": 24949, + "dk": 24950, + "consider": 24951, + "Ġïĺ¹": 24952, + "Ġwrites": 24953, + "cardia": 24954, + "ctomy": 24955, + "omatous": 24956, + "Symbol": 24957, + "usten": 24958, + "Ġproteolytic": 24959, + "categories": 24960, + "Ġfic": 24961, + "Ġswing": 24962, + "Ġpassenger": 24963, + "Ġoverlapped": 24964, + "ifi": 24965, + "Ġmutational": 24966, + "ĠJosephson": 24967, + "Ġregret": 24968, + "ĠArk": 24969, + "ĠCFD": 24970, + "Ġmaneu": 24971, + "encoded": 24972, + "textsc": 24973, + "Ġdecompositions": 24974, + "ĠDeb": 24975, + "Ġmandibular": 24976, + "dU": 24977, + "ĠPIC": 24978, + "Ġtranscriptomic": 24979, + "Ġtelescop": 24980, + "ĠSantos": 24981, + "oE": 24982, + "ĠMCP": 24983, + "Ġindigenous": 24984, + "Ġmicrospheres": 24985, + "Ġcodew": 24986, + "zip": 24987, + "Ġfingers": 24988, + "Ġcampaigns": 24989, + "¸Ģł": 24990, + "Ġaccidents": 24991, + "ĠTools": 24992, + "Planck": 24993, + "»": 24994, + "eder": 24995, + "ingham": 24996, + "oxidase": 24997, + "Ġancestor": 24998, + "whose": 24999, + "Ġphospholipid": 25000, + "Ġconversation": 25001, + "ĠHof": 25002, + "cortical": 25003, + "glycos": 25004, + "Ġmanufacturers": 25005, + "opulmonary": 25006, + "Ġinclined": 25007, + "ĠBethe": 25008, + "Ġspending": 25009, + "ĠFusarium": 25010, + "uitively": 25011, + "Ġfemur": 25012, + "ĠLinks": 25013, + "Ġnitrite": 25014, + "Main": 25015, + "Ġflora": 25016, + "ĠPhD": 25017, + "ĠWriting": 25018, + "ĠHessian": 25019, + "Ġμs": 25020, + "ools": 25021, + "Ġvictims": 25022, + "ĠRew": 25023, + "ansen": 25024, + "Ear": 25025, + "Ġorn": 25026, + "Ġthermoelectric": 25027, + "ENSE": 25028, + "ĠWeighted": 25029, + "holes": 25030, + "Ġcen": 25031, + "Ġacuity": 25032, + "Ġvacancies": 25033, + "ĠDuke": 25034, + "Ġpaclitaxel": 25035, + "Ġconverts": 25036, + "bourne": 25037, + "ĠACS": 25038, + "osi": 25039, + "Ġcriminal": 25040, + "ĠIb": 25041, + "unes": 25042, + "ĠNanoc": 25043, + "Post": 25044, + "ĠMDS": 25045, + "Ġeconomics": 25046, + "Ġthoughts": 25047, + "Ġneuroprotective": 25048, + "Ġintersects": 25049, + "cers": 25050, + "atid": 25051, + "usa": 25052, + "ĠAns": 25053, + "Ġafterwards": 25054, + "ĠOFDM": 25055, + "ĠCMV": 25056, + "ĠCum": 25057, + "ATG": 25058, + "ĠImageNet": 25059, + "ĠAttack": 25060, + "ogeneities": 25061, + "Ġcounseling": 25062, + "ĠCONTR": 25063, + "ález": 25064, + "ĠDh": 25065, + "ĠGV": 25066, + "Ġpositional": 25067, + "Ġgang": 25068, + "ĠInteractive": 25069, + "wig": 25070, + "ĠTrace": 25071, + "ĠDSS": 25072, + "Ġsynthetase": 25073, + "ĠGalile": 25074, + "usually": 25075, + "ĠBass": 25076, + "ardless": 25077, + "Ġexecuting": 25078, + "KP": 25079, + "ĠNepal": 25080, + "READ": 25081, + "ĠLock": 25082, + "ohydro": 25083, + "rotation": 25084, + "dil": 25085, + "roscopically": 25086, + "reperfusion": 25087, + "Ġdishes": 25088, + "ĠProceedings": 25089, + "ĠNPC": 25090, + "Ġmonsoon": 25091, + "ĠLemmas": 25092, + "ĠChandra": 25093, + "Ġreactors": 25094, + "Ġtryptophan": 25095, + "ĠVT": 25096, + "ĠDEM": 25097, + "Ġlegislation": 25098, + "mk": 25099, + "Ġtoric": 25100, + "ĠPrograms": 25101, + "ĠPubMed": 25102, + "ĠrDNA": 25103, + "Ġposts": 25104, + "ĠâİĽ": 25105, + "Ġshedding": 25106, + "tolerant": 25107, + "Ġvoids": 25108, + "ĠCaribbean": 25109, + "CODE": 25110, + "Tube": 25111, + "ALSE": 25112, + "Ġchlorine": 25113, + "Ġcoerc": 25114, + "ĠRhiz": 25115, + "ĠKirk": 25116, + "ĠÃĸ": 25117, + "rout": 25118, + "icides": 25119, + "agu": 25120, + "ĠKw": 25121, + "Ġcru": 25122, + "Observe": 25123, + "ĠRevis": 25124, + "Ġanonym": 25125, + "Ġprerequ": 25126, + "ocortical": 25127, + "Ġrestaur": 25128, + "ĠPopulations": 25129, + "dst": 25130, + "Ġfort": 25131, + "regs": 25132, + "ĠPolarization": 25133, + "Ġpancreatitis": 25134, + "aph": 25135, + "threat": 25136, + "ften": 25137, + "ĠAlaska": 25138, + "ĠFlexible": 25139, + "Ġrepertoire": 25140, + "kan": 25141, + "mathchoice": 25142, + "Ġmitosis": 25143, + "Ġeat": 25144, + "utin": 25145, + "Ġrt": 25146, + "Ġdummy": 25147, + "ĠCys": 25148, + "ĠGor": 25149, + "earchers": 25150, + "HPLC": 25151, + "Ġbay": 25152, + "ĠNielsen": 25153, + "ĠRoc": 25154, + "iani": 25155, + "icit": 25156, + "rague": 25157, + "Ġcourts": 25158, + "testing": 25159, + "Ġamplify": 25160, + "Ġtuples": 25161, + "proliferative": 25162, + "ĠParas": 25163, + "Ġmagnets": 25164, + "Ġchemokines": 25165, + "ĠMitchell": 25166, + "ĠPetri": 25167, + "holtz": 25168, + "ych": 25169, + "matrices": 25170, + "Ġcorrecting": 25171, + "ĠPCa": 25172, + "ynamically": 25173, + "ĠNAFLD": 25174, + "Ġeffluent": 25175, + "itum": 25176, + "Ġthrows": 25177, + "ĠGuid": 25178, + "ochromatic": 25179, + "ĠFro": 25180, + "idad": 25181, + "romagnetism": 25182, + "Herm": 25183, + "ĠSpi": 25184, + "ĠQuas": 25185, + "domains": 25186, + "Ġquadrant": 25187, + "ĠSOX": 25188, + "ĠGovernor": 25189, + "Ġamenable": 25190, + "held": 25191, + "ĠCul": 25192, + "Ġunderwater": 25193, + "ĠKron": 25194, + "ĠSpati": 25195, + "anoyl": 25196, + "CU": 25197, + "ovir": 25198, + "Ġdemographics": 25199, + "Within": 25200, + "ĠMé": 25201, + "textsf": 25202, + "ĠLabel": 25203, + "Ġgenuine": 25204, + "Ġhill": 25205, + "ĠLaz": 25206, + "Ġtesticular": 25207, + "ĠBrow": 25208, + "ICATION": 25209, + "¡": 25210, + "ĠAIC": 25211, + "ancomycin": 25212, + "strual": 25213, + "Ġarrested": 25214, + "ĠSom": 25215, + "ĠIHC": 25216, + "ĠPose": 25217, + "ĠMö": 25218, + "istar": 25219, + "ĠPAM": 25220, + "ĠHCT": 25221, + "Ġtypedef": 25222, + "ĠMorse": 25223, + "ĠLeishman": 25224, + "limb": 25225, + "Ġspheroid": 25226, + "osely": 25227, + "ĠGuinea": 25228, + "renew": 25229, + "Ġpsoriasis": 25230, + "ista": 25231, + "ĠChung": 25232, + "orthogonal": 25233, + "ĠShear": 25234, + "ĠMuslim": 25235, + "ĠPict": 25236, + "Integer": 25237, + "Ġspacer": 25238, + "Ly": 25239, + "Ġdermal": 25240, + "Ġoncology": 25241, + "Ġdp": 25242, + "Ġphotoluminescence": 25243, + "regon": 25244, + "aminase": 25245, + "Ġáºĭ": 25246, + "Instance": 25247, + "verb": 25248, + "Ġmethylated": 25249, + "ĠGem": 25250, + "istently": 25251, + "ĠMgCl": 25252, + "ĠElevated": 25253, + "⣩": 25254, + "onstruct": 25255, + "Ġsnapshot": 25256, + "enem": 25257, + "ĠDisk": 25258, + "Ġhydrostatic": 25259, + "Ġïĥª": 25260, + "vor": 25261, + "ĠIE": 25262, + "ĠLY": 25263, + "ORF": 25264, + "Ġfoil": 25265, + "male": 25266, + "Ġdepended": 25267, + "sparse": 25268, + "Ġmetas": 25269, + "Ġtextures": 25270, + "Ġstacks": 25271, + "MHz": 25272, + "Ġfn": 25273, + "Ġultrac": 25274, + "ĠShould": 25275, + "Vec": 25276, + "nine": 25277, + "infinite": 25278, + "ĠLawrence": 25279, + "ĠInventory": 25280, + "ĠProstate": 25281, + "Ġgesture": 25282, + "ĠSuzuki": 25283, + "Abs": 25284, + "ricane": 25285, + "ĠPeriodic": 25286, + "Myc": 25287, + "ifiable": 25288, + "Ġinefficient": 25289, + "Ġcollapsed": 25290, + "Ġtopologically": 25291, + "Ġpreferable": 25292, + "Ġbronchial": 25293, + "uston": 25294, + "Ġflexion": 25295, + "ourney": 25296, + "translation": 25297, + "Ġepitaxial": 25298, + "Ġirradiance": 25299, + "Ġneighbours": 25300, + "switch": 25301, + "Ġactuators": 25302, + "SOD": 25303, + "mir": 25304, + "dies": 25305, + "ikawa": 25306, + "ĠALL": 25307, + "ĠRSV": 25308, + "ĠHEP": 25309, + "Ġendurance": 25310, + "connection": 25311, + "Ġgestures": 25312, + "odontic": 25313, + "ĠUnc": 25314, + "Ġdismutase": 25315, + "Having": 25316, + "mix": 25317, + "Ġneurogenesis": 25318, + "Ġmyocardium": 25319, + "ĠRussell": 25320, + "Hist": 25321, + "ĠSPI": 25322, + "triazol": 25323, + "agulant": 25324, + "ĠRequired": 25325, + "ĠshRNA": 25326, + "ĠArthur": 25327, + "Ġspawning": 25328, + "dried": 25329, + "Ġrectif": 25330, + "ĠÃī": 25331, + "Ġosteogenic": 25332, + "replace": 25333, + "Ġgaining": 25334, + "Ġneutralization": 25335, + "ĠHartree": 25336, + "Ġfollicles": 25337, + "Ġreligion": 25338, + "Ġduplex": 25339, + "Ġtransients": 25340, + "amped": 25341, + "Ġmicrotubules": 25342, + "interest": 25343, + "Ġsteels": 25344, + "Batch": 25345, + "Ġdenaturation": 25346, + "ĠPhillips": 25347, + "Ġquiet": 25348, + "ĠBureau": 25349, + "ĠRare": 25350, + "Ġquercetin": 25351, + "aults": 25352, + "Ġelution": 25353, + "uka": 25354, + "ĠInterpretation": 25355, + "RV": 25356, + "ĠESC": 25357, + "ĠKom": 25358, + "arettes": 25359, + "ĠïģĦ": 25360, + "Ġtradition": 25361, + "Ġdissected": 25362, + "Neigh": 25363, + "Ġsheaves": 25364, + "Ġbelonged": 25365, + "ĠHistoric": 25366, + "ĠOE": 25367, + "Ġjson": 25368, + "lemma": 25369, + "ĠYAP": 25370, + "odext": 25371, + "interface": 25372, + "Ġextremity": 25373, + "crossing": 25374, + "precedented": 25375, + "according": 25376, + "Ġconstructive": 25377, + "ĠStimulation": 25378, + "ĠHFD": 25379, + "Ġwavenumber": 25380, + "Ġhrs": 25381, + "Ġpapillomavirus": 25382, + "Ġvomiting": 25383, + "Ġreactivation": 25384, + "ometrically": 25385, + "ĠDimensions": 25386, + "objects": 25387, + "orton": 25388, + "ĠMathem": 25389, + "ĠOlive": 25390, + "Ġcrosstalk": 25391, + "partite": 25392, + "opathies": 25393, + "ĠCNTs": 25394, + "rousal": 25395, + "Ġcrowd": 25396, + "ĠLangmuir": 25397, + "ĠTox": 25398, + "echanics": 25399, + "imus": 25400, + "ĠShock": 25401, + "tanh": 25402, + "ĠBrillouin": 25403, + "Ġtransferring": 25404, + "Ġellipse": 25405, + "ĠAddition": 25406, + "ĠRural": 25407, + "Ġgeodesics": 25408, + "GEM": 25409, + "ĠPOS": 25410, + "ĠMission": 25411, + "ocarp": 25412, + "ĠJane": 25413, + "Lie": 25414, + "freq": 25415, + "opot": 25416, + "ĠVibrio": 25417, + "ĠObj": 25418, + "erts": 25419, + "ĠTrials": 25420, + "CFT": 25421, + "ĠCodes": 25422, + "μg": 25423, + "Reference": 25424, + "ĠFung": 25425, + "ĠSuppression": 25426, + "hog": 25427, + "Ġresistive": 25428, + "Chi": 25429, + "intered": 25430, + "Ġpostmenopausal": 25431, + "Statistical": 25432, + "ĠEdwards": 25433, + "Ġses": 25434, + "Ġfarming": 25435, + "quartile": 25436, + "cooled": 25437, + "Ġnanop": 25438, + "ĠProbing": 25439, + "ĠBernard": 25440, + "uni": 25441, + "ieties": 25442, + "ĠMarket": 25443, + "osum": 25444, + "ĠMessage": 25445, + "Ġaxiom": 25446, + "cg": 25447, + "ĠMoving": 25448, + "Resolution": 25449, + "Ġadsorbent": 25450, + "Ġmultin": 25451, + "Ġineffective": 25452, + "propag": 25453, + "hardt": 25454, + "Saharan": 25455, + "Wil": 25456, + "ĠIvan": 25457, + "irubin": 25458, + "Ġtrabec": 25459, + "alli": 25460, + "ĠCDCl": 25461, + "Ġsew": 25462, + "ĠIss": 25463, + "Ġaggression": 25464, + "ĠJuan": 25465, + "Ġdispersions": 25466, + "Ġauxin": 25467, + "FET": 25468, + "lp": 25469, + "reach": 25470, + "ĠPGE": 25471, + "chestr": 25472, + "Ġlecture": 25473, + "ĠDonald": 25474, + "slip": 25475, + "ĠHbA": 25476, + "ĠSecure": 25477, + "ĠBeh": 25478, + "Ġdamages": 25479, + "WH": 25480, + "alkyl": 25481, + "Ha": 25482, + "ĠThanks": 25483, + "Ġsensitization": 25484, + "Ġwaterm": 25485, + "Ġtwins": 25486, + "Ġcultivar": 25487, + "Ġzeolite": 25488, + "Variable": 25489, + "ĠBent": 25490, + "Ġantisense": 25491, + "ĠHansen": 25492, + "repreneur": 25493, + "ĠSNe": 25494, + "ĠEMG": 25495, + "Ġreacted": 25496, + "Ġoverflow": 25497, + "Ġformalin": 25498, + "ĠUsually": 25499, + "olybden": 25500, + "Ġacad": 25501, + "ATURE": 25502, + "Ġwaveguides": 25503, + "Ġchunk": 25504, + "Ġmodifies": 25505, + "Ġeryt": 25506, + "ĠZhong": 25507, + "Ġgranule": 25508, + "Ġcs": 25509, + "ĠGrade": 25510, + "Ġlandmarks": 25511, + "uristic": 25512, + "Ġamines": 25513, + "ĠIntrinsic": 25514, + "Ġerroneous": 25515, + "Ġlockdown": 25516, + "ypti": 25517, + "Child": 25518, + "Ġuniversities": 25519, + "Ġparasit": 25520, + "Ġignition": 25521, + "Tim": 25522, + "araj": 25523, + "ravel": 25524, + "ĠLands": 25525, + "ĠCircular": 25526, + "Ġrotate": 25527, + "Patients": 25528, + "ĠWB": 25529, + "Ġmyelin": 25530, + "ĠWeiss": 25531, + "Ġdipolar": 25532, + "Ġfollicle": 25533, + "ĠWatson": 25534, + "ĠIncor": 25535, + "Ġfoundations": 25536, + "ĠPip": 25537, + "Ġpressing": 25538, + "Ġforbidden": 25539, + "avan": 25540, + "ĠmAb": 25541, + "union": 25542, + "ĠFresh": 25543, + "ĠCorp": 25544, + "floxacin": 25545, + "coordinate": 25546, + "Ġshunt": 25547, + "Ġconstituted": 25548, + "aniline": 25549, + "Ġtweets": 25550, + "ĠChow": 25551, + "Ġmobilization": 25552, + "zyk": 25553, + "EST": 25554, + "neigh": 25555, + "ĠMeng": 25556, + "ĠResNet": 25557, + "ĠJet": 25558, + "Ġluminous": 25559, + "Ġstressors": 25560, + "does": 25561, + "trifluoromethyl": 25562, + "Ġconcert": 25563, + "ĠChoice": 25564, + "phim": 25565, + "alcoholic": 25566, + "ochem": 25567, + "iltered": 25568, + "Ġpredictable": 25569, + "Ġtran": 25570, + "ĠPra": 25571, + "Ġvalves": 25572, + "Ġautonomy": 25573, + "regulate": 25574, + "ĠBeach": 25575, + "ĠOntology": 25576, + "Ġisofl": 25577, + "Ġquoted": 25578, + "ĠLex": 25579, + "thy": 25580, + "Ġcomplaints": 25581, + "ĠTrees": 25582, + "Ġopposing": 25583, + "ĠAcceler": 25584, + "contrast": 25585, + "Ġcompeted": 25586, + "OE": 25587, + "ĠRoche": 25588, + "issance": 25589, + "Ġpeace": 25590, + "ĠAim": 25591, + "Ġinfertility": 25592, + "ĠAntarctica": 25593, + "thien": 25594, + "Summ": 25595, + "Ġjudgments": 25596, + "amides": 25597, + "Ġspill": 25598, + "Ġhereafter": 25599, + "ĠConstit": 25600, + "computer": 25601, + "Ġbegun": 25602, + "ocentric": 25603, + "Ġpumps": 25604, + "medium": 25605, + "chol": 25606, + "metallic": 25607, + "Ġflares": 25608, + "Ġpetroleum": 25609, + "Ġwithd": 25610, + "ĠTheatre": 25611, + "Ġunlabeled": 25612, + "Ġregularized": 25613, + "osteric": 25614, + "ĠPFS": 25615, + "Ġunem": 25616, + "Ġpresently": 25617, + "Ġbuffered": 25618, + "affinity": 25619, + "ĠDemographic": 25620, + "ĠKondo": 25621, + "Ġcenturies": 25622, + "Ġmigratory": 25623, + "arynx": 25624, + "Associated": 25625, + "anilino": 25626, + "grown": 25627, + "ĠExecutive": 25628, + "ĠEk": 25629, + "ĠHemat": 25630, + "ĠPlayer": 25631, + "ĠCHD": 25632, + "flex": 25633, + "ĠSever": 25634, + "altham": 25635, + "impro": 25636, + "anet": 25637, + "ocyst": 25638, + "ĠAster": 25639, + "COL": 25640, + "ĠSimilarity": 25641, + "ĠHoward": 25642, + "Ġmulticast": 25643, + "ĠEnsemble": 25644, + "ìĹ": 25645, + "olys": 25646, + "ĠGenomics": 25647, + "Ġresonators": 25648, + "Ġfistula": 25649, + "onen": 25650, + "users": 25651, + "Ġhypo": 25652, + "rogens": 25653, + "Ġmedal": 25654, + "ĠMIP": 25655, + "Ġvoltam": 25656, + "Ġappreciated": 25657, + "ĠPé": 25658, + "ĠGaia": 25659, + "Ġbuckling": 25660, + "Ġcongruence": 25661, + "furyl": 25662, + "ĠEpstein": 25663, + "Ġcascades": 25664, + "gold": 25665, + "Ġanhyd": 25666, + "Ġgraduated": 25667, + "Memory": 25668, + "ĠIndustry": 25669, + "ĠSchneider": 25670, + "Ġemployee": 25671, + "ĠCorn": 25672, + "MAC": 25673, + "rove": 25674, + "ropod": 25675, + "service": 25676, + "ĠOxidation": 25677, + "Ġenumeration": 25678, + "mad": 25679, + "ĠClose": 25680, + "ĠModular": 25681, + "Ġprogeny": 25682, + "Ġgt": 25683, + "reading": 25684, + "ĠIndic": 25685, + "opathologic": 25686, + "ĠPFNGL": 25687, + "XL": 25688, + "cis": 25689, + "ĠMike": 25690, + "ĠBBB": 25691, + "ĠExtreme": 25692, + "ĠChoose": 25693, + "Ġhorizontally": 25694, + "ĠASSERT": 25695, + "Ġglucocorticoid": 25696, + "Bay": 25697, + "Ġpdf": 25698, + "Ġcontainers": 25699, + "ĠLOC": 25700, + "ĠYield": 25701, + "oprote": 25702, + "Ġfructose": 25703, + "ĠICC": 25704, + "Ġdecid": 25705, + "rimidine": 25706, + "Ġfragmented": 25707, + "Ġisomorphisms": 25708, + "м": 25709, + "Ġintegrates": 25710, + "Ġfibration": 25711, + "ĠâĬ¤": 25712, + "Ġxenograft": 25713, + "nucleon": 25714, + "ĠCSP": 25715, + "Ġsut": 25716, + "ĠSpir": 25717, + "Ġdissoci": 25718, + "ĠTBI": 25719, + "ĠForces": 25720, + "Ġhypersurface": 25721, + "Ġmyosin": 25722, + "ĠQueensland": 25723, + "Neg": 25724, + "ĠURL": 25725, + "bind": 25726, + "Applied": 25727, + "ĠDob": 25728, + "ĠKE": 25729, + "Ġmemor": 25730, + "ĠArabic": 25731, + "ĠLateral": 25732, + "ĠStart": 25733, + "nose": 25734, + "tibility": 25735, + "asters": 25736, + "Ġusability": 25737, + "Ġincenti": 25738, + "ymn": 25739, + "ĠAnalytic": 25740, + "Pet": 25741, + "ĠMask": 25742, + "World": 25743, + "brand": 25744, + "Ġeliminates": 25745, + "Ġmerit": 25746, + "ĠPhilippines": 25747, + "ĠBCL": 25748, + "ĠOri": 25749, + "Ġparadigms": 25750, + "ĠInters": 25751, + "rizona": 25752, + "Ġconception": 25753, + "Ġrelied": 25754, + "ĠJoe": 25755, + "ĠApple": 25756, + "Ġlightweight": 25757, + "mortem": 25758, + "olig": 25759, + "Ġviz": 25760, + "Ġstones": 25761, + "Ġkeywords": 25762, + "ĠSecretary": 25763, + "TN": 25764, + "older": 25765, + "ĠIntestinal": 25766, + "Ġpossessed": 25767, + "Ġmonotonicity": 25768, + "emitting": 25769, + "ĠDefining": 25770, + "ĠParticularly": 25771, + "Ġautomorphisms": 25772, + "Ġerythemat": 25773, + "ĠWaters": 25774, + "ĠCyclic": 25775, + "maximal": 25776, + "xty": 25777, + "ĠSad": 25778, + "Ġuranium": 25779, + "Ġhypothalamus": 25780, + "ĠSUMO": 25781, + "Ġdealt": 25782, + "Ġkits": 25783, + "Ġpainting": 25784, + "ĠSier": 25785, + "chool": 25786, + "ODO": 25787, + "surfaces": 25788, + "ĠPneum": 25789, + "organized": 25790, + "ĠCPT": 25791, + "Ġinsoluble": 25792, + "ĠCoherent": 25793, + "Ġrecessive": 25794, + "Ġbivariate": 25795, + "Ġedit": 25796, + "Ġnationwide": 25797, + "MODE": 25798, + "chest": 25799, + "ĠSLC": 25800, + "Ġintraperitoneal": 25801, + "ĠDisordered": 25802, + "Ġinsufficiency": 25803, + "iev": 25804, + "iazole": 25805, + "Write": 25806, + "ĠDATA": 25807, + "toral": 25808, + "Ġqualities": 25809, + "Ġpossessing": 25810, + "ĠMats": 25811, + "Ġretinopathy": 25812, + "ĠBK": 25813, + "Ġnovelty": 25814, + "ceans": 25815, + "Ġreserves": 25816, + "ĠNADH": 25817, + "Ġisotherm": 25818, + "Ġsoldiers": 25819, + "pb": 25820, + "iterpen": 25821, + "ĠAgents": 25822, + "zu": 25823, + "Ġunwanted": 25824, + "Ġhyperparameters": 25825, + "ecan": 25826, + "ĠSES": 25827, + "ĠFG": 25828, + "ĠNavig": 25829, + "Ġtriangulation": 25830, + "Ġnetworking": 25831, + "Ġpolystyrene": 25832, + "Ġinductively": 25833, + "breviations": 25834, + "Ġneuromuscular": 25835, + "ĠLinux": 25836, + "studied": 25837, + "ĠBeing": 25838, + "Ġdeficiencies": 25839, + "ĠMatrices": 25840, + "Ġwearing": 25841, + "Ġhadrons": 25842, + "amyl": 25843, + "Ġdiscourse": 25844, + "ochlor": 25845, + "ĠMelan": 25846, + "ĠLan": 25847, + "VL": 25848, + "Ġmunicipal": 25849, + "Ġenrollment": 25850, + "ĠSymmetric": 25851, + "Ġdisciplines": 25852, + "ĠBaron": 25853, + "Research": 25854, + "Ġmagnetite": 25855, + "omide": 25856, + "polarization": 25857, + "leys": 25858, + "Ġseemingly": 25859, + "hepatic": 25860, + "Ġclo": 25861, + "ĠQuatern": 25862, + "Ġcompetit": 25863, + "Requ": 25864, + "gauge": 25865, + "Ġhydrochloride": 25866, + "dropout": 25867, + "panel": 25868, + "Ġaspirin": 25869, + "ĠRUN": 25870, + "Ġribbon": 25871, + "Ġinaccurate": 25872, + "ĠPall": 25873, + "ducers": 25874, + "Throughout": 25875, + "Ġcellul": 25876, + "Ġsuspect": 25877, + "Ġallelic": 25878, + "Ġsnake": 25879, + "ordinated": 25880, + "ĠAutophagy": 25881, + "Ġeig": 25882, + "Ġrif": 25883, + "ĠKennedy": 25884, + "Ġbottle": 25885, + "ĠYouth": 25886, + "awed": 25887, + "linearity": 25888, + "uker": 25889, + "ĠOX": 25890, + "extension": 25891, + "Ġward": 25892, + "ĠComplexes": 25893, + "Ġbiosensor": 25894, + "ĠCartan": 25895, + "dn": 25896, + "Ġsonic": 25897, + "Ġindexing": 25898, + "Ġdv": 25899, + "reliable": 25900, + "pk": 25901, + "RENT": 25902, + "Ġtanks": 25903, + "ĠHet": 25904, + "ĠWing": 25905, + "ĠCuO": 25906, + "Ġprintf": 25907, + "Ġluminosities": 25908, + "course": 25909, + "Ġscram": 25910, + "Ġsampler": 25911, + "Ġmultipliers": 25912, + "Default": 25913, + "odil": 25914, + "intr": 25915, + "sequencing": 25916, + "Ġtransmissions": 25917, + "ĠWhit": 25918, + "ĠOpportun": 25919, + "Ġinternally": 25920, + "Ġacknowledges": 25921, + "ĠEdition": 25922, + "Ġarteri": 25923, + "Ġalbedo": 25924, + "ĠNucleotide": 25925, + "Ġyes": 25926, + "ĠRelativistic": 25927, + "Ġvotes": 25928, + "ĠFormulation": 25929, + "uscitation": 25930, + "Ġconcurrently": 25931, + "uin": 25932, + "Ġnoninvasive": 25933, + "Ġprimates": 25934, + "μl": 25935, + "Ġsubtropical": 25936, + "gun": 25937, + "ĠSoutheast": 25938, + "ön": 25939, + "Ġequator": 25940, + "Ġworkshop": 25941, + "Ġschist": 25942, + "undant": 25943, + "ĠMODIS": 25944, + "tar": 25945, + "Ġaeg": 25946, + "Ġplotting": 25947, + "ĠDET": 25948, + "Manager": 25949, + "uned": 25950, + "oxifen": 25951, + "ĠInver": 25952, + "Ġxanth": 25953, + "ĠServer": 25954, + "Ġstretched": 25955, + "Global": 25956, + "Core": 25957, + "ĠWeber": 25958, + "yard": 25959, + "Ġexplores": 25960, + "ĠBiography": 25961, + "SNP": 25962, + "ĠNeutrino": 25963, + "Ġkilometres": 25964, + "Ġcommutes": 25965, + "Ġacceptability": 25966, + "ĠAntibodies": 25967, + "icol": 25968, + "Ġmuseum": 25969, + "Ġdenit": 25970, + "Ġextrapolated": 25971, + "Ġacetylcholine": 25972, + "Token": 25973, + "ĠFock": 25974, + "onde": 25975, + "Ġdiscriminative": 25976, + "ĠMant": 25977, + "Ġessence": 25978, + "celand": 25979, + "ĠChair": 25980, + "Ġintegrative": 25981, + "ĠSPD": 25982, + "henium": 25983, + "arbonate": 25984, + "BASE": 25985, + "regulates": 25986, + "patch": 25987, + "Ġdib": 25988, + "Ġantisymmetric": 25989, + "Ġwearable": 25990, + "Edge": 25991, + "rets": 25992, + "Ġperceive": 25993, + "ĠMagnesium": 25994, + "adows": 25995, + "Ġdisposal": 25996, + "Ġairport": 25997, + "ausea": 25998, + "fits": 25999, + "Ġnecro": 26000, + "ĠSIN": 26001, + "ĠDuc": 26002, + "ĠReading": 26003, + "bys": 26004, + "Ġreflective": 26005, + "his": 26006, + "ometries": 26007, + "Ġvirial": 26008, + "Ġartificially": 26009, + "children": 26010, + "ĠUltrasound": 26011, + "VIEW": 26012, + "Ġsculpt": 26013, + "Ġsurf": 26014, + "Ġsexually": 26015, + "Ġgeometrically": 26016, + "Ġdivisors": 26017, + "Ġinitiatives": 26018, + "acci": 26019, + "Ġkeratinocytes": 26020, + "aR": 26021, + "arot": 26022, + "Ġïĥ¨": 26023, + "computed": 26024, + "ĠTCGA": 26025, + "psychological": 26026, + "ĠMAN": 26027, + "ĠMPC": 26028, + "ticing": 26029, + "limiting": 26030, + "amins": 26031, + "Ġsurfactants": 26032, + "ĠSerb": 26033, + "Ġrhythms": 26034, + "ĠRouting": 26035, + "wang": 26036, + "Ġmicrostructures": 26037, + "ophytes": 26038, + "Ġanalgesic": 26039, + "FOR": 26040, + "qual": 26041, + "Ġpublish": 26042, + "ĠTiming": 26043, + "porous": 26044, + "ranging": 26045, + "eron": 26046, + "ĠZi": 26047, + "ĠMarshall": 26048, + "Width": 26049, + "Ġisomers": 26050, + "Ġ·": 26051, + "phenoxy": 26052, + "Ġureth": 26053, + "robl": 26054, + "Ġmentioning": 26055, + "ozyme": 26056, + "ĠLud": 26057, + "Ġopposition": 26058, + "Ġabandoned": 26059, + "Ġroutines": 26060, + "ĠHST": 26061, + "mutex": 26062, + "coded": 26063, + "eating": 26064, + "tert": 26065, + "emiconductor": 26066, + "dw": 26067, + "Ġbaryons": 26068, + "Ġleucine": 26069, + "otron": 26070, + "Ġendos": 26071, + "Ġreproduces": 26072, + "Ġanalgesia": 26073, + "Ġimmunoreactivity": 26074, + "ĠPrep": 26075, + "ĠGarcÃŃa": 26076, + "Ġincoherent": 26077, + "aned": 26078, + "lepton": 26079, + "andra": 26080, + "ulae": 26081, + "ĠHidden": 26082, + "FV": 26083, + "Ġgeneralizes": 26084, + "ĠStevens": 26085, + "ĠFoster": 26086, + "Ġfreshly": 26087, + "Ġhf": 26088, + "Denote": 26089, + "oes": 26090, + "ĠDin": 26091, + "Ġdetox": 26092, + "Ġdecoupled": 26093, + "Ġseparations": 26094, + "ucleotide": 26095, + "Ġelectrophysiological": 26096, + "ĠBALB": 26097, + "QTL": 26098, + "ĠACh": 26099, + "ĠRele": 26100, + "quez": 26101, + "MnO": 26102, + "ectures": 26103, + "Ġischa": 26104, + "Ġinsulators": 26105, + "cellulose": 26106, + "ĠFLAG": 26107, + "ombic": 26108, + "ĠUsed": 26109, + "jiang": 26110, + "expansion": 26111, + "ĠRepeat": 26112, + "ĠReserve": 26113, + "abelian": 26114, + "ĠHunting": 26115, + "GRO": 26116, + "lyte": 26117, + "ĠBark": 26118, + "Ġcreative": 26119, + "Ġbend": 26120, + "elerated": 26121, + "dish": 26122, + "Ġhighway": 26123, + "Ġcrossings": 26124, + "just": 26125, + "ono": 26126, + "ullivan": 26127, + "ĠDead": 26128, + "Ġtradeoff": 26129, + "eon": 26130, + "ogical": 26131, + "experiment": 26132, + "Ġconfers": 26133, + "ĠDot": 26134, + "Ġcoils": 26135, + "Ġaxion": 26136, + "ĠIRS": 26137, + "ĠÅ©": 26138, + "Ġglacier": 26139, + "ĠMoscow": 26140, + "ĠSpringer": 26141, + "Ġinvis": 26142, + "ĠArnold": 26143, + "University": 26144, + "attern": 26145, + "peror": 26146, + "ĠLimits": 26147, + "Ġincompatible": 26148, + "rather": 26149, + "ĠTes": 26150, + "Ġfailing": 26151, + "Ġthickening": 26152, + "Ġestradiol": 26153, + "asse": 26154, + "Ġnecessit": 26155, + "Ġsacrificed": 26156, + "ĠSear": 26157, + "ĠNorthe": 26158, + "raisebox": 26159, + "ĠSlow": 26160, + "ĠMunic": 26161, + "Ġlearner": 26162, + "igenic": 26163, + "Ġdermatitis": 26164, + "uten": 26165, + "Ġdeer": 26166, + "Ġhistamine": 26167, + "Lat": 26168, + "Mal": 26169, + "illy": 26170, + "Ġgeochemical": 26171, + "Ġspermatozoa": 26172, + "Ġvinyl": 26173, + "emet": 26174, + "Ġeffectors": 26175, + "ĠEncyclopedia": 26176, + "Ġordinal": 26177, + "Ġcontroversy": 26178, + "ĠPerspectives": 26179, + "oviruses": 26180, + "marked": 26181, + "ĠSPE": 26182, + "ĠNutri": 26183, + "Ġadhere": 26184, + "ĠHighway": 26185, + "Ġdistillation": 26186, + "MRT": 26187, + "pletion": 26188, + "Ġannihil": 26189, + "Ġwavefunction": 26190, + "Ġconfigured": 26191, + "Ġmethionine": 26192, + "Low": 26193, + "sensor": 26194, + "ĠSnow": 26195, + "Sample": 26196, + "Ġdefinitely": 26197, + "ĠMeth": 26198, + "rypt": 26199, + "Ġprompted": 26200, + "Ġmonolith": 26201, + "ĠEnvironments": 26202, + "tm": 26203, + "ĠCOD": 26204, + "oris": 26205, + "equations": 26206, + "âĺĨ": 26207, + "ĠNeighbor": 26208, + "Ġimagine": 26209, + "ĠUsers": 26210, + "ĠCamera": 26211, + "ĠModification": 26212, + "ĠAttacks": 26213, + "Ġinhalation": 26214, + "áº": 26215, + "Ġventil": 26216, + "ĠNU": 26217, + "ĠContrast": 26218, + "Ġconfining": 26219, + "Service": 26220, + "Wallis": 26221, + "ĠATR": 26222, + "Ġsubduction": 26223, + "Ġïģ¢": 26224, + "Ġtitration": 26225, + "Roche": 26226, + "viv": 26227, + "Ġbears": 26228, + "bola": 26229, + "Ġblinded": 26230, + "measures": 26231, + "ĠStack": 26232, + "occurrence": 26233, + "Ġpermeation": 26234, + "lar": 26235, + "eptors": 26236, + "ĠDIF": 26237, + "corrhiz": 26238, + "ĠVisc": 26239, + "figurable": 26240, + "Ġscheduler": 26241, + "Ġoccasions": 26242, + "amboo": 26243, + "Ġamp": 26244, + "gain": 26245, + "ĠCit": 26246, + "Ġpreceded": 26247, + "Ġtactile": 26248, + "Ġïĥ¦": 26249, + "generic": 26250, + "Ġretrograde": 26251, + "Ġfans": 26252, + "Ġfisher": 26253, + "Ġlights": 26254, + "eeper": 26255, + "Ġundesirable": 26256, + "wald": 26257, + "embol": 26258, + "Ġwrist": 26259, + "Ġauthorized": 26260, + "Ġchondrocytes": 26261, + "ĠEPA": 26262, + "neu": 26263, + "ĠOperations": 26264, + "Ġcheap": 26265, + "Ġanionic": 26266, + "ĠOregon": 26267, + "cot": 26268, + "reason": 26269, + "existence": 26270, + "ĠFinancial": 26271, + "olybdenum": 26272, + "cus": 26273, + "ĠNON": 26274, + "Ġlocked": 26275, + "Bit": 26276, + "Sil": 26277, + "mixing": 26278, + "ĠSites": 26279, + "aproteobacteria": 26280, + "ĠInner": 26281, + "Ġcarc": 26282, + "Ġbiotic": 26283, + "ĠFlag": 26284, + "Ġmagic": 26285, + "kinetic": 26286, + "icted": 26287, + "Ġbulb": 26288, + "supset": 26289, + "pez": 26290, + "derivative": 26291, + "ĠeIF": 26292, + "ĠRough": 26293, + "directional": 26294, + "exit": 26295, + "axy": 26296, + "xtures": 26297, + "phimurium": 26298, + "ĠTFs": 26299, + "athin": 26300, + "Ġorch": 26301, + "Ġspectro": 26302, + "ductase": 26303, + "quinolin": 26304, + "Ġgrasp": 26305, + "Ġparsing": 26306, + "Ġdifficile": 26307, + "ĠLDH": 26308, + "ĠJupiter": 26309, + "ĠFIF": 26310, + "ĠPrize": 26311, + "Ġintentions": 26312, + "session": 26313, + "powered": 26314, + "ĠBam": 26315, + "phasic": 26316, + "Ġignoring": 26317, + "ĠRichardson": 26318, + "principles": 26319, + "Ġofficially": 26320, + "Ct": 26321, + "Ġincon": 26322, + "ĠRegulates": 26323, + "Ġmisc": 26324, + "ĠEZ": 26325, + "Ġsynonym": 26326, + "Ġunfolding": 26327, + "ĠDEC": 26328, + "ĠRX": 26329, + "PDF": 26330, + "Ġbranes": 26331, + "typically": 26332, + "Ġcages": 26333, + "ifolia": 26334, + "ugu": 26335, + "ollen": 26336, + "Ġtablet": 26337, + "ĠSah": 26338, + "ĠPVD": 26339, + "Ġalert": 26340, + "Ġformerly": 26341, + "ĠKRAS": 26342, + "sun": 26343, + "Ġdeacetyl": 26344, + "Mer": 26345, + "Ġskewed": 26346, + "ĠPleistocene": 26347, + "ĠBetter": 26348, + "ĠHud": 26349, + "ĠBrook": 26350, + "Ġpts": 26351, + "ĠHU": 26352, + "omo": 26353, + "agrass": 26354, + "Ġenvironmentally": 26355, + "Ġhonest": 26356, + "ĠNine": 26357, + "Ġpigments": 26358, + "links": 26359, + "ĠTOP": 26360, + "ĠCytoplasm": 26361, + "Gib": 26362, + "Ġaccessing": 26363, + "mias": 26364, + "Ġexplosive": 26365, + "Ġreside": 26366, + "artan": 26367, + "Ġtransitional": 26368, + "Ġunprecedented": 26369, + "Ġrom": 26370, + "ĠTNFα": 26371, + "Ġprecipitated": 26372, + "Ġtie": 26373, + "ISS": 26374, + "Ġthicker": 26375, + "ĠLatent": 26376, + "ĠValueError": 26377, + "dq": 26378, + "dma": 26379, + "Ġchromatic": 26380, + "ĠSubsection": 26381, + "ĠFACS": 26382, + "Ġrenormalized": 26383, + "Prop": 26384, + "mTOR": 26385, + "ĠHCO": 26386, + "Ġoverlo": 26387, + "bsiella": 26388, + "ylobacter": 26389, + "Ġneuroimaging": 26390, + "Ġassemblage": 26391, + "Ġexpands": 26392, + "ĠîĪ": 26393, + "ĠFun": 26394, + "Ġcitation": 26395, + "IKV": 26396, + "Ġtroops": 26397, + "inistic": 26398, + "Ġcubes": 26399, + "Ġfont": 26400, + "ĠHos": 26401, + "geries": 26402, + "Ġsuccessively": 26403, + "Ġdecoherence": 26404, + "Springer": 26405, + "hin": 26406, + "atine": 26407, + "ĠâĪ¥": 26408, + "SAS": 26409, + "ét": 26410, + "ĠSediment": 26411, + "uously": 26412, + "ĠWars": 26413, + "indicated": 26414, + "Ġflask": 26415, + "AIDS": 26416, + "Ġcra": 26417, + "ĠLot": 26418, + "Ġprimal": 26419, + "Ġjustice": 26420, + "zag": 26421, + "Ġmaxillary": 26422, + "Ġgeneralizations": 26423, + "uela": 26424, + "Ġtagging": 26425, + "Ġpupil": 26426, + "Ġinexpensive": 26427, + "Ġwatch": 26428, + "ĠAMD": 26429, + "ĠFir": 26430, + "Ġneuroblastoma": 26431, + "Ġmaximizes": 26432, + "ĠObserved": 26433, + "mixture": 26434, + "Ġopportunistic": 26435, + "trial": 26436, + "ahan": 26437, + "Ġïģ¬": 26438, + "Ġcatar": 26439, + "ĠControls": 26440, + "ĠNewman": 26441, + "Ġmicrostructural": 26442, + "borns": 26443, + "Ġoxygenation": 26444, + "ĠMacro": 26445, + "ĠJak": 26446, + "plicating": 26447, + "Ġoligodend": 26448, + "Ġresorption": 26449, + "Ġdorm": 26450, + "Ġsolvers": 26451, + "ĠKruskal": 26452, + "ĠRevolution": 26453, + "ĠGastro": 26454, + "Driven": 26455, + "Ġtiter": 26456, + "Ġori": 26457, + "ĠPCL": 26458, + "Ġwetlands": 26459, + "Ġarticular": 26460, + "CCA": 26461, + "enoic": 26462, + "Ġtrick": 26463, + "operiod": 26464, + "ĠCochrane": 26465, + "aday": 26466, + "ĠCerebral": 26467, + "Ġmodulators": 26468, + "ĠSSC": 26469, + "Ġactivations": 26470, + "Ġadapting": 26471, + "ĠScalable": 26472, + "none": 26473, + "pip": 26474, + "Ġprivi": 26475, + "ĠPseudo": 26476, + "Ġdisappears": 26477, + "ĠEur": 26478, + "Ġunconstrained": 26479, + "Ġsubmit": 26480, + "Ġreputation": 26481, + "atar": 26482, + "ĠBai": 26483, + "arians": 26484, + "ĠIntracellular": 26485, + "trees": 26486, + "Ġwetting": 26487, + "ĠFrances": 26488, + "Ġeligibility": 26489, + "folder": 26490, + "ĠStaff": 26491, + "oki": 26492, + "Ġstrengthened": 26493, + "ĠCob": 26494, + "teral": 26495, + "ĠYeast": 26496, + "bye": 26497, + "decoder": 26498, + "Ġrainbow": 26499, + "perturbed": 26500, + "vc": 26501, + "Ġsupplemental": 26502, + "Ġbirths": 26503, + "WO": 26504, + "conc": 26505, + "stitution": 26506, + "hybrid": 26507, + "Ġki": 26508, + "Ġhypere": 26509, + "ĠSMA": 26510, + "formula": 26511, + "Ġundefined": 26512, + "naphth": 26513, + "Ġdeclining": 26514, + "Ġshielding": 26515, + "Yau": 26516, + "Ġrever": 26517, + "ĠWilk": 26518, + "Ġdecimal": 26519, + "HCO": 26520, + "angered": 26521, + "Ġerythrocyte": 26522, + "ĉĉĠĠĠ": 26523, + "nuclear": 26524, + "Ġabnormality": 26525, + "Pres": 26526, + "Participants": 26527, + "ĠWagner": 26528, + "Ġfibrils": 26529, + "Ġfetus": 26530, + "ĠExpress": 26531, + "request": 26532, + "minimum": 26533, + "ĠBooks": 26534, + "hetamine": 26535, + "ushes": 26536, + "ĠBach": 26537, + "ĠDOS": 26538, + "lectric": 26539, + "ĠTween": 26540, + "ĠHughes": 26541, + "Ġmartens": 26542, + "Ġnematic": 26543, + "Ġexperimentation": 26544, + "ĠParker": 26545, + "Ġepisodic": 26546, + "Ġtelem": 26547, + "ADE": 26548, + "columns": 26549, + "Ġfundamentally": 26550, + "enet": 26551, + "ĠVl": 26552, + "earth": 26553, + "Ġquantile": 26554, + "ĠReplication": 26555, + "Ġcleared": 26556, + "Energy": 26557, + "Smith": 26558, + "Ġantidepressant": 26559, + "mx": 26560, + "pmod": 26561, + "amid": 26562, + "Ġserotype": 26563, + "Ġundergraduate": 26564, + "ĠArizona": 26565, + "Ġpushed": 26566, + "ulu": 26567, + "ĠNIC": 26568, + "Ġrheological": 26569, + "omegal": 26570, + "ĠQing": 26571, + "orch": 26572, + "irmed": 26573, + "ĠQuery": 26574, + "Ġsandwich": 26575, + "Ġclinician": 26576, + "ĠElliptic": 26577, + "ĠMeh": 26578, + "DEV": 26579, + "ĠDetermining": 26580, + "alcogen": 26581, + "bench": 26582, + "azep": 26583, + "ĠMississ": 26584, + "tizing": 26585, + "ĠRBC": 26586, + "Ġofficials": 26587, + "Tag": 26588, + "kT": 26589, + "luence": 26590, + "ĠRoom": 26591, + "Ġlectin": 26592, + "bara": 26593, + "kyl": 26594, + "OND": 26595, + "ĠDose": 26596, + "Ġprism": 26597, + "Ġreductive": 26598, + "ĠSpectroscopic": 26599, + "odied": 26600, + "colone": 26601, + "ĠCONFIG": 26602, + "Ġbrittle": 26603, + "inverse": 26604, + "ĠBuff": 26605, + "ytocin": 26606, + "Ġformations": 26607, + "ĠConventional": 26608, + "prev": 26609, + "Ġferrite": 26610, + "ĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ": 26611, + "Ġadopts": 26612, + "ĠMiocene": 26613, + "management": 26614, + "ĠCRF": 26615, + "ĠHelm": 26616, + "Ġdoubled": 26617, + "ĠEFFECT": 26618, + "Ġdance": 26619, + "structions": 26620, + "rait": 26621, + "ifers": 26622, + "ellip": 26623, + "utting": 26624, + "prof": 26625, + "ĠQin": 26626, + "Ġabsc": 26627, + "Ġexploits": 26628, + "Ġcyber": 26629, + "definition": 26630, + "ĠCoronary": 26631, + "Ġdeterg": 26632, + "ĠPerception": 26633, + "ĠCurves": 26634, + "Ġnematodes": 26635, + "Ġlistening": 26636, + "Ġcatalase": 26637, + "Coll": 26638, + "ré": 26639, + "islative": 26640, + "Ġarriving": 26641, + "Ġviolating": 26642, + "д": 26643, + "hetics": 26644, + "ĠJar": 26645, + "concept": 26646, + "Ġbrush": 26647, + "immunity": 26648, + "Ġfingerprint": 26649, + "resid": 26650, + "Ġelevations": 26651, + "ockets": 26652, + "Ġcatechol": 26653, + "иÑ": 26654, + "Ġprecipitates": 26655, + "Ġsoccer": 26656, + "insulin": 26657, + "Ġpursue": 26658, + "ĠICA": 26659, + "ĠPolice": 26660, + "ĠMurphy": 26661, + "Task": 26662, + "ĠCoc": 26663, + "ĠHabit": 26664, + "ĠKP": 26665, + "Ġfloral": 26666, + "Ġhun": 26667, + "Ġhydrogenation": 26668, + "Ġspong": 26669, + "Ġchimeric": 26670, + "ĠKoch": 26671, + "gon": 26672, + "ĠSchur": 26673, + "ĠGreater": 26674, + "RX": 26675, + "Ġcing": 26676, + "ĠWaltham": 26677, + "angling": 26678, + "Ġcounties": 26679, + "Ġlamina": 26680, + "Ġcouncil": 26681, + "sort": 26682, + "ĠBarc": 26683, + "ĠDow": 26684, + "ĠZeng": 26685, + "Ġdevised": 26686, + "uitable": 26687, + "Ġmethylene": 26688, + "Ġsuperiority": 26689, + "Ġepidermis": 26690, + "Ġprag": 26691, + "ĠPED": 26692, + "threatening": 26693, + "ishi": 26694, + "Ġepsilon": 26695, + "address": 26696, + "ENTAL": 26697, + "ĠBle": 26698, + "ĠAntonio": 26699, + "oother": 26700, + "ĠAgar": 26701, + "Ġneighborhoods": 26702, + "Ġshortened": 26703, + "STATE": 26704, + "ĠSerial": 26705, + "MAR": 26706, + "OU": 26707, + "Ġencapsulation": 26708, + "ĠConsortium": 26709, + "Dr": 26710, + "profile": 26711, + "Ġemitter": 26712, + "Ġnecrotic": 26713, + "ĠAutonomous": 26714, + "ĠPhosphorylation": 26715, + "minim": 26716, + "anthin": 26717, + "ĠSph": 26718, + "ĠGur": 26719, + "dihydroxy": 26720, + "distributed": 26721, + "ĠRPMI": 26722, + "stones": 26723, + "Ġhyperfine": 26724, + "Ġislet": 26725, + "ĠSlo": 26726, + "pletely": 26727, + "Ġinactivated": 26728, + "ĠAgriculture": 26729, + "Ġtremend": 26730, + "Ġeveryone": 26731, + "omponent": 26732, + "ZnO": 26733, + "MPI": 26734, + "ĠDiamond": 26735, + "Ġ⣨": 26736, + "Cost": 26737, + "Ġdisabilities": 26738, + "inver": 26739, + "ĠCensus": 26740, + "echo": 26741, + "Ġvegetative": 26742, + "Ġwillingness": 26743, + "Ġrecap": 26744, + "ĠConstraint": 26745, + "ĠPatrick": 26746, + "Ġovert": 26747, + "Ġmoieties": 26748, + "orax": 26749, + "ippi": 26750, + "Direct": 26751, + "Ġcaries": 26752, + "Ġlocalities": 26753, + "lattices": 26754, + "ĠExploration": 26755, + "ĠAW": 26756, + "Ġlocking": 26757, + "Ġcoincident": 26758, + "Ġmultimedia": 26759, + "Ġtemporarily": 26760, + "ĠCaus": 26761, + "encia": 26762, + "Ġweathering": 26763, + "ĠHelicobacter": 26764, + "ĠThings": 26765, + "hips": 26766, + "moving": 26767, + "Ġsigmoid": 26768, + "isin": 26769, + "ĠBec": 26770, + "Ġmicrograms": 26771, + "bounds": 26772, + "ĠColumn": 26773, + "Ġcommuting": 26774, + "ĠJen": 26775, + "Ġhourly": 26776, + "MSC": 26777, + "Ġattendance": 26778, + "ĠâIJ£": 26779, + "ĠEO": 26780, + "prog": 26781, + "Ġrapamycin": 26782, + "ĠPredictors": 26783, + "ĠRetrieved": 26784, + "Ġsubspecies": 26785, + "Ġderives": 26786, + "ĠĤ": 26787, + "ĠGenerating": 26788, + "anners": 26789, + "Ġvolat": 26790, + "Ġvisiting": 26791, + "ĠCalculations": 26792, + "ña": 26793, + "Ġdesert": 26794, + "Ġexpectancy": 26795, + "BMCs": 26796, + "ĠExplo": 26797, + "Ġtravelling": 26798, + "icum": 26799, + "Ġsubdivision": 26800, + "Ġcrosslinking": 26801, + "benzoth": 26802, + "ĠTon": 26803, + "REN": 26804, + "Ġleth": 26805, + "rabbit": 26806, + "ĠAbove": 26807, + "ulted": 26808, + "Ġconstric": 26809, + "Jones": 26810, + "zhou": 26811, + "vern": 26812, + "ĠLady": 26813, + "ĠBuffer": 26814, + "ĠControlling": 26815, + "Ġmultiscale": 26816, + "nikov": 26817, + "acycl": 26818, + "Ġprosthesis": 26819, + "Af": 26820, + "ĠCorps": 26821, + "structed": 26822, + "Grid": 26823, + "inning": 26824, + "olding": 26825, + "Ġthiol": 26826, + "ikov": 26827, + "âĢ¢âĢ¢âĢ¢": 26828, + "Ġgovernments": 26829, + "rapping": 26830, + "Ġthrombocyt": 26831, + "Leg": 26832, + "RY": 26833, + "ĠIceland": 26834, + "ocycle": 26835, + "ĠMemorial": 26836, + "got": 26837, + "Ġidem": 26838, + "ĠBuild": 26839, + "olipoprotein": 26840, + "DV": 26841, + "Ġphthal": 26842, + "richment": 26843, + "ĠHaem": 26844, + "Ġanswering": 26845, + "ĠIJ": 26846, + "Ġtransgene": 26847, + "Ġrenamed": 26848, + "ĠImageJ": 26849, + "Ġcassette": 26850, + "Ġcoalescence": 26851, + "Ġcompaction": 26852, + "Ġwildlife": 26853, + "Ġwins": 26854, + "Ġsupernovae": 26855, + "enteric": 26856, + "isphere": 26857, + "Ġtracker": 26858, + "Ġevidences": 26859, + "Ġcomorbidity": 26860, + "ĠRules": 26861, + "phasing": 26862, + "ĠLangevin": 26863, + "ĠFit": 26864, + "Ġpsychiat": 26865, + "Ġbreakthrough": 26866, + "Ġcholinergic": 26867, + "ĠMetall": 26868, + "breeding": 26869, + "itinib": 26870, + "Ġsolo": 26871, + "abling": 26872, + "elief": 26873, + "oscill": 26874, + "rev": 26875, + "arya": 26876, + "Ġgoodness": 26877, + "ĠPBE": 26878, + "Ġawards": 26879, + "Ġcrani": 26880, + "Ġphotograp": 26881, + "arents": 26882, + "Ġfixes": 26883, + "rÃŃ": 26884, + "assuming": 26885, + "Ġcongruent": 26886, + "ĠMother": 26887, + "ĠNap": 26888, + "ĠProc": 26889, + "Ġcategorization": 26890, + "inch": 26891, + "ĠHorm": 26892, + "ĠInterventions": 26893, + "Ġnonequilibrium": 26894, + "Ġencrypted": 26895, + "primary": 26896, + "iens": 26897, + "lac": 26898, + "rams": 26899, + "Ġboards": 26900, + "ĠHell": 26901, + "charged": 26902, + "Ġperioperative": 26903, + "emp": 26904, + "ĠInvolvement": 26905, + "Russ": 26906, + "univers": 26907, + "ĠDJ": 26908, + "Ġdisagreement": 26909, + "Ġpert": 26910, + "Ġstroma": 26911, + "Ġcalcite": 26912, + "Ġrotary": 26913, + "Ġmethyltransferase": 26914, + "Ġancestry": 26915, + "ĠWitten": 26916, + "CRC": 26917, + "uretic": 26918, + "ophyta": 26919, + "provided": 26920, + "Ġcorrespondingly": 26921, + "bigcap": 26922, + "ĠAgilent": 26923, + "ë": 26924, + "rooms": 26925, + "Ġdisent": 26926, + "Ġdilutions": 26927, + "ĠMyel": 26928, + "Ġquasar": 26929, + "Ġtilted": 26930, + "Ġinternalization": 26931, + "ĠPrivate": 26932, + "ĠFriedman": 26933, + "Ġseventh": 26934, + "ĠClosed": 26935, + "CTC": 26936, + "gren": 26937, + "ĠColombia": 26938, + "odel": 26939, + "Ġpolitics": 26940, + "ĠMSSM": 26941, + "Ġmate": 26942, + "Ġcommod": 26943, + "ĠRus": 26944, + "Ġanesthetized": 26945, + "together": 26946, + "ĠBCS": 26947, + "ewski": 26948, + "romagnet": 26949, + "ĠCun": 26950, + "Ġcurative": 26951, + "Ġimputation": 26952, + "Ġcarbide": 26953, + "DFT": 26954, + "nsic": 26955, + "bee": 26956, + "Ġsplen": 26957, + "ĠMaryland": 26958, + "Ġoligonucleotide": 26959, + "ĠVeget": 26960, + "buffered": 26961, + "National": 26962, + "letic": 26963, + "ĠSyl": 26964, + "Ġseab": 26965, + "ardial": 26966, + "Ġportray": 26967, + "Ġaberrations": 26968, + "Ġstorms": 26969, + "ĠShan": 26970, + "ĠGenBank": 26971, + "issa": 26972, + "Ġcet": 26973, + "Ġbench": 26974, + "ĠRecommendations": 26975, + "Ġtriples": 26976, + "Ġïĥ¥": 26977, + "ĠNeuros": 26978, + "Ġdiscom": 26979, + "season": 26980, + "ĠExec": 26981, + "changing": 26982, + "Ġarrives": 26983, + "Hash": 26984, + "mRNA": 26985, + "Ġfric": 26986, + "asa": 26987, + "obia": 26988, + "Ġpostsynaptic": 26989, + "optimizer": 26990, + "ĠClouds": 26991, + "Ġhypersensitivity": 26992, + "vacc": 26993, + "ĠSig": 26994, + "philic": 26995, + "Ġgrounded": 26996, + "ĠWan": 26997, + "ĠCalabi": 26998, + "ĠMachines": 26999, + "Ġaxisymmetric": 27000, + "ĠSteve": 27001, + "Ġpulled": 27002, + "ĠExcel": 27003, + "Ġdiamonds": 27004, + "KR": 27005, + "West": 27006, + "ĠDest": 27007, + "Ġannular": 27008, + "Ġarchive": 27009, + "Ġparenchyma": 27010, + "ĠEH": 27011, + "ópez": 27012, + "Ġunpublished": 27013, + "Ġsoutheastern": 27014, + "Ġnests": 27015, + "dimensions": 27016, + "latitude": 27017, + "Orig": 27018, + "eced": 27019, + "ĠDraw": 27020, + "redshift": 27021, + "Ġamyl": 27022, + "omyelitis": 27023, + "Why": 27024, + "caro": 27025, + "iq": 27026, + "assess": 27027, + "ĠContin": 27028, + "Ġchirality": 27029, + "matical": 27030, + "Ġchaperone": 27031, + "Ġendometriosis": 27032, + "relu": 27033, + "Ġconverged": 27034, + "broad": 27035, + "ĠIterative": 27036, + "Ġvasculature": 27037, + "fund": 27038, + "ĠFly": 27039, + "Ġantigenic": 27040, + "Ġmeningitis": 27041, + "Ġentails": 27042, + "horn": 27043, + "Ġlocomotor": 27044, + "izard": 27045, + "Ġuneven": 27046, + "parity": 27047, + "packet": 27048, + "tubulin": 27049, + "Ġsewage": 27050, + "Ġdecentralized": 27051, + "Ġgrafted": 27052, + "Ġsep": 27053, + "ĠExtensive": 27054, + "Ġspline": 27055, + "quer": 27056, + "archit": 27057, + "Ġprimate": 27058, + "Ġïģ±": 27059, + "pyrimidin": 27060, + "ĠSAP": 27061, + "Ġunderlie": 27062, + "Ġanalyzes": 27063, + "ĠCCA": 27064, + "recogn": 27065, + "IPT": 27066, + "Different": 27067, + "ĠTEST": 27068, + "Ġunfavorable": 27069, + "edic": 27070, + "ĠAbnormal": 27071, + "pyrimidine": 27072, + "urine": 27073, + "embedded": 27074, + "varies": 27075, + "otropin": 27076, + "Ġsemen": 27077, + "Ġtransmittance": 27078, + "Ġabras": 27079, + "Ġó¸Ģł": 27080, + "Ġtriglyceride": 27081, + "bundle": 27082, + "ĠYb": 27083, + "ĠCarr": 27084, + "Ġnaming": 27085, + "Weight": 27086, + "Ġcondensates": 27087, + "Ġnos": 27088, + "amard": 27089, + "vertices": 27090, + "ELS": 27091, + "idone": 27092, + "Ġcontest": 27093, + "Ġheading": 27094, + "ĠGalerkin": 27095, + "GV": 27096, + "ĠGli": 27097, + "Ġfermented": 27098, + "Ġbilingual": 27099, + "Ġticks": 27100, + "Ġkary": 27101, + "ragal": 27102, + "ĠAber": 27103, + "ĠYouTube": 27104, + "UCTURE": 27105, + "branch": 27106, + "ر": 27107, + "ĠFH": 27108, + "onoi": 27109, + "imotor": 27110, + "Ġverifying": 27111, + "ĠConceptual": 27112, + "ĠDeterminants": 27113, + "urm": 27114, + "uronic": 27115, + "ĠKau": 27116, + "ĠConformal": 27117, + "Ġdropping": 27118, + "ĠFlows": 27119, + "gluon": 27120, + "again": 27121, + "ĠMRSA": 27122, + "warf": 27123, + "Ġemphasizes": 27124, + "Entry": 27125, + "ĠASP": 27126, + "resol": 27127, + "ventricular": 27128, + "ĠâĨĶ": 27129, + "Ġoverexpressing": 27130, + "omegalovirus": 27131, + "inoc": 27132, + "SCO": 27133, + "ĠPARP": 27134, + "ĠSchul": 27135, + "ĠCamb": 27136, + "ĠPod": 27137, + "ĠPun": 27138, + "ĠCompetition": 27139, + "ĠGATA": 27140, + "Ġmoon": 27141, + "Ġputs": 27142, + "angiogenic": 27143, + "ĠRepublican": 27144, + "ĠUbiqu": 27145, + "eys": 27146, + "ĠGong": 27147, + "arger": 27148, + "ĠIntermediate": 27149, + "Ġinterpolated": 27150, + "Ġenlargement": 27151, + "Ġinstruct": 27152, + "Ġrc": 27153, + "dioxo": 27154, + "eye": 27155, + "ĠCarls": 27156, + "ĠMeasured": 27157, + "ircles": 27158, + "ĠRaf": 27159, + "Ġarb": 27160, + "examples": 27161, + "Mi": 27162, + "ĠStern": 27163, + "ĠFK": 27164, + "Ġmillisecond": 27165, + "ĠIRF": 27166, + "ĠEpithelial": 27167, + "edicine": 27168, + "eles": 27169, + "sig": 27170, + "âĪĢ": 27171, + "ĠWiener": 27172, + "bauer": 27173, + "ouses": 27174, + "Ġcoloured": 27175, + "ĠIncrease": 27176, + "Ġtriglycerides": 27177, + "Ġaegypti": 27178, + "ĠNumerous": 27179, + "Ġretardation": 27180, + "Ġintercellular": 27181, + "ĠKlebsiella": 27182, + "ĠDra": 27183, + "ĠDIC": 27184, + "ĠThreshold": 27185, + "rainment": 27186, + "Ġreproducing": 27187, + "Ġulcers": 27188, + "Ġarousal": 27189, + "ĠHills": 27190, + "Ġcalves": 27191, + "ĠReservoir": 27192, + "ĠRadar": 27193, + "Ġpsychosis": 27194, + "ĠFORM": 27195, + "duration": 27196, + "ĠAcademic": 27197, + "catal": 27198, + "olla": 27199, + "olol": 27200, + "ĠCron": 27201, + "iko": 27202, + "Ġextremes": 27203, + "ĠTrypan": 27204, + "Ġbip": 27205, + "Ġalginate": 27206, + "ĠHoch": 27207, + "ĠBennett": 27208, + "ĠHippocamp": 27209, + "ĠGeological": 27210, + "Nevertheless": 27211, + "ĠHes": 27212, + "ĠAdding": 27213, + "Ġexternally": 27214, + "Ġslag": 27215, + "Ġteach": 27216, + "ĠStanley": 27217, + "controller": 27218, + "ĠUnits": 27219, + "ĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ": 27220, + "Ġaerodynamic": 27221, + "ovalent": 27222, + "cube": 27223, + "ÅŁ": 27224, + "require": 27225, + "romolecules": 27226, + "irteen": 27227, + "Ġclauses": 27228, + "Ġdefeat": 27229, + "policy": 27230, + "Ġfaithful": 27231, + "Ġpq": 27232, + "ĠTanaka": 27233, + "ĠEver": 27234, + "Ġunpredict": 27235, + "auty": 27236, + "ĠGALAX": 27237, + "Ġtide": 27238, + "ĠFiltering": 27239, + "Ġeuthan": 27240, + "merce": 27241, + "DEX": 27242, + "Ġnesting": 27243, + "DN": 27244, + "IRT": 27245, + "ĠThr": 27246, + "tissue": 27247, + "Ġpalae": 27248, + "Ï©": 27249, + "Ġdilated": 27250, + "Ġpinning": 27251, + "Rb": 27252, + "ĠSap": 27253, + "ragonal": 27254, + "ĠSPR": 27255, + "ĠDial": 27256, + "Ġacupuncture": 27257, + "diameter": 27258, + "ĠPCB": 27259, + "Parameters": 27260, + "ĠProfiles": 27261, + "transfected": 27262, + "liter": 27263, + "ĠRights": 27264, + "Ġcontributor": 27265, + "ĠCorrel": 27266, + "Ġregressions": 27267, + "Ġsegmental": 27268, + "Shape": 27269, + "IAN": 27270, + "ecom": 27271, + "comings": 27272, + "Ġhemorrhagic": 27273, + "opos": 27274, + "Ġrefraction": 27275, + "PFC": 27276, + "proj": 27277, + "ovo": 27278, + "ĠDerived": 27279, + "Ġundirected": 27280, + "Ġlos": 27281, + "Ġengaging": 27282, + "cans": 27283, + "Ġdestructive": 27284, + "Pop": 27285, + "Ġmakers": 27286, + "ĠWor": 27287, + "ĠAreas": 27288, + "vasion": 27289, + "Ġparaformaldehyde": 27290, + "abinoid": 27291, + "cpy": 27292, + "proxim": 27293, + "Ġenamel": 27294, + "Ġpaediatric": 27295, + "ĠChildhood": 27296, + "Ġpectin": 27297, + "ofilm": 27298, + "Ġcarboxylic": 27299, + "Ġausten": 27300, + "Ġunequal": 27301, + "ĠCountry": 27302, + "Ġiterated": 27303, + "Ġflanking": 27304, + "Ġtraction": 27305, + "anson": 27306, + "iscus": 27307, + "ĠDavies": 27308, + "raham": 27309, + "terozoic": 27310, + "ĠBrass": 27311, + "Oc": 27312, + "Ġunification": 27313, + "meter": 27314, + "ĠNeon": 27315, + "building": 27316, + "icting": 27317, + "Ġjustification": 27318, + "Prior": 27319, + "Ġfirms": 27320, + "Ġeducated": 27321, + "Ġintersecting": 27322, + "Ġboosting": 27323, + "Pass": 27324, + "member": 27325, + "contains": 27326, + "rano": 27327, + "relax": 27328, + "ĠCollaborative": 27329, + "Ġpx": 27330, + "Ġseeding": 27331, + "cripts": 27332, + "inez": 27333, + "omeres": 27334, + "Ġsiblings": 27335, + "anging": 27336, + "fert": 27337, + "Ġrecovering": 27338, + "pure": 27339, + "Ġsd": 27340, + "ĠVul": 27341, + "pedance": 27342, + "Ġfighting": 27343, + "Super": 27344, + "ĠIto": 27345, + "Ġperimeter": 27346, + "ĠInhibitors": 27347, + "electrode": 27348, + "enabled": 27349, + "fb": 27350, + "ĠPCs": 27351, + "Ġnausea": 27352, + "ĠConversion": 27353, + "Ġsla": 27354, + "Ġinvertebrates": 27355, + "ĠBrian": 27356, + "Ġcontiguous": 27357, + "ĠACKNOWLEDGM": 27358, + "urface": 27359, + "Ġcoars": 27360, + "ĠLeh": 27361, + "ĠCompression": 27362, + "cycles": 27363, + "Ġsinh": 27364, + "ĠOccup": 27365, + "strength": 27366, + "Ġconstr": 27367, + "Ġpesticide": 27368, + "Ġbisp": 27369, + "ĠTn": 27370, + "Ġparentheses": 27371, + "degrad": 27372, + "Ġhyperglycemia": 27373, + "PW": 27374, + "kj": 27375, + "ecological": 27376, + "Ġthy": 27377, + "Ġeleg": 27378, + "ĠSynaptic": 27379, + "scaled": 27380, + "tity": 27381, + "Ġequity": 27382, + "Ġblockchain": 27383, + "ĠLithium": 27384, + "Ġspark": 27385, + "Ġentitled": 27386, + "Ġconventions": 27387, + "Argument": 27388, + "Ġretail": 27389, + "Ġneoplastic": 27390, + "Ġdamped": 27391, + "ĠSurveillance": 27392, + "ĠAnna": 27393, + "Ġspacetimes": 27394, + "inges": 27395, + "ahashi": 27396, + "ĠInfections": 27397, + "Ġneglecting": 27398, + "Ġevaporated": 27399, + "vastatin": 27400, + "Ġgh": 27401, + "ĠNLP": 27402, + "Ġphones": 27403, + "Ġlifted": 27404, + "Ġdivisible": 27405, + "Ġdurability": 27406, + "osited": 27407, + "Ġexcitability": 27408, + "Ġbuoyancy": 27409, + "Ġuncontrolled": 27410, + "bran": 27411, + "ĠPhe": 27412, + "Ġimmunocomp": 27413, + "Ġeventual": 27414, + "Ġclassroom": 27415, + "Ġmicrographs": 27416, + "Ġrecharge": 27417, + "ettes": 27418, + "ĠDiver": 27419, + "ĠDall": 27420, + "Ġmetac": 27421, + "Ġneuroendocrine": 27422, + "topology": 27423, + "ĠHawking": 27424, + "omson": 27425, + "ĠHarry": 27426, + "mouth": 27427, + "Ġdeciding": 27428, + "Ġuncovered": 27429, + "Ġgolden": 27430, + "ĠCastle": 27431, + "Ġfiducial": 27432, + "Aware": 27433, + "ĠGan": 27434, + "erahertz": 27435, + "ĠSaturn": 27436, + "LN": 27437, + "Unit": 27438, + "ĥĹ": 27439, + "Ġbinder": 27440, + "INFO": 27441, + "ĠTemper": 27442, + "ipel": 27443, + "Ġnumerator": 27444, + "Ġwebsites": 27445, + "Ġthreatened": 27446, + "Ġremnants": 27447, + "ĠFinnish": 27448, + "hof": 27449, + "media": 27450, + "concentration": 27451, + "ĠReed": 27452, + "ĠLeishmania": 27453, + "Ġmultifunctional": 27454, + "racy": 27455, + "Ġdistribute": 27456, + "ĠDecay": 27457, + "Ġgrinding": 27458, + "Loss": 27459, + "MPL": 27460, + "ĠLakes": 27461, + "ĠQR": 27462, + "ĠStructured": 27463, + "ĠMalaria": 27464, + "Ġflavonoid": 27465, + "Ġtowns": 27466, + "opia": 27467, + "ĠVec": 27468, + "othy": 27469, + "Ġsingles": 27470, + "Ġpenetrate": 27471, + "ĠPig": 27472, + "ieved": 27473, + "Ġderivations": 27474, + "Ġdiscomfort": 27475, + "afenib": 27476, + "ĠLegendre": 27477, + "ĠPax": 27478, + "ĠMX": 27479, + "ĠExtrem": 27480, + "ĠForeign": 27481, + "ĠCourse": 27482, + "ĠHit": 27483, + "vage": 27484, + "Ġclique": 27485, + "Ġcompensatory": 27486, + "User": 27487, + "Ġdraws": 27488, + "ĠProtective": 27489, + "Ġallocate": 27490, + "ĠPant": 27491, + "Ġdash": 27492, + "Ġparal": 27493, + "ĠCirculating": 27494, + "ĠHistone": 27495, + "ĠÅ«": 27496, + "Ġprojec": 27497, + "ĠAAA": 27498, + "ĠPMS": 27499, + "glacial": 27500, + "ĠMeeting": 27501, + "ĠAntibiotic": 27502, + "ategorical": 27503, + "Ġattenuate": 27504, + "Power": 27505, + "owicz": 27506, + "ĠDefault": 27507, + "Ġmarsh": 27508, + "plasm": 27509, + "ĠPathology": 27510, + "ĠEf": 27511, + "Lys": 27512, + "flies": 27513, + "Ġinterviewed": 27514, + "ĠQA": 27515, + "Ġimpuls": 27516, + "Ġpapillary": 27517, + "dR": 27518, + "uh": 27519, + "ĠJing": 27520, + "Ġrescaled": 27521, + "efficiency": 27522, + "Ġef": 27523, + "ĠEisen": 27524, + "Ġattacked": 27525, + "Ġopto": 27526, + "Ġspeculated": 27527, + "haz": 27528, + "Ġideally": 27529, + "ymenoptera": 27530, + "Ġlr": 27531, + "ĠIz": 27532, + "resource": 27533, + "ĠFacility": 27534, + "ĠAcquisition": 27535, + "Ġpostural": 27536, + "autiful": 27537, + "Ġgingival": 27538, + "Ġpertaining": 27539, + "ĠExtra": 27540, + "ĠProgramme": 27541, + "hesus": 27542, + "fermion": 27543, + "Ġsteadily": 27544, + "Ġterminus": 27545, + "Parser": 27546, + "ĠInclusion": 27547, + "ĠWuhan": 27548, + "Ġrepetitions": 27549, + "done": 27550, + "ĠCep": 27551, + "Ġunstructured": 27552, + "ĠCollectively": 27553, + "Ġsettling": 27554, + "Ġjaw": 27555, + "ĠUni": 27556, + "Ġrestoring": 27557, + "urtles": 27558, + "Full": 27559, + "Ġdynamo": 27560, + "IGO": 27561, + "ĠBAT": 27562, + "ová": 27563, + "venues": 27564, + "ĠPerhaps": 27565, + "sensing": 27566, + "ĠIschem": 27567, + "odemographic": 27568, + "Ss": 27569, + "ĠLund": 27570, + "Ġelite": 27571, + "protocol": 27572, + "ĠChristopher": 27573, + "basic": 27574, + "Ġpuber": 27575, + "Ġmagnetism": 27576, + "vars": 27577, + "inducing": 27578, + "Ġdated": 27579, + "Ġenemy": 27580, + "ĠStop": 27581, + "social": 27582, + "ĠdÏĦ": 27583, + "ĠBun": 27584, + "Small": 27585, + "purpose": 27586, + "Ġhunting": 27587, + "CPU": 27588, + "ĠJunior": 27589, + "REL": 27590, + "Ġcontractile": 27591, + "Ġsilicone": 27592, + "adrenergic": 27593, + "bz": 27594, + "Ġfus": 27595, + "ifted": 27596, + "sep": 27597, + "âĪĴâĪŀ": 27598, + "Ġdrum": 27599, + "----------": 27600, + "ĠTregs": 27601, + "itarian": 27602, + "century": 27603, + "âĬ¥": 27604, + "Numer": 27605, + "ĠBenz": 27606, + "Ġcommunicating": 27607, + "Ġpaternal": 27608, + "ĠFGFR": 27609, + "ĠâĤ¬": 27610, + "Ġdeviate": 27611, + "fre": 27612, + "Ġmolten": 27613, + "Ġstandardization": 27614, + "Ġfunctionalities": 27615, + "ĠPaulo": 27616, + "Ġbucket": 27617, + "ĠConcentrations": 27618, + "ĠKum": 27619, + "Ġmimicking": 27620, + "Drop": 27621, + "zoa": 27622, + "ĠNuclei": 27623, + "brack": 27624, + "ecolor": 27625, + "Ġcarn": 27626, + "Ġveterinary": 27627, + "Ġchemotherapeutic": 27628, + "Ġferment": 27629, + "lasting": 27630, + "ĠRogers": 27631, + "ieri": 27632, + "Ġconverters": 27633, + "ĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ": 27634, + "ĠRepair": 27635, + "Europe": 27636, + "TIME": 27637, + "Ġties": 27638, + "ĠPIN": 27639, + "Ġtribut": 27640, + "Ġhomogenization": 27641, + "excitation": 27642, + "atization": 27643, + "ĠRash": 27644, + "Ġprecession": 27645, + "ás": 27646, + "Ġspiking": 27647, + "ĠGrassmann": 27648, + "minister": 27649, + "Ġfactorial": 27650, + "ĠDeut": 27651, + "sampled": 27652, + "Ġeukaryotes": 27653, + "overlapping": 27654, + "agglut": 27655, + "Ġprescribing": 27656, + "Ġcro": 27657, + "omechanical": 27658, + "iza": 27659, + "ĠManufact": 27660, + "native": 27661, + "ursive": 27662, + "ĠIssues": 27663, + "Ġstreptomycin": 27664, + "endi": 27665, + "ĠSpr": 27666, + "ceq": 27667, + "arginine": 27668, + "ixon": 27669, + "ĠFoundations": 27670, + "Single": 27671, + "Ġoxal": 27672, + "Ġhydrate": 27673, + "Iterator": 27674, + "kii": 27675, + "aminated": 27676, + "Ġsprings": 27677, + "oln": 27678, + "ĠSetup": 27679, + "Ġripening": 27680, + "Ġtheoretic": 27681, + "Ġcfg": 27682, + "μL": 27683, + "Gordon": 27684, + "SK": 27685, + "Ġnations": 27686, + "Query": 27687, + "Ùħ": 27688, + "Ġfores": 27689, + "requencies": 27690, + "ĠPharmaceutical": 27691, + "ĠAllocation": 27692, + "otypical": 27693, + "ĠPilot": 27694, + "thora": 27695, + "ĠVand": 27696, + "Ġsyringe": 27697, + "ĠRAP": 27698, + "rometric": 27699, + "Ġïģ´": 27700, + "Ġcitations": 27701, + "would": 27702, + "Ġnortheastern": 27703, + "comparison": 27704, + "locus": 27705, + "ethe": 27706, + "ĠKB": 27707, + "Ġhomologs": 27708, + "Ġencephalitis": 27709, + "Ġzig": 27710, + "Ġincentive": 27711, + "Ġconfidential": 27712, + "Ġvestibular": 27713, + "ĠOTUs": 27714, + "Ġsynovial": 27715, + "ĠRelativity": 27716, + "Ġsubdivided": 27717, + "chez": 27718, + "Ġlikewise": 27719, + "ĠPDMS": 27720, + "ĠÅł": 27721, + "Ġsocieties": 27722, + "ocyanate": 27723, + "gia": 27724, + "Ġlocalize": 27725, + "Ġlactation": 27726, + "Ġnodule": 27727, + "ĠCOR": 27728, + "Ġharboring": 27729, + "ĠEQU": 27730, + "harvest": 27731, + "Ġbandgap": 27732, + "rk": 27733, + "Ġresistor": 27734, + "Ġye": 27735, + "ĠAsymmetric": 27736, + "Ġpropagators": 27737, + "Ġdiagnosing": 27738, + "ĠAffairs": 27739, + "Ġejecta": 27740, + "Ġisomer": 27741, + "Ġix": 27742, + "Ġfoliation": 27743, + "Ġcapacitors": 27744, + "Ġcad": 27745, + "ĠNeutroph": 27746, + "pliance": 27747, + "Ġcompressible": 27748, + "ĠHunter": 27749, + "ĠMZ": 27750, + "ĠWeib": 27751, + "Ġnoncoding": 27752, + "Ġmountains": 27753, + "Ġadvertising": 27754, + "alez": 27755, + "bright": 27756, + "limsup": 27757, + "Ci": 27758, + "ĠNev": 27759, + "ĠStrains": 27760, + "ostomy": 27761, + "opal": 27762, + "Ġconcatenated": 27763, + "ĠPerf": 27764, + "CHO": 27765, + "Ġturtles": 27766, + "ĠFra": 27767, + "Ġallogeneic": 27768, + "Ġunsuccessful": 27769, + "YM": 27770, + "erver": 27771, + "Ġcuc": 27772, + "Ġfires": 27773, + "chart": 27774, + "Ġinterrupted": 27775, + "Ġdecides": 27776, + "Ġauction": 27777, + "ĠUntil": 27778, + "ĠATG": 27779, + "Ġdiam": 27780, + "magnitude": 27781, + "Ġdl": 27782, + "Vertex": 27783, + "mont": 27784, + "Ġfemtosecond": 27785, + "Params": 27786, + "Ġlysate": 27787, + "ishers": 27788, + "ĠPAT": 27789, + "ĠKev": 27790, + "ĠKnock": 27791, + "Ġgroove": 27792, + "Lu": 27793, + "ĠJohann": 27794, + "Ġreplica": 27795, + "ĠMATERIALS": 27796, + "Ġlots": 27797, + "Ġgenerically": 27798, + "ĠAltered": 27799, + "ĠIdentity": 27800, + "Ġunfolded": 27801, + "CES": 27802, + "ingular": 27803, + "ĠFraction": 27804, + "ĠProliferation": 27805, + "ĠVienna": 27806, + "acia": 27807, + "pless": 27808, + "ĠSevent": 27809, + "Ġturbines": 27810, + "lysine": 27811, + "Ġperoxis": 27812, + "ARP": 27813, + "ĠEpis": 27814, + "ĠSYBR": 27815, + "Builder": 27816, + "Ġspherically": 27817, + "Ġdefend": 27818, + "Performance": 27819, + "Ġmortar": 27820, + "ĠConcepts": 27821, + "works": 27822, + "Ġreinforce": 27823, + "á¹": 27824, + "Ġcus": 27825, + "ĠCIF": 27826, + "ĠAgricultural": 27827, + "crystalline": 27828, + "rish": 27829, + "Ġreferenced": 27830, + "Ġactress": 27831, + "Ġboundedness": 27832, + "SiC": 27833, + "Ġâ": 27834, + "Ġjack": 27835, + "Ġterminate": 27836, + "ĠJA": 27837, + "ĠKrish": 27838, + "MMP": 27839, + "kx": 27840, + "ĠPSR": 27841, + "endl": 27842, + "WHO": 27843, + "ĠSão": 27844, + "ĠCultural": 27845, + "ĠEh": 27846, + "ulis": 27847, + "vik": 27848, + "prises": 27849, + "ixel": 27850, + "ĠMetrics": 27851, + "Ġdiscontinuities": 27852, + "ĠUne": 27853, + "SCR": 27854, + "Ġprojecting": 27855, + "ĠOriginal": 27856, + "ĠHumans": 27857, + "transcriptional": 27858, + "HK": 27859, + "ĠJain": 27860, + "atisfaction": 27861, + "mesenchymal": 27862, + "Ġpyramid": 27863, + "Ġascorbic": 27864, + "game": 27865, + "Ġnoun": 27866, + "otoxins": 27867, + "peptide": 27868, + "Ġglassy": 27869, + "Ġtalking": 27870, + "Dem": 27871, + "ĠSchro": 27872, + "ĠAssumptions": 27873, + "Ġðx": 27874, + "Ġaneurysms": 27875, + "MASS": 27876, + "ĠHou": 27877, + "exposure": 27878, + "ĠLLC": 27879, + "Ġnoises": 27880, + "CTG": 27881, + "ĠElementary": 27882, + "flip": 27883, + "Ġdysp": 27884, + "Ġmessenger": 27885, + "ĠImportant": 27886, + "Ġimposes": 27887, + "Ġorganelles": 27888, + "assertEqual": 27889, + "Ġjustif": 27890, + "ucine": 27891, + "Ġformic": 27892, + "ormalization": 27893, + "ĠRadial": 27894, + "ĠCurve": 27895, + "ĠCrohn": 27896, + "Ġbrowser": 27897, + "Ġeffusion": 27898, + "Ġhandles": 27899, + "varsigma": 27900, + "Ġspecialists": 27901, + "Ġpainful": 27902, + "Ġerythematosus": 27903, + "Ġfen": 27904, + "nitrophenyl": 27905, + "Ġlegacy": 27906, + "ĠQDs": 27907, + "rapper": 27908, + "Ġmonotherapy": 27909, + "ĠBelt": 27910, + "ZZ": 27911, + "Ġsintered": 27912, + "enedi": 27913, + "Hb": 27914, + "tv": 27915, + "ĠNas": 27916, + "ovis": 27917, + "Ġmucin": 27918, + "Ġaccelerates": 27919, + "Ġacquiring": 27920, + "luc": 27921, + "Ġdilaton": 27922, + "ĠPitts": 27923, + "Ġequivariant": 27924, + "ĠLyman": 27925, + "ĠYa": 27926, + "Ġprogressed": 27927, + "ĠAfterwards": 27928, + "ĠCHAR": 27929, + "Don": 27930, + "Ġhistologic": 27931, + "Ġcircuitry": 27932, + "pene": 27933, + "opres": 27934, + "ĠStefan": 27935, + "Ġsemiclassical": 27936, + "mund": 27937, + "ĠWaste": 27938, + "BQ": 27939, + "Ġadiponectin": 27940, + "Ġunseen": 27941, + "Ġbiomechanical": 27942, + "Ġhazardous": 27943, + "ructive": 27944, + "xyl": 27945, + "opf": 27946, + "Ġprion": 27947, + "ĠInfinite": 27948, + "Ġtracers": 27949, + "ĠHarrison": 27950, + "Ġfibrinogen": 27951, + "Ġhydrolys": 27952, + "Ġislets": 27953, + "Ġparallelism": 27954, + "Spect": 27955, + "Ġimperative": 27956, + "Ġcured": 27957, + "ĠDSB": 27958, + "idefinite": 27959, + "icker": 27960, + "Ġdivergences": 27961, + "ĠShapiro": 27962, + "abd": 27963, + "ĠLum": 27964, + "ĠVD": 27965, + "Ġfisheries": 27966, + "geon": 27967, + "copenia": 27968, + "ĠClay": 27969, + "Ġmaximized": 27970, + "ĠGrey": 27971, + "ĠBatch": 27972, + "Ġinfest": 27973, + "Ġample": 27974, + "Ġestate": 27975, + "ĠSupreme": 27976, + "AO": 27977, + "isia": 27978, + "ĠSmad": 27979, + "Carlo": 27980, + "ĠSubst": 27981, + "Ġmonoidal": 27982, + "Ġnumeric": 27983, + "Plot": 27984, + "Ġdystrophy": 27985, + "hypertensive": 27986, + "Ġstool": 27987, + "alsy": 27988, + "Ġcheese": 27989, + "nih": 27990, + "Ġbought": 27991, + "ĠSQ": 27992, + "Ġclues": 27993, + "Ġmeiotic": 27994, + "Ġgoats": 27995, + "ĠGTPase": 27996, + "Ġrescaling": 27997, + "NUM": 27998, + "icing": 27999, + "ĠÄĢ": 28000, + "Ġpretty": 28001, + "ligand": 28002, + "English": 28003, + "ĠIntelligent": 28004, + "Every": 28005, + "ĠPolitical": 28006, + "enton": 28007, + "Ġpassages": 28008, + "ĠRemarks": 28009, + "sb": 28010, + "Network": 28011, + "ĠLRR": 28012, + "Ġcurl": 28013, + "ursion": 28014, + "ĠAver": 28015, + "ĠGLP": 28016, + "heren": 28017, + "atan": 28018, + "ICENSE": 28019, + "Ġlatex": 28020, + "EMI": 28021, + "quasi": 28022, + "ĠOm": 28023, + "Ġreviewing": 28024, + "Background": 28025, + "Ġsom": 28026, + "Ġsnapshots": 28027, + "brow": 28028, + "who": 28029, + "ĠTail": 28030, + "ĠMSM": 28031, + "ĠGm": 28032, + "Ġphi": 28033, + "rency": 28034, + "separated": 28035, + "Ġgig": 28036, + "osides": 28037, + "Ġpean": 28038, + "Ġappealing": 28039, + "PU": 28040, + "nk": 28041, + "Ġquer": 28042, + "ĠCharg": 28043, + "ĠMolecules": 28044, + "localization": 28045, + "Idx": 28046, + "lap": 28047, + "ĠTax": 28048, + "ĠExponential": 28049, + "ĠInhibitor": 28050, + "ĠBiomedical": 28051, + "urethane": 28052, + "lerene": 28053, + "rogenesis": 28054, + "ĠLai": 28055, + "ĠAggregation": 28056, + "ĠCaCl": 28057, + "Ġsensible": 28058, + "Ġconjunc": 28059, + "paper": 28060, + "ĠCovid": 28061, + "ĠProcedures": 28062, + "Ġknew": 28063, + "Ġsetae": 28064, + "ĠAlle": 28065, + "ĠExcept": 28066, + "Ġpresynaptic": 28067, + "flower": 28068, + "Ġultrasonography": 28069, + "Ġentertain": 28070, + "iors": 28071, + "ĠEry": 28072, + "ĠInteger": 28073, + "Ġrepressor": 28074, + "Ġlaterally": 28075, + "Ġcomplemented": 28076, + "TAG": 28077, + "ĠAround": 28078, + "ĠLister": 28079, + "bitrary": 28080, + "backward": 28081, + "MeV": 28082, + "Ġwhisk": 28083, + "AMs": 28084, + "ĠBulk": 28085, + "Ġquiver": 28086, + "Ġdamaging": 28087, + "ĠQuantifying": 28088, + "Ġsuprem": 28089, + "tel": 28090, + "Ġtear": 28091, + "oters": 28092, + "vidin": 28093, + "Ġtubules": 28094, + "Ġipsilateral": 28095, + "isive": 28096, + "Ġsuitably": 28097, + "riel": 28098, + "Ġtuber": 28099, + "Ġfavors": 28100, + "Ġcentim": 28101, + "Ġtransversal": 28102, + "ĠCHO": 28103, + "Ġtrimester": 28104, + "CAC": 28105, + "cognitive": 28106, + "ĠUTC": 28107, + "pute": 28108, + "Ġmidline": 28109, + "amers": 28110, + "evaluation": 28111, + "Dav": 28112, + "Ġbags": 28113, + "timer": 28114, + "Ġshortcomings": 28115, + "ĠErd": 28116, + "Ġdiscriminator": 28117, + "Ant": 28118, + "sizes": 28119, + "Ġbist": 28120, + "ingual": 28121, + "ĠCategory": 28122, + "Ġpulsars": 28123, + "ĠSchwartz": 28124, + "ĠDrop": 28125, + "Sequence": 28126, + "Ġtann": 28127, + "ĠSymptoms": 28128, + "Dict": 28129, + "ĠBlu": 28130, + "Supplemental": 28131, + "Ġdisabled": 28132, + "ĠKoz": 28133, + "Ġinvoked": 28134, + "ĠCQ": 28135, + "ĠConnectivity": 28136, + "Ġtelescopes": 28137, + "oso": 28138, + "Ġphytochemical": 28139, + "Ġorthogonality": 28140, + "Ġinvisible": 28141, + "ĠSCF": 28142, + "ĠAvoid": 28143, + "ĠHus": 28144, + "micron": 28145, + "aternity": 28146, + "Project": 28147, + "Ġadvancing": 28148, + "ĠLorentzian": 28149, + "Sa": 28150, + "tÃŀ": 28151, + "ĠUP": 28152, + "Ġarts": 28153, + "Ġzer": 28154, + "asket": 28155, + "Ġappeal": 28156, + "nick": 28157, + "ĠCloning": 28158, + "Ġswap": 28159, + "Ġphospholipids": 28160, + "bg": 28161, + "othel": 28162, + "asco": 28163, + "Track": 28164, + "Ġsubmanifold": 28165, + "Offset": 28166, + "ĠBird": 28167, + "problems": 28168, + "DCs": 28169, + "Ġdow": 28170, + "Ġdeionized": 28171, + "Ġsubclass": 28172, + "Ġpublishing": 28173, + "ĠCarter": 28174, + "Ġsynergy": 28175, + "Ġweakened": 28176, + "ĠGlas": 28177, + "ĠPie": 28178, + "henko": 28179, + "Ġsetups": 28180, + "ĠBernstein": 28181, + "Ġÿ": 28182, + "ĠShu": 28183, + "ĠChanging": 28184, + "osov": 28185, + "ĠMeteor": 28186, + "inth": 28187, + "rah": 28188, + "paramet": 28189, + "rena": 28190, + "Ġnewborns": 28191, + "ische": 28192, + "rotating": 28193, + "Ġconfident": 28194, + "fac": 28195, + "ĠTerr": 28196, + "Ġlinewidth": 28197, + "ICP": 28198, + "thony": 28199, + "Ġlanes": 28200, + "Ġsmoother": 28201, + "mony": 28202, + "ĠCNNs": 28203, + "Port": 28204, + "Ġtransiently": 28205, + "Ġsurgeries": 28206, + "Ġsubmerged": 28207, + "Ġpuncture": 28208, + "Ġdichlor": 28209, + "Ġsystematics": 28210, + "Ġcontigs": 28211, + "Ġresiding": 28212, + "BW": 28213, + "EO": 28214, + "Gold": 28215, + "ionate": 28216, + "vocab": 28217, + "dW": 28218, + "STAR": 28219, + "ĠPLC": 28220, + "athi": 28221, + "ĠInfectious": 28222, + "Light": 28223, + "á»": 28224, + "ĠRal": 28225, + "Ġpropagates": 28226, + "ĠLikelihood": 28227, + "hill": 28228, + "curl": 28229, + "checkpoint": 28230, + "rax": 28231, + "Ġvancomycin": 28232, + "ĠUSD": 28233, + "opheles": 28234, + "Ġfiltr": 28235, + "Ġstoichiometry": 28236, + "âĶĢâĶĢ": 28237, + "ĠNad": 28238, + "accessible": 28239, + "Ġtoy": 28240, + "Ġnude": 28241, + "ĠSut": 28242, + "essential": 28243, + "ĠOL": 28244, + "Ġpertin": 28245, + "Ġrecur": 28246, + "Ġcapill": 28247, + "Ġcomputable": 28248, + "Ġsuction": 28249, + "Ġsoftening": 28250, + "ĠESI": 28251, + "Ġmonitors": 28252, + "Ġpyridine": 28253, + "ĠSensors": 28254, + "ĠCombinatorial": 28255, + "atta": 28256, + "ĠAMS": 28257, + "ĠDul": 28258, + "pleteness": 28259, + "Eth": 28260, + "Ġû": 28261, + "Ġexcised": 28262, + "ĠDiabetic": 28263, + "ĠIowa": 28264, + "Ġimmunostaining": 28265, + "Ġillnesses": 28266, + "Ġenumer": 28267, + "ĠIranian": 28268, + "Ġthumb": 28269, + "orphisms": 28270, + "Ġlegitimate": 28271, + "lg": 28272, + "ĠSVD": 28273, + "Ġdesk": 28274, + "Format": 28275, + "Bon": 28276, + "Ġgarden": 28277, + "Ġinterpersonal": 28278, + "Ġelbow": 28279, + "ĠDemonstr": 28280, + "Ġnonspecific": 28281, + "Ferm": 28282, + "ivalently": 28283, + "phthalene": 28284, + "ARGET": 28285, + "Valid": 28286, + "Ġsunlight": 28287, + "Ġrescued": 28288, + "DAR": 28289, + "ĠInvariant": 28290, + "Ġidle": 28291, + "Ġalkaloids": 28292, + "scales": 28293, + "ses": 28294, + "obicity": 28295, + "beat": 28296, + "Ġcentrifugal": 28297, + "analytical": 28298, + "pv": 28299, + "Ġtutorial": 28300, + "ĠNation": 28301, + "generator": 28302, + "Ġcollisional": 28303, + "ĠCME": 28304, + "Ġscrap": 28305, + "ĠQSO": 28306, + "Ġwax": 28307, + "ĠScenario": 28308, + "Ġminimizer": 28309, + "ĠMDPI": 28310, + "Ġprostaglandin": 28311, + "olites": 28312, + "ocysteine": 28313, + "Ġcompactification": 28314, + "Ġfrailty": 28315, + "opsin": 28316, + "Ġjunior": 28317, + "loud": 28318, + "Ġtitled": 28319, + "Ġeconomically": 28320, + "thiophene": 28321, + "ĠInvestigating": 28322, + "ĠEsp": 28323, + "Ġelusive": 28324, + "Ġmalware": 28325, + "ĠTHP": 28326, + "imidazole": 28327, + "Ġretains": 28328, + "ĠMIR": 28329, + "ffl": 28330, + "jac": 28331, + "ĠPART": 28332, + "ĠDCM": 28333, + "transport": 28334, + "MAPK": 28335, + "Problem": 28336, + "Su": 28337, + "Ġdelim": 28338, + "Ġpsychometric": 28339, + "vitably": 28340, + "Ġhypergeometric": 28341, + "Ġuterus": 28342, + "Ġanaesthesia": 28343, + "ĠAvenue": 28344, + "Ġmeanings": 28345, + "Ġrapidity": 28346, + "Ġdendrites": 28347, + "grain": 28348, + "ĠNile": 28349, + "Ġfacies": 28350, + "Ġpipelines": 28351, + "ĠCampylobacter": 28352, + "ĠMembers": 28353, + "benzoate": 28354, + "Request": 28355, + "Ġpk": 28356, + "Ġrefused": 28357, + "caus": 28358, + "ĠSay": 28359, + "lane": 28360, + "ĠPSO": 28361, + "Ġgathering": 28362, + "Ġrefriger": 28363, + "RCC": 28364, + "Ġfibronectin": 28365, + "help": 28366, + "ĠIntensity": 28367, + "CLC": 28368, + "Que": 28369, + "elly": 28370, + "Ġilluminated": 28371, + "Ġpedestrian": 28372, + "ĠMercury": 28373, + "Ġafforded": 28374, + "Ġpathophysiological": 28375, + "ĠNGS": 28376, + "assa": 28377, + "Ġendors": 28378, + "Ġsensation": 28379, + "Ġstreamflow": 28380, + "avin": 28381, + "ĠGABAergic": 28382, + "Ġretirement": 28383, + "Cells": 28384, + "oca": 28385, + "Ġoptimizations": 28386, + "Ġdigraph": 28387, + "ĠAutism": 28388, + "octurnal": 28389, + "oscience": 28390, + "ĠEllis": 28391, + "ĠAj": 28392, + "ĠWSN": 28393, + "Ġshooting": 28394, + "iper": 28395, + "îĦĥ": 28396, + "ĠWeather": 28397, + "Ġreceptive": 28398, + "Ġquartic": 28399, + "ocyclic": 28400, + "PATH": 28401, + "sizeof": 28402, + "Ġmelts": 28403, + "Ġdipoles": 28404, + "Ġbimodal": 28405, + "summary": 28406, + "Ġinsomnia": 28407, + "opyran": 28408, + "Ġwrapped": 28409, + "ĠJosé": 28410, + "AH": 28411, + "cia": 28412, + "Ġobeys": 28413, + "ĠKay": 28414, + "intervention": 28415, + "Ġrouter": 28416, + "ĠDrugs": 28417, + "owska": 28418, + "ĠArr": 28419, + "ĠCaptain": 28420, + "ĠTMS": 28421, + "adv": 28422, + "Ġboat": 28423, + "Ġtrusted": 28424, + "sever": 28425, + "illars": 28426, + "ĠMissouri": 28427, + "Ġequivalents": 28428, + "ĠHarvard": 28429, + "ĠClarke": 28430, + "resonant": 28431, + "rady": 28432, + "triggered": 28433, + "Ġcleft": 28434, + "Ġunic": 28435, + "Ġbrainstem": 28436, + "Ġthrombin": 28437, + "ĠFlight": 28438, + "Ġsectional": 28439, + "Ġconcatenation": 28440, + "Ġcantilever": 28441, + "eton": 28442, + "Ġdecode": 28443, + "ofacial": 28444, + "Action": 28445, + "ĠIllustration": 28446, + "vertical": 28447, + "chall": 28448, + "ĠRegistry": 28449, + "MAT": 28450, + "Ġconson": 28451, + "Ġneoadjuvant": 28452, + "ĠWistar": 28453, + "ĠImper": 28454, + "Ġaltitudes": 28455, + "Ġsubpopulation": 28456, + "ĠScene": 28457, + "tensorflow": 28458, + "slow": 28459, + "Ġhint": 28460, + "Ġbeamforming": 28461, + "ein": 28462, + "Ġimpregn": 28463, + "ĠRFID": 28464, + "ĠAnalyzing": 28465, + "ĠPent": 28466, + "ĠDNS": 28467, + "ĠGilbert": 28468, + "Ġcrater": 28469, + "Comparing": 28470, + "Ġbf": 28471, + "Ġflights": 28472, + "Ġmalnutrition": 28473, + "SMC": 28474, + "Ġerythrop": 28475, + "ĠTumors": 28476, + "Tx": 28477, + "Ġisospin": 28478, + "ĠKub": 28479, + "iking": 28480, + "Ġcorticosteroids": 28481, + "ursor": 28482, + "ĠBurg": 28483, + "inspired": 28484, + "ĠIgn": 28485, + "Ġmycel": 28486, + "prediction": 28487, + "methods": 28488, + "Copy": 28489, + "ĠRW": 28490, + "ĠKnight": 28491, + "Ġdemethyl": 28492, + "ìĦ": 28493, + "Ġcili": 28494, + "Ġbes": 28495, + "ĠEck": 28496, + "Ġdilatation": 28497, + "Ġanimation": 28498, + "abstract": 28499, + "Ġcircumvent": 28500, + "Ġinoculum": 28501, + "Seg": 28502, + "ĠCaps": 28503, + "erers": 28504, + "PLS": 28505, + "ĠPeer": 28506, + "Ġverifies": 28507, + "ategy": 28508, + "ogenetics": 28509, + "Ġoligonucleotides": 28510, + "ractical": 28511, + "Ġdiverges": 28512, + "ĠStanford": 28513, + "ĠAi": 28514, + "Ġweighing": 28515, + "Tg": 28516, + "reinfor": 28517, + "ĠAlam": 28518, + "quiry": 28519, + "ĠNob": 28520, + "Ġlinearization": 28521, + "ĠVenez": 28522, + "nexin": 28523, + "levels": 28524, + "Lip": 28525, + "ĠPatel": 28526, + "ĠMagnitude": 28527, + "etitive": 28528, + "ĠEagle": 28529, + "Ġsputum": 28530, + "ĠCOS": 28531, + "Ġincubator": 28532, + "Ul": 28533, + "ĠReceptors": 28534, + "ĠSchott": 28535, + "GCG": 28536, + "ĠZeiss": 28537, + "ĠEntanglement": 28538, + "ĠVaccine": 28539, + "orted": 28540, + "Ġnb": 28541, + "ĠSj": 28542, + "ĠMrs": 28543, + "Ġcalf": 28544, + "Ġintegrability": 28545, + "ĠPhoton": 28546, + "Ġgondii": 28547, + "ĠMIL": 28548, + "Ġaliph": 28549, + "ĠDip": 28550, + "falls": 28551, + "ctrl": 28552, + "ku": 28553, + "etent": 28554, + "plt": 28555, + "Ġpersisted": 28556, + "ĠManager": 28557, + "Ġprerequisite": 28558, + "filling": 28559, + "ĠMEA": 28560, + "Sym": 28561, + "ĠGrain": 28562, + "Ġductal": 28563, + "ĠTODO": 28564, + "Ġaffinities": 28565, + "Ġdegenerative": 28566, + "ĠFitz": 28567, + "ovar": 28568, + "ĠTriple": 28569, + "Ġdendrim": 28570, + "ĠFranklin": 28571, + "mag": 28572, + "otely": 28573, + "Ġstabilizes": 28574, + "Ġcash": 28575, + "ĠSquad": 28576, + "Ġchampion": 28577, + "PDB": 28578, + "Ġurg": 28579, + "Ġalcoholic": 28580, + "Ġtar": 28581, + "yled": 28582, + "Version": 28583, + "Ġsale": 28584, + "ĠMLP": 28585, + "outer": 28586, + "Ġsimplifying": 28587, + "ĠExtract": 28588, + "Param": 28589, + "ĠRestric": 28590, + "Ġtractable": 28591, + "ĠArchive": 28592, + "Response": 28593, + "ADDR": 28594, + "Ġcommutation": 28595, + "Rich": 28596, + "ĠAndrews": 28597, + "Ġosteoclast": 28598, + "romic": 28599, + "ĠShift": 28600, + "Ġaccelerometer": 28601, + "ĠSent": 28602, + "Ġchances": 28603, + "osting": 28604, + "Ġmethacrylate": 28605, + "Ġgluons": 28606, + "Ġôı½": 28607, + "Ġpolygons": 28608, + "ĠRCTs": 28609, + "Ġinfancy": 28610, + "Ġproceeded": 28611, + "ĠHorizontal": 28612, + "COR": 28613, + "Ġcaching": 28614, + "ĠNHS": 28615, + "phobic": 28616, + "ĠXMM": 28617, + "Ġmicrobiological": 28618, + "GMP": 28619, + "ÙĨ": 28620, + "ĠTSS": 28621, + "ĠSul": 28622, + "ĠFact": 28623, + "ĠWE": 28624, + "Ġcertainty": 28625, + "ensitivity": 28626, + "Ġdeconvolution": 28627, + "ĠGain": 28628, + "Ġblots": 28629, + "Ġseeks": 28630, + "Ġcosh": 28631, + "ennessee": 28632, + "Ġslave": 28633, + "ĠTran": 28634, + "Ġtranspose": 28635, + "reated": 28636, + "Ġshading": 28637, + "ĠBU": 28638, + "ĠOV": 28639, + "ĠLook": 28640, + "Ġcomprehensively": 28641, + "ĠFreder": 28642, + "Handler": 28643, + "fibr": 28644, + "Ġmissense": 28645, + "targets": 28646, + "promoting": 28647, + "ĠPep": 28648, + "varpi": 28649, + "ĠHarmonic": 28650, + "ĠAIS": 28651, + "Ġmonocyt": 28652, + "Ġthinning": 28653, + "Ġpheromone": 28654, + "Water": 28655, + "anase": 28656, + "ĠSang": 28657, + "Ġsubstructure": 28658, + "wp": 28659, + "ĠKansas": 28660, + "DEBUG": 28661, + "ĠProbe": 28662, + "Ġpatterned": 28663, + "clean": 28664, + "Ġbroiler": 28665, + "odextrin": 28666, + "aided": 28667, + "oprol": 28668, + "ublin": 28669, + "inum": 28670, + "Ġanatomic": 28671, + "Ġplating": 28672, + "arro": 28673, + "ucal": 28674, + "Ġspeedup": 28675, + "Ġhaemorrh": 28676, + "eptidase": 28677, + "Ġsagittal": 28678, + "Ġintim": 28679, + "ĠFISH": 28680, + "Ġscarc": 28681, + "ATCC": 28682, + "incor": 28683, + "Ġserological": 28684, + "ente": 28685, + "Ġshale": 28686, + "Ġoverfitting": 28687, + "ĠExcess": 28688, + "ĠALP": 28689, + "Pool": 28690, + "dry": 28691, + "yu": 28692, + "ĠPMMA": 28693, + "ĠHypoxia": 28694, + "nothing": 28695, + "chestra": 28696, + "coloneqq": 28697, + "Ġbibli": 28698, + "ĠEXPECT": 28699, + "BAL": 28700, + "ethan": 28701, + "ĠâĪĺ": 28702, + "Ġjourney": 28703, + "Ġbiocompatibility": 28704, + "PAN": 28705, + "Ġbon": 28706, + "ĠRoh": 28707, + "Ġpolarisation": 28708, + "Spin": 28709, + "idences": 28710, + "ĠBCR": 28711, + "ĠHIP": 28712, + "ĠThick": 28713, + "Ġrecognizes": 28714, + "Ġsar": 28715, + "Ġamend": 28716, + "questions": 28717, + "Ġcaregiver": 28718, + "ĠMarie": 28719, + "Ġmetalloproteinase": 28720, + "Ġaldehydes": 28721, + "Ġinterneurons": 28722, + "Ġtetrahedral": 28723, + "guez": 28724, + "Ġquasiparticle": 28725, + "Ġot": 28726, + "decreasing": 28727, + "stre": 28728, + "Ġphotoperiod": 28729, + "Ġprioriti": 28730, + "Ġapo": 28731, + "Ġimmunosuppression": 28732, + "ĠPierre": 28733, + "LPS": 28734, + "Ġclumps": 28735, + "ĠPlane": 28736, + "Ġturbidity": 28737, + "Ġpollutant": 28738, + "Ġbioch": 28739, + "ĠTRE": 28740, + "Ġdesigners": 28741, + "Ġrenders": 28742, + "Ġreplaces": 28743, + "ĠPLS": 28744, + "Ġhumoral": 28745, + "Bas": 28746, + "reira": 28747, + "ĠAedes": 28748, + "vitamin": 28749, + "curves": 28750, + "ociceptive": 28751, + "Ġindisp": 28752, + "Ġoxy": 28753, + "Ġedible": 28754, + "ĠMesenchymal": 28755, + "ĠDegree": 28756, + "ž": 28757, + "ĠOak": 28758, + "ĠBhatt": 28759, + "onso": 28760, + "ĠSBP": 28761, + "ĠAux": 28762, + "Ġmartingale": 28763, + "ĠMicrobiota": 28764, + "glow": 28765, + "Ġexud": 28766, + "apolis": 28767, + "Ġsomehow": 28768, + "Ġcentred": 28769, + "Channel": 28770, + "ĠNormalized": 28771, + "ilitation": 28772, + "Ġtranscriptase": 28773, + "Ġcryo": 28774, + "predicted": 28775, + "ĠDAG": 28776, + "Ġrf": 28777, + "endor": 28778, + "INTER": 28779, + "ĠMesh": 28780, + "ĠFundament": 28781, + "ycle": 28782, + "Ġprimitives": 28783, + "radiated": 28784, + "Ġrho": 28785, + "enesulf": 28786, + "ĠFSH": 28787, + "ĠEcos": 28788, + "localized": 28789, + "Ġenterprise": 28790, + "cephalus": 28791, + "Ġcarcass": 28792, + "AY": 28793, + "ecurity": 28794, + "ĠTMD": 28795, + "Ġlb": 28796, + "ĠAeros": 28797, + "ĠMER": 28798, + "Attr": 28799, + "ĠACL": 28800, + "ĠBarb": 28801, + "cout": 28802, + "Ġdeoxy": 28803, + "atios": 28804, + "Ġpersists": 28805, + "Ġviolent": 28806, + "Abelian": 28807, + "Ġellips": 28808, + "iong": 28809, + "Ġsuccessor": 28810, + "ĠGonzález": 28811, + "living": 28812, + "ĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ": 28813, + "imentin": 28814, + "Ġcapsules": 28815, + "VIS": 28816, + "ĠPOP": 28817, + "arithmic": 28818, + "OO": 28819, + "wl": 28820, + "inoic": 28821, + "ĠCenters": 28822, + "roblasts": 28823, + "those": 28824, + "ĠMJ": 28825, + "Ġfronts": 28826, + "Ġunint": 28827, + "Ġfacile": 28828, + "coherent": 28829, + "avour": 28830, + "ceptive": 28831, + "tah": 28832, + "Ġrelatedness": 28833, + "dE": 28834, + "ungen": 28835, + "#####": 28836, + "Ġamphi": 28837, + "ĠGuy": 28838, + "stars": 28839, + "ectom": 28840, + "Ġlaying": 28841, + "Ġspider": 28842, + "ACs": 28843, + "Ġseedling": 28844, + "Ġduplicated": 28845, + "iche": 28846, + "ĠMST": 28847, + "grass": 28848, + "Ġprophylactic": 28849, + "eks": 28850, + "Ġlaryngeal": 28851, + "ĠSper": 28852, + "ĠWals": 28853, + "Ġcholec": 28854, + "ĠPlanet": 28855, + "ĠHEPES": 28856, + "Ġdiploid": 28857, + "constraint": 28858, + "Pyx": 28859, + "ACh": 28860, + "ĠCui": 28861, + "ĠShared": 28862, + "ĠCand": 28863, + "ĠGö": 28864, + "Ġdetached": 28865, + "Ġpassengers": 28866, + "Ġaliphatic": 28867, + "Ġpour": 28868, + "Ġaccesses": 28869, + "ĠWald": 28870, + "Ġdecorated": 28871, + "Ġcarotenoids": 28872, + "uestions": 28873, + "ĠImpacts": 28874, + "SAT": 28875, + "aru": 28876, + "ĠPir": 28877, + "ĠConfiguration": 28878, + "ĠCongo": 28879, + "ĠLing": 28880, + "Ġdesic": 28881, + "Ġmacrom": 28882, + "Ġlacked": 28883, + "Ġencompasses": 28884, + "Ġpumped": 28885, + "ĠForty": 28886, + "rexate": 28887, + "ifferentiated": 28888, + "Ġnoble": 28889, + "Ġradion": 28890, + "Ġimmigrants": 28891, + "Ġbiodegradable": 28892, + "Ġmigrating": 28893, + "argv": 28894, + "COM": 28895, + "ĠObservational": 28896, + "Ġcannabis": 28897, + "yama": 28898, + "Ġconcentric": 28899, + "Conn": 28900, + "talion": 28901, + "Ġresponders": 28902, + "utenant": 28903, + "ĠTrim": 28904, + "Ġcontributors": 28905, + "Ġcontracted": 28906, + "ĠXenopus": 28907, + "Ġloud": 28908, + "ĠEnhancing": 28909, + "Ġinfarct": 28910, + "Ġok": 28911, + "Ġasks": 28912, + "relin": 28913, + "Ġillustrative": 28914, + "vdash": 28915, + "dg": 28916, + "Ġfoc": 28917, + "Ġlivers": 28918, + "ĠOtt": 28919, + "ĠTSP": 28920, + "logger": 28921, + "depending": 28922, + "Ġdisproportion": 28923, + "Ġintric": 28924, + "Ġimmunized": 28925, + "varez": 28926, + "Ġsalic": 28927, + "ĠInstitutes": 28928, + "KEY": 28929, + "Ġendoscopy": 28930, + "erk": 28931, + "eliness": 28932, + "ĠSag": 28933, + "athyroid": 28934, + "Ġacidity": 28935, + "arov": 28936, + "ĠVoronoi": 28937, + "Experimental": 28938, + "Ġgently": 28939, + "Measure": 28940, + "ïĺº": 28941, + "Ġwonder": 28942, + "ĠPancreatic": 28943, + "ĠHispanic": 28944, + "ĠEug": 28945, + "reducing": 28946, + "tainment": 28947, + "Ġsurprise": 28948, + "Ġæ": 28949, + "criter": 28950, + "ĠHypertension": 28951, + "tique": 28952, + "ĠCris": 28953, + "compatible": 28954, + "enson": 28955, + "Ġdistributional": 28956, + "ĠNAT": 28957, + "widths": 28958, + "Ġisotherms": 28959, + "ĠPrad": 28960, + "Ġbiodies": 28961, + "Ġorbifold": 28962, + "ĠEOS": 28963, + "Ġatax": 28964, + "ĠBod": 28965, + "ĠNMD": 28966, + "Ġmonoxide": 28967, + "ĠUkraine": 28968, + "foli": 28969, + "ĠDro": 28970, + "Ġunavailable": 28971, + "Ġbrighter": 28972, + "âĬĹ": 28973, + "omethane": 28974, + "Ġdream": 28975, + "Ġspo": 28976, + "ĠMaur": 28977, + "Ġoccasional": 28978, + "Ġinconsistency": 28979, + "ĠTac": 28980, + "opts": 28981, + "ĠGAB": 28982, + "ĠTao": 28983, + "ĠMatthew": 28984, + "ý": 28985, + "Ġpiano": 28986, + "ĠRCC": 28987, + "ĠOK": 28988, + "ĠKul": 28989, + "methan": 28990, + "ĠPROC": 28991, + "Ġconversations": 28992, + "ĠCSI": 28993, + "angent": 28994, + "ĠXue": 28995, + "Ġgraphic": 28996, + "dening": 28997, + "healthy": 28998, + "Ġfp": 28999, + "azone": 29000, + "Ġdiscipline": 29001, + "Ġprogresses": 29002, + "Ġbamboo": 29003, + "Ġcharm": 29004, + "ĠActivated": 29005, + "ĠSharp": 29006, + "ynes": 29007, + "Ġtoolbox": 29008, + "Ġheterostructures": 29009, + "piperazin": 29010, + "Ġarose": 29011, + "ĠInterval": 29012, + "Ġstripe": 29013, + "ĠChak": 29014, + "Ġcuff": 29015, + "RESS": 29016, + "Ġnonuniform": 29017, + "Ġbeetle": 29018, + "Prec": 29019, + "zc": 29020, + "Thread": 29021, + "bet": 29022, + "Ġee": 29023, + "ĠOptional": 29024, + "Ġtroph": 29025, + "ĠPuer": 29026, + "ĠFron": 29027, + "Ġmultiplet": 29028, + "Ġcalorimetry": 29029, + "Ġmonocytogenes": 29030, + "ĠHimal": 29031, + "Ġdrill": 29032, + "AGA": 29033, + "Ġferritin": 29034, + "Ġdpi": 29035, + "ĠCarm": 29036, + "Ġgone": 29037, + "Ġunidirectional": 29038, + "Ġreminis": 29039, + "Ġadjustable": 29040, + "ĠAustin": 29041, + "SARS": 29042, + "dal": 29043, + "Ġcef": 29044, + "equivariant": 29045, + "baseline": 29046, + "Ġspinors": 29047, + "ĠPrint": 29048, + "Ġmile": 29049, + "ĠLinc": 29050, + "mutation": 29051, + "Ġmucus": 29052, + "ĠHSC": 29053, + "Ġthermod": 29054, + "Ġpaint": 29055, + "Ġdistinctly": 29056, + "athy": 29057, + "Ġpharmacy": 29058, + "ĠBulg": 29059, + "ĠGang": 29060, + "hicle": 29061, + "ogan": 29062, + "ĠJian": 29063, + "ĠIndiana": 29064, + "Ġinstanton": 29065, + "Ġpalladium": 29066, + "fiber": 29067, + "npy": 29068, + "ĠUA": 29069, + "ĠQT": 29070, + "ceptible": 29071, + "etine": 29072, + "ĠHoles": 29073, + "Ġdependences": 29074, + "Ġthresholding": 29075, + "ĠMaintenance": 29076, + "Ġparticipates": 29077, + "ĠGenomes": 29078, + "factorial": 29079, + "ĠLiber": 29080, + "ĠThermodynamic": 29081, + "Ġelective": 29082, + "ucher": 29083, + "Ġhyperther": 29084, + "Ġstomatal": 29085, + "ĠBirth": 29086, + "cholesterol": 29087, + "Ġnotch": 29088, + "Ġsymbiotic": 29089, + "Ġbusinesses": 29090, + "Ġappreciable": 29091, + "Ġspecialization": 29092, + "ár": 29093, + "actyl": 29094, + "ĠGraphPad": 29095, + "osper": 29096, + "Ġorchestr": 29097, + "Ġdihydro": 29098, + "Ġconcluding": 29099, + "CLK": 29100, + "Ġeqs": 29101, + "ĠProgression": 29102, + "Ġclubs": 29103, + "aku": 29104, + "events": 29105, + "Ġsplenic": 29106, + "Ġbunch": 29107, + "ĠTm": 29108, + "ĠMobility": 29109, + "Ġtwofold": 29110, + "Ġradially": 29111, + "LSTM": 29112, + "MH": 29113, + "ĠCoal": 29114, + "Ġfrontier": 29115, + "Jan": 29116, + "Jun": 29117, + "ĠSimpson": 29118, + "Ġabstracts": 29119, + "Pal": 29120, + "Ġunim": 29121, + "Ġrobo": 29122, + "ĠIIB": 29123, + "depleted": 29124, + "Ġmorphologically": 29125, + "Ġenforcement": 29126, + "Ġdwell": 29127, + "Ġstagn": 29128, + "Ġlimestone": 29129, + "Ġmicrov": 29130, + "Ġïĥ¸": 29131, + "Luc": 29132, + "pacs": 29133, + "cyano": 29134, + "Ġintraocular": 29135, + "ĠCalculate": 29136, + "Support": 29137, + "SYS": 29138, + "ĠVS": 29139, + "CMs": 29140, + "Constant": 29141, + "ĠDj": 29142, + "Ġunbalanced": 29143, + "Ġrepeatability": 29144, + "gins": 29145, + "irect": 29146, + "ĠMOR": 29147, + "ĠBailey": 29148, + "Ġadvancement": 29149, + "Ġpursuit": 29150, + "Ġarom": 29151, + "proced": 29152, + "ĠInitiative": 29153, + "Ġincentives": 29154, + "Ġsurpass": 29155, + "genes": 29156, + "ĠIND": 29157, + "LH": 29158, + "Ġsuicidal": 29159, + "Ġbiodiesel": 29160, + "xz": 29161, + "ÙĬ": 29162, + "lea": 29163, + "ĠAnthony": 29164, + "Learning": 29165, + "Ġundo": 29166, + "Ġïĥº": 29167, + "ĠCommunities": 29168, + "hua": 29169, + "itime": 29170, + "ĠDean": 29171, + "Ġplasmin": 29172, + "ÃŃnez": 29173, + "ohydrate": 29174, + "Ġneurodevelop": 29175, + "Ġstoichiometric": 29176, + "ĠOncology": 29177, + "Ġshower": 29178, + "ĠDMS": 29179, + "WOR": 29180, + "ĠPIP": 29181, + "Ġsteric": 29182, + "mittees": 29183, + "istol": 29184, + "oxins": 29185, + "noon": 29186, + "FFT": 29187, + "Ġá»": 29188, + "opoiesis": 29189, + "Ġresembling": 29190, + "ĠBord": 29191, + "Ġprobiotics": 29192, + "ocysts": 29193, + "grey": 29194, + "ĠCatalog": 29195, + "IZATION": 29196, + "illes": 29197, + "ĠAlan": 29198, + "ĠÅ·": 29199, + "ĠLeib": 29200, + "ĠReasoning": 29201, + "biological": 29202, + "uterine": 29203, + "vacizumab": 29204, + "lecommun": 29205, + "ĠWarm": 29206, + "epage": 29207, + "variants": 29208, + "BSA": 29209, + "Ġïĥ¶": 29210, + "Ġhepatocyte": 29211, + "ketch": 29212, + "Ġstripping": 29213, + "ĠAdverse": 29214, + "ĠFeas": 29215, + "Ġïĥ¯": 29216, + "Pac": 29217, + "Ġindentation": 29218, + "Ġsecular": 29219, + "Ġidentifiable": 29220, + "running": 29221, + "Ġrd": 29222, + "Ġzyg": 29223, + "ĠDictionary": 29224, + "Ġresveratrol": 29225, + "inesterase": 29226, + "Ġtetracycline": 29227, + "ubles": 29228, + "Ġthroat": 29229, + "ĠLamb": 29230, + "aryon": 29231, + "ĠSQL": 29232, + "ĠÃľ": 29233, + "Ġglycemic": 29234, + "Ġcompetent": 29235, + "ĠAgreement": 29236, + "oiced": 29237, + "Ġconstitutively": 29238, + "Ġelectrocardi": 29239, + "oplasma": 29240, + "ĠîĦĥ": 29241, + "anide": 29242, + "Ġreorganization": 29243, + "Ġuninfected": 29244, + "UTE": 29245, + "Ġroyal": 29246, + "ĠSit": 29247, + "Ġmarital": 29248, + "ĠKobayashi": 29249, + "Barr": 29250, + "ĠTennessee": 29251, + "ĠChromat": 29252, + "ĠDerm": 29253, + "projection": 29254, + "ĠJob": 29255, + "Ġâīł": 29256, + "ĠTrip": 29257, + "Ġisop": 29258, + "Ġprojector": 29259, + "Ġatmospheres": 29260, + "Ġperforation": 29261, + "storage": 29262, + "iths": 29263, + "Ġmonomeric": 29264, + "ĠUSB": 29265, + "ĠEve": 29266, + "Ġspore": 29267, + "ĠmT": 29268, + "oxazole": 29269, + "ĠDeformation": 29270, + "Ġtextual": 29271, + "Ġwarf": 29272, + "Ġneuropathic": 29273, + "prepared": 29274, + "Ġblended": 29275, + "ĠHouston": 29276, + "************************************************************************": 29277, + "esters": 29278, + "Equals": 29279, + "Ġallergen": 29280, + "Ġpertinent": 29281, + "facts": 29282, + "uctions": 29283, + "Ġclocks": 29284, + "ĠVia": 29285, + "ĠCDF": 29286, + "Ġestuary": 29287, + "Ġphenomenology": 29288, + "arus": 29289, + "APH": 29290, + "Ġargues": 29291, + "Ġinserts": 29292, + "gow": 29293, + "hart": 29294, + "Ġchemotaxis": 29295, + "Ġpv": 29296, + "Ġrein": 29297, + "ĠGrim": 29298, + "ĠVF": 29299, + "Ġeffic": 29300, + "ĠProfiling": 29301, + "Ġanodic": 29302, + "ĠDENV": 29303, + "ĠWit": 29304, + "ĠSYSTEM": 29305, + "ĠCayley": 29306, + "Eng": 29307, + "ĠAQP": 29308, + "interactions": 29309, + "iliarity": 29310, + "ĠPromotes": 29311, + "Ġdams": 29312, + "ington": 29313, + "ffff": 29314, + "Ġintran": 29315, + "ĠTurbulence": 29316, + "ĠBianchi": 29317, + "CRE": 29318, + "ĠNOD": 29319, + "apine": 29320, + "ĠKane": 29321, + "ĠPDGF": 29322, + "ĠAxis": 29323, + "ĠCausal": 29324, + "ĠPoor": 29325, + "ĠWords": 29326, + "ĠHRV": 29327, + "Ġcyanobacteria": 29328, + "Ġreminiscent": 29329, + "ĠRemarkably": 29330, + "heet": 29331, + "@@": 29332, + "bil": 29333, + "Ġdiscriminating": 29334, + "ĠBaltic": 29335, + "ĠQuebec": 29336, + "Ġdefensive": 29337, + "âĪ©": 29338, + "kr": 29339, + "ĠRPE": 29340, + "seeking": 29341, + "ĠMovie": 29342, + "Ġinnovations": 29343, + "lept": 29344, + "Ġkw": 29345, + "Ġtibia": 29346, + "Ġneat": 29347, + "ytest": 29348, + "Ġthinner": 29349, + "Ġosteoblasts": 29350, + "ĠNorthwest": 29351, + "MOS": 29352, + "ĠPQ": 29353, + "Ġspi": 29354, + "Ġresponds": 29355, + "Ġhistorically": 29356, + "ĠPackage": 29357, + "ĠCoastal": 29358, + "ĠMississippi": 29359, + "ĠPVA": 29360, + "pering": 29361, + "indole": 29362, + "Ġprospectively": 29363, + "ĠHemisphere": 29364, + "Ġbarely": 29365, + "ánchez": 29366, + "aggered": 29367, + "yptian": 29368, + "ĠGest": 29369, + "yline": 29370, + "Ġphotochemical": 29371, + "oscalar": 29372, + "porated": 29373, + "Ġmetabolomics": 29374, + "Ġosteoblast": 29375, + "EGFP": 29376, + "eriatric": 29377, + "DW": 29378, + "quest": 29379, + "ĠHave": 29380, + "Ġspondyl": 29381, + "ĠPrimer": 29382, + "Ġsinks": 29383, + "Ġgaussian": 29384, + "ĠKhal": 29385, + "Enc": 29386, + "ĠAnopheles": 29387, + "Thanks": 29388, + "Ġconstrued": 29389, + "ĠUSS": 29390, + "ĠZeeman": 29391, + "Ġexported": 29392, + "ĠLevi": 29393, + "Ġcommander": 29394, + "connect": 29395, + "Ġnomenclature": 29396, + "therefore": 29397, + "ulata": 29398, + "Ġentrepreneur": 29399, + "Ġneuroscience": 29400, + "zan": 29401, + "Ġextant": 29402, + "ATIVE": 29403, + "opez": 29404, + "Ġenforced": 29405, + "ĠInnovation": 29406, + "earance": 29407, + "Ġimpressive": 29408, + "ĠPlac": 29409, + "ĠMoz": 29410, + "ĠStark": 29411, + "Ġrival": 29412, + "ĠCapital": 29413, + "Ġgranularity": 29414, + "Ġdiaphragm": 29415, + "utaneous": 29416, + "inds": 29417, + "Ġphotograph": 29418, + "Ġrectangles": 29419, + "TGF": 29420, + "Ġseaf": 29421, + "Ġmaze": 29422, + "ĠHW": 29423, + "Ġcorrelators": 29424, + "Ġdistinguishable": 29425, + "Ġconfounders": 29426, + "Ġlandslide": 29427, + "Ġtoll": 29428, + "Ġwastes": 29429, + "ĠWF": 29430, + "Ġendoc": 29431, + "Ġcapsid": 29432, + "ecund": 29433, + "ĠRBD": 29434, + "psin": 29435, + "Ġobstetric": 29436, + "Ġnanosheets": 29437, + "ocols": 29438, + "rens": 29439, + "ĠSubstituting": 29440, + "Ġcustomized": 29441, + "Ġresuscitation": 29442, + "Ġtubulin": 29443, + "ophyte": 29444, + "~~~~~~~~": 29445, + "plants": 29446, + "hicillin": 29447, + "halo": 29448, + "ruitment": 29449, + "ĠConcrete": 29450, + "Ġnanorods": 29451, + "ĠForms": 29452, + "Ġdying": 29453, + "discharge": 29454, + "Ġwellbeing": 29455, + "Ġwarmer": 29456, + "ĠSSD": 29457, + "ĠAUT": 29458, + "ĠConjug": 29459, + "Ġjuveniles": 29460, + "Ġinevitably": 29461, + "ĠMCS": 29462, + "approach": 29463, + "ĠMason": 29464, + "ĠGust": 29465, + "ĠThermodynamics": 29466, + "Ġpeel": 29467, + "ĠTranscriptome": 29468, + "Ġindispensable": 29469, + "urgery": 29470, + "posity": 29471, + "Ġpolarizations": 29472, + "ĠOthers": 29473, + "Ġsandy": 29474, + "Ġgliomas": 29475, + "Ġpursued": 29476, + "VEL": 29477, + "Ġrst": 29478, + "posium": 29479, + "nearest": 29480, + "Ġdisseminated": 29481, + "ĠMYC": 29482, + "Ġaldehyde": 29483, + "ĠDiagnostics": 29484, + "mans": 29485, + "Ġasphal": 29486, + "ĠSelect": 29487, + "ĠRecon": 29488, + "andro": 29489, + "DIM": 29490, + "Ġfeces": 29491, + "illon": 29492, + "ĠMALDI": 29493, + "nf": 29494, + "ĠElim": 29495, + "Ġhappy": 29496, + "ĠKarl": 29497, + "ĠInser": 29498, + "Ġinterrog": 29499, + "Intern": 29500, + "Ġtensorflow": 29501, + "Ġhaloes": 29502, + "Ġanticipate": 29503, + "ĠDPPH": 29504, + "rÃŃguez": 29505, + "Her": 29506, + "anate": 29507, + "Ġdressing": 29508, + "ĠHoly": 29509, + "Ġnewer": 29510, + "rides": 29511, + "placed": 29512, + "inetobacter": 29513, + "ĠOccurrence": 29514, + "edema": 29515, + "ĠIk": 29516, + "abad": 29517, + "ĠTransitions": 29518, + "Ġoutlines": 29519, + "Ġcochlear": 29520, + "Gy": 29521, + "success": 29522, + "ĠMEM": 29523, + "astype": 29524, + "Ġnormalizing": 29525, + "Ġterminates": 29526, + "Ġsuddenly": 29527, + "bbox": 29528, + "ĠPul": 29529, + "ĠPTP": 29530, + "aginal": 29531, + "Ġpretrained": 29532, + "Ġunreliable": 29533, + "ĠGraphical": 29534, + "ĠSeyfert": 29535, + "Ġcharacterizations": 29536, + "Ġtx": 29537, + "Ġbicarbonate": 29538, + "mathord": 29539, + "Ġheritability": 29540, + "stackexchange": 29541, + "iri": 29542, + "âĢĸ": 29543, + "ipit": 29544, + "attle": 29545, + "Ġarena": 29546, + "iba": 29547, + "ĠAX": 29548, + "ĠGPs": 29549, + "ophilia": 29550, + "SEL": 29551, + "osystem": 29552, + "ĠâĬ¢": 29553, + "ĠNucleus": 29554, + "redited": 29555, + "ACR": 29556, + "ĠAntenna": 29557, + "ĠCdc": 29558, + "orie": 29559, + "Ġequilibration": 29560, + "elong": 29561, + "stability": 29562, + "ĠSchist": 29563, + "Ġinjecting": 29564, + "hp": 29565, + "Ġvitamins": 29566, + "Poisson": 29567, + "ortal": 29568, + "ĠÃĬ": 29569, + "ĠÄı": 29570, + "Ill": 29571, + "Ġutils": 29572, + "ов": 29573, + "ĠGrom": 29574, + "::::": 29575, + "ĠGnRH": 29576, + "ĠSierra": 29577, + "Ġdrafted": 29578, + "Ġcapita": 29579, + "ships": 29580, + "Ġtimestamp": 29581, + "Ġsubstituents": 29582, + "ĠNotable": 29583, + "ĠPurpose": 29584, + "inol": 29585, + "Ġai": 29586, + "Ġfog": 29587, + "otone": 29588, + "ĠPlaces": 29589, + "byshev": 29590, + "tiology": 29591, + "ription": 29592, + "Ġyards": 29593, + "ĠXI": 29594, + "Ġtechnically": 29595, + "GAM": 29596, + "ĠABS": 29597, + "platform": 29598, + "ĠWO": 29599, + "PROC": 29600, + "Ġreconstit": 29601, + "ĠAnomalous": 29602, + "ĠBiol": 29603, + "Stage": 29604, + "ĠReviews": 29605, + "Ġrecalling": 29606, + "Ġillegal": 29607, + "lund": 29608, + "¬": 29609, + "uthenium": 29610, + "ĠPes": 29611, + "Ġovaries": 29612, + "solutions": 29613, + "massive": 29614, + "ĠRAW": 29615, + "Ġreconnection": 29616, + "ĠSusceptibility": 29617, + "Ġeconomical": 29618, + "cultured": 29619, + "ĠSham": 29620, + "sqcup": 29621, + "Ġpear": 29622, + "deposition": 29623, + "uchs": 29624, + "ĠSaw": 29625, + "Ġembolism": 29626, + "Bur": 29627, + "nar": 29628, + "oule": 29629, + "Ġtextile": 29630, + "seven": 29631, + "thio": 29632, + "Ġdenoising": 29633, + "CEP": 29634, + "Ġubiquitination": 29635, + "ĠCarlos": 29636, + "aP": 29637, + "Ġfolder": 29638, + "Ġhematological": 29639, + "iluminescence": 29640, + "ĠFuel": 29641, + "icion": 29642, + "aculture": 29643, + "ARB": 29644, + "ĠTravel": 29645, + "Func": 29646, + "acles": 29647, + "ĠInte": 29648, + "Ġvacua": 29649, + "Ġcocktail": 29650, + "ĠInsp": 29651, + "Ġcorporate": 29652, + "Ġdepicting": 29653, + "Ġsprint": 29654, + "ĠmTORC": 29655, + "Ġcimg": 29656, + "ocarbon": 29657, + "ĠDave": 29658, + "ĠGb": 29659, + "iji": 29660, + "targeting": 29661, + "Ġsequestration": 29662, + "Bri": 29663, + "IGF": 29664, + "Ġanalytics": 29665, + "ĠAcinetobacter": 29666, + "gets": 29667, + "MPS": 29668, + "ogluc": 29669, + "Cent": 29670, + "Ġverbs": 29671, + "Ġinductance": 29672, + "diagram": 29673, + "Ġrecalled": 29674, + "Ġcosme": 29675, + "Ġautomotive": 29676, + "ĠPDEs": 29677, + "ĠReid": 29678, + "Ġadapter": 29679, + "ĠOliver": 29680, + "Ġavalanche": 29681, + "Vir": 29682, + "ĠToxicity": 29683, + "ĠLeu": 29684, + "Conclusions": 29685, + "Ġtetragonal": 29686, + "ĠDMF": 29687, + "umannii": 29688, + "ĠRequirements": 29689, + "toc": 29690, + "ité": 29691, + "Ġcontinent": 29692, + "ĠHank": 29693, + "ĠDefinitions": 29694, + "GPU": 29695, + "origin": 29696, + "Ġdichro": 29697, + "Mus": 29698, + "Ġbival": 29699, + "Ġimpulsive": 29700, + "Ġassemble": 29701, + "Ġpipes": 29702, + "docs": 29703, + "Ġexchanger": 29704, + "Ġallograft": 29705, + "loyd": 29706, + "ĠÌĭ": 29707, + "Ġantenatal": 29708, + "Ġgrassland": 29709, + "Ġhystere": 29710, + "ĠAntigen": 29711, + "ĠGeneric": 29712, + "ĠTuring": 29713, + "ĠExcell": 29714, + "ĠHein": 29715, + "aja": 29716, + "uminum": 29717, + "citabine": 29718, + "facial": 29719, + "iteration": 29720, + "Ġslurry": 29721, + "AML": 29722, + "ergetic": 29723, + "ĠTHF": 29724, + "Ġkilometers": 29725, + "fg": 29726, + "educ": 29727, + "idian": 29728, + "Ġpredicates": 29729, + "Ġradios": 29730, + "ĠPeri": 29731, + "ĠShell": 29732, + "Ġarcsec": 29733, + "Ġstriatal": 29734, + "Ġceiling": 29735, + "olithic": 29736, + "Ġexhaustion": 29737, + "PUT": 29738, + "thers": 29739, + "ymp": 29740, + "ĠQian": 29741, + "ĠProgressive": 29742, + "Ġwel": 29743, + "ĠConvention": 29744, + "ĠCurie": 29745, + "ĠMans": 29746, + "ĠNova": 29747, + "ĠWells": 29748, + "dew": 29749, + "Standard": 29750, + "realistic": 29751, + "transpose": 29752, + "serial": 29753, + "ĠTx": 29754, + "ĠAMR": 29755, + "Ġindeterm": 29756, + "ĠLiouville": 29757, + "hookrightarrow": 29758, + "ARs": 29759, + "Ġbaseball": 29760, + "acious": 29761, + "agnetization": 29762, + "estimate": 29763, + "ĠPAS": 29764, + "Ġmeals": 29765, + "multiple": 29766, + "ĠBiomarkers": 29767, + "Wide": 29768, + "ĠTomography": 29769, + "////////////////////////////////": 29770, + "Ġresins": 29771, + "Ġanywhere": 29772, + "INC": 29773, + "ĠTeaching": 29774, + "ĠSamuel": 29775, + "Ġhallmark": 29776, + "ĠThyroid": 29777, + "othi": 29778, + "Ġconstraining": 29779, + "ĠBarrett": 29780, + "ĠErrors": 29781, + "Cole": 29782, + "sharing": 29783, + "HDL": 29784, + "Effect": 29785, + "ĠTolerance": 29786, + "Ġstressful": 29787, + "ĠBalance": 29788, + "ĠTech": 29789, + "Ġvalleys": 29790, + "setup": 29791, + "ĠRadical": 29792, + "ĠMacrophages": 29793, + "Ġinterrupt": 29794, + "Ġdiatom": 29795, + "colored": 29796, + "Ġpyrid": 29797, + "FDG": 29798, + "æ": 29799, + "Ġreared": 29800, + "ĠRating": 29801, + "Ġopaque": 29802, + "package": 29803, + "Ġnasopharyngeal": 29804, + "Ġpreconditioning": 29805, + "Diptera": 29806, + "ĠMing": 29807, + "ĠCaro": 29808, + "ĠImmunity": 29809, + "rifuge": 29810, + "ĠObjectives": 29811, + "ghan": 29812, + "uccin": 29813, + "ĠFors": 29814, + "ĠFITC": 29815, + "Ġseats": 29816, + "ĠImpaired": 29817, + "Ġreefs": 29818, + "emaker": 29819, + "Ġoffices": 29820, + "Ġaccepting": 29821, + "ĠTRAN": 29822, + "ĠTargets": 29823, + "Ġcorrelator": 29824, + "Ġsupercapac": 29825, + "inburgh": 29826, + "Ġcollider": 29827, + "Ġenteric": 29828, + "ĠSTRUCTURE": 29829, + "Ġminister": 29830, + "ĠArchae": 29831, + "Loop": 29832, + "ĠASA": 29833, + "Ġcontacted": 29834, + "Ġhistidine": 29835, + "folded": 29836, + "Search": 29837, + "Ġrespects": 29838, + "ĠATF": 29839, + "Ġtrouble": 29840, + "Ġprevailing": 29841, + "Cp": 29842, + "ĠTCM": 29843, + "ĠSpinal": 29844, + "Ġguides": 29845, + "evitable": 29846, + "Ġbrick": 29847, + "strings": 29848, + "ĠHungary": 29849, + "Ġeps": 29850, + "entricular": 29851, + "Specifically": 29852, + "ando": 29853, + "issues": 29854, + "osomiasis": 29855, + "kDa": 29856, + "Ġaside": 29857, + "Ġadenine": 29858, + "Ġmotivate": 29859, + "stratig": 29860, + "BLE": 29861, + "ĠDeposition": 29862, + "motor": 29863, + "ĠHers": 29864, + "Ġnebul": 29865, + "ĠBarrier": 29866, + "Unlike": 29867, + "Ġballistic": 29868, + "Ġsouthwestern": 29869, + "ĠMontreal": 29870, + "Scan": 29871, + "Ġmould": 29872, + "Ġinterrup": 29873, + "smallmatrix": 29874, + "Ġelaborated": 29875, + "ucks": 29876, + "APS": 29877, + "ĠConsumption": 29878, + "capacity": 29879, + "innitus": 29880, + "Ġgovernance": 29881, + "Ġpalsy": 29882, + "Ġsubmission": 29883, + "Ġtemple": 29884, + "ĠIIA": 29885, + "methionine": 29886, + "Ġkerat": 29887, + "Ġridges": 29888, + "Promega": 29889, + "cols": 29890, + "ISP": 29891, + "Ġapnea": 29892, + "ĠFlat": 29893, + "ĠEpigenetic": 29894, + "Ġparish": 29895, + "ĠParametric": 29896, + "dash": 29897, + "future": 29898, + "rise": 29899, + "Ġcontracting": 29900, + "algia": 29901, + "Ġgoto": 29902, + "stadt": 29903, + "Ġfabricate": 29904, + "Ġdimerization": 29905, + "dump": 29906, + "ĠLyn": 29907, + "Ġrecycled": 29908, + "posedness": 29909, + "ĠSensory": 29910, + "ïĿ": 29911, + "ĠWet": 29912, + "Ġdiethyl": 29913, + "Ġblades": 29914, + "Ġtimed": 29915, + "Ġkeyword": 29916, + "Ġpolytope": 29917, + "ĠGot": 29918, + "Ġapproximates": 29919, + "Without": 29920, + "ĠBere": 29921, + "ĠLp": 29922, + "oplasty": 29923, + "ĠFibr": 29924, + "modulated": 29925, + "ĠARM": 29926, + "Ġunderestimate": 29927, + "ĠCBS": 29928, + "ĠLectures": 29929, + "uncan": 29930, + "ĠSeismic": 29931, + "Soft": 29932, + "Ġzooplankton": 29933, + "Ġencephalopathy": 29934, + "ĠSSA": 29935, + "ĠCros": 29936, + "ĠHann": 29937, + "Ġshuffle": 29938, + "scription": 29939, + "ĠRevers": 29940, + "Studies": 29941, + "Ġsocially": 29942, + "Ġsubcl": 29943, + "ĠYong": 29944, + "ogh": 29945, + "Ġïģ³": 29946, + "UDY": 29947, + "ĠHaar": 29948, + "ĠDoctor": 29949, + "Ġintakes": 29950, + "Ġbarrel": 29951, + "ĠTRPV": 29952, + "ĠAggreg": 29953, + "nyi": 29954, + "tuned": 29955, + "acquired": 29956, + "Ġhook": 29957, + "FGF": 29958, + "«": 29959, + "ĠInjection": 29960, + "Ġgravel": 29961, + "Ġmicrog": 29962, + "Ġmenstrual": 29963, + "Feature": 29964, + "IRE": 29965, + "uu": 29966, + "ĠSrc": 29967, + "ĠStore": 29968, + "Ġinitiator": 29969, + "PSO": 29970, + "Ġepileptic": 29971, + "Ġcingulate": 29972, + "IJ": 29973, + "Row": 29974, + "Ġsinging": 29975, + "ĠMethan": 29976, + "ĠAldrich": 29977, + "Ġtremendous": 29978, + "amining": 29979, + "Ġtracts": 29980, + "Ġâİ£": 29981, + "klah": 29982, + "Div": 29983, + "indol": 29984, + "Ġindole": 29985, + "exper": 29986, + "Ġglycer": 29987, + "Ġbenzyl": 29988, + "Ġworsening": 29989, + "Ġunambiguous": 29990, + "uart": 29991, + "Ġparsim": 29992, + "ricks": 29993, + "Ġtrail": 29994, + "ĠBlanc": 29995, + "Ġaminotransferase": 29996, + "ĠDOC": 29997, + "Ġfumig": 29998, + "idic": 29999, + "ĠConsequences": 30000, + "Ġacidification": 30001, + "ĠCIFAR": 30002, + "ĠDatasets": 30003, + "ĠAMI": 30004, + "Ġexplants": 30005, + "ĠDiverse": 30006, + "Ġdephasing": 30007, + "Ġparliament": 30008, + "ipient": 30009, + "Ġhoneycomb": 30010, + "heavy": 30011, + "Ġwatermark": 30012, + "MED": 30013, + "datasets": 30014, + "waters": 30015, + "Provid": 30016, + "interpret": 30017, + "rovirus": 30018, + "Io": 30019, + "RAD": 30020, + "Ġlunar": 30021, + "Ġweaning": 30022, + "Ġsensorimotor": 30023, + "uca": 30024, + "Ġinfect": 30025, + "ĠUnique": 30026, + "GRP": 30027, + "QoL": 30028, + "ospec": 30029, + "Ġforwarding": 30030, + "Estim": 30031, + "ÅĦski": 30032, + "ĠMs": 30033, + "achn": 30034, + "Ġrota": 30035, + "Ġappointment": 30036, + "ĠMedal": 30037, + "Ġadenovirus": 30038, + "quinol": 30039, + "Ġdeuterium": 30040, + "tep": 30041, + "ĠStyle": 30042, + "Nd": 30043, + "ayama": 30044, + "ĠHamm": 30045, + "ĠSpecification": 30046, + "vability": 30047, + "tha": 30048, + "Ġjitter": 30049, + "Ġâݦ": 30050, + "aqu": 30051, + "wire": 30052, + "Ġclassically": 30053, + "Ġsuperpotential": 30054, + "ĠSpecim": 30055, + "ĠVariance": 30056, + "Ġalbums": 30057, + "ĠSenior": 30058, + "Ġneurotransmitter": 30059, + "ĠRecombinant": 30060, + "DCS": 30061, + "vl": 30062, + "Ġpf": 30063, + "Ġinevitable": 30064, + "ĠNick": 30065, + "Ġmanipulating": 30066, + "ituximab": 30067, + "ceiver": 30068, + "ĠBren": 30069, + "ĠRace": 30070, + "Ġretarded": 30071, + "modulin": 30072, + "Clinical": 30073, + "Ġneurologic": 30074, + "ĠRegiment": 30075, + "Ġzoom": 30076, + "ĠOrthogonal": 30077, + "ĠConcerning": 30078, + "ĠJurassic": 30079, + "ĠArtem": 30080, + "ĠMelbourne": 30081, + "bins": 30082, + "jl": 30083, + "Ġinhab": 30084, + "Ġsqrt": 30085, + "Ġsemisimple": 30086, + "astric": 30087, + "ĠProxim": 30088, + "ĠVariants": 30089, + "Ġaesthetic": 30090, + "Ġsummarised": 30091, + "ĠBecker": 30092, + "OCH": 30093, + "dale": 30094, + "Ġmounting": 30095, + "andering": 30096, + "Ġsoftmax": 30097, + "Ġneuroinflammation": 30098, + "Ġesophagus": 30099, + "operators": 30100, + "ĠADAM": 30101, + "Ġviolate": 30102, + "ĠPHY": 30103, + "ede": 30104, + "ĠCher": 30105, + "orsal": 30106, + "Ġmetamorphic": 30107, + "ĠICM": 30108, + "ĠAbcam": 30109, + "slot": 30110, + "serine": 30111, + "Ġduplicates": 30112, + "ĠMEMS": 30113, + "ĠAbl": 30114, + "ĠChel": 30115, + "ĠAuthority": 30116, + "Ġgeo": 30117, + "Ġhomeomorphism": 30118, + "Ġimmunomodulatory": 30119, + "ĠTU": 30120, + "ĠKT": 30121, + "aterally": 30122, + "oxides": 30123, + "tebral": 30124, + "Ġcataract": 30125, + "leaved": 30126, + "igu": 30127, + "ateur": 30128, + "ĠRé": 30129, + "Ġdiscoveries": 30130, + "boson": 30131, + "ocated": 30132, + "jpg": 30133, + "ĠSato": 30134, + "ĠPROP": 30135, + "ĠImplement": 30136, + "ELISA": 30137, + "iqueness": 30138, + "Ġsymbion": 30139, + "ĠFaraday": 30140, + "ĠPPARγ": 30141, + "witz": 30142, + "reward": 30143, + "ĠBush": 30144, + "stressed": 30145, + "ĠAbor": 30146, + "Ġairways": 30147, + "Ġinterferometry": 30148, + "Circ": 30149, + "Ġimmunoprecipitation": 30150, + "ĠApache": 30151, + "rophosph": 30152, + "ĠoC": 30153, + "Ġfrog": 30154, + "ĠGU": 30155, + "ffe": 30156, + "ĠStro": 30157, + "Ġdodecyl": 30158, + "dan": 30159, + "folds": 30160, + "ĠMust": 30161, + "Ġsurroundings": 30162, + "Ġcodons": 30163, + "onda": 30164, + "tb": 30165, + "odge": 30166, + "avas": 30167, + "ĠSeason": 30168, + "tude": 30169, + "ĠPlasticity": 30170, + "ĠHawaii": 30171, + "DEG": 30172, + "ĠCMD": 30173, + "Ġsingleton": 30174, + "keley": 30175, + "Ġalgebraically": 30176, + "Ġnanostructured": 30177, + "easible": 30178, + "Ġoverlooked": 30179, + "ĠPulse": 30180, + "romechanical": 30181, + "ĠElse": 30182, + "Ġexcitons": 30183, + "ĠConstrained": 30184, + "Ġcohesion": 30185, + "Ġrealizing": 30186, + "ĠRadiative": 30187, + "Ġtrypan": 30188, + "xs": 30189, + "ĠTas": 30190, + "Ġmainstream": 30191, + "Ġcompactly": 30192, + "growing": 30193, + "esc": 30194, + "ĠdN": 30195, + "ĠSignatures": 30196, + "ĠFundamentals": 30197, + "Ġexpose": 30198, + "ĠRang": 30199, + "Ġhanded": 30200, + "Ġfunctionalization": 30201, + "Ġpassiv": 30202, + "altern": 30203, + "agul": 30204, + "Ġschematically": 30205, + "OW": 30206, + "ĠÖ": 30207, + "ĠPOD": 30208, + "Ġhear": 30209, + "ymore": 30210, + "ĠPremier": 30211, + "South": 30212, + "Ä«": 30213, + "ĠOBS": 30214, + "ĠAlg": 30215, + "glia": 30216, + "ĠTransmembrane": 30217, + "Ġspheroids": 30218, + "ĠRHS": 30219, + "Ġinches": 30220, + "ĠKato": 30221, + "Ġie": 30222, + "ĠCommercial": 30223, + "Ġanalytes": 30224, + "Ġrisky": 30225, + "Ġpiston": 30226, + "ĠMarkovian": 30227, + "Ġdrama": 30228, + "Ġci": 30229, + "ĠHistological": 30230, + "Ġactuation": 30231, + "discrete": 30232, + "carbamoyl": 30233, + "SMA": 30234, + "Ġfeeds": 30235, + "Ġneoplasia": 30236, + "ĠController": 30237, + "been": 30238, + "glutamine": 30239, + "injected": 30240, + "Ġcrab": 30241, + "ĠCauses": 30242, + "ĠStory": 30243, + "Ġvanadium": 30244, + "ĠTitan": 30245, + "enix": 30246, + "assign": 30247, + "Ġimmunogenicity": 30248, + "ĠApparent": 30249, + "Ġenhancers": 30250, + "ĠSou": 30251, + "alloy": 30252, + "mathbin": 30253, + "Ġsedation": 30254, + "ĠWorkshop": 30255, + "gover": 30256, + "lst": 30257, + "Ġupwelling": 30258, + "mez": 30259, + "Ġpolypropylene": 30260, + "ĠColorectal": 30261, + "ĠRelaxation": 30262, + "Ġfragile": 30263, + "Äĥ": 30264, + "Ġsubgraphs": 30265, + "theoretical": 30266, + "Operator": 30267, + "lywood": 30268, + "awn": 30269, + "ĠPercentage": 30270, + "methylation": 30271, + "corrhizal": 30272, + "Grad": 30273, + "dens": 30274, + "ĠHα": 30275, + "Ġupcoming": 30276, + "Ġvirgin": 30277, + "Names": 30278, + "ĠRyd": 30279, + "Ġâݤ": 30280, + "phosphorylation": 30281, + "renewal": 30282, + "Year": 30283, + "Init": 30284, + "Ġselling": 30285, + "ĠMASS": 30286, + "rophin": 30287, + "ijn": 30288, + "Conversely": 30289, + "Ġuniversally": 30290, + "orhombic": 30291, + "Ġunpredictable": 30292, + "Fock": 30293, + "chair": 30294, + "ivas": 30295, + "networks": 30296, + "Ġterritories": 30297, + "thia": 30298, + "ĠAmplification": 30299, + "March": 30300, + "Ġflam": 30301, + "ĠChart": 30302, + "Ġshortage": 30303, + "AMET": 30304, + "Ġgrape": 30305, + "Ġvoltammetry": 30306, + "د": 30307, + "ĠSCH": 30308, + "Ġepithel": 30309, + "ĠChromosome": 30310, + "ĠXL": 30311, + "ĠPersistent": 30312, + "Ġtraveled": 30313, + "Ġmeridional": 30314, + "Ġfprintf": 30315, + "Ġgum": 30316, + "visory": 30317, + "Unfortunately": 30318, + "Ġanteced": 30319, + "Ġfrictional": 30320, + "DAT": 30321, + "acl": 30322, + "ĠPregnancy": 30323, + "ĠBZ": 30324, + "regulatory": 30325, + "stimulating": 30326, + "Japan": 30327, + "machine": 30328, + "uti": 30329, + "ĠLer": 30330, + "Ġnanoflu": 30331, + "prototype": 30332, + "identification": 30333, + "klahoma": 30334, + "ĠEmploy": 30335, + "Schwarz": 30336, + "Ġincorrectly": 30337, + "atto": 30338, + "rization": 30339, + "ismuth": 30340, + "Ġiris": 30341, + "imentary": 30342, + "Ġinflationary": 30343, + "Ġoutflows": 30344, + "ĠLic": 30345, + "oreductase": 30346, + "Ġproceeding": 30347, + "ĠTAC": 30348, + "ĠHTL": 30349, + "Ġresides": 30350, + "stral": 30351, + "ĠTransf": 30352, + "Ġdichotom": 30353, + "Filter": 30354, + "June": 30355, + "isure": 30356, + "ĠAde": 30357, + "Ġijk": 30358, + "ĠPhilos": 30359, + "Ġstayed": 30360, + "Ġtamoxifen": 30361, + "Ġasparagine": 30362, + "exception": 30363, + "Ġaccumulating": 30364, + "astro": 30365, + "Change": 30366, + "uzi": 30367, + "Ġlon": 30368, + "Instead": 30369, + "Ġcentrally": 30370, + "ĠDental": 30371, + "classified": 30372, + "ĠEgyptian": 30373, + "Address": 30374, + "ĠQuaternary": 30375, + "ĠUSP": 30376, + "coin": 30377, + "Ġembryogenesis": 30378, + "ï̍": 30379, + "Null": 30380, + "ĠMixing": 30381, + "intensive": 30382, + "Ġnormative": 30383, + "ĠLef": 30384, + "Ġrumen": 30385, + "ĠThai": 30386, + "Ġswallow": 30387, + "Component": 30388, + "Ġrobotics": 30389, + "ĠCad": 30390, + "ĠCIP": 30391, + "ĠAcids": 30392, + "ĠOffic": 30393, + "urer": 30394, + "ĠWick": 30395, + "Ġkink": 30396, + "ĠScha": 30397, + "ĠCharacteristic": 30398, + "families": 30399, + "ĠGCs": 30400, + "ĠOptimizing": 30401, + "Ġtimer": 30402, + "él": 30403, + "jin": 30404, + "reversal": 30405, + "Ġsandstone": 30406, + "HN": 30407, + "tk": 30408, + "Ġptr": 30409, + "Ġmonochromatic": 30410, + "Ġfeedforward": 30411, + "dington": 30412, + "Ġcriticism": 30413, + "Ġsig": 30414, + "Ġpace": 30415, + "ĠTK": 30416, + "ĠWas": 30417, + "Ġcertificate": 30418, + "Ġstuck": 30419, + "Ġcorrid": 30420, + "Ġlocalisation": 30421, + "Ġsilk": 30422, + "Ġdigest": 30423, + "ĠTemple": 30424, + "ĠPosterior": 30425, + "Ġcommutator": 30426, + "tsch": 30427, + "perme": 30428, + "ysed": 30429, + "Ġmenu": 30430, + "Ġmidw": 30431, + "ocatalytic": 30432, + "Ġppb": 30433, + "Types": 30434, + "arri": 30435, + "ĠLOD": 30436, + "Ġloan": 30437, + "secret": 30438, + "Ġcarbons": 30439, + "ĠHolog": 30440, + "olipids": 30441, + "Ġuplo": 30442, + "ĠDNase": 30443, + "Ġpuzzle": 30444, + "Ġstance": 30445, + "ĠManchester": 30446, + "ĠDetector": 30447, + "ims": 30448, + "ĠTerms": 30449, + "ĠPGC": 30450, + "Ġincidents": 30451, + "ieh": 30452, + "ĠIDs": 30453, + "ĠAhmad": 30454, + "Ġnights": 30455, + "Ġbiomo": 30456, + "ĠMethylation": 30457, + "uator": 30458, + "resize": 30459, + "ĠFinger": 30460, + "ĠWo": 30461, + "Ġposter": 30462, + "Ġsolidification": 30463, + "ĠValidity": 30464, + "ĠDendritic": 30465, + "Ġadherent": 30466, + "issions": 30467, + "inction": 30468, + "Ġantagonistic": 30469, + "ĠPreliminaries": 30470, + "Ġcoval": 30471, + "Ġmovies": 30472, + "Ġbudding": 30473, + "Kn": 30474, + "ĠGit": 30475, + "ĠThereafter": 30476, + "Ġcapacitive": 30477, + "Az": 30478, + "ĠTLS": 30479, + "Ġinitiates": 30480, + "ĠDMR": 30481, + "Ġâī«": 30482, + "ĠMyocardial": 30483, + "ĠRotation": 30484, + "CONFIG": 30485, + "Ġvowel": 30486, + "Ġolivine": 30487, + "Hamiltonian": 30488, + "Ġstalk": 30489, + "Neu": 30490, + "Rest": 30491, + "anical": 30492, + "Ġdst": 30493, + "Ġresh": 30494, + "Ġexpressive": 30495, + "Ġinfectivity": 30496, + "oku": 30497, + "CTL": 30498, + "Frequency": 30499, + "Ġpremise": 30500, + "Walk": 30501, + "ĠâĹ": 30502, + "Ġrelapsed": 30503, + "tured": 30504, + "ĠUML": 30505, + "ovan": 30506, + "ĠResearchers": 30507, + "Ġconveniently": 30508, + "usk": 30509, + "INIT": 30510, + "Eqs": 30511, + "Factory": 30512, + "Ġunsteady": 30513, + "ĠAnsw": 30514, + "Ala": 30515, + "nitine": 30516, + "qp": 30517, + "ulous": 30518, + "research": 30519, + "ĠBrom": 30520, + "ĠDemoc": 30521, + "configuration": 30522, + "ulosic": 30523, + "Ġfra": 30524, + "Ġgift": 30525, + "Third": 30526, + "Claim": 30527, + "ÄŁ": 30528, + "odiazep": 30529, + "Ġprox": 30530, + "ocystis": 30531, + "ĠRPA": 30532, + "ĠLikert": 30533, + "RMS": 30534, + "tech": 30535, + "Ġacous": 30536, + "TLR": 30537, + "buck": 30538, + "ĠTherap": 30539, + "ussions": 30540, + "helor": 30541, + "ĠEmotion": 30542, + "bird": 30543, + "Ġthio": 30544, + "Ġquantitation": 30545, + "bracket": 30546, + "Ġpercept": 30547, + "Ġsubcategory": 30548, + "Ġlightning": 30549, + "Ġhernia": 30550, + "Ġneurotrophic": 30551, + "SDS": 30552, + "ĠAnders": 30553, + "Ġslowing": 30554, + "strongly": 30555, + "ĠCounting": 30556, + "ĠIncluding": 30557, + "ductions": 30558, + "ubated": 30559, + "ĠStorm": 30560, + "correlated": 30561, + "Ġautoantibodies": 30562, + "ĠMerg": 30563, + "ocer": 30564, + "micutes": 30565, + "Ġnonlinearities": 30566, + "ĠCentury": 30567, + "ĠLandscape": 30568, + "ĠDerivatives": 30569, + "ĠContrary": 30570, + "Ġcompile": 30571, + "ĠHepatic": 30572, + "Ġponds": 30573, + "Ġorganize": 30574, + "DMSO": 30575, + "Position": 30576, + "Ġbrach": 30577, + "Ġinflat": 30578, + "ospace": 30579, + "Ġskewness": 30580, + "Ġagitation": 30581, + "ĠHOMO": 30582, + "EU": 30583, + "Ġcommented": 30584, + "Ġcorpora": 30585, + "Ġmalt": 30586, + "Hermitian": 30587, + "iday": 30588, + "ĠHelmholtz": 30589, + "roblast": 30590, + "ĠCTR": 30591, + "unching": 30592, + "ĠMond": 30593, + "ĠComment": 30594, + "Ġosteosarcoma": 30595, + "posterior": 30596, + "Ġthymus": 30597, + "Ġcigarettes": 30598, + "NW": 30599, + "olem": 30600, + "ĠHox": 30601, + "ĠNFL": 30602, + "ĠAvailable": 30603, + "ĠSiber": 30604, + "ĠFeld": 30605, + "Ġborderline": 30606, + "Ġbeats": 30607, + "Ġorganised": 30608, + "Ġdistinguishes": 30609, + "Ġdialog": 30610, + "ĠBerger": 30611, + "oleic": 30612, + "Ġnumbered": 30613, + "Ġreachable": 30614, + "ĠRobertson": 30615, + "ĠChamber": 30616, + "ndarray": 30617, + "Ġcytoskeletal": 30618, + "Ġblending": 30619, + "blood": 30620, + "Import": 30621, + "Ġoverwhelming": 30622, + "Ġio": 30623, + "Ġoutage": 30624, + "ĠScholar": 30625, + "placing": 30626, + "ĠPolyp": 30627, + "Decl": 30628, + "ĠMEDLINE": 30629, + "ĠKM": 30630, + "ĠDAP": 30631, + "errors": 30632, + "ĠSHR": 30633, + "ĠDex": 30634, + "ĠGAS": 30635, + "ĠGian": 30636, + "Ġclinicopathological": 30637, + "Ġïģ·": 30638, + "ĠPredictions": 30639, + "ĠQuadratic": 30640, + "Ġarrhythmias": 30641, + "arid": 30642, + "Ġclothing": 30643, + "ĠFracture": 30644, + "ĉĠĠĠĠĠ": 30645, + "addy": 30646, + "ĠAlberta": 30647, + "ĠWed": 30648, + "phire": 30649, + "ĠEncryp": 30650, + "ĠLAB": 30651, + "ĠFano": 30652, + "CTT": 30653, + "Ġoryz": 30654, + "iliac": 30655, + "ĠLiao": 30656, + "versus": 30657, + "Ġmeso": 30658, + "Ġmidpoint": 30659, + "Ġstator": 30660, + "ĠJenn": 30661, + "ovsky": 30662, + "Ġuncover": 30663, + "erenn": 30664, + "ĠMcM": 30665, + "âīĪ": 30666, + "ĠCircuits": 30667, + "Ġfetuses": 30668, + "Ġagglomer": 30669, + "Ġfb": 30670, + "Ġyy": 30671, + "atech": 30672, + "ARG": 30673, + "Ġbaumannii": 30674, + "Ġellipsoid": 30675, + "Ġloses": 30676, + "Ġunve": 30677, + "Ġbutt": 30678, + "Ġmulticentre": 30679, + "iline": 30680, + "Ġresort": 30681, + "Ġcerebrovascular": 30682, + "ĠDecreased": 30683, + "jud": 30684, + "sus": 30685, + "amol": 30686, + "constraints": 30687, + "Ġteen": 30688, + "ĠPassive": 30689, + "ĠCaucasian": 30690, + "Ġcran": 30691, + "ĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ": 30692, + "ün": 30693, + "ĠDNMT": 30694, + "Ġterror": 30695, + "adrenal": 30696, + "Ġangiogenic": 30697, + "ĠInhibitory": 30698, + "prag": 30699, + "Ġcob": 30700, + "elsh": 30701, + "Ġenhancements": 30702, + "ĠShaw": 30703, + "ĠTakahashi": 30704, + "Ġsulphur": 30705, + "Ġgravitation": 30706, + "ĠPVDF": 30707, + "must": 30708, + "¢": 30709, + "asymptotic": 30710, + "elman": 30711, + "ĠPros": 30712, + "ĠMAD": 30713, + "ĠLen": 30714, + "therapy": 30715, + "efully": 30716, + "sulfur": 30717, + "ĠTCA": 30718, + "additive": 30719, + "talk": 30720, + "Ġpiglets": 30721, + "Ġprospect": 30722, + "ecundity": 30723, + "ĠXiang": 30724, + "handler": 30725, + "Ġclath": 30726, + "Ġmillimeter": 30727, + "jar": 30728, + "Ġbiophysical": 30729, + "Ġcomplexities": 30730, + "ĠHerb": 30731, + "Ġrecovers": 30732, + "ĠVincent": 30733, + "ĠPuerto": 30734, + "Earth": 30735, + "RAM": 30736, + "Ġcables": 30737, + "designed": 30738, + "ĠOscillation": 30739, + "Ġmeiosis": 30740, + "Ġfleet": 30741, + "ĠHuntington": 30742, + "ĠBeg": 30743, + "ĠECs": 30744, + "ĠAntic": 30745, + "Ġpractitioner": 30746, + "cultural": 30747, + "kat": 30748, + "Ġrecoil": 30749, + "ĠImplicit": 30750, + "Ġsummaries": 30751, + "Ġdiscontinued": 30752, + "Ġencompassing": 30753, + "ĠAltogether": 30754, + "ĠDIST": 30755, + "Ġconstellation": 30756, + "ĠExisting": 30757, + "Ġconductors": 30758, + "oplasm": 30759, + "ĠCosmology": 30760, + "Zero": 30761, + "ĠInform": 30762, + "Ġendangered": 30763, + "Ġweapons": 30764, + "atype": 30765, + "ĠAsc": 30766, + "Ġfluence": 30767, + "Ġferric": 30768, + "ĠLaurent": 30769, + "Early": 30770, + "Ġsgn": 30771, + "ĠHadamard": 30772, + "Ġastron": 30773, + "Cys": 30774, + "ĠThm": 30775, + "Ġdece": 30776, + "erencing": 30777, + "ĠMeans": 30778, + "Ġhydrated": 30779, + "ÙĪ": 30780, + "Ġrigorously": 30781, + "Ġambulatory": 30782, + "ĠDOI": 30783, + "Handle": 30784, + "ĠEnterobacteriaceae": 30785, + "ĠRQ": 30786, + "ĠGFR": 30787, + "prote": 30788, + "Ġmigrated": 30789, + "thening": 30790, + "ĠHopkins": 30791, + "ĠPsychology": 30792, + "igl": 30793, + "ĠEDS": 30794, + "Ġâζ": 30795, + "Ġremotely": 30796, + "ĠÂ¥": 30797, + "Ġinspiration": 30798, + "ĠâĮ¬": 30799, + "olian": 30800, + "Ġsaliency": 30801, + "ĠDog": 30802, + "ĠRosa": 30803, + "oya": 30804, + "Ġoccupies": 30805, + "camera": 30806, + "Ġdecompression": 30807, + "Ġscatt": 30808, + "Ġinvestigator": 30809, + "Ġcounterex": 30810, + "ĠIFNγ": 30811, + "ĠPittsburgh": 30812, + "Ġadminister": 30813, + "negl": 30814, + "ussis": 30815, + "MPC": 30816, + "ĠSwitching": 30817, + "Ġcooler": 30818, + "Ġbronchi": 30819, + "Ġparalle": 30820, + "Ġspeckle": 30821, + "Ġphysiologic": 30822, + "INVAL": 30823, + "Ġheterologous": 30824, + "|||": 30825, + "orghum": 30826, + "GAL": 30827, + "Ġmalformations": 30828, + "Ġweakening": 30829, + "Ġpsycho": 30830, + "ĠIH": 30831, + "Ġcontradictory": 30832, + "Ġphonological": 30833, + "ĠPerturbation": 30834, + "bB": 30835, + "ĠNos": 30836, + "TRUE": 30837, + "folding": 30838, + "phenol": 30839, + "ĠLSM": 30840, + "ĠâĪĹ": 30841, + "ĠAngle": 30842, + "Ġprovincial": 30843, + "FeO": 30844, + "ÅĽ": 30845, + "ĠIber": 30846, + "ressors": 30847, + "Ġproliferating": 30848, + "zers": 30849, + "organism": 30850, + "âĨĵ": 30851, + "ZO": 30852, + "cimg": 30853, + "Ġunperturbed": 30854, + "Ġjj": 30855, + "Ġelectrodynamics": 30856, + "ĠEpit": 30857, + "NTs": 30858, + "ĠBloom": 30859, + "Ġlanth": 30860, + "aminant": 30861, + "ĠSwift": 30862, + "European": 30863, + "Ġafferent": 30864, + "Reduce": 30865, + "published": 30866, + "ĠFitting": 30867, + "ĠFungal": 30868, + "Ġtribe": 30869, + "recting": 30870, + "Ġconjugacy": 30871, + "imeters": 30872, + "ĠCec": 30873, + "ĠKH": 30874, + "castle": 30875, + "Ġseptal": 30876, + "releasing": 30877, + "Ġoss": 30878, + "Ġ¦": 30879, + "ĠMissing": 30880, + "ĠFatigue": 30881, + "ĠBaseball": 30882, + "Ġimmunoblotting": 30883, + "Ġoh": 30884, + "orations": 30885, + "Ġvine": 30886, + "azy": 30887, + "serum": 30888, + "Ġlookup": 30889, + "Ġneovascular": 30890, + "iah": 30891, + "soil": 30892, + "Ġairflow": 30893, + "ĠSloan": 30894, + "him": 30895, + "çļ": 30896, + "located": 30897, + "zantine": 30898, + "ĠSuccessful": 30899, + "eminal": 30900, + "ĠDimensional": 30901, + "ĠNSA": 30902, + "ĠLogistic": 30903, + "emetery": 30904, + "Ġbrak": 30905, + "antal": 30906, + "south": 30907, + "Ġprototypes": 30908, + "Ġadvised": 30909, + "Ġidealized": 30910, + "ophytic": 30911, + "nbsp": 30912, + "Binary": 30913, + "Hyp": 30914, + "Joh": 30915, + "polation": 30916, + "Ġpolyvinyl": 30917, + "estimated": 30918, + "Ġoxytocin": 30919, + "ĠLetter": 30920, + "ĠImpair": 30921, + "Ġenvelopes": 30922, + "mainly": 30923, + "Ġmys": 30924, + "Ġintras": 30925, + "Ġbiogenic": 30926, + "cysteine": 30927, + "Ġuric": 30928, + "ĠCyan": 30929, + "ryption": 30930, + "Ġphotoreceptor": 30931, + "ĠToxic": 30932, + "ĠGamm": 30933, + "Ġcontainment": 30934, + "IgG": 30935, + "Squ": 30936, + "Ġperfused": 30937, + "Ġbiosensors": 30938, + "Ġmagmatic": 30939, + "Rate": 30940, + "ĠTf": 30941, + "Ġsecrete": 30942, + "Ġcriticality": 30943, + "Ġcompositionally": 30944, + "ĠBruce": 30945, + "SZ": 30946, + "ĠSport": 30947, + "ĠEI": 30948, + "Ġdiseased": 30949, + "Ġpreschool": 30950, + "ĠHarvey": 30951, + "ĠPTH": 30952, + "Ġbilayers": 30953, + "ĠOscillations": 30954, + "ĠHonor": 30955, + "ĠCCN": 30956, + "ĠMOT": 30957, + "ĠLloyd": 30958, + "Ġtrapez": 30959, + "Ġbuds": 30960, + "OFFSET": 30961, + "Ġmacromolecules": 30962, + "Ġbilirubin": 30963, + "olly": 30964, + "Ġutilities": 30965, + "ministered": 30966, + "Ġglobe": 30967, + "OLOGY": 30968, + "ropods": 30969, + "ĠMDM": 30970, + "ĠPyObject": 30971, + "macroph": 30972, + "ĠPBMCs": 30973, + "ospheres": 30974, + "Ġcatastrophic": 30975, + "ĠNavigation": 30976, + "ĠLSD": 30977, + "Ġcream": 30978, + "Ġdereg": 30979, + "bonded": 30980, + "rents": 30981, + "Ġpotentiation": 30982, + "Ġstro": 30983, + "Ġsteeper": 30984, + "ulinum": 30985, + "Ġperiodontitis": 30986, + "arization": 30987, + "âĪª": 30988, + "amicin": 30989, + "Ġmagnetized": 30990, + "ĠNutritional": 30991, + "Ġaccord": 30992, + "gaard": 30993, + "FTIR": 30994, + "ramethyl": 30995, + "ĠGle": 30996, + "Mel": 30997, + "ĠCTL": 30998, + "Ġtranslating": 30999, + "Ġautoimmunity": 31000, + "olerant": 31001, + "triangleq": 31002, + "amo": 31003, + "Ġvel": 31004, + "ĠHCN": 31005, + "ĠHamming": 31006, + "ĠVenus": 31007, + "ĠGad": 31008, + "ĠOwing": 31009, + "Information": 31010, + "ĠSchemes": 31011, + "carotene": 31012, + "Its": 31013, + "anis": 31014, + "Ġreplay": 31015, + "Ġtouc": 31016, + "LECT": 31017, + "ĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ": 31018, + "Ġtabulated": 31019, + "ĠSchottky": 31020, + "Far": 31021, + "amation": 31022, + "ĠRies": 31023, + "Ġexpects": 31024, + "ĠInstability": 31025, + "Ġsons": 31026, + "Ġdeck": 31027, + "Ġïģ¥": 31028, + "ĠSignature": 31029, + "Ġlithosphere": 31030, + "WW": 31031, + "makers": 31032, + "ughters": 31033, + "Ġâİ¡": 31034, + "ardian": 31035, + "à¦": 31036, + "Ġaccepts": 31037, + "ĠOSA": 31038, + "Ġγδ": 31039, + "nonumber": 31040, + "Select": 31041, + "lite": 31042, + "ĠAqueous": 31043, + "agawa": 31044, + "ĠEdinburgh": 31045, + "ĠMembranes": 31046, + "ĠSIG": 31047, + "akia": 31048, + "Ġtestes": 31049, + "Ġheli": 31050, + "++++": 31051, + "Ġultrafast": 31052, + "Ġmaneuver": 31053, + "ĠDate": 31054, + "phin": 31055, + "ĠKad": 31056, + "Ġtransferase": 31057, + "Pers": 31058, + "Ġtones": 31059, + "ĠSGD": 31060, + "anto": 31061, + "ĠOrange": 31062, + "ĠGeography": 31063, + "ĠAccumulation": 31064, + "aty": 31065, + "Ġbeating": 31066, + "Ġoverlying": 31067, + "ĠNDVI": 31068, + "ĠTownship": 31069, + "jing": 31070, + "ĠNOS": 31071, + "player": 31072, + "ĠMDD": 31073, + "ĠHungarian": 31074, + "Ġdw": 31075, + "ĠHin": 31076, + "Ġvalidating": 31077, + "Ġcolorimetric": 31078, + "ĠSupersymmetric": 31079, + "FUNC": 31080, + "gically": 31081, + "ofuran": 31082, + "-------": 31083, + "Ġimping": 31084, + "similarity": 31085, + "ĠDOX": 31086, + "ĠGlo": 31087, + "ivirus": 31088, + "listed": 31089, + "Ġbusy": 31090, + "iprofloxacin": 31091, + "Ġanxi": 31092, + "Ġblunt": 31093, + "Ġprocedural": 31094, + "Ġunknowns": 31095, + "AdS": 31096, + "thickness": 31097, + "follows": 31098, + "closing": 31099, + "environmental": 31100, + "ĠFeeding": 31101, + "unami": 31102, + "ende": 31103, + "ipine": 31104, + "Ġimpacting": 31105, + "Ġpenetrating": 31106, + "ambia": 31107, + "ĠWavelet": 31108, + "Ġfilamentous": 31109, + "Ġleng": 31110, + "ĠSCA": 31111, + "ĠEther": 31112, + "metall": 31113, + "Ġfringe": 31114, + "ĠAdjust": 31115, + "usz": 31116, + "ĠRey": 31117, + "ĠBoyd": 31118, + "Ġburnout": 31119, + "Ġcook": 31120, + "Ġnowadays": 31121, + "ĠDispersion": 31122, + "ĠRodriguez": 31123, + "Factor": 31124, + "ĠOklahoma": 31125, + "Ġunital": 31126, + "Ġpredictability": 31127, + "Ġlithography": 31128, + "ès": 31129, + "Willi": 31130, + "unal": 31131, + "asting": 31132, + "correction": 31133, + "ĠDed": 31134, + "ĠSocio": 31135, + "ĠChapman": 31136, + "ĠEco": 31137, + "Ġoncogene": 31138, + "ĠDrive": 31139, + "Ġfunnel": 31140, + "uis": 31141, + "ĠGENER": 31142, + "ĠACR": 31143, + "Ġworkloads": 31144, + "Ġoctahedral": 31145, + "vich": 31146, + "enburg": 31147, + "Ġimproper": 31148, + "decoded": 31149, + "Ġimmunosorbent": 31150, + "Ġinhomogeneity": 31151, + "RK": 31152, + "onically": 31153, + "Ġglycoproteins": 31154, + "onics": 31155, + "ĠFok": 31156, + "ĠBras": 31157, + "ĠCalculus": 31158, + "ĠMoss": 31159, + "ĠRK": 31160, + "Ġviolet": 31161, + "Ġlymphomas": 31162, + "enspace": 31163, + "ĠPalae": 31164, + "Ġrenin": 31165, + "phant": 31166, + "ĠRED": 31167, + "Ġfaulty": 31168, + "Riemann": 31169, + "Ãī": 31170, + "ĠElli": 31171, + "Bol": 31172, + "Tn": 31173, + "Yang": 31174, + "gender": 31175, + "Ġdetuning": 31176, + "Ġoperon": 31177, + "Ġinsecticide": 31178, + "esi": 31179, + "amon": 31180, + "ĠSCD": 31181, + "ĠBath": 31182, + "ĠâĢĸ": 31183, + "ĠGeographic": 31184, + "Ġcyclohex": 31185, + "ĠConfidence": 31186, + "Ġcomet": 31187, + "Ġfolate": 31188, + "observer": 31189, + "Ġvisitors": 31190, + "extra": 31191, + "ateness": 31192, + "ĠSPT": 31193, + "arcane": 31194, + "Ġholistic": 31195, + "semi": 31196, + "ĠMild": 31197, + "Ġsmear": 31198, + "Ġcyclase": 31199, + "Ġanymore": 31200, + "Ġseagrass": 31201, + "Ġconsortium": 31202, + "Ġfinishes": 31203, + "cyan": 31204, + "ductance": 31205, + "frost": 31206, + "hereafter": 31207, + "Ġprescriptions": 31208, + "Ġcmd": 31209, + "ĠPerceived": 31210, + "coordinates": 31211, + "Ġstyl": 31212, + "ĠBard": 31213, + "ĠHoll": 31214, + "ĠsiRNAs": 31215, + "sugg": 31216, + "Ġthr": 31217, + "Ġmainland": 31218, + "SCH": 31219, + "Ġassertions": 31220, + "Ġbabies": 31221, + "Ġrecapit": 31222, + "Tok": 31223, + "Ġresected": 31224, + "construct": 31225, + "Ber": 31226, + "Ġcholine": 31227, + "Ġunitarity": 31228, + "Ġcatalyzes": 31229, + "detector": 31230, + "ĠSMB": 31231, + "tery": 31232, + "cluded": 31233, + "ĠAbbreviations": 31234, + "ĠOliveira": 31235, + "LOC": 31236, + "zin": 31237, + "ĠLorenz": 31238, + "Kernel": 31239, + "lyn": 31240, + "ĠLEP": 31241, + "soni": 31242, + "Ġseptum": 31243, + "TMS": 31244, + "Ġunmodified": 31245, + "borough": 31246, + "ĠAudio": 31247, + "Ġdollars": 31248, + "CMD": 31249, + "Ġnorthwestern": 31250, + "Ġpalmit": 31251, + "ragalactic": 31252, + "ĠMiz": 31253, + "FH": 31254, + "confidence": 31255, + "NEXT": 31256, + "ĠAGE": 31257, + "ĠEqn": 31258, + "ĠClasses": 31259, + "Ġmisleading": 31260, + "ĠPKA": 31261, + "Ġanchored": 31262, + "ĠRip": 31263, + "phag": 31264, + "Ġintubation": 31265, + "ĠAngular": 31266, + "ĠBEC": 31267, + "Thr": 31268, + "Ġorganisations": 31269, + "Ġcomfortable": 31270, + "Ġcommissioned": 31271, + "poll": 31272, + "ydia": 31273, + "instead": 31274, + "Ġpassword": 31275, + "Ġcompliant": 31276, + "ĠPrecipitation": 31277, + "ophosphamide": 31278, + "usters": 31279, + "Ġpneumococcal": 31280, + "Ġtomographic": 31281, + "tidae": 31282, + "ĠFirmicutes": 31283, + "bw": 31284, + "ĠPDB": 31285, + "ĠGPUs": 31286, + "ĠPlanar": 31287, + "Ġverbose": 31288, + "Summary": 31289, + "lance": 31290, + "ĠEGFP": 31291, + "ongru": 31292, + "Complex": 31293, + "ĠWheat": 31294, + "uche": 31295, + "ĠMCA": 31296, + "ĠProjection": 31297, + "Ġstats": 31298, + "Ġsummand": 31299, + "dimethoxyphenyl": 31300, + "ĠABSTRACT": 31301, + "Ġcarotenoid": 31302, + "Ġbroke": 31303, + "ĠDesigning": 31304, + "ĠHetero": 31305, + "ĠCarlsbad": 31306, + "Cov": 31307, + "ineral": 31308, + "Ġanalyte": 31309, + "ĠColeman": 31310, + "Ġeigenstate": 31311, + "ĠHolland": 31312, + "ERSION": 31313, + "ĠDak": 31314, + "ellers": 31315, + "ĠÃĺ": 31316, + "missing": 31317, + "deposited": 31318, + "ĠLincoln": 31319, + "anion": 31320, + "ĠSPEC": 31321, + "Ġfertilizer": 31322, + "ĠCPS": 31323, + "Ġcofactor": 31324, + "Ġtren": 31325, + "Ġcalendar": 31326, + "Ġyoungest": 31327, + "STATUS": 31328, + "ĠEXPERIMENTAL": 31329, + "Ġsr": 31330, + "Ġnl": 31331, + "ĠMES": 31332, + "Study": 31333, + "padding": 31334, + "Ġatopic": 31335, + "ĠOG": 31336, + "Ġentrainment": 31337, + "AFM": 31338, + "ĠCou": 31339, + "Web": 31340, + "ĠMicroscopic": 31341, + "Ġunambiguously": 31342, + "Day": 31343, + "yotrophic": 31344, + "reous": 31345, + "Ġsarcom": 31346, + "ĠVAL": 31347, + "Ġhindered": 31348, + "ĠREM": 31349, + "otrexate": 31350, + "ocarcin": 31351, + "ĠAlk": 31352, + "Ġbrevity": 31353, + "factual": 31354, + "Cer": 31355, + "diox": 31356, + "ophical": 31357, + "Ġlytic": 31358, + "Take": 31359, + "Ġintend": 31360, + "ĠCla": 31361, + "Ġasteroid": 31362, + "ĠSEP": 31363, + "apenem": 31364, + "universal": 31365, + "Ġoceans": 31366, + "Ġmonoid": 31367, + "Ġseparator": 31368, + "ĠPorous": 31369, + "Ġpostoperatively": 31370, + "Ġsemin": 31371, + "ĠDisplay": 31372, + "Ġhydrolase": 31373, + "transferases": 31374, + "Ġthrombus": 31375, + "ĠOv": 31376, + "ĠDielectric": 31377, + "Ġcompelling": 31378, + "assing": 31379, + "ĠMAS": 31380, + "ullary": 31381, + "ĠMori": 31382, + "ĠPathogenesis": 31383, + "ĠBreaking": 31384, + "ĠPLGA": 31385, + "cooling": 31386, + "§": 31387, + "Ġfee": 31388, + "Ġreducible": 31389, + "Ġdiverge": 31390, + "Ġqueues": 31391, + "Ġmushroom": 31392, + "Ġdeacetylase": 31393, + "YFP": 31394, + "Ġdisreg": 31395, + "ĠArrays": 31396, + "processes": 31397, + "ĠTransportation": 31398, + "Ġundetectable": 31399, + "bursts": 31400, + "Ġphospholipase": 31401, + "Option": 31402, + "asin": 31403, + "Ġnocturnal": 31404, + "tez": 31405, + "ĠDisruption": 31406, + "oserine": 31407, + "behavior": 31408, + "ĠTony": 31409, + "ĠKot": 31410, + "ieval": 31411, + "Ġmyofib": 31412, + "Ġhalogen": 31413, + "ĠCPR": 31414, + "ployed": 31415, + "ĠPolymers": 31416, + "Ġadenoma": 31417, + "Ġquartile": 31418, + "Ġquaternary": 31419, + "ĠIraq": 31420, + "Ġsieve": 31421, + "Ġintractable": 31422, + "Ġfabrics": 31423, + "continuum": 31424, + "ĠEmergence": 31425, + "Pot": 31426, + "itism": 31427, + "veness": 31428, + "hoe": 31429, + "Ġredes": 31430, + "ĠHRP": 31431, + "ploidy": 31432, + "picuous": 31433, + "ogo": 31434, + "ĠGag": 31435, + "Ġnominated": 31436, + "occupied": 31437, + "Ġquench": 31438, + "ropolis": 31439, + "nucleotide": 31440, + "ĠEventually": 31441, + "Ñı": 31442, + "ĠClock": 31443, + "ĠSteady": 31444, + "opolymers": 31445, + "ĠARE": 31446, + "irnov": 31447, + "helf": 31448, + "blob": 31449, + "download": 31450, + "PLL": 31451, + "UNT": 31452, + "predictions": 31453, + "Ġoccipital": 31454, + "toxic": 31455, + "ĠVice": 31456, + "Ġangio": 31457, + "CuO": 31458, + "Ġresistances": 31459, + "fflffl": 31460, + "Distribution": 31461, + "Gre": 31462, + "onamide": 31463, + "ĠIOP": 31464, + "UNEL": 31465, + "Ġaids": 31466, + "ĠHUV": 31467, + "ECM": 31468, + "ĠPAD": 31469, + "ĠAgNPs": 31470, + "Print": 31471, + "Ġlamellar": 31472, + "ĠUltrason": 31473, + "severe": 31474, + "ĠAnnotation": 31475, + "NIR": 31476, + "sgn": 31477, + "ĠOften": 31478, + "Ġiterate": 31479, + "Ġcarriage": 31480, + "spherical": 31481, + "ĠFrid": 31482, + "Ġdiffract": 31483, + "ĠBasal": 31484, + "Ġunsatisf": 31485, + "ĠDysfunction": 31486, + "arboxylic": 31487, + "ĠCollective": 31488, + "Ġdegrading": 31489, + "Ġadiposity": 31490, + "Ġfifty": 31491, + "Ġpars": 31492, + "ĠOptimized": 31493, + "ocaine": 31494, + "Ġbb": 31495, + "ĠShip": 31496, + "ĠLW": 31497, + "Ġtremor": 31498, + "Ġã": 31499, + "Ġnucleons": 31500, + "Ġscientist": 31501, + "ĠMish": 31502, + "gression": 31503, + "ĠMerc": 31504, + "ĠFlem": 31505, + "Ġcorals": 31506, + "Incre": 31507, + "ĠDSP": 31508, + "Ġdefenses": 31509, + "dimer": 31510, + "atherine": 31511, + "otubes": 31512, + "stride": 31513, + "ĠAlterations": 31514, + "Ġoest": 31515, + "ĠBIC": 31516, + "Ġradiated": 31517, + "Ġketamine": 31518, + "Ġdissimilarity": 31519, + "ĠAncient": 31520, + "ĠHed": 31521, + "Ġattr": 31522, + "ĠIsa": 31523, + "Ġionospheric": 31524, + "Ġgovernor": 31525, + "ĠEstimated": 31526, + "Ġultrathin": 31527, + "Update": 31528, + "Ġimmunoassay": 31529, + "Ġconjectured": 31530, + "ĠREF": 31531, + "ĠSiegel": 31532, + "Adv": 31533, + "Mem": 31534, + "Ġpups": 31535, + "ĠAPPL": 31536, + "ecomposable": 31537, + "journal": 31538, + "ĠRol": 31539, + "ĠLob": 31540, + "rington": 31541, + "Ġnonsingular": 31542, + "Ġcitric": 31543, + "iones": 31544, + "ositis": 31545, + "ALY": 31546, + "Ġmentions": 31547, + "ĠMarkers": 31548, + "algebraic": 31549, + "Ġflattened": 31550, + "Ġmail": 31551, + "ĠTGA": 31552, + "ĠPMA": 31553, + "ĠNaval": 31554, + "Ġfacilitation": 31555, + "Ġunidentified": 31556, + "Ġempathy": 31557, + "jectories": 31558, + "logits": 31559, + "Ġpermanently": 31560, + "Ġbottles": 31561, + "ĠBengal": 31562, + "Ġpeanut": 31563, + "Ġcapillaries": 31564, + "erents": 31565, + "ĠLooking": 31566, + "changes": 31567, + "ĠMagell": 31568, + "ĠCMC": 31569, + "ĠVerm": 31570, + "Ġsubscales": 31571, + "demand": 31572, + "orexia": 31573, + "Ġachievements": 31574, + "ĠRobustness": 31575, + "ĠWallace": 31576, + "ĠDTT": 31577, + "ogels": 31578, + "ocker": 31579, + "ĠSpike": 31580, + "Ġpainter": 31581, + "Ġbuses": 31582, + "Ġpolluted": 31583, + "Ġtort": 31584, + "ĠPPP": 31585, + "nex": 31586, + "extended": 31587, + "ucalypt": 31588, + "Ġprostatic": 31589, + "ĠFCC": 31590, + "Ġkick": 31591, + "oyal": 31592, + "epochs": 31593, + "hss": 31594, + "yon": 31595, + "Ġdans": 31596, + "ĠAw": 31597, + "Ġadversely": 31598, + "Ġaltogether": 31599, + "Ġophthalm": 31600, + "Ġcpu": 31601, + "ĠFRET": 31602, + "Ġforensic": 31603, + "Ġhotspots": 31604, + "Ġpaintings": 31605, + "Ġomn": 31606, + "ĠpS": 31607, + "oglu": 31608, + "ofol": 31609, + "FTs": 31610, + "Ġdermat": 31611, + "pragma": 31612, + "Ġbump": 31613, + "ĠCir": 31614, + "aS": 31615, + "Ġnaked": 31616, + "ĠNLS": 31617, + "ĠSpitzer": 31618, + "Ġsalvage": 31619, + "Ġintuitively": 31620, + "Ġcasual": 31621, + "Ġfired": 31622, + "verages": 31623, + "ĠBurden": 31624, + "Wang": 31625, + "ylem": 31626, + "Ġradiographs": 31627, + "ĠSchiff": 31628, + "OLUTION": 31629, + "Cross": 31630, + "Ġhints": 31631, + "owing": 31632, + "ĠStreng": 31633, + "ĠANY": 31634, + "Ġworry": 31635, + "ĠRoger": 31636, + "Ġtrabecular": 31637, + "Band": 31638, + "ĠNec": 31639, + "ipes": 31640, + "tool": 31641, + "ĠILC": 31642, + "iÄĩ": 31643, + "ocean": 31644, + "ĠAri": 31645, + "AMA": 31646, + "ĠVertex": 31647, + "activate": 31648, + "Location": 31649, + "onts": 31650, + "Ġhs": 31651, + "Ġslender": 31652, + "refring": 31653, + "ĠEndogenous": 31654, + "adiabatic": 31655, + "Ġcryptic": 31656, + "Ġeradication": 31657, + "ĠKevin": 31658, + "Ġmc": 31659, + "Ġcardio": 31660, + "Ġphosphoryl": 31661, + "Witten": 31662, + "Ġscl": 31663, + "ĠIw": 31664, + "ĠMade": 31665, + "Ġfounding": 31666, + "oflag": 31667, + "aline": 31668, + "horizontal": 31669, + "ĠGeneralization": 31670, + "psychiatric": 31671, + "ĠDuncan": 31672, + "ĠSnO": 31673, + "ĠAar": 31674, + "Ġgg": 31675, + "Ġpremi": 31676, + "ĠStrom": 31677, + "ĠExplan": 31678, + "Ġlethality": 31679, + "ÏĤ": 31680, + "odo": 31681, + "Ġsubscrib": 31682, + "ĠSTUDY": 31683, + "Ġoutperformed": 31684, + "Ġcovalently": 31685, + "MHC": 31686, + "fail": 31687, + "ĠKac": 31688, + "EGR": 31689, + "ĠTRI": 31690, + "robot": 31691, + "ĠCandidate": 31692, + "ĠTNBC": 31693, + "Ġarchaeological": 31694, + "Eukary": 31695, + "Ġlava": 31696, + "dipole": 31697, + "Ġuncons": 31698, + "Anti": 31699, + "Ġprednis": 31700, + "ĠRobin": 31701, + "Ġstratigraphic": 31702, + "Ġ¤": 31703, + "Ġfinance": 31704, + "ĠStudio": 31705, + "render": 31706, + "Ġrearing": 31707, + "Ġger": 31708, + "ĠOpt": 31709, + "ĠManifolds": 31710, + "Ġdestabil": 31711, + "Ġtelomerase": 31712, + "Ġpicking": 31713, + "Ġamplicon": 31714, + "Ġyearly": 31715, + "ĠNCC": 31716, + "inser": 31717, + "ĠEnrichment": 31718, + "ĠMicrostructure": 31719, + "ĠWarren": 31720, + "ophysics": 31721, + "Ġfifteen": 31722, + "Åij": 31723, + "Ġreviewer": 31724, + "Ġskilled": 31725, + "Ġmagnetoresistance": 31726, + "Ġreconfiguration": 31727, + "Ġpoet": 31728, + "Ġpredetermined": 31729, + "Ġcryopres": 31730, + "Ġattractors": 31731, + "Ġprojectile": 31732, + "ĠCrystals": 31733, + "ĠMCM": 31734, + "ĠXanth": 31735, + "Ġclockwise": 31736, + "regnant": 31737, + "Ġgated": 31738, + "ryza": 31739, + "ĠProsp": 31740, + "adin": 31741, + "Ġmolybdenum": 31742, + "ĠAlps": 31743, + "ĠBald": 31744, + "Ġhalluc": 31745, + "udo": 31746, + "Ġmont": 31747, + "ĠFlash": 31748, + "Ġpulling": 31749, + "ĠLQ": 31750, + "ĠWalsh": 31751, + "ĠThomson": 31752, + "meson": 31753, + "Ġintercal": 31754, + "Ġelapsed": 31755, + "FFFF": 31756, + "ĠForecasting": 31757, + "à¯": 31758, + "ĠLSP": 31759, + "endorf": 31760, + "Ġxml": 31761, + "substrate": 31762, + "Mu": 31763, + "during": 31764, + "oconstr": 31765, + "EMA": 31766, + "Ġïĥ«": 31767, + "ĠDFS": 31768, + "ĠVon": 31769, + "Ġfathers": 31770, + "Ġunco": 31771, + "ĠUnderg": 31772, + "Ġmultiplexing": 31773, + "atra": 31774, + "Ġcohesive": 31775, + "ĠUI": 31776, + "ĠPrev": 31777, + "çļĦ": 31778, + "cum": 31779, + "hf": 31780, + "ĠSCN": 31781, + "atalysis": 31782, + "ĠArsen": 31783, + "amping": 31784, + "ĠPlastic": 31785, + "ĠMadison": 31786, + "Ġsupremum": 31787, + "ĠCited": 31788, + "Ġaren": 31789, + "iski": 31790, + "inel": 31791, + "stro": 31792, + "Ġcorrupted": 31793, + "Ġglab": 31794, + "Ġcardiopulmonary": 31795, + "Ġpragmatic": 31796, + "CAG": 31797, + "Stack": 31798, + "thioxo": 31799, + "ĠReproductive": 31800, + "Ġsteatosis": 31801, + "Best": 31802, + "ĠBars": 31803, + "Ġracing": 31804, + "ĠUtah": 31805, + "equivalence": 31806, + "ĠFifty": 31807, + "ĠCytokine": 31808, + "Ġutilised": 31809, + "horizon": 31810, + "ouracil": 31811, + "iversary": 31812, + "emer": 31813, + "ĠQuestions": 31814, + "Ġlinkages": 31815, + "anchez": 31816, + "VV": 31817, + "Ġphotodet": 31818, + "kowski": 31819, + "REST": 31820, + "Ġhosting": 31821, + "Ġpushing": 31822, + "Ġneurotoxicity": 31823, + "SQ": 31824, + "rst": 31825, + "Ġhockey": 31826, + "Ġtrips": 31827, + "ĠIndoor": 31828, + "ematics": 31829, + "Ġtransect": 31830, + "ĠABI": 31831, + "agar": 31832, + "âĪļ": 31833, + "egenerate": 31834, + "ĠQP": 31835, + "MID": 31836, + "ĠAccept": 31837, + "ĠCyber": 31838, + "North": 31839, + "Ġdθ": 31840, + "alla": 31841, + "Ġbraid": 31842, + "finding": 31843, + "alin": 31844, + "ĠLST": 31845, + "ĠLax": 31846, + "udin": 31847, + "ĠiNOS": 31848, + "convert": 31849, + "ACA": 31850, + "ĠGuan": 31851, + "Ġlymphocytic": 31852, + "Ġsyllable": 31853, + "ĠTOR": 31854, + "ĠSCR": 31855, + "ĠAJ": 31856, + "Ġoutburst": 31857, + "bladder": 31858, + "OTA": 31859, + "audio": 31860, + "chromen": 31861, + "ÑģÑĤ": 31862, + "Ġgratefully": 31863, + "Ġtiling": 31864, + "Ġquit": 31865, + "shan": 31866, + "ĠAccretion": 31867, + "Ġnarrowing": 31868, + "ĠInduces": 31869, + "Mic": 31870, + "Ġfuc": 31871, + "Ġthalamus": 31872, + "ANES": 31873, + "Ġquaternion": 31874, + "ĠListeria": 31875, + "duality": 31876, + "hend": 31877, + "ande": 31878, + "Ġparo": 31879, + "Ġinspected": 31880, + "question": 31881, + "ĠHoney": 31882, + "Ġchunks": 31883, + "Ġforearm": 31884, + "radients": 31885, + "ificantly": 31886, + "obank": 31887, + "Ġsomewhere": 31888, + "Ġmonetary": 31889, + "ĠLouisiana": 31890, + "Ġemulsions": 31891, + "Ġprogrammable": 31892, + "Ġmanifests": 31893, + "ĠMartinez": 31894, + "Ġted": 31895, + "emen": 31896, + "anni": 31897, + "Ġoverlaid": 31898, + "Ġvirulent": 31899, + "Mask": 31900, + "ĠUtility": 31901, + "Ġwk": 31902, + "osexual": 31903, + "ĠEarl": 31904, + "dar": 31905, + "hdr": 31906, + "ractors": 31907, + "Ġconstructor": 31908, + "Ġnascent": 31909, + "inzburg": 31910, + "ĠCraig": 31911, + "Ġplexus": 31912, + "reverse": 31913, + "ograv": 31914, + "tags": 31915, + "Ġcalibrate": 31916, + "à®": 31917, + "Ġhide": 31918, + "ĠFol": 31919, + "Ġinteracted": 31920, + "Ġconfron": 31921, + "market": 31922, + "Ġsociodemographic": 31923, + "ĠLucas": 31924, + "ĠMCT": 31925, + "ĠRSS": 31926, + "Ġmicroplate": 31927, + "underst": 31928, + "Ital": 31929, + "ĠCMR": 31930, + "recy": 31931, + "ĠPCOS": 31932, + "Ġdetoxification": 31933, + "Ġsubtree": 31934, + "Ġsubsections": 31935, + "Ġpropositions": 31936, + "Acknowledgements": 31937, + "reinforced": 31938, + "lis": 31939, + "ĠCIR": 31940, + "Ġimprinted": 31941, + "vium": 31942, + "afic": 31943, + "Ġchecklist": 31944, + "ĠRx": 31945, + "ĠEph": 31946, + "Ġsolder": 31947, + "transformation": 31948, + "ĠStrait": 31949, + "azar": 31950, + "Ġhandler": 31951, + "kelet": 31952, + "BCL": 31953, + "Math": 31954, + "Ġwishes": 31955, + "uminescent": 31956, + "ĠPEC": 31957, + "irt": 31958, + "ylidene": 31959, + "Ġloosely": 31960, + "naissance": 31961, + "ILs": 31962, + "foil": 31963, + "ĠGNU": 31964, + "ĠKet": 31965, + "vix": 31966, + "ĠPlain": 31967, + "ĠRES": 31968, + "Ġparenting": 31969, + "ĠConnection": 31970, + "Ġrhizosphere": 31971, + "oprevalence": 31972, + "iatic": 31973, + "ĠpA": 31974, + "ĠVil": 31975, + "setting": 31976, + "ĠReLU": 31977, + "ĠBOOST": 31978, + "Ġappreciate": 31979, + "bx": 31980, + "orest": 31981, + "ologie": 31982, + "Ġpalp": 31983, + "foo": 31984, + "usual": 31985, + "Ġquestioned": 31986, + "Ġtrigon": 31987, + "ĠGFAP": 31988, + "ĠKyoto": 31989, + "dise": 31990, + "antile": 31991, + "ück": 31992, + "ĠQuantization": 31993, + "Ġscler": 31994, + "Ġbehalf": 31995, + "ĠDuality": 31996, + "Ġmagnetically": 31997, + "Ġelegant": 31998, + "UA": 31999, + "epis": 32000, + "Ġsubclinical": 32001, + "ontrol": 32002, + "ĠChemicals": 32003, + "Utils": 32004, + "Ġlowers": 32005, + "extraction": 32006, + "Ġamplifiers": 32007, + "ĠEntry": 32008, + "ĠWORK": 32009, + "Ġthrombocytopenia": 32010, + "Mil": 32011, + "idus": 32012, + "embry": 32013, + "manager": 32014, + "ĠCoordination": 32015, + "ĠPhenotypic": 32016, + "chunk": 32017, + "Ġhypotension": 32018, + "Ġcryogenic": 32019, + "Ġreactants": 32020, + "ĠMMSE": 32021, + "Ġcentros": 32022, + "ĠButler": 32023, + "Ġcavitation": 32024, + "ĠLessons": 32025, + "estion": 32026, + "ĠMIS": 32027, + "associ": 32028, + "APE": 32029, + "ĠEulerian": 32030, + "Ġrecreational": 32031, + "ĠNeo": 32032, + "ĠCDM": 32033, + "repeat": 32034, + "details": 32035, + "Bal": 32036, + "STA": 32037, + "Ġâīº": 32038, + "ĠCamero": 32039, + "ĠTelevision": 32040, + "Ġworkforce": 32041, + "Ġcomputerized": 32042, + "Ġextraordinary": 32043, + "Ġribonucle": 32044, + "Ġhydrophobicity": 32045, + "ĠFeasibility": 32046, + "Ol": 32047, + "Tw": 32048, + "ĠMam": 32049, + "ĠFAC": 32050, + "profit": 32051, + "negligible": 32052, + "ĠFruit": 32053, + "Ġears": 32054, + "Ġshearing": 32055, + "ĠCorresponding": 32056, + "fun": 32057, + "ieck": 32058, + "mos": 32059, + "ĠEMI": 32060, + "ĠSometimes": 32061, + "Ġfluorine": 32062, + "Ġdetergent": 32063, + "Ġalg": 32064, + "races": 32065, + "ivable": 32066, + "COMM": 32067, + "ĠSwitch": 32068, + "Ġstrained": 32069, + "virtual": 32070, + "Temperature": 32071, + "Ġcredible": 32072, + "ĠGPCR": 32073, + "ĠDebye": 32074, + "ĠLit": 32075, + "Ġhemic": 32076, + "Ġtransducers": 32077, + "metast": 32078, + "adiene": 32079, + "Ġoryzae": 32080, + "tn": 32081, + "Ġafternoon": 32082, + "ĠArabian": 32083, + "ĠChromatin": 32084, + "Ġxenografts": 32085, + "Ġcryptographic": 32086, + "Ġaxillary": 32087, + "Ġvolunteer": 32088, + "ĠNevada": 32089, + "Ġpions": 32090, + "unknown": 32091, + "ĠFU": 32092, + "venously": 32093, + "radio": 32094, + "ĠLabour": 32095, + "ĠVillage": 32096, + "Ric": 32097, + "Ġmetat": 32098, + "Ġserotypes": 32099, + "regression": 32100, + "saturation": 32101, + "rera": 32102, + "Ġfarther": 32103, + "Ġrounding": 32104, + "Ġlibitum": 32105, + "Ġshuff": 32106, + "ĠOw": 32107, + "Ġlocalised": 32108, + "ĠALG": 32109, + "Ġhypertrophic": 32110, + "ppm": 32111, + "imine": 32112, + "ĠAthe": 32113, + "Ġanhydro": 32114, + "Ġsupramolecular": 32115, + "Ġmacros": 32116, + "aceted": 32117, + "ĠOliv": 32118, + "Ġmotivational": 32119, + "ĠCave": 32120, + "enzie": 32121, + "Ġaffiliated": 32122, + "Fermi": 32123, + "Ġequalities": 32124, + "ĠMilan": 32125, + "Ġdressed": 32126, + "Ġanger": 32127, + "ados": 32128, + "Ġavg": 32129, + "ĠPhon": 32130, + "Ġradioactivity": 32131, + "ĠEch": 32132, + "Ġorganoids": 32133, + "Ġïģ§": 32134, + "ĠAnthrop": 32135, + "lateral": 32136, + "Ġalpine": 32137, + "Ġaudit": 32138, + "WER": 32139, + "ĠCSC": 32140, + "Ġrankings": 32141, + "ĠERR": 32142, + "GLER": 32143, + "Obviously": 32144, + "ĠMadrid": 32145, + "obenzene": 32146, + "othermia": 32147, + "Ġresponsibilities": 32148, + "omestic": 32149, + "ĠInflation": 32150, + "Ġepidemics": 32151, + "Ġtaut": 32152, + "phos": 32153, + "ĠUnless": 32154, + "Ġgeomagnetic": 32155, + "ĠCFTR": 32156, + "veld": 32157, + "arietal": 32158, + "Ġendotoxin": 32159, + "ADP": 32160, + "Ġsuppressive": 32161, + "randial": 32162, + "Ġïĥ©": 32163, + "excited": 32164, + "ĠInnate": 32165, + "ĠLópez": 32166, + "omycetes": 32167, + "Ġbeautiful": 32168, + "irk": 32169, + "ĠHwang": 32170, + "ĠUSE": 32171, + "ÏĢi": 32172, + "Record": 32173, + "Attribute": 32174, + "Ġreacts": 32175, + "ĠBund": 32176, + "Ġcowork": 32177, + "Ġconfluence": 32178, + "ĠRegardless": 32179, + "Ġmetagenomic": 32180, + "MAL": 32181, + "Ġaided": 32182, + "anga": 32183, + "Ġamn": 32184, + "ĠICI": 32185, + "ĠPML": 32186, + "Ġdelivers": 32187, + "Ġkeyp": 32188, + "Ġbeetles": 32189, + "Ġoxidant": 32190, + "Immun": 32191, + "Ġrhythmic": 32192, + "female": 32193, + "JC": 32194, + "PAD": 32195, + "genitor": 32196, + "AMS": 32197, + "catalytic": 32198, + "ĠMom": 32199, + "ĠHert": 32200, + "adish": 32201, + "Ġcontention": 32202, + "Ġyolk": 32203, + "Ġdemyel": 32204, + "Ġsucc": 32205, + "Ġtravels": 32206, + "Ve": 32207, + "ĠFul": 32208, + "ĠRif": 32209, + "Ġintrons": 32210, + "encaps": 32211, + "colour": 32212, + "Ġhotel": 32213, + "Access": 32214, + "adoop": 32215, + "Ġcoalition": 32216, + "ĠMuh": 32217, + "ĠLTP": 32218, + "autom": 32219, + "ĠLak": 32220, + "Ġremedi": 32221, + "Ġtrailing": 32222, + "insulator": 32223, + "ĠRelig": 32224, + "ĠHudson": 32225, + "emics": 32226, + "OAc": 32227, + "ourt": 32228, + "Ġrelic": 32229, + "ĠMixture": 32230, + "Ġcalorimeter": 32231, + "ĠRDF": 32232, + "ĠHodgkin": 32233, + "Newtonian": 32234, + "ĠDelayed": 32235, + "ĠNortheast": 32236, + "hering": 32237, + "Ġhelices": 32238, + "Ġprincipally": 32239, + "Ġsuspicion": 32240, + "Ġextremities": 32241, + "Ġdeadline": 32242, + "ĠEnterococcus": 32243, + "mj": 32244, + "Ġhp": 32245, + "ĠNAS": 32246, + "ouss": 32247, + "Ġintramuscular": 32248, + "LIN": 32249, + "Ġchicks": 32250, + "Score": 32251, + "Ġfür": 32252, + "ĠRSA": 32253, + "Ġkr": 32254, + "Ġphotography": 32255, + "Ġclearing": 32256, + "holomorphic": 32257, + "them": 32258, + "Ġpom": 32259, + "ĠLis": 32260, + "Ġdiscard": 32261, + "Ġguan": 32262, + "cx": 32263, + "ubov": 32264, + "ĠConsistency": 32265, + "Ġplei": 32266, + "ĠUrinary": 32267, + "Ġbreadth": 32268, + "EI": 32269, + "mechan": 32270, + "Ġdq": 32271, + "ĠBlast": 32272, + "coeff": 32273, + "ILD": 32274, + "Ġunemployment": 32275, + "Arm": 32276, + "ĠCn": 32277, + "moderate": 32278, + "Ġaggress": 32279, + "Ġcircumf": 32280, + "los": 32281, + "Ġbaro": 32282, + "velope": 32283, + "Ġulcerative": 32284, + "Ġhelicase": 32285, + "HW": 32286, + "KG": 32287, + "rion": 32288, + "Ġgenotyped": 32289, + "Ġarid": 32290, + "ĠAndreas": 32291, + "Ġthereof": 32292, + "ĠOperating": 32293, + "ĠNEW": 32294, + "ĠAntibacterial": 32295, + "ĠDarwin": 32296, + "Ġreferee": 32297, + "Ġdome": 32298, + "agus": 32299, + "ĠDMD": 32300, + "ATOR": 32301, + "Currently": 32302, + "ĠInequalities": 32303, + "dN": 32304, + "olymer": 32305, + "empirical": 32306, + "ĠBraun": 32307, + "FIN": 32308, + "ĠOber": 32309, + "prone": 32310, + "Ġdiminish": 32311, + "ĠGraduate": 32312, + "ĠTSH": 32313, + "ĠHsu": 32314, + "oidosis": 32315, + "Ġepidural": 32316, + "Ġreinforcing": 32317, + "Ġtheatre": 32318, + "Ġvib": 32319, + "ĠHob": 32320, + "collection": 32321, + "MANGLER": 32322, + "ĠHecke": 32323, + "Ġtruck": 32324, + "Ġmotivates": 32325, + "ĠVOC": 32326, + "Ġunbound": 32327, + "ramid": 32328, + "iously": 32329, + "ĠFernández": 32330, + "ĠFacial": 32331, + "oxazol": 32332, + "Ġtreadm": 32333, + "ĠResid": 32334, + "Loader": 32335, + "ĠRunning": 32336, + "otinib": 32337, + "PAC": 32338, + "VII": 32339, + "iu": 32340, + "Ġcite": 32341, + "ĠHockey": 32342, + "ESC": 32343, + "rhoea": 32344, + "Ġmacaques": 32345, + "Ġmediast": 32346, + "atim": 32347, + "ĠTMP": 32348, + "ĠAGB": 32349, + "ĠRup": 32350, + "uga": 32351, + "Ġassurance": 32352, + "pay": 32353, + "energies": 32354, + "ĠKend": 32355, + "tillery": 32356, + "Ġanesthetic": 32357, + "Window": 32358, + "Ġbeverages": 32359, + "aguchi": 32360, + "ĠFLT": 32361, + "ĠBounded": 32362, + "ĠPolymerase": 32363, + "Sam": 32364, + "ĠOrbit": 32365, + "Ġseasonality": 32366, + "Ġtachycardia": 32367, + "esteem": 32368, + "ĠPerfect": 32369, + "SEC": 32370, + "later": 32371, + "tale": 32372, + "ĠFormally": 32373, + "LG": 32374, + "zyn": 32375, + "Ġmicroalgae": 32376, + "Ġindium": 32377, + "erennial": 32378, + "ĠIPT": 32379, + "Ġkj": 32380, + "ĠPDA": 32381, + "Ġassimil": 32382, + "wheel": 32383, + "ĠSOS": 32384, + "ĠPFC": 32385, + "Ġdecoded": 32386, + "ATS": 32387, + "Ġsocietal": 32388, + "Ġdiffeomorphisms": 32389, + "Ġtraverse": 32390, + "Ġcollateral": 32391, + "gives": 32392, + "ĠCEN": 32393, + "Ġrand": 32394, + "Ġherself": 32395, + "Ġpayments": 32396, + "Ġpsi": 32397, + "âIJ£": 32398, + "ĠGromov": 32399, + "Ġaccidental": 32400, + "ĠReality": 32401, + "Ġlogistics": 32402, + "Ġrobustly": 32403, + "ĠSarah": 32404, + "NU": 32405, + "dates": 32406, + "ĠCUR": 32407, + "ĠDream": 32408, + "Ġdegrades": 32409, + "ĠGEO": 32410, + "Ġbutterfly": 32411, + "Ġpendulum": 32412, + "qa": 32413, + "Ġaspartate": 32414, + "pseudo": 32415, + "Ġallosteric": 32416, + "derr": 32417, + "ĠQoL": 32418, + "Agilent": 32419, + "ĠHardware": 32420, + "ĠCumulative": 32421, + "Ġpn": 32422, + "quantitative": 32423, + "Ġappraisal": 32424, + "Ġpolyacrylamide": 32425, + "Ġmildly": 32426, + "Ġcontraceptive": 32427, + "ĠPublished": 32428, + "Ġuplift": 32429, + "beh": 32430, + "Ġadaptor": 32431, + "ĠEqual": 32432, + "thienyl": 32433, + "atched": 32434, + "Ġreply": 32435, + "Ġupwards": 32436, + "Ġautopsy": 32437, + "simulation": 32438, + "Ġgranite": 32439, + "Ġpelvis": 32440, + "Ġhatching": 32441, + "ĠSPS": 32442, + "ĠGEM": 32443, + "illiard": 32444, + "ĠRetrospective": 32445, + "ĠEarthqu": 32446, + "ĠInvestigations": 32447, + "ĠMerck": 32448, + "Ġcholangi": 32449, + "Ġinfiltrating": 32450, + "Ġoverestimated": 32451, + "focused": 32452, + "Amin": 32453, + "Ġpreeclampsia": 32454, + "ospatial": 32455, + "ĠTRAIL": 32456, + "Pair": 32457, + "Ġsubmarine": 32458, + "Ġproteolysis": 32459, + "Ġcomplements": 32460, + "ĠKirch": 32461, + "Ġcentrom": 32462, + "Ġnap": 32463, + "ĠWear": 32464, + "Ġpunishment": 32465, + "Ġautoregressive": 32466, + "Ġcomposer": 32467, + "ĠEngel": 32468, + "Ġanaemia": 32469, + "ĠKronecker": 32470, + "ĠDid": 32471, + "ĠCarp": 32472, + "peer": 32473, + "Ġbugs": 32474, + "ĠIslamic": 32475, + "ithromycin": 32476, + "Ġconsec": 32477, + "Ġfamiliarity": 32478, + "etaxel": 32479, + "Ġintensively": 32480, + "ĠUpt": 32481, + "Ġindica": 32482, + "ADA": 32483, + "ĠChebyshev": 32484, + "Ġhierarchies": 32485, + "Ġworthwhile": 32486, + "Ġburned": 32487, + "ĠHMGB": 32488, + "Ġpolygonal": 32489, + "brile": 32490, + "Ġzoon": 32491, + "warning": 32492, + "Eukaryota": 32493, + "dA": 32494, + "ĠRepeated": 32495, + "ĠCastro": 32496, + "Ġmetropolitan": 32497, + "ontinuous": 32498, + "ĠBarnes": 32499, + "ĠPostoperative": 32500, + "Ġcytology": 32501, + "Ġspotted": 32502, + "versity": 32503, + "affine": 32504, + "sorted": 32505, + "ĠProto": 32506, + "ĠDescriptive": 32507, + "Ġhitting": 32508, + "Ġanalogously": 32509, + "feedback": 32510, + "Ġspiritual": 32511, + "ĠLINE": 32512, + "ressin": 32513, + "ophthal": 32514, + "Ġpolyunsaturated": 32515, + "Ġpiper": 32516, + "observations": 32517, + "ĭ¤": 32518, + "irre": 32519, + "ĠWNT": 32520, + "Ġundifferentiated": 32521, + "erald": 32522, + "ĠCTC": 32523, + "Ġhomomorphisms": 32524, + "ĠNeonatal": 32525, + "Fin": 32526, + "rozen": 32527, + "ĠLux": 32528, + "Ġmodifier": 32529, + "ĠKA": 32530, + "osaur": 32531, + "Ġinterventional": 32532, + "ĠHapl": 32533, + "Ġluminance": 32534, + "Ġunfortunately": 32535, + "Ġsleeping": 32536, + "Ġcitrus": 32537, + "resonance": 32538, + "Ġmoss": 32539, + "ulay": 32540, + "ĠPenn": 32541, + "administration": 32542, + "ĠNGF": 32543, + "Ġsecured": 32544, + "ĠAEs": 32545, + "ĠPWM": 32546, + "occo": 32547, + "obuf": 32548, + "Ġphotocurrent": 32549, + "ĠScilabDouble": 32550, + "April": 32551, + "Ġforamin": 32552, + "Ġparalysis": 32553, + "ĠQuark": 32554, + "eqref": 32555, + "ĠBrooks": 32556, + "ĠCollision": 32557, + "War": 32558, + "Ġig": 32559, + "amylase": 32560, + "istered": 32561, + "Ġretraction": 32562, + "ĠMultiplex": 32563, + "ĠMao": 32564, + "Common": 32565, + "ĠEconomics": 32566, + "ĠCriterion": 32567, + "ĠCCC": 32568, + "ĠLei": 32569, + "Ġorthorhombic": 32570, + "Ġaliquots": 32571, + "Ġstric": 32572, + "ĠLenn": 32573, + "Ġdisclosure": 32574, + "ameth": 32575, + "Ġnormalisation": 32576, + "Ġphylogen": 32577, + "ĠQTLs": 32578, + "ĠVersus": 32579, + "ĠUtilization": 32580, + "yne": 32581, + "unted": 32582, + "ĠDuff": 32583, + "ĠGJ": 32584, + "Ġoptimised": 32585, + "iformis": 32586, + "ĠIncreases": 32587, + "ĠFDG": 32588, + "ĠBattery": 32589, + "Phe": 32590, + "ĠCCS": 32591, + "Ġchrys": 32592, + "ofen": 32593, + "Ġmulticomponent": 32594, + "discussed": 32595, + "bonding": 32596, + "oretically": 32597, + "ĠAlliance": 32598, + "Ġheadquarters": 32599, + "ĠGlasgow": 32600, + "Ġbout": 32601, + "Ġeighth": 32602, + "Ġincurred": 32603, + "ĠBarry": 32604, + "Ġquadric": 32605, + "Ġduties": 32606, + "Ġmindfulness": 32607, + "rastructural": 32608, + "Train": 32609, + "shitz": 32610, + "CDC": 32611, + "Ġdyslipidemia": 32612, + "Ġalleged": 32613, + "Ġbronze": 32614, + "Ġattainment": 32615, + "QD": 32616, + "rombin": 32617, + "Ġapolipoprotein": 32618, + "owned": 32619, + "Ġgeographically": 32620, + "working": 32621, + "ĠBlind": 32622, + "Ġdonation": 32623, + "ĠSerge": 32624, + "Ġspreads": 32625, + "ĠHeterogeneity": 32626, + "ĠFré": 32627, + "Ġdefer": 32628, + "Ġlifts": 32629, + "EGFR": 32630, + "ĠPortland": 32631, + "Ġbrothers": 32632, + "ĠTrypanosoma": 32633, + "inian": 32634, + "Ġpressed": 32635, + "Ġtransduced": 32636, + "Ġpolyn": 32637, + "Ġlisteners": 32638, + "boards": 32639, + "ĠSustainable": 32640, + "alan": 32641, + "ĠSullivan": 32642, + "Assumption": 32643, + "often": 32644, + "jp": 32645, + "orative": 32646, + "plers": 32647, + "Ġmodularity": 32648, + "ĠHermite": 32649, + "Ġhydroxyapatite": 32650, + "ĠHirsch": 32651, + "Determ": 32652, + "facing": 32653, + "irradiated": 32654, + "Ġharsh": 32655, + "Ġtolerate": 32656, + "ĠTrap": 32657, + "ĠAware": 32658, + "otax": 32659, + "ATING": 32660, + "Ġhistopathology": 32661, + "ĠIsraeli": 32662, + "clockwise": 32663, + "zig": 32664, + "ĠJC": 32665, + "ĠQuick": 32666, + "ĠSLAM": 32667, + "Ġfox": 32668, + "ĠRav": 32669, + "generating": 32670, + "Ġhematoxylin": 32671, + "yltransferase": 32672, + "Ġcorroborated": 32673, + "FDR": 32674, + "oard": 32675, + "Ġequid": 32676, + "Ġ»": 32677, + "Ġneuropsychological": 32678, + "Ġbreakup": 32679, + "Ġemphasizing": 32680, + "Ġemissivity": 32681, + "blocking": 32682, + "Ġparall": 32683, + "Ġtilting": 32684, + "Ġpeng": 32685, + "ĠScan": 32686, + "Ġionosphere": 32687, + "Ġmount": 32688, + "forest": 32689, + "Ġcallus": 32690, + "αβ": 32691, + "ĠChristmas": 32692, + "ĠMagazine": 32693, + "evaluate": 32694, + "ĠPag": 32695, + "ĠBeat": 32696, + "Ġaccumulates": 32697, + "Ġcrowding": 32698, + "unneling": 32699, + "ĠÑ": 32700, + "ĠACP": 32701, + "geometry": 32702, + "MPT": 32703, + "Ġpharmacists": 32704, + "Ġpullback": 32705, + "Ġductility": 32706, + "Supervised": 32707, + "Ġlymphoblastic": 32708, + "pea": 32709, + "typical": 32710, + "broken": 32711, + "Fc": 32712, + "Ġlining": 32713, + "ĠDum": 32714, + "Ġmultiples": 32715, + "ów": 32716, + "Ġmerits": 32717, + "Ġextinct": 32718, + "ĠNursing": 32719, + "ĠExploiting": 32720, + "ĠBhattach": 32721, + "July": 32722, + "tze": 32723, + "thromb": 32724, + "teenth": 32725, + "Ġtoxicities": 32726, + "Ġdenitrification": 32727, + "Ġexposition": 32728, + "Ġimperf": 32729, + "Ġsurname": 32730, + "pointer": 32731, + "ĠErn": 32732, + "ĠAbundance": 32733, + "ĠDunn": 32734, + "ophora": 32735, + "Ġtoolkit": 32736, + "Load": 32737, + "ĠDerivation": 32738, + "could": 32739, + "ĠCaspase": 32740, + "ĠSprague": 32741, + "ĠTrp": 32742, + "Ġbrightest": 32743, + "illard": 32744, + "Ġinterdisciplinary": 32745, + "Ġquarant": 32746, + "Ġhypersurfaces": 32747, + "eliac": 32748, + "ĠALMA": 32749, + "Ġacrylic": 32750, + "Ġgentle": 32751, + "Deep": 32752, + "ĠPandemic": 32753, + "Ġinfeasible": 32754, + "Ġradiol": 32755, + "ABP": 32756, + "Ġmesenteric": 32757, + "ylinder": 32758, + "packed": 32759, + "Ġsomatosensory": 32760, + "Ġpave": 32761, + "ĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ": 32762, + "Ġpharmacology": 32763, + "Ġtanh": 32764, + "ĠMtb": 32765, + "Ġchimpan": 32766, + "Ġautophagic": 32767, + "Ġwithdrawn": 32768, + "ĠMCC": 32769, + "ZF": 32770, + "ĠSpl": 32771, + "ĠLau": 32772, + "Ġbiologic": 32773, + "electrons": 32774, + "Ġunderestimation": 32775, + "Ġcharacterise": 32776, + "circular": 32777, + "ĠTHEORY": 32778, + "Brown": 32779, + "FBS": 32780, + "Jo": 32781, + "dG": 32782, + "mars": 32783, + "articular": 32784, + "ĠPren": 32785, + "ĠMSA": 32786, + "ĠItem": 32787, + "Ġsemidefinite": 32788, + "ĠGibson": 32789, + "Ġtourism": 32790, + "ĠKok": 32791, + "Ġexposing": 32792, + "Ġintravenously": 32793, + "driver": 32794, + "ĠFortunately": 32795, + "ĠSach": 32796, + "Ġcontaminant": 32797, + "Ġabrog": 32798, + "ĠEmotional": 32799, + "VALUE": 32800, + "dispersion": 32801, + "Jacobi": 32802, + "ĠImperial": 32803, + "Ion": 32804, + "Lin": 32805, + "fidelity": 32806, + "ĠBirds": 32807, + "ĠConcurrent": 32808, + "matism": 32809, + "coal": 32810, + "Ġtq": 32811, + "ĠMnO": 32812, + "Ġfossils": 32813, + "Ġtender": 32814, + "Ġrhesus": 32815, + "Ġbloom": 32816, + "abdominal": 32817, + "Ġscalp": 32818, + "Ġhomeostatic": 32819, + "ĠHunt": 32820, + "ĠPharmacokine": 32821, + "brown": 32822, + "ĠHYP": 32823, + "Ġdissociated": 32824, + "ĠSoccer": 32825, + "ĠInequality": 32826, + "maker": 32827, + "Ġshade": 32828, + "ĠZur": 32829, + "observation": 32830, + "altered": 32831, + "UU": 32832, + "Ġtheor": 32833, + "epit": 32834, + "Ġphylum": 32835, + "Ġvigorous": 32836, + "ĠACM": 32837, + "Ġmethotrexate": 32838, + "demographic": 32839, + "Ġsingly": 32840, + "ĠPhysiology": 32841, + "Ġremodelling": 32842, + "ĠKrist": 32843, + "ropies": 32844, + "flows": 32845, + "hardness": 32846, + "ighteen": 32847, + "breve": 32848, + "ĠRetinal": 32849, + "Ġscintill": 32850, + "Ġutterance": 32851, + "Ġmonolithic": 32852, + "ĠVlad": 32853, + "ĠLMC": 32854, + "ipt": 32855, + "arrows": 32856, + "ĠPublishing": 32857, + "ĠStreptomyces": 32858, + "fal": 32859, + "Ġtroposphere": 32860, + "Ben": 32861, + "candid": 32862, + "ĠSic": 32863, + "timore": 32864, + "Len": 32865, + "inen": 32866, + "ampered": 32867, + "ĠMonth": 32868, + "Ġopponent": 32869, + "August": 32870, + "Ġstaggered": 32871, + "centre": 32872, + "expect": 32873, + "Ġreddening": 32874, + "ĠTl": 32875, + "hibition": 32876, + "Ġmicroparticles": 32877, + "ĠIntrac": 32878, + "ĠInitialize": 32879, + "Ġdictated": 32880, + "Dig": 32881, + "äº": 32882, + "healing": 32883, + "ĠdV": 32884, + "Ġappetite": 32885, + "Ġunusually": 32886, + "ĠAstronomy": 32887, + "Ġware": 32888, + "Ġovercoming": 32889, + "Ġcolliders": 32890, + "ĠUSING": 32891, + "ocarditis": 32892, + "Pick": 32893, + "Ġdub": 32894, + "ĠJason": 32895, + "ĠEditor": 32896, + "ê³": 32897, + "Ġlags": 32898, + "Ġcls": 32899, + "Ġsurgically": 32900, + "ĠPVC": 32901, + "particularly": 32902, + "Ġredist": 32903, + "Ġlogics": 32904, + "skii": 32905, + "ĠDVD": 32906, + "Ġcomply": 32907, + "azi": 32908, + "ĠInteracts": 32909, + "boolean": 32910, + "ĠERP": 32911, + "ĠErr": 32912, + "otranspiration": 32913, + "ĠPérez": 32914, + "Asp": 32915, + "amiliar": 32916, + "ĠFetal": 32917, + "Ġdeclaration": 32918, + "kinson": 32919, + "tube": 32920, + "Ġphysiologically": 32921, + "cue": 32922, + "ĠEri": 32923, + "Ġenvision": 32924, + "external": 32925, + "intermediate": 32926, + "Ġshopping": 32927, + "ĠFras": 32928, + "ĠHaj": 32929, + "ĠAlger": 32930, + "Ġanthropometric": 32931, + "Ġcancelled": 32932, + "HPV": 32933, + "kers": 32934, + "afa": 32935, + "Ġvulnerabilities": 32936, + "electrolyte": 32937, + "ĠGonzalez": 32938, + "íķĺ": 32939, + "qv": 32940, + "Ġdeaf": 32941, + "Ġbutyrate": 32942, + "ĠCoefficient": 32943, + "Ġstarburst": 32944, + "Ġpolymorph": 32945, + "ĠERA": 32946, + "ĠMaximal": 32947, + "ĠMueller": 32948, + "Ġabsorbers": 32949, + "Ġarab": 32950, + "retions": 32951, + "Ġnebula": 32952, + "Ġmines": 32953, + "ен": 32954, + "%%%%%%%%%%%%%%%%": 32955, + "Ġbandpass": 32956, + "Ġpolyurethane": 32957, + "ReLU": 32958, + "ĠFerro": 32959, + "picillin": 32960, + "CAD": 32961, + "Ty": 32962, + "ĠPCD": 32963, + "ĠBAC": 32964, + "Ġplanktonic": 32965, + "Fer": 32966, + "Ġcricket": 32967, + "Ġmanure": 32968, + "ouns": 32969, + "âΧ": 32970, + "Ġtorques": 32971, + "mitian": 32972, + "Ġtion": 32973, + "ĠGarden": 32974, + "Ġfolk": 32975, + "Ġsuspicious": 32976, + "ÃĤ": 32977, + "odia": 32978, + "istencies": 32979, + "ãĢī": 32980, + "ĠInvitrogen": 32981, + "ĠSUN": 32982, + "ĠSuperior": 32983, + "Ġdiscontinuation": 32984, + "cock": 32985, + "knot": 32986, + "Ġextens": 32987, + "ĠWhitney": 32988, + "Ġharbour": 32989, + "PID": 32990, + "Ġpmol": 32991, + "olymph": 32992, + "Ġgard": 32993, + "ĠOvarian": 32994, + "Ġrepressed": 32995, + "ĠAlab": 32996, + "ĠÃĦ": 32997, + "ulex": 32998, + "ĠAustrian": 32999, + "Ġaflat": 33000, + "Ġparathyroid": 33001, + "Ġgroupoid": 33002, + "Ġdevast": 33003, + "ĠKv": 33004, + "Ġborrow": 33005, + "Ġunconventional": 33006, + "Ġborehole": 33007, + "ÑĮ": 33008, + "ĠDays": 33009, + "Ġlexic": 33010, + "Nor": 33011, + "ĠHerc": 33012, + "assays": 33013, + "Ġdrawings": 33014, + "defin": 33015, + "evoked": 33016, + "Ġȳ": 33017, + "ĠSunday": 33018, + "ĠChes": 33019, + "considered": 33020, + "opedic": 33021, + "larger": 33022, + "ominant": 33023, + "ĠBomb": 33024, + "Ġfiss": 33025, + "Ġhinge": 33026, + "ĠIonic": 33027, + "Ġdestro": 33028, + "Ġcomplementarity": 33029, + "Higgs": 33030, + "oria": 33031, + "ourcing": 33032, + "ĠXin": 33033, + "Ġworkspace": 33034, + "ĠLigand": 33035, + "Ġstruggle": 33036, + "ĠImmunohistochemical": 33037, + "Ġnick": 33038, + "ĠGuard": 33039, + "rigid": 33040, + "Ġaquaculture": 33041, + "Experiment": 33042, + "ËĪ": 33043, + "tir": 33044, + "ĠSMS": 33045, + "Ġbevacizumab": 33046, + "Ġmodulations": 33047, + "Ġgeophysical": 33048, + "Properties": 33049, + "Ġpainted": 33050, + "Ġsanc": 33051, + "Ġintimate": 33052, + "Ġnail": 33053, + "identity": 33054, + "Ġdatum": 33055, + "anthus": 33056, + "Ġdyadic": 33057, + "Ġconvincing": 33058, + "elem": 33059, + "Ġhiding": 33060, + "Ġrugby": 33061, + "ĠXe": 33062, + "ĠIssue": 33063, + "Ġvesicular": 33064, + "ĠKelvin": 33065, + "Ġdistancing": 33066, + "echnology": 33067, + "afers": 33068, + "ĠAuthentic": 33069, + "PubMed": 33070, + "Ġdeformity": 33071, + "ĠChaos": 33072, + "ĠShield": 33073, + "oxetine": 33074, + "ĠWorkers": 33075, + "ĠMOI": 33076, + "Ġdehydrated": 33077, + "ĠGastric": 33078, + "Ġmonomials": 33079, + "odox": 33080, + "ĠDublin": 33081, + "Ġleishman": 33082, + "Ġplanner": 33083, + "circle": 33084, + "Ġfractured": 33085, + "ĠLocally": 33086, + "ĠActions": 33087, + "Ġlichen": 33088, + "hannel": 33089, + "ĠTAG": 33090, + "Ġdecisive": 33091, + "ĠQM": 33092, + "Ġbiomaterials": 33093, + "ĠViruses": 33094, + "hydroxyphenyl": 33095, + "ĠIAA": 33096, + "ĠRU": 33097, + "violating": 33098, + "Ġpockets": 33099, + "chant": 33100, + "iberg": 33101, + "lectomy": 33102, + "olerae": 33103, + "Ġattracting": 33104, + "Ġketone": 33105, + "ĠCod": 33106, + "Ġmicroarrays": 33107, + "ĠMetals": 33108, + "benzoyl": 33109, + "Ġsemigroups": 33110, + "Ġreconstituted": 33111, + "sites": 33112, + "anabe": 33113, + "ĠComposites": 33114, + "Ġwildtype": 33115, + "Ġleukaemia": 33116, + "Ġmurder": 33117, + "Ġdentin": 33118, + "Hub": 33119, + "Orient": 33120, + "onn": 33121, + "synchron": 33122, + "Ġchronically": 33123, + "methyleneamino": 33124, + "Ġdopant": 33125, + "Ġfecundity": 33126, + "delete": 33127, + "remia": 33128, + "ĠNHL": 33129, + "itidis": 33130, + "Ġcopep": 33131, + "XI": 33132, + "Ġlocating": 33133, + "ĠZIKV": 33134, + "hexa": 33135, + "ĠFactorization": 33136, + "ynchus": 33137, + "Methyl": 33138, + "hagen": 33139, + "ĠPaw": 33140, + "neath": 33141, + "bsite": 33142, + "Ġtrache": 33143, + "Bre": 33144, + "uw": 33145, + "roit": 33146, + "Ġreacting": 33147, + "ĠBae": 33148, + "Ġquotients": 33149, + "Ġpins": 33150, + "ĠVARI": 33151, + "Ġequine": 33152, + "ĠRunge": 33153, + "Ġcolonial": 33154, + "measurement": 33155, + "ĠAbbott": 33156, + "Ġortho": 33157, + "Ġmetaphor": 33158, + "benzoic": 33159, + "ĠTransformers": 33160, + "Lower": 33161, + "ĠOVA": 33162, + "radial": 33163, + "Flag": 33164, + "authorbs": 33165, + "Ġtreadmill": 33166, + "Ġenterica": 33167, + "ĠJulia": 33168, + "Ġplumes": 33169, + "Ġinvoke": 33170, + "chloric": 33171, + "olino": 33172, + "Ġinterruption": 33173, + "subunit": 33174, + "ĠMDP": 33175, + "Ġmanipulator": 33176, + "ĠScales": 33177, + "ĠHTML": 33178, + "ĠFrederick": 33179, + "Garc": 33180, + "Ġbell": 33181, + "ĠRect": 33182, + "romised": 33183, + "Word": 33184, + "oples": 33185, + "operated": 33186, + "Ġcollects": 33187, + "ĠHorizon": 33188, + "Ġsafer": 33189, + "dup": 33190, + "ĠMills": 33191, + "ALP": 33192, + "Ġexopl": 33193, + "ATTR": 33194, + "wara": 33195, + "ĉĉĉĉĉĉĉ": 33196, + "Ġdebug": 33197, + "Descriptor": 33198, + "statistics": 33199, + "ĠCub": 33200, + "STER": 33201, + "ĠStabilization": 33202, + "ĠIRAS": 33203, + "Ġconformally": 33204, + "Adap": 33205, + "ÂŃ": 33206, + "ĠQS": 33207, + "Ġmicrostrip": 33208, + "Ġdelicate": 33209, + "Ġpublisher": 33210, + "Ġhos": 33211, + "ĠSv": 33212, + "ĠDesert": 33213, + "ĠGuer": 33214, + "ĠCapture": 33215, + "EBP": 33216, + "dust": 33217, + "å¤": 33218, + "ĠOls": 33219, + "Ġsuperscript": 33220, + "ĠFluctuations": 33221, + "illium": 33222, + "Ġcaption": 33223, + "Ġconcur": 33224, + "Ġquantifies": 33225, + "sterdam": 33226, + "Ġspiked": 33227, + "Nan": 33228, + "usin": 33229, + "ĠLAN": 33230, + "Ġobserves": 33231, + "ĠAla": 33232, + "ĠIntuitively": 33233, + "curr": 33234, + "Ġshrinking": 33235, + "Ġcompressibility": 33236, + "orporeal": 33237, + "Ġdebt": 33238, + "çĶ": 33239, + "ĠTil": 33240, + "ĠWAT": 33241, + "odyne": 33242, + "Ġgateway": 33243, + "Ġductile": 33244, + "ĠJesus": 33245, + "ositol": 33246, + "ĠMales": 33247, + "Ġsolvation": 33248, + "Ġdisagree": 33249, + "Ġorthologs": 33250, + "San": 33251, + "igo": 33252, + "Ġphages": 33253, + "Ġnegatives": 33254, + "Ġinterpre": 33255, + "AAA": 33256, + "Ġgratings": 33257, + "ĠMoll": 33258, + "ĠRivers": 33259, + "Ġcruzi": 33260, + "ĠGenerate": 33261, + "ĠBarbara": 33262, + "ĠHeritage": 33263, + "ĠFluorescent": 33264, + "ĠLaws": 33265, + "ArrayExpr": 33266, + "Ġmultipole": 33267, + "Ġsqueezing": 33268, + "SPSS": 33269, + "lf": 33270, + "nlm": 33271, + "Ġworn": 33272, + "ĠKuz": 33273, + "Ġgenesis": 33274, + "ĠEmperor": 33275, + "volatile": 33276, + "Ġsibling": 33277, + "ivir": 33278, + "oen": 33279, + "Ġprotost": 33280, + "Ġtransformers": 33281, + "ennium": 33282, + "Ġproposing": 33283, + "Ġbroadcasting": 33284, + "QM": 33285, + "ĠDependent": 33286, + "Ġdisable": 33287, + "ĠUAS": 33288, + "Ġwarnings": 33289, + "Ġarmed": 33290, + "Ġjournalist": 33291, + "Ġmonoclinic": 33292, + "olium": 33293, + "aping": 33294, + "toon": 33295, + "Ġorthodontic": 33296, + "ĠNormalization": 33297, + "Ġmandible": 33298, + "aban": 33299, + "ĠWak": 33300, + "extend": 33301, + "Multiple": 33302, + "investig": 33303, + "iscal": 33304, + "uttered": 33305, + "Ġburg": 33306, + "decode": 33307, + "empor": 33308, + "ĠDuration": 33309, + "anny": 33310, + "oprost": 33311, + "ĠRenormalization": 33312, + "ĠFUNCTION": 33313, + "ytorch": 33314, + "Ġsynapt": 33315, + "ĠFormat": 33316, + "ĠCRT": 33317, + "ĠJonathan": 33318, + "ĠOFF": 33319, + "orr": 33320, + "Ġresur": 33321, + "Ġcorruption": 33322, + "dwelling": 33323, + "Ġbackup": 33324, + "AGT": 33325, + "ĠSafe": 33326, + "dorfer": 33327, + "Ġataxia": 33328, + "Ġparv": 33329, + "reader": 33330, + "Ġsubtract": 33331, + "embolism": 33332, + "Ġtinnitus": 33333, + "Ġcytomegalovirus": 33334, + "Ġdeleting": 33335, + "Tex": 33336, + "ĠCSS": 33337, + "ardt": 33338, + "Ġoutgrowth": 33339, + "Ġmyocytes": 33340, + "digital": 33341, + "Ġsubscale": 33342, + "uspension": 33343, + "Ġhamster": 33344, + "Ġinflaton": 33345, + "hara": 33346, + "urches": 33347, + "ĠCLE": 33348, + "ĠYas": 33349, + "ĠEncoding": 33350, + "ĠAuger": 33351, + "Ġanastomosis": 33352, + "Agent": 33353, + "ĠSIL": 33354, + "ĠCCT": 33355, + "Ġbrine": 33356, + "Ġoligo": 33357, + "Ġfluoro": 33358, + "Ġgallery": 33359, + "ddots": 33360, + "Ġcilia": 33361, + "ĠPPV": 33362, + "ĠUTR": 33363, + "Ġintertidal": 33364, + "ocalized": 33365, + "Ġcrowds": 33366, + "odor": 33367, + "Ġcov": 33368, + "Ġnonetheless": 33369, + "Ġïģ¤": 33370, + "Ġboosted": 33371, + "ĠChakra": 33372, + "Hal": 33373, + "Pear": 33374, + "Ġimprec": 33375, + "ĠSupplement": 33376, + "goal": 33377, + "Ġôı¼ģ": 33378, + "Ġstall": 33379, + "Ġherd": 33380, + "smaller": 33381, + "Ġreconstructing": 33382, + "Ġartefacts": 33383, + "Ġteg": 33384, + "conventional": 33385, + "radical": 33386, + "Ġliteral": 33387, + "framework": 33388, + "iprocal": 33389, + "EEG": 33390, + "Ġgins": 33391, + "odermal": 33392, + "ĠAgu": 33393, + "ĠTwelve": 33394, + "Mul": 33395, + "ب": 33396, + "irl": 33397, + "ĠBelief": 33398, + "Ġincont": 33399, + "ICC": 33400, + "hexane": 33401, + "Ġejected": 33402, + "ĠPSC": 33403, + "ĠHPC": 33404, + "ĠVH": 33405, + "Ġequivalences": 33406, + "plotlib": 33407, + "enital": 33408, + "rians": 33409, + "prov": 33410, + "ĠVibr": 33411, + "Ġgrammatical": 33412, + "bachia": 33413, + "acceptable": 33414, + "odicity": 33415, + "abb": 33416, + "Ġherbs": 33417, + "Ġpredominance": 33418, + "ĠOrientation": 33419, + "Ġinvertebrate": 33420, + "Ġpelagic": 33421, + "country": 33422, + "ĠOrigins": 33423, + "ĠAdolescents": 33424, + "ĠTuning": 33425, + "rainian": 33426, + "ĠScar": 33427, + "Ġlightest": 33428, + "Ġemitters": 33429, + "ĠTsai": 33430, + "ritical": 33431, + "ĠExpert": 33432, + "authors": 33433, + "ECTION": 33434, + "ĠSeverity": 33435, + "Nam": 33436, + "publ": 33437, + "ĠAbe": 33438, + "Ġnanocrystalline": 33439, + "ĠNakamura": 33440, + "ĠPec": 33441, + "ĠBug": 33442, + "Ġsensed": 33443, + "ONS": 33444, + "ICs": 33445, + "Ġelectrochem": 33446, + "ĠROM": 33447, + "ĠRecruitment": 33448, + "Ġ⣩": 33449, + "Ġbiomolecules": 33450, + "ĠBrac": 33451, + "Ġtransposition": 33452, + "ĠWP": 33453, + "ĠOmega": 33454, + "Ġdiagon": 33455, + "platelet": 33456, + "JM": 33457, + "acre": 33458, + "ĠASR": 33459, + "ĠKath": 33460, + "Ġpriv": 33461, + "oplasts": 33462, + "Samples": 33463, + "dF": 33464, + "atti": 33465, + "ĠSanger": 33466, + "ipitated": 33467, + "Ġricher": 33468, + "ĠGRA": 33469, + "Ġplantar": 33470, + "Ġfoams": 33471, + "Ġmathematic": 33472, + "Ġstaphyl": 33473, + "ĠUptake": 33474, + "Ġcant": 33475, + "ĠSZ": 33476, + "Ġdismiss": 33477, + "Ġselections": 33478, + "plitz": 33479, + "Ġexemplified": 33480, + "Ġtorsional": 33481, + "Ev": 33482, + "Ġvoters": 33483, + "ĠNest": 33484, + "yscale": 33485, + "Ġspeci": 33486, + "Ġpolished": 33487, + "Ġlatencies": 33488, + "qing": 33489, + "Ġonwards": 33490, + "llvm": 33491, + "theorem": 33492, + "logging": 33493, + "ĠALK": 33494, + "ĠBaum": 33495, + "ĠGhosh": 33496, + "Ġchairman": 33497, + "paired": 33498, + "ĠPAP": 33499, + "notes": 33500, + "olesterolem": 33501, + "Ġestuarine": 33502, + "ĠTibetan": 33503, + "ĠVER": 33504, + "Ġchecker": 33505, + "FLAGS": 33506, + "rolimus": 33507, + "ĠMutant": 33508, + "Ġspraying": 33509, + "ĠChest": 33510, + "olinium": 33511, + "ĠTriassic": 33512, + "Ġlidar": 33513, + "Art": 33514, + "ĠMilk": 33515, + "Ġindecomposable": 33516, + "Ġrocket": 33517, + "ĠPartners": 33518, + "Ġsemantically": 33519, + "entinel": 33520, + "Large": 33521, + "Pen": 33522, + "ĠTru": 33523, + "Ġheritage": 33524, + "ĠMutual": 33525, + "ĠChemotherapy": 33526, + "Ġdoubles": 33527, + "ĠEmbedded": 33528, + "itual": 33529, + "ĠBPA": 33530, + "Ġcholerae": 33531, + "ĠInside": 33532, + "ĠKatz": 33533, + "convergence": 33534, + "Ġindividualized": 33535, + "kinje": 33536, + "Ġdiscovering": 33537, + "Ġintricate": 33538, + "Ġinland": 33539, + "RECT": 33540, + "ĠChick": 33541, + "ĠSUR": 33542, + "Ġyeasts": 33543, + "luminosity": 33544, + "Ġfain": 33545, + "ioni": 33546, + "ĠTig": 33547, + "ounder": 33548, + "Ġdeliber": 33549, + "ĠConservative": 33550, + "ĠDelhi": 33551, + "BER": 33552, + "ĠYB": 33553, + "oley": 33554, + "ĠBeau": 33555, + "TEXT": 33556, + "Ġsqueezed": 33557, + "Ġsocket": 33558, + "ĠpT": 33559, + "pyrazol": 33560, + "coefficients": 33561, + "Ġrecruiting": 33562, + "Ġducts": 33563, + "Ġfoster": 33564, + "omeration": 33565, + "ĠPSI": 33566, + "ĠDup": 33567, + "Ġks": 33568, + "ĠOptics": 33569, + "Ġliterary": 33570, + "ĠNiO": 33571, + "ĠVEGFR": 33572, + "Ġgraviton": 33573, + "Ġutterances": 33574, + "Ġbrady": 33575, + "Ġforty": 33576, + "ĠTransplantation": 33577, + "Ġagreements": 33578, + "Leftrightarrow": 33579, + "waves": 33580, + "Ġacidosis": 33581, + "Ġwooden": 33582, + "ĠCytoplasmic": 33583, + "safe": 33584, + "Ġjumping": 33585, + "ennial": 33586, + "Various": 33587, + "ĠEryth": 33588, + "ulins": 33589, + "unlock": 33590, + "methylated": 33591, + "asserstein": 33592, + "Ġheterozygosity": 33593, + "oxycycl": 33594, + "Ġcreativity": 33595, + "MPLE": 33596, + "inative": 33597, + "Ġconvolutions": 33598, + "Ġnouns": 33599, + "egan": 33600, + "ĠAbraham": 33601, + "Ġdenser": 33602, + "Che": 33603, + "lc": 33604, + "ĉĉĉĠ": 33605, + "Ġsemim": 33606, + "ĠOuter": 33607, + "Ġcand": 33608, + "odule": 33609, + "esthesia": 33610, + "ĠJoy": 33611, + "ĠProtocols": 33612, + "ĠCalculated": 33613, + "atop": 33614, + "ĠFALSE": 33615, + "Ġrefin": 33616, + "Ġmigrants": 33617, + "ĠïĤ´": 33618, + "ĠSpecificity": 33619, + "ĠFellowship": 33620, + "ĠPMT": 33621, + "Ġdisclose": 33622, + "unches": 33623, + "Ġdiatoms": 33624, + "corr": 33625, + "Ġskyrm": 33626, + "Ġrenewal": 33627, + "gcd": 33628, + "cereb": 33629, + "Ġupright": 33630, + "Ġmesoscopic": 33631, + "hydraz": 33632, + "BAS": 33633, + "FLO": 33634, + "HCC": 33635, + "Mouse": 33636, + "Ġposet": 33637, + "Ġproteinuria": 33638, + "Ġreapp": 33639, + "ĠNickel": 33640, + "Ġstripes": 33641, + "Ġripple": 33642, + "September": 33643, + "odomain": 33644, + "ĠPope": 33645, + "ĠNons": 33646, + "Ġtechnic": 33647, + "Ġneutrop": 33648, + "descriptor": 33649, + "Ġdissipated": 33650, + "Ġglaciers": 33651, + "ĠHIGH": 33652, + "ĠLav": 33653, + "retely": 33654, + "Ġbackwards": 33655, + "Ġcritics": 33656, + "ĠExtending": 33657, + "bic": 33658, + "ĠChao": 33659, + "ofibr": 33660, + "Ġcounters": 33661, + "Ġstreets": 33662, + "Ġprosthetic": 33663, + "Ġbiodegradation": 33664, + "complexity": 33665, + "ĠSPL": 33666, + "ĠCAC": 33667, + "Ġadducts": 33668, + "Ġmorphometric": 33669, + "ĠMatt": 33670, + "Ġinducer": 33671, + "Ġastrocyte": 33672, + "Ġtriplets": 33673, + "Ġpertussis": 33674, + "PES": 33675, + "idy": 33676, + "uncertain": 33677, + "Ġhyperparameter": 33678, + "ĠInfrastructure": 33679, + "ìĿĺ": 33680, + "ZW": 33681, + "Ġaddr": 33682, + "Ġdisrupts": 33683, + "Ġoverestimate": 33684, + "ĠDYNA": 33685, + "Ġvolatiles": 33686, + "emerg": 33687, + "issue": 33688, + "cpp": 33689, + "Äħ": 33690, + "ĠVIP": 33691, + "Ġuve": 33692, + "ĠCNV": 33693, + "ylethyl": 33694, + "onazole": 33695, + "ĠHiro": 33696, + "Ġcn": 33697, + "tik": 33698, + "ubted": 33699, + "ĠJacobs": 33700, + "Ġadvocated": 33701, + "ĠBifid": 33702, + "material": 33703, + "Ġstyrene": 33704, + "ĠKeller": 33705, + "rocytic": 33706, + "pinephrine": 33707, + "ĠWritten": 33708, + "ĠRecommendation": 33709, + "bled": 33710, + "ĠBootstrap": 33711, + "thirds": 33712, + "Ġcaptain": 33713, + "equals": 33714, + "SRC": 33715, + "ĠKentucky": 33716, + "Ġeosinophils": 33717, + "Average": 33718, + "Hi": 33719, + "Whe": 33720, + "ĠDAT": 33721, + "ĠUM": 33722, + "Ġtendencies": 33723, + "ĠPeterson": 33724, + "Ġoccult": 33725, + "Ġexhibition": 33726, + "ĠINS": 33727, + "Ġadipocyte": 33728, + "Just": 33729, + "hift": 33730, + "tensors": 33731, + "Ġciliary": 33732, + "ipation": 33733, + "Ġmotivations": 33734, + "Ġwitnessed": 33735, + "itches": 33736, + "ĠSoy": 33737, + "Ġgib": 33738, + "eptic": 33739, + "ĠKOH": 33740, + "Ġïģ¨": 33741, + "ĠTorres": 33742, + "ο": 33743, + "arpo": 33744, + "okinase": 33745, + "ĠBudd": 33746, + "ĠGMM": 33747, + "Ġunderpin": 33748, + "Ġoptimistic": 33749, + "ogeography": 33750, + "numerical": 33751, + "ogg": 33752, + "Ġdisequilibrium": 33753, + "Ġswab": 33754, + "EDS": 33755, + "ĠPDFs": 33756, + "ĠSupernova": 33757, + "phospho": 33758, + "Ġlysosomes": 33759, + "galactic": 33760, + "ĠPerme": 33761, + "Ġfishery": 33762, + "ĠBOLD": 33763, + "Ġunravel": 33764, + "ĠEncryption": 33765, + "JP": 33766, + "hur": 33767, + "Ġdiscount": 33768, + "ĠWatanabe": 33769, + "ĠRheumat": 33770, + "FITC": 33771, + "Ġterahertz": 33772, + "ĠFont": 33773, + "iances": 33774, + "ĠAdditive": 33775, + "ĠEither": 33776, + "metadata": 33777, + "amphetamine": 33778, + "ĠPalmer": 33779, + "Ġleveraging": 33780, + "John": 33781, + "OCT": 33782, + "infer": 33783, + "ĠMSD": 33784, + "ĠâĪĵ": 33785, + "ouver": 33786, + "ĠAndersen": 33787, + "Ġworlds": 33788, + "Ġtori": 33789, + "Ġïģ°": 33790, + "engineering": 33791, + "ĠSquadron": 33792, + "Aff": 33793, + "åı": 33794, + "oxel": 33795, + "yletic": 33796, + "ĠCharacterizing": 33797, + "VT": 33798, + "rational": 33799, + "eremia": 33800, + "Ġcomplexation": 33801, + "ĠERα": 33802, + "carboxylic": 33803, + "ïĤ·": 33804, + "Ġgalactose": 33805, + "ĠAurora": 33806, + "Ġplasminogen": 33807, + "uren": 33808, + "igne": 33809, + "Ġrepaired": 33810, + "Ġblockers": 33811, + "ĠMNIST": 33812, + "Ïħ": 33813, + "ĠAxi": 33814, + "Ġstadium": 33815, + "diethyl": 33816, + "âĢİ": 33817, + "Ġcyclotron": 33818, + "Ġlymphaden": 33819, + "Ġvin": 33820, + "ĠMayer": 33821, + "Ġendometrium": 33822, + "ĠSpherical": 33823, + "Ġpersu": 33824, + "Ġimmortal": 33825, + "benzenesulf": 33826, + "ĠÅľ": 33827, + "Ġbite": 33828, + "ugged": 33829, + "ĠDiffraction": 33830, + "GTG": 33831, + "iate": 33832, + "Ġtp": 33833, + "Ġaber": 33834, + "ĠRein": 33835, + "Program": 33836, + "Style": 33837, + "ĠRegularization": 33838, + "ĠLeukemia": 33839, + "Ġprokaryotic": 33840, + "ocomial": 33841, + "skb": 33842, + "Ġdeviates": 33843, + "Ġfuse": 33844, + "ĠNull": 33845, + "ĠïĥĹ": 33846, + "ĠOperational": 33847, + "Ġcompressor": 33848, + "ĠRydberg": 33849, + "Ġfought": 33850, + "Ġeco": 33851, + "ĠSSP": 33852, + "CDs": 33853, + "ĠMEK": 33854, + "ĠAnisotropic": 33855, + "ĠDirection": 33856, + "ĠSpectrometry": 33857, + "Ġgluten": 33858, + "ĠPowell": 33859, + "recognized": 33860, + "Ġpsychotic": 33861, + "Ġhinder": 33862, + "Ġaccommodation": 33863, + "ĠNorman": 33864, + "Qx": 33865, + "Ġperiv": 33866, + "ĠUnknown": 33867, + "Ġjoins": 33868, + "ĠMinimization": 33869, + "ĠSons": 33870, + "ĠCin": 33871, + "Ġunavoid": 33872, + "ĠPTX": 33873, + "Ġcada": 33874, + "ĠLuk": 33875, + "Ġruling": 33876, + "Ġbiphasic": 33877, + "ĠComplications": 33878, + "ĠDefects": 33879, + "Content": 33880, + "ĠGregory": 33881, + "ĠWerner": 33882, + "ĠWeibull": 33883, + "eldom": 33884, + "Ġactivators": 33885, + "GLAPI": 33886, + "mathring": 33887, + "Ġhens": 33888, + "NSC": 33889, + "however": 33890, + "ĠTME": 33891, + "mafrost": 33892, + "coefficient": 33893, + "ĠInsect": 33894, + "ĠROIs": 33895, + "ĠBorrel": 33896, + "ĠQiu": 33897, + "Ġinhaled": 33898, + "idate": 33899, + "Ġantihypertensive": 33900, + "Ġtreats": 33901, + "ĠNearly": 33902, + "succ": 33903, + "ĠOrbital": 33904, + "eradish": 33905, + "administered": 33906, + "ĠÏĤ": 33907, + "ĠColony": 33908, + "ĠâĮĬ": 33909, + "ĠIndonesian": 33910, + "ĠBauer": 33911, + "ĠKod": 33912, + "manned": 33913, + "Resistant": 33914, + "Ġdaughters": 33915, + "ĠPredicted": 33916, + "Ġvocab": 33917, + "Ġcontrasted": 33918, + "margin": 33919, + "ĠDirected": 33920, + "EDTA": 33921, + "Ġsynchrony": 33922, + "icki": 33923, + "ĠSalv": 33924, + "treat": 33925, + "incess": 33926, + "varnothing": 33927, + "Ġhexane": 33928, + "Empty": 33929, + "Ġgemcitabine": 33930, + "omib": 33931, + "orepinephrine": 33932, + "proc": 33933, + "ĠMetS": 33934, + "ĠDRAM": 33935, + "Ġanticoagulant": 33936, + "nom": 33937, + "amater": 33938, + "ĠLiDAR": 33939, + "Ġmobil": 33940, + "Ġameliorates": 33941, + "niz": 33942, + "Ġja": 33943, + "Ġemuls": 33944, + "ĠZa": 33945, + "Ġastronomical": 33946, + "ĠAlfred": 33947, + "Hilbert": 33948, + "ĠKF": 33949, + "CRT": 33950, + "quadratic": 33951, + "Ġdifferentials": 33952, + "robacterium": 33953, + "ĠHippocampal": 33954, + "pull": 33955, + "ÄĻ": 33956, + "Ġsad": 33957, + "allyl": 33958, + "Ġhotspot": 33959, + "ĠElectronics": 33960, + "Ġconstitution": 33961, + "itonin": 33962, + "اÙĦ": 33963, + "Pc": 33964, + "Ġrevascular": 33965, + "Ġusable": 33966, + "ĠScatter": 33967, + "Ġgraphically": 33968, + "liminf": 33969, + "Ġrestaurant": 33970, + "ucalyptus": 33971, + "ACG": 33972, + "Analy": 33973, + "ĠMillipore": 33974, + "Ġmunicipalities": 33975, + "ĠMacrophage": 33976, + "Ġmacromolecular": 33977, + "License": 33978, + "gc": 33979, + "Ġlavage": 33980, + "ĠAES": 33981, + "ĠFCS": 33982, + "peritone": 33983, + "Ġmeasles": 33984, + "TEX": 33985, + "ĠVirulence": 33986, + "Ġhematoma": 33987, + "ĠFres": 33988, + "ĠNutrient": 33989, + "apar": 33990, + "ĠSpot": 33991, + "coplasma": 33992, + "ĠExpect": 33993, + "Ġciprofloxacin": 33994, + "phylaxis": 33995, + "ĠAtlanta": 33996, + "routing": 33997, + "arate": 33998, + "ĠCis": 33999, + "ensure": 34000, + "carriers": 34001, + "ĠVariant": 34002, + "surgical": 34003, + "ĠEstimate": 34004, + "à¹": 34005, + "ĠLiqu": 34006, + "Ġamalg": 34007, + "Ġbla": 34008, + "Ġthematic": 34009, + "IRQ": 34010, + "ACTION": 34011, + "ĠChristi": 34012, + "æľ": 34013, + "Ġnpy": 34014, + "death": 34015, + "Ġhairpin": 34016, + "Ġmultiplicities": 34017, + "Gibco": 34018, + "heated": 34019, + "afety": 34020, + "mutable": 34021, + "quarks": 34022, + "Sun": 34023, + "ql": 34024, + "Ġproductions": 34025, + "Ġgeology": 34026, + "Ġtides": 34027, + "atrix": 34028, + "Ġadmixture": 34029, + "translated": 34030, + "ĠAbu": 34031, + "nucleus": 34032, + "Ġweaknesses": 34033, + "Ġflavors": 34034, + "ĠLuis": 34035, + "ĠPutative": 34036, + "sentence": 34037, + "ĠMast": 34038, + "ĠMPS": 34039, + "ĠESS": 34040, + "Ġcompose": 34041, + "Ġbirefring": 34042, + "ĠRamsey": 34043, + "ĠCLL": 34044, + "Ġlignocell": 34045, + "ĠLamin": 34046, + "ĠWelsh": 34047, + "von": 34048, + "Ġpests": 34049, + "Ġfiction": 34050, + "ĠHRT": 34051, + "Ġassure": 34052, + "CTs": 34053, + "ĠPAHs": 34054, + "Ġcryptography": 34055, + "enerated": 34056, + "Ġops": 34057, + "ĠSynerg": 34058, + "iginal": 34059, + "ĠCraw": 34060, + "Ġkne": 34061, + "Ġcurvatures": 34062, + "Ġlux": 34063, + "ĠKenn": 34064, + "ĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ": 34065, + "println": 34066, + "Ġvertebrae": 34067, + "Ġrutile": 34068, + "ĠAerosol": 34069, + "referred": 34070, + "lactamase": 34071, + "vehicle": 34072, + "adir": 34073, + "izards": 34074, + "Ġcallback": 34075, + "Cluster": 34076, + "Ġsilt": 34077, + "Ġresearched": 34078, + "ĠGenerator": 34079, + "ĠRestoration": 34080, + "ĠChin": 34081, + "ometrical": 34082, + "ĠCoefficients": 34083, + "rachid": 34084, + "Face": 34085, + "Men": 34086, + "counts": 34087, + "Ġpeg": 34088, + "Ġecl": 34089, + "Ġcomedy": 34090, + "ĠLn": 34091, + "obuty": 34092, + "ĠSharing": 34093, + "Ġadequacy": 34094, + "urtosis": 34095, + "ĠPicard": 34096, + "Ġfestival": 34097, + "Ġdisposition": 34098, + "ĠComplement": 34099, + "ĠExclusion": 34100, + "Ġdextran": 34101, + "mons": 34102, + "ĠInterpolation": 34103, + "ĠSteven": 34104, + "Ġcelebrated": 34105, + "ĠhPa": 34106, + "ofrequency": 34107, + "Ġexceptionally": 34108, + "Ġenergetically": 34109, + "psychotic": 34110, + "Landau": 34111, + "Tuple": 34112, + "distributions": 34113, + "ĠRichards": 34114, + "Ġpolyps": 34115, + "ĠAbsence": 34116, + "Ġceleb": 34117, + "XG": 34118, + "Ġsimulates": 34119, + "mitters": 34120, + "Ġheatmap": 34121, + "ĠSDN": 34122, + "ĠSteps": 34123, + "Ġshallower": 34124, + "ĠTurbulent": 34125, + "YT": 34126, + "Ġnal": 34127, + "plicative": 34128, + "phae": 34129, + "ĠLeica": 34130, + "ĠAPPRO": 34131, + "Ġarrhythmia": 34132, + "Ġrewriting": 34133, + "Ġunsafe": 34134, + "Ġcoworkers": 34135, + "ĠGAD": 34136, + "ivol": 34137, + "Ġdisrupting": 34138, + "ĠUltraviolet": 34139, + "eree": 34140, + "ĠLopez": 34141, + "Ġnegation": 34142, + "Ġjaponica": 34143, + "ecessor": 34144, + "ĠPatch": 34145, + "Ġsoap": 34146, + "ĠYing": 34147, + "MSK": 34148, + "Ġtracheal": 34149, + "icos": 34150, + "Ġvp": 34151, + "FAIL": 34152, + "Ġcatabolism": 34153, + "solver": 34154, + "font": 34155, + "esp": 34156, + "ĠZou": 34157, + "Ġdarker": 34158, + "Ġlysozyme": 34159, + "covered": 34160, + "Ġmultitude": 34161, + "requently": 34162, + "Ġmetamorph": 34163, + "Ġchapters": 34164, + "hh": 34165, + "chl": 34166, + "redundant": 34167, + "acking": 34168, + "Ġentail": 34169, + "ĠPacket": 34170, + "ĠHabitat": 34171, + "imedia": 34172, + "ĠCof": 34173, + "phrase": 34174, + "Ġcloth": 34175, + "arsal": 34176, + "Ġdrums": 34177, + "TPUT": 34178, + "Args": 34179, + "ductory": 34180, + "ĠUltimately": 34181, + "icates": 34182, + "antigen": 34183, + "Though": 34184, + "ĠFlore": 34185, + "probs": 34186, + "Ġcirculatory": 34187, + "ĠContemporary": 34188, + "eplitz": 34189, + "Ġhatch": 34190, + "rized": 34191, + "ĠKop": 34192, + "mitting": 34193, + "Ġhyperspectral": 34194, + "ĠAbst": 34195, + "SIM": 34196, + "Ġfruitful": 34197, + "Ġrecipe": 34198, + "Ġimidazole": 34199, + "Ġsynonymous": 34200, + "Ġattribution": 34201, + "ĠMartÃŃnez": 34202, + "ĠRodrÃŃguez": 34203, + "particular": 34204, + "ĠInteracting": 34205, + "Conf": 34206, + "ORE": 34207, + "ĠTMA": 34208, + "ucidation": 34209, + "Ġbiochemistry": 34210, + "ĠLevy": 34211, + "Ġconcentrates": 34212, + "Ġinductor": 34213, + "Ġpyrophosph": 34214, + "Ġrespondent": 34215, + "Zhang": 34216, + "Ġrope": 34217, + "Ġdesignation": 34218, + "ĠClim": 34219, + "Ġconstrains": 34220, + "shelf": 34221, + "ĠdÏĥ": 34222, + "ĠTLC": 34223, + "ĠAhar": 34224, + "ĠMatch": 34225, + "ĠMOL": 34226, + "Ġfees": 34227, + "wealth": 34228, + "Ġhyperactivity": 34229, + "ĠBruker": 34230, + "ĠFreund": 34231, + "dichlorophenyl": 34232, + "rero": 34233, + "ĠFear": 34234, + "dotsc": 34235, + "Ġhyg": 34236, + "ĠTexture": 34237, + "Tak": 34238, + "ampled": 34239, + "Ġalgeb": 34240, + "subt": 34241, + "Ġdocumentary": 34242, + "ĠJE": 34243, + "CNS": 34244, + "Ġdeclar": 34245, + "Height": 34246, + "Ki": 34247, + "enoid": 34248, + "ĠCervical": 34249, + "fractory": 34250, + "Ġplanted": 34251, + "IFI": 34252, + "Ġconceptually": 34253, + "Ġfillers": 34254, + "icola": 34255, + "lean": 34256, + "Ġclump": 34257, + "Ġwriters": 34258, + "Generally": 34259, + "Ġost": 34260, + "opening": 34261, + "CLASS": 34262, + "Ġherpesvirus": 34263, + "Instit": 34264, + "Ġdrinks": 34265, + "ĠIntensive": 34266, + "Ġmusician": 34267, + "Ġanchors": 34268, + "Series": 34269, + "ĠFAM": 34270, + "ĠBott": 34271, + "ĠECC": 34272, + "Ġinversions": 34273, + "Ġacres": 34274, + "Ġswabs": 34275, + "ĠÍī": 34276, + "ĠBerkeley": 34277, + "Ġplum": 34278, + "Ġempower": 34279, + "Ġphotoemission": 34280, + "ĠRabi": 34281, + "East": 34282, + "Taylor": 34283, + "OSE": 34284, + "Ġdenied": 34285, + "ĠHTTP": 34286, + "MU": 34287, + "hew": 34288, + "Ġthri": 34289, + "ĠCERN": 34290, + "Ġsuffice": 34291, + "functionalized": 34292, + "Ġcrabs": 34293, + "Ġidempotent": 34294, + "Ġpostulate": 34295, + "ĠCBF": 34296, + "discrim": 34297, + "Character": 34298, + "ĠRecombination": 34299, + "Cache": 34300, + "omit": 34301, + "ĠAda": 34302, + "Ġcursor": 34303, + "EMT": 34304, + "Ġmesoscale": 34305, + "guide": 34306, + "Hyper": 34307, + "Ġht": 34308, + "renes": 34309, + "ussen": 34310, + "whereas": 34311, + "Ġintegrator": 34312, + "Ġsyncy": 34313, + "arous": 34314, + "Ġcounteract": 34315, + "halose": 34316, + "ĠNotation": 34317, + "ĠRelevance": 34318, + "vf": 34319, + "Ġinbred": 34320, + "Ġrecirc": 34321, + "Ġende": 34322, + "Ġpresidential": 34323, + "Ġlactose": 34324, + "acional": 34325, + "ospi": 34326, + "ĠVGG": 34327, + "oselectivity": 34328, + "ĠConfig": 34329, + "Ġfingerprints": 34330, + "Interface": 34331, + "purple": 34332, + "etus": 34333, + "ĠNin": 34334, + "ĠKras": 34335, + "ĠReports": 34336, + "ĠSeattle": 34337, + "ADC": 34338, + "Ġlipoproteins": 34339, + "cyclohexyl": 34340, + "opressin": 34341, + "Ġwavefront": 34342, + "tetrazol": 34343, + "thys": 34344, + "Ġdivor": 34345, + "aminophen": 34346, + "ĠPerry": 34347, + "ĠConsiderations": 34348, + "ĠHalo": 34349, + "Ġreflexive": 34350, + "thiazolidin": 34351, + "oxycycline": 34352, + "CW": 34353, + "odim": 34354, + "ĠChong": 34355, + "Ġequilibrated": 34356, + "rime": 34357, + "ymology": 34358, + "Ġdevoid": 34359, + "rigel": 34360, + "amatergic": 34361, + "Ġidentifications": 34362, + "Ġcontrollability": 34363, + "ecticut": 34364, + "ĠSynchronization": 34365, + "ulatus": 34366, + "Ġcorrelating": 34367, + "Ġmuons": 34368, + "Ġcompartmental": 34369, + "Ġinhomogeneities": 34370, + "Ġevacuation": 34371, + "respiratory": 34372, + "dimethoxy": 34373, + "Ġinterferometric": 34374, + "Ġastronomy": 34375, + "ZD": 34376, + "ĦĦ": 34377, + "elia": 34378, + "bler": 34379, + "Ġpioneering": 34380, + "Ġpits": 34381, + "Ġmansoni": 34382, + "ĠCOND": 34383, + "Ġcodeword": 34384, + "imura": 34385, + "ĠDopamine": 34386, + "ĠGiov": 34387, + "ĠCameroon": 34388, + "Sem": 34389, + "dong": 34390, + "otto": 34391, + "emies": 34392, + "Ġinterquartile": 34393, + "llbracket": 34394, + "otropies": 34395, + "Ġhappening": 34396, + "ĠPalm": 34397, + "Ġstuff": 34398, + "Ġparking": 34399, + "egal": 34400, + "ĠCOP": 34401, + "Ġorganizing": 34402, + "Ġpolyhedral": 34403, + "Ġprovenance": 34404, + "Js": 34405, + "chains": 34406, + "egu": 34407, + "mercap": 34408, + "leveland": 34409, + "Ġerythroid": 34410, + "ymptomatic": 34411, + "Ġzigzag": 34412, + "Ġinferring": 34413, + "Ġapprox": 34414, + "Ġdownlink": 34415, + "ĠDeficiency": 34416, + "rbracket": 34417, + "ĠTIM": 34418, + "STS": 34419, + "ainen": 34420, + "Ġunloading": 34421, + "ĠXP": 34422, + "ĠWhilst": 34423, + "ĠIDH": 34424, + "ĠTIMP": 34425, + "rrbracket": 34426, + "acities": 34427, + "Ġwhale": 34428, + "ĠWAR": 34429, + "Ġinfl": 34430, + "ĠPresentation": 34431, + "authorbsnm": 34432, + "Ġbactericidal": 34433, + "SPEC": 34434, + "Ġdysregulated": 34435, + "ĠICAM": 34436, + "nano": 34437, + "Ġwafers": 34438, + "ĠMUC": 34439, + "Ġalien": 34440, + "chke": 34441, + "Ġslabs": 34442, + "Ġbacking": 34443, + "nsis": 34444, + "Ġbalances": 34445, + "ethane": 34446, + "Linked": 34447, + "Chen": 34448, + "Hymenoptera": 34449, + "itations": 34450, + "ĠOUT": 34451, + "transplant": 34452, + "conditioned": 34453, + "ĠBenefits": 34454, + "Tyr": 34455, + "atmosp": 34456, + "ĠAdhesion": 34457, + "Ġthorac": 34458, + "activator": 34459, + "Ġphosphatidylinositol": 34460, + "Ġreportedly": 34461, + "ĠCLASS": 34462, + "Ġrenewed": 34463, + "ĠPharmacological": 34464, + "Ġminimise": 34465, + "glucosidase": 34466, + "adenosyl": 34467, + "Ġovip": 34468, + "initializer": 34469, + "Ġforage": 34470, + "rms": 34471, + "ĠImag": 34472, + "ĠAnnexin": 34473, + "ĠVehicles": 34474, + "Ġfles": 34475, + "sta": 34476, + "ĠGBS": 34477, + "ĠChat": 34478, + "measurements": 34479, + "ĠAuditory": 34480, + "Cut": 34481, + "Fv": 34482, + "Ġmaker": 34483, + "application": 34484, + "Ġreversing": 34485, + "Ġstip": 34486, + "Ġfaecalis": 34487, + "icycle": 34488, + "Ġtrimmed": 34489, + "Ġexacerbation": 34490, + "Ġtranscranial": 34491, + "ĠMomentum": 34492, + "Ġfc": 34493, + "ĠFOV": 34494, + "Ġangina": 34495, + "Ġnanostructure": 34496, + "Ġantagonism": 34497, + "ĠLEDs": 34498, + "ìĹIJ": 34499, + "Ġfals": 34500, + "aporation": 34501, + "ĠInvasive": 34502, + "ĠKm": 34503, + "ertation": 34504, + "Ġharness": 34505, + "Ġfertile": 34506, + "ĠTRUE": 34507, + "Ġshelter": 34508, + "ĠWolbachia": 34509, + "shoot": 34510, + "Ġsess": 34511, + "ĠHous": 34512, + "ĠAce": 34513, + "ĠCML": 34514, + "Ġproactive": 34515, + "Ġshots": 34516, + "Ġcoup": 34517, + "restling": 34518, + "uniformly": 34519, + "yam": 34520, + "olase": 34521, + "ĠICS": 34522, + "ĠEbola": 34523, + "rolling": 34524, + "trunc": 34525, + "ĠRepresentatives": 34526, + "Ġgrasping": 34527, + "ĠAnomaly": 34528, + "ĠMine": 34529, + "ĠMPO": 34530, + "leright": 34531, + "Ġinstitute": 34532, + "Ġsugarcane": 34533, + "ÑĢа": 34534, + "Ġoccluded": 34535, + "ĠMagellanic": 34536, + "BEC": 34537, + "Wi": 34538, + "oA": 34539, + "Ġgapped": 34540, + "ĠPRC": 34541, + "ĠMAE": 34542, + "Ġmusicians": 34543, + "ĠSignificantly": 34544, + "Ġforthcoming": 34545, + "Ġacclimation": 34546, + "required": 34547, + "verbal": 34548, + "ĠFX": 34549, + "ĠMLE": 34550, + "Ġcompass": 34551, + "ĠMultimodal": 34552, + "Grant": 34553, + "Ġtvb": 34554, + "Instruction": 34555, + "Ġsenses": 34556, + "urbed": 34557, + "hamn": 34558, + "Ġframed": 34559, + "Ġurothel": 34560, + "orin": 34561, + "seal": 34562, + "Ġflasks": 34563, + "shops": 34564, + "Ġwheels": 34565, + "ĠRadon": 34566, + "ĠPlanetary": 34567, + "Ġhedge": 34568, + "Ġdk": 34569, + "Ġevidently": 34570, + "threads": 34571, + "Ġtad": 34572, + "elim": 34573, + "imov": 34574, + "istem": 34575, + "andi": 34576, + "Ġleisure": 34577, + "ostom": 34578, + "Ġcaring": 34579, + "ĠSmoking": 34580, + "Ġcompetitors": 34581, + "AFS": 34582, + "xl": 34583, + "ĠSatur": 34584, + "ĠFerg": 34585, + "Ġchin": 34586, + "ĠCDR": 34587, + "ĠSOM": 34588, + "osaccharide": 34589, + "MODEL": 34590, + "ECC": 34591, + "Ġdas": 34592, + "agonist": 34593, + "stery": 34594, + "Ġrelays": 34595, + "zek": 34596, + "Ġneoplasm": 34597, + "Chip": 34598, + "Ġgill": 34599, + "lamed": 34600, + "cerning": 34601, + "Ġinconsistencies": 34602, + "aceans": 34603, + "ĠAdri": 34604, + "ĠAfghan": 34605, + "Ġniches": 34606, + "Ġtunnelling": 34607, + "gus": 34608, + "ĠIan": 34609, + "Ġburial": 34610, + "Transform": 34611, + "ocompatible": 34612, + "Ġseldom": 34613, + "Ġdisclosed": 34614, + "âĪķ": 34615, + "Ġrefining": 34616, + "Ġtyph": 34617, + "Ġcooperate": 34618, + "Ġasphalt": 34619, + "ĠConstitution": 34620, + "flavor": 34621, + "Ġwarp": 34622, + "ż": 34623, + "Ġcraw": 34624, + "ĠIndigenous": 34625, + "ĠPrevent": 34626, + "Ġtrigeminal": 34627, + "ĠFriedrich": 34628, + "ĠInterferon": 34629, + "iosity": 34630, + "warm": 34631, + "uson": 34632, + "Ġunderlies": 34633, + "Ġmultiplets": 34634, + "ĠSUPER": 34635, + "ĠManufacturing": 34636, + "Ġvimentin": 34637, + "ramine": 34638, + "Ġefficacious": 34639, + "iced": 34640, + "ĠVall": 34641, + "othorax": 34642, + "Ġaudi": 34643, + "Qs": 34644, + "ĠPAL": 34645, + "ĠHold": 34646, + "hattan": 34647, + "idding": 34648, + "wana": 34649, + "Ġpending": 34650, + "Ġperennial": 34651, + "Ġtouching": 34652, + "xpected": 34653, + "Distance": 34654, + "nav": 34655, + "Ġisomeric": 34656, + "ĠMCI": 34657, + "numbers": 34658, + "Ġreverses": 34659, + "Ġpolycystic": 34660, + "Hem": 34661, + "uities": 34662, + "optional": 34663, + "Ġsubcortical": 34664, + "ĠSupply": 34665, + "ĠCalder": 34666, + "Ġmangrove": 34667, + "Ġpads": 34668, + "urfaces": 34669, + "ĠFaster": 34670, + "Ġunderneath": 34671, + "Ġprolactin": 34672, + "Ġclearer": 34673, + "Ġscintillation": 34674, + "Ġhumidified": 34675, + "ĠWound": 34676, + "ĠHPA": 34677, + "Ġcollapsing": 34678, + "Ġbaryonic": 34679, + "ĠMEASU": 34680, + "ĠGü": 34681, + "Ġdetr": 34682, + "Ġsubstituent": 34683, + "ĠRomania": 34684, + "ĠInvolved": 34685, + "Ġduodenal": 34686, + "ĠAmp": 34687, + "ĠSIS": 34688, + "scher": 34689, + "auth": 34690, + "ĠRespond": 34691, + "ĠRanking": 34692, + "trip": 34693, + "xF": 34694, + "istin": 34695, + "Ġpauc": 34696, + "reflection": 34697, + "Ġcornea": 34698, + "Ġbolus": 34699, + "Ġpivot": 34700, + "October": 34701, + "ĠSERS": 34702, + "ĠXing": 34703, + "ANET": 34704, + "Chinese": 34705, + "ĠMusc": 34706, + "Dynamic": 34707, + "Mesh": 34708, + "Ġdiphosphate": 34709, + "Ġconspecific": 34710, + "lector": 34711, + "ĠEcu": 34712, + "ĠCoverage": 34713, + "ĠãĢĪ": 34714, + "COD": 34715, + "among": 34716, + "Ġposit": 34717, + "imumab": 34718, + "ĠpN": 34719, + "Ġcoaching": 34720, + "exports": 34721, + "Ġrealm": 34722, + "ĠFerreira": 34723, + "Ġnationally": 34724, + "Ġturtle": 34725, + "ubtedly": 34726, + "ĠDraft": 34727, + "Ġendl": 34728, + "ĠContinuum": 34729, + "embeddings": 34730, + "Ġá¹½": 34731, + "ĠCrime": 34732, + "Ġimmigration": 34733, + "ĠFilip": 34734, + "Ġgarnet": 34735, + "Ġobscure": 34736, + "ĠTYPE": 34737, + "Ġultrastructural": 34738, + "caemia": 34739, + "ĠSeman": 34740, + "rink": 34741, + "tiff": 34742, + "uccal": 34743, + "kee": 34744, + "itudinally": 34745, + "ĠAlloy": 34746, + "ĠAnalyzer": 34747, + "continue": 34748, + "ĠAlabama": 34749, + "QOL": 34750, + "Ġpollin": 34751, + "Ġcorrespondences": 34752, + "ĠResol": 34753, + "FIR": 34754, + "ulare": 34755, + "tawa": 34756, + "URCE": 34757, + "Ġurbanization": 34758, + "zd": 34759, + "Ġgloss": 34760, + "ERA": 34761, + "ĠDetermine": 34762, + "Date": 34763, + "ĠPSP": 34764, + "ĠShig": 34765, + "repta": 34766, + "ĠGait": 34767, + "neutrino": 34768, + "Ġpervasive": 34769, + "ĠâĢ¢âĢ¢âĢ¢": 34770, + "Ġhomozyg": 34771, + "Ġadaptively": 34772, + "graphic": 34773, + "ĠJohnston": 34774, + "zt": 34775, + "explicit": 34776, + "Ġhelmin": 34777, + "Ġpes": 34778, + "ARF": 34779, + "ĠFram": 34780, + "ĠAmsterdam": 34781, + "Ġlogarithms": 34782, + "ĠCreative": 34783, + "PageIndex": 34784, + "Ġpacing": 34785, + "ĠPCS": 34786, + "Ġforebrain": 34787, + "ĠCTCF": 34788, + "decomposition": 34789, + "Ġbearings": 34790, + "Ġanhydrous": 34791, + "Ġcb": 34792, + "ĠMON": 34793, + "ĠNodes": 34794, + "strum": 34795, + "ĠJans": 34796, + "Ġdelineate": 34797, + "Ġdichroism": 34798, + "conformal": 34799, + "Ġretreat": 34800, + "glial": 34801, + "Ġnuclease": 34802, + "ĠBaltimore": 34803, + "Ġpaying": 34804, + "Ġboreal": 34805, + "tipation": 34806, + "Root": 34807, + "SQL": 34808, + "sources": 34809, + "endo": 34810, + "ĠOrion": 34811, + "Plus": 34812, + "ĠDEL": 34813, + "ĠThan": 34814, + "Ġmonoph": 34815, + "Ġreflector": 34816, + "Ze": 34817, + "ĠLinking": 34818, + "sync": 34819, + "ĠCREB": 34820, + "national": 34821, + "urized": 34822, + "ĠPeptides": 34823, + "ĠBegin": 34824, + "borg": 34825, + "piperidyl": 34826, + "Ġoverestimation": 34827, + "RGB": 34828, + "TK": 34829, + "Ġbeings": 34830, + "Ġattains": 34831, + "Ġreservation": 34832, + "ĠMotivation": 34833, + "Ġtrimethyl": 34834, + "ĠTerminal": 34835, + "Ġintentional": 34836, + "Negative": 34837, + "ĠCronbach": 34838, + "dorferi": 34839, + "Daw": 34840, + "VAR": 34841, + "dP": 34842, + "imath": 34843, + "overex": 34844, + "Ġfibrotic": 34845, + "Ġsmartphones": 34846, + "Ġontologies": 34847, + "Good": 34848, + "utively": 34849, + "ĠVB": 34850, + "SPE": 34851, + "ĠMcDonald": 34852, + "galaxies": 34853, + "Ġbiochar": 34854, + "ĠEMS": 34855, + "ĠNf": 34856, + "orship": 34857, + "Ġbackscattering": 34858, + "Ġп": 34859, + "Ġanthocyanin": 34860, + "ĠPhoenix": 34861, + "contained": 34862, + "ĠPSII": 34863, + "hlung": 34864, + "ĠLAI": 34865, + "Ġlectures": 34866, + "Ġdispatch": 34867, + "VF": 34868, + "ĠMEC": 34869, + "ĠWes": 34870, + "Ġbackscatter": 34871, + "otite": 34872, + "ĠSRC": 34873, + "Ġcurrency": 34874, + "onyms": 34875, + "aspartate": 34876, + "Ġcoset": 34877, + "ĠCPP": 34878, + "orbing": 34879, + "ĠEmbeddings": 34880, + "ĠSurveys": 34881, + "Ġneurodevelopmental": 34882, + "ĠSRE": 34883, + "ĠInterior": 34884, + "ĠARDS": 34885, + "experiments": 34886, + "bromophenyl": 34887, + "ĠECL": 34888, + "ĠOPE": 34889, + "mediation": 34890, + "Ġthermoc": 34891, + "Ġinterpretable": 34892, + "ĠMicrobiome": 34893, + "eastern": 34894, + "¿": 34895, + "ĠTDP": 34896, + "athon": 34897, + "ĠByzantine": 34898, + "anyon": 34899, + "Ġepitaxy": 34900, + "Ġcriticized": 34901, + "Millipore": 34902, + "ĠDEP": 34903, + "ĠFreedom": 34904, + "junctions": 34905, + "ĠASM": 34906, + "ĠGren": 34907, + "Ġsigning": 34908, + "Ġconstituting": 34909, + "oproterozoic": 34910, + "ĠSynech": 34911, + "ĠVoice": 34912, + "Ġcholecyst": 34913, + "bilities": 34914, + "online": 34915, + "ĠEdd": 34916, + "ĠKup": 34917, + "ĠLett": 34918, + "ĠMarin": 34919, + "ĠGoal": 34920, + "ĠSYM": 34921, + "introduced": 34922, + "naphthyl": 34923, + "ĠLü": 34924, + "Ġmx": 34925, + "Ġblu": 34926, + "Ġrm": 34927, + "ĠDeletion": 34928, + "ĠConnecticut": 34929, + "Coleoptera": 34930, + "try": 34931, + "Ġsoot": 34932, + "ĠCountries": 34933, + "Ġsickle": 34934, + "Meta": 34935, + "ĠSib": 34936, + "ĠHNO": 34937, + "ĠUD": 34938, + "Ġexpr": 34939, + "Ġallowable": 34940, + "ĠIndirect": 34941, + "tisation": 34942, + "Ġadenomas": 34943, + "electronics": 34944, + "RNN": 34945, + "ĠTCF": 34946, + "Ġglucagon": 34947, + "ĠCitation": 34948, + "Ġgamb": 34949, + "andez": 34950, + "Ġtransmits": 34951, + "ajima": 34952, + "Ġholonomy": 34953, + "ìł": 34954, + "actam": 34955, + "ĠThreat": 34956, + "ĠPearl": 34957, + "Ġeruptions": 34958, + "ĠImmunohistochemistry": 34959, + "Yes": 34960, + "patrick": 34961, + "Ġama": 34962, + "Ġdrew": 34963, + "ĠTasks": 34964, + "ĠPIM": 34965, + "Ġdispat": 34966, + "ĠDetroit": 34967, + "Ġcoexist": 34968, + "arboxylase": 34969, + "IBM": 34970, + "ĠTUNEL": 34971, + "ĠUF": 34972, + "ĠANG": 34973, + "Ġsarcopenia": 34974, + "Ġhaptic": 34975, + "Ġcarbonates": 34976, + "Ġmitophagy": 34977, + "Ġcitizen": 34978, + "ĠCONTROL": 34979, + "fif": 34980, + "Ġwi": 34981, + "ĠGLO": 34982, + "ensored": 34983, + "ĠPara": 34984, + "ĠAbdel": 34985, + "oietin": 34986, + "Ġtoe": 34987, + "ĠSQU": 34988, + "ĠRag": 34989, + "Ġxylem": 34990, + "Ġliberal": 34991, + "ĠMargaret": 34992, + "Wa": 34993, + "kp": 34994, + "ĠPEM": 34995, + "ĠDDR": 34996, + "Ġgenotypic": 34997, + "ĠYM": 34998, + "INGS": 34999, + "keras": 35000, + "ĠEducational": 35001, + "ĠCultures": 35002, + "instr": 35003, + "ĠFuchs": 35004, + "agasc": 35005, + "factant": 35006, + "Ġtenth": 35007, + "ABL": 35008, + "Ġpermeable": 35009, + "ĠCameron": 35010, + "BrN": 35011, + "ĠMuller": 35012, + "ĠReversible": 35013, + "wild": 35014, + "Ġfusions": 35015, + "osulf": 35016, + "ĠEoS": 35017, + "ĠKö": 35018, + "detected": 35019, + "ĠCollagen": 35020, + "Ġdescendants": 35021, + "election": 35022, + "arange": 35023, + "Ġbounce": 35024, + "Ġcontag": 35025, + "Invalid": 35026, + "ĠCoating": 35027, + "tasks": 35028, + "arma": 35029, + "ĠKC": 35030, + "Ġdiar": 35031, + "ĠSuppress": 35032, + "Ġfractionated": 35033, + "Ġsnail": 35034, + "Ġmicrophone": 35035, + "ĠScienti": 35036, + "Ġchemiluminescence": 35037, + "software": 35038, + "Ġburgdorferi": 35039, + "Ġboot": 35040, + "ĠCSCs": 35041, + "ĠMSI": 35042, + "tsev": 35043, + "Ġheater": 35044, + "fractal": 35045, + "Ġendosomes": 35046, + "Uniform": 35047, + "Ġathlete": 35048, + "ĠDriven": 35049, + "Ġvivax": 35050, + "Kind": 35051, + "satisfies": 35052, + "Ġcorticosteroid": 35053, + "ĠEstablishment": 35054, + "calibration": 35055, + "Ġdimeric": 35056, + "Ġcereal": 35057, + "ĠSupervised": 35058, + "ĠSPM": 35059, + "MBER": 35060, + "Ġhemispheres": 35061, + "Ġpercentiles": 35062, + "Leu": 35063, + "Major": 35064, + "Ġexagger": 35065, + "ĠdsRNA": 35066, + "December": 35067, + "ĠZrO": 35068, + "Ġasymmetrical": 35069, + "ĠVAS": 35070, + "ĠJM": 35071, + "Ġintegrations": 35072, + "Ġhandover": 35073, + "Cycl": 35074, + "implant": 35075, + "Ġquote": 35076, + "Ġcyclone": 35077, + "ĠStephan": 35078, + "ĠFranco": 35079, + "Ġawake": 35080, + "Ġfeeder": 35081, + "CHAR": 35082, + "Condition": 35083, + "ĠCharl": 35084, + "ĠBrigade": 35085, + "Ġremediation": 35086, + "cig": 35087, + "ĠBohr": 35088, + "ĠVacuum": 35089, + "ĠToxoplasma": 35090, + "Ġghrelin": 35091, + "ĠTRAF": 35092, + "aye": 35093, + "Client": 35094, + "iliation": 35095, + "xyz": 35096, + "mingham": 35097, + "ĠSUB": 35098, + "ïĢł": 35099, + "Ġconversions": 35100, + "Ġmultipath": 35101, + "missive": 35102, + "Ġeqn": 35103, + "bulk": 35104, + "myc": 35105, + "Ġexacerbated": 35106, + "ت": 35107, + "Ġproteinase": 35108, + "Ġbuilder": 35109, + "ahara": 35110, + "Ġinvert": 35111, + "ĠReception": 35112, + "axanthin": 35113, + "Ġprimed": 35114, + "Ġcopula": 35115, + "Ġproceedings": 35116, + "Ġnondegenerate": 35117, + "Ġintox": 35118, + "Ġneedles": 35119, + "lengths": 35120, + "Ġtransposon": 35121, + "hon": 35122, + "ĠTPC": 35123, + "pland": 35124, + "oxyn": 35125, + "ICH": 35126, + "Ġintrauterine": 35127, + "Ġlaminated": 35128, + "ĠOBSERV": 35129, + "Match": 35130, + "ĠInsur": 35131, + "ĠAmyloid": 35132, + "Ġwarped": 35133, + "ematical": 35134, + "ĠPractices": 35135, + "ãģ®": 35136, + "ĠBrassica": 35137, + "Ġhyperthermia": 35138, + "Ġdn": 35139, + "ĠLIF": 35140, + "ĠMetropolitan": 35141, + "ĠBrdU": 35142, + "impact": 35143, + "filtered": 35144, + "ĠReagent": 35145, + "vp": 35146, + "ĠTip": 35147, + "ĠProportional": 35148, + "Ġbloodstream": 35149, + "Simple": 35150, + "Ġtyros": 35151, + "ĠHenri": 35152, + "Ġretrotrans": 35153, + "aciens": 35154, + "Ġmistakes": 35155, + "acylglycerol": 35156, + "ĠMirror": 35157, + "VERSION": 35158, + "vre": 35159, + "Ġbact": 35160, + "ocked": 35161, + "epsis": 35162, + "Ġsonication": 35163, + "ĠPurkinje": 35164, + "Ġmismatches": 35165, + "ĠAOD": 35166, + "Ġhypergraph": 35167, + "ĠMiami": 35168, + "ammed": 35169, + "Ġconversely": 35170, + "ĠGabor": 35171, + "ĠGDM": 35172, + "Ġcoiled": 35173, + "onica": 35174, + "Ġevolutions": 35175, + "ĠRBM": 35176, + "ĠReef": 35177, + "ĠAbram": 35178, + "ĠPrecise": 35179, + "increase": 35180, + "ĠPlatelet": 35181, + "Generator": 35182, + "Arch": 35183, + "ĠBened": 35184, + "preceq": 35185, + "measurable": 35186, + "CAS": 35187, + "ĠTourn": 35188, + "Ġgiants": 35189, + "Ġeddies": 35190, + "Ġcolumnar": 35191, + "aggregation": 35192, + "Ġzirconia": 35193, + "ducibility": 35194, + "Ġservo": 35195, + "Ġbeauty": 35196, + "Ġheap": 35197, + "ĠâĪĴâĪĴâĪĴ": 35198, + "Ġconductivities": 35199, + "Ġdarkness": 35200, + "Ġoccupying": 35201, + "ĠClean": 35202, + "bash": 35203, + "ulans": 35204, + "appy": 35205, + "ĠMarker": 35206, + "runtime": 35207, + "Ġhaemoglobin": 35208, + "Ġdesktop": 35209, + "mis": 35210, + "ĠSof": 35211, + "osse": 35212, + "Ġcomoving": 35213, + "Ġclutter": 35214, + "ourced": 35215, + "Ġsubj": 35216, + "arching": 35217, + "ĠSolomon": 35218, + "locking": 35219, + "Ġparap": 35220, + "Ġrotator": 35221, + "ĠACKNOWLEDGMENTS": 35222, + "Ter": 35223, + "yster": 35224, + "ĠWebb": 35225, + "Ġsubsample": 35226, + "osilicate": 35227, + "Training": 35228, + "orpha": 35229, + "Ġtimeout": 35230, + "otinamide": 35231, + "ĠFabry": 35232, + "ĠReceiver": 35233, + "Ġconjunctiv": 35234, + "ĠEcuador": 35235, + "ĠIda": 35236, + "Ġcasein": 35237, + "Ġïģ¸": 35238, + "ĠBarn": 35239, + "ĠSchools": 35240, + "elona": 35241, + "dip": 35242, + "ĠChrys": 35243, + "ICI": 35244, + "Ġposteriori": 35245, + "Ġbleaching": 35246, + "ĠPersonality": 35247, + "umbers": 35248, + "ĠModes": 35249, + "Ġnotification": 35250, + "Ġsupine": 35251, + "alued": 35252, + "keep": 35253, + "ĠFranz": 35254, + "Ġwounded": 35255, + "YL": 35256, + "Ġdilemma": 35257, + "ĠClara": 35258, + "ĠCarroll": 35259, + "Ġsickness": 35260, + "Ġproxies": 35261, + "ecks": 35262, + "ĠÏ«": 35263, + "Ġplanting": 35264, + "Ġciphertext": 35265, + "ĠFamilies": 35266, + "iesel": 35267, + "Ġincongru": 35268, + "ĠExcitation": 35269, + "Ġconferred": 35270, + "ĠButter": 35271, + "Impl": 35272, + "collision": 35273, + "idol": 35274, + "Ġacquires": 35275, + "ĠOwen": 35276, + "SAM": 35277, + "ĠGUT": 35278, + "lects": 35279, + "Ġdeleg": 35280, + "Shot": 35281, + "Ġanthrac": 35282, + "Russian": 35283, + "ĠPCE": 35284, + "ĠâĥĹ": 35285, + "ĠKab": 35286, + "NAC": 35287, + "Ġargparse": 35288, + "ĠViol": 35289, + "Ġanticoagulation": 35290, + "Ġcredibility": 35291, + "Ġrotavirus": 35292, + "ĠInvest": 35293, + "Ġrecol": 35294, + "variety": 35295, + "Ġdeformable": 35296, + "Ġenergetics": 35297, + "Ġconsultations": 35298, + "letics": 35299, + "ĠFoss": 35300, + "ĠLIGO": 35301, + "php": 35302, + "ĠChal": 35303, + "ĠMalawi": 35304, + "Ġstrokes": 35305, + "horm": 35306, + "Ġbs": 35307, + "Ġplural": 35308, + "strategy": 35309, + "Ġmisalignment": 35310, + "previous": 35311, + "filters": 35312, + "ĠDemographics": 35313, + "deterministic": 35314, + "Ġcyclophosphamide": 35315, + "Ġstreak": 35316, + "ĠBiosynthesis": 35317, + "Ġsubcutaneously": 35318, + "jn": 35319, + "Ġampicillin": 35320, + "ĠChag": 35321, + "iformes": 35322, + "IFICATION": 35323, + "Ġyourself": 35324, + "Ġtolerability": 35325, + "Ġautocl": 35326, + "rhs": 35327, + "Ġpupils": 35328, + "Ġgauged": 35329, + "Lay": 35330, + "ĠSanti": 35331, + "ĠDBP": 35332, + "ĠGary": 35333, + "drive": 35334, + "Ġtrustworth": 35335, + "Ġcontingency": 35336, + "Cube": 35337, + "Host": 35338, + "fu": 35339, + "Ġhsa": 35340, + "issner": 35341, + "ITT": 35342, + "ĠSrTiO": 35343, + "Ġcounselling": 35344, + "integrable": 35345, + "Ġunderway": 35346, + "Ġstandardised": 35347, + "bius": 35348, + "Firstly": 35349, + "Ġporphyrin": 35350, + "Area": 35351, + "iw": 35352, + "Ġub": 35353, + "ĠLynch": 35354, + "ĠWBC": 35355, + "ilden": 35356, + "Ġhomeless": 35357, + "Ġmagnetosphere": 35358, + "Ġnighttime": 35359, + "ncbi": 35360, + "Ġdownt": 35361, + "lethal": 35362, + "Ġinterim": 35363, + "ĠResil": 35364, + "Ġcontinually": 35365, + "ĠImmunofluorescence": 35366, + "Design": 35367, + "Ġadvocate": 35368, + "reptavidin": 35369, + "fw": 35370, + "story": 35371, + "ĠPSS": 35372, + "Ġfiled": 35373, + "Ġbedrock": 35374, + "Ġisoflurane": 35375, + "Ġreluct": 35376, + "eward": 35377, + "ĠIndependence": 35378, + "ĠBurkholder": 35379, + "Ġcinn": 35380, + "Ġcaptive": 35381, + "Ġcomposing": 35382, + "Ġrestraint": 35383, + "Ġquestionable": 35384, + "ĠTomato": 35385, + "Ġzeroth": 35386, + "rins": 35387, + "omez": 35388, + "Ġglia": 35389, + "ĠGlac": 35390, + "Independent": 35391, + "Ġobjectively": 35392, + "pA": 35393, + "Ġfavoring": 35394, + "ipelago": 35395, + "Ġincontinence": 35396, + "bium": 35397, + "ĠLZ": 35398, + "ĠLed": 35399, + "hexyl": 35400, + "Ġceased": 35401, + "Ġoleic": 35402, + "ĠImpairment": 35403, + "Ñĸ": 35404, + "ongo": 35405, + "Ġrunner": 35406, + "Ġcucumber": 35407, + "ĠPerform": 35408, + "Ġdoublets": 35409, + "Ġeigenfunction": 35410, + "Ġ̺": 35411, + "ĠHenderson": 35412, + "Klein": 35413, + "Tab": 35414, + "Ġbeer": 35415, + "ocom": 35416, + "unciation": 35417, + "------": 35418, + "ĠTSC": 35419, + "ogas": 35420, + "Ġrud": 35421, + "Ġincis": 35422, + "ĠLOG": 35423, + "FBQ": 35424, + "Ġinterconnection": 35425, + "î": 35426, + "arbox": 35427, + "ĠIBS": 35428, + "ĠNCT": 35429, + "ĠGand": 35430, + "Ġyaw": 35431, + "ĠTransverse": 35432, + "ĠSudan": 35433, + "Ġconstriction": 35434, + "Hor": 35435, + "Ġevasion": 35436, + "Ġmeromorphic": 35437, + "ĠPBMC": 35438, + "IUM": 35439, + "reed": 35440, + "ĠBö": 35441, + "ĠEMB": 35442, + "ukh": 35443, + "Ġwinners": 35444, + "Ġascites": 35445, + "Mes": 35446, + "Ġeclipse": 35447, + "ĠEocene": 35448, + "adiazol": 35449, + "Ġrecoveries": 35450, + "Starting": 35451, + "rema": 35452, + "ĠÃİ": 35453, + "monotonic": 35454, + "ĠMeOH": 35455, + "ĠFlood": 35456, + "Ġwatching": 35457, + "GTP": 35458, + "iel": 35459, + "müller": 35460, + "åħ": 35461, + "Ġpolyphenol": 35462, + "ĠLMI": 35463, + "redit": 35464, + "therm": 35465, + "Ġneurite": 35466, + "Quantum": 35467, + "rachlor": 35468, + "ĠRubin": 35469, + "Ġbfnm": 35470, + "Are": 35471, + "arachn": 35472, + "Ġduck": 35473, + "ĠTrajectory": 35474, + "ĠNitric": 35475, + "lv": 35476, + "uid": 35477, + "imag": 35478, + "ĠMULT": 35479, + "Ġgenre": 35480, + "arie": 35481, + "Ġtrifluor": 35482, + "ĠCorpus": 35483, + "oliosis": 35484, + "ĠCCK": 35485, + "Kit": 35486, + "father": 35487, + "Ġtennis": 35488, + "itsch": 35489, + "HCV": 35490, + "lantic": 35491, + "ĠAQ": 35492, + "izu": 35493, + "astatin": 35494, + "othio": 35495, + "ĠAnatomy": 35496, + "Ġáŀ": 35497, + "globulin": 35498, + "Ġinterpol": 35499, + "Ġtunnels": 35500, + "Ġnatri": 35501, + "imed": 35502, + "ĠDew": 35503, + "Ġsubscripts": 35504, + "tites": 35505, + "Ġhistologically": 35506, + "Opt": 35507, + "xn": 35508, + "Ġresampling": 35509, + "aney": 35510, + "Ġtrast": 35511, + "Ġsinensis": 35512, + "Ġsenescent": 35513, + "Fast": 35514, + "Ġhampered": 35515, + "Ġblocker": 35516, + "ushima": 35517, + "Ġhospitalizations": 35518, + "Lim": 35519, + "oons": 35520, + "ÿ": 35521, + "ĠAPS": 35522, + "ĠYok": 35523, + "ĠZam": 35524, + "Ġexperimenter": 35525, + "ĠDisks": 35526, + "Ġà¬": 35527, + "ĠScop": 35528, + "ĠAph": 35529, + "ĠParents": 35530, + "ĠPlots": 35531, + "ĠCONT": 35532, + "ĠÐĪ": 35533, + "Ġhomologue": 35534, + "ĠCooling": 35535, + "reth": 35536, + "Ġovari": 35537, + "ĠTamil": 35538, + "vrule": 35539, + "ĠPCP": 35540, + "arious": 35541, + "Active": 35542, + "oprotection": 35543, + "ĠAlfv": 35544, + "Ġinfra": 35545, + "ĠCoherence": 35546, + "closures": 35547, + "hydroxymethyl": 35548, + "EH": 35549, + "Ġmaser": 35550, + "ĠNIST": 35551, + "leck": 35552, + "concat": 35553, + "Ġtraine": 35554, + "Ġmixes": 35555, + "Ġribosomes": 35556, + "lia": 35557, + "puls": 35558, + "Ġascer": 35559, + "ĠBanks": 35560, + "ellin": 35561, + "applied": 35562, + "Ġclips": 35563, + "Ġmetap": 35564, + "Ġcopro": 35565, + "Ġepidid": 35566, + "ĠEpidemiological": 35567, + "ĠNicholas": 35568, + "ĠKings": 35569, + "Ġlarva": 35570, + "BsAg": 35571, + "ĠSánchez": 35572, + "ĠâĪİ": 35573, + "AMD": 35574, + "ĠHao": 35575, + "ĠBillboard": 35576, + "ĠAboriginal": 35577, + "Ġnylon": 35578, + "ĠNAN": 35579, + "cores": 35580, + "ĠCrop": 35581, + "Ġcommittees": 35582, + "Ġdihedral": 35583, + "ĠJuli": 35584, + "ĠAndy": 35585, + "hydration": 35586, + "corresponds": 35587, + "Mut": 35588, + "Ġtorn": 35589, + "ĠFEV": 35590, + "Ġxs": 35591, + "amphen": 35592, + "Ġsummarization": 35593, + "ĠErg": 35594, + "ËĨ": 35595, + "ĠJunction": 35596, + "ancouver": 35597, + "ĠExamining": 35598, + "ĠMarco": 35599, + "Pointer": 35600, + "Ġscarcity": 35601, + "uncing": 35602, + "Ġbijective": 35603, + "ĠMaine": 35604, + "ĠRHIC": 35605, + "Ġtowers": 35606, + "Ġgentamicin": 35607, + "Ġtonic": 35608, + "ĠkT": 35609, + "Ġclimbing": 35610, + "Ġrecruits": 35611, + "ĠHotel": 35612, + "ĠJews": 35613, + "ĠRUNX": 35614, + "Ġaustenite": 35615, + "ĠOfficer": 35616, + "inent": 35617, + "ucc": 35618, + "ĠBidirectional": 35619, + "Ġmayor": 35620, + "ĠAssays": 35621, + "ĠERG": 35622, + "SNPs": 35623, + "dine": 35624, + "China": 35625, + "starting": 35626, + "Ġirrational": 35627, + "ĠDIFFE": 35628, + "Ġmilliseconds": 35629, + "Lik": 35630, + "inone": 35631, + "ĠâģĦ": 35632, + "Ġconspicuous": 35633, + "Ġsurplus": 35634, + "ĠXiong": 35635, + "Ġupgrade": 35636, + "Ġtimep": 35637, + "ĠÄĮ": 35638, + "TeV": 35639, + "orbidities": 35640, + "invalid": 35641, + "Ġvide": 35642, + "terra": 35643, + "Ġantin": 35644, + "emens": 35645, + "ocese": 35646, + "ĠKI": 35647, + "Ġevolutionarily": 35648, + "Ker": 35649, + "ĠLES": 35650, + "clamp": 35651, + "Ġslowed": 35652, + "glycoprotein": 35653, + "entieth": 35654, + "Ġabroad": 35655, + "Ġinterpolating": 35656, + "Ġcatalyze": 35657, + "ĠBelgian": 35658, + "Ġphotographed": 35659, + "Ġpector": 35660, + "ĠSIV": 35661, + "ĠELECT": 35662, + "Ġdesal": 35663, + "oneph": 35664, + "ĠClos": 35665, + "Ġaffordable": 35666, + "birds": 35667, + "gom": 35668, + "Ġrr": 35669, + "Ġuni": 35670, + "ĠGenus": 35671, + "ĠRegge": 35672, + "ĠMultidimensional": 35673, + "Ġpsychopathology": 35674, + "Ġcertification": 35675, + "Pattern": 35676, + "ĠTower": 35677, + "Ġstern": 35678, + "Ġsublattice": 35679, + "Ġgrat": 35680, + "Ġlyrics": 35681, + "fmt": 35682, + "oceptive": 35683, + "ĠdP": 35684, + "ĠHolmes": 35685, + "Ġbudgets": 35686, + "Ġeutectic": 35687, + "ĠPv": 35688, + "ĠGott": 35689, + "Ġdisinfection": 35690, + "Ġretinoic": 35691, + "ĠObst": 35692, + "Ġreplen": 35693, + "Ġâĸł": 35694, + "Kutta": 35695, + "Please": 35696, + "ĠCAG": 35697, + "ĠStir": 35698, + "speaking": 35699, + "Ġinsecticides": 35700, + "ĠFungi": 35701, + "Hod": 35702, + "RON": 35703, + "coil": 35704, + "ĠVisible": 35705, + "Ġinception": 35706, + "ĠeGFR": 35707, + "Ġreionization": 35708, + "Ġdomination": 35709, + "ĠMetro": 35710, + "Ġswept": 35711, + "MDS": 35712, + "Ġsubsidence": 35713, + "ĠFalls": 35714, + "ĠDrum": 35715, + "ĠConserved": 35716, + "ĠMyers": 35717, + "Ġadaptability": 35718, + "Ġlyophil": 35719, + "ulina": 35720, + "arelli": 35721, + "ocycles": 35722, + "ĠSOA": 35723, + "ĠdsDNA": 35724, + "ĠCEO": 35725, + "Ġanchoring": 35726, + "Ġdeactivation": 35727, + "yler": 35728, + "Ġinterestingly": 35729, + "Ġiliac": 35730, + "ĠBorg": 35731, + "ĠPTC": 35732, + "ocyanin": 35733, + "Ġunused": 35734, + "ĠCarrier": 35735, + "Which": 35736, + "Ġintervening": 35737, + "Ġprivile": 35738, + "hit": 35739, + "Ġcheaper": 35740, + "ĠCyclin": 35741, + "plying": 35742, + "ĠCleveland": 35743, + "ĠHahn": 35744, + "Ġagglut": 35745, + "ĠAnch": 35746, + "ĠRedox": 35747, + "Will": 35748, + "ĠLinn": 35749, + "rones": 35750, + "ĠNewcastle": 35751, + "ĠExpected": 35752, + "ĠOpportunities": 35753, + "ĠLarger": 35754, + "Ġleach": 35755, + "Ġpepper": 35756, + "Sha": 35757, + "sector": 35758, + "you": 35759, + "Ġreplications": 35760, + "cholesterolem": 35761, + "ĠInvasion": 35762, + "Ġbony": 35763, + "ĠHuber": 35764, + "thend": 35765, + "Ġrealised": 35766, + "Ġinvestments": 35767, + "Cataly": 35768, + "ĠWitt": 35769, + "ĠKai": 35770, + "Ġetched": 35771, + "ĠSTEM": 35772, + "Ġexcludes": 35773, + "Exec": 35774, + "ĠStrongly": 35775, + "ĠSymposium": 35776, + "ĠTuberculosis": 35777, + "ilance": 35778, + "ĠRIS": 35779, + "apia": 35780, + "ensated": 35781, + "neb": 35782, + "ĠChains": 35783, + "Ġenthus": 35784, + "quadrup": 35785, + "decl": 35786, + "Ġbinned": 35787, + "Ġsynergistically": 35788, + "Ġgauges": 35789, + "whether": 35790, + "disease": 35791, + "Western": 35792, + "Ġhypothermia": 35793, + "ĠGardner": 35794, + "Ġaberration": 35795, + "Rod": 35796, + "Íĺ": 35797, + "Ġfd": 35798, + "Ġstood": 35799, + "Ġconditionally": 35800, + "Ġthrombol": 35801, + "PSC": 35802, + "Ġmk": 35803, + "ĠTER": 35804, + "odds": 35805, + "ĠKri": 35806, + "ĠIVF": 35807, + "Ġmites": 35808, + "ĠCHE": 35809, + "Ġqq": 35810, + "ĠInfants": 35811, + "ĠCharlot": 35812, + "becco": 35813, + "etom": 35814, + "ĠCDS": 35815, + "Ġarchaeal": 35816, + "ĠHNSCC": 35817, + "Ġmonodromy": 35818, + "amphenicol": 35819, + "apers": 35820, + "reactivity": 35821, + "Ġunderm": 35822, + "Internal": 35823, + "ĠLandsat": 35824, + "German": 35825, + "Ġcervix": 35826, + "idazole": 35827, + "ĠSketch": 35828, + "ĠLAM": 35829, + "ĠNerve": 35830, + "ĠTeh": 35831, + "Ġmussel": 35832, + "з": 35833, + "ĠMicroarray": 35834, + "wei": 35835, + "Ġwhey": 35836, + "Ġmixer": 35837, + "Ġreconfigurable": 35838, + "Ġvasculitis": 35839, + "Ġkwargs": 35840, + "Ġreus": 35841, + "correlations": 35842, + "Ġwoody": 35843, + "carbonate": 35844, + "ectomized": 35845, + "Ġretrans": 35846, + "Ġcytometric": 35847, + "ĠWildlife": 35848, + "ĠAnswering": 35849, + "Ġpencil": 35850, + "ĠDAS": 35851, + "akrish": 35852, + "CEPT": 35853, + "ĠÄĿ": 35854, + "ĠPersian": 35855, + "converting": 35856, + "Ġcater": 35857, + "Ġmeanwhile": 35858, + "TPA": 35859, + "Ġrum": 35860, + "ĠGros": 35861, + "upe": 35862, + "Ġregurg": 35863, + "Ġpenalties": 35864, + "Positive": 35865, + "****************************************************************************": 35866, + "XF": 35867, + "eenth": 35868, + "ĠCory": 35869, + "odulation": 35870, + "Ġquorum": 35871, + "codes": 35872, + "aram": 35873, + "ĠTSA": 35874, + "ĠPn": 35875, + "âĪij": 35876, + "prison": 35877, + "Ġconfidentiality": 35878, + "EPS": 35879, + "Xiv": 35880, + "iensis": 35881, + "estones": 35882, + "ĠZag": 35883, + "Ġpredecessor": 35884, + "Ġprize": 35885, + "Ġâݨ": 35886, + "steroidal": 35887, + "opard": 35888, + "Ġimpractical": 35889, + "Ġdemonstrations": 35890, + "Ġpredisposition": 35891, + "Ġkk": 35892, + "Ġmodifiers": 35893, + "Ġpreca": 35894, + "Ġexecutes": 35895, + "Ġbinning": 35896, + "Ġpedig": 35897, + "ĠKLF": 35898, + "ĠSkeletal": 35899, + "ĠCIN": 35900, + "atured": 35901, + "Ġdecomposes": 35902, + "Ġaphid": 35903, + "Bern": 35904, + "Pur": 35905, + "ĠEPO": 35906, + "merge": 35907, + "ĠCOSM": 35908, + "amyloid": 35909, + "monia": 35910, + "ĠScores": 35911, + "ĠRegistration": 35912, + "ĠAgrobacterium": 35913, + "Ġenterprises": 35914, + "locality": 35915, + "ĠITO": 35916, + "Ġtess": 35917, + "Ġfcc": 35918, + "ĠNc": 35919, + "Ġcoaxial": 35920, + "ĠAdvant": 35921, + "APC": 35922, + "ĠDemand": 35923, + "adjust": 35924, + "Points": 35925, + "Ġheterostructure": 35926, + "iffiffiffiffiffiffiffiffiffiffiffiffiffiffiffiff": 35927, + "DQ": 35928, + "Ġtensions": 35929, + "abund": 35930, + "ĠHutch": 35931, + "brew": 35932, + "Ġvitreous": 35933, + "ĠEZH": 35934, + "Ġmerc": 35935, + "Ġdebated": 35936, + "Ġpalate": 35937, + "ocolate": 35938, + "Ġevapotranspiration": 35939, + "ĠẼ": 35940, + "ĠHoffman": 35941, + "ĠGALAXIES": 35942, + "CAL": 35943, + "caps": 35944, + "legal": 35945, + "died": 35946, + "ĠIsolates": 35947, + "Ġaggrav": 35948, + "qs": 35949, + "ĠICT": 35950, + "Ġseals": 35951, + "Ġspinel": 35952, + "ĠGeor": 35953, + "Blue": 35954, + "Ġureter": 35955, + "spline": 35956, + "ĠIntroducing": 35957, + "thendieck": 35958, + "opper": 35959, + "Ġafterglow": 35960, + "Ġendosomal": 35961, + "Ġrealizes": 35962, + "solving": 35963, + "Ġmistake": 35964, + "ĠAtheros": 35965, + "ĠSBS": 35966, + "ĠRut": 35967, + "exist": 35968, + "Prof": 35969, + "ĠNeisser": 35970, + "MSG": 35971, + "ĠEarlier": 35972, + "ĠdT": 35973, + "ĠSpread": 35974, + "ĠReflection": 35975, + "secondary": 35976, + "approximate": 35977, + "Ġnigra": 35978, + "Solution": 35979, + "anone": 35980, + "ĠItems": 35981, + "Ġwavelets": 35982, + "ĠSoluble": 35983, + "Ġcircularly": 35984, + "ĠCUDA": 35985, + "Ġregenerated": 35986, + "SPI": 35987, + "ĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ": 35988, + "aturing": 35989, + "REQ": 35990, + "Ġinteroper": 35991, + "reev": 35992, + "ONT": 35993, + "ischen": 35994, + "ĠChoosing": 35995, + "phosphorylated": 35996, + "áĪ": 35997, + "Ġdress": 35998, + "ĠConform": 35999, + "Ġrememb": 36000, + "Ġischaemic": 36001, + "Basic": 36002, + "ĠPang": 36003, + "Ġcrit": 36004, + "ĠOrn": 36005, + "Ġgm": 36006, + "ĠFog": 36007, + "ĠBd": 36008, + "racheal": 36009, + "Ġphenols": 36010, + "ĠDistingu": 36011, + "Ġâİ©": 36012, + "ĠGRBs": 36013, + "ĠCeO": 36014, + "ĠBiomass": 36015, + "Ġaptamer": 36016, + "visc": 36017, + "hetically": 36018, + "Ġsid": 36019, + "omeg": 36020, + "Ġproportionality": 36021, + "ÃŃs": 36022, + "toplasmic": 36023, + "ĠConnected": 36024, + "Ġlaminin": 36025, + "strahlung": 36026, + "ĠLad": 36027, + "TRAN": 36028, + "är": 36029, + "Ġbasalt": 36030, + "ĠCurvature": 36031, + "Ġmitigating": 36032, + "opaedic": 36033, + "ĠMuhammad": 36034, + "CAR": 36035, + "Gi": 36036, + "Ġetch": 36037, + "hair": 36038, + "Ġpurine": 36039, + "Ġbenchmarking": 36040, + "reich": 36041, + "Ġmethicillin": 36042, + "âĪ¥": 36043, + "Ġmanages": 36044, + "solvent": 36045, + "ĠShao": 36046, + "hc": 36047, + "Ġstruck": 36048, + "Ġnucleosome": 36049, + "ĠPublication": 36050, + "Metric": 36051, + "Ġwines": 36052, + "ĠMBL": 36053, + "ĠHub": 36054, + "ĠAssistant": 36055, + "Ġreliance": 36056, + "Ġrouters": 36057, + "ĠHerz": 36058, + "ĠTobacco": 36059, + "rogram": 36060, + "ĠHSD": 36061, + "ĠLBP": 36062, + "Ġinflection": 36063, + "school": 36064, + "Ġsponsored": 36065, + "ĠCenozoic": 36066, + "Ġentertainment": 36067, + "atian": 36068, + "architecture": 36069, + "browse": 36070, + "REC": 36071, + "isture": 36072, + "ĠCholesterol": 36073, + "ĠSimplified": 36074, + "Ġpolypeptides": 36075, + "Ġpunctures": 36076, + "arachnoid": 36077, + "Self": 36078, + "Ġanorexia": 36079, + "ĠOle": 36080, + "ĉĉĠĠĠĠ": 36081, + "GBT": 36082, + "Ġcardiomyocyte": 36083, + "ĠFloquet": 36084, + "analog": 36085, + "Ġsensitized": 36086, + "ĠCephe": 36087, + "catch": 36088, + "chial": 36089, + "Ġceremony": 36090, + "Ġterat": 36091, + "Ġameliorate": 36092, + "olysin": 36093, + "etooth": 36094, + "akin": 36095, + "haem": 36096, + "Ġentropies": 36097, + "Ġargu": 36098, + "Ġcopied": 36099, + "lington": 36100, + "ĠHerpes": 36101, + "ĠSchwann": 36102, + "yk": 36103, + "ĠCEA": 36104, + "ĠICH": 36105, + "Ġwrink": 36106, + "Ġrunners": 36107, + "Ġgalvan": 36108, + "Ġconsolidated": 36109, + "ĠâĢ¡": 36110, + "ĠClassic": 36111, + "Ġepidemiologic": 36112, + "ĠDriving": 36113, + "Ġtrastuzumab": 36114, + "CYP": 36115, + "NCT": 36116, + "tability": 36117, + "Ġslee": 36118, + "ĠNeck": 36119, + "Ġassesses": 36120, + "Ġsymmetrically": 36121, + "ĠPotts": 36122, + "ĠRibosomal": 36123, + "diction": 36124, + "gall": 36125, + "ĠAcceleration": 36126, + "CLA": 36127, + "ACTER": 36128, + "xed": 36129, + "Ġgeriatric": 36130, + "threonine": 36131, + "Ġabort": 36132, + "Ġartem": 36133, + "ĠDisney": 36134, + "ĠCorrespondence": 36135, + "Ġrent": 36136, + "ĠNUM": 36137, + "ĠChun": 36138, + "ĠRecogn": 36139, + "Ġcrystallized": 36140, + "Ġcontradicting": 36141, + "visors": 36142, + "malignant": 36143, + "rophysiology": 36144, + "Infrared": 36145, + "gz": 36146, + "Ġsublim": 36147, + "omatosis": 36148, + "osyltransferase": 36149, + "Ġholography": 36150, + "orenstein": 36151, + "¾±": 36152, + "ĠSebas": 36153, + "accum": 36154, + "Upper": 36155, + "antenna": 36156, + "Ġblur": 36157, + "Ġsmell": 36158, + "Ġthreefold": 36159, + "ĠPlayers": 36160, + "Ġalleviated": 36161, + "Bin": 36162, + "Ġninet": 36163, + "ĠDna": 36164, + "Ġgeneralizing": 36165, + "Ġbreakage": 36166, + "ĠMorrison": 36167, + "macro": 36168, + "Reader": 36169, + "ogravimetric": 36170, + "Ġdh": 36171, + "lew": 36172, + "xton": 36173, + "Ġdeceleration": 36174, + "ĠCorrelated": 36175, + "ĠLegion": 36176, + "Ġgambling": 36177, + "Binding": 36178, + "ĠInAs": 36179, + "lowering": 36180, + "Ġeuthanized": 36181, + "ĠDallas": 36182, + "ĠDw": 36183, + "ĠDijk": 36184, + "ĠPolic": 36185, + "ĠTIME": 36186, + "ĠHEL": 36187, + "ĠLanguages": 36188, + "Ġparabol": 36189, + "porating": 36190, + "Ġfrustration": 36191, + "μM": 36192, + "balls": 36193, + "ĠArmstrong": 36194, + "Ġcontractility": 36195, + "Ġmetalloproteinases": 36196, + "americ": 36197, + "ĠZak": 36198, + "ĠCosts": 36199, + "Alex": 36200, + "dog": 36201, + "pw": 36202, + "ĠTight": 36203, + "ĠAnterior": 36204, + "Ġpeaking": 36205, + "Ġnegativity": 36206, + "Ġhydride": 36207, + "ĠLiv": 36208, + "Ġsterilized": 36209, + "Ġverbatim": 36210, + "Alternatively": 36211, + "REQU": 36212, + "ĠTyphimurium": 36213, + "ĠWeinberg": 36214, + "DSC": 36215, + "rq": 36216, + "Ġcorrug": 36217, + "Ġmicrons": 36218, + "coord": 36219, + "ioid": 36220, + "sat": 36221, + "Ġflocc": 36222, + "ĠAccelerated": 36223, + "Ġsixteen": 36224, + "absence": 36225, + "ĠSpeaker": 36226, + "omological": 36227, + "ĠApr": 36228, + "Ġmatroid": 36229, + "tight": 36230, + "ogenetically": 36231, + "rump": 36232, + "ĠInhibits": 36233, + "ĠOlympus": 36234, + "Ġpossession": 36235, + "Ġsupervisor": 36236, + "Ġconcise": 36237, + "optimized": 36238, + "vivo": 36239, + "Ġstepped": 36240, + "ocyanine": 36241, + "Five": 36242, + "anas": 36243, + "arten": 36244, + "ĠCaco": 36245, + "Ġsolutes": 36246, + "ITAL": 36247, + "ĠReddy": 36248, + "Ġwarping": 36249, + "Ġoligomer": 36250, + "Ġcapped": 36251, + "Ġvoted": 36252, + "ĠRico": 36253, + "ĠTrem": 36254, + "Ġlime": 36255, + "ĠISP": 36256, + "ĠLayers": 36257, + "skin": 36258, + "ranged": 36259, + "áz": 36260, + "Ġbioactivity": 36261, + "Ġdurable": 36262, + "Ġhn": 36263, + "ĠCAB": 36264, + "Ġva": 36265, + "ĠUWB": 36266, + "ĠStuart": 36267, + "Ġlengthy": 36268, + "Ġinvasiveness": 36269, + "ĠâĩĶ": 36270, + "joining": 36271, + "ĠRBCs": 36272, + "Ġresilient": 36273, + "ĠManipulation": 36274, + "Germ": 36275, + "contribution": 36276, + "Ġqualify": 36277, + "ĠDashed": 36278, + "Ġaccelerations": 36279, + "ĠCytochrome": 36280, + "Ġcircumstellar": 36281, + "cavity": 36282, + "Ġanatase": 36283, + "ĠDevi": 36284, + "Ġpursu": 36285, + "ĠMicroRNAs": 36286, + "Ġnorthward": 36287, + "Ġsunflower": 36288, + "ĠEntertainment": 36289, + "Pacific": 36290, + "ĠHolographic": 36291, + "uj": 36292, + "erell": 36293, + "methanol": 36294, + "Surface": 36295, + "opositive": 36296, + "Ġthreatening": 36297, + "Ġtranscend": 36298, + "Depend": 36299, + "Ġqi": 36300, + "tised": 36301, + "ĠBristol": 36302, + "ummation": 36303, + "Ġextractor": 36304, + "Ġfavoured": 36305, + "ĠPyro": 36306, + "ĠEngineers": 36307, + "flatten": 36308, + "tolerance": 36309, + "Ġxt": 36310, + "ĠTot": 36311, + "Ġtestbed": 36312, + "ICU": 36313, + "ĠSwarm": 36314, + "Ġinternationally": 36315, + "Ġantine": 36316, + "ĠInsurance": 36317, + "bai": 36318, + "nh": 36319, + "Ñĭ": 36320, + "osac": 36321, + "ĠLec": 36322, + "thor": 36323, + "Ġoutermost": 36324, + "Ġdoors": 36325, + "Ġbiometric": 36326, + "glutamate": 36327, + "ĠWoods": 36328, + "ĠMunich": 36329, + "uximab": 36330, + "places": 36331, + "Ġamyotrophic": 36332, + "ĠParam": 36333, + "ĠChristensen": 36334, + "Age": 36335, + "enne": 36336, + "Ġanim": 36337, + "Ġrecrystallization": 36338, + "ĠPropositions": 36339, + "Ġsnails": 36340, + "Secondly": 36341, + "ĠPUFA": 36342, + "France": 36343, + "Src": 36344, + "vitro": 36345, + "omass": 36346, + "uru": 36347, + "ĠLever": 36348, + "ectonic": 36349, + "embl": 36350, + "PCL": 36351, + "Ġcoordinator": 36352, + "ĠFoxp": 36353, + "ĠBirmingham": 36354, + "ĠLiberal": 36355, + "Ġcruise": 36356, + "Ġiθ": 36357, + "Ġsymp": 36358, + "azaki": 36359, + "ĠParse": 36360, + "Ġhydrologic": 36361, + "Ġprolongation": 36362, + "ĠHayes": 36363, + "Ġsubmuc": 36364, + "Ġagglomeration": 36365, + "ARE": 36366, + "ĠFMR": 36367, + "ĠLomb": 36368, + "mathchar": 36369, + "Ġstructuring": 36370, + "Ġelectrophoretic": 36371, + "Ġdiminishing": 36372, + "Ġbrake": 36373, + "chenko": 36374, + "ĠPereira": 36375, + "lens": 36376, + "Ġbackend": 36377, + "Ġillustrations": 36378, + "Ġdemanded": 36379, + "Ġnoticeably": 36380, + "ĠKaiser": 36381, + "ĠDavidson": 36382, + "Ġbraking": 36383, + "Tp": 36384, + "Forward": 36385, + "μν": 36386, + "ĠCdS": 36387, + "Ġasteroids": 36388, + "Provider": 36389, + "ĠEut": 36390, + "Ġtril": 36391, + "ungs": 36392, + "Ġdiving": 36393, + "ĠUAVs": 36394, + "ĠiPSC": 36395, + "iint": 36396, + "Ġ×": 36397, + "thrombin": 36398, + "Ġcoordinating": 36399, + "extrem": 36400, + "Ġembolization": 36401, + "ĠAdip": 36402, + "plated": 36403, + "ĠHag": 36404, + "ĠETS": 36405, + "Ġbrood": 36406, + "Ang": 36407, + "ĠPCV": 36408, + "detail": 36409, + "RSS": 36410, + "bens": 36411, + "Ġtier": 36412, + "ĠCock": 36413, + "Ġgay": 36414, + "Ġquint": 36415, + "Ġagenda": 36416, + "Ġaffairs": 36417, + "ĠModerate": 36418, + "helical": 36419, + "ĠEquivalent": 36420, + "Ġproportionally": 36421, + "Column": 36422, + "FWHM": 36423, + "Air": 36424, + "Enum": 36425, + "ifice": 36426, + "arcsec": 36427, + "ĠTRIM": 36428, + "ĠLabeling": 36429, + "QAM": 36430, + "pies": 36431, + "Ġisotropy": 36432, + "ĠGó": 36433, + "Ġpointers": 36434, + "tigraphy": 36435, + "ramers": 36436, + "Ġmacaque": 36437, + "Ġmisses": 36438, + "Ġellipticity": 36439, + "presented": 36440, + "galactosidase": 36441, + "ÉĽ": 36442, + "inion": 36443, + "Ġmite": 36444, + "lll": 36445, + "Objective": 36446, + "Ġprisoners": 36447, + "ĠHercules": 36448, + "Ġantis": 36449, + "Ġclosures": 36450, + "ĠMartian": 36451, + "Ġterpen": 36452, + "robust": 36453, + "Ġsequelae": 36454, + "alarial": 36455, + "ĠCSA": 36456, + "ĠBland": 36457, + "ĠGent": 36458, + "Ġorphan": 36459, + "Ġindent": 36460, + "bigwedge": 36461, + "Ġdefinable": 36462, + "Ġoligosaccharides": 36463, + "ĠBattalion": 36464, + "Ġisometries": 36465, + "azolin": 36466, + "ĠShown": 36467, + "spectra": 36468, + "Visual": 36469, + "<<<<<<<<": 36470, + "Ġlentiviral": 36471, + "othelioma": 36472, + "Ġtedious": 36473, + "ĠBCI": 36474, + "Ġgeologic": 36475, + "Ġconsumes": 36476, + "ĠAblation": 36477, + "least": 36478, + "Ġthigh": 36479, + "Ġsecrecy": 36480, + "covering": 36481, + "eiro": 36482, + "õ": 36483, + "ĠTBS": 36484, + "Ġisomerase": 36485, + "Ġrecommends": 36486, + "ĠVortex": 36487, + "ĠBray": 36488, + "Ġsubd": 36489, + "ĠOptions": 36490, + "Ġmetamaterial": 36491, + "ĠSquares": 36492, + "trap": 36493, + "imon": 36494, + "Ġhesit": 36495, + "Ġabc": 36496, + "cessing": 36497, + "ĠRET": 36498, + "Ġpinned": 36499, + "Ġketones": 36500, + "Ġwelded": 36501, + "ĠMitochondria": 36502, + "Ġingested": 36503, + "ĠQFT": 36504, + "Ġcomparator": 36505, + "Ġoxidoreductase": 36506, + "Ġtetrad": 36507, + "ĠSensitive": 36508, + "Ġcatchments": 36509, + "Ġrefugees": 36510, + "Ġpuberty": 36511, + "Arab": 36512, + "Ġinterannual": 36513, + "scattered": 36514, + "ĠMetam": 36515, + "Ġcyclization": 36516, + "pertures": 36517, + "ĠLINC": 36518, + "rules": 36519, + "ĠPont": 36520, + "PTH": 36521, + "ĉĉĉĉĉĉĉĉ": 36522, + "Santa": 36523, + "ĠLNC": 36524, + "Ġsubmodular": 36525, + "rective": 36526, + "Ġtrif": 36527, + "Ġsentinel": 36528, + "ĠTwin": 36529, + "keletons": 36530, + "miral": 36531, + "aming": 36532, + "ĠGay": 36533, + "Ġinterspecific": 36534, + "Ġrelieve": 36535, + "Ġendomorphism": 36536, + "ĠExpanding": 36537, + "ĠRuntime": 36538, + "yang": 36539, + "requires": 36540, + "odine": 36541, + "ometabolic": 36542, + "Store": 36543, + "planet": 36544, + "Ġrenov": 36545, + "___": 36546, + "adenosine": 36547, + "uitive": 36548, + "Ġkel": 36549, + "ĠProlong": 36550, + "ĠAdvance": 36551, + "Ġantimicrobials": 36552, + "ĠMunicipal": 36553, + "ĠNeutrophil": 36554, + "FAs": 36555, + "ĠFame": 36556, + "ibus": 36557, + "ETE": 36558, + "Ġstepping": 36559, + "ĠBlot": 36560, + "ĠLaura": 36561, + "Ġrocky": 36562, + "ĠLima": 36563, + "Ġmitigated": 36564, + "ĠLambert": 36565, + "Ġunexplored": 36566, + "Ġtrigonometric": 36567, + "pig": 36568, + "ĠHeli": 36569, + "Ġfinely": 36570, + "Ġoxidizing": 36571, + "Ġcolonoscopy": 36572, + "activities": 36573, + "ĠEasy": 36574, + "Ġunexplained": 36575, + "aky": 36576, + "ASM": 36577, + "worker": 36578, + "ĠCrist": 36579, + "ãĢģ": 36580, + "ulk": 36581, + "ĠSugg": 36582, + "ĠMim": 36583, + "Ġiterates": 36584, + "Ġsulfoxide": 36585, + "glucan": 36586, + "Ġreactant": 36587, + "Ġphagocytic": 36588, + "Brain": 36589, + "ucted": 36590, + "ĠScand": 36591, + "ĠCaCO": 36592, + "Ġaffiliation": 36593, + "Policy": 36594, + "ĠInfantry": 36595, + "Functional": 36596, + "rtimes": 36597, + "Ġwond": 36598, + "ardment": 36599, + "ĠWeil": 36600, + "Ġdirectors": 36601, + "uffix": 36602, + "ĠRuiz": 36603, + "ĠPhenomena": 36604, + "Ġmicrob": 36605, + "cosm": 36606, + "Ġutilisation": 36607, + "persed": 36608, + "Ġconsole": 36609, + "ticulate": 36610, + "Ġdesens": 36611, + "Ġreplicas": 36612, + "Ġpluripotency": 36613, + "ĠUkrainian": 36614, + "Ġhydrolyzed": 36615, + "ĠBiodiversity": 36616, + "Efficient": 36617, + "ĠKash": 36618, + "minor": 36619, + "Ġconclusive": 36620, + "Ġtentative": 36621, + "jira": 36622, + "Ġmb": 36623, + "ĠIPA": 36624, + "ĠPis": 36625, + "Ġgoverns": 36626, + "ĠSouthwest": 36627, + "oeba": 36628, + "ĠMohammad": 36629, + "albumin": 36630, + "circles": 36631, + "ĠHedge": 36632, + "ĠAmph": 36633, + "BACK": 36634, + "Old": 36635, + "histor": 36636, + "acular": 36637, + "ĠNOR": 36638, + "henius": 36639, + "visions": 36640, + "missibility": 36641, + "Ġthromboembolism": 36642, + "atized": 36643, + "Ġwil": 36644, + "awing": 36645, + "ASI": 36646, + "Ġheterodimer": 36647, + "Ġbuffering": 36648, + "ĠIdeally": 36649, + "ĠEgg": 36650, + "ographies": 36651, + "ĠAppl": 36652, + "ĠCIs": 36653, + "meaning": 36654, + "ĠSMAD": 36655, + "Ġphenylalanine": 36656, + "ĠTitanium": 36657, + "ĠZariski": 36658, + "Ġnymph": 36659, + "Ġhired": 36660, + "ĠPPC": 36661, + "ĠKG": 36662, + "ĠGuill": 36663, + "oglycans": 36664, + "erial": 36665, + "Dele": 36666, + "ilus": 36667, + "ĠFitness": 36668, + "Ġwhales": 36669, + "grant": 36670, + "mostly": 36671, + "Ġclimates": 36672, + "ĠCampaign": 36673, + "MgO": 36674, + "Ġepistemic": 36675, + "Lipschitz": 36676, + "ĠLAT": 36677, + "Ġcladding": 36678, + "vacuum": 36679, + "agglutinin": 36680, + "kill": 36681, + "Ġsail": 36682, + "Ġartistic": 36683, + "answ": 36684, + "ĠSDF": 36685, + "ĠKeith": 36686, + "Ġsorafenib": 36687, + "Ġgallbladder": 36688, + "directory": 36689, + "Ġphotoreceptors": 36690, + "ĠFokker": 36691, + "DU": 36692, + "Ġeditors": 36693, + "Ġtelecommun": 36694, + "ardia": 36695, + "ĠPublications": 36696, + "Ġscrews": 36697, + "ĠMathematica": 36698, + "RSV": 36699, + "ĠApply": 36700, + "ĠSTS": 36701, + "ĠMurine": 36702, + "Ġdump": 36703, + "Ġlingu": 36704, + "ĠDixon": 36705, + "Ġovercomes": 36706, + "ĠPreoperative": 36707, + "Ġmigrant": 36708, + "Ġbelieves": 36709, + "BK": 36710, + "actively": 36711, + "ĠISC": 36712, + "quas": 36713, + "Ġalga": 36714, + "ichael": 36715, + "Ġdisasters": 36716, + "Ġpracticed": 36717, + "hydrophobic": 36718, + "ĠNiño": 36719, + "ĠEthanol": 36720, + "QE": 36721, + "ĠSJ": 36722, + "ĠDengue": 36723, + "Ġappl": 36724, + "ĠYoon": 36725, + "enzo": 36726, + "IFY": 36727, + "Ġchronological": 36728, + "erin": 36729, + "ĠPeg": 36730, + "ĠRelevant": 36731, + "Ġqualification": 36732, + "evine": 36733, + "Ġdendrite": 36734, + "DTD": 36735, + "cholinesterase": 36736, + "watch": 36737, + "ĠSanchez": 36738, + "Ġwashes": 36739, + "Ġpermafrost": 36740, + "ĠTertiary": 36741, + "Ġsynthesizing": 36742, + "Ġexpedition": 36743, + "routine": 36744, + "ĠSearching": 36745, + "ĠSé": 36746, + "residual": 36747, + "ĠLCD": 36748, + "entities": 36749, + "Ġendovascular": 36750, + "Ġparamount": 36751, + "pher": 36752, + "Ġstraightforwardly": 36753, + "Ġvasodil": 36754, + "ĠSchistosoma": 36755, + "Ġpermissions": 36756, + "centred": 36757, + "Ġfrustrated": 36758, + "structuring": 36759, + "ĠSchl": 36760, + "ĠInitiation": 36761, + "Ġcuticle": 36762, + "Ġforgetting": 36763, + "ĠSas": 36764, + "ĠSult": 36765, + "uno": 36766, + "Ġdisintegration": 36767, + "ĠVG": 36768, + "Ġwards": 36769, + "ĠIRE": 36770, + "upro": 36771, + "Ġsubgen": 36772, + "Ġsubclasses": 36773, + "ĠStand": 36774, + "ĠHeight": 36775, + "interpretation": 36776, + "Ġglycan": 36777, + "ĠSolvent": 36778, + "ĠMalignant": 36779, + "Ġunsuitable": 36780, + "ĠCoxeter": 36781, + "Ġspermatogenesis": 36782, + "Ġfullerene": 36783, + "Fox": 36784, + "SOC": 36785, + "wet": 36786, + "armstadt": 36787, + "Ġpropofol": 36788, + "indexed": 36789, + "Ġsnakes": 36790, + "Edit": 36791, + "ĠmJy": 36792, + "RIB": 36793, + "Ġey": 36794, + "ĠAlkal": 36795, + "Ġtriaxial": 36796, + "PSK": 36797, + "neo": 36798, + "Ġendo": 36799, + "Ġglycosides": 36800, + "Ġsyllables": 36801, + "Ġsorghum": 36802, + "loor": 36803, + "Ġgeothermal": 36804, + "guinal": 36805, + "ĠSerbia": 36806, + "æĸ": 36807, + "ĠSentinel": 36808, + "ighters": 36809, + "Ġkeyboard": 36810, + "Ġbanana": 36811, + "granular": 36812, + "Ġdeciduous": 36813, + "ĠHAR": 36814, + "neuron": 36815, + "ĠCarn": 36816, + "Ġburns": 36817, + "Boost": 36818, + "ĠDeterministic": 36819, + "pipe": 36820, + "ĠFAD": 36821, + "ĠBovine": 36822, + "ĠRou": 36823, + "Ġkan": 36824, + "autonomous": 36825, + "utrients": 36826, + "Ġhypothyroidism": 36827, + "ĠSINR": 36828, + "stret": 36829, + "Ġunaltered": 36830, + "ĠZika": 36831, + "valley": 36832, + "Ġlongitudinally": 36833, + "Ġfluorescein": 36834, + "catheter": 36835, + "ĠCongenital": 36836, + "Ġpiez": 36837, + "Ġabbreviated": 36838, + "ĠChlamydia": 36839, + "Ġaired": 36840, + "Ġqueen": 36841, + "Ġinstructive": 36842, + "Ġabruptly": 36843, + "Ġrecurrences": 36844, + "IMP": 36845, + "Ġexosome": 36846, + "ĠHSCs": 36847, + "Writer": 36848, + "elis": 36849, + "ĠArithmetic": 36850, + "enarios": 36851, + "Ġligated": 36852, + "ĠLocalized": 36853, + "ĠFreeman": 36854, + "Ġcarniv": 36855, + "ĠCereb": 36856, + "Ġgrac": 36857, + "ĠGond": 36858, + "ĠVancouver": 36859, + "obox": 36860, + "Ġtyped": 36861, + "ĠÄ¥": 36862, + "Upon": 36863, + "Future": 36864, + "ENG": 36865, + "dead": 36866, + "Ġserpent": 36867, + "ĠAssignment": 36868, + "ĠUpdated": 36869, + "Ġhistorian": 36870, + "Ġtropospheric": 36871, + "Cloud": 36872, + "bumin": 36873, + "ĠPras": 36874, + "ĠBasket": 36875, + "ĠâĪĴâĪĴ": 36876, + "benzodi": 36877, + "ĠTrauma": 36878, + "ĠBehaviors": 36879, + "Ġpter": 36880, + "irradiation": 36881, + "Ġspoke": 36882, + "ariatric": 36883, + "Ġplugin": 36884, + "Ġsupersonic": 36885, + "Ġdocetaxel": 36886, + "itigation": 36887, + "Ġdigestibility": 36888, + "nem": 36889, + "Ġpb": 36890, + "ĠCSR": 36891, + "Ġfouling": 36892, + "Ġrheology": 36893, + "Ġfloods": 36894, + "Ġgluing": 36895, + "agascar": 36896, + "jets": 36897, + "pti": 36898, + "eston": 36899, + "ĠKü": 36900, + "Ġopenings": 36901, + "Ġisolating": 36902, + "ĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ": 36903, + "Ġsemiconducting": 36904, + "rative": 36905, + "ecology": 36906, + "urization": 36907, + "Ġmultifactorial": 36908, + "shadow": 36909, + "Ġcrosslinked": 36910, + "Ġphyla": 36911, + "Ġpremises": 36912, + "ĠLOW": 36913, + "generalized": 36914, + "ĠPolynomials": 36915, + "Ġbismuth": 36916, + "ĠRoz": 36917, + "ĠDecoding": 36918, + "ĠClassifier": 36919, + "conducting": 36920, + "Ġlitterm": 36921, + "Mann": 36922, + "Ġfant": 36923, + "ĠCZ": 36924, + "ĠPSNR": 36925, + "Ġstarring": 36926, + "ĠPolyg": 36927, + "ĠHolm": 36928, + "rg": 36929, + "additional": 36930, + "guan": 36931, + "professional": 36932, + "Ġinquiry": 36933, + "ĠPg": 36934, + "ĠSchmid": 36935, + "Ġheaded": 36936, + "chaft": 36937, + "ĠExpand": 36938, + "Ġcompanions": 36939, + "Van": 36940, + "ĠSie": 36941, + "Ġcanals": 36942, + "oredoxin": 36943, + "Ġcolliding": 36944, + "absolute": 36945, + "ĠPhotos": 36946, + "ĠLegacy": 36947, + "Ġrevascularization": 36948, + "ĠPSM": 36949, + "Ġexpenses": 36950, + "ISMA": 36951, + "intervals": 36952, + "Ġmulticellular": 36953, + "Ġnonsm": 36954, + "Ġresemblance": 36955, + "Hep": 36956, + "Ġwool": 36957, + "Ġniger": 36958, + "essa": 36959, + "asci": 36960, + "Ġrotates": 36961, + "Ġcompetitions": 36962, + "Ġarrivals": 36963, + "Ġlutein": 36964, + "Ġscholarship": 36965, + "Fran": 36966, + "Ġreused": 36967, + "ĠEquivalence": 36968, + "ĠGLUT": 36969, + "grading": 36970, + "salt": 36971, + "Ġcommensal": 36972, + "Ġfraud": 36973, + "oxib": 36974, + "Ġgastroenter": 36975, + "Ġrainy": 36976, + "Ġasserts": 36977, + "Operation": 36978, + "Ġflattening": 36979, + "Put": 36980, + "XB": 36981, + "ĠpM": 36982, + "Ġconic": 36983, + "obtain": 36984, + "ĠRober": 36985, + "November": 36986, + "ĠJP": 36987, + "Ġfebrile": 36988, + "ĠBarriers": 36989, + "================================================================": 36990, + "Ġhemicell": 36991, + "ĠSCS": 36992, + "ĠNem": 36993, + "Ġraster": 36994, + "clude": 36995, + "Ġïģ¦": 36996, + "ĠElliott": 36997, + "border": 36998, + "ĠdÏĨ": 36999, + "ribose": 37000, + "ĠEnv": 37001, + "ĠDiffuse": 37002, + "ĠSupersymmetry": 37003, + "Pearson": 37004, + "FETs": 37005, + "yah": 37006, + "ulia": 37007, + "ĠDwarf": 37008, + "ĠHull": 37009, + "ĠAttribution": 37010, + "Ġrepositories": 37011, + "ĠGNSS": 37012, + "ĠVectors": 37013, + "Ġsuccesses": 37014, + "ĠManhattan": 37015, + "umbent": 37016, + "digit": 37017, + "Ġcircumferential": 37018, + "Between": 37019, + "Deg": 37020, + "oue": 37021, + "й": 37022, + "ĠDere": 37023, + "ĠRf": 37024, + "Ġride": 37025, + "ĠVoc": 37026, + "Ġprotest": 37027, + "Ġpurpos": 37028, + "ĠProofs": 37029, + "namese": 37030, + "Ġbanking": 37031, + "ĠGastrointestinal": 37032, + "ĠUnt": 37033, + "Ġwhence": 37034, + "ĠYue": 37035, + "ĠRehabilitation": 37036, + "Ġexchanging": 37037, + "ĠACTH": 37038, + "Ġcapping": 37039, + "amido": 37040, + "ĠBap": 37041, + "Ġplat": 37042, + "toString": 37043, + "Ġelectroencephal": 37044, + "Ġelectrospun": 37045, + "Mpc": 37046, + "jord": 37047, + "onv": 37048, + "Ġcraft": 37049, + "ĠCCl": 37050, + "ĠStrip": 37051, + "Ġmeditation": 37052, + "oxidative": 37053, + "ĠReduce": 37054, + "ĠCommonwealth": 37055, + "Ġrifamp": 37056, + "Flu": 37057, + "Ġreanalysis": 37058, + "otrich": 37059, + "ĠESA": 37060, + "Ġjth": 37061, + "helin": 37062, + "ĠGenotype": 37063, + "Ġdiagonalization": 37064, + "ĠGabriel": 37065, + "Ġquarantine": 37066, + "ĠCrab": 37067, + "ĠDict": 37068, + "accumulation": 37069, + "bek": 37070, + "ĠDifferentially": 37071, + "Ġlactis": 37072, + "tetrahydrofuran": 37073, + "laser": 37074, + "ĠUm": 37075, + "Ġmega": 37076, + "rme": 37077, + "ĠIndians": 37078, + "ĠLeonard": 37079, + "Ġcommodity": 37080, + "Ġfumigatus": 37081, + "iou": 37082, + "ĠEchin": 37083, + "ostream": 37084, + "Ġmembran": 37085, + "simulations": 37086, + "backend": 37087, + "ĠOBJECT": 37088, + "giving": 37089, + "ÅĻ": 37090, + "Ġinfective": 37091, + "Alg": 37092, + "ĠHuh": 37093, + "ĠMICR": 37094, + "Ġfollowers": 37095, + "ferro": 37096, + "Ġcyanide": 37097, + "Present": 37098, + "ĠEND": 37099, + "ĠMCs": 37100, + "Ġtimeline": 37101, + "ĠEmbryonic": 37102, + "Identifier": 37103, + "Ġinconclusive": 37104, + "ĠGammaproteobacteria": 37105, + "nets": 37106, + "ĠHeating": 37107, + "ankar": 37108, + "thr": 37109, + "ĠKIT": 37110, + "ĠChip": 37111, + "Ġblob": 37112, + "Ġcalculator": 37113, + "Ġtextural": 37114, + "Ġalloying": 37115, + "Application": 37116, + "ĠProteomic": 37117, + "Ġantidepressants": 37118, + "urk": 37119, + "Ġcrystallography": 37120, + "Ġcredits": 37121, + "Ġmussels": 37122, + "Tom": 37123, + "ĠFST": 37124, + "ĠFold": 37125, + "ĠHew": 37126, + "Ann": 37127, + "brook": 37128, + "Ġglycolytic": 37129, + "Torch": 37130, + "Ġvm": 37131, + "ĠMare": 37132, + "ĠJy": 37133, + "Ġheterojunction": 37134, + "ĠBorrelia": 37135, + "Risk": 37136, + "ĠNaturally": 37137, + "Ġsupplying": 37138, + "signature": 37139, + "lk": 37140, + "Ġarachid": 37141, + "olov": 37142, + "ĠSok": 37143, + "ĠHö": 37144, + "ĠRaz": 37145, + "ĠVander": 37146, + "Ġdelib": 37147, + "Ġmyth": 37148, + "Ġmidbrain": 37149, + "Ġdeceased": 37150, + "ĠSCO": 37151, + "ĠThromb": 37152, + "Ġcurr": 37153, + "Ġsummit": 37154, + "miRNAs": 37155, + "dimethylamino": 37156, + "Ġphotocatalyst": 37157, + "verbose": 37158, + "gomery": 37159, + "Ġwed": 37160, + "ĠMate": 37161, + "Ġsigni": 37162, + "rastructures": 37163, + "Ġreciprocity": 37164, + "bner": 37165, + "mast": 37166, + "neck": 37167, + "Ġcoins": 37168, + "ĠHistogram": 37169, + "crit": 37170, + "Bbbk": 37171, + "AW": 37172, + "town": 37173, + "displacement": 37174, + "ĠNeph": 37175, + "separable": 37176, + "Ġdiastere": 37177, + "ĠMODELS": 37178, + "Depth": 37179, + "ĠNeisseria": 37180, + "pdev": 37181, + "uvial": 37182, + "ĠBMS": 37183, + "ĠDennis": 37184, + "Ġrp": 37185, + "Ġnanometer": 37186, + "rocyt": 37187, + "ĠRomanian": 37188, + "Ġconceivable": 37189, + "COS": 37190, + "alveolar": 37191, + "astig": 37192, + "abwe": 37193, + "encode": 37194, + "rolactone": 37195, + "Ġreadmission": 37196, + "intersection": 37197, + "Ġamplicons": 37198, + "timulated": 37199, + "Ġcollapses": 37200, + "ochromatin": 37201, + "Haw": 37202, + "ectrum": 37203, + "ftype": 37204, + "rica": 37205, + "Ġamid": 37206, + "MPO": 37207, + "ĠExtensions": 37208, + "Ġvaric": 37209, + "Ġdiminishes": 37210, + "Ġcatheters": 37211, + "Nodes": 37212, + "Ġbbox": 37213, + "emination": 37214, + "Ġtsunami": 37215, + "diagnosis": 37216, + "cod": 37217, + "qr": 37218, + "ĠFen": 37219, + "Ġworthy": 37220, + "ĠâĩIJ": 37221, + "informatic": 37222, + "ographer": 37223, + "Ġundetected": 37224, + "ĠNCAA": 37225, + "Ġcarcinogenic": 37226, + "RU": 37227, + "Ġaneu": 37228, + "plitudes": 37229, + "keeper": 37230, + "ĠÄģ": 37231, + "Ġautistic": 37232, + "Ġcompromising": 37233, + "Ġunimodal": 37234, + "Ġrumin": 37235, + "apa": 37236, + "Ġintolerance": 37237, + "Ġdirecting": 37238, + "Ġpea": 37239, + "Ġcommenced": 37240, + "Ġshadowing": 37241, + "Center": 37242, + "Ġclad": 37243, + "Ġblues": 37244, + "binits": 37245, + "Ġmisclassification": 37246, + "ĠFAST": 37247, + "Wat": 37248, + "ĠmCherry": 37249, + "Ġbrig": 37250, + "estradiol": 37251, + "Ġwavefunctions": 37252, + "Ġblooms": 37253, + "Ġaccent": 37254, + "aji": 37255, + "occurring": 37256, + "arrest": 37257, + "Ġspecialty": 37258, + "Ġunconditional": 37259, + "Ġsponges": 37260, + "Ġdysfunctional": 37261, + "ĠNOX": 37262, + "Ġultracold": 37263, + "Ġmartensite": 37264, + "OUS": 37265, + "nier": 37266, + "isic": 37267, + "ĠMatsum": 37268, + "Ġleukemic": 37269, + "ĠBradley": 37270, + "Density": 37271, + "ĠSemiconductor": 37272, + "ĠCause": 37273, + "ĠInset": 37274, + "ĠKem": 37275, + "ĠUPR": 37276, + "para": 37277, + "echst": 37278, + "ymet": 37279, + "Ġagro": 37280, + "ĠYY": 37281, + "ĠRegeneration": 37282, + "Ġancestors": 37283, + "ĠTissues": 37284, + "Ġsulfuric": 37285, + "kd": 37286, + "Ġlasing": 37287, + "ĠPup": 37288, + "aei": 37289, + "Ġmammal": 37290, + "ĠBradford": 37291, + "Ġsegregated": 37292, + "isolated": 37293, + "ĠCuba": 37294, + "Ġblockage": 37295, + "Ġseamless": 37296, + "Ġperoxisome": 37297, + "hui": 37298, + "Ġinaug": 37299, + "Ġinfecting": 37300, + "ĠChampion": 37301, + "ĠAttitudes": 37302, + "calculate": 37303, + "Ġtighter": 37304, + "ĠSAC": 37305, + "ĠEpi": 37306, + "Ġatm": 37307, + "Ġphysico": 37308, + "Ġnth": 37309, + "ĠCanyon": 37310, + "Ġseroprevalence": 37311, + "Ġhomo": 37312, + "ĠUniversit": 37313, + "Evaluation": 37314, + "ĠAPOE": 37315, + "job": 37316, + "ĠmK": 37317, + "Ġreign": 37318, + "abo": 37319, + "ĠRugby": 37320, + "ĠNets": 37321, + "Ġrituximab": 37322, + "ativeness": 37323, + "Ġphy": 37324, + "ornis": 37325, + "Ġfeedbacks": 37326, + "United": 37327, + "Princ": 37328, + "imbabwe": 37329, + "ĠGirls": 37330, + "Ġunavoidable": 37331, + "ĠSemantics": 37332, + "Break": 37333, + "FISH": 37334, + "Mix": 37335, + "Ġnx": 37336, + "ĠBao": 37337, + "dimethylphenyl": 37338, + "ĠTOF": 37339, + "ĠCrown": 37340, + "ĠGGA": 37341, + "ĠJH": 37342, + "Ġsuperstring": 37343, + "ĠCRY": 37344, + "Ġkindly": 37345, + "YN": 37346, + "Ġundoped": 37347, + "excluding": 37348, + "ĠLeo": 37349, + "ĠPROPERT": 37350, + "peritoneally": 37351, + "mant": 37352, + "ê°": 37353, + "Ġfranch": 37354, + "ĠProst": 37355, + "DEs": 37356, + "Ġcotrans": 37357, + "Ġrk": 37358, + "Ġgeneralizability": 37359, + "Author": 37360, + "ĠAndrea": 37361, + "ĠConfocal": 37362, + "ĠAdipose": 37363, + "îĹ": 37364, + "erjee": 37365, + "Ġanimated": 37366, + "ĠFad": 37367, + "ĠCorrosion": 37368, + "ĠCircadian": 37369, + "Ġaccelerators": 37370, + "ĠArkansas": 37371, + "Ġmars": 37372, + "ĠCuc": 37373, + "ĠInterfaces": 37374, + "Ġretrievals": 37375, + "Ġmelanin": 37376, + "ĠssDNA": 37377, + "vastava": 37378, + "Ġallergens": 37379, + "bud": 37380, + "Ġinaccessible": 37381, + "ictions": 37382, + "ĠMood": 37383, + "inda": 37384, + "Ġameric": 37385, + "Ġsymbiosis": 37386, + "bersome": 37387, + "occur": 37388, + "ĠMarcus": 37389, + "ĠSuperconductivity": 37390, + "ĠCort": 37391, + "ĠHMS": 37392, + "Ġphased": 37393, + "ĠJess": 37394, + "Ġpropulsion": 37395, + "extract": 37396, + "Ġsuccinate": 37397, + "ĠÖĴ": 37398, + "inkel": 37399, + "Ġsilence": 37400, + "ĠSUV": 37401, + "Ġconstituency": 37402, + "Ġbacteriophage": 37403, + "gem": 37404, + "ĠMCL": 37405, + "orene": 37406, + "ĠGoss": 37407, + "ICD": 37408, + "Ġglutamic": 37409, + "Ġcoexisting": 37410, + "STEMS": 37411, + "opotential": 37412, + "ĠEy": 37413, + "ĠLecture": 37414, + "ellae": 37415, + "Ġimmunoprec": 37416, + "Ġtimber": 37417, + "ĠVulner": 37418, + "Ġaroma": 37419, + "Ġsands": 37420, + "ĠSpan": 37421, + "Ġhern": 37422, + "Ġincubating": 37423, + "Ġtransmitters": 37424, + "ĠHomogeneous": 37425, + "ĠConstructing": 37426, + "dit": 37427, + "Ġtc": 37428, + "alass": 37429, + "Ġstents": 37430, + "ĠMID": 37431, + "Ġanoxic": 37432, + "Ġprovisions": 37433, + "ĠCapac": 37434, + "neutron": 37435, + "ĠVOCs": 37436, + "January": 37437, + "VAS": 37438, + "once": 37439, + "ĠCache": 37440, + "opulation": 37441, + "ĠVTE": 37442, + "Ġinterphase": 37443, + "Ġblog": 37444, + "ocusing": 37445, + "hiro": 37446, + "ĠREC": 37447, + "Ġanisotropies": 37448, + "benef": 37449, + "Ġconstipation": 37450, + "ĠCanal": 37451, + "Ġportrait": 37452, + "silyl": 37453, + "ĠLinked": 37454, + "ĠBowl": 37455, + "Ġmonopoles": 37456, + "ĠPerez": 37457, + "WIN": 37458, + "ĠTAP": 37459, + "Ġruthenium": 37460, + "ĠAdherence": 37461, + "ĠEnzymatic": 37462, + "Ġspecificities": 37463, + "Ġski": 37464, + "ĠCST": 37465, + "Ġpoetry": 37466, + "ATES": 37467, + "rama": 37468, + "lores": 37469, + "ALU": 37470, + "Ġvasoconstr": 37471, + "Ġgranulocyte": 37472, + "ibi": 37473, + "Ġopts": 37474, + "avesdrop": 37475, + "eptin": 37476, + "··": 37477, + "ĠJeong": 37478, + "Ġmedullary": 37479, + "ĠDemonstration": 37480, + "ĠFIB": 37481, + "ĠBRD": 37482, + "ĠVV": 37483, + "Ġallo": 37484, + "Rule": 37485, + "Tf": 37486, + "Ġunrealistic": 37487, + "Ġlatitudinal": 37488, + "ROP": 37489, + "ĠCorrelates": 37490, + "IU": 37491, + "ĠPore": 37492, + "ocrit": 37493, + "ĠKall": 37494, + "Ġcharcoal": 37495, + "ĠMongolia": 37496, + "âĪħ": 37497, + "ĠEntity": 37498, + "Ġgrams": 37499, + "graphene": 37500, + "mine": 37501, + "entric": 37502, + "ĠPp": 37503, + "ĠWelfare": 37504, + "ĠJets": 37505, + "Ġaffirm": 37506, + "ĠBelle": 37507, + "ĠStrategic": 37508, + "APIENTR": 37509, + "KH": 37510, + "rmann": 37511, + "Ġassociating": 37512, + "ĠSurviv": 37513, + "Ġnicotinic": 37514, + "ĠWLAN": 37515, + "п": 37516, + "Ġtears": 37517, + "ĠRevised": 37518, + "Ġphosphodies": 37519, + "Ġhorseradish": 37520, + "ĠLAR": 37521, + "took": 37522, + "ĠDescent": 37523, + "ĠNOx": 37524, + "ĠSteiner": 37525, + "ĠPermian": 37526, + "ĠVenezuela": 37527, + "Ġdesiccation": 37528, + "DIS": 37529, + "ĠMSP": 37530, + "Ġpopl": 37531, + "rels": 37532, + "Ġ": 37533, + "Ġlearnt": 37534, + "ĠBiofilm": 37535, + "ĠPCNA": 37536, + "ĠAttribute": 37537, + "ĠGrothendieck": 37538, + "ĠAdolescent": 37539, + "nv": 37540, + "stderr": 37541, + "obalt": 37542, + "ĠYamamoto": 37543, + "Ġaliquot": 37544, + "rater": 37545, + "ĠOre": 37546, + "ĠKIR": 37547, + "acker": 37548, + "Ġïĥ»": 37549, + "Ġstratosphere": 37550, + "ĠCust": 37551, + "respect": 37552, + "Ġglutamatergic": 37553, + "Ġencourages": 37554, + "ctic": 37555, + "itched": 37556, + "phins": 37557, + "Ġsuburb": 37558, + "Ġhomeomorphic": 37559, + "hexah": 37560, + "Ġminiatur": 37561, + "CAN": 37562, + "ahead": 37563, + "ĠBLE": 37564, + "ĠRBF": 37565, + "Ġacutely": 37566, + "Ġ": 37567, + "Ġantenn": 37568, + "URN": 37569, + "ĠGirl": 37570, + "Ġbioreactor": 37571, + "ĠLeibniz": 37572, + "Ġvial": 37573, + "ĠLich": 37574, + "obac": 37575, + "ĠWhenever": 37576, + "inhibition": 37577, + "Cast": 37578, + "Ġstripped": 37579, + "ĠAstrophysics": 37580, + "presence": 37581, + "ĠFloer": 37582, + "ipotent": 37583, + "dichloro": 37584, + "CLE": 37585, + "finger": 37586, + "onates": 37587, + "stri": 37588, + "ĠSperm": 37589, + "ĠDBS": 37590, + "opeptide": 37591, + "separation": 37592, + "athing": 37593, + "mathp": 37594, + "ouples": 37595, + "Ġentropic": 37596, + "Ġswollen": 37597, + "Ġdonated": 37598, + "Ġsettlements": 37599, + "ovenous": 37600, + "Perm": 37601, + "ĠSard": 37602, + "egen": 37603, + "ĠAlph": 37604, + "ĠCooperation": 37605, + "ĠPDAC": 37606, + "Final": 37607, + "lapse": 37608, + "Ġrevol": 37609, + "ĠIx": 37610, + "ĠLens": 37611, + "Ġkth": 37612, + "relaxation": 37613, + "ClO": 37614, + "ichloro": 37615, + "Ġwrapper": 37616, + "ĠSimultaneously": 37617, + "Compute": 37618, + "ëĬ": 37619, + "implantation": 37620, + "ĠVLA": 37621, + "heme": 37622, + "ĠMayor": 37623, + "ĠFacilit": 37624, + "Ġbatt": 37625, + "immer": 37626, + "Ġcurated": 37627, + "Ġconfluent": 37628, + "generational": 37629, + "starts": 37630, + "Ġgranulosa": 37631, + "arboxylate": 37632, + "ĠRiesz": 37633, + "Ġtextbook": 37634, + "Ġconstitutional": 37635, + "ĠPeace": 37636, + "ĠCommander": 37637, + "Ġobscured": 37638, + "vil": 37639, + "addition": 37640, + "ĠWasserstein": 37641, + "coords": 37642, + "ĠProbes": 37643, + "Ġdelineated": 37644, + "TZVP": 37645, + "ĠINF": 37646, + "Ġdosages": 37647, + "Ġoligomerization": 37648, + "ĠNADP": 37649, + "MKII": 37650, + "omin": 37651, + "Ġlhs": 37652, + "ughen": 37653, + "ĠJong": 37654, + "ancel": 37655, + "letter": 37656, + "ĠANC": 37657, + "FUNCTION": 37658, + "Ġtram": 37659, + "Their": 37660, + "ĠGenerated": 37661, + "Ġpolycyclic": 37662, + "Ġculmin": 37663, + "Ġrectum": 37664, + "Ġceft": 37665, + "Ġmetamaterials": 37666, + "ĠBiotech": 37667, + "Ġmyself": 37668, + "Ġunifying": 37669, + "Ġeman": 37670, + "ĠSinger": 37671, + "triangleright": 37672, + "omel": 37673, + "ĠCFA": 37674, + "ocha": 37675, + "ĠGSM": 37676, + "Ġcentrifuge": 37677, + "ĠIndo": 37678, + "Ġtransporting": 37679, + "LIB": 37680, + "Ġoxalate": 37681, + "ĠDulbecco": 37682, + "Ġali": 37683, + "arginal": 37684, + "hoo": 37685, + "ischem": 37686, + "APIENTRYP": 37687, + "Apart": 37688, + "LDA": 37689, + "ensile": 37690, + "settings": 37691, + "Ġephem": 37692, + "ampa": 37693, + "Ġduplications": 37694, + "ĠWheeler": 37695, + "Physical": 37696, + "ĠCompletion": 37697, + "ĠOrdered": 37698, + "Logger": 37699, + "Ġinterferences": 37700, + "ĠPollution": 37701, + "Optimal": 37702, + "Sv": 37703, + "aicin": 37704, + "Ġpicks": 37705, + "diversity": 37706, + "tigens": 37707, + "Ġdimorphism": 37708, + "feres": 37709, + "ĠRobotic": 37710, + "Ġconfirmatory": 37711, + "Ġcathodic": 37712, + "Ġspirals": 37713, + "Ġspruce": 37714, + "Lagrange": 37715, + "wat": 37716, + "ĠAllan": 37717, + "denote": 37718, + "CID": 37719, + "always": 37720, + "ithe": 37721, + "ĠChim": 37722, + "conditional": 37723, + "barrier": 37724, + "Ġvisualizing": 37725, + "Ġïĥ¹": 37726, + "Schmidt": 37727, + "Ġconventionally": 37728, + "ĠQUANT": 37729, + "GROUND": 37730, + "Ġug": 37731, + "ĠCWE": 37732, + "ĠInspired": 37733, + "Ġbuyer": 37734, + "Ġthermost": 37735, + "Ġkinematical": 37736, + "anolic": 37737, + "Ġdif": 37738, + "Ġ": 37739, + "ĠGeo": 37740, + "Examples": 37741, + "consistency": 37742, + "ĠPalace": 37743, + "ĠVaccination": 37744, + "Ġnatriuretic": 37745, + "YAG": 37746, + "ĠCTCs": 37747, + "Univers": 37748, + "ĠAcknowledgement": 37749, + "membered": 37750, + "vv": 37751, + "ĠSession": 37752, + "Ġinstar": 37753, + "ĠLevin": 37754, + "AVI": 37755, + "Ġproliferator": 37756, + "oliths": 37757, + "ĠTemperatures": 37758, + "imming": 37759, + "ĠToeplitz": 37760, + "ICATIONS": 37761, + "ĠIntegrals": 37762, + "Ġspliced": 37763, + "Dest": 37764, + "resulting": 37765, + "ĠHope": 37766, + "Ġenclosure": 37767, + "ieves": 37768, + "flav": 37769, + "ĠAbdul": 37770, + "Ġleishmaniasis": 37771, + "ò": 37772, + "oskeleton": 37773, + "Ġadduct": 37774, + "ĠInfluences": 37775, + "EQU": 37776, + "ĠSitu": 37777, + "Ġseas": 37778, + "ĠReich": 37779, + "cyst": 37780, + "ĠEVOLUTION": 37781, + "Ġwithstand": 37782, + "ĠGinzburg": 37783, + "RNAi": 37784, + "ĠNonparametric": 37785, + "ĠPrincess": 37786, + "Ġintravascular": 37787, + "UTIONS": 37788, + "Ġglutar": 37789, + "Ġcoincided": 37790, + "ĠSaito": 37791, + "pretrained": 37792, + "combined": 37793, + "ĠTAM": 37794, + "Ġalarms": 37795, + "Ġcyclooxygenase": 37796, + "Ġbn": 37797, + "Ġplagi": 37798, + "Particle": 37799, + "GGG": 37800, + "etics": 37801, + "amber": 37802, + "ABSTRACT": 37803, + "ĠExtracts": 37804, + "ĉĉĉĠĠĠĠ": 37805, + "ĠPhylogeny": 37806, + "tow": 37807, + "ĠContaining": 37808, + "Ġendonuclease": 37809, + "incubation": 37810, + "Ġofficinal": 37811, + "Ġexplosions": 37812, + "layout": 37813, + "Ġtouchdown": 37814, + "ĠRevealed": 37815, + "Ġinfiltrate": 37816, + "enith": 37817, + "timulation": 37818, + "ĠKind": 37819, + "ervices": 37820, + "PDA": 37821, + "Ġcereus": 37822, + "Env": 37823, + "Ġlapa": 37824, + "kamp": 37825, + "mult": 37826, + "enthal": 37827, + "ĠGoldstone": 37828, + "siRNA": 37829, + "strept": 37830, + "Qual": 37831, + "mother": 37832, + "dio": 37833, + "Ġinfrequent": 37834, + "Ġcyclospor": 37835, + "hepatitis": 37836, + "thrombotic": 37837, + "GST": 37838, + "ĠLj": 37839, + "ĠUR": 37840, + "ofect": 37841, + "ĠArrow": 37842, + "ethnic": 37843, + "ĠBarcelona": 37844, + "Care": 37845, + "titious": 37846, + "Ġeta": 37847, + "Ġvirions": 37848, + "smash": 37849, + "ĠâIJ¤": 37850, + "Ġavenues": 37851, + "obarb": 37852, + "ĠComments": 37853, + "Ġanyway": 37854, + "afil": 37855, + "ĠBea": 37856, + "ĠBoys": 37857, + "ĠAutomata": 37858, + "ĠSuperconducting": 37859, + "Pic": 37860, + "kHz": 37861, + "Ġnorepinephrine": 37862, + "ĠGPC": 37863, + "Ġunderlined": 37864, + "brahim": 37865, + "Ġelectrospray": 37866, + "Ġsesqu": 37867, + "ĠTournament": 37868, + "Austr": 37869, + "ĠGrowing": 37870, + "ĠWebsite": 37871, + "LDH": 37872, + "covariance": 37873, + "several": 37874, + "stabilized": 37875, + "Ġdecarboxylase": 37876, + "Ġremed": 37877, + "rhoe": 37878, + "ĠSRS": 37879, + "ĠTreated": 37880, + "ĠMadagascar": 37881, + "ĠMagic": 37882, + "Ġweapon": 37883, + "ĠYoshida": 37884, + "Ġhypoglycemia": 37885, + "ĠBifidobacterium": 37886, + "entitious": 37887, + ":::": 37888, + "ĠSingles": 37889, + "Ġnicely": 37890, + "Ġunexpectedly": 37891, + "ibles": 37892, + "ariae": 37893, + "Ġcentroids": 37894, + "Ġbroadened": 37895, + "ĠJohns": 37896, + "ĠBacteroid": 37897, + "Ġframing": 37898, + "Primary": 37899, + "ĠPicture": 37900, + "government": 37901, + "Ġreq": 37902, + "ĠTry": 37903, + "ibo": 37904, + "Ġliquef": 37905, + "osensitivity": 37906, + "Ġslaughter": 37907, + "ĠDAR": 37908, + "Ġlogit": 37909, + "Ġpromises": 37910, + "Ġlawyer": 37911, + "ĠFPG": 37912, + "TCP": 37913, + "Ġintercalation": 37914, + "ĠBoe": 37915, + "Ġwideband": 37916, + "Ġjudgement": 37917, + "romagnets": 37918, + "Lastly": 37919, + "ĠIschemic": 37920, + "IMA": 37921, + "food": 37922, + "much": 37923, + "Ġavenue": 37924, + "Ġschistosomiasis": 37925, + "ĠExecution": 37926, + "DQU": 37927, + "GIS": 37928, + "kines": 37929, + "akage": 37930, + "echt": 37931, + "ĠScaff": 37932, + "ĠStrings": 37933, + "ĠMultilevel": 37934, + "Ġcumbersome": 37935, + "ĠRaymond": 37936, + "Ġirregularities": 37937, + "ĠAGNs": 37938, + "ĠMetastatic": 37939, + "ĠIberian": 37940, + "Mb": 37941, + "RNP": 37942, + "hong": 37943, + "isinin": 37944, + "Ġthirteen": 37945, + "ĠFAS": 37946, + "Ġsealing": 37947, + "Ġapatite": 37948, + "Ġserially": 37949, + "ĠÅĿ": 37950, + "DEL": 37951, + "Fo": 37952, + "ĠSoph": 37953, + "ĠBear": 37954, + "ĠJosh": 37955, + "reck": 37956, + "uller": 37957, + "Ġexcursion": 37958, + "Ġembodied": 37959, + "Ġhybridized": 37960, + "ĠLieutenant": 37961, + "Period": 37962, + "Ġmollus": 37963, + "CVD": 37964, + "Ren": 37965, + "REAM": 37966, + "ĠBACK": 37967, + "Ġaccreting": 37968, + "Ġculturing": 37969, + "ĠBurst": 37970, + "ĠSegment": 37971, + "Ġasterisk": 37972, + "ĠIdeal": 37973, + "Ġintertw": 37974, + "ĠAtoms": 37975, + "ĠSTE": 37976, + "Ġïģª": 37977, + "Ġremarked": 37978, + "Ġhairs": 37979, + "âľ": 37980, + "ĠMetropolis": 37981, + "ĠPartially": 37982, + "ĠObserver": 37983, + "Ġhematologic": 37984, + "obilization": 37985, + "ĠBergman": 37986, + "Ġcartesian": 37987, + "Ġclathrin": 37988, + "ĠSung": 37989, + "Ġration": 37990, + "Ġscoliosis": 37991, + "ohl": 37992, + "mutant": 37993, + "NNs": 37994, + "ĠRahman": 37995, + "ĠSpatially": 37996, + "PIP": 37997, + "Yb": 37998, + "Ġdiaz": 37999, + "vertebral": 38000, + "adzu": 38001, + "alski": 38002, + "answer": 38003, + "Ġgeochemistry": 38004, + "Ġstemming": 38005, + "wes": 38006, + "oxys": 38007, + "Ġmats": 38008, + "eva": 38009, + "ĠHyperbolic": 38010, + "arbage": 38011, + "Ġclipping": 38012, + "ĠSugar": 38013, + "ĠCognition": 38014, + "ĠDIV": 38015, + "Ġtempt": 38016, + "ĠPathogen": 38017, + "ĠPedro": 38018, + "Ġwak": 38019, + "entries": 38020, + "ĠGCM": 38021, + "projective": 38022, + "Ġproficiency": 38023, + "ĠKnown": 38024, + "Ġlexicon": 38025, + "ĠMendelian": 38026, + "Ġzoonotic": 38027, + "leans": 38028, + "ĠTalk": 38029, + "Ġkurtosis": 38030, + "NAS": 38031, + "ĠNowadays": 38032, + "ĠLil": 38033, + "ĠWMAP": 38034, + "Ġdisperse": 38035, + "Ġcolloids": 38036, + "ebra": 38037, + "OMET": 38038, + "ĠDCT": 38039, + "ĠRise": 38040, + "Ġintergenic": 38041, + "GTH": 38042, + "Ġtapered": 38043, + "Markovian": 38044, + "Protocol": 38045, + "ĠVegetation": 38046, + "rats": 38047, + "Ġdivalent": 38048, + "ĠCrust": 38049, + "zyg": 38050, + "Ġpigmentation": 38051, + "graduate": 38052, + "ĠRicc": 38053, + "Ġcounterexample": 38054, + "Ġsativ": 38055, + "Ġls": 38056, + "ĠCirculation": 38057, + "isotropic": 38058, + "ĠENSO": 38059, + "Ġtroponin": 38060, + "Ġdissolving": 38061, + "Ġcosmetic": 38062, + "Hf": 38063, + "further": 38064, + "Ġpanc": 38065, + "Ġhops": 38066, + "intra": 38067, + "ĠZhe": 38068, + "ĠReliable": 38069, + "ivolumab": 38070, + "MX": 38071, + "Rab": 38072, + "ĠPES": 38073, + "ĠBü": 38074, + "Ġadhered": 38075, + "Ġfluency": 38076, + "ĠClaus": 38077, + "Ġdelamination": 38078, + "Ġguanine": 38079, + "ĠMultiscale": 38080, + "ĠEquip": 38081, + "ĠIllustr": 38082, + "Ġtetrahydro": 38083, + "fel": 38084, + "lists": 38085, + "Îŀ": 38086, + "emulsion": 38087, + "ĠNZ": 38088, + "Ġwasn": 38089, + "aira": 38090, + "Ġarguing": 38091, + "miRNA": 38092, + "ĠExpressed": 38093, + "Ġspectrophotometric": 38094, + "Ġileum": 38095, + "Ġflames": 38096, + "Fit": 38097, + "Gon": 38098, + "ĠCulex": 38099, + "Ġunweighted": 38100, + "Ġnanob": 38101, + "SHV": 38102, + "Ġaligning": 38103, + "Ġshuttle": 38104, + "Ġchloroquine": 38105, + "Ġpyrite": 38106, + "ĠRica": 38107, + "Ġrift": 38108, + "Ġcathepsin": 38109, + "ĠPROCESS": 38110, + "Pf": 38111, + "Raw": 38112, + "rayfish": 38113, + "SAL": 38114, + "collapse": 38115, + "................": 38116, + "atases": 38117, + "Ġworkshops": 38118, + "ophile": 38119, + "ĠâĬĥ": 38120, + "Ġbifurcations": 38121, + "Trace": 38122, + "Ġpause": 38123, + "Ġorbiting": 38124, + "oliubov": 38125, + "ĠCurtis": 38126, + "ĠRevisiting": 38127, + "oret": 38128, + "Ġinfused": 38129, + "luents": 38130, + "Ġplastid": 38131, + "Ġïģ¹": 38132, + "Ġexecutions": 38133, + "ĠGraves": 38134, + "locally": 38135, + "ĠAtmosphere": 38136, + "diabetes": 38137, + "ĠPradesh": 38138, + "ĠCofactor": 38139, + "isomorphic": 38140, + "Ġbod": 38141, + "ĠCBD": 38142, + "Ġincap": 38143, + "Ġretrovirus": 38144, + "Ġlipophilic": 38145, + "Ġlinoleic": 38146, + "Ġtravelled": 38147, + "covalent": 38148, + "pick": 38149, + "upl": 38150, + "ĠPole": 38151, + "ĠThym": 38152, + "ĠTeich": 38153, + "Ġcollaborators": 38154, + "Ġinstantons": 38155, + "ĠMEGA": 38156, + "ĠHepatocellular": 38157, + "Ġinfestation": 38158, + "ĠPiezo": 38159, + "ĠLub": 38160, + "ĠNCs": 38161, + "Ġnucleoside": 38162, + "Ġosteogenesis": 38163, + "Eigen": 38164, + "RMSE": 38165, + "Ġlax": 38166, + "ĠKost": 38167, + "ĠVero": 38168, + "ĠChou": 38169, + "electrochemical": 38170, + "Ġcompeti": 38171, + "chia": 38172, + "Ġsubmodule": 38173, + "ĠAllow": 38174, + "Ġresolvent": 38175, + "Ġsweeps": 38176, + "Ġsuperconformal": 38177, + "pyrrolidine": 38178, + "lofen": 38179, + "åŃ": 38180, + "Ġdeserves": 38181, + "ĠZimbabwe": 38182, + "azines": 38183, + "ĠConsult": 38184, + "Ġcastle": 38185, + "Ġpharmaceuticals": 38186, + "Ġparacrine": 38187, + "Ġjejuni": 38188, + "Ġarguably": 38189, + "ĠeNOS": 38190, + "Ġherds": 38191, + "Ġvehicular": 38192, + "Ġtriangulated": 38193, + "Ġîµ": 38194, + "ĠGrande": 38195, + "Ġanthocyanins": 38196, + "ĠDuan": 38197, + "ĠVibration": 38198, + "Ġtriad": 38199, + "Ġhousekeeping": 38200, + "Bor": 38201, + "Ġpub": 38202, + "Ġmalformation": 38203, + "glucosamine": 38204, + "inhibitory": 38205, + "Dirac": 38206, + "ĠCSD": 38207, + "ĠRotating": 38208, + "ĠHTLV": 38209, + "Ġdemol": 38210, + "infiltr": 38211, + "Ġhemolytic": 38212, + "Ġcarbapenem": 38213, + "Ġluminescent": 38214, + "ĠPlanets": 38215, + "Ġmellifera": 38216, + "Ġcorticosterone": 38217, + "ĠAddress": 38218, + "Ġhubs": 38219, + "omethacin": 38220, + "åIJ": 38221, + "ĠChampions": 38222, + "ĠRevision": 38223, + "ĠHerbert": 38224, + "Ġambiguities": 38225, + "KERN": 38226, + "Ġdé": 38227, + "Ġlp": 38228, + "Ġenvis": 38229, + "ĠChol": 38230, + "ropin": 38231, + "Ġdrone": 38232, + "meyer": 38233, + "Ġisotype": 38234, + "ĠVu": 38235, + "ERC": 38236, + "Ġversatility": 38237, + "Speed": 38238, + "Ġaetiology": 38239, + "Ġgonadotropin": 38240, + "Ġcognate": 38241, + "ĠCotton": 38242, + "reasonable": 38243, + "disable": 38244, + "Ġdevastating": 38245, + "Pier": 38246, + "POL": 38247, + "ĠBé": 38248, + "incter": 38249, + "aluable": 38250, + "Ġpolyhedron": 38251, + "ĠRelay": 38252, + "Ġworkflows": 38253, + "FEM": 38254, + "inp": 38255, + "Ġmph": 38256, + "softmax": 38257, + "mur": 38258, + "vr": 38259, + "Ġerent": 38260, + "ĠKN": 38261, + "Ġstatin": 38262, + "Ġflatness": 38263, + "ĠArchitectures": 38264, + "ĠVeterinary": 38265, + "Ġnosocomial": 38266, + "Sk": 38267, + "XML": 38268, + "ĠFos": 38269, + "ĠLor": 38270, + "Ġradiography": 38271, + "ĠBlum": 38272, + "ĠDiscrimination": 38273, + "Ġpunc": 38274, + "Ġexits": 38275, + "ĠBilateral": 38276, + "msstrahlung": 38277, + "Ġcolonized": 38278, + "ĠFibrosis": 38279, + "Ġchaperones": 38280, + "aboratory": 38281, + "ĠPersistence": 38282, + "Ġlumped": 38283, + "Ġrabies": 38284, + "ĠBurns": 38285, + "Dense": 38286, + "ontium": 38287, + "acetylation": 38288, + "ĠFET": 38289, + "Ġhandful": 38290, + "biology": 38291, + "Ġundesired": 38292, + "Limit": 38293, + "ĠNBA": 38294, + "ĠSeoul": 38295, + "APT": 38296, + "ĠTransgenic": 38297, + "oxygenation": 38298, + "Button": 38299, + "ĠTreatments": 38300, + "ZV": 38301, + "isomorphism": 38302, + "octa": 38303, + "iffe": 38304, + "odeoxy": 38305, + "Ġorganelle": 38306, + "Ġcolloid": 38307, + "Ġceramide": 38308, + "Ġtqdm": 38309, + "GPS": 38310, + "ĠISR": 38311, + "oclinic": 38312, + "ĠLyme": 38313, + "Ġepig": 38314, + "ĠTrail": 38315, + "IPS": 38316, + "Ġsorts": 38317, + "ĠZebrafish": 38318, + "Ġhydroxylase": 38319, + "Smirnov": 38320, + "Bax": 38321, + "ĠDance": 38322, + "ĠHors": 38323, + "Ġreachability": 38324, + "Parallel": 38325, + "ĠESBL": 38326, + "Ġuplink": 38327, + "Ġpostprandial": 38328, + "solar": 38329, + "itabine": 38330, + "ordism": 38331, + "Neasy": 38332, + "Ġabandon": 38333, + "IMI": 38334, + "fake": 38335, + "statistical": 38336, + "ĠCars": 38337, + "ibia": 38338, + "ĠÃĩ": 38339, + "spc": 38340, + "MDP": 38341, + "tizations": 38342, + "International": 38343, + "ularis": 38344, + "Ġvacuoles": 38345, + "KC": 38346, + "ĠAPT": 38347, + "ĠBt": 38348, + "ĠBom": 38349, + "ĠGMP": 38350, + "Ġpioneer": 38351, + "ĠChairman": 38352, + "ĠTucker": 38353, + "ĠRAF": 38354, + "ĠNASH": 38355, + "ĠWIT": 38356, + "ynyl": 38357, + "Ġsupplier": 38358, + "ansky": 38359, + "Ġdecomposing": 38360, + "ĠUVB": 38361, + "ophenol": 38362, + "Ġbarium": 38363, + "ĠSMT": 38364, + "otocin": 38365, + "lytic": 38366, + "ranking": 38367, + "ĠDirections": 38368, + "Ġinnervation": 38369, + "switching": 38370, + "dac": 38371, + "ĠhT": 38372, + "Ġdoctr": 38373, + "ĠIncremental": 38374, + "ĠEarthquake": 38375, + "Has": 38376, + "Lee": 38377, + "mates": 38378, + "proline": 38379, + "ĠREE": 38380, + "Ġviolates": 38381, + "ðx": 38382, + "Ġhomogenates": 38383, + "Boolean": 38384, + "Ġdoxycycline": 38385, + "ĠMOF": 38386, + "iophen": 38387, + "Ġappreciation": 38388, + "finals": 38389, + "characteristic": 38390, + "ĠContinental": 38391, + "Bus": 38392, + "Esc": 38393, + "XP": 38394, + "ÛĮ": 38395, + "ĠCTA": 38396, + "Maxwell": 38397, + "Ġarchaea": 38398, + "Nik": 38399, + "NONE": 38400, + "TW": 38401, + "tering": 38402, + "ĠPerman": 38403, + "Ġrestores": 38404, + "opathogenic": 38405, + "ĠMontgomery": 38406, + "Ġglucocorticoids": 38407, + "Ġud": 38408, + "ĠNuss": 38409, + "ĠNé": 38410, + "ĠSturm": 38411, + "Ġattaching": 38412, + "Ġintraperitoneally": 38413, + "lasov": 38414, + "Ġstellate": 38415, + "Ġantiproliferative": 38416, + "Ġmicroorganism": 38417, + "Ġvisu": 38418, + "Ġjudges": 38419, + "randomized": 38420, + "allowed": 38421, + "Ġdeprived": 38422, + "development": 38423, + "scribed": 38424, + "etherian": 38425, + "ĠFraser": 38426, + "Ram": 38427, + "bib": 38428, + "Ġliner": 38429, + "Ġguns": 38430, + "resnet": 38431, + "ĠLTR": 38432, + "ighting": 38433, + "Initi": 38434, + "ĠZimm": 38435, + "ĠGeology": 38436, + "Ġantioxidative": 38437, + "Ġmagenta": 38438, + "ĠNigerian": 38439, + "galaxy": 38440, + "ĠMelanoma": 38441, + "Found": 38442, + "Ġbum": 38443, + "ĠTrop": 38444, + "ĠDos": 38445, + "Ġmetab": 38446, + "Ġinvoking": 38447, + "ĠSchizophrenia": 38448, + "CFG": 38449, + "Ġgelation": 38450, + "Ġopioids": 38451, + "pis": 38452, + "Ġchurches": 38453, + "Ġcanonically": 38454, + "Ġjug": 38455, + "Ġacceptors": 38456, + "DMEM": 38457, + "Ġobliqu": 38458, + "ĠMedicare": 38459, + "arpoon": 38460, + "ZIP": 38461, + "oreactive": 38462, + "Ġimprinting": 38463, + "ĠVinc": 38464, + "Ġ¿": 38465, + "Ġrestart": 38466, + "Ġdentate": 38467, + "enzymatic": 38468, + "Ġinguinal": 38469, + "ĠNt": 38470, + "Ġunobserved": 38471, + "uctuation": 38472, + "Ġbiasing": 38473, + "Ġintegrins": 38474, + "Ġurl": 38475, + "FPGAM": 38476, + "ĠCLUST": 38477, + "omatology": 38478, + "Ġmetallicities": 38479, + "Ġintentionally": 38480, + "FPGAMGR": 38481, + "Typ": 38482, + "Ġally": 38483, + "Ġcomic": 38484, + "ĠLions": 38485, + "Ġimputed": 38486, + "ĠÃŁ": 38487, + "lexia": 38488, + "ĠJanus": 38489, + "Ġbrass": 38490, + "ĠDownloaded": 38491, + "BUFF": 38492, + "identical": 38493, + "Ġpsychiatry": 38494, + "CCT": 38495, + "ifar": 38496, + "ĠMandel": 38497, + "Ġoptoelectronic": 38498, + "Ġisomerization": 38499, + "ĠFant": 38500, + "ĠLion": 38501, + "ĠLov": 38502, + "ĠNaf": 38503, + "esta": 38504, + "Ġbiocompatible": 38505, + "Ġsecretions": 38506, + "sci": 38507, + "ĠRetro": 38508, + "oisomerase": 38509, + "ĠSnap": 38510, + "Ġsplittings": 38511, + "Ġscavenger": 38512, + "procedure": 38513, + "Dawley": 38514, + "ëĭ¤": 38515, + "unate": 38516, + "ĠDye": 38517, + "ĠNEC": 38518, + "Ġnanocl": 38519, + "Ġplanetes": 38520, + "ĠTRPM": 38521, + "Ġvoices": 38522, + "ĠHierarchy": 38523, + "mv": 38524, + "Ġlasts": 38525, + "Ġhoped": 38526, + "Ġmedians": 38527, + "ĠAndreev": 38528, + "Ġheightened": 38529, + "ä»": 38530, + "Ġindefinite": 38531, + "ĠKamp": 38532, + "angel": 38533, + "grids": 38534, + "archae": 38535, + "Ġtherapists": 38536, + "ĠMiR": 38537, + "Ġnegotiation": 38538, + "HSP": 38539, + "ĠCustom": 38540, + "Ġstria": 38541, + "Ġunacceptable": 38542, + "retin": 38543, + "penet": 38544, + "ĠORR": 38545, + "ĠLifetime": 38546, + "ĠPhosphate": 38547, + "Ġtropics": 38548, + "ĠWelch": 38549, + "ĠPyr": 38550, + "Ġamputation": 38551, + "ĠArtin": 38552, + "ĠCaO": 38553, + "Ġconjectures": 38554, + "Ġatrium": 38555, + "ĠComplementary": 38556, + "ĠAluminum": 38557, + "Ġmicrow": 38558, + "iliated": 38559, + "ĠImmuno": 38560, + "Ġbinocular": 38561, + "ĠWeakly": 38562, + "Ġimmunogenic": 38563, + "Ġbathym": 38564, + "ĠPhenotype": 38565, + "Ġsialic": 38566, + "Six": 38567, + "Ġakin": 38568, + "rotor": 38569, + "helm": 38570, + "CCESS": 38571, + "Ġneuroprotection": 38572, + "ĠFifth": 38573, + "Ġcontingent": 38574, + "Ġsketched": 38575, + "Imp": 38576, + "Ġcached": 38577, + "urement": 38578, + "ĠBic": 38579, + "ĠKah": 38580, + "beration": 38581, + "atterson": 38582, + "Ġglycation": 38583, + "Ġinvestors": 38584, + "Assisted": 38585, + "iales": 38586, + "science": 38587, + "Ġpilots": 38588, + "uscripts": 38589, + "MICS": 38590, + "Ġorthopedic": 38591, + "warfs": 38592, + "greater": 38593, + "ĠArtery": 38594, + "Video": 38595, + "Ġarrange": 38596, + "avar": 38597, + "charges": 38598, + "dialdehyde": 38599, + "ĠTPA": 38600, + "Ġspelling": 38601, + "ĠSeiberg": 38602, + "Ġnavigate": 38603, + "ĠPowder": 38604, + "ĠRings": 38605, + "ĠChron": 38606, + "ĠAtg": 38607, + "Ġhomocysteine": 38608, + "ĠIdentify": 38609, + "Ġoak": 38610, + "Ġliability": 38611, + "Ġoperands": 38612, + "ĠCTD": 38613, + "Ġalleviates": 38614, + "mA": 38615, + "ĠLanger": 38616, + "Ġsubmanifolds": 38617, + "ĠJag": 38618, + "Ġradiance": 38619, + "constants": 38620, + "ĠMorocco": 38621, + "Engine": 38622, + "á¸": 38623, + "âĤ¬": 38624, + "revers": 38625, + "PCI": 38626, + "unsqueeze": 38627, + "oconversion": 38628, + "Ġintensified": 38629, + "Ġrefinements": 38630, + "ofectamine": 38631, + "ayas": 38632, + "Ġincidental": 38633, + "ĠThur": 38634, + "Ġoverd": 38635, + "Ġbitter": 38636, + "Ġignores": 38637, + "ан": 38638, + "ĠOTU": 38639, + "Ġserr": 38640, + "aby": 38641, + "ĠGCN": 38642, + "ĠConsumer": 38643, + "Ġconcordant": 38644, + "ĠMRC": 38645, + "ĠEconomy": 38646, + "satisfying": 38647, + "Ġbiotinylated": 38648, + "Numerical": 38649, + "ĠRashba": 38650, + "stochastic": 38651, + "ĠLal": 38652, + "Ġburdens": 38653, + "Alloc": 38654, + "ĠGraphics": 38655, + "ĠLRRK": 38656, + "AIC": 38657, + "ĠTed": 38658, + "ĠSark": 38659, + "owl": 38660, + "Ġhemost": 38661, + "ĠAnat": 38662, + "Ġhoming": 38663, + "ĠCharlie": 38664, + "ĠBruc": 38665, + "ihara": 38666, + "ingen": 38667, + "ĠVern": 38668, + "ĠYers": 38669, + "Ġids": 38670, + "ĠcircRNAs": 38671, + "Ġconducive": 38672, + "ĠBRST": 38673, + "Ġgallium": 38674, + "Ġdichotomy": 38675, + "Fr": 38676, + "etition": 38677, + "Ġcesarean": 38678, + "olan": 38679, + "Ġrn": 38680, + "ubstituted": 38681, + "ĠLeaves": 38682, + "ĠLeader": 38683, + "coloring": 38684, + "Draw": 38685, + "Ġserous": 38686, + "Err": 38687, + "Ġinnermost": 38688, + "ĠHamburg": 38689, + "Stor": 38690, + "jes": 38691, + "Ġtol": 38692, + "idade": 38693, + "Ġrv": 38694, + "ĠInversion": 38695, + "Ġmultiphase": 38696, + "Ġpseudor": 38697, + "ĠGoodman": 38698, + "ĠJSON": 38699, + "Ġcorridor": 38700, + "Ġpork": 38701, + "ĠSale": 38702, + "ĠNatal": 38703, + "Ġattacking": 38704, + "ĠSheet": 38705, + "Ġstreamwise": 38706, + "Ġatomistic": 38707, + "Ġfirmly": 38708, + "ĠAchie": 38709, + "Ġpir": 38710, + "ĠIKK": 38711, + "ĠFalk": 38712, + "ileptic": 38713, + "ĠTRPC": 38714, + "Ġadhesions": 38715, + "HRP": 38716, + "Ġpaucity": 38717, + "Split": 38718, + "UDI": 38719, + "ĠSend": 38720, + "ĠPine": 38721, + "ĠLon": 38722, + "ĠLost": 38723, + "efer": 38724, + "concaten": 38725, + "Ġloyal": 38726, + "Ġglycop": 38727, + "ĠObserving": 38728, + "ĠMohamed": 38729, + "YR": 38730, + "ĠFilters": 38731, + "cas": 38732, + "pages": 38733, + "ĠdA": 38734, + "Ġareal": 38735, + "adis": 38736, + "ĠLHS": 38737, + "ĠThereby": 38738, + "Ġvisualizations": 38739, + "Ġtwistor": 38740, + "unitary": 38741, + "Ġarchives": 38742, + "Ġphenolics": 38743, + "hik": 38744, + "sson": 38745, + "ĠIK": 38746, + "ĠStudying": 38747, + "Ġtwisting": 38748, + "ĠHydrodynamic": 38749, + "Ġsplitter": 38750, + "Ġurothelial": 38751, + "Ġalken": 38752, + "ĠGPI": 38753, + "Ġcortices": 38754, + "Ġcropping": 38755, + "Patient": 38756, + "ĠChlamyd": 38757, + "inberg": 38758, + "ĠAircraft": 38759, + "cele": 38760, + "ectral": 38761, + "Ġconferences": 38762, + "Ġcreatine": 38763, + "alty": 38764, + "proportional": 38765, + "Ġleptonic": 38766, + "Ġovulation": 38767, + "uerre": 38768, + "tezomib": 38769, + "dle": 38770, + "initeness": 38771, + "ĠSpecimens": 38772, + "Ġcoma": 38773, + "inephrine": 38774, + "Ġepim": 38775, + "ĠPercent": 38776, + "CoO": 38777, + "ĠLoading": 38778, + "Ġvenue": 38779, + "ĠTNM": 38780, + "Ġpacemaker": 38781, + "ĠHoffmann": 38782, + "Tech": 38783, + "nie": 38784, + "ĠOrleans": 38785, + "Ġmagnetron": 38786, + "Ġhospitality": 38787, + "ĠNordic": 38788, + "oproliferative": 38789, + "Ġundoubtedly": 38790, + "ĠSrin": 38791, + "Ġhumic": 38792, + "ĠIntegrative": 38793, + "ĠCampus": 38794, + "Ġplantarum": 38795, + "radiative": 38796, + "Ġiterator": 38797, + "ĠMesozoic": 38798, + "APs": 38799, + "carinic": 38800, + "Ġcheckpoints": 38801, + "ĠïĤ£": 38802, + "ĠmAbs": 38803, + "ĠLiverpool": 38804, + "ìĿ´": 38805, + "ĠEcosystem": 38806, + "Ġneovascularization": 38807, + "Ġdemoc": 38808, + "loops": 38809, + "ĠSURF": 38810, + "Ġpassivation": 38811, + "Ġconsecutively": 38812, + "ĠAlfvén": 38813, + "ĠSSE": 38814, + "Ġouts": 38815, + "stimulation": 38816, + "Ġphilosophical": 38817, + "ĠSask": 38818, + "Ġflakes": 38819, + "Ġfingerprinting": 38820, + "Ġbuffalo": 38821, + "ĠWikimedia": 38822, + "Ġreconstitution": 38823, + "Ġepithelia": 38824, + "onk": 38825, + "eny": 38826, + "ĠMQ": 38827, + "ĠFork": 38828, + "endance": 38829, + "Ġgeneralisation": 38830, + "Ġpeoples": 38831, + "Ġconnector": 38832, + "gesia": 38833, + "interference": 38834, + "Ġcoloration": 38835, + "calculation": 38836, + "ĠAxial": 38837, + "ĠDESIGN": 38838, + "Ġrecession": 38839, + "Ġdissolve": 38840, + "ĠPartitioning": 38841, + "QxMD": 38842, + "GES": 38843, + "Vo": 38844, + "khar": 38845, + "ĠEAE": 38846, + "Ġcoarser": 38847, + "Ġposttraumatic": 38848, + "Ġsynthesised": 38849, + "silica": 38850, + "tetrahydropy": 38851, + "ĠPorter": 38852, + "vark": 38853, + "entanyl": 38854, + "Ġconve": 38855, + "Ġrafts": 38856, + "brecht": 38857, + "Ġrectifier": 38858, + "Ġoroph": 38859, + "ĠCEP": 38860, + "Ġhistones": 38861, + "Ġstandpoint": 38862, + "Ġancillary": 38863, + "ĠHurricane": 38864, + "cro": 38865, + "Ġreb": 38866, + "ĠiT": 38867, + "Ġgeography": 38868, + "olarization": 38869, + "ĠManaging": 38870, + "Ġxylose": 38871, + "utherland": 38872, + "ĠTaqMan": 38873, + "KN": 38874, + "Ġtm": 38875, + "ĠTAS": 38876, + "istle": 38877, + "âĢ«": 38878, + "Ġmycorrhizal": 38879, + "ĠTerrestrial": 38880, + "hausen": 38881, + "observable": 38882, + "Brien": 38883, + "Ġneutropenia": 38884, + "Taken": 38885, + "ĠSMI": 38886, + "Ġpolishing": 38887, + "Ġphotop": 38888, + "Ġthermalization": 38889, + "Ġpseudoscalar": 38890, + "ĠDominic": 38891, + "romyalgia": 38892, + "Ġechocardiographic": 38893, + "Illumina": 38894, + "ĠIPC": 38895, + "ĠHuss": 38896, + "essive": 38897, + "uptake": 38898, + "Ġweekend": 38899, + "Ġcorroborate": 38900, + "ĠTasman": 38901, + "herty": 38902, + "Ġperine": 38903, + "Ġtransports": 38904, + "Ġglance": 38905, + "retinal": 38906, + "Proto": 38907, + "igenes": 38908, + "Ġprohibited": 38909, + "behavioral": 38910, + "opherol": 38911, + "ë¡": 38912, + "ĠNecess": 38913, + "obiology": 38914, + "okk": 38915, + "Ġtraversal": 38916, + "ĠAndes": 38917, + "Resource": 38918, + "olitic": 38919, + "ça": 38920, + "irie": 38921, + "arctan": 38922, + "Ġmorphogenetic": 38923, + "ĠHui": 38924, + "losses": 38925, + "Ġfulfilling": 38926, + "Ġhurricane": 38927, + "ombo": 38928, + "Ġgs": 38929, + "ĠLv": 38930, + "ĠNerv": 38931, + "ellosis": 38932, + "Ġconfront": 38933, + "Ġorthologous": 38934, + "Ġwettability": 38935, + "Ġcyanobacterial": 38936, + "Ġcassava": 38937, + "AUT": 38938, + "avi": 38939, + "hlen": 38940, + "ĠSLA": 38941, + "Ġconvol": 38942, + "Ġintermetallic": 38943, + "inside": 38944, + "Ġpolarizability": 38945, + "Ġensuing": 38946, + "Ġchloroplasts": 38947, + "lid": 38948, + "lips": 38949, + "Ġrebound": 38950, + "ĠCary": 38951, + "ĠLambda": 38952, + "ĠViv": 38953, + "Ġcalcination": 38954, + "ĠÌĨ": 38955, + "Ġcounterfactual": 38956, + "ĠSilica": 38957, + "Referee": 38958, + "Ġhomologues": 38959, + "ĠSpatiotemporal": 38960, + "ĠArrhenius": 38961, + "Ġinflamed": 38962, + "ĠZambia": 38963, + "Ġantipsychotic": 38964, + "helper": 38965, + "Blood": 38966, + "Ġpurchasing": 38967, + "ĠSchwinger": 38968, + "ĠWilkinson": 38969, + "Ġfainter": 38970, + "Ġrash": 38971, + "ĠJang": 38972, + "ĠConductivity": 38973, + "ropoda": 38974, + "ĠSeq": 38975, + "Ġpropolis": 38976, + "Ġtubule": 38977, + "ĠLieb": 38978, + "optimization": 38979, + "mounted": 38980, + "emes": 38981, + "canic": 38982, + "oradiotherapy": 38983, + "ĠJenkins": 38984, + "Nc": 38985, + "Together": 38986, + "Ġfove": 38987, + "Ġmv": 38988, + "ĠDefect": 38989, + "ät": 38990, + "ĠFinance": 38991, + "umarin": 38992, + "mittance": 38993, + "erel": 38994, + "ĠFren": 38995, + "ĠRhyth": 38996, + "ramified": 38997, + "Ġhypercholesterolem": 38998, + "Ġstimulatory": 38999, + "ĠRichmond": 39000, + "Ġadvancements": 39001, + "bles": 39002, + "xu": 39003, + "allation": 39004, + "Ġintral": 39005, + "iterpene": 39006, + "Concerning": 39007, + "Ġbulky": 39008, + "Ġá¾±": 39009, + "computation": 39010, + "ĠAgarwal": 39011, + "Central": 39012, + "XPS": 39013, + "Ġtalks": 39014, + "ĠTap": 39015, + "imilar": 39016, + "ĠNCI": 39017, + "Ġaccused": 39018, + "Ġtranscriptomes": 39019, + "Ġprovisioning": 39020, + "ĠEtOH": 39021, + "gm": 39022, + "Ġtid": 39023, + "ĠPOC": 39024, + "ffman": 39025, + "ĠIner": 39026, + "ĠUB": 39027, + "incubated": 39028, + "ĠAtrial": 39029, + "Ġfourteen": 39030, + "ĠAstronomical": 39031, + "ĠMiguel": 39032, + "ĠKov": 39033, + "Ġscipy": 39034, + "Ġthermoplastic": 39035, + "ĠManuel": 39036, + "ĠPromotion": 39037, + "ĠAccessed": 39038, + "Ġterritorial": 39039, + "inas": 39040, + "ĠMPs": 39041, + "monitoring": 39042, + "ĠSimulating": 39043, + "Ġpanor": 39044, + "Ġrheumatic": 39045, + "selectin": 39046, + "ĠLaparoscopic": 39047, + "HLA": 39048, + "ĠYale": 39049, + "spread": 39050, + "ETS": 39051, + "Ġglycans": 39052, + "Ġimmigrant": 39053, + "Donald": 39054, + "ĠCASE": 39055, + "ĠHII": 39056, + "glomer": 39057, + "Ġïĥİ": 39058, + "ĠExperiences": 39059, + "ĠVietnamese": 39060, + "Hodgkin": 39061, + "oader": 39062, + "heart": 39063, + "Ġremedy": 39064, + "Ġfacilitators": 39065, + "openhagen": 39066, + "dodec": 39067, + "ĠFriend": 39068, + "ĠTouch": 39069, + "arms": 39070, + "CRs": 39071, + "Ġultrahigh": 39072, + "ĠDriver": 39073, + "GEMENTS": 39074, + "ĠOu": 39075, + "Ġendocarditis": 39076, + "Ġautoencoder": 39077, + "Ġich": 39078, + "Ġfetch": 39079, + "urian": 39080, + "ĠORFs": 39081, + "Ġpermeabilized": 39082, + "ĠWiFi": 39083, + "ĠLithuan": 39084, + "Structure": 39085, + "Ln": 39086, + "houses": 39087, + "Ġought": 39088, + "ĠConcluding": 39089, + "Ġanniversary": 39090, + "ĠCreation": 39091, + "Ġblindness": 39092, + "ĠpcDNA": 39093, + "ĠSusan": 39094, + "ĠBenjamini": 39095, + "ĠSentence": 39096, + "Ġsnd": 39097, + "Ġfins": 39098, + "phis": 39099, + "ĠModules": 39100, + "Ġneuropsychiatric": 39101, + "ĠPotassium": 39102, + "Ġsacrifice": 39103, + "Ġdyspnea": 39104, + "Ġdeliberately": 39105, + "omegaly": 39106, + "Media": 39107, + "Temporal": 39108, + "Ġshark": 39109, + "SCAN": 39110, + "splitting": 39111, + "Ġmisuse": 39112, + "Ġbirefringence": 39113, + "ĠÖĴâĨĴ": 39114, + "Ġpier": 39115, + "Ġnurs": 39116, + "ĠSclerosis": 39117, + "adhy": 39118, + "Ġundetermined": 39119, + "Ġcomplementation": 39120, + "ĠAffect": 39121, + "ĠHamps": 39122, + "Ġgob": 39123, + "ĠFate": 39124, + "ĠHAL": 39125, + "ĠKiss": 39126, + "Ġmicrobe": 39127, + "Ġcarbonaceous": 39128, + "Ġliposome": 39129, + "ĠUsage": 39130, + "Ġquasiparticles": 39131, + "Ġcasp": 39132, + "ĠNarrow": 39133, + "Ġoutlook": 39134, + "ĠChord": 39135, + "Ġclaiming": 39136, + "Ġdiverging": 39137, + "ĠBioinformatics": 39138, + "ĠPsychiatric": 39139, + "ĠMasters": 39140, + "Ġllvm": 39141, + "ĠIQR": 39142, + "phases": 39143, + "ĠThy": 39144, + "erger": 39145, + "ĠDipl": 39146, + "SFR": 39147, + "Ġcredited": 39148, + "ĠTetra": 39149, + "âĭ¯": 39150, + "Ġamniotic": 39151, + "ĠCharlotte": 39152, + "Cox": 39153, + "Hard": 39154, + "article": 39155, + "ĠDEA": 39156, + "ĠEclipse": 39157, + "ĠLMP": 39158, + "Ġimprison": 39159, + "ĠVarying": 39160, + "ESCs": 39161, + "ĠTHEO": 39162, + "Ġnervosa": 39163, + "Ġprecedes": 39164, + "Ġgyro": 39165, + "ĠWORDS": 39166, + "ĠDakota": 39167, + "utory": 39168, + "ĠEmer": 39169, + "adam": 39170, + "ĠNah": 39171, + "ĠVirgo": 39172, + "Setting": 39173, + "PQ": 39174, + "å®": 39175, + "erus": 39176, + "Ġcep": 39177, + "Ġbd": 39178, + "dier": 39179, + "Ġimbalanced": 39180, + "Ġtimestep": 39181, + "än": 39182, + "ĠRabbit": 39183, + "Ġhamsters": 39184, + "Ġmedulla": 39185, + "ĠChromatography": 39186, + "INPUT": 39187, + "Ġlossy": 39188, + "Pseud": 39189, + "ĠPBL": 39190, + "ĠDomestic": 39191, + "iau": 39192, + "ancell": 39193, + "Ġmultilayers": 39194, + "Ġsubsidi": 39195, + "ĠUtilizing": 39196, + "tune": 39197, + "rehend": 39198, + "arte": 39199, + "Ġburs": 39200, + "ĠNHE": 39201, + "Ġcloseness": 39202, + "ĠColour": 39203, + "ĠHomo": 39204, + "Equations": 39205, + "Ġsutures": 39206, + "acus": 39207, + "Ġknocked": 39208, + "Ġsecretary": 39209, + "Ġascertained": 39210, + "Ġinpatients": 39211, + "irts": 39212, + "Ġplut": 39213, + "ansson": 39214, + "rami": 39215, + "Ġosteotomy": 39216, + "ĠPrimers": 39217, + "ĠLegislative": 39218, + "ĠCardiology": 39219, + "Ġadmitting": 39220, + "Ġexcavation": 39221, + "ĠHedgehog": 39222, + "WG": 39223, + "frozen": 39224, + "Ġliber": 39225, + "ĠICE": 39226, + "chosen": 39227, + "ĠKohn": 39228, + "Stop": 39229, + "Phil": 39230, + "phagia": 39231, + "ĠBCA": 39232, + "Ġempt": 39233, + "Ġzz": 39234, + "opers": 39235, + "ĠSixty": 39236, + "eckman": 39237, + "Ġtransferrin": 39238, + "Ġpenalized": 39239, + "Being": 39240, + "Ġextruded": 39241, + "Ġminiature": 39242, + "Ġeditorial": 39243, + "Ġinterconnect": 39244, + "gro": 39245, + "kv": 39246, + "olen": 39247, + "ĠSYSTEMS": 39248, + "ĠColonel": 39249, + "ĠMediated": 39250, + "ĠEMD": 39251, + "Ġknife": 39252, + "Ġcytogenetic": 39253, + "Ġdigitized": 39254, + "abinoids": 39255, + "arterial": 39256, + "Ġdiarrhoea": 39257, + "bag": 39258, + "Ġbuccal": 39259, + "stay": 39260, + "ĠLAMP": 39261, + "oko": 39262, + "ĠPolyt": 39263, + "masked": 39264, + "ĠTunable": 39265, + "Ġcoagul": 39266, + "paras": 39267, + "Ġterminating": 39268, + "ICAg": 39269, + "ĠExcellence": 39270, + "Ġregurgitation": 39271, + "DQUFD": 39272, + "Jack": 39273, + "Ġapertures": 39274, + "ĠIp": 39275, + "ĠHCMV": 39276, + "ĠGom": 39277, + "Ġnucleophilic": 39278, + "Ġparenteral": 39279, + "TIM": 39280, + "oine": 39281, + "ĠnT": 39282, + "ĠSense": 39283, + "ĠFocal": 39284, + "ranges": 39285, + "Ġhept": 39286, + "ĠPlat": 39287, + "Ġmyx": 39288, + "Ġcodebook": 39289, + "Expl": 39290, + "ĠRhoA": 39291, + "Ġrhinitis": 39292, + "ĠErratum": 39293, + "Oriented": 39294, + "Well": 39295, + "doping": 39296, + "Ġbup": 39297, + "ĠImpedance": 39298, + "Ġsubstitutes": 39299, + "actorily": 39300, + "Ġcollaborations": 39301, + "ĠWayne": 39302, + "Ġvowels": 39303, + "ĠShadow": 39304, + "Ġphenology": 39305, + "Ġconcurrency": 39306, + "having": 39307, + "ĠCES": 39308, + "ĠFIN": 39309, + "ĠLoh": 39310, + "oxa": 39311, + "ĠAlN": 39312, + "ĠAlvarez": 39313, + "instit": 39314, + "Ġgermplasm": 39315, + "ĠBoliv": 39316, + "ĠRCP": 39317, + "assador": 39318, + "Ġesp": 39319, + "Ġphenotyping": 39320, + "Ġskipping": 39321, + "ĠFractal": 39322, + "ĠPEDOT": 39323, + "wake": 39324, + "ĠFIT": 39325, + "ĠESD": 39326, + "ĠAntif": 39327, + "ubiquitin": 39328, + "ĠAerial": 39329, + "ĠPrognosis": 39330, + "ĠRELATED": 39331, + "Ġstratigraphy": 39332, + "vatron": 39333, + "ĠPROPERTIES": 39334, + "Ġicon": 39335, + "isers": 39336, + "Ġwal": 39337, + "Ġstamp": 39338, + "ĠOptimum": 39339, + "Ġoligomeric": 39340, + "Ġinnerv": 39341, + "YA": 39342, + "Abcam": 39343, + "Ġvials": 39344, + "ĠGrig": 39345, + "Ġunaware": 39346, + "Ġopera": 39347, + "ĠWarner": 39348, + "Ġprotonated": 39349, + "ĠDRG": 39350, + "Ġtroubles": 39351, + "Ġpropositional": 39352, + "ĠAfghanistan": 39353, + "ĠHampshire": 39354, + "Gd": 39355, + "lung": 39356, + "Ġaviation": 39357, + "Ġapartment": 39358, + "Ġinfusions": 39359, + "Ġbroilers": 39360, + "ĠDisability": 39361, + "ĠRobots": 39362, + "Ġdebugging": 39363, + "ĠìĿ": 39364, + "Wilson": 39365, + "uprofen": 39366, + "obarbital": 39367, + "JB": 39368, + "isance": 39369, + "itizer": 39370, + "MIS": 39371, + "ĠARF": 39372, + "Ġprostheses": 39373, + "Ġdichloromethane": 39374, + "mCherry": 39375, + "ĠSSS": 39376, + "ĠLPA": 39377, + "SCF": 39378, + "attract": 39379, + "Ġcalibrations": 39380, + "Ġfibril": 39381, + "Ġhaploid": 39382, + "usalem": 39383, + "ĠNut": 39384, + "Ġdeut": 39385, + "chronic": 39386, + "NAP": 39387, + "ĠCytokines": 39388, + "rageen": 39389, + "ĠCategories": 39390, + "rains": 39391, + "Ġsummands": 39392, + "Ġproliferate": 39393, + "rylov": 39394, + "Ġpleasure": 39395, + "Ġdensit": 39396, + "ĠSURVE": 39397, + "HIP": 39398, + "hall": 39399, + "ĠFUS": 39400, + "Ġwasting": 39401, + "ERY": 39402, + "Ġstatins": 39403, + "Ġeastward": 39404, + "sometimes": 39405, + "Ġwrapping": 39406, + "ĠTWO": 39407, + "vine": 39408, + "Ġsacchar": 39409, + "Ġamateur": 39410, + "ĠÃĽ": 39411, + "Ġmyster": 39412, + "ĠMyo": 39413, + "Ġrhabd": 39414, + "ĠProtease": 39415, + "Ġcholera": 39416, + "ĠGov": 39417, + "ĠGCC": 39418, + "Ġclays": 39419, + "transmission": 39420, + "ĠHollywood": 39421, + "Ġxenob": 39422, + "FLOAT": 39423, + "Ġascent": 39424, + "Ġsharks": 39425, + "Ġinterferes": 39426, + "ĠFormer": 39427, + "ĠHartmann": 39428, + "sha": 39429, + "ĠSave": 39430, + "Ġparks": 39431, + "ĠVenn": 39432, + "Ġunions": 39433, + "Ġdiscour": 39434, + "Ġsuperlattices": 39435, + "Ġcoupler": 39436, + "proteins": 39437, + "ĠStationary": 39438, + "ĠEthernet": 39439, + "ĠFréchet": 39440, + "Ġkines": 39441, + "Ġjazz": 39442, + "Asn": 39443, + "Ġextensional": 39444, + "Ġtelomeres": 39445, + "Ġpermitting": 39446, + "Ġexhausted": 39447, + "ĠSphing": 39448, + "Turn": 39449, + "mind": 39450, + "Ġsf": 39451, + "ĠHak": 39452, + "ranolol": 39453, + "portation": 39454, + "Consistent": 39455, + "Ġventilated": 39456, + "ĠDISTRIB": 39457, + "Ġtelling": 39458, + "Ġmannose": 39459, + "ÃŃaz": 39460, + "Ġborne": 39461, + "Ġintensification": 39462, + "Ġenjoyed": 39463, + "ĠBruno": 39464, + "ĠSaturday": 39465, + "Ġcocycle": 39466, + "itate": 39467, + "Ġgolf": 39468, + "approved": 39469, + "ĠNikol": 39470, + "itri": 39471, + "ĠSentiment": 39472, + "Ġglow": 39473, + "Ġgyp": 39474, + "ĠPCT": 39475, + "aber": 39476, + "ĠWis": 39477, + "porum": 39478, + "Ġhyphae": 39479, + "feas": 39480, + "ĠTraits": 39481, + "ĠConflicts": 39482, + "degrading": 39483, + "Raman": 39484, + "pharmac": 39485, + "Ġimmunocyt": 39486, + "ĠBlake": 39487, + "Ġpseudoc": 39488, + "ĠCharacterisation": 39489, + "ĠGalileo": 39490, + "Enabl": 39491, + "Jy": 39492, + "Ġclav": 39493, + "Ġϳ": 39494, + "Ġcommunicated": 39495, + "eutical": 39496, + "Ġnanotechnology": 39497, + "ĠHassan": 39498, + "ĠTec": 39499, + "Ġhanging": 39500, + "ĠBSD": 39501, + "ĠContour": 39502, + "Ġfragility": 39503, + "Ġdisruptions": 39504, + "Ġfiniteness": 39505, + "ĠPhilippine": 39506, + "nicity": 39507, + "Ùĩ": 39508, + "ĠCrim": 39509, + "ĠCNF": 39510, + "ĠISI": 39511, + "adapter": 39512, + "ĠUCP": 39513, + "Ġtextured": 39514, + "AAV": 39515, + "keto": 39516, + "Np": 39517, + "counting": 39518, + "hynchus": 39519, + "Ġprosec": 39520, + "ĠAnnot": 39521, + "ĠHarbor": 39522, + "degrees": 39523, + "akar": 39524, + "ĠVik": 39525, + "bfd": 39526, + "Ġdrip": 39527, + "ĠCaucas": 39528, + "Ġtrench": 39529, + "Ġweed": 39530, + "Ġdistractor": 39531, + "genetic": 39532, + "specifically": 39533, + "ulfite": 39534, + "ĠConsistently": 39535, + "Ġbreakfast": 39536, + "Ġbullet": 39537, + "Ġlegisl": 39538, + "ĠTraumatic": 39539, + "Ġcollectors": 39540, + "ĠBullet": 39541, + "ĠMYB": 39542, + "ĠPink": 39543, + "versive": 39544, + "ĠAttem": 39545, + "Ġculturally": 39546, + "Bell": 39547, + "undef": 39548, + "vii": 39549, + "Ġhistocompatibility": 39550, + "letcher": 39551, + "ĠStef": 39552, + "Amp": 39553, + "ĠRid": 39554, + "ĠEucl": 39555, + "Ġdecryption": 39556, + "ĠSpencer": 39557, + "ĠBitcoin": 39558, + "wic": 39559, + "Ġcomplicate": 39560, + "ĠProposal": 39561, + "ĠÄĪ": 39562, + "aviruses": 39563, + "ĠFay": 39564, + "ĠRd": 39565, + "ĠGale": 39566, + "ĠMetastasis": 39567, + "ĠImprovements": 39568, + "©": 39569, + "Ġpolyester": 39570, + "Ġstratospheric": 39571, + "ĠSAH": 39572, + "Ġamphip": 39573, + "ĠAFP": 39574, + "ĠHair": 39575, + "ĠEPI": 39576, + "ĠUltrast": 39577, + "Ġâĭ¯": 39578, + "Ġgapless": 39579, + "Ham": 39580, + "etto": 39581, + "Ġthreonine": 39582, + "ĠECO": 39583, + "Ġia": 39584, + "Ġundist": 39585, + "Ġradiology": 39586, + "Ġsuperlattice": 39587, + "ibraries": 39588, + "Ġturbid": 39589, + "ĠPotentials": 39590, + "ĠPipeline": 39591, + "Ġwarfarin": 39592, + "WISE": 39593, + "ĠLid": 39594, + "Ġrecurring": 39595, + "ĠMono": 39596, + "ĠGovern": 39597, + "ĠAwareness": 39598, + "olab": 39599, + "iflora": 39600, + "stris": 39601, + "INDEX": 39602, + "ĠDementia": 39603, + "Does": 39604, + "wright": 39605, + "Íī": 39606, + "Ġsb": 39607, + "ĠDOM": 39608, + "ĠHBsAg": 39609, + "clinic": 39610, + "ĠExped": 39611, + "Ġproteas": 39612, + "Ġsterilization": 39613, + "ĠBanerjee": 39614, + "ĠPersonnel": 39615, + "âĮĭ": 39616, + "onephritis": 39617, + "omite": 39618, + "ĠCCF": 39619, + "ositi": 39620, + "ĠEucalyptus": 39621, + "ĠIsotope": 39622, + "coli": 39623, + "possibility": 39624, + "Ġstrontium": 39625, + "Ġraref": 39626, + "ĠInterstellar": 39627, + "kinin": 39628, + "ylethanol": 39629, + "JT": 39630, + "north": 39631, + "Ġcensored": 39632, + "istive": 39633, + "Ġnoticing": 39634, + "Ġshipping": 39635, + "Embed": 39636, + "Observ": 39637, + "Ġzeolites": 39638, + "ubit": 39639, + "Ġflaps": 39640, + "Ġdrifts": 39641, + "Ġtherapist": 39642, + "Ġpollination": 39643, + "aliplatin": 39644, + "Johnson": 39645, + "Ġimperfections": 39646, + "NY": 39647, + "Ġthalamic": 39648, + "ocarb": 39649, + "ozotocin": 39650, + "Ġtetramer": 39651, + "Plas": 39652, + "Ġmultichannel": 39653, + "ĠInsight": 39654, + "opods": 39655, + "ĠNacional": 39656, + "Ġimatinib": 39657, + "actual": 39658, + "ĠXOR": 39659, + "Ġblight": 39660, + "ĠLeading": 39661, + "amese": 39662, + "ĠAmplitude": 39663, + "ĠMonitor": 39664, + "ĠNeurological": 39665, + "propagating": 39666, + "Ġpaddle": 39667, + "ĠHarvest": 39668, + "Ġodont": 39669, + "BUF": 39670, + "Ġtactics": 39671, + "ĠAnisotropy": 39672, + "adip": 39673, + "ĠAlpine": 39674, + "Ġfeels": 39675, + "Ġmedieval": 39676, + "Ġelucidation": 39677, + "Ġheterotrophic": 39678, + "Ġrelaxing": 39679, + "Ġhappiness": 39680, + "ĠCytotoxicity": 39681, + "ĠRANKL": 39682, + "Walker": 39683, + "mig": 39684, + "ĠSSL": 39685, + "ĠSepsis": 39686, + "ĠGes": 39687, + "Ġhydrochloric": 39688, + "Ġclarification": 39689, + "Ġdisparate": 39690, + "tested": 39691, + "Ġdatap": 39692, + "Ġnovels": 39693, + "ĠMicroc": 39694, + "ál": 39695, + "ĠARC": 39696, + "ĠYangtze": 39697, + "etomidine": 39698, + "ĠMatrigel": 39699, + "ihilation": 39700, + "ĠcDNAs": 39701, + "Ġprostat": 39702, + "ĠRailroad": 39703, + "UBLE": 39704, + "ĠPARTIC": 39705, + "ĠSax": 39706, + "Ġinsecurity": 39707, + "Ġcrushed": 39708, + "Ġhalves": 39709, + "giant": 39710, + "ĠCroatia": 39711, + "icyclo": 39712, + "ĠUnexpected": 39713, + "Ġloneliness": 39714, + "anu": 39715, + "Ġchampions": 39716, + "uberculosis": 39717, + "Ġequi": 39718, + "Ġaccreted": 39719, + "Ġinvading": 39720, + "Ġafferents": 39721, + "Ġalternation": 39722, + "Ġkinet": 39723, + "ĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ": 39724, + "ĠMAGNET": 39725, + "ĠFIFA": 39726, + "zadeh": 39727, + "iphenyl": 39728, + "ĠKro": 39729, + "ĠEvaluate": 39730, + "illiant": 39731, + "curvature": 39732, + "ĠPierce": 39733, + "better": 39734, + "nos": 39735, + "à¥": 39736, + "ĠKCN": 39737, + "ĠStrand": 39738, + "caemic": 39739, + "ĠHoechst": 39740, + "ĠEXT": 39741, + "ĠLLVM": 39742, + "BZ": 39743, + "tgt": 39744, + "ondialdehyde": 39745, + "ĠEvid": 39746, + "ĠGul": 39747, + "Ġmultiplications": 39748, + "Ġauth": 39749, + "ĠAustral": 39750, + "Ġstaying": 39751, + "ĠGlutamate": 39752, + "Ġstray": 39753, + "ĠISA": 39754, + "Ġlowland": 39755, + "Ġparallels": 39756, + "Ġattractiveness": 39757, + "Ġelectrospinning": 39758, + "Ġportrayed": 39759, + "ospecific": 39760, + "folate": 39761, + "Ġcoeff": 39762, + "ĠEstrogen": 39763, + "tumour": 39764, + "Ġhysterectomy": 39765, + "Ġinositol": 39766, + "ĠBaz": 39767, + "istein": 39768, + "Ġcrucially": 39769, + "Ġdinoflag": 39770, + "ÍĶÍĴ": 39771, + "ĠDragon": 39772, + "ĠSpor": 39773, + "ĠMater": 39774, + "ĠHero": 39775, + "plicing": 39776, + "ĠANT": 39777, + "ĠFormic": 39778, + "Queue": 39779, + "ocarcinomas": 39780, + "UPS": 39781, + "ĠPc": 39782, + "encoders": 39783, + "Ġinvaded": 39784, + "ĠPhases": 39785, + "Ġpostmortem": 39786, + "Ġslows": 39787, + "ĠMcL": 39788, + "ĠVerma": 39789, + "ĠViability": 39790, + "Ġcompensating": 39791, + "Ġclamped": 39792, + "jm": 39793, + "ĠRiv": 39794, + "upon": 39795, + "ĠDickinson": 39796, + "initiated": 39797, + "Ġsider": 39798, + "ĠSelen": 39799, + "ĠAka": 39800, + "idelberg": 39801, + "Ġqualifying": 39802, + "Ġenforcing": 39803, + "otrophs": 39804, + "ĠSNAP": 39805, + "Ġrust": 39806, + "imburs": 39807, + "Ġimmunocompromised": 39808, + "ĠFleming": 39809, + "Ġlizards": 39810, + "dialysis": 39811, + "ĠUnivariate": 39812, + "Ġgasoline": 39813, + "Ġtenure": 39814, + "Ġsustaining": 39815, + "Ġmotone": 39816, + "bay": 39817, + "wani": 39818, + "orestation": 39819, + "ĠXII": 39820, + "Ġradiofrequency": 39821, + "ĠGuided": 39822, + "Individual": 39823, + "ĠSpectrometer": 39824, + "ĠGoing": 39825, + "ĠMartins": 39826, + "Approxim": 39827, + "amak": 39828, + "ĠâĪı": 39829, + "ĠOmn": 39830, + "Ġoutpatients": 39831, + "Ġhyperbol": 39832, + "ĠPerceptual": 39833, + "ĠBurke": 39834, + "Boltzmann": 39835, + "ĠMd": 39836, + "Ġpaw": 39837, + "ĠCathedral": 39838, + "Ġhyaluron": 39839, + "Ġbrachial": 39840, + "Ġaflatoxin": 39841, + "imo": 39842, + "Ġenrol": 39843, + "Ġdetonation": 39844, + "Ġoverly": 39845, + "thest": 39846, + "Ġsecondly": 39847, + "ĠSchiz": 39848, + "ĠIGFBP": 39849, + "atechin": 39850, + "Ġsaves": 39851, + "tiers": 39852, + "ĠBates": 39853, + "Ġalliance": 39854, + "Ġattri": 39855, + "Ġastro": 39856, + "ĠPathological": 39857, + "Ġgambiae": 39858, + "Park": 39859, + "idable": 39860, + "ĠNil": 39861, + "ĠJas": 39862, + "Ġneeding": 39863, + "meier": 39864, + "Ġferroptosis": 39865, + "ĠGuidance": 39866, + "AZ": 39867, + "iol": 39868, + "Ġacknowledg": 39869, + "exual": 39870, + "Ġmenopause": 39871, + "Ġadjunct": 39872, + "capture": 39873, + "ĠDeputy": 39874, + "Ġbial": 39875, + "ifa": 39876, + "ĠChitosan": 39877, + "ĠTopics": 39878, + "ĠPlasmid": 39879, + "calculations": 39880, + "give": 39881, + "responders": 39882, + "ulla": 39883, + "ĠMoreno": 39884, + "Ġcommentary": 39885, + "ĠMahm": 39886, + "": 39887, + "onacci": 39888, + "ĠCould": 39889, + "ĠTRP": 39890, + "seconds": 39891, + "GraphPad": 39892, + "Little": 39893, + "hey": 39894, + "Ġalike": 39895, + "ĠDias": 39896, + "aroo": 39897, + "Ġı": 39898, + "Ġtaxes": 39899, + "phenanth": 39900, + "ĠCheung": 39901, + "ĠPiet": 39902, + "Df": 39903, + "GU": 39904, + "mectin": 39905, + "zee": 39906, + "Ġdλ": 39907, + "Ġsyntheses": 39908, + "ĠáĪ": 39909, + "Simulation": 39910, + "ĠEleven": 39911, + "worms": 39912, + "lymphocyte": 39913, + "Ġhaemorrhage": 39914, + "ĠOwn": 39915, + "ĠKant": 39916, + "Ġoverse": 39917, + "Ġideation": 39918, + "ĠHarper": 39919, + "Acknowledgments": 39920, + "vili": 39921, + "yna": 39922, + "ĠRecurrence": 39923, + "oza": 39924, + "Ġhenceforth": 39925, + "zees": 39926, + "Ġquasic": 39927, + "Ġchoroidal": 39928, + "Ġantimalarial": 39929, + "Ġcoarsening": 39930, + "Deb": 39931, + "diam": 39932, + "ĠWeights": 39933, + "Ġbuying": 39934, + "Ġmessaging": 39935, + "February": 39936, + "Extended": 39937, + "ĠRossi": 39938, + "Ġmistaken": 39939, + "Ġutero": 39940, + "jas": 39941, + "icitis": 39942, + "ĠTidal": 39943, + "Ġpharyngeal": 39944, + "click": 39945, + "Ġmyo": 39946, + "knock": 39947, + "Ġprominence": 39948, + "Ġamphiphilic": 39949, + "corn": 39950, + "Ġonboard": 39951, + "ĠDud": 39952, + "ĠWoman": 39953, + "ĠOutbreak": 39954, + "Ġpreferably": 39955, + "Ġsketches": 39956, + "Sat": 39957, + "fixing": 39958, + "ĠMey": 39959, + "ĠLetters": 39960, + "ITIES": 39961, + "ĠSDP": 39962, + "ĠLNCaP": 39963, + "DX": 39964, + "Fluor": 39965, + "Rv": 39966, + "Sect": 39967, + "ĠIons": 39968, + "Ġtrachom": 39969, + "Ġultrastructure": 39970, + "qvist": 39971, + "rophe": 39972, + "Ġreceipt": 39973, + "ĠQuint": 39974, + "Ġswapping": 39975, + "aminidase": 39976, + "Ġarchival": 39977, + "ĠCreating": 39978, + "ĠBarton": 39979, + "diagnosed": 39980, + "atological": 39981, + "olph": 39982, + "ĠPFA": 39983, + "ĠLAP": 39984, + "Ġunphysical": 39985, + "eqn": 39986, + "Ġquartiles": 39987, + "olytica": 39988, + "ĠFreed": 39989, + "Ġventilator": 39990, + "Ġkaryotype": 39991, + "Sta": 39992, + "still": 39993, + "ĠTate": 39994, + "urability": 39995, + "ĠGron": 39996, + "Ġtrimer": 39997, + "IPA": 39998, + "adeca": 39999, + "ĠImplementing": 40000, + "sity": 40001, + "itr": 40002, + "Ġbom": 40003, + "Ġnonrelativistic": 40004, + "Ġmicelle": 40005, + "ĠAdminist": 40006, + "Ġelectrolysis": 40007, + "harmon": 40008, + "OLOGICAL": 40009, + "Liter": 40010, + "ĠGUI": 40011, + "ĠQL": 40012, + "months": 40013, + "Ġsuperflu": 40014, + "cuts": 40015, + "Ġelicits": 40016, + "Ġmultiplexed": 40017, + "overlap": 40018, + "Ġcadaver": 40019, + "Ġou": 40020, + "ĠSheng": 40021, + "erea": 40022, + "ĠNBC": 40023, + "Ġdeter": 40024, + "tyrosine": 40025, + "ĠParts": 40026, + "Ġessay": 40027, + "kas": 40028, + "itted": 40029, + "ĠPZT": 40030, + "essler": 40031, + "Ġsimulators": 40032, + "Ġradiating": 40033, + "cutting": 40034, + "ĠCalculating": 40035, + "THER": 40036, + "ĠROCK": 40037, + "communic": 40038, + "Ġbonus": 40039, + "ĠCPA": 40040, + "ĠPUR": 40041, + "ulton": 40042, + "ĠZhi": 40043, + "Ġcaloric": 40044, + "Ġinterpolate": 40045, + "ĠSecretion": 40046, + "Ġneurocognitive": 40047, + "Ġgadolinium": 40048, + "frequencies": 40049, + "ĠTract": 40050, + "Ġminimax": 40051, + "ĠBrock": 40052, + "rypsin": 40053, + "ĠResonant": 40054, + "ĠACKNOWLEDGEMENTS": 40055, + "Dom": 40056, + "Ġholotype": 40057, + "Special": 40058, + "Ġimmunoreactive": 40059, + "ARNING": 40060, + "Panel": 40061, + "ĠJohannes": 40062, + "RFP": 40063, + "zzi": 40064, + "ĠPomer": 40065, + "Ġtransects": 40066, + "Ġpoured": 40067, + "EDs": 40068, + "ĠCircum": 40069, + "Ġabnormally": 40070, + "ĠPunj": 40071, + "Gol": 40072, + "Hop": 40073, + "Hex": 40074, + "ILE": 40075, + "Ġsourced": 40076, + "oclase": 40077, + "protobuf": 40078, + "Ġfrogs": 40079, + "ĠOttawa": 40080, + "Ġbiogeochemical": 40081, + "Ġlentivirus": 40082, + "Young": 40083, + "ĠIPS": 40084, + "assen": 40085, + "Ġunrestricted": 40086, + "Ġmatplotlib": 40087, + "Ġchloramphenicol": 40088, + "ĠContextual": 40089, + "ĠHawaiian": 40090, + "Legend": 40091, + "Sparse": 40092, + "bore": 40093, + "gaussian": 40094, + "uke": 40095, + "Ġâ̰": 40096, + "retest": 40097, + "SSE": 40098, + "preting": 40099, + "ĠPanama": 40100, + "ĠBroadband": 40101, + "conjugate": 40102, + "Bytes": 40103, + "GSH": 40104, + "Uns": 40105, + "rina": 40106, + "Ġdrained": 40107, + "Ġscap": 40108, + "Ġinvested": 40109, + "Ġsatisfactorily": 40110, + "Ġherbivores": 40111, + "Ġarachidonic": 40112, + "ymetrix": 40113, + "Ġnect": 40114, + "Ġconges": 40115, + "ĠMerr": 40116, + "ĠMai": 40117, + "Chain": 40118, + "Ġretrieving": 40119, + "Collection": 40120, + "ĠMTX": 40121, + "ĠFernando": 40122, + "hg": 40123, + "ĠRams": 40124, + "thresh": 40125, + "apsules": 40126, + "Ġconduit": 40127, + "swap": 40128, + "Ġblowing": 40129, + "ĠNyquist": 40130, + "Ġunconscious": 40131, + "ĠDIFFERENT": 40132, + "Techn": 40133, + "hiz": 40134, + "îĤ": 40135, + "Ġdξ": 40136, + "ĠSto": 40137, + "ĠFlavon": 40138, + "David": 40139, + "Ġfiltrate": 40140, + "lith": 40141, + "ĠWool": 40142, + "ĠKnot": 40143, + "Ġhalide": 40144, + "Ġbioassay": 40145, + "ĠGoldberg": 40146, + "ĠTrichoderma": 40147, + "Ġintraspecific": 40148, + "crystall": 40149, + "ĠRend": 40150, + "ourg": 40151, + "Ġundertake": 40152, + "ĠEnum": 40153, + "infect": 40154, + "Ġmidgut": 40155, + "attack": 40156, + "ĠCircle": 40157, + "Ġpleiotropic": 40158, + "escent": 40159, + "ĠFri": 40160, + "philis": 40161, + "astings": 40162, + "Ġbiogas": 40163, + "ĠÄľ": 40164, + "Ġaccompany": 40165, + "Ġrolled": 40166, + "Ġchirp": 40167, + "Ġsomatostatin": 40168, + "varkappa": 40169, + "Scal": 40170, + "Ġdrow": 40171, + "romed": 40172, + "ĠLup": 40173, + "ĠLuminosity": 40174, + "ĠNig": 40175, + "ferromagnetic": 40176, + "ĠToy": 40177, + "Ġcannabinoid": 40178, + "ĠHOX": 40179, + "iele": 40180, + "ĠCTX": 40181, + "Ġhydrop": 40182, + "Ġfavorite": 40183, + "Ġstretches": 40184, + "evaluated": 40185, + "ogroups": 40186, + "acal": 40187, + "ollo": 40188, + "Ġgenders": 40189, + "ĠGraft": 40190, + "Ġincidences": 40191, + "Ġreplacements": 40192, + "ĠTRUNC": 40193, + "CRF": 40194, + "Ġequalization": 40195, + "ĠRenew": 40196, + "Ġplethora": 40197, + "ĠEncoder": 40198, + "Mit": 40199, + "Ġcaches": 40200, + "orate": 40201, + "endors": 40202, + "ĠCaution": 40203, + "ĠAbel": 40204, + "compression": 40205, + "ĠLarsen": 40206, + "ĠElimination": 40207, + "Ġtester": 40208, + "Ġninth": 40209, + "ĠLö": 40210, + "Ġspiders": 40211, + "Ġpoem": 40212, + "Ġeducators": 40213, + "ĠEnhances": 40214, + "destructive": 40215, + "Fourier": 40216, + "Ġseismicity": 40217, + "ĠYunnan": 40218, + "Riemannian": 40219, + "WID": 40220, + "vular": 40221, + "ĠBorder": 40222, + "Ġcombin": 40223, + "singlet": 40224, + "ĠEddington": 40225, + "ĠTemplate": 40226, + "ĠPAX": 40227, + "Ġbasalts": 40228, + "Enh": 40229, + "Ġassistants": 40230, + "ĠCascade": 40231, + "Ġinbreeding": 40232, + "chini": 40233, + "Ġupgraded": 40234, + "ĠTransit": 40235, + "survival": 40236, + "Ġinjector": 40237, + "ĠPascal": 40238, + "DEVICE": 40239, + "Ġfost": 40240, + "ĠKand": 40241, + "Ġextragalactic": 40242, + "ependently": 40243, + "Ġexcite": 40244, + "Ġfulfil": 40245, + "Ġriparian": 40246, + "Ġuploaded": 40247, + "aun": 40248, + "lod": 40249, + "saving": 40250, + "ĠHib": 40251, + "ĠEra": 40252, + "obese": 40253, + "Ġui": 40254, + "Ġspectrally": 40255, + "keV": 40256, + "xxx": 40257, + "ĠOtto": 40258, + "Ġétale": 40259, + "LAT": 40260, + "dermal": 40261, + "diaz": 40262, + "ĠPli": 40263, + "Ġlegume": 40264, + "Ġinspect": 40265, + "Ġthymic": 40266, + "ĠHormone": 40267, + "áĢ": 40268, + "inot": 40269, + "ĠShib": 40270, + "ĠBCC": 40271, + "ĠVital": 40272, + "Ġprofits": 40273, + "ĠFederated": 40274, + "Ġflipped": 40275, + "Ġproprietary": 40276, + "incorporated": 40277, + "Ġbacteremia": 40278, + "Ġáŀĩ": 40279, + "fins": 40280, + "ä½": 40281, + "esia": 40282, + "ĠHollow": 40283, + "geons": 40284, + "Ġtrehalose": 40285, + "ERO": 40286, + "osterol": 40287, + "omus": 40288, + "ĠCrystall": 40289, + "Ġcuration": 40290, + "Ġmagnon": 40291, + "ĠAmend": 40292, + "Ġharb": 40293, + "Ġneutrality": 40294, + "ĠDelphi": 40295, + "Ġnonsense": 40296, + "ĠHomeostasis": 40297, + "Ġexpenditures": 40298, + "Sequential": 40299, + "imodular": 40300, + "Ġzenith": 40301, + "ĠMoran": 40302, + "Ġbootstrapping": 40303, + "iomy": 40304, + "lactic": 40305, + "iture": 40306, + "Ġnat": 40307, + "Ġgab": 40308, + "Ġchat": 40309, + "regional": 40310, + "Ġcrashes": 40311, + "ĠAFB": 40312, + "Ġcrowded": 40313, + "Ġtweet": 40314, + "engineered": 40315, + "ĠCharged": 40316, + "Sche": 40317, + "ITIONS": 40318, + "ĠCoral": 40319, + "ĠEli": 40320, + "Ġinverting": 40321, + "Ġpedag": 40322, + "ĠSanders": 40323, + "Meanwhile": 40324, + "ĠGriffiths": 40325, + "PSCs": 40326, + "tize": 40327, + "ĠMail": 40328, + "Ġundec": 40329, + "Ġhermitian": 40330, + "ĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ": 40331, + "ĠExplos": 40332, + "Ġwestward": 40333, + "ĠConfirm": 40334, + "Begin": 40335, + "Ġfactories": 40336, + "ĠPRL": 40337, + "shear": 40338, + "Header": 40339, + "ĠFLAGS": 40340, + "anomal": 40341, + "ĠQW": 40342, + "ĠÌħ": 40343, + "oinositi": 40344, + "Ġmammography": 40345, + "Ġdepositional": 40346, + "EXP": 40347, + "residue": 40348, + "Ġunsatisfactory": 40349, + "Aβ": 40350, + "MUX": 40351, + "Ġstaged": 40352, + "ĠMMT": 40353, + "ĠKus": 40354, + "llo": 40355, + "Ġtrainer": 40356, + "adden": 40357, + "Ġpinch": 40358, + "WARE": 40359, + "Ġcabinet": 40360, + "CSP": 40361, + "ecum": 40362, + "oteric": 40363, + "ĠHav": 40364, + "Ġresume": 40365, + "Ġnetworked": 40366, + "share": 40367, + "ĠColle": 40368, + "Ġchemotactic": 40369, + "ĠGlyc": 40370, + "olkit": 40371, + "Ġbotulinum": 40372, + "ĠNeighborhood": 40373, + "mV": 40374, + "ĠHQ": 40375, + "efaciens": 40376, + "gett": 40377, + "Ġgeost": 40378, + "ĠCDW": 40379, + "Ģ̇": 40380, + "Ġfloors": 40381, + "representing": 40382, + "odiode": 40383, + "ĠInstance": 40384, + "Ġmonodis": 40385, + "drying": 40386, + "reasing": 40387, + "igi": 40388, + "Ġgout": 40389, + "ĠIEC": 40390, + "Ġflush": 40391, + "Ġtraded": 40392, + "Review": 40393, + "ĠïĤ¢": 40394, + "Ġà¤": 40395, + "Ġabbreviations": 40396, + "otherapies": 40397, + "Ġindeterminate": 40398, + "Ġglutaraldehyde": 40399, + "Ġattrition": 40400, + "jump": 40401, + "inde": 40402, + "ĠGri": 40403, + "arction": 40404, + "TRAIN": 40405, + "Ġescaped": 40406, + "atement": 40407, + "ĠPam": 40408, + "ĠGAM": 40409, + "productive": 40410, + "ĠAmericas": 40411, + "agenesis": 40412, + "ĠMixtures": 40413, + "ĠHooft": 40414, + "ĠWindow": 40415, + "Ġnodular": 40416, + "Ġechin": 40417, + "DOF": 40418, + "ĠDDT": 40419, + "electrical": 40420, + "ĠDecentralized": 40421, + "Ġcontradict": 40422, + "French": 40423, + "Ġaustr": 40424, + "ĠAPD": 40425, + "ĠDIM": 40426, + "ĠSten": 40427, + "ronomic": 40428, + "ĠPolymorphism": 40429, + "Ġcocc": 40430, + "itings": 40431, + "Ġsubcritical": 40432, + "ĠUniqueness": 40433, + "OPEN": 40434, + "rotoxicity": 40435, + "GenBank": 40436, + "atabases": 40437, + "Nets": 40438, + "uistic": 40439, + "yric": 40440, + "ĠSID": 40441, + "Ġcooked": 40442, + "ĠJudge": 40443, + "Ġparameterizations": 40444, + "Ġenumerated": 40445, + "ĠAsthma": 40446, + "Develop": 40447, + "Ġcake": 40448, + "ĠAges": 40449, + "venile": 40450, + "Ġflor": 40451, + "Ġcouldn": 40452, + "detach": 40453, + "Ġpipette": 40454, + "ĠInstant": 40455, + "Ġtentatively": 40456, + "ĠINTEGR": 40457, + "HQ": 40458, + "Mapping": 40459, + "cq": 40460, + "åĪ": 40461, + "SUM": 40462, + "fractions": 40463, + "ĠClaud": 40464, + "Formula": 40465, + "Axis": 40466, + "ĠBilly": 40467, + "ĠMethane": 40468, + "ĠIGM": 40469, + "cannot": 40470, + "س": 40471, + "Ġciting": 40472, + "ĠDynam": 40473, + "Ġlett": 40474, + "egler": 40475, + "ĠPhysicians": 40476, + "xFF": 40477, + "Ġoyster": 40478, + "ĠTOC": 40479, + "Ġsubarachnoid": 40480, + "ĠCOM": 40481, + "ITER": 40482, + "Ġbenzodiazep": 40483, + "Ġuncomplicated": 40484, + "tillo": 40485, + "Carbon": 40486, + "atem": 40487, + "Ġsel": 40488, + "ingo": 40489, + "IVITY": 40490, + "Ġcavern": 40491, + "Ġspacelike": 40492, + "ĠCollisions": 40493, + "straint": 40494, + "Ġmycobacterial": 40495, + "Ġtrachomatis": 40496, + "Ai": 40497, + "mf": 40498, + "ĠTric": 40499, + "tico": 40500, + "ĠElection": 40501, + "ĠKDM": 40502, + "ĠExosomes": 40503, + "afluor": 40504, + "Ġformalized": 40505, + "ĠELF": 40506, + "vphantom": 40507, + "ĠSME": 40508, + "ichuan": 40509, + "ĠVMs": 40510, + "Ġrostral": 40511, + "ofer": 40512, + "ramanian": 40513, + "intercal": 40514, + "Merck": 40515, + "ĠFerguson": 40516, + "HU": 40517, + "lj": 40518, + "Ġrack": 40519, + "Ġmicrograph": 40520, + "CTS": 40521, + "Ġpassively": 40522, + "ĠMasses": 40523, + "rangians": 40524, + "ĠADM": 40525, + "ĠProvided": 40526, + "ĠVeterans": 40527, + "sound": 40528, + "gex": 40529, + "ĠSpiral": 40530, + "Ġfossa": 40531, + "Ġthermodynamically": 40532, + "ĠStaining": 40533, + "Ġkar": 40534, + "eflon": 40535, + "ĠBruns": 40536, + "VAE": 40537, + "olyticus": 40538, + "Ġintranasal": 40539, + "ĠProspects": 40540, + "athers": 40541, + "Ġnumbering": 40542, + "ĠReplacement": 40543, + "ĠNoncommutative": 40544, + "quisitions": 40545, + "ĠSIMD": 40546, + "ĠArterial": 40547, + "ĠHGF": 40548, + "ĠGPP": 40549, + "Ġfluvial": 40550, + "neri": 40551, + "ĠCompressed": 40552, + "VIDIA": 40553, + "Ġshocked": 40554, + "dys": 40555, + "invariance": 40556, + "enstein": 40557, + "ĠSCM": 40558, + "ĠDod": 40559, + "Ġsho": 40560, + "Chlor": 40561, + "duino": 40562, + "Ġurgently": 40563, + "soc": 40564, + "etching": 40565, + "Ġdiffractive": 40566, + "ĠZF": 40567, + "Ġhyperplanes": 40568, + "Ġmyri": 40569, + "Ġferromagnetism": 40570, + "filament": 40571, + "Ġjustifies": 40572, + "fault": 40573, + "ĠHTS": 40574, + "ĠEPC": 40575, + "too": 40576, + "ĠTRAP": 40577, + "ión": 40578, + "rv": 40579, + "ĠBPD": 40580, + "ĠNod": 40581, + "posit": 40582, + "ĠConvers": 40583, + "Ġzeroes": 40584, + "ĠGlen": 40585, + "ĠDisturb": 40586, + "Ġtableau": 40587, + "Ġpseudot": 40588, + "ĠCollider": 40589, + "Ġadsorbents": 40590, + "ĠGrove": 40591, + "Ġkingdom": 40592, + "Est": 40593, + "Xs": 40594, + "czyk": 40595, + "ĠWille": 40596, + "ĠVOL": 40597, + "scar": 40598, + "ĠAdler": 40599, + "ĠOrchestra": 40600, + "Ġsparsely": 40601, + "glycosylation": 40602, + "Lac": 40603, + "otions": 40604, + "ĠIle": 40605, + "Ġbeacon": 40606, + "ĠRn": 40607, + "ullah": 40608, + "Ġtimelike": 40609, + "ĠForests": 40610, + "Ġupload": 40611, + "jit": 40612, + "ĠEDM": 40613, + "Ġtransplants": 40614, + "marker": 40615, + "ĠBreeding": 40616, + "ÎĶÎĶ": 40617, + "Ġfavorably": 40618, + "ĠTransformations": 40619, + "abeled": 40620, + "ĠPolitics": 40621, + "episode": 40622, + "Ġfut": 40623, + "Ġdithi": 40624, + "ĠMw": 40625, + "Ġtranspiration": 40626, + "Ġunlimited": 40627, + "ĠAntiv": 40628, + "PPV": 40629, + "Ġnomogram": 40630, + "Ġinvented": 40631, + "ĠSchedule": 40632, + "allows": 40633, + "Ġtransvers": 40634, + "Ġworkpiece": 40635, + "blacksquare": 40636, + "callback": 40637, + "ĠAthletic": 40638, + "hans": 40639, + "poles": 40640, + "Ġeavesdrop": 40641, + "ĠCone": 40642, + "oclim": 40643, + "ĠGhost": 40644, + "iterations": 40645, + "Ġweaken": 40646, + "Ġalkaloid": 40647, + "Ġveterans": 40648, + "Ġprostatectomy": 40649, + "Ġbog": 40650, + "ĠCed": 40651, + "ĠFever": 40652, + "ylan": 40653, + "archive": 40654, + "Ġattackers": 40655, + "Making": 40656, + "bane": 40657, + "ĠPull": 40658, + "ĠFLO": 40659, + "Ġcoaches": 40660, + "ĠVSM": 40661, + "okh": 40662, + "ĠSpo": 40663, + "amilial": 40664, + "principle": 40665, + "Ġaggressiveness": 40666, + "Ġgardens": 40667, + "SIG": 40668, + "Ġmbar": 40669, + ".....": 40670, + "Ġoptimizes": 40671, + "Ġmorphologic": 40672, + "hani": 40673, + "Ġgermanium": 40674, + "Enabled": 40675, + "wb": 40676, + "Ġforamen": 40677, + "ĠSPA": 40678, + "Ġmagnified": 40679, + "ĠSlater": 40680, + "ĠSyrian": 40681, + "Ġtert": 40682, + "Ġtraditions": 40683, + "Ġoffensive": 40684, + "Ġhydrology": 40685, + "ergetics": 40686, + "Phot": 40687, + "Ġperovskites": 40688, + "Ġwavenumbers": 40689, + "Ġosteoclasts": 40690, + "imedean": 40691, + "ĠBasketball": 40692, + "benzodiox": 40693, + "ĠTRUNCATED": 40694, + "Ġbishop": 40695, + "ĠSgr": 40696, + "ĠSatisfaction": 40697, + "agnostic": 40698, + "numeric": 40699, + "Ġnormals": 40700, + "Ġhumor": 40701, + "Ġcontinents": 40702, + "NATION": 40703, + "Lagrangian": 40704, + "Ġknees": 40705, + "ĠIDE": 40706, + "adas": 40707, + "adia": 40708, + "ĠOU": 40709, + "onds": 40710, + "ĠChaud": 40711, + "Ġslicing": 40712, + "ĠActor": 40713, + "Alt": 40714, + "Ġbroadcasts": 40715, + "osaurs": 40716, + "Ġpickle": 40717, + "Ġunfamiliar": 40718, + "allus": 40719, + "Ġhashing": 40720, + "incidence": 40721, + "Ġmetabolized": 40722, + "Ġhomogeneously": 40723, + "ĠFalcon": 40724, + "ĠÑģ": 40725, + "ĠCere": 40726, + "ĠCLA": 40727, + "ĠPaste": 40728, + "ĠFry": 40729, + "ĠDre": 40730, + "adult": 40731, + "Ġdiscounted": 40732, + "sensitized": 40733, + "erculous": 40734, + "ĠPixel": 40735, + "ĠBram": 40736, + "allo": 40737, + "ipative": 40738, + "ipality": 40739, + "ĠStrict": 40740, + "ĠTrinity": 40741, + "ĠClassifiers": 40742, + "ĠBasel": 40743, + "ĠCurcumin": 40744, + "ĠLUMO": 40745, + "Ġmediastinal": 40746, + "ĠFFA": 40747, + "Ġplenty": 40748, + "prised": 40749, + "Ġprinter": 40750, + "Ġcalcare": 40751, + "insn": 40752, + "ontology": 40753, + "Ġgrounding": 40754, + "ĠALDH": 40755, + "STRING": 40756, + "ĠFemales": 40757, + "ĠFocusing": 40758, + "assessment": 40759, + "ĠBluetooth": 40760, + "ëĬĶ": 40761, + "Ġego": 40762, + "ĠDAC": 40763, + "onding": 40764, + "randa": 40765, + "ĠLudwig": 40766, + "Ġantecedent": 40767, + "ĠErnst": 40768, + "dX": 40769, + "odeling": 40770, + "âĢĭ": 40771, + "Inser": 40772, + "ognormal": 40773, + "ĠTevatron": 40774, + "Ġcovariances": 40775, + "riging": 40776, + "ĠMgSO": 40777, + "carbonitrile": 40778, + "ĠLoren": 40779, + "Ġpolytopes": 40780, + "ĠParental": 40781, + "Ġunhealthy": 40782, + "itherto": 40783, + "ĠMotif": 40784, + "DataType": 40785, + "ĠMIPS": 40786, + "ĠPhosphorus": 40787, + "MoO": 40788, + "ĠPerturbations": 40789, + "Ġaphids": 40790, + "Ġanhydride": 40791, + "ideration": 40792, + "ĠMits": 40793, + "gravit": 40794, + "Ġdestinations": 40795, + "Commun": 40796, + "Ġtetrahedron": 40797, + "implicit": 40798, + "Ġassort": 40799, + "ĠSubt": 40800, + "ĠAcetyl": 40801, + "ecium": 40802, + "ĠNie": 40803, + "Ġoperand": 40804, + "ĠScher": 40805, + "azoles": 40806, + "tlement": 40807, + "ĠBlocking": 40808, + "Ġbottlenecks": 40809, + "ĠOccupational": 40810, + "HAS": 40811, + "Teller": 40812, + "Ġvague": 40813, + "esting": 40814, + "SNE": 40815, + "Ġphotoionization": 40816, + "Ġcomplaint": 40817, + "uspid": 40818, + "owler": 40819, + "Ġendocytic": 40820, + "Ġflocks": 40821, + "epsin": 40822, + "colors": 40823, + "otopes": 40824, + "ĠDepletion": 40825, + "ELLAR": 40826, + "armed": 40827, + "ĠTIR": 40828, + "Ġbullying": 40829, + "Ġâݧ": 40830, + "osporidium": 40831, + "Mr": 40832, + "ĠCic": 40833, + "ogal": 40834, + "Ġsectioned": 40835, + "Chapter": 40836, + "ĠContents": 40837, + "ĠPaths": 40838, + "ĠExplain": 40839, + "computing": 40840, + "Ġshrub": 40841, + "ĠMalaysian": 40842, + "Beta": 40843, + "Mad": 40844, + "Ros": 40845, + "Ġeyel": 40846, + "ĠACF": 40847, + "ĠMm": 40848, + "texture": 40849, + "Ġinterpretability": 40850, + "ĠTopic": 40851, + "Ġbadly": 40852, + "ĠmAh": 40853, + "Eg": 40854, + "RQ": 40855, + "pins": 40856, + "etc": 40857, + "ĠVogel": 40858, + "Ġhypoc": 40859, + "Ġrunaway": 40860, + "Ġpersonally": 40861, + "Ġbinders": 40862, + "sensory": 40863, + "ĠIPv": 40864, + "ranked": 40865, + "Ġfibrations": 40866, + "Ġstrawberry": 40867, + "arotomy": 40868, + "FLI": 40869, + "rator": 40870, + "odys": 40871, + "iran": 40872, + "ĠBead": 40873, + "ĠDAM": 40874, + "âĪĥ": 40875, + "Ġillusion": 40876, + "pidium": 40877, + "Place": 40878, + "Region": 40879, + "Ġallocations": 40880, + "Ġohmic": 40881, + "Ġnf": 40882, + "imino": 40883, + "ĠBris": 40884, + "itizing": 40885, + "proper": 40886, + "subgroup": 40887, + "Ġsalience": 40888, + "rangement": 40889, + "ĠMeaning": 40890, + "Ġbarcode": 40891, + "Ġneuropeptide": 40892, + "Ġendosperm": 40893, + "imab": 40894, + "Ġnanod": 40895, + "ĠMetz": 40896, + "Ġcollocation": 40897, + "ĠInfected": 40898, + "Ġpackaged": 40899, + "ĠADA": 40900, + "ĠBarth": 40901, + "ĠCNC": 40902, + "Ġcascaded": 40903, + "ĠStockholm": 40904, + "itas": 40905, + "ĠMMR": 40906, + "ĠDrought": 40907, + "Ġpermissible": 40908, + "Ġsciatic": 40909, + "Ġfringes": 40910, + "Ġexecutable": 40911, + "Ġstemness": 40912, + "ĠEndoscopic": 40913, + "aporin": 40914, + "TOP": 40915, + "eB": 40916, + "tur": 40917, + "ĠStages": 40918, + "anches": 40919, + "Ġnonperturbative": 40920, + "Ġmaritime": 40921, + "Ġcoverslips": 40922, + "Ġlagoon": 40923, + "Experiments": 40924, + "Ġcodewords": 40925, + "Encoder": 40926, + "das": 40927, + "prac": 40928, + "Ġpaddy": 40929, + "Ġdraining": 40930, + "Ġkids": 40931, + "Ġenemies": 40932, + "Ġmotile": 40933, + "Ġslack": 40934, + "bees": 40935, + "ĠSuppl": 40936, + "ĠBarber": 40937, + "ĠSPH": 40938, + "Ġcrystallite": 40939, + "fitted": 40940, + "cyclopent": 40941, + "ĠRMSD": 40942, + "Ġparkinson": 40943, + "Ġuncorrected": 40944, + "ĠSyntax": 40945, + "Ġmultinomial": 40946, + "ĠIncorporating": 40947, + "akrishnan": 40948, + "JL": 40949, + "NESS": 40950, + "mim": 40951, + "ĠTET": 40952, + "ĠPorph": 40953, + "ĠSchwe": 40954, + "Ġcatalogs": 40955, + "ĠAuthentication": 40956, + "Bro": 40957, + "ugar": 40958, + "ĠAmpl": 40959, + "Ġsapiens": 40960, + "ĠGANs": 40961, + "Ġnecessitates": 40962, + "tg": 40963, + "edal": 40964, + "ĠRear": 40965, + "opeptidase": 40966, + "ĠInformed": 40967, + "Ġtailor": 40968, + "ĠNNLO": 40969, + "Ġhemodynamics": 40970, + "Sy": 40971, + "dating": 40972, + "achers": 40973, + "ĠTodd": 40974, + "ĠMario": 40975, + "ĠUGT": 40976, + "ĠValent": 40977, + "Ġstreamlines": 40978, + "Ġwarrants": 40979, + "Ġrew": 40980, + "ĠMud": 40981, + "ĠGK": 40982, + "Ġcoke": 40983, + "ĠUran": 40984, + "Ġgrooves": 40985, + "ronate": 40986, + "ĠRadius": 40987, + "ĠSuite": 40988, + "atumoral": 40989, + "Workspace": 40990, + "ĠSynergistic": 40991, + "ĠAtherosclerosis": 40992, + "maz": 40993, + "argmax": 40994, + "shield": 40995, + "ontin": 40996, + "Ġlistener": 40997, + "ocytoma": 40998, + "ĠGrav": 40999, + "ĠJerusalem": 41000, + "pyrrolidin": 41001, + "ĠSprings": 41002, + "Ġseafloor": 41003, + "Ġdips": 41004, + "istani": 41005, + "obis": 41006, + "Ġphotodynamic": 41007, + "ADO": 41008, + "Ġionisation": 41009, + "Ġbarn": 41010, + "igenetics": 41011, + "Ġeconomies": 41012, + "ĠGlacier": 41013, + "Ġç": 41014, + "imes": 41015, + "ĠSasaki": 41016, + "chio": 41017, + "Ġassisting": 41018, + "ostin": 41019, + "Ġindiff": 41020, + "ĠShot": 41021, + "ĠNeuron": 41022, + "CDD": 41023, + "ĠCONST": 41024, + "ĠBSs": 41025, + "tas": 41026, + "association": 41027, + "parg": 41028, + "Ġescal": 41029, + "exercise": 41030, + "ĠAdela": 41031, + "Ġmyogenic": 41032, + "ĠNOAA": 41033, + "yclo": 41034, + "linal": 41035, + "ĠHut": 41036, + "Ġintroductory": 41037, + "Ġheterochromatin": 41038, + "Ġchemoresistance": 41039, + "Ġsimplifications": 41040, + "pyridin": 41041, + "Ġamyloidosis": 41042, + "Ġnanofiber": 41043, + "ĠSutton": 41044, + "ĠResilience": 41045, + "Parent": 41046, + "ĠLMS": 41047, + "Ġlets": 41048, + "ĠElderly": 41049, + "Ġirrevers": 41050, + "sheets": 41051, + "Effects": 41052, + "Ġellipses": 41053, + "ĠPhilosophy": 41054, + "Ġphotographic": 41055, + "HEAD": 41056, + "Ġreversibility": 41057, + "Ġfederated": 41058, + "ĠPhosphoserine": 41059, + "estimation": 41060, + "ĠHumph": 41061, + "Json": 41062, + "Ġfills": 41063, + "Ġverm": 41064, + "ĠSeif": 41065, + "withstanding": 41066, + "ĠYamada": 41067, + "eria": 41068, + "ĠFLA": 41069, + "ĠVick": 41070, + "toid": 41071, + "annier": 41072, + "Ġcancerous": 41073, + "PRINT": 41074, + "ĠMechanistic": 41075, + "Ġdusty": 41076, + "ĠAppend": 41077, + "ycin": 41078, + "Ġazo": 41079, + "Ġsizing": 41080, + "Ġcrayfish": 41081, + "avis": 41082, + "ĠAdvent": 41083, + "ĠCommunist": 41084, + "ĠIMU": 41085, + "pixels": 41086, + "Hall": 41087, + "past": 41088, + "ĠRous": 41089, + "ressional": 41090, + "aird": 41091, + "ĠADD": 41092, + "Ġsummarizing": 41093, + "Ġelectrol": 41094, + "Station": 41095, + "ĠLyα": 41096, + "ĠTMEM": 41097, + "Ġpeptidase": 41098, + "Dual": 41099, + "git": 41100, + "ĠBOD": 41101, + "ĠRham": 41102, + "ĠKak": 41103, + "Ġreadiness": 41104, + "ĠCompare": 41105, + "ĠRamos": 41106, + "significantly": 41107, + "Ġhairy": 41108, + "Ġvasopressin": 41109, + "ĠGuideline": 41110, + "BNP": 41111, + "Ġdirty": 41112, + "Ġinfimum": 41113, + "ĠAless": 41114, + "ĠVolcano": 41115, + "Magn": 41116, + "YY": 41117, + "ughlin": 41118, + "boplatin": 41119, + "ĠCantor": 41120, + "Ġclothes": 41121, + "ĠRw": 41122, + "Ġuseless": 41123, + "ĠKdV": 41124, + "operoxidase": 41125, + "ĠCorrect": 41126, + "Ġfatality": 41127, + "ĠRestriction": 41128, + "Computer": 41129, + "Department": 41130, + "Il": 41131, + "gang": 41132, + "ĠElectroc": 41133, + "obaric": 41134, + "Phen": 41135, + "Ġned": 41136, + "adh": 41137, + "issing": 41138, + "tooth": 41139, + "Ġmanuscripts": 41140, + "Ġbiotechnology": 41141, + "Supp": 41142, + "ĠPairwise": 41143, + "ĠParsing": 41144, + "dH": 41145, + "melt": 41146, + "rz": 41147, + "ĠCatalyst": 41148, + "emption": 41149, + "Ġshowers": 41150, + "BLEM": 41151, + "ĠBrothers": 41152, + "banon": 41153, + "Ġbrachy": 41154, + "metallicity": 41155, + "ĠCIS": 41156, + "ĠCopenhagen": 41157, + "Ġelder": 41158, + "Ġfinanc": 41159, + "odesic": 41160, + "Ġdevise": 41161, + "Ġsurvives": 41162, + "ĠðtÃŀ": 41163, + "Ġfascinating": 41164, + "Ġparallax": 41165, + "HOR": 41166, + "yth": 41167, + "onins": 41168, + "ĠEHR": 41169, + "ĠGates": 41170, + "obase": 41171, + "ĠConway": 41172, + "operations": 41173, + "manuel": 41174, + "ĠAbdominal": 41175, + "ĠARG": 41176, + "ĠGrö": 41177, + "Ġphotosens": 41178, + "ĠCoulter": 41179, + "ĠJulian": 41180, + "ĠLevine": 41181, + "Ġsarcoidosis": 41182, + "Ġpillars": 41183, + "ĠdR": 41184, + "ĠWG": 41185, + "Ġspeculation": 41186, + "anski": 41187, + "ĠGaussians": 41188, + "Schw": 41189, + "ĠNambu": 41190, + "parents": 41191, + "intrinsic": 41192, + "ĠKendall": 41193, + "ĠRg": 41194, + "Ġprototypical": 41195, + "brella": 41196, + "Ġtetrap": 41197, + "ĠPathophys": 41198, + "nmt": 41199, + "Ġergodicity": 41200, + "ĠYersinia": 41201, + "QO": 41202, + "ĠIAV": 41203, + "Ġchocolate": 41204, + "Ġconferring": 41205, + "ClNO": 41206, + "otia": 41207, + "Complete": 41208, + "ĠAMPA": 41209, + "ïĢŃ": 41210, + "Ġḡ": 41211, + "ĠiPSCs": 41212, + "ĠApparently": 41213, + "Ġintoxication": 41214, + "ĠFather": 41215, + "percent": 41216, + "Ġshaker": 41217, + "Ġfinancing": 41218, + "Ġgenitalia": 41219, + "members": 41220, + "Ġstagnation": 41221, + "hmatic": 41222, + "rored": 41223, + "Ġconidia": 41224, + "ataloader": 41225, + "ĠNeil": 41226, + "Ġliteratures": 41227, + "ĠGlc": 41228, + "ĠDevelopments": 41229, + "differentiation": 41230, + "ĠRevisited": 41231, + "nil": 41232, + "tom": 41233, + "diol": 41234, + "ĠAbell": 41235, + "Ġplastics": 41236, + "ĠLuke": 41237, + "adjacent": 41238, + "ĠBHs": 41239, + "ĠPositioning": 41240, + "ør": 41241, + "overexpressing": 41242, + "Ec": 41243, + "Pref": 41244, + "orting": 41245, + "Ġinch": 41246, + "ĠCatherine": 41247, + "ĠDMP": 41248, + "ĠOe": 41249, + "endothelial": 41250, + "ICES": 41251, + "ĠHadron": 41252, + "Ġrevisit": 41253, + "ĠPictures": 41254, + "ĠKnockdown": 41255, + "ilian": 41256, + "ĠALA": 41257, + "opamine": 41258, + "ĠLah": 41259, + "climate": 41260, + "Ġdistraction": 41261, + "arski": 41262, + "ĠAccount": 41263, + "reflex": 41264, + "Ġelongate": 41265, + "ĠAmbient": 41266, + "Cx": 41267, + "Machine": 41268, + "ĠJPEG": 41269, + "Ġclassifies": 41270, + "ĠRegulate": 41271, + "oplasia": 41272, + "injury": 41273, + "neighbors": 41274, + "ĠFORMATION": 41275, + "FIS": 41276, + "Sz": 41277, + "ĠOSC": 41278, + "Ġassembling": 41279, + "Ġintracerebral": 41280, + "supers": 41281, + "ĠpF": 41282, + "Ġheal": 41283, + "ĠVries": 41284, + "arche": 41285, + "ĠDecom": 41286, + "ĠDiffic": 41287, + "agenta": 41288, + "ĠSpirit": 41289, + "ĠIntersection": 41290, + "Ġendorsed": 41291, + "ĠNobel": 41292, + "iÏī": 41293, + "wu": 41294, + "phaly": 41295, + "Ġqueu": 41296, + "ĠForum": 41297, + "lander": 41298, + "Ġspectrometric": 41299, + "ĠHankel": 41300, + "ĠCSE": 41301, + "Ġresumed": 41302, + "ĠGRE": 41303, + "ACES": 41304, + "CTA": 41305, + "Ġbehaved": 41306, + "Monitor": 41307, + "ĠNikon": 41308, + "ĠCHARACTER": 41309, + "ĠSAL": 41310, + "ĠIch": 41311, + "ĠHSF": 41312, + "Ġgenotoxic": 41313, + "ificance": 41314, + "ĠChiang": 41315, + "ĠZen": 41316, + "ĠArrows": 41317, + "ĠPDT": 41318, + "ĠFLASH": 41319, + "ocortex": 41320, + "onstructing": 41321, + "Treatment": 41322, + "ĉĠĠĠĠĠĠ": 41323, + "edullary": 41324, + "ilty": 41325, + "indentation": 41326, + "Ġamended": 41327, + "Ġfled": 41328, + "rophication": 41329, + "ĠGLM": 41330, + "ĠOpera": 41331, + "HLH": 41332, + "Lite": 41333, + "bmod": 41334, + "Ġaversion": 41335, + "ĠSweet": 41336, + "Ġstreptavidin": 41337, + "ĠPairs": 41338, + "ugos": 41339, + "epoxy": 41340, + "Ġunspecified": 41341, + "Ġmicrochannel": 41342, + "ĠVictorian": 41343, + "Could": 41344, + "informed": 41345, + "Ġsits": 41346, + "Ġrx": 41347, + "Ġnep": 41348, + "touch": 41349, + "ROI": 41350, + "Ġheaders": 41351, + "flush": 41352, + "ĠPathogenic": 41353, + "Ġspacings": 41354, + "hetti": 41355, + "Ġmotivating": 41356, + "Ġstakeholder": 41357, + "ĠSymbolic": 41358, + "ĠCrani": 41359, + "Ġdispute": 41360, + "Ġassists": 41361, + "indler": 41362, + "ĠSpatio": 41363, + "ohm": 41364, + "Ġextrapolate": 41365, + "Ġelaboration": 41366, + "ĠGTPases": 41367, + "Ġcellulase": 41368, + "ĠTuc": 41369, + "olide": 41370, + "ĠAIM": 41371, + "plast": 41372, + "ĠBible": 41373, + "opoly": 41374, + "ubo": 41375, + "acean": 41376, + "ĠPenrose": 41377, + "ĠMapReduce": 41378, + "ĠKwon": 41379, + "Wall": 41380, + "Ġgcd": 41381, + "ĠArbitrary": 41382, + "Product": 41383, + "ĠGitHub": 41384, + "Fn": 41385, + "Ġck": 41386, + "ĠAus": 41387, + "Ġgrave": 41388, + "Ġmetabolically": 41389, + "Ġlisten": 41390, + "Ġextractions": 41391, + "ĠTrunc": 41392, + "ĠRadiology": 41393, + "conserving": 41394, + "ĠCompositional": 41395, + "reporting": 41396, + "sustain": 41397, + "îĢ": 41398, + "ĠObl": 41399, + "ĠkN": 41400, + "Stre": 41401, + "ĠSupergravity": 41402, + "ĠPVP": 41403, + "Ġcivilian": 41404, + "ĠTunnel": 41405, + "Ġhelicopter": 41406, + "ĠCambrian": 41407, + "Ġrg": 41408, + "ĠUPF": 41409, + "Ġpolyd": 41410, + "Ġobservability": 41411, + "container": 41412, + "nitros": 41413, + "ĠCutting": 41414, + "Ġdecouple": 41415, + "Ġcarboxy": 41416, + "crow": 41417, + "Ġcx": 41418, + "ĠKell": 41419, + "Ġprojectors": 41420, + "Ġmyocarditis": 41421, + "itoneum": 41422, + "conditioning": 41423, + "ĠETH": 41424, + "oyama": 41425, + "Ġphosphates": 41426, + "ĠSubjective": 41427, + "ĠSerre": 41428, + "Ġcollagenase": 41429, + "Ġvibrating": 41430, + "streptomycin": 41431, + "zhen": 41432, + "Ġcres": 41433, + "Ġcull": 41434, + "Ġhaven": 41435, + "ĠGPL": 41436, + "lessness": 41437, + "Ġviewpoints": 41438, + ",,,": 41439, + "ochromic": 41440, + "uyama": 41441, + "Ġpartnerships": 41442, + "LICENSE": 41443, + "ĠSIFT": 41444, + "resources": 41445, + "ĠGos": 41446, + "ivac": 41447, + "Ġneurogenic": 41448, + "Adj": 41449, + "Ġaquifers": 41450, + "ĠGlycos": 41451, + "ĠMatthews": 41452, + "ĠFriday": 41453, + "ĠpX": 41454, + "Ġante": 41455, + "ĠFenton": 41456, + "ĠEukary": 41457, + "ibal": 41458, + "ideae": 41459, + "Attention": 41460, + "ĠPolymerization": 41461, + "Ġflipping": 41462, + "ĠMediates": 41463, + "Ġstationarity": 41464, + "Ġechoes": 41465, + "alidomide": 41466, + "Ġdelineation": 41467, + "Ġnaval": 41468, + "ĠSomatic": 41469, + "Ġstub": 41470, + "ĠBever": 41471, + "ĠRiz": 41472, + "Ġresummation": 41473, + "Ġassault": 41474, + "Ġpreexisting": 41475, + "Ġhypermethylation": 41476, + "Ġconserving": 41477, + "recorded": 41478, + "amn": 41479, + "Ġchow": 41480, + "ĠKill": 41481, + "ĠProduced": 41482, + "Ġrefs": 41483, + "ĠEnzymes": 41484, + "Ġdeepest": 41485, + "&&&": 41486, + "ĠFRP": 41487, + "Ġmilieu": 41488, + "Ġirrigated": 41489, + "ĠAnatomical": 41490, + "Ġdissect": 41491, + "iliensis": 41492, + "razolo": 41493, + "ĠProbable": 41494, + "solve": 41495, + "confirmed": 41496, + "ohydrodynamic": 41497, + "library": 41498, + "ĠCiti": 41499, + "ĠPHA": 41500, + "itsky": 41501, + "Ġelectrone": 41502, + "naive": 41503, + "Ġribs": 41504, + "ĠPhotonic": 41505, + "Ġsubstantia": 41506, + "ĠESTIM": 41507, + "Ġduodenum": 41508, + "ĠChagas": 41509, + "ĠSURVEY": 41510, + "Press": 41511, + "bian": 41512, + "¤": 41513, + "hei": 41514, + "ĠVAR": 41515, + "Ġcolocalization": 41516, + "Ġpolyl": 41517, + "Ġdough": 41518, + "Ġmicrocontroller": 41519, + "Ġinternalized": 41520, + "Ġdischarging": 41521, + "ĠChlamydomonas": 41522, + "orad": 41523, + "itel": 41524, + "ĠWend": 41525, + "Ġlogits": 41526, + "Ġelectrocataly": 41527, + "ĠAmar": 41528, + "Ġappreciably": 41529, + "Ġneurotransmitters": 41530, + "formerly": 41531, + "cul": 41532, + "rata": 41533, + "ĠBalk": 41534, + "ĠOsm": 41535, + "Ġsymptomatology": 41536, + "ĠFIELD": 41537, + "ĠAPs": 41538, + "Ġgymn": 41539, + "ĠMMS": 41540, + "Ġrefresh": 41541, + "ĠIndices": 41542, + "Ġimplantable": 41543, + "shuffle": 41544, + "ĠHeath": 41545, + "Ġcrisp": 41546, + "Ġdissertation": 41547, + "ĠUlt": 41548, + "Description": 41549, + "ĠOriginally": 41550, + "ĠFn": 41551, + "ĠFLOW": 41552, + "ubility": 41553, + "Ġexams": 41554, + "ĠShor": 41555, + "ĠCdTe": 41556, + "psycho": 41557, + "Ġdictates": 41558, + "Ġparenchymal": 41559, + "ĠPretreatment": 41560, + "Ġremembered": 41561, + "Ġbras": 41562, + "otid": 41563, + "Ġrecommender": 41564, + "Ġflesh": 41565, + "pitch": 41566, + "inist": 41567, + "Ġbtitle": 41568, + "Ġlc": 41569, + "assigned": 41570, + "ĠAdvisory": 41571, + "ĠGeneva": 41572, + "weighting": 41573, + "ĠABTS": 41574, + "Ġhexagon": 41575, + "ovskite": 41576, + "ĠAPIs": 41577, + "Ġbolometric": 41578, + "Ġmultifaceted": 41579, + "iak": 41580, + "Ġtn": 41581, + "ĠBibli": 41582, + "prosy": 41583, + "ĠJama": 41584, + "Ġinfrastructures": 41585, + "ĠShare": 41586, + "Ġintrogression": 41587, + "transforms": 41588, + "Report": 41589, + "ĠTRANS": 41590, + "ĠEXP": 41591, + "ĠCBT": 41592, + "ĠUbiquitin": 41593, + "ĠThickness": 41594, + "pub": 41595, + "toxin": 41596, + "ĠFriction": 41597, + "ĠLAG": 41598, + "mails": 41599, + "ĠImmediately": 41600, + "Ġweakest": 41601, + "ĠMRS": 41602, + "Ġcalcareous": 41603, + "bath": 41604, + "Ġcg": 41605, + "urational": 41606, + "tero": 41607, + "ĠInoue": 41608, + "Ġinstructor": 41609, + "acceptor": 41610, + "ĠEvolving": 41611, + "ĠLuther": 41612, + "Ġresigned": 41613, + "ĠChond": 41614, + "ERF": 41615, + "Ġselector": 41616, + "Ġnewspapers": 41617, + "GRA": 41618, + "Spe": 41619, + "VH": 41620, + "rA": 41621, + "otine": 41622, + "ĠFACT": 41623, + "composition": 41624, + "riding": 41625, + "PCM": 41626, + "Ġmiddleware": 41627, + "ĠGRP": 41628, + "Phosph": 41629, + "ĠEPIC": 41630, + "speech": 41631, + "vortex": 41632, + "ĠHerschel": 41633, + "amis": 41634, + "otube": 41635, + "ĠGomez": 41636, + "comyc": 41637, + "ĠPhyto": 41638, + "ĠConserv": 41639, + "Ġcava": 41640, + "arrhyth": 41641, + "ĠRestricted": 41642, + "ilicity": 41643, + "ogap": 41644, + "CTP": 41645, + "ĠLatino": 41646, + "attenuated": 41647, + "mobility": 41648, + "anen": 41649, + "Ġnif": 41650, + "ĠVideos": 41651, + "ĠSchubert": 41652, + "Features": 41653, + "opropanol": 41654, + "ĠThirdly": 41655, + "atula": 41656, + "ĠCemetery": 41657, + "entist": 41658, + "Ġdeli": 41659, + "trials": 41660, + "Ġgranulation": 41661, + "TTG": 41662, + "Ġteleost": 41663, + "morill": 41664, + "orse": 41665, + "otypically": 41666, + "ĠAbility": 41667, + "Amino": 41668, + "aqueous": 41669, + "ĠpCO": 41670, + "econ": 41671, + "ĠLign": 41672, + "essels": 41673, + "Ġformulating": 41674, + "ĠToo": 41675, + "ĠTranslational": 41676, + "ourses": 41677, + "ubiquit": 41678, + "statistic": 41679, + "Ġbursting": 41680, + "Ġestuaries": 41681, + "ĠNanocom": 41682, + "Ġexports": 41683, + "Ġê": 41684, + "contaminated": 41685, + "Ġtubing": 41686, + "Ġautomobile": 41687, + "Ġmissile": 41688, + "Ġhierarchically": 41689, + "Ġrepairs": 41690, + "ĠImproves": 41691, + "ĠEFFECTS": 41692, + "QDs": 41693, + "roz": 41694, + "aric": 41695, + "Ġparsed": 41696, + "ĠBrink": 41697, + "Ġprogressing": 41698, + "ĠpermNeigh": 41699, + "Agg": 41700, + "ZX": 41701, + "sink": 41702, + "Ġwise": 41703, + "etence": 41704, + "ĠIc": 41705, + "loo": 41706, + "meida": 41707, + "Ġpolariton": 41708, + "ĠConnections": 41709, + "Ġhallmarks": 41710, + "Longrightarrow": 41711, + "Ġtheater": 41712, + "esar": 41713, + "Ġreimburs": 41714, + "Ġlogo": 41715, + "Ġexcreted": 41716, + "ĠNoisy": 41717, + "Ġleaks": 41718, + "ĠDaph": 41719, + "Ġparagraphs": 41720, + "Ġlandslides": 41721, + "Ġpreclude": 41722, + "Ġcoercivity": 41723, + "ĠBurkholderia": 41724, + "ĠGómez": 41725, + "price": 41726, + "Theory": 41727, + "surgery": 41728, + "fname": 41729, + "failure": 41730, + "liness": 41731, + "refer": 41732, + "rique": 41733, + "ĠDogs": 41734, + "ĠEUV": 41735, + "ĠVapor": 41736, + "CSR": 41737, + "Biggl": 41738, + "Constraint": 41739, + "gravitational": 41740, + "Ġcombinatorics": 41741, + "Ġarticulated": 41742, + "ĠBaxter": 41743, + "ĠUltrasonic": 41744, + "LTE": 41745, + "lop": 41746, + "Ġinteratomic": 41747, + "intuitive": 41748, + "simplex": 41749, + "Ġexperimented": 41750, + "organizing": 41751, + "ĠOsaka": 41752, + "hadron": 41753, + "Ġdendrimers": 41754, + "ĠElsevier": 41755, + "CIP": 41756, + "ĠBAP": 41757, + "ĠAlonso": 41758, + "artet": 41759, + "antis": 41760, + "Ġextracorporeal": 41761, + "Ġpowdered": 41762, + "ĠSettings": 41763, + "etallic": 41764, + "ĠTEC": 41765, + "ĠArena": 41766, + "Ġanod": 41767, + "ĠReagents": 41768, + "licenses": 41769, + "ĠRemove": 41770, + "Ġpronunciation": 41771, + "thinspace": 41772, + "ĠClinically": 41773, + "gative": 41774, + "Ġwage": 41775, + "ĠHap": 41776, + "ĠGrac": 41777, + "fft": 41778, + "Ġformate": 41779, + "infeld": 41780, + "ĠQuin": 41781, + "Ġglomerul": 41782, + "Way": 41783, + "Ġkills": 41784, + "Ġtransversely": 41785, + "variation": 41786, + "ennas": 41787, + "ĠPLL": 41788, + "Ġinstrumented": 41789, + "ĠSpark": 41790, + "Ġpillar": 41791, + "ĠCaval": 41792, + "aders": 41793, + "issen": 41794, + "scene": 41795, + "otherm": 41796, + "ées": 41797, + "Ġpracticing": 41798, + "ĠBMSCs": 41799, + "ĠFernandez": 41800, + "Ġbeside": 41801, + "few": 41802, + "ĠCru": 41803, + "Ġprod": 41804, + "anders": 41805, + "azoline": 41806, + "Ġlegislative": 41807, + "balances": 41808, + "URL": 41809, + "Ġstereotactic": 41810, + "Ġtribes": 41811, + "Ġá¹¼": 41812, + "ĠPANI": 41813, + "adreno": 41814, + "gotten": 41815, + "cranial": 41816, + "ĠMick": 41817, + "ĠMMC": 41818, + "adiazole": 41819, + "entiation": 41820, + "ĠGln": 41821, + "ĠHolstein": 41822, + "ĠExplorer": 41823, + "Ġosse": 41824, + "arthy": 41825, + "ĠEVALU": 41826, + "adrenaline": 41827, + "JJ": 41828, + "ĠCMA": 41829, + "ĠInactivation": 41830, + "ABS": 41831, + "ĠSTZ": 41832, + "Configuration": 41833, + "ĠOlfactory": 41834, + "ĠSulfur": 41835, + "symbols": 41836, + "ĠASTM": 41837, + "divergence": 41838, + "ĠOCR": 41839, + "medical": 41840, + "Ġviewer": 41841, + "Ġbombardment": 41842, + "fair": 41843, + "nice": 41844, + "elberg": 41845, + "ĠGPT": 41846, + "ĠKow": 41847, + "Ġphotosphere": 41848, + "Ġlabile": 41849, + "ĠShang": 41850, + "Ġhomotopic": 41851, + "SVD": 41852, + "becomes": 41853, + "Ġgonor": 41854, + "Ġdeuteron": 41855, + "Ġphylogenies": 41856, + "ĠSAF": 41857, + "rapment": 41858, + "ĠCHF": 41859, + "Plan": 41860, + "ĠLegal": 41861, + "ĠFredholm": 41862, + "Ġsharper": 41863, + "Ġnanorib": 41864, + "ĠBuffalo": 41865, + "BMD": 41866, + "Ġlg": 41867, + "osup": 41868, + "ĠOPC": 41869, + "Ġendophytic": 41870, + "ATR": 41871, + "ĠExpressions": 41872, + "ĠMusical": 41873, + "Introduction": 41874, + "ĠSLM": 41875, + "çois": 41876, + "oglycos": 41877, + "aglia": 41878, + "mussen": 41879, + "formans": 41880, + "Ġsubstructures": 41881, + "ympan": 41882, + "hae": 41883, + "shi": 41884, + "ĠInterpret": 41885, + "Ġcatabolic": 41886, + "Ġoccupations": 41887, + "ĠBifurc": 41888, + "Hydroxy": 41889, + "ĠKauf": 41890, + "sleep": 41891, + "amas": 41892, + "ĠSf": 41893, + "ĠMBP": 41894, + "Ġnonalcoholic": 41895, + "Ġdiscordant": 41896, + "Ġepigen": 41897, + "PRI": 41898, + "ĠRedshift": 41899, + "warn": 41900, + "Ġlaptop": 41901, + "Ġabrasive": 41902, + "îĤĿ": 41903, + "lh": 41904, + "ĠKnee": 41905, + "Ġscrat": 41906, + "Ġpoloidal": 41907, + "ĠUniv": 41908, + "omyosin": 41909, + "ĠAugmented": 41910, + "Ġtaxonom": 41911, + "ZrO": 41912, + "Ġphytochemicals": 41913, + "iten": 41914, + "ĠPatterson": 41915, + "thym": 41916, + "dihydropy": 41917, + "Ġky": 41918, + "ĠMetazoa": 41919, + "ALLY": 41920, + "Ġretinoblastoma": 41921, + "concatenate": 41922, + "Male": 41923, + "Ġomission": 41924, + "icher": 41925, + "ĠAzer": 41926, + "opp": 41927, + "pleasant": 41928, + "ningham": 41929, + "Ġaxially": 41930, + "HDFS": 41931, + "Ġfictional": 41932, + "Ï«": 41933, + "Ġnarc": 41934, + "Ġundertook": 41935, + "Ġmicrocirc": 41936, + "ONLY": 41937, + "IVER": 41938, + "ĠCycles": 41939, + "Meas": 41940, + "ĠGriffin": 41941, + "ĠPliocene": 41942, + "ĠpI": 41943, + "ĠAviation": 41944, + "ĠCategorical": 41945, + "ĠNils": 41946, + "Ġresidency": 41947, + "ĠLaur": 41948, + "Ġprefers": 41949, + "ĠassertEquals": 41950, + "Ġliquor": 41951, + "dM": 41952, + "osperm": 41953, + "ĠFUT": 41954, + "AlO": 41955, + "Ġcytometer": 41956, + "Ġstabilizers": 41957, + "Ġpremium": 41958, + "Serial": 41959, + "ĠWalking": 41960, + "íķľ": 41961, + "Ġconfronted": 41962, + "encapsulated": 41963, + "Card": 41964, + "ĠSeeds": 41965, + "abular": 41966, + "ukov": 41967, + "Listener": 41968, + "Choose": 41969, + "ĠSjö": 41970, + "Make": 41971, + "Ġisoc": 41972, + "amount": 41973, + "ATC": 41974, + "ija": 41975, + "Ġsulcus": 41976, + "ĠMöbius": 41977, + "ĠPrenatal": 41978, + "Ġß": 41979, + "Ġisochron": 41980, + "Ġbeans": 41981, + "ĠDens": 41982, + "ĠWelling": 41983, + "ĠOman": 41984, + "Stats": 41985, + "ĠValid": 41986, + "ĠReward": 41987, + "GK": 41988, + "Ġâ©": 41989, + "Ġelectroporation": 41990, + "ĠSNRs": 41991, + "Ġgarlic": 41992, + "ĠParticipant": 41993, + "ĠSplitting": 41994, + "ĠMeteorological": 41995, + "morillonite": 41996, + "Ġoedema": 41997, + "ĠDots": 41998, + "ĠClare": 41999, + "Ġstarter": 42000, + "Ġclimatology": 42001, + "Ġcommence": 42002, + "Ġfallen": 42003, + "ĠAuNPs": 42004, + "attrs": 42005, + "Ġconsultant": 42006, + "twisted": 42007, + "Solving": 42008, + "Ġcoercive": 42009, + "ë¡ľ": 42010, + "Kar": 42011, + "Ġstit": 42012, + "ĠSSB": 42013, + "ĠIW": 42014, + "Ġcanvas": 42015, + "pyruvate": 42016, + "methylsulfanyl": 42017, + "Ġastrophysics": 42018, + "ĠTraditionally": 42019, + "Ġexcitonic": 42020, + "wear": 42021, + "ĠTin": 42022, + "rosh": 42023, + "ĠClient": 42024, + "ĠCorrections": 42025, + "ĠPopular": 42026, + "ĠLiquids": 42027, + "finder": 42028, + "Ġstran": 42029, + "pline": 42030, + "orella": 42031, + "Ġincur": 42032, + "Ġarche": 42033, + "Ġmedically": 42034, + "Mur": 42035, + "peter": 42036, + "Ġbeverage": 42037, + "ĠNWs": 42038, + "Ġfolic": 42039, + "Ġspeculative": 42040, + "Ġñ": 42041, + "Ġribbons": 42042, + "ĠPriest": 42043, + "Quanti": 42044, + "Ġundisturbed": 42045, + "atche": 42046, + "assi": 42047, + "ĠPerforming": 42048, + "ĠElong": 42049, + "Ġmatchings": 42050, + "Ġfranchise": 42051, + "gio": 42052, + "ĠSarg": 42053, + "Ġaboard": 42054, + "cyclodextrin": 42055, + "ĠTHER": 42056, + "Ġsaturate": 42057, + "ĠKinematics": 42058, + "Ġpeptidoglycan": 42059, + "ĠShelf": 42060, + "tocopherol": 42061, + "Bottom": 42062, + "mith": 42063, + "rdx": 42064, + "zos": 42065, + "ĠtRNAs": 42066, + "utf": 42067, + "ENA": 42068, + "Ġlesson": 42069, + "Ġpolaron": 42070, + "braska": 42071, + "Ġathletic": 42072, + "Ġscrambled": 42073, + "Ġpursuing": 42074, + "Ġbodily": 42075, + "Ġcac": 42076, + "imen": 42077, + "ĠIκB": 42078, + "ĠRö": 42079, + "ĠRFC": 42080, + "ĠLPC": 42081, + "ĠiÏī": 42082, + "Ġdiary": 42083, + "Ġqueueing": 42084, + "ĠDivergence": 42085, + "ĠShigella": 42086, + "ĠUltrastruct": 42087, + "Ġtriphosphate": 42088, + "ĠImplant": 42089, + "Ġferrous": 42090, + "ĠBurton": 42091, + "ĠHertz": 42092, + "fabric": 42093, + "turing": 42094, + "ĠSSM": 42095, + "ograd": 42096, + "Ġmetazo": 42097, + "Chang": 42098, + "Ġadipogenesis": 42099, + "Ġnuisance": 42100, + "Ġanonymity": 42101, + "Ġrefrigerator": 42102, + "ìľ": 42103, + "oction": 42104, + "Ġsparing": 42105, + "Ġchalcogen": 42106, + "Ġobservatory": 42107, + "Ġbooster": 42108, + "ĠAndré": 42109, + "ĠSTO": 42110, + "yryl": 42111, + "ĠEDX": 42112, + "ĠDenver": 42113, + "Ġhomogenate": 42114, + "Callback": 42115, + "aC": 42116, + "hours": 42117, + "kova": 42118, + "ĠAUD": 42119, + "Ġspare": 42120, + "Ġpartons": 42121, + "ĠIntake": 42122, + "Ġrecognizable": 42123, + "ĠGoldstein": 42124, + "Ġstrikingly": 42125, + "Ġsanitation": 42126, + "Finder": 42127, + "Generation": 42128, + "boy": 42129, + "tam": 42130, + "ĠRPM": 42131, + "ivious": 42132, + "ylak": 42133, + "ophiles": 42134, + "Ġpriest": 42135, + "Ġeasiest": 42136, + "Ġdeliveries": 42137, + "Elmer": 42138, + "Ġzirconium": 42139, + "ĠMishra": 42140, + "ĠâĶ": 42141, + "ĠWDM": 42142, + "Ġperid": 42143, + "ĠZT": 42144, + "Ġlocalizes": 42145, + "ĠORs": 42146, + "ĠIDO": 42147, + "Ġpleasant": 42148, + "ĠMWCNTs": 42149, + "ĠJimmy": 42150, + "ĠYeh": 42151, + "gathered": 42152, + "kil": 42153, + "ĠKappa": 42154, + "Ġcartoon": 42155, + "ĠHeuristic": 42156, + "Ġsz": 42157, + "Ġorifice": 42158, + "Ġmannit": 42159, + "ĠCOMM": 42160, + "ICK": 42161, + "Ġfarmer": 42162, + "ĠSilencing": 42163, + "Ġprefixes": 42164, + "qc": 42165, + "ineurin": 42166, + "Ġinflated": 42167, + "ĠRez": 42168, + "Ġhydrodynamical": 42169, + "Ġoscillate": 42170, + "Ġpedestrians": 42171, + "ĠAngiotensin": 42172, + "ĠViscosity": 42173, + "Ġoligodendrocytes": 42174, + "Ġparotid": 42175, + "Layout": 42176, + "rageenan": 42177, + "Ġè": 42178, + "ĠmN": 42179, + "Ġdozen": 42180, + "exclusion": 42181, + "Ġpanic": 42182, + "ĠPDI": 42183, + "Ġtwentieth": 42184, + "ĠElectroph": 42185, + "Ġmicrobiology": 42186, + "Server": 42187, + "ĠParticipation": 42188, + "DET": 42189, + "Poss": 42190, + "ĠNemat": 42191, + "ĠNRF": 42192, + "arguments": 42193, + "Ġamylase": 42194, + "Ġargv": 42195, + "Ġresolves": 42196, + "Ġrevisions": 42197, + "Packet": 42198, + "Tools": 42199, + "YE": 42200, + "Ġtire": 42201, + "induction": 42202, + "asive": 42203, + "tonic": 42204, + "ém": 42205, + "carrying": 42206, + "ĠImmunoblot": 42207, + "ĠIPF": 42208, + "Ġdeteriorated": 42209, + "Ġjurisdiction": 42210, + "released": 42211, + "osmotic": 42212, + "Ġrestaurants": 42213, + "ï¸": 42214, + "ĠNm": 42215, + "Ġflips": 42216, + "Ġseparability": 42217, + "ĠRecursive": 42218, + "Ġpasture": 42219, + "ĠÄī": 42220, + "Ġblastocyst": 42221, + "MCP": 42222, + "Tb": 42223, + "uene": 42224, + "esulf": 42225, + "essim": 42226, + "Ġhen": 42227, + "ĠKull": 42228, + "ylum": 42229, + "arev": 42230, + "uests": 42231, + "ĠZip": 42232, + "Ġboats": 42233, + "Command": 42234, + "Continu": 42235, + "ĠBogoliubov": 42236, + "Ġmannitol": 42237, + "Know": 42238, + "г": 42239, + "ĠHack": 42240, + "Ġmassively": 42241, + "ĠAlloys": 42242, + "ĠCDP": 42243, + "ĠStereo": 42244, + "ĠElectrode": 42245, + "Ġisoflav": 42246, + "Ġinteroperability": 42247, + "ĠAdelaide": 42248, + "ĠPPD": 42249, + "ĠKou": 42250, + "Ġdiap": 42251, + "Ġconserve": 42252, + "Ġaggregating": 42253, + "Gluc": 42254, + "Ġîģ": 42255, + "Ġgust": 42256, + "ĠLeb": 42257, + "ETIC": 42258, + "ĠConsol": 42259, + "ĠMorita": 42260, + "Relative": 42261, + "Ġpaleo": 42262, + "Ġwitnesses": 42263, + "ĠLauren": 42264, + "azepine": 42265, + "ĠTY": 42266, + "ĠIdi": 42267, + "ĠMent": 42268, + "ĠRCA": 42269, + "igenin": 42270, + "ĠDefence": 42271, + "Ġpyrimidine": 42272, + "ĠTiN": 42273, + "Ġendothelin": 42274, + "Ġpandas": 42275, + "Ġswallowing": 42276, + "Ġcongestive": 42277, + "Ġvinc": 42278, + "ĠDIP": 42279, + "ĠHough": 42280, + "Ġzw": 42281, + "ĠKimura": 42282, + "representations": 42283, + "ĠPromote": 42284, + "ĠTerry": 42285, + "Ġhatched": 42286, + "lookup": 42287, + "Electron": 42288, + "Ġdormancy": 42289, + "Ġresign": 42290, + "Ġvaluations": 42291, + "Ġmakeup": 42292, + "ĠAmy": 42293, + "CLUD": 42294, + "SEP": 42295, + "tubule": 42296, + "Ġsoldier": 42297, + "ĠTz": 42298, + "ĠTrump": 42299, + "ĠKramer": 42300, + "coni": 42301, + "Ġengraft": 42302, + "Ġvacuole": 42303, + "Ġreplicating": 42304, + "itonitis": 42305, + "ĠBacteri": 42306, + "vaccinated": 42307, + "olt": 42308, + "ĠAhn": 42309, + "Ġanem": 42310, + "ĠBIT": 42311, + "geo": 42312, + "Ġmicrogravity": 42313, + "ĠShir": 42314, + "ĠWorldwide": 42315, + "Ġignor": 42316, + "ĠËĩ": 42317, + "Ġlubrication": 42318, + "java": 42319, + "vt": 42320, + "Ġyl": 42321, + "Ġhills": 42322, + "ĠFOL": 42323, + "Ġbasaltic": 42324, + "Neill": 42325, + "ĠEthiopian": 42326, + "ĠNOTCH": 42327, + "ĠMOSFET": 42328, + "leaving": 42329, + "ĠPter": 42330, + "ĠWeld": 42331, + "aple": 42332, + "Ġsandwic": 42333, + "Ġazide": 42334, + "ĠStimuli": 42335, + "Ġlizard": 42336, + "ĠCinc": 42337, + "ĠHain": 42338, + "icals": 42339, + "Ġcontacting": 42340, + "ĠMarx": 42341, + "Ġpsychotherapy": 42342, + "ĠRetin": 42343, + "Ġcatheterization": 42344, + "ĠNanoparticle": 42345, + "ĠTCC": 42346, + "vermectin": 42347, + "ĠBASE": 42348, + "Ġnotor": 42349, + "Ġelectronically": 42350, + "steroid": 42351, + "Ġbilaterally": 42352, + "Ġnephritis": 42353, + "Ġirritation": 42354, + "ĠProlonged": 42355, + "Your": 42356, + "heuristic": 42357, + "urgeon": 42358, + "Ġleftmost": 42359, + "ĠREVIEW": 42360, + "Ġgastrectomy": 42361, + "ENTIAL": 42362, + "Means": 42363, + "ĠDyson": 42364, + "Ġbrands": 42365, + "yields": 42366, + "mercapto": 42367, + "rub": 42368, + "ouncement": 42369, + "errno": 42370, + "Ġviewers": 42371, + "butan": 42372, + "ĠMalay": 42373, + "ylindrical": 42374, + "Ġprominently": 42375, + "Ġescaping": 42376, + "Ġquerying": 42377, + "Storage": 42378, + "Fos": 42379, + "codec": 42380, + "ĠcM": 42381, + "strates": 42382, + "glove": 42383, + "ĠTrajectories": 42384, + "Ġsterol": 42385, + "Ġhematopoiesis": 42386, + "Ġcuprates": 42387, + "Ok": 42388, + "daily": 42389, + "ĠBIO": 42390, + "ĠLICENSE": 42391, + "ellations": 42392, + "assy": 42393, + "SURE": 42394, + "Ġepinephrine": 42395, + "Ġdownwards": 42396, + "corner": 42397, + "assertTrue": 42398, + "Ġáºij": 42399, + "ĠSouza": 42400, + "MAG": 42401, + "porph": 42402, + "Ġeffluents": 42403, + "loem": 42404, + "oaddition": 42405, + "obutyl": 42406, + "elestial": 42407, + "Fem": 42408, + "mpi": 42409, + "ĠRs": 42410, + "ellates": 42411, + "ĠKag": 42412, + "Ġuncoupled": 42413, + "Ġlegumes": 42414, + "Ġomitting": 42415, + "û": 42416, + "ĠTABLE": 42417, + "haled": 42418, + "ĠÅģ": 42419, + "Ġmisfit": 42420, + "Ġmolars": 42421, + "otechnological": 42422, + "Markov": 42423, + "Ġpraised": 42424, + "ĠDab": 42425, + "ĠVij": 42426, + "entilation": 42427, + "ĠChatter": 42428, + "Ġboiled": 42429, + "Ġcatches": 42430, + "annotation": 42431, + "Signal": 42432, + "Ġleverages": 42433, + "Ġadvisory": 42434, + "song": 42435, + "ondition": 42436, + "Ġfug": 42437, + "raps": 42438, + "ĠMCD": 42439, + "particip": 42440, + "obian": 42441, + "Ġcounsel": 42442, + "ĠPRP": 42443, + "ediol": 42444, + "ĠŨ": 42445, + "Ġbruce": 42446, + "Shanghai": 42447, + "DataFrame": 42448, + "ĠCorrespondingly": 42449, + "Ġacrylamide": 42450, + "fellow": 42451, + "lob": 42452, + "igt": 42453, + "ĠCrystallization": 42454, + "Ġindomethacin": 42455, + "ĠPDR": 42456, + "giate": 42457, + "ĠPanels": 42458, + "complexes": 42459, + "ĠNicol": 42460, + "Ġfoliar": 42461, + "cubic": 42462, + "ĠdE": 42463, + "ĠCCM": 42464, + "plating": 42465, + "Ġresistors": 42466, + "ĠGaz": 42467, + "Ġoverturn": 42468, + "Ġrepress": 42469, + "Ġpots": 42470, + "ĠPIK": 42471, + "Ġdermis": 42472, + "Represent": 42473, + "ĠAndersson": 42474, + "Ġretrotranspos": 42475, + "ASA": 42476, + "Counter": 42477, + "Tet": 42478, + "imin": 42479, + "performed": 42480, + "ĠNept": 42481, + "Ġheel": 42482, + "rold": 42483, + "aires": 42484, + "Ġreadability": 42485, + "Ġbenefited": 42486, + "Ġpulsation": 42487, + "ĠBalancing": 42488, + "Ġevaporator": 42489, + "Ġeigens": 42490, + "ĠHospit": 42491, + "Ġtrails": 42492, + "ĠCoordinate": 42493, + "accase": 42494, + "ĠHRMS": 42495, + "signaling": 42496, + "ĠNPY": 42497, + "Ġameliorated": 42498, + "tuples": 42499, + "Ġmetasurface": 42500, + "ĠFrancesco": 42501, + "Ġspoof": 42502, + "îŸ": 42503, + "Fu": 42504, + "JK": 42505, + "ej": 42506, + "Ġgoss": 42507, + "ĠHim": 42508, + "Ġprioritized": 42509, + "Ġmesothelioma": 42510, + "idxs": 42511, + "Ġreconnaissance": 42512, + "Ġlamps": 42513, + "ãĢĤ": 42514, + "Ġreformulation": 42515, + "Ġdelirium": 42516, + "ĠNPR": 42517, + "ĠGamb": 42518, + "illas": 42519, + "-----": 42520, + "Ġdrilled": 42521, + "ĠGenotyping": 42522, + "ĠBlank": 42523, + "Ġpropeller": 42524, + "Ġcereals": 42525, + "ĠAirborne": 42526, + "ĠPhotocatalytic": 42527, + "ĠCavity": 42528, + "Ġdolphins": 42529, + "ĠsgRNA": 42530, + "understood": 42531, + "eous": 42532, + "Ġsax": 42533, + "olayer": 42534, + "ĠPend": 42535, + "ĠGET": 42536, + "cled": 42537, + "Ġü": 42538, + "Ġcytosine": 42539, + "Ġparallelization": 42540, + "MMs": 42541, + "ĠOrganisation": 42542, + "Models": 42543, + "Ġaccommodated": 42544, + "Ġcholest": 42545, + "Ġinactivity": 42546, + "ĠBoss": 42547, + "ĠGCS": 42548, + "Ġsoaked": 42549, + "ĠSecreted": 42550, + "Ġvacuolar": 42551, + "zoan": 42552, + "ĠGreene": 42553, + "Ġbolt": 42554, + "bj": 42555, + "ĠTall": 42556, + "Ġstor": 42557, + "ĠMob": 42558, + "Ġblurred": 42559, + "INO": 42560, + "ĠMetallic": 42561, + "uffs": 42562, + "ĠNOTE": 42563, + "Ġsonicated": 42564, + "obutyric": 42565, + "ĠtDCS": 42566, + "ĠNes": 42567, + "ospir": 42568, + "weigh": 42569, + "ĠRegulator": 42570, + "Ġhemolysis": 42571, + "Ġsounding": 42572, + "Ġcruciate": 42573, + "Ġcapsaicin": 42574, + "ĠTyrosine": 42575, + "ĠTell": 42576, + "ĠPEP": 42577, + "ĠRc": 42578, + "ĠEating": 42579, + "ĠGoals": 42580, + "uret": 42581, + "Ġearn": 42582, + "Ġcolleges": 42583, + "Ġchemoattract": 42584, + "Ġỹ": 42585, + "ĠEchocardi": 42586, + "Fort": 42587, + "sodium": 42588, + "amined": 42589, + "ĠNPP": 42590, + "ĠKalu": 42591, + "Ġdecipher": 42592, + "tetramethyl": 42593, + "ĠOPN": 42594, + "straight": 42595, + "Ġtempered": 42596, + "ĠHindu": 42597, + "ĠOrdinary": 42598, + "ĠAChE": 42599, + "JNK": 42600, + "fos": 42601, + "vcpu": 42602, + "enamide": 42603, + "ĠCrack": 42604, + "apical": 42605, + "Ġantiserum": 42606, + "triplet": 42607, + "decision": 42608, + "Ġcancels": 42609, + "ĠPMN": 42610, + "Ġporphy": 42611, + "ĠDSA": 42612, + "Ġsubmatrix": 42613, + "Ġjas": 42614, + "Ġreptiles": 42615, + "ĠSoon": 42616, + "ĠStatistically": 42617, + "Ġleveraged": 42618, + "Williams": 42619, + "FLD": 42620, + "folk": 42621, + "Ġbang": 42622, + "ĠSCL": 42623, + "asses": 42624, + "Ġtendons": 42625, + "founded": 42626, + "ĠRicketts": 42627, + "inset": 42628, + "Ġspun": 42629, + "Ġunramified": 42630, + "Ġrape": 42631, + "ĠZZ": 42632, + "ĠNebula": 42633, + "Ġthrombotic": 42634, + "ĠBoron": 42635, + "ĠArgon": 42636, + "pooling": 42637, + "ĠMarginal": 42638, + "Ġfellowship": 42639, + "Ġerythropoietin": 42640, + "mathpzc": 42641, + "xL": 42642, + "ĠSik": 42643, + "ĠBayer": 42644, + "Ġoverdose": 42645, + "ĠCOI": 42646, + "ĠLesions": 42647, + "ĠCompetitive": 42648, + "ĠODEs": 42649, + "wrap": 42650, + "achlor": 42651, + "Ġsubordinate": 42652, + "ĠYBa": 42653, + "headed": 42654, + "Ġgrasses": 42655, + "Ġbirational": 42656, + "ĠJeffrey": 42657, + "Ġmolding": 42658, + "Ġlidocaine": 42659, + "ĠFOXO": 42660, + "terminated": 42661, + "ĠâĩIJâĩĴ": 42662, + "ĠMEL": 42663, + "ticulum": 42664, + "Ġré": 42665, + "Ġclaud": 42666, + "Ġjamming": 42667, + "Static": 42668, + "Ġtributary": 42669, + "atet": 42670, + "edonia": 42671, + "ĠCMP": 42672, + "ĠVN": 42673, + "represents": 42674, + "SOURCE": 42675, + "uckland": 42676, + "ĠInterests": 42677, + "ĠNanoscale": 42678, + "oconjug": 42679, + "Ġcatalogues": 42680, + "ĠActinobacteria": 42681, + "Fixed": 42682, + "basal": 42683, + "Ġantiparallel": 42684, + "Ġconfusing": 42685, + "Ġmarkings": 42686, + "Ġdistinctions": 42687, + "ĠHua": 42688, + "ĠWatts": 42689, + "Ġnanofluid": 42690, + "Ġdiffractometer": 42691, + "Later": 42692, + "migration": 42693, + "Ġcoplanar": 42694, + "Ġhypomethyl": 42695, + "PDS": 42696, + "SOs": 42697, + "Correspond": 42698, + "Ġelucidating": 42699, + "IZED": 42700, + "EVs": 42701, + "gart": 42702, + "mTc": 42703, + "ĠTUR": 42704, + "uracies": 42705, + "Ġfollower": 42706, + "Ġhaze": 42707, + "OUTPUT": 42708, + "ĠMyeloid": 42709, + "BUFFER": 42710, + "Camp": 42711, + "anim": 42712, + "ĠTES": 42713, + "ĠCricket": 42714, + "ĠPaired": 42715, + "ĠPAGE": 42716, + "ĠBid": 42717, + "Ġyrs": 42718, + "Ġendow": 42719, + "ircase": 42720, + "ĠSupported": 42721, + "Ġleaflet": 42722, + "ĠPromoter": 42723, + "Ġconvinced": 42724, + "liers": 42725, + "hera": 42726, + "Ġseller": 42727, + "agreement": 42728, + "Ġunary": 42729, + "onstructed": 42730, + "Ġrestrained": 42731, + "ĠPetersen": 42732, + "Analog": 42733, + "Ġexacerbations": 42734, + "Ġperforated": 42735, + "tids": 42736, + "ĠMSH": 42737, + "oui": 42738, + "ĠCori": 42739, + "ĠCruc": 42740, + "Ġfracturing": 42741, + "Ġinfertile": 42742, + "ĠPROBLEM": 42743, + "ĠFriedmann": 42744, + "Ġspectrophotometry": 42745, + "ERGY": 42746, + "otus": 42747, + "proposed": 42748, + "ĠMoisture": 42749, + "ĠNoether": 42750, + "ĠLaunch": 42751, + "ĠLearn": 42752, + "Ġvena": 42753, + "Ġfasci": 42754, + "Ġquiescence": 42755, + "ĠPrand": 42756, + "ĠConvert": 42757, + "Ġtriage": 42758, + "ANE": 42759, + "Ġfeedstock": 42760, + "ĠdBm": 42761, + "Ġneoformans": 42762, + "GSE": 42763, + "ĠAPE": 42764, + "Ġcardiometabolic": 42765, + "Ġmagnetometer": 42766, + "Environment": 42767, + "Ġfulfills": 42768, + "ĠManganese": 42769, + "BMP": 42770, + "ĠRatios": 42771, + "istable": 42772, + "assume": 42773, + "Ġrespected": 42774, + "Ġscars": 42775, + "Ġsupporters": 42776, + "ĠAugmentation": 42777, + "Ġglycosylated": 42778, + "ĠUltrafast": 42779, + "Ġdemethylation": 42780, + "metastatic": 42781, + "cylinder": 42782, + "Ġhang": 42783, + "ĠMAV": 42784, + "disjoint": 42785, + "pharose": 42786, + "ĠLebanon": 42787, + "PIs": 42788, + "labeling": 42789, + "Ġneutralino": 42790, + "ĠSOCS": 42791, + "xcb": 42792, + "ĠTerritory": 42793, + "ĠPolicies": 42794, + "King": 42795, + "Ġallied": 42796, + "Ġsaturates": 42797, + "muscle": 42798, + "odimers": 42799, + "Ġbt": 42800, + "ĠHang": 42801, + "ĠEb": 42802, + "Ġchimer": 42803, + "Ġnotational": 42804, + "Ġcolder": 42805, + "ultz": 42806, + "transverse": 42807, + "HOUT": 42808, + "ĠKarls": 42809, + "torsion": 42810, + "JI": 42811, + "Ġmari": 42812, + "emon": 42813, + "Ġlogarithmically": 42814, + "ĠâIJ¦": 42815, + "ìĿĦ": 42816, + "Ġaeration": 42817, + "Ġsoma": 42818, + "ĠSomal": 42819, + "Ġspoil": 42820, + "diver": 42821, + "Ġbreakpoints": 42822, + "ĠHarmon": 42823, + "Ġpharmacologic": 42824, + "ĠMosquito": 42825, + "ĠModifications": 42826, + "Ġadjo": 42827, + "ĠPapers": 42828, + "generally": 42829, + "ïĺ¹": 42830, + "TARGET": 42831, + "ĠPrix": 42832, + "ocaps": 42833, + "ĠEin": 42834, + "Ġmicrogrid": 42835, + "ĠInterplay": 42836, + "Ġcopying": 42837, + "Alpha": 42838, + "ĠSlope": 42839, + "ĠLipofectamine": 42840, + "highest": 42841, + "DRO": 42842, + "ĠHipp": 42843, + "Ġshaken": 42844, + "Ġunderline": 42845, + "Ġfilmed": 42846, + "maturity": 42847, + "icture": 42848, + "ILS": 42849, + "Span": 42850, + "Ġinverters": 42851, + "QUE": 42852, + "determining": 42853, + "Ġeosinophilic": 42854, + "DY": 42855, + "ĠLID": 42856, + "ĠGig": 42857, + "Ġintraepithelial": 42858, + "NbO": 42859, + "freedom": 42860, + "Ġassured": 42861, + "ĠArche": 42862, + "ĠSubstitution": 42863, + "ĠSrivastava": 42864, + "ĠMozamb": 42865, + "Ġaro": 42866, + "orc": 42867, + "ĠIbrahim": 42868, + "ĠDST": 42869, + "Ġabl": 42870, + "Ġxer": 42871, + "ountable": 42872, + "Ġlossless": 42873, + "Ġconcentrating": 42874, + "Ġstains": 42875, + "ĠSolve": 42876, + "continuity": 42877, + "ĠTorr": 42878, + "Ġpitfalls": 42879, + "bestos": 42880, + "Otherwise": 42881, + "adhyay": 42882, + "bard": 42883, + "ĠCAA": 42884, + "odetic": 42885, + "Ġasthmatic": 42886, + "Ġrationality": 42887, + "ĠYorkshire": 42888, + "neighborhood": 42889, + "Ġheroin": 42890, + "Ġscatterers": 42891, + "ĠHearing": 42892, + "ĠEFT": 42893, + "ĠNurses": 42894, + "ĠGLI": 42895, + "ĠZeta": 42896, + "ĠNeigh": 42897, + "Ġventure": 42898, + "Ġtoxicological": 42899, + "Ġrolls": 42900, + "fv": 42901, + "Ġcrick": 42902, + "ĠdÏī": 42903, + "avia": 42904, + "elder": 42905, + "Ġinvade": 42906, + "extracted": 42907, + "MLP": 42908, + "ĠPAI": 42909, + "ĠMellitus": 42910, + "Ġbrucei": 42911, + "gpio": 42912, + "emotional": 42913, + "ĠDale": 42914, + "ĠEz": 42915, + "Ġtransactivation": 42916, + "Ġquantiles": 42917, + "Ġnucleosynthesis": 42918, + "ĠAmel": 42919, + "Ġchromophore": 42920, + "Ġliterally": 42921, + "bandwidth": 42922, + "atohepatitis": 42923, + "Ġultrafiltration": 42924, + "Martin": 42925, + "Ġangioplasty": 42926, + "insertion": 42927, + "Dan": 42928, + "squeeze": 42929, + "usr": 42930, + "uconazole": 42931, + "ĠFAR": 42932, + "Ġshadows": 42933, + "Ġimitation": 42934, + "ĠKann": 42935, + "hesi": 42936, + "Ġmicellar": 42937, + "vester": 42938, + "ĠPerse": 42939, + "acetamol": 42940, + "GRAPH": 42941, + "ĠAIPS": 42942, + "Ġpromptly": 42943, + "anchor": 42944, + "Ġischaemia": 42945, + "pump": 42946, + "Ġmafic": 42947, + "Ġlazy": 42948, + "ĠCEL": 42949, + "ĠGorenstein": 42950, + "ĠWGS": 42951, + "Ġsignifies": 42952, + "Ġsplines": 42953, + "determination": 42954, + "Ġrelaying": 42955, + "piperazine": 42956, + "Ġsyncytial": 42957, + "ĠAub": 42958, + "ĠDX": 42959, + "Ġorthotopic": 42960, + "ĠLinkage": 42961, + "Ġharmony": 42962, + "ĠKazakh": 42963, + "ĠVladimir": 42964, + "Ġpray": 42965, + "imolar": 42966, + "Ġgrayscale": 42967, + "Ġanalyst": 42968, + "ĠTransl": 42969, + "Ġmeniscus": 42970, + "ĠMedica": 42971, + "osaurus": 42972, + "Ġв": 42973, + "Ġinfiltrated": 42974, + "Ġâĸ³": 42975, + "Ġsaccades": 42976, + "Ġdisentangle": 42977, + "Hart": 42978, + "fined": 42979, + "Ġbicycle": 42980, + "ository": 42981, + "unlikely": 42982, + "erephthal": 42983, + "ĠLia": 42984, + "Ġgroupings": 42985, + "Ġcategorize": 42986, + "Ġbiogeography": 42987, + "ĠAPPROACH": 42988, + "ĠNing": 42989, + "ĠGrap": 42990, + "versa": 42991, + "Ġradiologists": 42992, + "ĠRecording": 42993, + "Ġboiler": 42994, + "adders": 42995, + "Candid": 42996, + "MQ": 42997, + "Ġbw": 42998, + "ĠSector": 42999, + "ĠHIT": 43000, + "ĠESCC": 43001, + "essence": 43002, + "orean": 43003, + "estyles": 43004, + "SUCCESS": 43005, + "nein": 43006, + "ultra": 43007, + "ramp": 43008, + "Thomas": 43009, + "ĠPrepar": 43010, + "ĠInstitut": 43011, + "Ġherbicide": 43012, + "ĠChaotic": 43013, + "Ġsphincter": 43014, + "Ġcompactifications": 43015, + "Clear": 43016, + "Trp": 43017, + "Decoder": 43018, + "Ġsapphire": 43019, + "ĠIdaho": 43020, + "persing": 43021, + "chiral": 43022, + "ĠDischarge": 43023, + "Accordingly": 43024, + "ĠArthritis": 43025, + "ĠJaneiro": 43026, + "nj": 43027, + "ĠKd": 43028, + "Ġoutlets": 43029, + "Ġsusceptibilities": 43030, + "Ġdiverged": 43031, + "Ġroller": 43032, + "sufficient": 43033, + "clustering": 43034, + "ĠTehran": 43035, + "Ġtb": 43036, + "blank": 43037, + "Ġdigitally": 43038, + "Ġnecrotizing": 43039, + "FALSE": 43040, + "Ġwhor": 43041, + "errals": 43042, + "ĠMotivated": 43043, + "enzae": 43044, + "ĠRefinement": 43045, + "Ġticket": 43046, + "Ġprotrusions": 43047, + "ĠDonaldson": 43048, + "ĠBeth": 43049, + "Ġsputtered": 43050, + "Ġautocrine": 43051, + "copene": 43052, + "Ġcollar": 43053, + "Ġuppermost": 43054, + "Ġoxygenated": 43055, + "Intro": 43056, + "âĨIJ": 43057, + "ĠHippo": 43058, + "Ġdune": 43059, + "idines": 43060, + "ĠHä": 43061, + "Ġregi": 43062, + "Ġnois": 43063, + "Ġphotodiode": 43064, + "ĠFeb": 43065, + "mutated": 43066, + "ĠCFL": 43067, + "stepping": 43068, + "Selection": 43069, + "ĠWebster": 43070, + "ĠHERA": 43071, + "indicating": 43072, + "Ġtrainees": 43073, + "Rot": 43074, + "ĠFAK": 43075, + "ĠAsn": 43076, + "Ġfats": 43077, + "foliation": 43078, + "Ġarticulation": 43079, + "Ġcusps": 43080, + "ĠJennifer": 43081, + "Ġintimately": 43082, + "ĠPing": 43083, + "sov": 43084, + "oxious": 43085, + "hydrate": 43086, + "ĠArchives": 43087, + "Gonz": 43088, + "Ġé": 43089, + "Ġchl": 43090, + "ĠOLS": 43091, + "coph": 43092, + "Ġairline": 43093, + "Ġfoetal": 43094, + "ĠRolling": 43095, + "ĠGENERAL": 43096, + "ONAL": 43097, + "agons": 43098, + "ĠDorsal": 43099, + "Ġritual": 43100, + "butyrate": 43101, + "oglut": 43102, + "Ġhexa": 43103, + "ĠSyria": 43104, + "Ġontogeny": 43105, + "ĠFBG": 43106, + "coverage": 43107, + "Ġtachyon": 43108, + "ĠPermanent": 43109, + "lum": 43110, + "Ġsv": 43111, + "Ġoo": 43112, + "energetic": 43113, + "altitude": 43114, + "Inc": 43115, + "ĠNebraska": 43116, + "ĠRESP": 43117, + "Ġdysbiosis": 43118, + "Ġmarketed": 43119, + "oxicillin": 43120, + "ĠBroadcast": 43121, + "racyclo": 43122, + "ĠFifteen": 43123, + "ĠNarayan": 43124, + "Ġlettuce": 43125, + "orea": 43126, + "Ġintercepts": 43127, + "Ġworkstation": 43128, + "ĠPlains": 43129, + "CCL": 43130, + "Ġorientable": 43131, + "ĠBoosting": 43132, + "ĠSOI": 43133, + "ĠChecking": 43134, + "ĠFIFO": 43135, + "Ġinsets": 43136, + "ĠSRT": 43137, + "Ġacrom": 43138, + "owner": 43139, + "MIX": 43140, + "ĠArb": 43141, + "Ġfaeces": 43142, + "ĠCarlson": 43143, + "Ġperivascular": 43144, + "infiltrating": 43145, + "Ìħ": 43146, + "Ġmalle": 43147, + "ocate": 43148, + "ĠBold": 43149, + "unctive": 43150, + "excess": 43151, + "Ġloosen": 43152, + "Ġprioritization": 43153, + "Ġannotate": 43154, + "Ġgrammars": 43155, + "Ġbred": 43156, + "Ġexocytosis": 43157, + "ĠDahl": 43158, + "athyroidism": 43159, + "veli": 43160, + "Ġopted": 43161, + "Ġsmoked": 43162, + "ĠPlates": 43163, + "EMG": 43164, + "ROW": 43165, + "IFIC": 43166, + "OLS": 43167, + "oregulatory": 43168, + "Ġwhiskers": 43169, + "secretase": 43170, + "Ġexaggerated": 43171, + "ĠBib": 43172, + "deformed": 43173, + "Ġzur": 43174, + "ropine": 43175, + "Ġpairings": 43176, + "chromosome": 43177, + "Elements": 43178, + "priority": 43179, + "Ġlyophilized": 43180, + "ĠChaudh": 43181, + "Wilk": 43182, + "ĠCation": 43183, + "otta": 43184, + "Ġnonconvex": 43185, + "Ġdepolymer": 43186, + "MMARY": 43187, + "Controlled": 43188, + "carboxy": 43189, + "Ġaugmenting": 43190, + "Ġappointments": 43191, + "Ġtraversed": 43192, + "ĠFletcher": 43193, + "Ġexpiratory": 43194, + "Ġelephant": 43195, + "ĠBlocks": 43196, + "ĠFluids": 43197, + "walls": 43198, + "increased": 43199, + "propanamide": 43200, + "ĠAkaike": 43201, + "ĠCBM": 43202, + "ĠEcho": 43203, + "admissible": 43204, + "Ġdisassembly": 43205, + "ĠarXiv": 43206, + "icke": 43207, + "LIST": 43208, + "phenotype": 43209, + "ĠProvincial": 43210, + "legend": 43211, + "PAS": 43212, + "rnn": 43213, + "sand": 43214, + "Ġbariatric": 43215, + "ĠPush": 43216, + "ĠApoE": 43217, + "caprolactone": 43218, + "modeling": 43219, + "Ġŵ": 43220, + "Ġsupercapacitors": 43221, + "oron": 43222, + "ĠpK": 43223, + "strophy": 43224, + "ĠSuc": 43225, + "unda": 43226, + "team": 43227, + "Ġitiner": 43228, + "Ġswell": 43229, + "ĠBioactive": 43230, + "ĠIndicators": 43231, + "ĠIFT": 43232, + "ĠDK": 43233, + "Ġcapit": 43234, + "shapes": 43235, + "Ġtrachea": 43236, + "delayed": 43237, + "ĠGuangdong": 43238, + "Lepid": 43239, + "TGA": 43240, + "hk": 43241, + "olon": 43242, + "ogenin": 43243, + "ĠAck": 43244, + "Ġlogically": 43245, + "contributions": 43246, + "ĠCleavage": 43247, + "hurst": 43248, + "bdd": 43249, + "STD": 43250, + "ĠFut": 43251, + "tek": 43252, + "ĠInher": 43253, + "Ġchemis": 43254, + "Ġbreakpoint": 43255, + "estimates": 43256, + "ĠOttoman": 43257, + "ĠNafion": 43258, + "WIDTH": 43259, + "Ġsizable": 43260, + "ĠTsu": 43261, + "embolic": 43262, + "Ġrightmost": 43263, + "ĠCellulose": 43264, + "ictionaries": 43265, + "ĠMycoplasma": 43266, + "ĠBurgers": 43267, + "ĠKeplerian": 43268, + "UCTION": 43269, + "VB": 43270, + "Ġbcc": 43271, + "raid": 43272, + "ENDIX": 43273, + "Ġscoping": 43274, + "ĠPRI": 43275, + "ĠCdSe": 43276, + "ĠGreedy": 43277, + "ĠHammer": 43278, + "ĠBacteroides": 43279, + "informative": 43280, + "Ġresembled": 43281, + "yllium": 43282, + "Twenty": 43283, + "Ġpounds": 43284, + "Ġunpolarized": 43285, + "Ġconfigure": 43286, + "Ġtranscriptionally": 43287, + "Ġmicroscale": 43288, + "ĠPutting": 43289, + "Ġpyrrol": 43290, + "ĠLASSO": 43291, + "filtration": 43292, + "Ġtech": 43293, + "performing": 43294, + "Along": 43295, + "ĠCTLA": 43296, + "Ġauthorization": 43297, + "URAL": 43298, + "Ġleaky": 43299, + "Optical": 43300, + "ĠReveal": 43301, + "ĠHUVECs": 43302, + "Wu": 43303, + "custom": 43304, + "dible": 43305, + "Ġ": 43306, + "CDCl": 43307, + "Ġemphys": 43308, + "Neut": 43309, + "collagen": 43310, + "necessarily": 43311, + "ĠRoots": 43312, + "Pose": 43313, + "Tu": 43314, + "Ġclue": 43315, + "Ġperturbing": 43316, + "ĠHelium": 43317, + "ĠCombustion": 43318, + "nitrogen": 43319, + "amplified": 43320, + "prove": 43321, + "ĠSoils": 43322, + "normalization": 43323, + "ĠCHOP": 43324, + "ĠMcLe": 43325, + "Ġstrikes": 43326, + "Ġcropped": 43327, + "ĠKuo": 43328, + "Ġvagal": 43329, + "Ġdinucleotide": 43330, + "ĠIsaac": 43331, + "ĠLOX": 43332, + "Ġdirectionality": 43333, + "Ġchemoradiotherapy": 43334, + "calculus": 43335, + "ĠMohammed": 43336, + "mapped": 43337, + "Ġreforms": 43338, + "Ġreordering": 43339, + "ĠBm": 43340, + "ĠESCs": 43341, + "ĠNUC": 43342, + "thaw": 43343, + "Ġnanoporous": 43344, + "Ġtrainable": 43345, + "ĠATT": 43346, + "feats": 43347, + "OFDM": 43348, + "ĠSHP": 43349, + "ĠRichter": 43350, + "Ġsprayed": 43351, + "ĠJefferson": 43352, + "FOX": 43353, + "bh": 43354, + "otte": 43355, + "Ġleiomy": 43356, + "ospores": 43357, + "specificity": 43358, + "ĠRefer": 43359, + "ĠHaas": 43360, + "Move": 43361, + "Materials": 43362, + "tec": 43363, + "utility": 43364, + "entional": 43365, + "ĠMPP": 43366, + "chond": 43367, + "Ġseepage": 43368, + "Ġpeach": 43369, + "ĠÎĶt": 43370, + "embryonic": 43371, + "Yan": 43372, + "Ġliposomal": 43373, + "ĠValencia": 43374, + "ĠEndo": 43375, + "ĠPAO": 43376, + "Ġdialect": 43377, + "Ġchondrocyte": 43378, + "ĠMillimeter": 43379, + "ĠRegularity": 43380, + "destroy": 43381, + "ĠCondensation": 43382, + "Bayes": 43383, + "abundance": 43384, + "ĠdU": 43385, + "ĠSSI": 43386, + "ĠHAND": 43387, + "Ġconsulted": 43388, + "Ġsuppliers": 43389, + "Ġdemo": 43390, + "registered": 43391, + "Ġmicrosomal": 43392, + "Ġlambs": 43393, + "responsiveness": 43394, + "Dy": 43395, + "GAS": 43396, + "UME": 43397, + "Ġaero": 43398, + "Ġcalmodulin": 43399, + "Ġcalcined": 43400, + "Ġinsula": 43401, + "ĠMei": 43402, + "ĠREAL": 43403, + "Ġcontractible": 43404, + "ĠEssentially": 43405, + "Ġgaming": 43406, + "Ġspillover": 43407, + "residues": 43408, + "âİ": 43409, + "ĠEMC": 43410, + "ĠSDE": 43411, + "ĠSerine": 43412, + "ecki": 43413, + "ĠPrinceton": 43414, + "ĠBACKGROUND": 43415, + "masks": 43416, + "ĠLom": 43417, + "ffield": 43418, + "efitinib": 43419, + "Ġpatents": 43420, + "ĠBez": 43421, + "loads": 43422, + "Ġgonadal": 43423, + "Ġnitrocellulose": 43424, + "âĻĤ": 43425, + "Ġthrown": 43426, + "Ġrectification": 43427, + "mina": 43428, + "iscid": 43429, + "ĠBiobank": 43430, + "paramagnetic": 43431, + "GSK": 43432, + "ĠDerivative": 43433, + "criterion": 43434, + "ĠMonthly": 43435, + "ë¥": 43436, + "ĠSichuan": 43437, + "Ġimmunologic": 43438, + "Ġheterotic": 43439, + "ĠMcCl": 43440, + "ĠSMART": 43441, + "ĠBatteries": 43442, + "Ġpremiered": 43443, + "Ġcryopreservation": 43444, + "Nu": 43445, + "valho": 43446, + "Ġflotation": 43447, + "topological": 43448, + "ĠNanjing": 43449, + "Ġjuxt": 43450, + "ĠFeder": 43451, + "Ġprofoundly": 43452, + "cad": 43453, + "ienced": 43454, + "chuk": 43455, + "ĠIng": 43456, + "ĠKSHV": 43457, + "aminobenz": 43458, + "ĉĉĉĠĠĠ": 43459, + "Ġmetaph": 43460, + "ĠEpidemic": 43461, + "ĠAssociate": 43462, + "Ġsaccade": 43463, + "Ġdawn": 43464, + "Ġreheating": 43465, + "Ġspell": 43466, + "fractive": 43467, + "ĠToolkit": 43468, + "Ġrecognise": 43469, + "pathogen": 43470, + "Ġophthalmic": 43471, + "Ġqueried": 43472, + "thens": 43473, + "ithine": 43474, + "umably": 43475, + "Ġstrides": 43476, + "haul": 43477, + "Ġpassion": 43478, + "Ġdysfunctions": 43479, + "Byte": 43480, + "Ġcaesarean": 43481, + "prey": 43482, + "ĠHorse": 43483, + "ĠGABAA": 43484, + "Natural": 43485, + "kos": 43486, + "inators": 43487, + "odings": 43488, + "ARRAY": 43489, + "Ġunipotent": 43490, + "Ġelectromy": 43491, + "compart": 43492, + "Liu": 43493, + "encephalic": 43494, + "ĠCOMPAR": 43495, + "Ġsymbionts": 43496, + "ivacaine": 43497, + "OI": 43498, + "PVA": 43499, + "ĠNVIDIA": 43500, + "calibrated": 43501, + "Ġquest": 43502, + "NAD": 43503, + "ĠXyl": 43504, + "Ġpharmacist": 43505, + "directly": 43506, + "Ġquadruple": 43507, + "ethanone": 43508, + "ĠBulgaria": 43509, + "Ġoviposition": 43510, + "runs": 43511, + "Ġnociceptive": 43512, + "Ġasexual": 43513, + "SULT": 43514, + "Ġwouldn": 43515, + "ĠIndustries": 43516, + "abilizing": 43517, + "ĠCompressive": 43518, + "COOH": 43519, + "USH": 43520, + "kiewicz": 43521, + "Ġigneous": 43522, + "Ġdisappoint": 43523, + "ĠCKM": 43524, + "ĠDiagrams": 43525, + "ĠFlam": 43526, + "ĠGould": 43527, + "Ġcoenzyme": 43528, + "Ġparan": 43529, + "Ġ¶": 43530, + "Ġprogrammer": 43531, + "ĠTransforming": 43532, + "Ġmuscarinic": 43533, + "onucleotide": 43534, + "FIELD": 43535, + "ĠFuji": 43536, + "Ġnondec": 43537, + "Ġblanket": 43538, + "Ġpredisposing": 43539, + "ĠTrigger": 43540, + "Ġwelcome": 43541, + "Family": 43542, + "UINT": 43543, + "hfill": 43544, + "tvb": 43545, + "ĠBatt": 43546, + "Ġunmet": 43547, + "ĠApo": 43548, + "otient": 43549, + "Ġfundus": 43550, + "ĠLearned": 43551, + "Ġintrusions": 43552, + "Ġsolubilization": 43553, + "fundamental": 43554, + "ĠSantiago": 43555, + "Ġhpi": 43556, + "throw": 43557, + "ĠInto": 43558, + "timeout": 43559, + "Ġthickened": 43560, + "iasm": 43561, + "Ġgravitino": 43562, + "branched": 43563, + "VIII": 43564, + "Ġoch": 43565, + "Ġgym": 43566, + "ĠKrylov": 43567, + "Ġcorrective": 43568, + "ĠInstitution": 43569, + "Ġcrimes": 43570, + "ĠBacteroidetes": 43571, + "ĠEhr": 43572, + "Ġseated": 43573, + "rolizumab": 43574, + "Ġfactorized": 43575, + "rotational": 43576, + "Ġadministrators": 43577, + "âĭĨ": 43578, + "ineralization": 43579, + "lining": 43580, + "âĹ": 43581, + "urai": 43582, + "ĠFAP": 43583, + "ĠFisheries": 43584, + "ĠESO": 43585, + "temper": 43586, + "Biggr": 43587, + "ĠAlternating": 43588, + "twin": 43589, + "amatsu": 43590, + "Ġintrad": 43591, + "overflow": 43592, + "Ġcomparability": 43593, + "Ġsynoptic": 43594, + "USB": 43595, + "dbg": 43596, + "demonstr": 43597, + "ĠAchieving": 43598, + "Ġtectonics": 43599, + "ĠRandall": 43600, + "ĠPrepared": 43601, + "Ġsublimation": 43602, + "ĠBaj": 43603, + "Ġclutch": 43604, + "Ġsubdomain": 43605, + "Ġflaws": 43606, + "influ": 43607, + "Ġwidening": 43608, + "Ġmelted": 43609, + "Ġadministrator": 43610, + "Ġsubsidiary": 43611, + "ĠPricing": 43612, + "ticus": 43613, + "ogi": 43614, + "ĠAlign": 43615, + "ĠADV": 43616, + "Ġvastly": 43617, + "benchmark": 43618, + "Ġprioritize": 43619, + "Radi": 43620, + "essed": 43621, + "Ġsuprac": 43622, + "accard": 43623, + "Ġbiomimetic": 43624, + "ĠIrradiation": 43625, + "ĠALGOR": 43626, + "Ġpedigree": 43627, + "ĠCMT": 43628, + "odym": 43629, + "ĠVig": 43630, + "ĠBiochemistry": 43631, + "ĠAccum": 43632, + "Indices": 43633, + "hardtii": 43634, + "ĠRalph": 43635, + "Ġruminants": 43636, + "iT": 43637, + "onau": 43638, + "aner": 43639, + "planned": 43640, + "evers": 43641, + "Ġretroviral": 43642, + "Ġquantifier": 43643, + "ĠExtracting": 43644, + "Ġacetylated": 43645, + "Orth": 43646, + "ĠSenator": 43647, + "Ġnanosecond": 43648, + "Ġanticipation": 43649, + "ĠECMO": 43650, + "Ġsemicirc": 43651, + "ĠCryptosporidium": 43652, + "ĠTARGET": 43653, + "Ġapples": 43654, + "efield": 43655, + "Ġreman": 43656, + "Ġserovar": 43657, + "ĠTransactions": 43658, + "transitions": 43659, + "ursions": 43660, + "ĠMelatonin": 43661, + "Ġcholecystectomy": 43662, + "ĠAntiviral": 43663, + "hous": 43664, + "Ġotol": 43665, + "Ġmaj": 43666, + "Ġeclip": 43667, + "arel": 43668, + "ATIONAL": 43669, + "MIM": 43670, + "ĠCImg": 43671, + "ĠEndomet": 43672, + "ĠHayashi": 43673, + "Ġchimpanzees": 43674, + "mbf": 43675, + "ĠIPV": 43676, + "actoring": 43677, + "outside": 43678, + "neapolis": 43679, + "Ġdiscarding": 43680, + "numtype": 43681, + "ĠREST": 43682, + "Ġflagellar": 43683, + "ĠChandrase": 43684, + "hofer": 43685, + "Ġelectrocardiogram": 43686, + "Gb": 43687, + "mock": 43688, + "oeb": 43689, + "ĠSMO": 43690, + "ĠMord": 43691, + "ĠBoz": 43692, + "Ġminors": 43693, + "INLINE": 43694, + "Ġthermogravimetric": 43695, + "ĠMelting": 43696, + "ĠNSW": 43697, + "Sham": 43698, + "lotinib": 43699, + "Ġacquisitions": 43700, + "taz": 43701, + "Ġdefaults": 43702, + "Ġoscillates": 43703, + "ĠCaption": 43704, + "Ġdisruptive": 43705, + "Ġsweeping": 43706, + "ĠToolbox": 43707, + "Ġurethral": 43708, + "HBV": 43709, + "ĠRCS": 43710, + "Ġoxys": 43711, + "immuno": 43712, + "htm": 43713, + "oflavin": 43714, + "HIF": 43715, + "ĠSBA": 43716, + "ĠCPE": 43717, + "Ġwhites": 43718, + "ĠReactor": 43719, + "Ġpurp": 43720, + "Ġelectrocatalytic": 43721, + "Ġnonex": 43722, + "Ġtyphimurium": 43723, + "Ġeurop": 43724, + "concave": 43725, + "macrophage": 43726, + "SER": 43727, + "Ġlapse": 43728, + "Ġanatom": 43729, + "ĠRAC": 43730, + "tax": 43731, + "Ġmins": 43732, + "Ġsensu": 43733, + "ĠHebrew": 43734, + "Ġrealism": 43735, + "ĠMicroglia": 43736, + "Ġserialized": 43737, + "ĠHazard": 43738, + "Ġmetamorphosis": 43739, + "ĠIRA": 43740, + "Ġsmearing": 43741, + "Ġphotolysis": 43742, + "Ġchildbirth": 43743, + "Ġsilenced": 43744, + "rawal": 43745, + "Ġquadrants": 43746, + "butanol": 43747, + "Ġstochastically": 43748, + "ĠChambers": 43749, + "ĠNavarro": 43750, + "Ġprocurement": 43751, + "cite": 43752, + "ĠSle": 43753, + "ĠHadoop": 43754, + "Ġdelaying": 43755, + "Atlantic": 43756, + "Spain": 43757, + "falfa": 43758, + "odialysis": 43759, + "ynia": 43760, + "Ġplateaus": 43761, + "Ġmultimode": 43762, + "RESET": 43763, + "ĠRocky": 43764, + "ĠRodrigues": 43765, + "fMRI": 43766, + "rint": 43767, + "ĠTAL": 43768, + "Ġspecular": 43769, + "construction": 43770, + "ĠAthens": 43771, + "Ġcrosslink": 43772, + "Ġcountably": 43773, + "Ġspreadsheet": 43774, + "cribes": 43775, + "consistently": 43776, + "Ġfloodplain": 43777, + "EINVAL": 43778, + "Maca": 43779, + "Ġei": 43780, + "Ġhitherto": 43781, + "Ġsemif": 43782, + "Ġcontinual": 43783, + "ĠHomology": 43784, + "Ġphotocatalysts": 43785, + "isable": 43786, + "ĠHAT": 43787, + "Ġpolyhedra": 43788, + "ĠMayo": 43789, + "Ġlactating": 43790, + "sampler": 43791, + "Ġappliances": 43792, + "TU": 43793, + "Ġchess": 43794, + "ĠTing": 43795, + "Ġinvitation": 43796, + "Ġdistributing": 43797, + "ashima": 43798, + "Ġultral": 43799, + "trend": 43800, + "Ġrelaxations": 43801, + "ĠHelen": 43802, + "Ġbedding": 43803, + "Ġglandular": 43804, + "Ġincrementally": 43805, + "Ġconceal": 43806, + "claimed": 43807, + "ĠEddy": 43808, + "Ġmos": 43809, + "ĠTube": 43810, + "ĠToda": 43811, + "raj": 43812, + "ĠMü": 43813, + "ĠUll": 43814, + "Ġune": 43815, + "berine": 43816, + "Ġpolicym": 43817, + "Ġscholarly": 43818, + "Ġshoreline": 43819, + "Ġaldosterone": 43820, + "ĠBrucella": 43821, + "THE": 43822, + "REAL": 43823, + "Ġexome": 43824, + "ĠDAB": 43825, + "Ġextras": 43826, + "Ġbanding": 43827, + "ĠSiemens": 43828, + "ĠBoost": 43829, + "ĠSupernovae": 43830, + "ĠTracing": 43831, + "Ġascorbate": 43832, + "Italy": 43833, + "bund": 43834, + "Ġdecrement": 43835, + "Ġneurophysiological": 43836, + "Ġblackbody": 43837, + "ĠMcN": 43838, + "Ġcompetencies": 43839, + "oscape": 43840, + "ĠHonours": 43841, + "Ġmastitis": 43842, + "criteria": 43843, + "Ġbiaxial": 43844, + "Ġthawed": 43845, + "ĠFoll": 43846, + "oreceptor": 43847, + "Ġinvention": 43848, + "ADs": 43849, + "Show": 43850, + "------------------------------------------------": 43851, + "Ġellipsoidal": 43852, + "Ġfocussed": 43853, + "ĠDat": 43854, + "ĠRim": 43855, + "ĠLX": 43856, + "ĠGER": 43857, + "insler": 43858, + "Ġtopoisomerase": 43859, + "Ġhyperlipidemia": 43860, + "Ġmystery": 43861, + "Ġnitrification": 43862, + "Ġoncogenes": 43863, + "ĠFuller": 43864, + "ĠBartlett": 43865, + "Ġamphibians": 43866, + "SST": 43867, + "bly": 43868, + "leads": 43869, + "ecycle": 43870, + "ampl": 43871, + "ĠPOM": 43872, + "ĠDCF": 43873, + "strass": 43874, + "antibody": 43875, + "nonlinear": 43876, + "ĠBroadway": 43877, + "ĠCatalogue": 43878, + "ĠμA": 43879, + "Ġacetaminophen": 43880, + "Ġcrystallites": 43881, + "ĠNanotubes": 43882, + "ĠAcknowledgment": 43883, + "Ġmetamorphism": 43884, + "Ġtwinning": 43885, + "ĠAzerbai": 43886, + "xA": 43887, + "CCC": 43888, + "ĠSolids": 43889, + "preds": 43890, + "ĠMontana": 43891, + "WRITE": 43892, + "Ratio": 43893, + "Ġpunch": 43894, + "Ġriding": 43895, + "Ġacne": 43896, + "ĠUre": 43897, + "Ġcorr": 43898, + "ĠQOL": 43899, + "Ġinsult": 43900, + "Ġdominantly": 43901, + "Ġsubsamples": 43902, + "rews": 43903, + "ĠPreservation": 43904, + "ĠAffine": 43905, + "methanone": 43906, + "Ġhedgehog": 43907, + "JH": 43908, + "Ġlined": 43909, + "Ġsten": 43910, + "ĠDarmstadt": 43911, + "ĠLasso": 43912, + "Ġdeproton": 43913, + "Ġshoes": 43914, + "Ġmotives": 43915, + "Ġmicroscop": 43916, + "ophthora": 43917, + "Ġmacron": 43918, + "Ġencouragement": 43919, + "acrylic": 43920, + "ĠTensorFlow": 43921, + "Wrapper": 43922, + "oise": 43923, + "ayak": 43924, + "Ġrepresses": 43925, + "Ġpruned": 43926, + "ĠClar": 43927, + "ĠâĬ²": 43928, + "ĠUnderlying": 43929, + "Implemented": 43930, + "Ġsweat": 43931, + "Ġmeteorites": 43932, + "Ġtweez": 43933, + "Ġprosocial": 43934, + "Ġabrasion": 43935, + "hail": 43936, + "Ġshorth": 43937, + "ismatch": 43938, + "INTR": 43939, + "ĠTrin": 43940, + "Ġphysicists": 43941, + "ĠPEO": 43942, + "ĠMagneto": 43943, + "ĠJacobson": 43944, + "ĠMMPs": 43945, + "ĠIntravenous": 43946, + "Ġneurotransmission": 43947, + "ĠPneumonia": 43948, + "Lind": 43949, + "yre": 43950, + "Ġmaternity": 43951, + "ĠIris": 43952, + "riatal": 43953, + "ĠâĢļ": 43954, + "medetomidine": 43955, + "Ġtriterpen": 43956, + "ĠManuscript": 43957, + "ĠEndoplasmic": 43958, + "ĠPotter": 43959, + "Ġgerminal": 43960, + "Ġphotosystem": 43961, + "Guided": 43962, + "Ġguitarist": 43963, + "ĠChilean": 43964, + "ĠSalvador": 43965, + "ÉĻ": 43966, + "Ġcelestial": 43967, + "omand": 43968, + "Ġnk": 43969, + "Ġvendors": 43970, + "ĠPINK": 43971, + "ĠInorganic": 43972, + "Ġmoderated": 43973, + "SUS": 43974, + "ĠJoshi": 43975, + "ĠStata": 43976, + "ikes": 43977, + "oye": 43978, + "ĠJohnny": 43979, + "Leica": 43980, + "Ġkaon": 43981, + "ĠEquipment": 43982, + "Kim": 43983, + "gado": 43984, + "tures": 43985, + "Ġelem": 43986, + "ĠAAC": 43987, + "cea": 43988, + "odality": 43989, + "Ġaniline": 43990, + "Ġexothermic": 43991, + "ĠGunn": 43992, + "ĠJU": 43993, + "plicable": 43994, + "scapes": 43995, + "typed": 43996, + "Ġinspiratory": 43997, + "REGIST": 43998, + "ĠBryan": 43999, + "Ġanxious": 44000, + "ĠCarpenter": 44001, + "ĠPharmacokinetics": 44002, + "inferior": 44003, + "Frag": 44004, + "ZY": 44005, + "Ġoesophageal": 44006, + "ĠSuk": 44007, + "ĠPron": 44008, + "ĠCDI": 44009, + "AGC": 44010, + "keywords": 44011, + "susceptible": 44012, + "Germany": 44013, + "MAS": 44014, + "iC": 44015, + "anmar": 44016, + "Ġexiting": 44017, + "ĠHale": 44018, + "Ġrhamn": 44019, + "industrial": 44020, + "Ġraft": 44021, + "embrolizumab": 44022, + "Ġdeploying": 44023, + "Ġsalicylic": 44024, + "Rn": 44025, + "Ġcensor": 44026, + "ĠdX": 44027, + "Ġforum": 44028, + "MSI": 44029, + "blad": 44030, + "Ġsquir": 44031, + "CPP": 44032, + "Ġgrapevine": 44033, + "ĠRAFT": 44034, + "Monte": 44035, + "Ġmicroflora": 44036, + "rcl": 44037, + "Ġdecap": 44038, + "ANC": 44039, + "Ġbroaden": 44040, + "Ġfreed": 44041, + "Ġsouthward": 44042, + "ĠJacques": 44043, + "Ġrequesting": 44044, + "ĠAspect": 44045, + "arajan": 44046, + "Failed": 44047, + "fprintf": 44048, + "pytest": 44049, + "ʹ": 44050, + "ĠCm": 44051, + "until": 44052, + "neiss": 44053, + "Ġmonos": 44054, + "ospinal": 44055, + "olsky": 44056, + "contrib": 44057, + "Container": 44058, + "ĠVolunte": 44059, + "ĠAttributes": 44060, + "ĠTumour": 44061, + "Ġreinhardtii": 44062, + "Ġcentromere": 44063, + "ĠSymph": 44064, + "ĠAo": 44065, + "agens": 44066, + "pleted": 44067, + "ieder": 44068, + "Ġactivist": 44069, + "ĠAlmeida": 44070, + "Ġdisturbing": 44071, + "Ġreflexes": 44072, + "DSS": 44073, + "Ġforwards": 44074, + "ronym": 44075, + "ĠIntegrity": 44076, + "WEEN": 44077, + "Ġôı¼Į": 44078, + "Ġfaithfully": 44079, + "Ġpericardial": 44080, + "Japanese": 44081, + "ĠCENP": 44082, + "Kr": 44083, + "Ġdefending": 44084, + "Ġzon": 44085, + "insensitive": 44086, + "Ġlabs": 44087, + "ĠCaM": 44088, + "ĠEurop": 44089, + "MEA": 44090, + "BLAST": 44091, + "xN": 44092, + "alen": 44093, + "Ġclk": 44094, + "lineage": 44095, + "coating": 44096, + "Ġtailoring": 44097, + "CONTR": 44098, + "Ġadrenergic": 44099, + "interpreted": 44100, + "NIH": 44101, + "amoeba": 44102, + "ĠCyr": 44103, + "Ġtriplicates": 44104, + "defining": 44105, + "ĠLogan": 44106, + "tesy": 44107, + "ĠTwist": 44108, + "ĠGrammar": 44109, + "ĠSustained": 44110, + "Ġanharmonic": 44111, + "Ġalve": 44112, + "Ġruler": 44113, + "Ġquanta": 44114, + "Ġdirects": 44115, + "Ġoffloading": 44116, + "ĠGeophysical": 44117, + "Require": 44118, + "Ġhepatoma": 44119, + "Ġfoo": 44120, + "ĠGeorges": 44121, + "Ġbouts": 44122, + "ĠTAK": 44123, + "Ġantidiabetic": 44124, + "ĠReported": 44125, + "presenting": 44126, + "ĠLayered": 44127, + "RENCE": 44128, + "Ġuveitis": 44129, + "bage": 44130, + "Ġfentanyl": 44131, + "ensemble": 44132, + "ĠOSCC": 44133, + "Ġminers": 44134, + "looking": 44135, + "ĠBeer": 44136, + "precipitation": 44137, + "ĠEnterprise": 44138, + "Ġserotonergic": 44139, + "Ġseesaw": 44140, + "ĠAthletics": 44141, + "Ġhydrolytic": 44142, + "Ġtalent": 44143, + "Ġdiscernible": 44144, + "FIL": 44145, + "lives": 44146, + "ĠSales": 44147, + "ĠSSc": 44148, + "erend": 44149, + "clim": 44150, + "antid": 44151, + "INTS": 44152, + "Ġattenuating": 44153, + "Ġtwists": 44154, + "Ġoxygenase": 44155, + "rini": 44156, + "Macaulay": 44157, + "zm": 44158, + "ĠPOT": 44159, + "ĠMp": 44160, + "ĠHey": 44161, + "ĠOVER": 44162, + "illion": 44163, + "Ġinvaluable": 44164, + "Ġantiplatelet": 44165, + "Ġmutans": 44166, + "Ġgraduates": 44167, + "GRAM": 44168, + "ispheric": 44169, + "Ġincompatibility": 44170, + "Ġcarboxylase": 44171, + "Ġbiocontrol": 44172, + "ĠPhysicochemical": 44173, + "ïĻ": 44174, + "Ġlae": 44175, + "ĠAortic": 44176, + "ĠRacing": 44177, + "ĠECD": 44178, + "ivic": 44179, + "Ġelectromechanical": 44180, + "ĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ": 44181, + "Ġsteer": 44182, + "ĠOutside": 44183, + "Ġadenocarcinomas": 44184, + "ĠKeep": 44185, + "Ġcocon": 44186, + "Ġmoderating": 44187, + "Ġreformulated": 44188, + "Ġfundamentals": 44189, + "ĠTesla": 44190, + "ĠStirling": 44191, + "orated": 44192, + "opid": 44193, + "Ġparox": 44194, + "Ġtrivalent": 44195, + "Ġexchangeable": 44196, + "Ġdebuted": 44197, + "Very": 44198, + "reements": 44199, + "ĠTomm": 44200, + "ĠCyn": 44201, + "ĠCatalysts": 44202, + "quat": 44203, + "ĠFER": 44204, + "ĠRum": 44205, + "Ġscanners": 44206, + "ĠâĨĴâĪŀ": 44207, + "otropical": 44208, + "Ġvenues": 44209, + "estimator": 44210, + "Ġemptying": 44211, + "GPP": 44212, + "VIR": 44213, + "Ġcomplicates": 44214, + "ĠNIS": 44215, + "ĠZhen": 44216, + "ĠBlues": 44217, + "Ġtextbooks": 44218, + "Ġsixty": 44219, + "Ġethers": 44220, + "Ġfinancially": 44221, + "ĠmHealth": 44222, + "ĠTut": 44223, + "Ġlaryng": 44224, + "ĠGs": 44225, + "ĠWatch": 44226, + "Ġsev": 44227, + "Ġital": 44228, + "assed": 44229, + "Ġ÷": 44230, + "ĠConsent": 44231, + "Ġnuts": 44232, + "vitreal": 44233, + "Ġmetaphase": 44234, + "Ġtitania": 44235, + "Ġfoils": 44236, + "Ġgalectin": 44237, + "initialize": 44238, + "Ġadvisor": 44239, + "Ġadministering": 44240, + "Bool": 44241, + "Ġcem": 44242, + "Ġreforming": 44243, + "Ġgn": 44244, + "ysh": 44245, + "Ġattor": 44246, + "SCI": 44247, + "Exc": 44248, + "builder": 44249, + "Ġcerium": 44250, + "Ġregistries": 44251, + "ĠMatsumoto": 44252, + "Ġtempting": 44253, + "isha": 44254, + "Ġreorientation": 44255, + "ĠMold": 44256, + "ĠRAGE": 44257, + "yson": 44258, + "Ġunequiv": 44259, + "Ġrelocation": 44260, + "ĠÃķ": 44261, + "ĠReform": 44262, + "ĠREQU": 44263, + "Ġcommensurate": 44264, + "catalog": 44265, + "ĠTPS": 44266, + "Ġlamb": 44267, + "Ġprefactor": 44268, + "archy": 44269, + "Ġdopants": 44270, + "drv": 44271, + "ĠPARAMET": 44272, + "schedule": 44273, + "ochemically": 44274, + "ĠeHealth": 44275, + "unas": 44276, + "ĠPinus": 44277, + "ĠHSA": 44278, + "Ġinterrelations": 44279, + "Ġdepot": 44280, + "ĠPlatinum": 44281, + "Ġlifelong": 44282, + "Ġpersistently": 44283, + "ĠParadox": 44284, + "ĠConformational": 44285, + "esophag": 44286, + "ĠAAT": 44287, + "plin": 44288, + "ĠFCN": 44289, + "ĠDt": 44290, + "oposide": 44291, + "Ġchal": 44292, + "Ġhalt": 44293, + "ĠDetect": 44294, + "Ġdiscriminated": 44295, + "ĠLagrangians": 44296, + "Appro": 44297, + "Ġȧ": 44298, + "Ġimpulsivity": 44299, + "BAT": 44300, + "Chemical": 44301, + "gather": 44302, + "ĠUNC": 44303, + "intron": 44304, + "ĠSimulator": 44305, + "ĠGla": 44306, + "TTT": 44307, + "ĠVolatile": 44308, + "Ġsubsid": 44309, + "ĠBroadcasting": 44310, + "Ġstreptozotocin": 44311, + "Ġfumar": 44312, + "ĠMPEG": 44313, + "Ġinfluenzae": 44314, + "subjects": 44315, + "Ġappropriateness": 44316, + "Ġarcmin": 44317, + "Ġstranded": 44318, + "oylation": 44319, + "ĠDEX": 44320, + "oviral": 44321, + "ĠQuarter": 44322, + "colytic": 44323, + "Ġfriendship": 44324, + "HES": 44325, + "loxacin": 44326, + "Ġere": 44327, + "ĠTrad": 44328, + "uristics": 44329, + "ĠECT": 44330, + "ĠEGCG": 44331, + "ĠLRP": 44332, + "ĠGAG": 44333, + "ĠInP": 44334, + "Ġcontempor": 44335, + "Ġmicror": 44336, + "ierstrass": 44337, + "ĠElectrosp": 44338, + "needed": 44339, + "atmosphere": 44340, + "nT": 44341, + "Ġbandwidths": 44342, + "Ġdiversified": 44343, + "ĠAppropriate": 44344, + "restore": 44345, + "rocnem": 44346, + "ĠLaguerre": 44347, + "ĠSongs": 44348, + "ĠKaluza": 44349, + "ĠSymmetries": 44350, + "ĠSchmitt": 44351, + "Ġbiomolecular": 44352, + "scalebox": 44353, + "Ġintrahepatic": 44354, + "understanding": 44355, + "ĠABCG": 44356, + "Ġunderestimates": 44357, + "ĠStreaming": 44358, + "Ġfictitious": 44359, + "oplasmosis": 44360, + "resident": 44361, + "ĠBary": 44362, + "ĠComa": 44363, + "scrip": 44364, + "Ġdegran": 44365, + "ĠCaMKII": 44366, + "ĠBalmer": 44367, + "ĠPlasm": 44368, + "Ġchelating": 44369, + "ĠParadigm": 44370, + "Ġopponents": 44371, + "EK": 44372, + "Pin": 44373, + "Ġmsec": 44374, + "adone": 44375, + "acht": 44376, + "CCG": 44377, + "ECO": 44378, + "normalize": 44379, + "ĠDesigns": 44380, + "Ġyellowish": 44381, + "glutamyl": 44382, + "Ġdomestication": 44383, + "Ġmonophyletic": 44384, + "dles": 44385, + "nested": 44386, + "ĠGrace": 44387, + "ĠStudios": 44388, + "ĠDiscussions": 44389, + "ophenoxy": 44390, + "Ġveterin": 44391, + "Ġendosym": 44392, + "uttinger": 44393, + "batches": 44394, + "ĠFiji": 44395, + "ĠRNF": 44396, + "ousa": 44397, + "ĠKY": 44398, + "Ġspectrograph": 44399, + "ERIC": 44400, + "ĠMyanmar": 44401, + "ĠConstraining": 44402, + "Ġecologically": 44403, + "Ġfrost": 44404, + "arboux": 44405, + "ĠFibonacci": 44406, + "Ġcanceled": 44407, + "ĠISSN": 44408, + "Rect": 44409, + "another": 44410, + "ĠMMA": 44411, + "OLO": 44412, + "ĠTruth": 44413, + "Ġorthopaedic": 44414, + "Ġtraversing": 44415, + "ischemic": 44416, + "ĠMozambique": 44417, + "ĠMSR": 44418, + "apace": 44419, + "ĠThread": 44420, + "ologia": 44421, + "Ġcalm": 44422, + "methyltransferase": 44423, + "Ġͪ": 44424, + "Ġdrove": 44425, + "Ġcommanded": 44426, + "Ġfeline": 44427, + "ĠRush": 44428, + "ĠLisa": 44429, + "Ġsuperspace": 44430, + "arcy": 44431, + "ĠRegulated": 44432, + "ĠResting": 44433, + "causing": 44434, + "psychotics": 44435, + "qt": 44436, + "Ġtulare": 44437, + "Ġrelocated": 44438, + "Ġrepell": 44439, + "Ġpredatory": 44440, + "people": 44441, + "traits": 44442, + "Euclidean": 44443, + "FDA": 44444, + "XRT": 44445, + "pC": 44446, + "pand": 44447, + "ĠÆ": 44448, + "reve": 44449, + "Ġbids": 44450, + "Ġcousin": 44451, + "Ġsubdomains": 44452, + "tilb": 44453, + "énez": 44454, + "linearly": 44455, + "oproteins": 44456, + "Ġcodec": 44457, + "Ġcontraception": 44458, + "ĠCdk": 44459, + "Ġrailroad": 44460, + "Bench": 44461, + "rng": 44462, + "ĠDLA": 44463, + "entile": 44464, + "ĠCOCO": 44465, + "ĠMatth": 44466, + "ĠOverl": 44467, + "ĠRoutine": 44468, + "Ġmultifocal": 44469, + "Ġartefact": 44470, + "Ġsculpture": 44471, + "cies": 44472, + "mate": 44473, + "ĠØ": 44474, + "urek": 44475, + "ĠBend": 44476, + "ĠNathan": 44477, + "Ġdew": 44478, + "ymia": 44479, + "azzi": 44480, + "ĠErk": 44481, + "Ġgraduation": 44482, + "Boundary": 44483, + "Gra": 44484, + "Ġbfd": 44485, + "ĠSert": 44486, + "Ġovershoot": 44487, + "ĠSeo": 44488, + "Ġsklearn": 44489, + "Ġconservatively": 44490, + "piracy": 44491, + "Ġlaunching": 44492, + "XD": 44493, + "arbitrary": 44494, + "perone": 44495, + "Ġshops": 44496, + "competitive": 44497, + "ĠPakistani": 44498, + "Ġcompetitor": 44499, + "biotics": 44500, + "raits": 44501, + "ĠNoble": 44502, + "Ġsubregions": 44503, + "ĠJump": 44504, + "roller": 44505, + "tris": 44506, + "Ġmacrol": 44507, + "ós": 44508, + "ĠPenic": 44509, + "Ġmicrosomes": 44510, + "Ġimprecise": 44511, + "Ġdowntown": 44512, + "ĠeQTL": 44513, + "ifest": 44514, + "ĠMFI": 44515, + "Ġrarity": 44516, + "âĢĻâĢĻ": 44517, + "Ġbelts": 44518, + "Ġglycosyl": 44519, + "ĠNicolas": 44520, + "synthesis": 44521, + "Oh": 44522, + "hierarch": 44523, + "pps": 44524, + "anets": 44525, + "roads": 44526, + "ATIC": 44527, + "MIMO": 44528, + "ĠContract": 44529, + "Leib": 44530, + "opyrox": 44531, + "Ġinformational": 44532, + "Synonyms": 44533, + "challenge": 44534, + "ĠProximal": 44535, + "ĠCrawford": 44536, + "Ġisopropyl": 44537, + "Ġsubfamilies": 44538, + "ĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ": 44539, + "Ġannotators": 44540, + "Ġreconcile": 44541, + "Ġparsimony": 44542, + "Ġcaspases": 44543, + "cott": 44544, + "environments": 44545, + "Ġdrm": 44546, + "ĠPIL": 44547, + "ĠMec": 44548, + "ĠInfer": 44549, + "ĠSirt": 44550, + "Shell": 44551, + "agulants": 44552, + "seismic": 44553, + "Ġsuburban": 44554, + "ĠXXX": 44555, + "iodes": 44556, + "Ġbackpropagation": 44557, + "traditional": 44558, + "Ġphotocon": 44559, + "ĠMicrobiology": 44560, + "QT": 44561, + "uridine": 44562, + "Ġchop": 44563, + "ĠThé": 44564, + "Ġprejud": 44565, + "Ġencoders": 44566, + "collected": 44567, + "remark": 44568, + "Ġsunspot": 44569, + "ĠPhenolic": 44570, + "Understanding": 44571, + "Ġrejecting": 44572, + "Ġromantic": 44573, + "Ġcentimeters": 44574, + "Ġhallucinations": 44575, + "Home": 44576, + "casted": 44577, + "Ġcw": 44578, + "rai": 44579, + "ĠDisplacement": 44580, + "PHY": 44581, + "carbam": 44582, + "Ġxenon": 44583, + "Ġnarratives": 44584, + "Ġdollar": 44585, + "Ġdynasty": 44586, + "ì§": 44587, + "Ġinforming": 44588, + "ĠOCD": 44589, + "ák": 44590, + "Ġoverheads": 44591, + "juana": 44592, + "ĠKraus": 44593, + "fx": 44594, + "kaya": 44595, + "Ġnid": 44596, + "ĠGrab": 44597, + "Ġinflores": 44598, + "Arc": 44599, + "============": 44600, + "Ġcondenser": 44601, + "Ġnanocar": 44602, + "ommens": 44603, + "Ġsaturating": 44604, + "rece": 44605, + "elif": 44606, + "ĠALE": 44607, + "ĠBub": 44608, + "ĠLaf": 44609, + "andran": 44610, + "Ġpouch": 44611, + "roline": 44612, + "ACHE": 44613, + "CCD": 44614, + "Ġcoolant": 44615, + "Ġgrasslands": 44616, + "ĠSynchronous": 44617, + "izziness": 44618, + "Ġcetuximab": 44619, + "Ġdichotomous": 44620, + "roch": 44621, + "ĠAuckland": 44622, + "obesity": 44623, + "ikit": 44624, + "Ġoperad": 44625, + "ĠOnset": 44626, + "Ġbeforehand": 44627, + "Ġuncomp": 44628, + "USED": 44629, + "ubbing": 44630, + "ĠSMBH": 44631, + "ĠExpedition": 44632, + "Ġhib": 44633, + "ĠPPR": 44634, + "ĠNED": 44635, + "udio": 44636, + "ĠJal": 44637, + "ĠArp": 44638, + "ĠBee": 44639, + "ĠVarieties": 44640, + "Comm": 44641, + "About": 44642, + "ĠAttachment": 44643, + "ODULE": 44644, + "Calculate": 44645, + "Tan": 44646, + "inism": 44647, + "Ġara": 44648, + "Ġcabin": 44649, + "Ġconnexin": 44650, + "Ġcomets": 44651, + "umptive": 44652, + "Ġdestabilization": 44653, + "ĠHolt": 44654, + "ructose": 44655, + "anishi": 44656, + "plasticity": 44657, + "omycosis": 44658, + "ovician": 44659, + "________________": 44660, + "rar": 44661, + "Ġwore": 44662, + "udine": 44663, + "ĠInvariance": 44664, + "Ġperitonitis": 44665, + "Ġmetrology": 44666, + "Ġcloses": 44667, + "Ġcolorless": 44668, + "Noise": 44669, + "DIO": 44670, + "ĠLifshitz": 44671, + "zul": 44672, + "estive": 44673, + "ĠMPA": 44674, + "ĠBooth": 44675, + "ĠDoll": 44676, + "arene": 44677, + "geness": 44678, + "Ġmolecularly": 44679, + "ĠPerkin": 44680, + "Ġdosimetry": 44681, + "ĠSOFT": 44682, + "ĠPyTorch": 44683, + "Ġquarters": 44684, + "ĠKuhn": 44685, + "Ġsplenocytes": 44686, + "RW": 44687, + "cart": 44688, + "leb": 44689, + "Ġcondom": 44690, + "ĠHoc": 44691, + "Ġextents": 44692, + "Ġslug": 44693, + "ĠSupplementation": 44694, + "diffic": 44695, + "esterly": 44696, + "Yu": 44697, + "antigens": 44698, + "ĠÃĴ": 44699, + "Changes": 44700, + "Ġpropylene": 44701, + "ĠPrison": 44702, + "ĠAlgorithmic": 44703, + "Ġtolerances": 44704, + "Adam": 44705, + "Ġesterase": 44706, + "Ġmilder": 44707, + "ĠConvection": 44708, + "PTR": 44709, + "kpc": 44710, + "Ġexo": 44711, + "ĠFah": 44712, + "ĠYFP": 44713, + "ĠCRM": 44714, + "Ġhepatotoxicity": 44715, + "Ġnicotinamide": 44716, + "Ġpatchy": 44717, + "depends": 44718, + "ĠpB": 44719, + "Ġeel": 44720, + "Ġnv": 44721, + "ĠSes": 44722, + "ĠHZ": 44723, + "Ġimprint": 44724, + "epileptic": 44725, + "fluctuations": 44726, + "Ġformalize": 44727, + "chev": 44728, + "Ġdipping": 44729, + "ĠPyramid": 44730, + "Ġholo": 44731, + "ĠMTs": 44732, + "Ġlaminates": 44733, + "Ġwormhole": 44734, + "LAP": 44735, + "hape": 44736, + "Ġak": 44737, + "Ġreals": 44738, + "Ġbystand": 44739, + "Ġinterleaved": 44740, + "Ġxz": 44741, + "ovy": 44742, + "Ġcoprime": 44743, + "uclides": 44744, + "Ġtrimming": 44745, + "MICAL": 44746, + "pyrrole": 44747, + "Ia": 44748, + "NLS": 44749, + "Quality": 44750, + "takes": 44751, + "zinc": 44752, + "ĠPione": 44753, + "ĠEwing": 44754, + "ĠLCA": 44755, + "ĠÃĶ": 44756, + "ictus": 44757, + "Ġcollim": 44758, + "Ġphylogenetically": 44759, + "ĠKeeping": 44760, + "ĠFaith": 44761, + "bonds": 44762, + "titer": 44763, + "Ġsubcategories": 44764, + "shaded": 44765, + "Ġphotospheric": 44766, + "ĠAppearance": 44767, + "ĠUniversities": 44768, + "Ġglomeruli": 44769, + "ĠPrefrontal": 44770, + "Ġprivilege": 44771, + "iH": 44772, + "uya": 44773, + "ĠLCL": 44774, + "ĠInGaAs": 44775, + "Inspired": 44776, + "atalog": 44777, + "ĠPerceptions": 44778, + "ĠNaHCO": 44779, + "Ġstreamline": 44780, + "trajectory": 44781, + "ĠMicrom": 44782, + "Ġbedside": 44783, + "ĠRomero": 44784, + "Ġgaugino": 44785, + "DEN": 44786, + "Fa": 44787, + "Olymp": 44788, + "eal": 44789, + "uels": 44790, + "icylic": 44791, + "Ġgod": 44792, + "Ġattaining": 44793, + "Ġprotests": 44794, + "Ġnowhere": 44795, + "desorption": 44796, + "ĠHydroxy": 44797, + "ĠErbB": 44798, + "ĠSPAR": 44799, + "Ġhinders": 44800, + "herenkov": 44801, + "KERNEL": 44802, + "Ġsect": 44803, + "ulong": 44804, + "Ġpreprocessed": 44805, + "fractional": 44806, + "oyage": 44807, + "Ġphosphatases": 44808, + "Ġcoastline": 44809, + "Ġhref": 44810, + "ĠSutherland": 44811, + "oxone": 44812, + "Ġhomomorphic": 44813, + "DEM": 44814, + "Ġbovis": 44815, + "ĠCBP": 44816, + "plen": 44817, + "ĠBuc": 44818, + "ĠGior": 44819, + "Ġcompost": 44820, + "ĠOracle": 44821, + "ĠSphere": 44822, + "ĠSchre": 44823, + "derivatives": 44824, + "lytes": 44825, + "ĠYo": 44826, + "Ġcyclones": 44827, + "ĠMaize": 44828, + "Ġunfair": 44829, + "Template": 44830, + "Ġimpregnation": 44831, + "Ġlaparoscopy": 44832, + "Ġhamiltonian": 44833, + "ignore": 44834, + "Ġdisposable": 44835, + "earic": 44836, + "Ġelectoral": 44837, + "ccos": 44838, + "ĠShh": 44839, + "Ġturbo": 44840, + "Ġintrusive": 44841, + "Ġprecedence": 44842, + "annotated": 44843, + "Ġdystonia": 44844, + "Fat": 44845, + "uins": 44846, + "Ġsway": 44847, + "arizing": 44848, + "illen": 44849, + "Ġyi": 44850, + "Ġnormed": 44851, + "ĠÌĤ": 44852, + "ĠExtr": 44853, + "ĠProteome": 44854, + "Document": 44855, + "ĠQUANTUM": 44856, + "titi": 44857, + "ĠCPC": 44858, + "ĠMiles": 44859, + "ĠBoc": 44860, + "ĠRTS": 44861, + "CTX": 44862, + "Ġsafegu": 44863, + "ĠNormally": 44864, + "ĠÃľber": 44865, + "onious": 44866, + "ĠSCE": 44867, + "Ġalfalfa": 44868, + "ĠLut": 44869, + "Ġcout": 44870, + "Ġenlarge": 44871, + "ĠEnable": 44872, + "Ġvirion": 44873, + "ĠShallow": 44874, + "definitely": 44875, + "ĠColin": 44876, + "ĠRetention": 44877, + "Ġmimicry": 44878, + "################################################################": 44879, + "NSCLC": 44880, + "Ġgratitude": 44881, + "Ġtending": 44882, + "ĠIDS": 44883, + "eret": 44884, + "rican": 44885, + "Ġxn": 44886, + "ĠYoo": 44887, + "Ġoptimise": 44888, + "Arrow": 44889, + "ĠTransferase": 44890, + "PKC": 44891, + "ĠGuangzhou": 44892, + "ruc": 44893, + "yrid": 44894, + "isz": 44895, + "ĠFIX": 44896, + "ĠDatabases": 44897, + "astron": 44898, + "Ġplayback": 44899, + "Ġnarrowly": 44900, + "Correlation": 44901, + "ĠAffinity": 44902, + "Ġfunctorial": 44903, + "Ġlectins": 44904, + "Ġruptured": 44905, + "Display": 44906, + "ĠSymptom": 44907, + "Ġequidistant": 44908, + "ĠRiccati": 44909, + "ĠAchievement": 44910, + "grand": 44911, + "onated": 44912, + "ĠdH": 44913, + "ĠFID": 44914, + "ĠDER": 44915, + "ĠCoA": 44916, + "Ġgasification": 44917, + "ĠCONS": 44918, + "Ġaccompanies": 44919, + "Ġimpede": 44920, + "Ġprecede": 44921, + "Ġkitchen": 44922, + "progress": 44923, + "Ġwiring": 44924, + "lerenes": 44925, + "ĠGius": 44926, + "Ġtransp": 44927, + "retrie": 44928, + "ijer": 44929, + "affer": 44930, + "Ġbirthday": 44931, + "ĠHald": 44932, + "Ġmusculus": 44933, + "ĠToken": 44934, + "ĠBowel": 44935, + "Ġskipped": 44936, + "Cha": 44937, + "bv": 44938, + "ĠBlow": 44939, + "Ġpreoperatively": 44940, + "Ġglove": 44941, + "ĠLeven": 44942, + "Ġmesop": 44943, + "ĠAuxiliary": 44944, + "ensuremath": 44945, + "jus": 44946, + "Å©": 44947, + "Ġvoter": 44948, + "ĠHitch": 44949, + "proxy": 44950, + "ĠKut": 44951, + "Ġpoems": 44952, + "ĠAngl": 44953, + "cera": 44954, + "Ġstarred": 44955, + "AGES": 44956, + "Science": 44957, + "Analyses": 44958, + "Ġreferees": 44959, + "Ġabrogated": 44960, + "Ġdesalination": 44961, + "ĠPrandtl": 44962, + "Pit": 44963, + "Ġnatal": 44964, + "ogran": 44965, + "ystitis": 44966, + "Ġdesm": 44967, + "Ġcurious": 44968, + "Ġdemon": 44969, + "uzzi": 44970, + "ochondrial": 44971, + "ĠTreaty": 44972, + "Tracker": 44973, + "rhoeae": 44974, + "LW": 44975, + "furt": 44976, + "Ġomp": 44977, + "isational": 44978, + "Ġmemorial": 44979, + "ĠLatency": 44980, + "ĠHypot": 44981, + "Ġglued": 44982, + "exactly": 44983, + "Ġcontraind": 44984, + "Cancer": 44985, + "Ġffi": 44986, + "ĠNAA": 44987, + "ĠChr": 44988, + "egg": 44989, + "ĠMotiv": 44990, + "Ġlayouts": 44991, + "Ġclimb": 44992, + "Ġappendicitis": 44993, + "CUDA": 44994, + "Ġphotoproduction": 44995, + "ĠSIP": 44996, + "Ġveto": 44997, + "perin": 44998, + "ĠUnity": 44999, + "byear": 45000, + "Ġforwarded": 45001, + "ĠDominant": 45002, + "holz": 45003, + "ĠThoracic": 45004, + "DEFINE": 45005, + "Ġtyrosinase": 45006, + "Bad": 45007, + "INA": 45008, + "fuel": 45009, + "Ġgi": 45010, + "ĠVIS": 45011, + "astolic": 45012, + "Ġoxaliplatin": 45013, + "effector": 45014, + "ĉĉĉĉĠ": 45015, + "еÑĢ": 45016, + "ĠBaby": 45017, + "Ġwashout": 45018, + "pituitary": 45019, + "NGC": 45020, + "Ġdns": 45021, + "ĠPoz": 45022, + "ĠUz": 45023, + "positron": 45024, + "ĠElectrons": 45025, + "Ġhemangi": 45026, + "ĠZnS": 45027, + "ĠTEMP": 45028, + "ĠExperimentally": 45029, + "fluorouracil": 45030, + "Ġlaparotomy": 45031, + "analyzer": 45032, + "ocorticoid": 45033, + "ĠIMPL": 45034, + "ĠDNNs": 45035, + "ĠFresnel": 45036, + "Mont": 45037, + "Ġtapes": 45038, + "ulomb": 45039, + "impedance": 45040, + "ĠHET": 45041, + "atha": 45042, + "modulation": 45043, + "ĠCortic": 45044, + "Ġâľĵ": 45045, + "ĠFairness": 45046, + "ĠStiff": 45047, + "Ġbuttons": 45048, + "css": 45049, + "Ġandroid": 45050, + "elast": 45051, + "ĠTeflon": 45052, + "ĠMBC": 45053, + "ĠJT": 45054, + "Ġmultilayered": 45055, + "ĠRee": 45056, + "uitar": 45057, + "ĠPhilips": 45058, + "ĠSkip": 45059, + "doctoral": 45060, + "iyama": 45061, + "ĠLeadership": 45062, + "ĠCrisis": 45063, + "Ġdesensitization": 45064, + "vous": 45065, + "ĠSPP": 45066, + "ĠPGA": 45067, + "ĠNever": 45068, + "Ġdefeating": 45069, + "Ġfibromyalgia": 45070, + "ĠMRP": 45071, + "ĠABCA": 45072, + "ĠLowe": 45073, + "Ġeroded": 45074, + "Ġaugments": 45075, + "ĠBoris": 45076, + "Ġnephrectomy": 45077, + "ĠSherman": 45078, + "Ġrefrigeration": 45079, + "ĠHernández": 45080, + "Ãĺ": 45081, + "ĠTors": 45082, + "chus": 45083, + "ĠVarg": 45084, + "Ġroset": 45085, + "CLR": 45086, + "DEP": 45087, + "Strong": 45088, + "Ġcinerea": 45089, + "ĠHeinrich": 45090, + "Rout": 45091, + "odus": 45092, + "ĠPhone": 45093, + "ĠPerl": 45094, + "Ġseasonally": 45095, + "holding": 45096, + "Ġencephalomyelitis": 45097, + "Ġfascia": 45098, + "Ġlittermates": 45099, + "ĠWITHOUT": 45100, + "б": 45101, + "Ġalerts": 45102, + "ĠKoll": 45103, + "ĠUrs": 45104, + "elfand": 45105, + "ĠRNAP": 45106, + "Ġinvariably": 45107, + "Ġscintigraphy": 45108, + "ĠSebastian": 45109, + "kinesia": 45110, + "CUR": 45111, + "inants": 45112, + "ĠpET": 45113, + "idial": 45114, + "ĠUPLC": 45115, + "Ġsuis": 45116, + "Ġbasolateral": 45117, + "ĠModulates": 45118, + "orbic": 45119, + "Img": 45120, + "Ġparasitism": 45121, + "Ġlaminate": 45122, + "ogeographic": 45123, + "ĠRibeiro": 45124, + "ĠGlutathione": 45125, + "ĠAberrant": 45126, + "Ġsclero": 45127, + "ĠDLS": 45128, + "ĠRuth": 45129, + "Ġrecast": 45130, + "recated": 45131, + "okie": 45132, + "ĠParks": 45133, + "Ġfoliations": 45134, + "ĠDawson": 45135, + "Ġtannins": 45136, + "ĠAaron": 45137, + "pS": 45138, + "itating": 45139, + "ĠITC": 45140, + "ipients": 45141, + "ohy": 45142, + "CCs": 45143, + "Ġethanolic": 45144, + "corhynchus": 45145, + "Ġorientational": 45146, + "Ġhabituation": 45147, + "Ġconversational": 45148, + "ĠVentricular": 45149, + "Ġintercalated": 45150, + "Ġphosphodiesterase": 45151, + "ĠSeifert": 45152, + "wk": 45153, + "algesia": 45154, + "Ġstegan": 45155, + "ĠLus": 45156, + "ophantine": 45157, + "Ġcorrects": 45158, + "ĠObama": 45159, + "latency": 45160, + "Ġsonar": 45161, + "ORMAL": 45162, + "Ġseaweed": 45163, + "ĠPowers": 45164, + "ĠShapley": 45165, + "Lore": 45166, + "Ġawa": 45167, + "alach": 45168, + "ĠFon": 45169, + "ensate": 45170, + "Ġoptima": 45171, + "INF": 45172, + "Ġpolygenic": 45173, + "Ġmesoderm": 45174, + "Conver": 45175, + "BRID": 45176, + "ĠHelp": 45177, + "ĠRasmussen": 45178, + "Ġprokaryotes": 45179, + "ĠEurasian": 45180, + "ĠPermeability": 45181, + "Ġnau": 45182, + "ĠClem": 45183, + "odilation": 45184, + "ĠDiaz": 45185, + "itious": 45186, + "ĠChad": 45187, + "ORA": 45188, + "ĠSimons": 45189, + "ĠDistances": 45190, + "Ġastrometric": 45191, + "ĠCPUs": 45192, + "Ġthioredoxin": 45193, + "perturbation": 45194, + "Ġdendrimer": 45195, + "algal": 45196, + "Ġceliac": 45197, + "asz": 45198, + "ĠPPE": 45199, + "qua": 45200, + "ĠBoll": 45201, + "chr": 45202, + "Ġpreview": 45203, + "ĠProjections": 45204, + "ĠAsians": 45205, + "ĠInferring": 45206, + "ĠNaive": 45207, + "ĠHiggins": 45208, + "ĠLocated": 45209, + "cardiac": 45210, + "ĠLarson": 45211, + "hazard": 45212, + "ĠScientists": 45213, + "Ġpinn": 45214, + "ENCY": 45215, + "forme": 45216, + "chitects": 45217, + "ofluorescent": 45218, + "ĠPortal": 45219, + "Ġpupae": 45220, + "interesting": 45221, + "įĢ": 45222, + "react": 45223, + "atos": 45224, + "enin": 45225, + "tio": 45226, + "ĠCapp": 45227, + "ĠMau": 45228, + "ĠLSC": 45229, + "ĠVlasov": 45230, + "Ġsubsum": 45231, + "Ġdeserve": 45232, + "ASD": 45233, + "Rece": 45234, + "Ġconsonant": 45235, + "Ġimpregnated": 45236, + "Ġlignocellulosic": 45237, + "Ġsows": 45238, + "lement": 45239, + "ĠTier": 45240, + "ĠMEF": 45241, + "ĠHugh": 45242, + "inck": 45243, + "pyrazole": 45244, + "ULATIONS": 45245, + "ĠALI": 45246, + "ĠDrift": 45247, + "Ġsolubilized": 45248, + "Ġdrafting": 45249, + "icyclic": 45250, + "Ġredesign": 45251, + "Ġdeliberate": 45252, + "Ġtapping": 45253, + "ĠTomas": 45254, + "ĠTunneling": 45255, + "ĠCBR": 45256, + "Ġanodes": 45257, + "ĠLSR": 45258, + "ĠNath": 45259, + "rosive": 45260, + "ĠHeidelberg": 45261, + "Ġcrushing": 45262, + "ĠShore": 45263, + "Ġmalondialdehyde": 45264, + "ĠMRD": 45265, + "ogloss": 45266, + "ncia": 45267, + "Ġgranuloma": 45268, + "Ġplaintext": 45269, + "Ġarteriovenous": 45270, + "Ġrifampicin": 45271, + "Lepidoptera": 45272, + "Oct": 45273, + "Ġlone": 45274, + "ĠAppe": 45275, + "ĠIntermitt": 45276, + "compile": 45277, + "potentials": 45278, + "ĠStandardized": 45279, + "Ġventilatory": 45280, + "Ġhypercholesterolemia": 45281, + "ĠEVALUATION": 45282, + "ked": 45283, + "xC": 45284, + "enos": 45285, + "Ġbauthorbsnm": 45286, + "ĠRost": 45287, + "mathopen": 45288, + "Ġcontested": 45289, + "Ġros": 45290, + "otho": 45291, + "Ġemits": 45292, + "erozo": 45293, + "Ġpropranolol": 45294, + "Ġexacerbate": 45295, + "Integrating": 45296, + "ĠWarsaw": 45297, + "Ñĩ": 45298, + "refractory": 45299, + "ĠMort": 45300, + "phosphonate": 45301, + "GLT": 45302, + "ĠChloride": 45303, + "ĠLUAD": 45304, + "ĠSQUID": 45305, + "ĠOBSERVATIONS": 45306, + "Ħĺ": 45307, + "agles": 45308, + "uger": 45309, + "Ġdiffusing": 45310, + "ylar": 45311, + "Ġantip": 45312, + "renormal": 45313, + "Ġsheared": 45314, + "ĠAndr": 45315, + "ymptotics": 45316, + "ĠIdentified": 45317, + "Ġflexor": 45318, + "Liouville": 45319, + "ĠCytotoxic": 45320, + "Lock": 45321, + "donald": 45322, + "ĠSHA": 45323, + "projected": 45324, + "plicial": 45325, + "Ġbasics": 45326, + "ĠCarvalho": 45327, + "Ġheterocyclic": 45328, + "Ġfluorophore": 45329, + "ĠIntrigu": 45330, + "ĠAnnealing": 45331, + "Gln": 45332, + "Hispanic": 45333, + "Ġsaus": 45334, + "ĠTCS": 45335, + "ĠHAP": 45336, + "Ġytt": 45337, + "Ġconsulting": 45338, + "rects": 45339, + "Ġinfall": 45340, + "LEV": 45341, + "triazole": 45342, + "Ġnarrowed": 45343, + "Ġamphoteric": 45344, + "ĠSorting": 45345, + "ĠMoments": 45346, + "Ġarabin": 45347, + "Ġcoconut": 45348, + "ĠIntriguingly": 45349, + "Ġpushes": 45350, + "Ġmec": 45351, + "ĠNair": 45352, + "Ġcolistin": 45353, + "ĠObtained": 45354, + "dfs": 45355, + "Ġcompetency": 45356, + "WORD": 45357, + "ĠAAS": 45358, + "ĠBNP": 45359, + "ĠHAS": 45360, + "ĠLun": 45361, + "ĠLnc": 45362, + "Ġhydrocephalus": 45363, + "Ġhomological": 45364, + "Ġcarbonic": 45365, + "ĠHiSeq": 45366, + "community": 45367, + "Ġcephalospor": 45368, + "Ġhostile": 45369, + "provide": 45370, + "Ġskyrmion": 45371, + "DAG": 45372, + "Ġcnt": 45373, + "Ġhay": 45374, + "Ġorderings": 45375, + "Ġflock": 45376, + "HEA": 45377, + "ĠNeurom": 45378, + "Ġboosts": 45379, + "ĠCardinal": 45380, + "ĠBachelor": 45381, + "Ġdecent": 45382, + "ĠYak": 45383, + "Ġcalcd": 45384, + "ĠBoer": 45385, + "Ġtranscriptomics": 45386, + "Ġrearranged": 45387, + "ĠPolymorphisms": 45388, + "ĠPrasad": 45389, + "oinositide": 45390, + "bars": 45391, + "Ġãģ": 45392, + "ĠSAA": 45393, + "Ġonion": 45394, + "agel": 45395, + "ĠHp": 45396, + "ogrel": 45397, + "divisions": 45398, + "andan": 45399, + "arias": 45400, + "Ġcolo": 45401, + "ragon": 45402, + "Ġschizophren": 45403, + "âī¡": 45404, + "Ġreplicative": 45405, + "Ġdegenerated": 45406, + "Ġsteepest": 45407, + "Volume": 45408, + "IENT": 45409, + "Public": 45410, + "Ten": 45411, + "enberger": 45412, + "ĠCoun": 45413, + "ĠEpp": 45414, + "izo": 45415, + "Ġcomplexed": 45416, + "Ġferroc": 45417, + "kenstein": 45418, + "ĠJerry": 45419, + "Ġparadoxical": 45420, + "xg": 45421, + "icer": 45422, + "osol": 45423, + "Ġannu": 45424, + "Ġankyl": 45425, + "chung": 45426, + "entious": 45427, + "Ġpreshe": 45428, + "enetic": 45429, + "ĠHealing": 45430, + "ĠParabolic": 45431, + "Ġfigs": 45432, + "ĠKinematic": 45433, + "Ġobligate": 45434, + "ĠLayout": 45435, + "Ġtelemedicine": 45436, + "ĠLennard": 45437, + "pci": 45438, + "arone": 45439, + "ĠZach": 45440, + "Ġprototyping": 45441, + "ĠMetagen": 45442, + "IMAL": 45443, + "conscious": 45444, + "Ġquadrilateral": 45445, + "ĠUncertainties": 45446, + "ĠPrefecture": 45447, + "GBM": 45448, + "rals": 45449, + "alus": 45450, + "Ġhopes": 45451, + "Ġclicks": 45452, + "ĠJD": 45453, + "lectance": 45454, + "Ġpathologists": 45455, + "ussels": 45456, + "tisone": 45457, + "CPT": 45458, + "Ġmiscon": 45459, + "ĠNeurode": 45460, + "Ġmutagenic": 45461, + "ĠMultimedia": 45462, + "Original": 45463, + "ĠDrake": 45464, + "PWM": 45465, + "Ġpiles": 45466, + "stant": 45467, + "ARA": 45468, + "ĠRING": 45469, + "modifying": 45470, + "Ġastrocyt": 45471, + "ĠCyst": 45472, + "Ġlegends": 45473, + "glucuron": 45474, + "Ġincompletely": 45475, + "ĠConfed": 45476, + "ĠDLBCL": 45477, + "ĠPapua": 45478, + "Ġcontrastive": 45479, + "ĠSIMULATION": 45480, + "ĠJuvenile": 45481, + "aggregated": 45482, + "ĠcGMP": 45483, + "ictive": 45484, + "ĠHNF": 45485, + "ĠNPV": 45486, + "ĠKoc": 45487, + "ometallic": 45488, + "mini": 45489, + "ĠQuantit": 45490, + "ĠCornell": 45491, + "Ġdeduction": 45492, + "Ġcoinciding": 45493, + "ĠIrr": 45494, + "Precision": 45495, + "Ġginseng": 45496, + "ões": 45497, + "jer": 45498, + "ĠReader": 45499, + "ĠByr": 45500, + "corrections": 45501, + "devices": 45502, + "Ġambul": 45503, + "Ġpedicle": 45504, + "ĠDependency": 45505, + "ĠStriking": 45506, + "Ġwarehouse": 45507, + "Ġrecirculation": 45508, + "Ġgonorrhoeae": 45509, + "ĠPRES": 45510, + "ĠBhar": 45511, + "Ġflushing": 45512, + "torus": 45513, + "ĠIRB": 45514, + "glycine": 45515, + "Ġmethamphetamine": 45516, + "Ġmirrored": 45517, + "ĠWilliamson": 45518, + "Ġcathodes": 45519, + "hydroxylase": 45520, + "Radio": 45521, + "Ġfurniture": 45522, + "ĠRosenberg": 45523, + "ĠNSAIDs": 45524, + "semiconductor": 45525, + "Ġasynchron": 45526, + "ĠBerm": 45527, + "ĠInten": 45528, + "ibe": 45529, + "Force": 45530, + "pathogenic": 45531, + "smokers": 45532, + "Ġdiphenyl": 45533, + "Ġи": 45534, + "Ġstandalone": 45535, + "Ġlithospheric": 45536, + "Ġtradeoffs": 45537, + "Ġantich": 45538, + "Ġthymidine": 45539, + "ĠMedicinal": 45540, + "Ġentrepreneurial": 45541, + "Ġtrapezoidal": 45542, + "ĠAsynchronous": 45543, + "tifying": 45544, + "ĠCollapse": 45545, + "ĠHEV": 45546, + "ĠFrozen": 45547, + "ĠTeichmüller": 45548, + "rocnemius": 45549, + "Ġfern": 45550, + "Ġws": 45551, + "omol": 45552, + "Ġenclosing": 45553, + "rapid": 45554, + "Ġlogged": 45555, + "varvec": 45556, + "Ġamplifying": 45557, + "differences": 45558, + "otonin": 45559, + "ĠPromoting": 45560, + "ĠFritz": 45561, + "Ġattainable": 45562, + "Ġaltim": 45563, + "ĠOGD": 45564, + "Ġthermometer": 45565, + "Solver": 45566, + "ĠBirk": 45567, + "LENBQU": 45568, + "ĠGateway": 45569, + "Ġengraftment": 45570, + "FIF": 45571, + "HSD": 45572, + "Ġrestructuring": 45573, + "ĠTensile": 45574, + "ĠCele": 45575, + "ylus": 45576, + "Ġfeather": 45577, + "Ġdrifting": 45578, + "ĠPreclinical": 45579, + "yrrole": 45580, + "Ġcommem": 45581, + "Ġfixations": 45582, + "Petsc": 45583, + "ĠIschemia": 45584, + "aA": 45585, + "asoro": 45586, + "ĠSony": 45587, + "ĠUt": 45588, + "Ġextensor": 45589, + "ĠChau": 45590, + "ĠIsotopic": 45591, + "ILI": 45592, + "CNP": 45593, + "ĠDEF": 45594, + "Ġmountainous": 45595, + "Ġsarcomas": 45596, + "ugoslav": 45597, + "CALL": 45598, + "Sensitive": 45599, + "atro": 45600, + "Ġuncoupling": 45601, + "skew": 45602, + "ĠEmissions": 45603, + "innati": 45604, + "Ġconceptualization": 45605, + "Ġowns": 45606, + "Ġsquadron": 45607, + "ĠStrengths": 45608, + "Coh": 45609, + "UAL": 45610, + "magenta": 45611, + "usb": 45612, + "ĠSPC": 45613, + "cones": 45614, + "ĠSelecting": 45615, + "ĠParish": 45616, + "Ġvalidates": 45617, + "ĠÍĹ": 45618, + "Ġposteriorly": 45619, + "omonad": 45620, + "VOL": 45621, + "jectivity": 45622, + "ĠCLO": 45623, + "ĠVTA": 45624, + "Ġunpleasant": 45625, + "Ġcareers": 45626, + "Ġautomorphic": 45627, + "ĠNanow": 45628, + "Ġasterisks": 45629, + "ĠSchulz": 45630, + "publication": 45631, + "Ġbiv": 45632, + "Ġrug": 45633, + "recognition": 45634, + "Ġreferrals": 45635, + "Ġneurones": 45636, + "ĠCaffe": 45637, + "Connor": 45638, + "ĠSheffield": 45639, + "unitinib": 45640, + "ĠAntagon": 45641, + "Ġpneumatic": 45642, + "Ġcleaner": 45643, + "ĠBAO": 45644, + "ĠScilabString": 45645, + "neighbour": 45646, + "Euler": 45647, + "ĠTuple": 45648, + "oty": 45649, + "dian": 45650, + "Ġyoga": 45651, + "Ġevanes": 45652, + "Ġstarved": 45653, + "Ġfluctuate": 45654, + "ĠBiomarker": 45655, + "Ġimpulses": 45656, + "Ġossification": 45657, + "Ġdemyelination": 45658, + "ĠSAD": 45659, + "essing": 45660, + "Ġreddish": 45661, + "Ġsynth": 45662, + "Ġcurvilinear": 45663, + "ĠDenis": 45664, + "Ġphonetic": 45665, + "Ġhammer": 45666, + "Ġepidermidis": 45667, + "Ġplagioclase": 45668, + "Ġĉ": 45669, + "Ġwolf": 45670, + "osced": 45671, + "Ġphotothermal": 45672, + "Ġchewing": 45673, + "Maximum": 45674, + "Ġmismatched": 45675, + "ĠFcγ": 45676, + "Ġumbrella": 45677, + "ĠSiberian": 45678, + "arra": 45679, + "ipped": 45680, + "ympathetic": 45681, + "acceleration": 45682, + "Ġeigenmodes": 45683, + "ĠEquivalently": 45684, + "ĠPRISMA": 45685, + "conservative": 45686, + "ñez": 45687, + "Ġvolcanoes": 45688, + "Ġtelemetry": 45689, + "mile": 45690, + "ĠBoch": 45691, + "oprim": 45692, + "Ġincipient": 45693, + "Ġunderstandable": 45694, + "atricyclo": 45695, + "ĠLogical": 45696, + "ĠQueue": 45697, + "Ġcryostat": 45698, + "definecolor": 45699, + "ĠSae": 45700, + "Ġarct": 45701, + "Ġsoul": 45702, + "ĠHistopathological": 45703, + "ĠNeurot": 45704, + "Ġmethanolic": 45705, + "Px": 45706, + "ĠTitle": 45707, + "otomic": 45708, + "ĠEld": 45709, + "ĠEMA": 45710, + "Ġdebrid": 45711, + "timulatory": 45712, + "ĠZan": 45713, + "Ġnormot": 45714, + "Ġfluidity": 45715, + "Ġfluidized": 45716, + "previously": 45717, + "Ġcracked": 45718, + "ĠExplaining": 45719, + "ĠONE": 45720, + "ĠFlora": 45721, + "ĠHybridization": 45722, + "Ġreticul": 45723, + "FK": 45724, + "notic": 45725, + "ĠnA": 45726, + "ĠPab": 45727, + "ticum": 45728, + "andy": 45729, + "ugia": 45730, + "ilet": 45731, + "MING": 45732, + "Ġrests": 45733, + "ompact": 45734, + "Ġtrackers": 45735, + "phosphatase": 45736, + "ĠTransfection": 45737, + "ĠHospitals": 45738, + "acrine": 45739, + "ĠDell": 45740, + "ĠVAE": 45741, + "ĠThroughput": 45742, + "hevsky": 45743, + "ĠSommer": 45744, + "PSA": 45745, + "ìļ": 45746, + "Ġbush": 45747, + "Ġlunch": 45748, + "ĠSwe": 45749, + "ĠInstruction": 45750, + "akami": 45751, + "Ġdisinfect": 45752, + "Ġcorps": 45753, + "ĉĉĠĠ": 45754, + "Ġprompts": 45755, + "MSH": 45756, + "ĠAgrawal": 45757, + "Ġlysosome": 45758, + "integrin": 45759, + "ĠỸ": 45760, + "Ġnondecreasing": 45761, + "ĠRequest": 45762, + "ĠREP": 45763, + "occus": 45764, + "Ġlagrangian": 45765, + "oregulation": 45766, + "ол": 45767, + "ĠBoson": 45768, + "Iso": 45769, + "atellites": 45770, + "resectable": 45771, + "riv": 45772, + "Ġdeaminase": 45773, + "Ġcoheren": 45774, + "Ġdecoy": 45775, + "ĠExtinction": 45776, + "acetone": 45777, + "Ġgovernmental": 45778, + "Ġcumulants": 45779, + "Ġviscosities": 45780, + "Register": 45781, + "documented": 45782, + "Ġimmortalized": 45783, + "DPP": 45784, + "Gel": 45785, + "bron": 45786, + "kow": 45787, + "ĠProportion": 45788, + "ĠChase": 45789, + "ĠClad": 45790, + "Ġadapts": 45791, + "ĠCAV": 45792, + "Ġż": 45793, + "Ġpelleted": 45794, + "Ġpenguin": 45795, + "ĠZhejiang": 45796, + "feasible": 45797, + "DIV": 45798, + "iya": 45799, + "Ġthrowing": 45800, + "resia": 45801, + "ĠNr": 45802, + "ESP": 45803, + "CDF": 45804, + "suppressed": 45805, + "Ġtetrachlor": 45806, + "Ġaerospace": 45807, + "Until": 45808, + "Ġpayoffs": 45809, + "Ġtownship": 45810, + "Ġesterification": 45811, + "ĠAchilles": 45812, + "Ġracem": 45813, + "opyranoside": 45814, + "ĠCSM": 45815, + "assis": 45816, + "Ġsupercell": 45817, + "ĠRegime": 45818, + "IRA": 45819, + "Ġsubsequences": 45820, + "ĠPenet": 45821, + "ĠAnalytics": 45822, + "ĠLVEF": 45823, + "Ġbiphenyl": 45824, + "Gradient": 45825, + "osylation": 45826, + "ĠWRF": 45827, + "ofs": 45828, + "conductors": 45829, + "Ġbacked": 45830, + "pidal": 45831, + "ĠNFAT": 45832, + "ĠRemember": 45833, + "Ġtelomeric": 45834, + "Ġtaurine": 45835, + "increases": 45836, + "Ġunintended": 45837, + "ĠNervous": 45838, + "Ras": 45839, + "ylyl": 45840, + "Ġaestiv": 45841, + "ĠSick": 45842, + "ĠTheta": 45843, + "Ġcliques": 45844, + "Ġsofter": 45845, + "ĠQRS": 45846, + "lliptic": 45847, + "ĠImmunotherapy": 45848, + "QUF": 45849, + "onomously": 45850, + "ĠFLU": 45851, + "ĠIncorporation": 45852, + "ĠFormicidae": 45853, + "JR": 45854, + "whole": 45855, + "Ġcasing": 45856, + "Ġnob": 45857, + "ĠDou": 45858, + "Ġintronic": 45859, + "Ġentrapment": 45860, + "orbits": 45861, + "Ġsalam": 45862, + "ĠCRS": 45863, + "ĠSwan": 45864, + "ĠEdgar": 45865, + "Ġconcomitantly": 45866, + "atetracyclo": 45867, + "ĠAHR": 45868, + "ticks": 45869, + "ĠBing": 45870, + "ĠRift": 45871, + "Ġplugging": 45872, + "ĠscRNA": 45873, + "Ġoutreach": 45874, + "inskii": 45875, + "Ġcustomary": 45876, + "Ġmd": 45877, + "ĠOzone": 45878, + "ussing": 45879, + "others": 45880, + "Ġentirety": 45881, + "Arth": 45882, + "Acet": 45883, + "ĠFleet": 45884, + "ĠBehavioural": 45885, + "ĠQSOs": 45886, + "arina": 45887, + "Ġprodrug": 45888, + "ĠBros": 45889, + "ĠWorth": 45890, + "Ġyz": 45891, + "contig": 45892, + "ĠAmorphous": 45893, + "ĠErlang": 45894, + "Ġhonour": 45895, + "ĠâIJ¥": 45896, + "Ġinfiltrates": 45897, + "ĠIvanov": 45898, + "ĠMunicipality": 45899, + "ĠDialogue": 45900, + "tone": 45901, + "Ġpytest": 45902, + "iculus": 45903, + "ĠGoth": 45904, + "ĠXC": 45905, + "ĠSUMMARY": 45906, + "Ġshrinks": 45907, + "Ġinverses": 45908, + "iomas": 45909, + "robi": 45910, + "ĠTPR": 45911, + "ĠANA": 45912, + "istries": 45913, + "Ġregiment": 45914, + "indo": 45915, + "ĠReproduction": 45916, + "loqu": 45917, + "inflation": 45918, + "ETX": 45919, + "Ġïĺ»": 45920, + "ĠAPPENDIX": 45921, + "Ġworsened": 45922, + "Ġpsoriatic": 45923, + "Ġmidwives": 45924, + "Ġtouched": 45925, + "Ëĩ": 45926, + "ĠPatric": 45927, + "ĠDON": 45928, + "ĠLIM": 45929, + "akos": 45930, + "ĠVie": 45931, + "ĠAntit": 45932, + "Ġflake": 45933, + "ĠSchle": 45934, + "ĠCoronal": 45935, + "Ġsalary": 45936, + "slight": 45937, + "ĠCAF": 45938, + "Ġsummarise": 45939, + "Ġflavus": 45940, + "ĠBalanced": 45941, + "ĠPHOT": 45942, + "Ġmillet": 45943, + "Ġurgency": 45944, + "ĠGleason": 45945, + "ĠMie": 45946, + "ĠDp": 45947, + "ĠGarg": 45948, + "Ġleprosy": 45949, + "Ġunoccupied": 45950, + "ĠStret": 45951, + "ilept": 45952, + "ĠChor": 45953, + "ibrate": 45954, + "ĠÍļ": 45955, + "ĠPHB": 45956, + "Ġmonoter": 45957, + "ĠJavaScript": 45958, + "btn": 45959, + "ĠPulsar": 45960, + "ĠKirchhoff": 45961, + "Ġoverseas": 45962, + "Ġdephosphorylation": 45963, + "ortin": 45964, + "ĠPolyakov": 45965, + "Ġinsightful": 45966, + "ĠPurified": 45967, + "Ġanchorage": 45968, + "ĠGlycoprotein": 45969, + "studies": 45970, + "Ġchronology": 45971, + "roxine": 45972, + "ĠNeptune": 45973, + "Ban": 45974, + "Ġlion": 45975, + "PSD": 45976, + "ĠBarr": 45977, + "Ġdonkey": 45978, + "Ġlikelihoods": 45979, + "atchewan": 45980, + "otet": 45981, + "ospha": 45982, + "ticism": 45983, + "Ġry": 45984, + "asthen": 45985, + "rhotic": 45986, + "ĠSubgroup": 45987, + "yev": 45988, + "ĠPatri": 45989, + "provides": 45990, + "SGD": 45991, + "berell": 45992, + "vw": 45993, + "ĠAACR": 45994, + "Ġsmears": 45995, + "ODS": 45996, + "supplemented": 45997, + "ĠEngagement": 45998, + "oglobulins": 45999, + "Ġirregularly": 46000, + "ĠSzeg": 46001, + "ĠWolff": 46002, + "Ġenantiomers": 46003, + "Ġobeying": 46004, + "Ġdestroying": 46005, + "omially": 46006, + "ĠAti": 46007, + "ĠGAT": 46008, + "ĠInvariants": 46009, + "ĠScoring": 46010, + "Ġhalides": 46011, + "Ġtransformants": 46012, + "Ġforested": 46013, + "Ġgallic": 46014, + "ĠBetti": 46015, + "threaded": 46016, + "ĠBudget": 46017, + "junctive": 46018, + "ĠInnovative": 46019, + "Ġpositrons": 46020, + "Brazil": 46021, + "eira": 46022, + "Ġlavas": 46023, + "ĠLt": 46024, + "photo": 46025, + "Ġspam": 46026, + "Ġih": 46027, + "ustering": 46028, + "Ġbioluminescence": 46029, + "ĠShapes": 46030, + "ULTI": 46031, + "triangles": 46032, + "ĠSMN": 46033, + "enhancing": 46034, + "ĠReduces": 46035, + "ĠTHEOREM": 46036, + "Dop": 46037, + "ĠdL": 46038, + "emptive": 46039, + "Ġreminder": 46040, + "Ġgonads": 46041, + "Ġxylan": 46042, + "cultures": 46043, + "tles": 46044, + "Ġtd": 46045, + "Ġerected": 46046, + "terone": 46047, + "ĠPDC": 46048, + "Ġincongruent": 46049, + "Ġmembranous": 46050, + "pac": 46051, + "yless": 46052, + "Ġsubalgebras": 46053, + "ĠChir": 46054, + "ĠZIP": 46055, + "autious": 46056, + "Ġlightly": 46057, + "ĠPhotometric": 46058, + "Transfer": 46059, + "Ġketo": 46060, + "Ġexercised": 46061, + "dispersive": 46062, + "ĠBETWEEN": 46063, + "rou": 46064, + "Ġgarbage": 46065, + "ĠMaf": 46066, + "ĠDoming": 46067, + "ĠSubspace": 46068, + "ĠMarÃŃa": 46069, + "Ġtetrahedra": 46070, + "ĠBarker": 46071, + "Side": 46072, + "bishop": 46073, + "iD": 46074, + "reversible": 46075, + "orman": 46076, + "orescein": 46077, + "ĠContrib": 46078, + "Ġderivatization": 46079, + "romeres": 46080, + "ĠALD": 46081, + "EEK": 46082, + "ĠTreating": 46083, + "combination": 46084, + "ïĺ»": 46085, + "restriction": 46086, + "supseteq": 46087, + "ĠRAPD": 46088, + "Ġamendment": 46089, + "zynski": 46090, + "Ġcaves": 46091, + "ilot": 46092, + "Ġabundantly": 46093, + "на": 46094, + "Ġinjectable": 46095, + "ĠReinforced": 46096, + "ĠWidth": 46097, + "ĠHaemophilus": 46098, + "ilane": 46099, + "props": 46100, + "Ġintervertebral": 46101, + "Ġscroll": 46102, + "Ġamput": 46103, + "ĠUnusual": 46104, + "Ġstatically": 46105, + "Ġsynergies": 46106, + "Ġdims": 46107, + "plasmic": 46108, + "Ġneutralized": 46109, + "Selected": 46110, + "Ġinherits": 46111, + "ĠAutomation": 46112, + "Ġprotoplanetary": 46113, + "Statement": 46114, + "ĠAPOBEC": 46115, + "Ġcertificates": 46116, + "ĠCitrus": 46117, + "quadruplex": 46118, + "Nord": 46119, + "Ġfran": 46120, + "ĠCarcin": 46121, + "utan": 46122, + "ĠPump": 46123, + "ĠBav": 46124, + "ĠGras": 46125, + "tingales": 46126, + "Ġcausally": 46127, + "Ġradon": 46128, + "Compare": 46129, + "Ġclamping": 46130, + "irreducible": 46131, + "IHC": 46132, + "ĠÙ": 46133, + "Ġcyp": 46134, + "ĠTPP": 46135, + "ĠSuff": 46136, + "undra": 46137, + "ĠVilla": 46138, + "Ġrelieved": 46139, + "ĠJCM": 46140, + "Ġtreaty": 46141, + "IGEN": 46142, + "ĠDevonian": 46143, + "Ġerythropo": 46144, + "RAP": 46145, + "Ġaversive": 46146, + "entate": 46147, + "odactyl": 46148, + "ĠParal": 46149, + "Ġmilled": 46150, + "Ġbioinformatic": 46151, + "okinetic": 46152, + "ĠSTRING": 46153, + "ĠPedersen": 46154, + "database": 46155, + "inorganic": 46156, + "Ġdeput": 46157, + "Ġneb": 46158, + "iped": 46159, + "Ġdiffused": 46160, + "othione": 46161, + "Ġnonstationary": 46162, + "Ġundertaking": 46163, + "ĠEnabling": 46164, + "Ġdenatured": 46165, + "Ġloader": 46166, + "ĠLyon": 46167, + "iparametric": 46168, + "Ġmeristem": 46169, + "ĠAngiogenesis": 46170, + "ĠPulsed": 46171, + "Ġexcer": 46172, + "ĠDf": 46173, + "arches": 46174, + "Ġcollide": 46175, + "ĠRelational": 46176, + "ĠNFκB": 46177, + "Metadata": 46178, + "ĠAddressing": 46179, + "Ġpercussion": 46180, + "ĠFlorence": 46181, + "Ġnymphs": 46182, + "Cn": 46183, + "storm": 46184, + "ĠGraz": 46185, + "composite": 46186, + "ĠAdmiral": 46187, + "ĠScotia": 46188, + "Ġbremsstrahlung": 46189, + "apsack": 46190, + "Ġminimizers": 46191, + "Ġmanageable": 46192, + "Ġcarboxylate": 46193, + "Ġintermediary": 46194, + "ĠBranching": 46195, + "scheduler": 46196, + "inoculated": 46197, + "ĠExtremely": 46198, + "Ġantennae": 46199, + "ĠTill": 46200, + "RESH": 46201, + "Ġopacities": 46202, + "Ġchemopre": 46203, + "Ġadenylate": 46204, + "Ġcircumstance": 46205, + "ĠHashimoto": 46206, + "ÄĽ": 46207, + "ceae": 46208, + "ĠFm": 46209, + "ĠBX": 46210, + "Ġmeantime": 46211, + "accurate": 46212, + "collinear": 46213, + "ACTIC": 46214, + "ĠSlovenia": 46215, + "Fed": 46216, + "Kh": 46217, + "Tm": 46218, + "fork": 46219, + "inology": 46220, + "lef": 46221, + "ĠDCS": 46222, + "Ġheritable": 46223, + "Ġannouncement": 46224, + "Ġbusinessman": 46225, + "Ġbortezomib": 46226, + "Ġtourist": 46227, + "ĠEtymology": 46228, + "Ġdoctrine": 46229, + "BIN": 46230, + "suffix": 46231, + "aras": 46232, + "ĠSau": 46233, + "unboldmath": 46234, + "ĠMEP": 46235, + "inker": 46236, + "Ġoptimism": 46237, + "ĠLeuc": 46238, + "efulness": 46239, + "crust": 46240, + "ĠKeys": 46241, + "ĠâϦ": 46242, + "ĠBrandt": 46243, + "âĮ¬": 46244, + "ĠSeventy": 46245, + "Ġnursery": 46246, + "Ġdeputy": 46247, + "ì": 46248, + "onis": 46249, + "amus": 46250, + "ĠCig": 46251, + "Ġexergy": 46252, + "ĠFrequent": 46253, + "Ġabor": 46254, + "ĠJazz": 46255, + "Ġstatue": 46256, + "ĠScenarios": 46257, + "Ġcytological": 46258, + "figures": 46259, + "MCI": 46260, + "dirname": 46261, + "Ġcytokinesis": 46262, + "delivery": 46263, + "ĠBowen": 46264, + "Ġflanked": 46265, + "Ġregenerating": 46266, + "ĠFerrari": 46267, + "kiss": 46268, + "ĠAval": 46269, + "ĠCIT": 46270, + "ĠMum": 46271, + "ĠLSB": 46272, + "ogging": 46273, + "Ġunited": 46274, + "Ġtritium": 46275, + "ontamination": 46276, + "coef": 46277, + "Ġpropell": 46278, + "triple": 46279, + "Ġimmense": 46280, + "Ġcomplained": 46281, + "Ġdielectrics": 46282, + "ĠCardiomy": 46283, + "Ġflooded": 46284, + "ĠCovariance": 46285, + "Attendance": 46286, + "TMP": 46287, + "Ġsob": 46288, + "ĠSonic": 46289, + "ĠFTS": 46290, + "ĠRSD": 46291, + "essors": 46292, + "ĠWon": 46293, + "iffs": 46294, + "Ġflowchart": 46295, + "ĠElemental": 46296, + "Ġìŀ": 46297, + "Ġfoliage": 46298, + "differentiated": 46299, + "ĠGlobular": 46300, + "Ġperceptron": 46301, + "candidate": 46302, + "Social": 46303, + "Witt": 46304, + "dyn": 46305, + "paces": 46306, + "ĠmGlu": 46307, + "Ġbanned": 46308, + "olinite": 46309, + "ĠFriends": 46310, + "ĠLibraries": 46311, + "unces": 46312, + "ĠReach": 46313, + "ĠSkills": 46314, + "Ġrecipes": 46315, + "Ġcannula": 46316, + "ĠOrthodox": 46317, + "ĠCarbohydrate": 46318, + "Ġaromatase": 46319, + "Åijs": 46320, + "Ġemanating": 46321, + "elected": 46322, + "Ġtense": 46323, + "ĠFLC": 46324, + "ĠLET": 46325, + "herjee": 46326, + "Ġsubband": 46327, + "ophone": 46328, + "ĠActual": 46329, + "msgs": 46330, + "EMD": 46331, + "ISON": 46332, + "leyball": 46333, + "ĠNiu": 46334, + "Ġberries": 46335, + "diagnostic": 46336, + "NER": 46337, + "ĠdΩ": 46338, + "percentage": 46339, + "ĠHerman": 46340, + "ĠGSD": 46341, + "Ġsubproblem": 46342, + "overall": 46343, + "ophor": 46344, + "Ġdelocalized": 46345, + "account": 46346, + "ĠGeographical": 46347, + "distances": 46348, + "Ġàµ": 46349, + "Ġneurotoxic": 46350, + "opodia": 46351, + "ĠDicer": 46352, + "ĠðxÃŀ": 46353, + "Ġdunes": 46354, + "Ġwhit": 46355, + "ĠImmediate": 46356, + "Ġ̸": 46357, + "Ġadhesives": 46358, + "ĠNSs": 46359, + "Ġguessing": 46360, + "ĠColumbus": 46361, + "ĠUrugu": 46362, + "behaviour": 46363, + "ĠSerbian": 46364, + "benzodioxol": 46365, + "implementation": 46366, + "osensitive": 46367, + "ĠFill": 46368, + "phage": 46369, + "recovery": 46370, + "ESR": 46371, + "Ġanalysts": 46372, + "Ġdissatisfaction": 46373, + "banded": 46374, + "ĠDepressive": 46375, + "ĠRTs": 46376, + "Refs": 46377, + "millimeter": 46378, + "ĠOlsen": 46379, + "ampton": 46380, + "ĠACA": 46381, + "ĠAvian": 46382, + "ĠFowler": 46383, + "ubini": 46384, + "estamps": 46385, + "ĠProtest": 46386, + "Connection": 46387, + "Ġmerchant": 46388, + "ĠENC": 46389, + "ĠRyu": 46390, + "ĠLymphoma": 46391, + "ĠLarry": 46392, + "Ġjaponicum": 46393, + "ĠSymbols": 46394, + "Lib": 46395, + "VG": 46396, + "ĠTav": 46397, + "ĠAssim": 46398, + "ĠLeung": 46399, + "dependency": 46400, + "largest": 46401, + "ĠDOE": 46402, + "Ġaligns": 46403, + "oflurane": 46404, + "ĠAdjusted": 46405, + "Ġpeculiarities": 46406, + "decrease": 46407, + "ĠPlacement": 46408, + "vig": 46409, + "zak": 46410, + "Ġpenta": 46411, + "Ġfres": 46412, + "Ġacros": 46413, + "Ġsolvability": 46414, + "ansions": 46415, + "ALA": 46416, + "Ġmalfunction": 46417, + "ĠGiovanni": 46418, + "AOR": 46419, + "Had": 46420, + "Ġporn": 46421, + "undice": 46422, + "ĠUi": 46423, + "Ġexpelled": 46424, + "ĠAnk": 46425, + "Ġdiscounting": 46426, + "ĠRegulating": 46427, + "astery": 46428, + "phenylethyl": 46429, + "Ġcastration": 46430, + "Ġerythromycin": 46431, + "Ġbifunctional": 46432, + "��": 46433, + "ĠAlgeria": 46434, + "mess": 46435, + "Ġwis": 46436, + "ĠTay": 46437, + "assumed": 46438, + "Ġescalation": 46439, + "Ġhydroper": 46440, + "Ġcallosum": 46441, + "Ġatomization": 46442, + "ĠSAW": 46443, + "Ġacetylcholinesterase": 46444, + "Ġsucceeds": 46445, + "Ġphysiotherapy": 46446, + "tro": 46447, + "Ġmason": 46448, + "ĠTMB": 46449, + "Ġphant": 46450, + "Ġadjusts": 46451, + "antha": 46452, + "ĠEisenstein": 46453, + "Ġshorthand": 46454, + "GABA": 46455, + "Ġprover": 46456, + "Ġpatrol": 46457, + "ĠModal": 46458, + "ollaries": 46459, + "ĠInterfacial": 46460, + "ĠCIA": 46461, + "attn": 46462, + "ĠCryptococcus": 46463, + "athecal": 46464, + "ĠFreshwater": 46465, + "Ġspectrogram": 46466, + "opidogrel": 46467, + "morphism": 46468, + "Ġrelapsing": 46469, + "Ġgeneralizable": 46470, + "ĠShale": 46471, + "ĠTransplant": 46472, + "contraction": 46473, + "URI": 46474, + "ĠPetrov": 46475, + "ĠSliding": 46476, + "Ġanteriorly": 46477, + "Ġquasilinear": 46478, + "Ġripples": 46479, + "ZP": 46480, + "bacterial": 46481, + "spr": 46482, + "animal": 46483, + "Ġreporters": 46484, + "ĠBSS": 46485, + "ĠDia": 46486, + "ĠRSC": 46487, + "ounding": 46488, + "ITHM": 46489, + "logical": 46490, + "Ġpolycarbonate": 46491, + "Animal": 46492, + "umbai": 46493, + "Ġarchived": 46494, + "ĠDurham": 46495, + "âĸĪ": 46496, + "ĠVermont": 46497, + "Ġpw": 46498, + "essen": 46499, + "Ġconstexpr": 46500, + "ĠPruss": 46501, + "Ġsharpness": 46502, + "divide": 46503, + "primitive": 46504, + "Ġacrylate": 46505, + "MYC": 46506, + "ĠMonday": 46507, + "ĠSrinivas": 46508, + "Born": 46509, + "attice": 46510, + "omorpha": 46511, + "ĠMERS": 46512, + "ĠFactory": 46513, + "ĠWN": 46514, + "rectile": 46515, + "Ġheats": 46516, + "UNK": 46517, + "Ġsynchronize": 46518, + "ĠAttenuation": 46519, + "Children": 46520, + "Pat": 46521, + "pregnant": 46522, + "Ġwished": 46523, + "Ġthawing": 46524, + "ĠBey": 46525, + "ĠDÃŃaz": 46526, + "Ġleather": 46527, + "ĠUnic": 46528, + "Ġspecialised": 46529, + "Ġcatalytically": 46530, + "PLGA": 46531, + "hydroxyethyl": 46532, + "Ġmagmas": 46533, + "Ġpronoun": 46534, + "Ġeutrophication": 46535, + "ĠWeekly": 46536, + "MHD": 46537, + "malloc": 46538, + "ecologic": 46539, + "ilo": 46540, + "ĠFrequencies": 46541, + "Ġorchestra": 46542, + "Ġmetabolomic": 46543, + "ĠBlockade": 46544, + "Ġasserted": 46545, + "ĠLewy": 46546, + "Ġalleviating": 46547, + "Ġocclusions": 46548, + "Ġchoroid": 46549, + "technical": 46550, + "Ġenvisioned": 46551, + "ĠHousing": 46552, + "Pn": 46553, + "ĠTECH": 46554, + "ĠSSH": 46555, + "ĠValle": 46556, + "ylmethyl": 46557, + "Ġphloem": 46558, + "ĠProjects": 46559, + "button": 46560, + "Ġaccelerometers": 46561, + "umni": 46562, + "ĠHandling": 46563, + "Ġvaso": 46564, + "permeable": 46565, + "Ġcords": 46566, + "ĠCf": 46567, + "ĠDz": 46568, + "Ġeditions": 46569, + "Ġhumerus": 46570, + "doors": 46571, + "Ġdorsolateral": 46572, + "Ġaptamers": 46573, + "Ġcommodities": 46574, + "osperms": 46575, + "Ġprednisone": 46576, + "IQ": 46577, + "Metal": 46578, + "tus": 46579, + "Ġisotopy": 46580, + "ĠTheater": 46581, + "iffi": 46582, + "Ġyarn": 46583, + "deletion": 46584, + "ĠQPO": 46585, + "Ġmultiobjective": 46586, + "Ġurchin": 46587, + "Ġpulsations": 46588, + "ĠSRP": 46589, + "ðtÃŀ": 46590, + "glucoside": 46591, + "Ġdepartures": 46592, + "PyObject": 46593, + "ĠBandwidth": 46594, + "ĠAcceptance": 46595, + "reys": 46596, + "ĠION": 46597, + "Ġcompuls": 46598, + "ĠJW": 46599, + "Ġparthen": 46600, + "Close": 46601, + "ĠBaTiO": 46602, + "ñoz": 46603, + "aggregate": 46604, + "Initially": 46605, + "qh": 46606, + "ĠCancers": 46607, + "opin": 46608, + "never": 46609, + "isman": 46610, + "Ġconstancy": 46611, + "Ġtrucks": 46612, + "Ġvisualisation": 46613, + "ĠIllness": 46614, + "Ġsulphide": 46615, + "ĠMetabolites": 46616, + "Ġoxysporum": 46617, + "HPP": 46618, + "Ġnoradrenaline": 46619, + "Ġcommutativity": 46620, + "Quad": 46621, + "NiO": 46622, + "ĠGetting": 46623, + "Ġbait": 46624, + "Ġë°": 46625, + "Ġmentally": 46626, + "Ġauroral": 46627, + "ĠDrawing": 46628, + "Sin": 46629, + "receiver": 46630, + "atov": 46631, + "isotope": 46632, + "Ġisothi": 46633, + "ĠSenes": 46634, + "ĠACO": 46635, + "ĠGCT": 46636, + "ysmal": 46637, + "ĠVog": 46638, + "Ġdistractors": 46639, + "Ġconnectedness": 46640, + "Ġaccumbens": 46641, + "äck": 46642, + "hydrated": 46643, + "Ġpharmacodynamic": 46644, + "Ġmineralogy": 46645, + "Ġarthropods": 46646, + "Ġmycotoxins": 46647, + "Ġbattles": 46648, + "ĠSara": 46649, + "ĠEIS": 46650, + "ĠWinn": 46651, + "Ġlimbic": 46652, + "WORK": 46653, + "Ž": 46654, + "Ġeaten": 46655, + "ĠTod": 46656, + "apillary": 46657, + "oxyp": 46658, + "ĠNewly": 46659, + "Ġcamel": 46660, + "arrison": 46661, + "ECTOR": 46662, + "Ġhopefully": 46663, + "ĠHurwitz": 46664, + "Ġibuprofen": 46665, + "ĠFIRST": 46666, + "Ġbistable": 46667, + "Ġdismissed": 46668, + "gat": 46669, + "inogen": 46670, + "ĠPON": 46671, + "phas": 46672, + "ĠKorn": 46673, + "Ġpolyaniline": 46674, + "ĠMicroscope": 46675, + "Ġmucous": 46676, + "Ġcollisionless": 46677, + "hydrogenase": 46678, + "Build": 46679, + "pairing": 46680, + "ĠWIMP": 46681, + "builtin": 46682, + "ĠSeparate": 46683, + "ĠCunningham": 46684, + "ĠNecessary": 46685, + "Ġbry": 46686, + "ecrosis": 46687, + "ĠLSS": 46688, + "Ġsyphilis": 46689, + "ĠVid": 46690, + "Ġcarrot": 46691, + "ĠResistant": 46692, + "registration": 46693, + "Ġmyopathy": 46694, + "Ġangry": 46695, + "MDR": 46696, + "Ġhypothesised": 46697, + "ĠVolterra": 46698, + "elevation": 46699, + "Ġmycobacteria": 46700, + "Ġcaudate": 46701, + "iidae": 46702, + "ĠÇ": 46703, + "ĠDich": 46704, + "ĠReth": 46705, + "ellus": 46706, + "chamber": 46707, + "shine": 46708, + "ochore": 46709, + "ĠColumns": 46710, + "COUNT": 46711, + "Ġïĥ²": 46712, + "ĠPrimordial": 46713, + "Ġnegotiations": 46714, + "stedt": 46715, + "RII": 46716, + "UES": 46717, + "tiques": 46718, + "ĠPfe": 46719, + "Ġplast": 46720, + "pron": 46721, + "ĠZw": 46722, + "inkler": 46723, + "Ġmetabolome": 46724, + "EGA": 46725, + "ĠSpectrophot": 46726, + "Ġubiquity": 46727, + "ĠElectrodes": 46728, + "Ġchondro": 46729, + "DomainIs": 46730, + "ĠResidues": 46731, + "ĠdnsDomainIs": 46732, + "DIC": 46733, + "pth": 46734, + "Ġaest": 46735, + "Ġcient": 46736, + "Ġpessim": 46737, + "Ġreinst": 46738, + "ĠSans": 46739, + "endazole": 46740, + "ĠUrine": 46741, + "Ġsubacute": 46742, + "iximab": 46743, + "Ġprofitable": 46744, + "Ġmaximise": 46745, + "ĠDelaware": 46746, + "Ġclinicopathologic": 46747, + "ThermoFisher": 46748, + "FAR": 46749, + "RAS": 46750, + "witch": 46751, + "inactivated": 46752, + "enesis": 46753, + "unless": 46754, + "ĠPanc": 46755, + "ĠMTS": 46756, + "ĠBast": 46757, + "Ġchilling": 46758, + "Ġincumbent": 46759, + "Ġjelly": 46760, + "Ġdistributive": 46761, + "Ġcyto": 46762, + "schen": 46763, + "Ġinducers": 46764, + "ĠNonequilibrium": 46765, + "ĠRobotics": 46766, + "ĠArgentine": 46767, + "Ġmeridian": 46768, + "Ġhunger": 46769, + "Adaptive": 46770, + "Ġgor": 46771, + "ilepsy": 46772, + "Ġnonvanishing": 46773, + "Ġpeti": 46774, + "ĠMetformin": 46775, + "Ġbiomaterial": 46776, + "Ġantennal": 46777, + "ĠAffective": 46778, + "ĠAquatic": 46779, + "enediamine": 46780, + "ĠSiberia": 46781, + "ĠPenicillium": 46782, + "Functions": 46783, + "Ġlec": 46784, + "Ġfeld": 46785, + "ĠSpart": 46786, + "ĠCement": 46787, + "addi": 46788, + "sek": 46789, + "ĠNp": 46790, + "olesky": 46791, + "ĠMacroscopic": 46792, + "ères": 46793, + "Ġcaveat": 46794, + "Ġcourtship": 46795, + "mice": 46796, + "Ġfence": 46797, + "Ġmined": 46798, + "ulink": 46799, + "IDA": 46800, + "Ġtruncate": 46801, + "ĠCatalan": 46802, + "Ġtranst": 46803, + "Ġamendments": 46804, + "uncertainty": 46805, + "Ġoropharyngeal": 46806, + "ĠAid": 46807, + "oulder": 46808, + "ĠIncident": 46809, + "ĠáIJ": 46810, + "angiogenesis": 46811, + "ĠBEH": 46812, + "Ġicosa": 46813, + "ĠFOXP": 46814, + "fragment": 46815, + "Ġscintillator": 46816, + "JO": 46817, + "Law": 46818, + "ĠpL": 46819, + "Ġetoposide": 46820, + "Ġpolyaden": 46821, + "Ġhabitual": 46822, + "Ġtaxi": 46823, + "Ġcumulant": 46824, + "Ġhindrance": 46825, + "trigger": 46826, + "ratios": 46827, + "ilio": 46828, + "ĠPIR": 46829, + "ĠTheod": 46830, + "ĠMorton": 46831, + "ĠHaf": 46832, + "ĠOch": 46833, + "ĠExo": 46834, + "Ġurtic": 46835, + "ĠCFRP": 46836, + "Screen": 46837, + "Slice": 46838, + "Ġmushrooms": 46839, + "Ġevanescent": 46840, + "Sx": 46841, + "ËIJ": 46842, + "ìŀ": 46843, + "Ġsigm": 46844, + "icl": 46845, + "Ġguests": 46846, + "ĠGIST": 46847, + "Ġdeformities": 46848, + "polyacrylamide": 46849, + "Significant": 46850, + "Ġimpressions": 46851, + "jmath": 46852, + "emoral": 46853, + "ĠBn": 46854, + "ĠHDR": 46855, + "ĠKeck": 46856, + "Ġvaline": 46857, + "spi": 46858, + "iterate": 46859, + "Ġsync": 46860, + "otiana": 46861, + "Interval": 46862, + "ĠBrauer": 46863, + "Ġsticky": 46864, + "ĠNeuroscience": 46865, + "Baxter": 46866, + "Ġcasts": 46867, + "allocation": 46868, + "neal": 46869, + "Ġbiop": 46870, + "Ġrestorations": 46871, + "Images": 46872, + "mitic": 46873, + "ĠElevation": 46874, + "Ġabstinence": 46875, + "ĠLesser": 46876, + "ĠRainfall": 46877, + "PAM": 46878, + "Wol": 46879, + "usch": 46880, + "Ġpromisc": 46881, + "naïve": 46882, + "Ġdeduc": 46883, + "accharide": 46884, + "Ġnominally": 46885, + "ĠExploratory": 46886, + "Ġreconciliation": 46887, + "linalg": 46888, + "TCR": 46889, + "Ġsore": 46890, + "ĠNab": 46891, + "Ġoutgroup": 46892, + "Ġmonophosphate": 46893, + "insu": 46894, + "ĠAddis": 46895, + "SPR": 46896, + "pointing": 46897, + "HERE": 46898, + "ĠTechnological": 46899, + "Ġcochlea": 46900, + "Ġspheroidal": 46901, + "ĠBaldwin": 46902, + "Feed": 46903, + "Ġfusing": 46904, + "Ġasper": 46905, + "Ġexosomal": 46906, + "ĠLinguistic": 46907, + "SCA": 46908, + "ĠEmpty": 46909, + "Ġvacant": 46910, + "glycol": 46911, + "immunoprecipitation": 46912, + "ĠITER": 46913, + "SnO": 46914, + "patterns": 46915, + "continental": 46916, + "ĠAccelerating": 46917, + "ĠAveraging": 46918, + "Ġchemoattractant": 46919, + "hb": 46920, + "sulph": 46921, + "ĠBx": 46922, + "Ġcomplicating": 46923, + "ĠWare": 46924, + "Ġsoaking": 46925, + "Ġupregulate": 46926, + "---------": 46927, + "Ġsemester": 46928, + "ĠBrod": 46929, + "Ġcascading": 46930, + "ĠCastell": 46931, + "Ġẽ": 46932, + "ĠEQUATIONS": 46933, + "Ġparsimonious": 46934, + "Ġsorbent": 46935, + "Ġeug": 46936, + "odin": 46937, + "ĠWig": 46938, + "ĠThir": 46939, + "Ġsolv": 46940, + "Ġcarboplatin": 46941, + "Ġzebra": 46942, + "venient": 46943, + "ĠmedRxiv": 46944, + "Ġautobi": 46945, + "Ġrepeatable": 46946, + "Ġmigrations": 46947, + "Ġд": 46948, + "holonomic": 46949, + "Ġmoderator": 46950, + "Ġchimera": 46951, + "ĠGrassmannian": 46952, + "ĠRonald": 46953, + "ĠVega": 46954, + "astes": 46955, + "Ġquotes": 46956, + "Ġmonic": 46957, + "Ġprecoding": 46958, + "ĠAssisted": 46959, + "ĠNetworking": 46960, + "Ġfabricating": 46961, + "Ġbotanical": 46962, + "Ġswarms": 46963, + "Ġmartensitic": 46964, + "elliptic": 46965, + "pherd": 46966, + "baryon": 46967, + "xfe": 46968, + "route": 46969, + "ĠFIL": 46970, + "opies": 46971, + "ĠPCBs": 46972, + "Ġerasure": 46973, + "ĠRemodeling": 46974, + "Ġanaer": 46975, + "Smad": 46976, + "injured": 46977, + "Ġimmunocompetent": 46978, + "dell": 46979, + "failed": 46980, + "Ġsinking": 46981, + "oracic": 46982, + "Ġdred": 46983, + "ĠVDR": 46984, + "Ġconnectors": 46985, + "Ġintratumoral": 46986, + "Ġcommutators": 46987, + "ĠAleks": 46988, + "ĠDicty": 46989, + "Ak": 46990, + "Ġrecalc": 46991, + "Ġisl": 46992, + "otrim": 46993, + "ncephal": 46994, + "ĠRees": 46995, + "Ġsteatohepatitis": 46996, + "ĠPolarized": 46997, + "SBATCH": 46998, + "ĠCrossing": 46999, + "Accuracy": 47000, + "ĠGiardia": 47001, + "ĠNovo": 47002, + "Ġvigilance": 47003, + "Ġphosphatidylcholine": 47004, + "ĠUEFA": 47005, + "Jim": 47006, + "Ġfasted": 47007, + "ĠTiny": 47008, + "Ġlang": 47009, + "issociation": 47010, + "Auto": 47011, + "ĠNorfolk": 47012, + "ĠArms": 47013, + "ĠSWI": 47014, + "ĠAmbros": 47015, + "transfection": 47016, + "Oryza": 47017, + "harm": 47018, + "ĠDs": 47019, + "Ġintrag": 47020, + "Ġcaller": 47021, + "Ġwritings": 47022, + "ĠElast": 47023, + "ĠMarvel": 47024, + "ĠImmunodeficiency": 47025, + "ĠMillion": 47026, + "Texture": 47027, + "ĠIceCube": 47028, + "snap": 47029, + "Ġenjoys": 47030, + "ĠChapel": 47031, + "ĠEstablishing": 47032, + "Actually": 47033, + "Ġphosphorylates": 47034, + "Ġchinensis": 47035, + "Ġrhabdomy": 47036, + "Ġemphysema": 47037, + "Middle": 47038, + "nant": 47039, + "Ñħ": 47040, + "Ġtart": 47041, + "lowest": 47042, + "hemia": 47043, + "Ġutilising": 47044, + "constit": 47045, + "Ġmagmatism": 47046, + "оÑĢ": 47047, + "ĠHasan": 47048, + "dispersed": 47049, + "Hear": 47050, + "Qt": 47051, + "zations": 47052, + "alon": 47053, + "ĠStau": 47054, + "ĠAmer": 47055, + "osystems": 47056, + "Ġdemarc": 47057, + "ĠNeoproterozoic": 47058, + "ĠMek": 47059, + "ĠDisclosure": 47060, + "Ġhematocrit": 47061, + "ĠCytoscape": 47062, + "Ġramification": 47063, + "Ġcommunicative": 47064, + "Ġbutterflies": 47065, + "Ġantisera": 47066, + "Ġaestivum": 47067, + "Bra": 47068, + "LTP": 47069, + "socket": 47070, + "ĠCherenkov": 47071, + "Ġchlam": 47072, + "angial": 47073, + "ultured": 47074, + "enged": 47075, + "ĠClinton": 47076, + "Ġmyoblasts": 47077, + "ĠCompensation": 47078, + "ymmetrically": 47079, + "Ġemployer": 47080, + "ozol": 47081, + "ĠSAXS": 47082, + "Ġretinas": 47083, + "piperidine": 47084, + "XYZ": 47085, + "ĠRoughly": 47086, + "Prep": 47087, + "Ġbinge": 47088, + "Ġerect": 47089, + "ĠOPER": 47090, + "Ġstressor": 47091, + "Christ": 47092, + "ĠPDZ": 47093, + "Ġsubstan": 47094, + "ĠSnail": 47095, + "Ġlamellae": 47096, + "ĠCycling": 47097, + "shifting": 47098, + "ĠHsieh": 47099, + "verify": 47100, + "Ġpreimage": 47101, + "Ġartillery": 47102, + "Ġepil": 47103, + "ĠApost": 47104, + "Ġhelmet": 47105, + "Ġmachined": 47106, + "ĠMinneapolis": 47107, + "ĠCryp": 47108, + "Ġsituational": 47109, + "passing": 47110, + "quinazolin": 47111, + "ĠCroatian": 47112, + "Ġstaircase": 47113, + "Bonnet": 47114, + "NLP": 47115, + "cium": 47116, + "Ġskeletons": 47117, + "Ġoxim": 47118, + "orib": 47119, + "Ġreticular": 47120, + "ĠSLS": 47121, + "ĠAromatic": 47122, + "ĠKes": 47123, + "Ġphor": 47124, + "Ġinvocation": 47125, + "Ġdozens": 47126, + "aively": 47127, + "Ġdetectability": 47128, + "Ġconcerted": 47129, + "yrins": 47130, + "ĠProcessor": 47131, + "Ġtolerable": 47132, + "attached": 47133, + "Ġannexin": 47134, + "ĠROSAT": 47135, + "ĠAlternate": 47136, + "ĠWavelength": 47137, + "ĠWillis": 47138, + "Ġsemicontinuous": 47139, + "Ġadvocacy": 47140, + "Ġobligation": 47141, + "chanter": 47142, + "ĠInsertion": 47143, + "Ġsymbiont": 47144, + "ZM": 47145, + "Ġtars": 47146, + "rof": 47147, + "Ġrevival": 47148, + "ĠTST": 47149, + "ĠEMP": 47150, + "Ġmex": 47151, + "ullin": 47152, + "ĠAdop": 47153, + "ĠDNAs": 47154, + "Ġemployers": 47155, + "MTs": 47156, + "ĠMartÃŃn": 47157, + "electrodes": 47158, + "ĠMedicaid": 47159, + "Ġtgt": 47160, + "Ġlognormal": 47161, + "ĠFrames": 47162, + "Ġpermissive": 47163, + "ĠArduino": 47164, + "Ġsemilinear": 47165, + "ĠAssign": 47166, + "ĠPrEP": 47167, + "ĠSiamese": 47168, + "benzimidazol": 47169, + "connectivity": 47170, + "ĠPEI": 47171, + "Ġbisulfite": 47172, + "Ġacetyltransferase": 47173, + "Ġswimmer": 47174, + "juven": 47175, + "Ġjejunum": 47176, + "ĠCincinnati": 47177, + "tai": 47178, + "ĠQI": 47179, + "ĠCommut": 47180, + "spacing": 47181, + "Ġaffords": 47182, + "itisation": 47183, + "elasticity": 47184, + "Ġdragon": 47185, + "Ġproteasomal": 47186, + "Ġpant": 47187, + "ĠNitro": 47188, + "Ġspic": 47189, + "Ġnanopl": 47190, + "ĠAllied": 47191, + "Ġthorax": 47192, + "ĠFTO": 47193, + "ĠJurkat": 47194, + "chiatry": 47195, + "young": 47196, + "directions": 47197, + "Ġneocortex": 47198, + "ĠKik": 47199, + "ango": 47200, + "clay": 47201, + "iodo": 47202, + "Ġabovementioned": 47203, + "ĠGuardian": 47204, + "Conjecture": 47205, + "ĠTrend": 47206, + "Ġfertilized": 47207, + "ĠSulfate": 47208, + "ochronology": 47209, + "Ġcraniofacial": 47210, + "ĠSaskatchewan": 47211, + "QQ": 47212, + "hman": 47213, + "Ġzym": 47214, + "logs": 47215, + "Ġïģ®": 47216, + "Ġgraduating": 47217, + "pinene": 47218, + "ĠîĢ": 47219, + "Ġetiological": 47220, + "ĠComprehension": 47221, + "Ġwandering": 47222, + "Ġlan": 47223, + "Ġsyst": 47224, + "returns": 47225, + "MOF": 47226, + "choalveolar": 47227, + "ĠArmen": 47228, + "Ġbimetallic": 47229, + "ĠPollen": 47230, + "Files": 47231, + "Ġssp": 47232, + "ENSI": 47233, + "ĠYus": 47234, + "Ġfinest": 47235, + "AGEN": 47236, + "Ġmicrobiomes": 47237, + "Ġpalind": 47238, + "Ġpetals": 47239, + "ĠRadiotherapy": 47240, + "ophenone": 47241, + "speaker": 47242, + "Ġcopepods": 47243, + "Ġkanamycin": 47244, + "Ġdegranulation": 47245, + "Construct": 47246, + "alter": 47247, + "ĠFgf": 47248, + "ĠNBS": 47249, + "ĠIncomplete": 47250, + "Ġparcel": 47251, + "neau": 47252, + "ĠÃIJ": 47253, + "ĠCHA": 47254, + "Ġduals": 47255, + "Ġsilicates": 47256, + "ĠGlobally": 47257, + "Ġkinesin": 47258, + "fid": 47259, + "ĠCPD": 47260, + "ĠYad": 47261, + "Ġdepress": 47262, + "ODY": 47263, + "ĠHistograms": 47264, + "ĠSummarization": 47265, + "automatic": 47266, + "ĠDomin": 47267, + "otransformation": 47268, + "Ġventricles": 47269, + "Widget": 47270, + "ĠPetersburg": 47271, + "Ġcholangiocarcinoma": 47272, + "Ġnectar": 47273, + "PIC": 47274, + "Scope": 47275, + "Tek": 47276, + "nitz": 47277, + "ĠPHD": 47278, + "Ġspiro": 47279, + "ĠCOG": 47280, + "ĠDioxide": 47281, + "conductivity": 47282, + "ĠGranger": 47283, + "ĠWearable": 47284, + "ĠKenneth": 47285, + "CCR": 47286, + "LINK": 47287, + "ĠÜ": 47288, + "retic": 47289, + "lya": 47290, + "Ġdemocratic": 47291, + "Ġradiograph": 47292, + "ĠRelax": 47293, + "ĠIncubation": 47294, + "ĠDenoising": 47295, + "COLOR": 47296, + "ĠClosure": 47297, + "HMM": 47298, + "urd": 47299, + "rada": 47300, + "ĠRv": 47301, + "ĠLuz": 47302, + "alls": 47303, + "Ġmultispectral": 47304, + "INED": 47305, + "SCN": 47306, + "Ġdyslexia": 47307, + "Ġsettlers": 47308, + "ĠVLSI": 47309, + "Ġavid": 47310, + "Ġlarynx": 47311, + "ĠChess": 47312, + "ĠFAA": 47313, + "Ġdefender": 47314, + "Ġlipolysis": 47315, + "ĠElmer": 47316, + "ĠAffymetrix": 47317, + "Ġrhodamine": 47318, + "Morph": 47319, + "Site": 47320, + "purity": 47321, + "ĠÊ": 47322, + "ĠTank": 47323, + "ĠMiao": 47324, + "Ġrecrystall": 47325, + "Weyl": 47326, + "ĠGuil": 47327, + "Ġmisfolded": 47328, + "suited": 47329, + "ĠApproximations": 47330, + "ĠABCB": 47331, + "donor": 47332, + "GWAS": 47333, + "---------------": 47334, + "Ġputida": 47335, + "Ġimpingement": 47336, + "yaml": 47337, + "Hill": 47338, + "Ġtl": 47339, + "agua": 47340, + "timing": 47341, + "Ġregenerate": 47342, + "Ġmultilingual": 47343, + "rador": 47344, + "classifier": 47345, + "ĠJohansson": 47346, + "Ġsulfides": 47347, + "hammer": 47348, + "Ġwalked": 47349, + "Ġallocating": 47350, + "ĠGustav": 47351, + "Ġimmunoprecipitated": 47352, + "ĠBrisbane": 47353, + "Ġsandwiched": 47354, + "ĠChatterjee": 47355, + "omandibular": 47356, + "Ġosc": 47357, + "Ġassass": 47358, + "Ġmultistage": 47359, + "Ġmultipartite": 47360, + "Ġpigmented": 47361, + "ĠVisualizing": 47362, + "Keys": 47363, + "pipeline": 47364, + "Ġdubbed": 47365, + "Ġcroc": 47366, + "ĠDLC": 47367, + "ĠRAT": 47368, + "ĠNex": 47369, + "plica": 47370, + "tingham": 47371, + "ĠSpider": 47372, + "Ġuncle": 47373, + "auts": 47374, + "ĠHowe": 47375, + "Ġarthropod": 47376, + "ĠPapad": 47377, + "urgy": 47378, + "Ġacclim": 47379, + "Broad": 47380, + "acer": 47381, + "vez": 47382, + "ĠDivers": 47383, + "Ġmodifiable": 47384, + "Ġantipsychotics": 47385, + "Prog": 47386, + "osahexa": 47387, + "ambrian": 47388, + "ĠIonization": 47389, + "ZA": 47390, + "oate": 47391, + "Ġpays": 47392, + "Ġewes": 47393, + "Ġbeaches": 47394, + "Ġevil": 47395, + "ĠCDs": 47396, + "naud": 47397, + "Ġconformity": 47398, + "ĠDMN": 47399, + "Ġcollaborate": 47400, + "Ġdeteriorate": 47401, + "VALID": 47402, + "ĠVegas": 47403, + "Ġultracent": 47404, + "BRA": 47405, + "Rub": 47406, + "YC": 47407, + "fh": 47408, + "åľ": 47409, + "ĠOWL": 47410, + "oseismic": 47411, + "oferrin": 47412, + "ochthon": 47413, + "ĠTNFR": 47414, + "smallsetminus": 47415, + "ĠArgument": 47416, + "Ġgranulocytes": 47417, + "Ġramified": 47418, + "Ġepiphy": 47419, + "fusc": 47420, + "ecdot": 47421, + "Ġhw": 47422, + "ĠNMS": 47423, + "ercus": 47424, + "Ġtether": 47425, + "ĠTrait": 47426, + "AgCl": 47427, + "ĠNearby": 47428, + "Ġhelminth": 47429, + "Ġlaevis": 47430, + "ĠBAR": 47431, + "ĠNancy": 47432, + "ĠGyn": 47433, + "Ġsecreting": 47434, + "Stellar": 47435, + "Ġsilhou": 47436, + "IMT": 47437, + "Ġscaffolding": 47438, + "ĠConverter": 47439, + "hid": 47440, + "Ġnud": 47441, + "estrian": 47442, + "anno": 47443, + "Ġdepiction": 47444, + "oremost": 47445, + "ĠShand": 47446, + "ABCD": 47447, + "ĠPDL": 47448, + "Ġdysphagia": 47449, + "Ġintrat": 47450, + "Ġhemip": 47451, + "Ġadaptable": 47452, + "longmapsto": 47453, + "ssbauer": 47454, + "ĠMcCarthy": 47455, + "ĠAutoimmune": 47456, + "ĠCutaneous": 47457, + "Inserting": 47458, + "Material": 47459, + "ĠAa": 47460, + "ĠGav": 47461, + "Ġmonocular": 47462, + "equil": 47463, + "ĠGeoff": 47464, + "Ġtethered": 47465, + "obilized": 47466, + "ĠShortly": 47467, + "Details": 47468, + "Ġrefugee": 47469, + "Ġabscisic": 47470, + "FBQyx": 47471, + "Ġdemocracy": 47472, + "crafted": 47473, + "difluor": 47474, + "yder": 47475, + "essment": 47476, + "Ġhistopathologic": 47477, + "Ġastrocytic": 47478, + "Ġwithdrew": 47479, + "Ġmoles": 47480, + "athic": 47481, + "mono": 47482, + "manual": 47483, + "Ġfoodborne": 47484, + "ĠRepository": 47485, + "Ġcovert": 47486, + "OTE": 47487, + "Ġtightness": 47488, + "Ġinstantiated": 47489, + "Ġwatermarking": 47490, + "Ġartemisinin": 47491, + "Language": 47492, + "OES": 47493, + "cant": 47494, + "already": 47495, + "unts": 47496, + "itia": 47497, + "ĠKaren": 47498, + "Ġalluvial": 47499, + "stratigraphy": 47500, + "ĠPIV": 47501, + "ĠFaces": 47502, + "ĠBim": 47503, + "applications": 47504, + "tails": 47505, + "Ġeld": 47506, + "IRB": 47507, + "ĠINTE": 47508, + "ĠNotImplemented": 47509, + "Ġmisclassified": 47510, + "Ġfertilizers": 47511, + "ĠElectricity": 47512, + "Ġtributaries": 47513, + "ĠDeutsch": 47514, + "Ġsleeve": 47515, + "fuzzy": 47516, + "ĠMTL": 47517, + "ĠBres": 47518, + "ĠWyn": 47519, + "Ġkyr": 47520, + "neuronal": 47521, + "oxymethyl": 47522, + "disorder": 47523, + "inches": 47524, + "ramidal": 47525, + "Ġpolyimide": 47526, + "ResNet": 47527, + "ĠEdmund": 47528, + "Ġdegeneracies": 47529, + "utherford": 47530, + "Dropout": 47531, + "ijĢ": 47532, + "Ġvoiced": 47533, + "ĠGomes": 47534, + "ivities": 47535, + "conductance": 47536, + "compl": 47537, + "vecs": 47538, + "Ġtuna": 47539, + "ĠKinect": 47540, + "Ġconveyed": 47541, + "Ġsphingosine": 47542, + "bat": 47543, + "ĠPurs": 47544, + "ounded": 47545, + "ĠStam": 47546, + "ĠXIII": 47547, + "ĠComics": 47548, + "MSM": 47549, + "SSL": 47550, + "Ġperfluor": 47551, + "Ġfluorinated": 47552, + "folios": 47553, + "Ġreposition": 47554, + "ĠSerr": 47555, + "ĠCors": 47556, + "ĠLabs": 47557, + "Ġcox": 47558, + "ĠAcquired": 47559, + "Ġreasoned": 47560, + "Genome": 47561, + "ĠPiper": 47562, + "Ġcompactified": 47563, + "Ġherbivore": 47564, + "lofenac": 47565, + "Ġboss": 47566, + "ĠBs": 47567, + "ĠEMR": 47568, + "Ġshoe": 47569, + "Ġcarers": 47570, + "Chrom": 47571, + "SVP": 47572, + "ĠTriangle": 47573, + "Ġhematite": 47574, + "dorf": 47575, + "ĠMovements": 47576, + "ĠVesicles": 47577, + "Olympus": 47578, + "Mol": 47579, + "Ġlend": 47580, + "uras": 47581, + "ĠASE": 47582, + "ĠWKB": 47583, + "proved": 47584, + "ĠKV": 47585, + "ĠUART": 47586, + "logarithmic": 47587, + "ĠADI": 47588, + "ĠDoing": 47589, + "Ġcease": 47590, + "Ġlengthening": 47591, + "Ġpyrophosphate": 47592, + "Fre": 47593, + "ĠCLD": 47594, + "ĠMLS": 47595, + "ĠPlum": 47596, + "Ġpropionate": 47597, + "ĠGuatem": 47598, + "CKD": 47599, + "Ġisos": 47600, + "ĠManning": 47601, + "neuro": 47602, + "OPER": 47603, + "ĠWilhelm": 47604, + "Ġacademia": 47605, + "AChR": 47606, + "ĠInertial": 47607, + "Occ": 47608, + "ujan": 47609, + "onas": 47610, + "Ġinulin": 47611, + "icia": 47612, + "andal": 47613, + "ĠKahn": 47614, + "Ġunmanned": 47615, + "ĠCoarse": 47616, + "Ġguilty": 47617, + "ĠPei": 47618, + "ĠLuca": 47619, + "ĠFibroblast": 47620, + "avian": 47621, + "vx": 47622, + "Ġdizziness": 47623, + "ĠDox": 47624, + "ĠHour": 47625, + "Ġdecoration": 47626, + "Ġverifier": 47627, + "rado": 47628, + "Ġfootprints": 47629, + "Ġdispensable": 47630, + "ĠAnaerobic": 47631, + "IoT": 47632, + "ĠRisks": 47633, + "ĠGLS": 47634, + "Ġchords": 47635, + "oidy": 47636, + "Ġneurolog": 47637, + "ruh": 47638, + "Ġvirtualization": 47639, + "Ġprotonation": 47640, + "ĠConstantin": 47641, + "Ġkeypoints": 47642, + "Buck": 47643, + "Hopf": 47644, + "Much": 47645, + "regime": 47646, + "Ġpromised": 47647, + "aij": 47648, + "ĠDesulf": 47649, + "ĠFormulas": 47650, + "Ġhump": 47651, + "lnc": 47652, + "ĠSuicide": 47653, + "ĠHOMA": 47654, + "oglycer": 47655, + "ĠProteomics": 47656, + "Ġdictate": 47657, + "ĠSpermat": 47658, + "Fun": 47659, + "Ġsag": 47660, + "ĠFam": 47661, + "eppe": 47662, + "ĠJah": 47663, + "Ġarisen": 47664, + "opharmaceutical": 47665, + "SAGE": 47666, + "ĠTHIS": 47667, + "enhance": 47668, + "Ġnapus": 47669, + "roe": 47670, + "ensch": 47671, + "deformation": 47672, + "bones": 47673, + "ĠErnest": 47674, + "irability": 47675, + "decom": 47676, + "Ġcrustaceans": 47677, + "Ġguaranteeing": 47678, + "OVAs": 47679, + "ĠMulticenter": 47680, + "ĠctDNA": 47681, + "Ġforaminifera": 47682, + "Linn": 47683, + "Ġcups": 47684, + "esch": 47685, + "ĠdF": 47686, + "ĠTah": 47687, + "pll": 47688, + "projects": 47689, + "ĠUCI": 47690, + "Ġhumanized": 47691, + "Ġabsl": 47692, + "ĠScho": 47693, + "Ġliterals": 47694, + "ĠSVR": 47695, + "Ġtoxicology": 47696, + "pgf": 47697, + "ĠIPTG": 47698, + "ĠMEASUREM": 47699, + "oing": 47700, + "ĠPasc": 47701, + "ĠBau": 47702, + "ĠWannier": 47703, + "Ġhypre": 47704, + "attributes": 47705, + "Ġpreconditioner": 47706, + "Writing": 47707, + "Ġgypsum": 47708, + "yuan": 47709, + "Ġupregulates": 47710, + "Ġtelec": 47711, + "ĠDiscre": 47712, + "guard": 47713, + "Ġdebates": 47714, + "Ġparasitoid": 47715, + "Lam": 47716, + "tige": 47717, + "Ġisopropanol": 47718, + "ĠIwas": 47719, + "plify": 47720, + "indolin": 47721, + "ĠApollo": 47722, + "Ġlanded": 47723, + "Ġbeamline": 47724, + "Union": 47725, + "Ġreciproc": 47726, + "ĠRossby": 47727, + "principal": 47728, + "Ġdescendant": 47729, + "ĠAnalogously": 47730, + "Ġderegulation": 47731, + "DSM": 47732, + "cta": 47733, + "Ġrebuilt": 47734, + "ĠMund": 47735, + "ĠFEC": 47736, + "ryn": 47737, + "plice": 47738, + "ĠYugoslav": 47739, + "ĠNorthwestern": 47740, + "ĠHomogen": 47741, + "ĠLISA": 47742, + "Ġinvestor": 47743, + "HSA": 47744, + "HPO": 47745, + "Ġdictionaries": 47746, + "ĠCategor": 47747, + "Ġcompacted": 47748, + "tilled": 47749, + "ç»": 47750, + "Ġfines": 47751, + "urans": 47752, + "Ġbetweenness": 47753, + "ĠZig": 47754, + "schema": 47755, + "Ġcommune": 47756, + "ĠQuinn": 47757, + "Ġanaphylaxis": 47758, + "TIES": 47759, + "Ġsnowpack": 47760, + "ĠDOA": 47761, + "agos": 47762, + "ĠOdd": 47763, + "arde": 47764, + "Ġevoke": 47765, + "ĠOcular": 47766, + "Ġfaulting": 47767, + "Ġvolcanism": 47768, + "ĠPaleozoic": 47769, + "Ġmycelium": 47770, + "ĠAdjustment": 47771, + "ICT": 47772, + "Nov": 47773, + "alias": 47774, + "ĠTul": 47775, + "ĠHh": 47776, + "Ġevade": 47777, + "ORs": 47778, + "Ġstrengthens": 47779, + "ĠUSGS": 47780, + "Ġlicensing": 47781, + "ĠClement": 47782, + "ĠPhytophthora": 47783, + "rified": 47784, + "Ġeighteen": 47785, + "Ġtops": 47786, + "ĠCLP": 47787, + "Ġstabilities": 47788, + "ĠPPT": 47789, + "ĠBIN": 47790, + "ĠRak": 47791, + "Ġgenistein": 47792, + "volve": 47793, + "Ġquicker": 47794, + "ĠCaused": 47795, + "benefit": 47796, + "YB": 47797, + "lift": 47798, + "Ġhood": 47799, + "ĠSCs": 47800, + "ofa": 47801, + "ĠMicron": 47802, + "angiotensin": 47803, + "Ġfeathers": 47804, + "Ġantiferromagnet": 47805, + "DECREF": 47806, + "yledons": 47807, + "Ġmyriad": 47808, + "Ġiz": 47809, + "ĠTrough": 47810, + "âĪ«": 47811, + "hemoglobin": 47812, + "ĠEnvelope": 47813, + "ĠClick": 47814, + "soliton": 47815, + "ĠSynchrotron": 47816, + "Ġlagged": 47817, + "MYB": 47818, + "Ġtrophoblast": 47819, + "Ġinterrogation": 47820, + "onvuls": 47821, + "Bac": 47822, + "Ġaperiodic": 47823, + "Ġgpu": 47824, + "Ġpropidium": 47825, + "teps": 47826, + "ĠKarp": 47827, + "ĠVaz": 47828, + "ackage": 47829, + "onson": 47830, + "Instr": 47831, + "filer": 47832, + "rifugation": 47833, + "KOV": 47834, + "fourth": 47835, + "Ġôı¼IJ": 47836, + "hyperbolic": 47837, + "schetz": 47838, + "Discussion": 47839, + "ĠOriented": 47840, + "jad": 47841, + "Ġauctions": 47842, + "usivity": 47843, + "ĠCran": 47844, + "Ġkd": 47845, + "Ġintest": 47846, + "rosarcoma": 47847, + "ugger": 47848, + "ĠILP": 47849, + "ĠSTA": 47850, + "Ġreversals": 47851, + "Ġgrapes": 47852, + "ĠPopulus": 47853, + "ĠKitaev": 47854, + "ĠAVP": 47855, + "Previously": 47856, + "Ġquadratically": 47857, + "ĠLOCAL": 47858, + "Bert": 47859, + "PED": 47860, + "live": 47861, + "à¬": 47862, + "Ġbidding": 47863, + "Ġtoss": 47864, + "ento": 47865, + "Ġthylak": 47866, + "Ġcomprehend": 47867, + "Ġdive": 47868, + "Ġapplicants": 47869, + "ĠÄħ": 47870, + "ĠVolcanic": 47871, + "adaptation": 47872, + "Ġá¹Ģ": 47873, + "ĠJanssen": 47874, + "Ġadjoining": 47875, + "ozolomide": 47876, + "CIS": 47877, + "dC": 47878, + "ducted": 47879, + "ĠAnast": 47880, + "ĠEmployment": 47881, + "ĠEndocrine": 47882, + "siloxane": 47883, + "Session": 47884, + "ĠNarr": 47885, + "ĠâĪĴâĪĨ": 47886, + "deev": 47887, + "othiaz": 47888, + "ringing": 47889, + "pointed": 47890, + "Ġacetylene": 47891, + "Ġglobulin": 47892, + "packing": 47893, + "ĠUses": 47894, + "AES": 47895, + "Hen": 47896, + "ĠSavage": 47897, + "ĠCanc": 47898, + "isto": 47899, + "ĠChromosomal": 47900, + "Ġcemented": 47901, + "Ġpyrox": 47902, + "ĠConstitutive": 47903, + "Ġphthalate": 47904, + "mechanism": 47905, + "Ġcyclosporine": 47906, + "PAP": 47907, + "arted": 47908, + "ĠRDT": 47909, + "Ġplains": 47910, + "Clone": 47911, + "propanol": 47912, + "regularity": 47913, + "Ġcotangent": 47914, + "ĠLeslie": 47915, + "ĠNitrate": 47916, + "ĠKawasaki": 47917, + "ĠPageRank": 47918, + "Ġanhydrase": 47919, + "ĠKrishna": 47920, + "Ġhemicellulose": 47921, + "Ġery": 47922, + "llis": 47923, + "Ġmicrogram": 47924, + "ĠDeligne": 47925, + "Ġenforces": 47926, + "Ġthrombolysis": 47927, + "Parse": 47928, + "orvastatin": 47929, + "Ġmated": 47930, + "ĠCrystalline": 47931, + "Ġautoradi": 47932, + "Ġthermophilic": 47933, + "infectious": 47934, + "Ġultram": 47935, + "ĠMLL": 47936, + "ĠFibers": 47937, + "Ġulceration": 47938, + "omedial": 47939, + "stratigraphic": 47940, + "Ġtouches": 47941, + "rhe": 47942, + "Ġtame": 47943, + "ĠCulic": 47944, + "ARDS": 47945, + "chter": 47946, + "Ġcounterclockwise": 47947, + "Ġcamps": 47948, + "VDC": 47949, + "Ġmethadone": 47950, + "dependently": 47951, + "validate": 47952, + "Ġprecludes": 47953, + "Ġparliamentary": 47954, + "ĠINTEREST": 47955, + "ĠSerg": 47956, + "ĠCBC": 47957, + "erella": 47958, + "ayi": 47959, + "ĠRAB": 47960, + "Ġchym": 47961, + "Ġnanospheres": 47962, + "Ġdiabetics": 47963, + "conservation": 47964, + "Ġpermeate": 47965, + "plotted": 47966, + "Ġnaphthalene": 47967, + "ĠBonn": 47968, + "ĠElectrostatic": 47969, + "Ġinventories": 47970, + "Gaussianity": 47971, + "ĠAdenosine": 47972, + "Delay": 47973, + "ĠBeginning": 47974, + "Ġsided": 47975, + "ĠCushing": 47976, + "ĠHv": 47977, + "Ġcoined": 47978, + "ĠAlm": 47979, + "scanning": 47980, + "fertil": 47981, + "Ġαv": 47982, + "ĠReactivity": 47983, + "Ġproximate": 47984, + "dependencies": 47985, + "Ġdensification": 47986, + "Ġôı¼ij": 47987, + "Ġbacteriocin": 47988, + "weakly": 47989, + "Ġdentistry": 47990, + "ĠOriental": 47991, + "Ġdormant": 47992, + "ĠpC": 47993, + "Ġmum": 47994, + "REs": 47995, + "Ġconval": 47996, + "Ġbiota": 47997, + "Ġmultilinear": 47998, + "ĠPTFE": 47999, + "Ġnarrowband": 48000, + "ĠRotational": 48001, + "Ġhoneybee": 48002, + "ĠChlorophyll": 48003, + "Baseline": 48004, + "Fern": 48005, + "Ġlk": 48006, + "ĠMash": 48007, + "rived": 48008, + "ĠBases": 48009, + "ĠDah": 48010, + "ĠKui": 48011, + "ĠÃĵ": 48012, + "ĠRecycl": 48013, + "AGN": 48014, + "PDE": 48015, + "Ġclimatological": 48016, + "ĠBasically": 48017, + "conserved": 48018, + "absorbing": 48019, + "ĠKoszul": 48020, + "oussines": 48021, + "Ġmdx": 48022, + "ithymia": 48023, + "ĠHinton": 48024, + "Ġkh": 48025, + "Ġadmittance": 48026, + "ĠVy": 48027, + "Ġextrema": 48028, + "Ġcreftype": 48029, + "subst": 48030, + "Ġbleomycin": 48031, + "LINEAR": 48032, + "AQ": 48033, + "iom": 48034, + "Ġnong": 48035, + "opian": 48036, + "sein": 48037, + "udal": 48038, + "Ġearning": 48039, + "Ġstandardize": 48040, + "ĠParticular": 48041, + "Ġwavevector": 48042, + "dxdy": 48043, + "ĠMacDonald": 48044, + "ĠEstuary": 48045, + "validated": 48046, + "ĠHurst": 48047, + "ĠMukherjee": 48048, + "Ġbivalves": 48049, + "Ġjugular": 48050, + "Ub": 48051, + "vill": 48052, + "enough": 48053, + "Ġinforms": 48054, + "anatomical": 48055, + "ulou": 48056, + "resa": 48057, + "ĠPMC": 48058, + "ĠMira": 48059, + "ĠRPL": 48060, + "ĠSDC": 48061, + "Ġhemi": 48062, + "MoS": 48063, + "ĠFloat": 48064, + "Ġocclusal": 48065, + "ĠRainbow": 48066, + "ĠProviding": 48067, + "Ġsupercapacitor": 48068, + "osf": 48069, + "ĠIRT": 48070, + "Ġadm": 48071, + "Ġdecoders": 48072, + "ĠXR": 48073, + "ĠRescue": 48074, + "Ġentom": 48075, + "Ġmortal": 48076, + "Angle": 48077, + "India": 48078, + "ĠMali": 48079, + "Ġinspecting": 48080, + "ĠGALAXY": 48081, + "ĠEriks": 48082, + "YF": 48083, + "rings": 48084, + "Ġsir": 48085, + "Ġgsl": 48086, + "ĠBubble": 48087, + "ĠDCA": 48088, + "ĠWidespread": 48089, + "assignment": 48090, + "Ġgeomorph": 48091, + "ĠPreference": 48092, + "COPD": 48093, + "processors": 48094, + "cutoff": 48095, + "ĠFlower": 48096, + "phenomen": 48097, + "music": 48098, + "ĠSlovakia": 48099, + "Supporting": 48100, + "blow": 48101, + "edit": 48102, + "ĠTrophy": 48103, + "ĠASF": 48104, + "ĠMoses": 48105, + "Ġindels": 48106, + "Ġnonhuman": 48107, + "Ġhandic": 48108, + "Ġrepairing": 48109, + "Ġmicrometer": 48110, + "ĠPhilippe": 48111, + "Ġexudates": 48112, + "ĠâĹĭ": 48113, + "Ġamalgam": 48114, + "Kin": 48115, + "fors": 48116, + "fron": 48117, + "Ġanabolic": 48118, + "ĠEich": 48119, + "NAN": 48120, + "Ġpseudogap": 48121, + "analyzed": 48122, + "Ġtackled": 48123, + "aginous": 48124, + "Ġlubricant": 48125, + "Ġradionuclides": 48126, + "arrestin": 48127, + "oussinesq": 48128, + "Lif": 48129, + "Î¥": 48130, + "received": 48131, + "astive": 48132, + "ĠPBC": 48133, + "Ġamoxicillin": 48134, + "copper": 48135, + "ubling": 48136, + "ophages": 48137, + "ĠSeas": 48138, + "ĠElite": 48139, + "PMMA": 48140, + "Ġcholang": 48141, + "Depending": 48142, + "Ġasbestos": 48143, + "ĠFecal": 48144, + "ĠRath": 48145, + "ĠLey": 48146, + "Ġfactored": 48147, + "bbles": 48148, + "Ġtokenizer": 48149, + "Ġofficinalis": 48150, + "ĠNUCLE": 48151, + "ĠSemicon": 48152, + "ĠBous": 48153, + "ĠRis": 48154, + "Ġloans": 48155, + "ACP": 48156, + "âĻĢ": 48157, + "phosate": 48158, + "Ġcherry": 48159, + "anan": 48160, + "arre": 48161, + "ĠCredit": 48162, + "isexual": 48163, + "ĠActa": 48164, + "ĠLetting": 48165, + "ĠInfarction": 48166, + "ĠAccounting": 48167, + "Ġcounterstained": 48168, + "Ġaerogel": 48169, + "standardized": 48170, + "Ġlyase": 48171, + "segments": 48172, + "Ġbachelor": 48173, + "Ġhue": 48174, + "ĠNETs": 48175, + "Ġunadjusted": 48176, + "Ġmicrohardness": 48177, + "Ġsinglets": 48178, + "ĠSPACE": 48179, + "ĠHydraulic": 48180, + "METHOD": 48181, + "ĠBjör": 48182, + "ĠKU": 48183, + "Ġrepur": 48184, + "Ġradiocarbon": 48185, + "Ġheterogeneities": 48186, + "Ġgastrocnemius": 48187, + "ĠLTD": 48188, + "Ġaccidentally": 48189, + "Processing": 48190, + "Doppler": 48191, + "TBI": 48192, + "Ġlingual": 48193, + "ĠAGS": 48194, + "ĠFrontal": 48195, + "ĠBrack": 48196, + "thema": 48197, + "Ġrepresentable": 48198, + "Ġpressurized": 48199, + "ADR": 48200, + "ĠMicrofluid": 48201, + "Ġê°": 48202, + "Ġreusable": 48203, + "Ġvendor": 48204, + "aller": 48205, + "Ġdiversion": 48206, + "FAST": 48207, + "ĠKirby": 48208, + "ĠStimulus": 48209, + "Ġattachments": 48210, + "ĠBridging": 48211, + "ĠRoberto": 48212, + "Ġqueuing": 48213, + "tling": 48214, + "roots": 48215, + "ĠMx": 48216, + "ĠMarrow": 48217, + "ĠLocus": 48218, + "Ġunimportant": 48219, + "ergarten": 48220, + "ÃŃk": 48221, + "ĠPotent": 48222, + "ĠBrunswick": 48223, + "ĠSCT": 48224, + "ĠMour": 48225, + "emias": 48226, + "ĠNCS": 48227, + "chicine": 48228, + "ĠOryza": 48229, + "Ġwherever": 48230, + "ĠXGB": 48231, + "COX": 48232, + "Ġhydrogenated": 48233, + "Ġhydraz": 48234, + "ĠPersons": 48235, + "Ġframeshift": 48236, + "Ġelectrolytic": 48237, + "ĠSenegal": 48238, + "Ġphagocyt": 48239, + "Ġinstantaneously": 48240, + "ĠGroundwater": 48241, + "Ġimperial": 48242, + "ĠRhode": 48243, + "ÅĦska": 48244, + "ovisual": 48245, + "ontsize": 48246, + "ĠExplanation": 48247, + "Ġempowerment": 48248, + "NTA": 48249, + "Pu": 48250, + "Por": 48251, + "Sched": 48252, + "eats": 48253, + "Ġys": 48254, + "inous": 48255, + "Ġwilt": 48256, + "ĠMov": 48257, + "ecton": 48258, + "ĠGins": 48259, + "introduction": 48260, + "inception": 48261, + "ĠInterpreting": 48262, + "Ġstartup": 48263, + "Ġalbino": 48264, + "Ġtetras": 48265, + "ĠHousehold": 48266, + "ĠELM": 48267, + "Ġsporulation": 48268, + "Ġosmol": 48269, + "Bis": 48270, + "erule": 48271, + "ĠEAR": 48272, + "Ġimbalances": 48273, + "Ġkt": 48274, + "Ġjl": 48275, + "gesterone": 48276, + "erala": 48277, + "ĠPointer": 48278, + "ĠHRQoL": 48279, + "ĠRiet": 48280, + "ĠEscape": 48281, + "purified": 48282, + "Ġinstantiation": 48283, + "matis": 48284, + "iona": 48285, + "Ġnoxious": 48286, + "ĠNog": 48287, + "Ġjam": 48288, + "ĠAntoni": 48289, + "ĠGodd": 48290, + "ĠPersonalized": 48291, + "Ġpermuted": 48292, + "ĠSHE": 48293, + "ĠOblast": 48294, + "ĠForbes": 48295, + "ĠResveratrol": 48296, + "ĠFeSe": 48297, + "Ġelectrodeposition": 48298, + "Ġhomeobox": 48299, + "Ġpyogenes": 48300, + "Ġviolin": 48301, + "Ġisoelectric": 48302, + "ĠPPG": 48303, + "probably": 48304, + "AMPK": 48305, + "ĠWolfe": 48306, + "Ġultrafine": 48307, + "Beyond": 48308, + "onat": 48309, + "edian": 48310, + "ENABLE": 48311, + "ĠHAM": 48312, + "sout": 48313, + "ĠOpinion": 48314, + "rinted": 48315, + "typing": 48316, + "Unknown": 48317, + "Ġbuckets": 48318, + "Ġintuitionistic": 48319, + "algorithms": 48320, + "SSC": 48321, + "bir": 48322, + "ĠPond": 48323, + "advert": 48324, + "ipin": 48325, + "Ġupwind": 48326, + "ĠClaire": 48327, + "ĠMaturation": 48328, + "ĠPrP": 48329, + "OPO": 48330, + "FORMANCE": 48331, + "ĠdM": 48332, + "ĠCities": 48333, + "Ġinterrelated": 48334, + "ĠApparatus": 48335, + "Ġprecious": 48336, + "criptors": 48337, + "Ġpreparedness": 48338, + "ĠARCH": 48339, + "ĠPathogens": 48340, + "HOST": 48341, + "ĠGibbons": 48342, + "Ġirregularity": 48343, + "ĠLipids": 48344, + "Ġcfu": 48345, + "Ġvasodilation": 48346, + "imetre": 48347, + "improved": 48348, + "mq": 48349, + "ĠHens": 48350, + "ĠLoci": 48351, + "uncredited": 48352, + "Ġmultigrid": 48353, + "tigo": 48354, + "Ġaccountability": 48355, + "enchyme": 48356, + "Ġdisadvantaged": 48357, + "Ġbisphenol": 48358, + "Ġtic": 48359, + "Ġforks": 48360, + "ĠWester": 48361, + "ĠVii": 48362, + "ĠJere": 48363, + "simultaneous": 48364, + "ĠGuarant": 48365, + "ĠDoyle": 48366, + "Ġpotentiates": 48367, + "lassified": 48368, + "Ġileal": 48369, + "Ġvasoconstriction": 48370, + "MODULE": 48371, + "Nano": 48372, + "Wood": 48373, + "ĠTAT": 48374, + "urious": 48375, + "unya": 48376, + "Ġinstillation": 48377, + "ĠSimmons": 48378, + "ĠDirectional": 48379, + "Ġmalate": 48380, + "Ġplantation": 48381, + "Ġunsolved": 48382, + "ĠTauri": 48383, + "Ġovine": 48384, + "Ġkeratinocyte": 48385, + "ĠKullback": 48386, + "ĠKazakhstan": 48387, + "Ġhirs": 48388, + "ĠAerobic": 48389, + "ĠHai": 48390, + "ĠRiley": 48391, + "ensible": 48392, + "Ġinterplanetary": 48393, + "Ġtransits": 48394, + "Ġgenerous": 48395, + "Ġcalpain": 48396, + "Ġappended": 48397, + "ĠHydrodynamics": 48398, + "Ġcolonize": 48399, + "Ġheartbeat": 48400, + "Ġmetastas": 48401, + "Ġpyreth": 48402, + "ĠPAK": 48403, + "ĠС": 48404, + "multiplet": 48405, + "ĠBrady": 48406, + "Ġpropria": 48407, + "ĠFrontier": 48408, + "ĠJoyce": 48409, + "ĠPGF": 48410, + "ĠMcl": 48411, + "recurrent": 48412, + "ĠReplacing": 48413, + "inference": 48414, + "ĠWhitt": 48415, + "Ġschooling": 48416, + "ĠHarold": 48417, + "Ġabstractions": 48418, + "âĬķ": 48419, + "memcpy": 48420, + "Ġmicronucle": 48421, + "Ġradionuclide": 48422, + "otyl": 48423, + "ĠMIF": 48424, + "ĠMUS": 48425, + "Ġexfoli": 48426, + "ĠFamilial": 48427, + "Ġclam": 48428, + "ONO": 48429, + "Ġvanilla": 48430, + "Ġpastoris": 48431, + "ĠATL": 48432, + "ĠBursts": 48433, + "Quantitative": 48434, + "Ġeliciting": 48435, + "Ġgranulomatous": 48436, + "Ġbrowsing": 48437, + "tracks": 48438, + "Ġhij": 48439, + "ĠBCP": 48440, + "incomp": 48441, + "azid": 48442, + "ckpt": 48443, + "Ġlinkers": 48444, + "Ġsquid": 48445, + "Ġheadaches": 48446, + "ĠMoral": 48447, + "Ġstabilisation": 48448, + "&&&&": 48449, + "ĠSufficient": 48450, + "ĠArchaea": 48451, + "Ġìł": 48452, + "ĠLuciferase": 48453, + "Camera": 48454, + "expanded": 48455, + "Ġmysterious": 48456, + "HPS": 48457, + "ĠBJ": 48458, + "ĠKNN": 48459, + "Ġsuperhydrophobic": 48460, + "ĠHydrothermal": 48461, + "ĠRusso": 48462, + "ĠArsenic": 48463, + "Ġnormotensive": 48464, + "ultimate": 48465, + "ĠCMIP": 48466, + "examined": 48467, + "Ġmicroporous": 48468, + "Ġforever": 48469, + "ĠSTING": 48470, + "IGS": 48471, + "ĉĉĉĠĠ": 48472, + "Plant": 48473, + "Ġcoherently": 48474, + "charging": 48475, + "Ġinherit": 48476, + "alternative": 48477, + "ĠBaptist": 48478, + "Fm": 48479, + "bipy": 48480, + "Ġoler": 48481, + "ĠSubstit": 48482, + "Ġultrap": 48483, + "freeze": 48484, + "pergill": 48485, + "POSE": 48486, + "Ġadvertisements": 48487, + "ECHAN": 48488, + "Bayesian": 48489, + "Ġcobordism": 48490, + "¸°": 48491, + "ĠAER": 48492, + "ĠAIP": 48493, + "ĠLNA": 48494, + "essentially": 48495, + "reciprocal": 48496, + "ĠAnand": 48497, + "Ġsmeared": 48498, + "onese": 48499, + "ethylamine": 48500, + "ĠERS": 48501, + "Ġjudicial": 48502, + "Ġwoodland": 48503, + "ĠGregor": 48504, + "Ġtabular": 48505, + "avirin": 48506, + "mirror": 48507, + "Ġjaundice": 48508, + "astigotes": 48509, + "ĠLGBT": 48510, + "ĠNaj": 48511, + "Ġsubscheme": 48512, + "Ġmultiuser": 48513, + "Ġdrains": 48514, + "Ġevacuated": 48515, + "phosphoryl": 48516, + "ĠFeldman": 48517, + "ĠTRIzol": 48518, + "ĠBLEU": 48519, + "aromatic": 48520, + "oviÄĩ": 48521, + "pion": 48522, + "repr": 48523, + "roth": 48524, + "ĠFES": 48525, + "ĠLeeds": 48526, + "Ġung": 48527, + "obranch": 48528, + "Ġpatency": 48529, + "ĠScr": 48530, + "ĠSimplex": 48531, + "pecies": 48532, + "Ġbenefici": 48533, + "Ġpolymerases": 48534, + "ĠCygn": 48535, + "octadec": 48536, + "Ġpunctured": 48537, + "Ġjaponicus": 48538, + "ĠFPGAs": 48539, + "frown": 48540, + "Ġeb": 48541, + "utiny": 48542, + "ĠPoy": 48543, + "ĠBrent": 48544, + "ĠBAM": 48545, + "ĠHick": 48546, + "ĠNPS": 48547, + "ĠGDF": 48548, + "ĠVIRT": 48549, + "Ġinterl": 48550, + "ĠscFv": 48551, + "Ġteamm": 48552, + "Ġparticipatory": 48553, + "Ġexistential": 48554, + "Ġosteomyelitis": 48555, + "Ġpneumothorax": 48556, + "stdout": 48557, + "Ġsingletons": 48558, + "hypothesis": 48559, + "stratified": 48560, + "USD": 48561, + "onasal": 48562, + "eris": 48563, + "imits": 48564, + "ĠICs": 48565, + "ĠEncephal": 48566, + "izi": 48567, + "ĠGradients": 48568, + "Ġallop": 48569, + "Ġcorp": 48570, + "constructed": 48571, + "Ġmonument": 48572, + "simulator": 48573, + "ĠFermions": 48574, + "ĠWyoming": 48575, + "Ġprednisolone": 48576, + "Lang": 48577, + "Notes": 48578, + "eer": 48579, + "Ġfighter": 48580, + "entrant": 48581, + "ĠNij": 48582, + "ĠGPD": 48583, + "ĠProl": 48584, + "Ġrealisation": 48585, + "Ġpackings": 48586, + "ĠDiscovering": 48587, + "ĠAnglo": 48588, + "ĠCassini": 48589, + "execute": 48590, + "Ġinhabited": 48591, + "across": 48592, + "ĠCram": 48593, + "ĠNBR": 48594, + "antes": 48595, + "Ġdispersing": 48596, + "achandran": 48597, + "ĠUND": 48598, + "Ġshoulders": 48599, + "Ġcrises": 48600, + "ustrine": 48601, + "Ġpropane": 48602, + "UNE": 48603, + "brush": 48604, + "Ġetiologies": 48605, + "Ġshotgun": 48606, + "showing": 48607, + "ĠPhytochemical": 48608, + "ĠMehta": 48609, + "orrhea": 48610, + "ĠImagery": 48611, + "Tre": 48612, + "wc": 48613, + "Ġeluent": 48614, + "ondin": 48615, + "ĠAttitude": 48616, + "Ġferromagnet": 48617, + "Ġcountermeasures": 48618, + "Ġalkanes": 48619, + "ĠCapillary": 48620, + "latent": 48621, + "Ġsolubil": 48622, + "Viewer": 48623, + "ázquez": 48624, + "ĠPunjab": 48625, + "aas": 48626, + "tang": 48627, + "Ġimports": 48628, + "ĠYounger": 48629, + "roughly": 48630, + "Weinberg": 48631, + "ĠAtkinson": 48632, + "bfa": 48633, + "MPa": 48634, + "steel": 48635, + "PCP": 48636, + "chlorinated": 48637, + "ĠPsychometric": 48638, + "Ġpyroptosis": 48639, + "Ġwatched": 48640, + "ĠPercutaneous": 48641, + "RBD": 48642, + "VARI": 48643, + "atu": 48644, + "ĠWake": 48645, + "Ġcanyon": 48646, + "iparous": 48647, + "Ġscall": 48648, + "completely": 48649, + "interfer": 48650, + "ophyceae": 48651, + "Ġfatalities": 48652, + "czak": 48653, + "ĠPathophysiology": 48654, + "Lem": 48655, + "lach": 48656, + "tuary": 48657, + "Ġalex": 48658, + "Ġsisters": 48659, + "Ġpum": 48660, + "ĠCatch": 48661, + "ĠEber": 48662, + "inex": 48663, + "phthe": 48664, + "Ġboar": 48665, + "ĠSoul": 48666, + "Ġcatfish": 48667, + "Ġcloudy": 48668, + "ĠBuilt": 48669, + "ophylline": 48670, + "ĠRibosome": 48671, + "ĠAnomalies": 48672, + "YD": 48673, + "categorical": 48674, + "wor": 48675, + "openta": 48676, + "ĠLIB": 48677, + "Ġrick": 48678, + "Ġradiations": 48679, + "Ġhypercube": 48680, + "Ġmaltreatment": 48681, + "ĠîĦĦ": 48682, + "dispersity": 48683, + "continent": 48684, + "Digital": 48685, + "ĠCoryneb": 48686, + "Ġrevert": 48687, + "ĠTEA": 48688, + "ĠMLR": 48689, + "ĠFCM": 48690, + "ĠLamp": 48691, + "izabilities": 48692, + "Ġcarved": 48693, + "ĠMonoclonal": 48694, + "Ġpenis": 48695, + "ĠMorales": 48696, + "Enter": 48697, + "esterification": 48698, + "Ġcabbage": 48699, + "RANTIES": 48700, + "Ġdebridement": 48701, + "Lead": 48702, + "cAMP": 48703, + "Ġcesium": 48704, + "ĠCubic": 48705, + "Ġunimodular": 48706, + "ĠExport": 48707, + "Ġanalyser": 48708, + "denotes": 48709, + "Ġradically": 48710, + "ĠHistology": 48711, + "Ġmelanomas": 48712, + "Ġworship": 48713, + "ĠHimalayan": 48714, + "ĠIntegrable": 48715, + "benzenesulfonamide": 48716, + "Ġharbored": 48717, + "Putting": 48718, + "ĠTir": 48719, + "ĠUTI": 48720, + "centers": 48721, + "ĠPluripot": 48722, + "Ġharbors": 48723, + "Ġcarbam": 48724, + "ĠAppalach": 48725, + "ĠJoan": 48726, + "ĠCommissioner": 48727, + "ĠGemini": 48728, + "Near": 48729, + "OPS": 48730, + "QG": 48731, + "pytorch": 48732, + "staining": 48733, + "ĠhCG": 48734, + "Ġgavage": 48735, + "perhaps": 48736, + "ĠGrib": 48737, + "ĠZah": 48738, + "Ġcomparably": 48739, + "ĠBioscience": 48740, + "SPL": 48741, + "Connell": 48742, + "ĠAirway": 48743, + "primed": 48744, + "Ġsubmucosal": 48745, + "Enhanced": 48746, + "Ġwisdom": 48747, + "VN": 48748, + "ĠMumbai": 48749, + "rius": 48750, + "ĠRGD": 48751, + "ĠRNeasy": 48752, + "mai": 48753, + "ĠADL": 48754, + "Ġadoptive": 48755, + "Outlined": 48756, + "ĠWARRANTIES": 48757, + "ĠViolence": 48758, + "Ġcaterp": 48759, + "Fund": 48760, + "dθ": 48761, + "ĠPok": 48762, + "ĠBenson": 48763, + "ĠRIG": 48764, + "ĠVs": 48765, + "Ġinstants": 48766, + "ĠMultidrug": 48767, + "PDMS": 48768, + "CONST": 48769, + "Ġcartridge": 48770, + "ĠLifestyle": 48771, + "ĠCONDITIONS": 48772, + "odysplastic": 48773, + "CONTROL": 48774, + "LHC": 48775, + "tire": 48776, + "ĠStain": 48777, + "Ġyx": 48778, + "Ġjunctional": 48779, + "obo": 48780, + "annah": 48781, + "ĠCPAP": 48782, + "Ġsoundness": 48783, + "ĠUltimate": 48784, + "silicon": 48785, + "Ġparalog": 48786, + "Events": 48787, + "Gas": 48788, + "JE": 48789, + "ĠJorge": 48790, + "Ġoverproduction": 48791, + "Ġmaxilla": 48792, + "ĠReasons": 48793, + "weeks": 48794, + "ĠNearest": 48795, + "Ġheadspace": 48796, + "ĠATC": 48797, + "balancing": 48798, + "Ġjudging": 48799, + "ĠUniversality": 48800, + "Ġsinuses": 48801, + "Ġretroperitoneal": 48802, + "Detection": 48803, + "Ġhydrolysate": 48804, + "Hoch": 48805, + "wrapper": 48806, + "ĠpKa": 48807, + "Ġlasso": 48808, + "ĠAlu": 48809, + "ĠAPR": 48810, + "ĠDors": 48811, + "ĠDarboux": 48812, + "ĠRFS": 48813, + "ĠKhar": 48814, + "ĠThrom": 48815, + "Ġdesignate": 48816, + "arco": 48817, + "Ġthermostat": 48818, + "ĠGlacial": 48819, + "IFF": 48820, + "ĠManifest": 48821, + "Ġinterspersed": 48822, + "hauser": 48823, + "ĠDDX": 48824, + "Ġale": 48825, + "tides": 48826, + "Ġlaccase": 48827, + "ĠHered": 48828, + "ĠRacial": 48829, + "ĠKats": 48830, + "ajo": 48831, + "ĠCoordinated": 48832, + "ĠProbably": 48833, + "Ġtitanate": 48834, + "SLAM": 48835, + "driving": 48836, + "ĠEmergent": 48837, + "ĠDrives": 48838, + "Ġobligations": 48839, + "Ġnebulae": 48840, + "fried": 48841, + "ithin": 48842, + "ĠPGD": 48843, + "occlusion": 48844, + "ĠUH": 48845, + "Ġsubroutine": 48846, + "oidin": 48847, + "Ġannoy": 48848, + "ĠVirasoro": 48849, + "instances": 48850, + "ĠDerby": 48851, + "Ġtriangulations": 48852, + "Ġcutoffs": 48853, + "ĠOrganizational": 48854, + "ĠVenk": 48855, + "ĠEGTA": 48856, + "ĠDeutsche": 48857, + "Ġantineut": 48858, + "ĠVulnerability": 48859, + "iated": 48860, + "Ġavium": 48861, + "Ġhsp": 48862, + "emulsions": 48863, + "adherence": 48864, + "ĠUPS": 48865, + "maph": 48866, + "ĠVAP": 48867, + "relatively": 48868, + "Ġmaxill": 48869, + "ophase": 48870, + "Threshold": 48871, + "ĠSupp": 48872, + "ethoprim": 48873, + "Ġpenetrated": 48874, + "Ġblasting": 48875, + "ĠAdvantages": 48876, + "BUS": 48877, + "olson": 48878, + "recv": 48879, + "Ġcarnitine": 48880, + "tening": 48881, + "Ġprovoked": 48882, + "various": 48883, + "ĠCalab": 48884, + "leneck": 48885, + "ĠParkin": 48886, + "Ġblowup": 48887, + "ĠDWI": 48888, + "synthesized": 48889, + "Ġdisproportionately": 48890, + "Ġcardiorespiratory": 48891, + "ĠXanthomonas": 48892, + "Ġpuncta": 48893, + "bddc": 48894, + "ĠPACS": 48895, + "aseg": 48896, + "Ġincurs": 48897, + "osta": 48898, + "ĠJL": 48899, + "ĠWeierstrass": 48900, + "oleucine": 48901, + "Ġfinals": 48902, + "Ġcausation": 48903, + "ĠDirective": 48904, + "ĠPorto": 48905, + "ĠFlores": 48906, + "arbonyl": 48907, + "----------------------------------------------------------------------------": 48908, + "historic": 48909, + "Kähler": 48910, + "onna": 48911, + "Ġcel": 48912, + "ĠTBA": 48913, + "ĠOphthal": 48914, + "Ġsubthreshold": 48915, + "Ġlips": 48916, + "ĠSubstrates": 48917, + "Ġpeninsula": 48918, + "Ġadsor": 48919, + "Ġdryness": 48920, + "masses": 48921, + "ème": 48922, + "strok": 48923, + "ĠExpanded": 48924, + "Ġgc": 48925, + "ĠGolf": 48926, + "Ġcritique": 48927, + "ĠÍ©": 48928, + "Ġlensed": 48929, + "ĠKingma": 48930, + "ĠGoldman": 48931, + "ĠFacile": 48932, + "Carl": 48933, + "Ġchondrites": 48934, + "ĠCohomology": 48935, + "ĠSocioeconomic": 48936, + "ĠDominican": 48937, + "ĠAzerbaijan": 48938, + "ĠAne": 48939, + "ĠMidd": 48940, + "ĠNed": 48941, + "Ġemulate": 48942, + "ĠShakes": 48943, + "Ġliked": 48944, + "Ġbuildup": 48945, + "Ġexcessively": 48946, + "ĠŶ": 48947, + "ĠAdapted": 48948, + "Ġauthenticated": 48949, + "Ġlocomotive": 48950, + "Ġsubmill": 48951, + "Ġinterpreter": 48952, + "ĠVibrational": 48953, + "Rα": 48954, + "laden": 48955, + "pkl": 48956, + "rw": 48957, + "yet": 48958, + "enzymes": 48959, + "Ġwav": 48960, + "ĠNMT": 48961, + "athion": 48962, + "Ġbiotechnological": 48963, + "arcs": 48964, + "Ġactuated": 48965, + "Ġherring": 48966, + "ECG": 48967, + "OCI": 48968, + "Activated": 48969, + "Ġparaph": 48970, + "Observation": 48971, + "ĠEkman": 48972, + "ancellor": 48973, + "velihood": 48974, + "Gauss": 48975, + "HAL": 48976, + "rdev": 48977, + "tbl": 48978, + "icits": 48979, + "ĠRoux": 48980, + "opram": 48981, + "Ġseropositive": 48982, + "ĠPhysically": 48983, + "ĠEdu": 48984, + "Ġdocumenting": 48985, + "Ġо": 48986, + "ĠSmaller": 48987, + "chery": 48988, + "Ġlanthanide": 48989, + "Today": 48990, + "ÑĨ": 48991, + "Ġotitis": 48992, + "ĠCores": 48993, + "ifolium": 48994, + "ĠZel": 48995, + "Ġtimings": 48996, + "coarse": 48997, + "repair": 48998, + "ĠLDPC": 48999, + "Ġbowl": 49000, + "ĠEpidermal": 49001, + "Ġâľ²": 49002, + "Ġsynonyms": 49003, + "ĠENT": 49004, + "Ġbilliard": 49005, + "Ġejac": 49006, + "ĠBAA": 49007, + "Ġscientif": 49008, + "Ġγγ": 49009, + "Ġcapsular": 49010, + "Ġazithromycin": 49011, + "Ġcredentials": 49012, + "Ġḳ": 49013, + "ĠGlioblastoma": 49014, + "Ġuncoated": 49015, + "Ġhibern": 49016, + "ĠTos": 49017, + "ĠBaro": 49018, + "ĠKass": 49019, + "yland": 49020, + "ĠXM": 49021, + "Ġaggra": 49022, + "Ġneutralize": 49023, + "licted": 49024, + "Ġsoundtrack": 49025, + "ĠKnud": 49026, + "ensorship": 49027, + "empfer": 49028, + "ĠHaldane": 49029, + "ĠRocks": 49030, + "ĠGou": 49031, + "ĠOpi": 49032, + "ibacterium": 49033, + "eptives": 49034, + "usta": 49035, + "pars": 49036, + "ukawa": 49037, + "bein": 49038, + "elius": 49039, + "averaging": 49040, + "ĠMWCNT": 49041, + "Ġshielded": 49042, + "Ġquaternionic": 49043, + "BatchNorm": 49044, + "Ġdella": 49045, + "ĠTp": 49046, + "Ġbyproduct": 49047, + "ĠGow": 49048, + "ĠJO": 49049, + "Ġparameterize": 49050, + "gler": 49051, + "Ġfacult": 49052, + "Ġ͵": 49053, + "Ġnomination": 49054, + "Ġbaths": 49055, + "Ġinstallations": 49056, + "ĠJustin": 49057, + "Ġchampionships": 49058, + "tap": 49059, + "ĠSanc": 49060, + "ĠSusp": 49061, + "Ġsubleading": 49062, + "Ġdefended": 49063, + "Ġbutyl": 49064, + "remote": 49065, + "Ġcarbides": 49066, + "ĠPredicts": 49067, + "ĠPriority": 49068, + "ĠAntibiotics": 49069, + "ĠPUFAs": 49070, + "ĠMICs": 49071, + "ĠMaximization": 49072, + "bare": 49073, + "ĠPCN": 49074, + "Ġinfested": 49075, + "Ġsolenoid": 49076, + "Ġagronomic": 49077, + "ANGE": 49078, + "Rev": 49079, + "ĠNKG": 49080, + "Ġsaponins": 49081, + "Recommend": 49082, + "Ġsharpen": 49083, + "othioyl": 49084, + "suspended": 49085, + "atron": 49086, + "usage": 49087, + "ilter": 49088, + "ĠNER": 49089, + "CRIPT": 49090, + "infections": 49091, + "Ġheterosexual": 49092, + "Ġmesoc": 49093, + "ĠBobby": 49094, + "allocate": 49095, + "ĠPayne": 49096, + "ĠSultan": 49097, + "è¡": 49098, + "racles": 49099, + "ribe": 49100, + "ĠDoub": 49101, + "ĠPAF": 49102, + "communication": 49103, + "Ġnineteenth": 49104, + "Ġpoplar": 49105, + "pgfstrok": 49106, + "pgfstrokecolor": 49107, + "SLE": 49108, + "ecia": 49109, + "Ġdetach": 49110, + "Ġcharity": 49111, + "Ġmonochrom": 49112, + "Ġprescribe": 49113, + "Ġsupermassive": 49114, + "Ġguards": 49115, + "Ġcycloaddition": 49116, + "Cohen": 49117, + "phosphatidyl": 49118, + "created": 49119, + "ĠElectrodynamics": 49120, + "Ġplasmons": 49121, + "Ñģк": 49122, + "ĠDaphnia": 49123, + "easy": 49124, + "Ġaq": 49125, + "Ġfimb": 49126, + "Ġwrest": 49127, + "ĠTend": 49128, + "hipp": 49129, + "Ġorganisational": 49130, + "MAE": 49131, + "OPY": 49132, + "Ġpotentiated": 49133, + "Ġbrute": 49134, + "omassie": 49135, + "aunay": 49136, + "luster": 49137, + "Ġophi": 49138, + "unge": 49139, + "ĠPom": 49140, + "Ġplague": 49141, + "berries": 49142, + "Ġinviscid": 49143, + "ĠQE": 49144, + "incident": 49145, + "ximide": 49146, + "Ġestrogens": 49147, + "ĠTransparent": 49148, + "vereign": 49149, + "ĠPreferred": 49150, + "Ġelastase": 49151, + "Civ": 49152, + "JF": 49153, + "Ku": 49154, + "caster": 49155, + "Ġspends": 49156, + "Ġabstracted": 49157, + "otechnical": 49158, + "Ġbreeders": 49159, + "Ġsyringae": 49160, + "Ġclotting": 49161, + "African": 49162, + "PEC": 49163, + "usep": 49164, + "Ġstark": 49165, + "solete": 49166, + "ofovir": 49167, + "Ġsensations": 49168, + "azawa": 49169, + "Ġbiomechanics": 49170, + "Ġemergencies": 49171, + "Ġspectrometers": 49172, + "Ġhemispheric": 49173, + "Ġdiscriminatory": 49174, + "ĠInspection": 49175, + "ndim": 49176, + "REP": 49177, + "ĠWess": 49178, + "Ġhyperalgesia": 49179, + "IRC": 49180, + "Ġauthorship": 49181, + "CPA": 49182, + "Ġrotationally": 49183, + "ĠPyth": 49184, + "ĠYamaguchi": 49185, + "Fields": 49186, + "Ġenrolment": 49187, + "ĠRethinking": 49188, + "Gate": 49189, + "ìĺ": 49190, + "Ġcements": 49191, + "ĠTTS": 49192, + "ĠFink": 49193, + "adus": 49194, + "ĠLl": 49195, + "Ġimplicate": 49196, + "annihilation": 49197, + "Ġfeeders": 49198, + "ĠPDX": 49199, + "ĠFrançois": 49200, + "Spearman": 49201, + "ĠBenchmarking": 49202, + "Forest": 49203, + "evidence": 49204, + "enoyl": 49205, + "olactone": 49206, + "cephaly": 49207, + "ĠPEA": 49208, + "ĠNSE": 49209, + "Ġnotified": 49210, + "Ġpolyelectrolyte": 49211, + "ĠMalik": 49212, + "anthine": 49213, + "tetrad": 49214, + "Ġflagella": 49215, + "ãĥ¼": 49216, + "orpion": 49217, + "Ġbuyers": 49218, + "Ġoligodendrocyte": 49219, + "ĠNMDAR": 49220, + "ĠHarvesting": 49221, + "Ġkarst": 49222, + "IBD": 49223, + "ĠFolk": 49224, + "Ġsubcarrier": 49225, + "Ġnotices": 49226, + "ĠYous": 49227, + "awak": 49228, + "Ġadversaries": 49229, + "Looking": 49230, + "Ġthymocytes": 49231, + "Ġmeningioma": 49232, + "Ġilluminate": 49233, + "ĠSPDX": 49234, + "Ġimpinging": 49235, + "associative": 49236, + "Ġtiger": 49237, + "leon": 49238, + "Ġstature": 49239, + "ĠHood": 49240, + "ĠRutherford": 49241, + "ĠEIT": 49242, + "Ġinfantile": 49243, + "ĠQubit": 49244, + "Ġpacks": 49245, + "ĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ": 49246, + "azolam": 49247, + "า": 49248, + "ĠTunisia": 49249, + "dilution": 49250, + "Ġsympatric": 49251, + "Ġliquefaction": 49252, + "porphyrin": 49253, + "Gn": 49254, + "Rib": 49255, + "isothermal": 49256, + "apo": 49257, + "Ġregressors": 49258, + "mani": 49259, + "ĠILs": 49260, + "oxidants": 49261, + "Atom": 49262, + "ligo": 49263, + "ĠSRAM": 49264, + "alcone": 49265, + "csr": 49266, + "Ġcautious": 49267, + "Ġhandlers": 49268, + "Ġgastritis": 49269, + "ĠSupervision": 49270, + "Ġevaporative": 49271, + "RUN": 49272, + "ĠICG": 49273, + "ĠPrague": 49274, + "ĠMLC": 49275, + "ĠMoney": 49276, + "ĠRm": 49277, + "ĠECS": 49278, + "velt": 49279, + "ĠVg": 49280, + "Ġbiography": 49281, + "Ġministry": 49282, + "convolution": 49283, + "ogenomics": 49284, + "rounding": 49285, + "ĠPhag": 49286, + "Ġaudiences": 49287, + "ĠHCWs": 49288, + "Ġblastocysts": 49289, + "Ġdiagonals": 49290, + "Ġprecautions": 49291, + "Íĵ": 49292, + "ecewise": 49293, + "ĠToxin": 49294, + "ĠHapp": 49295, + "ĠâĢĭ": 49296, + "Ġpopulate": 49297, + "mmol": 49298, + "ĠPRS": 49299, + "Ġreinforces": 49300, + "ISTIC": 49301, + "ozoites": 49302, + "arrival": 49303, + "Ġpavement": 49304, + "REGISTER": 49305, + "ĠGases": 49306, + "ĠExhib": 49307, + "Ġfactorizations": 49308, + "Ġmyopia": 49309, + "Ġmovable": 49310, + "ĠLIMIT": 49311, + "Ġsoleus": 49312, + "DOUBLE": 49313, + "ĠInputs": 49314, + "footnotes": 49315, + "BITS": 49316, + "ĠCyprus": 49317, + "reports": 49318, + "ĠPAA": 49319, + "istal": 49320, + "Ġgroupoids": 49321, + "orphin": 49322, + "ĠCoordinates": 49323, + "boro": 49324, + "ĠOslo": 49325, + "whenever": 49326, + "Ġplausibility": 49327, + "ĠFoxO": 49328, + "ĠIntrusion": 49329, + "Ġsimplices": 49330, + "ĠFaso": 49331, + "Ġpicosecond": 49332, + "ĠAnsatz": 49333, + "Importantly": 49334, + "ĠHutchinson": 49335, + "ovani": 49336, + "ĠAsymptotics": 49337, + "Ġappra": 49338, + "ĠExogenous": 49339, + "Ġcaptions": 49340, + "ĠAcanth": 49341, + "Ġillicit": 49342, + "ĠBladder": 49343, + "Ġboom": 49344, + "ĠSalinity": 49345, + "Ġmuscul": 49346, + "eptidyl": 49347, + "Ġavalanches": 49348, + "Helper": 49349, + "Ġbivalve": 49350, + "Ġreimbursement": 49351, + "zzo": 49352, + "romatosis": 49353, + "Ġparacetamol": 49354, + "vio": 49355, + "Ġvalpro": 49356, + "clamation": 49357, + "Ġuu": 49358, + "ĠSoC": 49359, + "ĠGiac": 49360, + "Ġlycopene": 49361, + "Flags": 49362, + "Ġsticking": 49363, + "Ġsqueeze": 49364, + "synthetic": 49365, + "osahexaenoic": 49366, + "mobile": 49367, + "vect": 49368, + "ĠBaryon": 49369, + "Ġnef": 49370, + "Ġflatter": 49371, + "SSI": 49372, + "Ġschw": 49373, + "ancreas": 49374, + "Buf": 49375, + "Solid": 49376, + "ĠRIPA": 49377, + "Square": 49378, + "Ġtranscendental": 49379, + "Ġcyn": 49380, + "Ġmf": 49381, + "ĠSew": 49382, + "ĠPIT": 49383, + "ocs": 49384, + "ĠBash": 49385, + "Ġsurprised": 49386, + "Ġautonomously": 49387, + "Ġlocalizing": 49388, + "Ġvisitor": 49389, + "Ġpersisting": 49390, + "Ġlandfill": 49391, + "datetime": 49392, + "Ġfiref": 49393, + "ĠEngineered": 49394, + "ĠSnyder": 49395, + "ochromes": 49396, + "fractionated": 49397, + "GPI": 49398, + "Ġwoven": 49399, + "ĠTMR": 49400, + "Ġforgotten": 49401, + "ĠMult": 49402, + "ĠBipolar": 49403, + "ĠHisp": 49404, + "opeptides": 49405, + "apamil": 49406, + "Ġrouted": 49407, + "Ġagn": 49408, + "Ġdaylight": 49409, + "ĠÍĶ": 49410, + "BBB": 49411, + "ĠMajority": 49412, + "Ġconfounded": 49413, + "ĠCaroline": 49414, + "ĠShimura": 49415, + "ruction": 49416, + "Ġtympan": 49417, + "acio": 49418, + "ĠTFE": 49419, + "ĠTutorial": 49420, + "Ġallyl": 49421, + "ĠFrost": 49422, + "ĠRPS": 49423, + "ĠWah": 49424, + "Ġiy": 49425, + "Ġsubproblems": 49426, + "Ġairfoil": 49427, + "Ġembeds": 49428, + "ĠMorning": 49429, + "Ġminorities": 49430, + "ĠMembership": 49431, + "Ġquadriceps": 49432, + "yly": 49433, + "ĠBodies": 49434, + "ĠRAND": 49435, + "Ġrationally": 49436, + "ĠManifold": 49437, + "phosphine": 49438, + "considering": 49439, + "ezvous": 49440, + "ĠKnowing": 49441, + "Ġtumorigenic": 49442, + "Ġilluminating": 49443, + "ĠFernandes": 49444, + "polynomials": 49445, + "ĠBulgarian": 49446, + "ĠBhattacharya": 49447, + "ospira": 49448, + "Api": 49449, + "henne": 49450, + "Ġmays": 49451, + "ĠArgin": 49452, + "interpol": 49453, + "ĠAndean": 49454, + "ĠPDS": 49455, + "ĠCNP": 49456, + "Ġtransfusions": 49457, + "ĠNanom": 49458, + "Ġsynergism": 49459, + "Ġbentonite": 49460, + "Ġgravitons": 49461, + "aquette": 49462, + "Ġfissure": 49463, + "tandem": 49464, + "wash": 49465, + "ĠEyes": 49466, + "Ġdilepton": 49467, + "Ġrectified": 49468, + "ĠArist": 49469, + "iscible": 49470, + "Ġirq": 49471, + "Ġligaments": 49472, + "security": 49473, + "Ġvascularization": 49474, + "NaCl": 49475, + "ĠStraight": 49476, + "ĠLeptin": 49477, + "ĠAbundances": 49478, + "ĠKEY": 49479, + "ĠMothers": 49480, + "ĠRenewable": 49481, + "Ġmasonry": 49482, + "ëı": 49483, + "raceutical": 49484, + "Ġarity": 49485, + "ĠAlves": 49486, + "ospectral": 49487, + "Ġimmunod": 49488, + "Ġmarble": 49489, + "Ġcoverings": 49490, + "ĠConstants": 49491, + "ĠReversal": 49492, + "Works": 49493, + "ĠNurse": 49494, + "ĠAggregate": 49495, + "acillin": 49496, + "plug": 49497, + "Ġjury": 49498, + "oneogenesis": 49499, + "Ġamoeb": 49500, + "aukee": 49501, + "Ġphosphoric": 49502, + "ĠRemoving": 49503, + "Ġworsen": 49504, + "ĠESRD": 49505, + "ĠHernandez": 49506, + "ĠEugene": 49507, + "viscosity": 49508, + "FID": 49509, + "¦": 49510, + "ĠÝ": 49511, + "ĠStig": 49512, + "ĠSING": 49513, + "ĠIMRT": 49514, + "ĠBq": 49515, + "ĠDME": 49516, + "ĠHOM": 49517, + "physis": 49518, + "obes": 49519, + "Ġsuperfields": 49520, + "Ġargc": 49521, + "Ġmaladaptive": 49522, + "ĠEditing": 49523, + "Ġcondem": 49524, + "ubei": 49525, + "stimulus": 49526, + "Ġabbreviation": 49527, + "Haus": 49528, + "ĠNeeds": 49529, + "Ġadhering": 49530, + "ĠVPA": 49531, + "ofrontal": 49532, + "ĠŪ": 49533, + "ĠVerde": 49534, + "ĠSlav": 49535, + "ĠPropag": 49536, + "Ġcongeners": 49537, + "Ġtilapia": 49538, + "ĠRachel": 49539, + "Less": 49540, + "Ġmasc": 49541, + "entangled": 49542, + "ĠDTI": 49543, + "atik": 49544, + "rolases": 49545, + "ĠYen": 49546, + "armor": 49547, + "ĠDecisions": 49548, + "Ġηp": 49549, + "Intuitively": 49550, + "ĠPharmaceuticals": 49551, + "Ju": 49552, + "uddin": 49553, + "ĠWASP": 49554, + "nton": 49555, + "Ġbiot": 49556, + "ĠZNF": 49557, + "Ġcrush": 49558, + "ĠParity": 49559, + "SIST": 49560, + "EVENT": 49561, + "ĠSquamous": 49562, + "Student": 49563, + "Ġgingivalis": 49564, + "fused": 49565, + "ĠMises": 49566, + "ĠFDTD": 49567, + "oreceptors": 49568, + "Ġdiscretion": 49569, + "ORTC": 49570, + "MSP": 49571, + "Ġexposes": 49572, + "Ġchlorinated": 49573, + "ĠUpregulation": 49574, + "ĠLimb": 49575, + "Ġanticor": 49576, + "Regular": 49577, + "Advanced": 49578, + "Xe": 49579, + "aghan": 49580, + "ĠBLA": 49581, + "Ġcoasts": 49582, + "ĠThirteen": 49583, + "hesin": 49584, + "ĠMethanol": 49585, + "rotus": 49586, + "ĠStephens": 49587, + "Book": 49588, + "ĠHistorically": 49589, + "ĠEmploying": 49590, + "Ġcorrugated": 49591, + "mercaptoethanol": 49592, + "ĠDnmt": 49593, + "ĠQueries": 49594, + "DRB": 49595, + "ĠGRU": 49596, + "FLU": 49597, + "ĠCarboniferous": 49598, + "OBJECT": 49599, + "ĠDiscriminative": 49600, + "ĠBurgess": 49601, + "Ġplanetesimals": 49602, + "ĠAmendment": 49603, + "ĠStrikingly": 49604, + "tric": 49605, + "ecure": 49606, + "Ġtransposable": 49607, + "rolateral": 49608, + "Ġhisti": 49609, + "egaard": 49610, + "Ġskim": 49611, + "ĠSPF": 49612, + "Statistics": 49613, + "Ġintestines": 49614, + "feng": 49615, + "lain": 49616, + "Ġtheat": 49617, + "Ġorogen": 49618, + "Ġpill": 49619, + "odopa": 49620, + "Ġcorrelative": 49621, + "ACO": 49622, + "Ġadjunction": 49623, + "ĠCarey": 49624, + "Ġteleportation": 49625, + "ĠBoundaries": 49626, + "ĠGoodfellow": 49627, + "ĠLinda": 49628, + "ĠPolymeric": 49629, + "Ġexertion": 49630, + "Ġsteeply": 49631, + "Ġprotrusion": 49632, + "Ġhyaluronic": 49633, + "ĠRochester": 49634, + "ENSIONAL": 49635, + "Dar": 49636, + "fet": 49637, + "ĠFSS": 49638, + "hemically": 49639, + "Ġflashes": 49640, + "Ġdeviated": 49641, + "feldt": 49642, + "Ġsticks": 49643, + "Ġoctet": 49644, + "Ġgravitationally": 49645, + "footnotesize": 49646, + "Lex": 49647, + "ovi": 49648, + "Ġwired": 49649, + "ĠSMP": 49650, + "ermans": 49651, + "Ġunbroken": 49652, + "Ġemulation": 49653, + "simulated": 49654, + "Ġminimality": 49655, + "ardiac": 49656, + "Ġshipw": 49657, + "Genetic": 49658, + "ĠHermann": 49659, + "ynchronization": 49660, + "ĠNapole": 49661, + "Ġmonodisperse": 49662, + "Rho": 49663, + "rists": 49664, + "Ġfx": 49665, + "ĠFUV": 49666, + "ĠGelfand": 49667, + "hemispheric": 49668, + "ronidazole": 49669, + "Ġsupersaturation": 49670, + "oudh": 49671, + "olitical": 49672, + "ĠAiry": 49673, + "Ġmanifestly": 49674, + "ĠHMG": 49675, + "Ġadvertisement": 49676, + "ĠBrooklyn": 49677, + "Ġparalleled": 49678, + "answered": 49679, + "ĠNotImplementedError": 49680, + "UPD": 49681, + "oust": 49682, + "ĠTeng": 49683, + "Ġfortified": 49684, + "ĠSort": 49685, + "ENE": 49686, + "ĠFris": 49687, + "ĠHIS": 49688, + "ĠROT": 49689, + "ĠNested": 49690, + "produce": 49691, + "ĠKerala": 49692, + "genomic": 49693, + "ĠIsab": 49694, + "Ġuracil": 49695, + "burger": 49696, + "ĠLogarithmic": 49697, + "Ġsterility": 49698, + "Ġunemployed": 49699, + "Ġoriental": 49700, + "Ko": 49701, + "jima": 49702, + "ĠCTP": 49703, + "ĠLAD": 49704, + "Ġconformers": 49705, + "CGG": 49706, + "Perkin": 49707, + "Ġbridged": 49708, + "ĠDissociation": 49709, + "ĠQiagen": 49710, + "Ġwealthy": 49711, + "Ġanaesthetic": 49712, + "ĠMinimizing": 49713, + "Ġacoustics": 49714, + "bucket": 49715, + "ĠSertoli": 49716, + "rath": 49717, + "saw": 49718, + "Ġgarn": 49719, + "ĠTheoretically": 49720, + "ticul": 49721, + "ĠThinking": 49722, + "iker": 49723, + "ĠChit": 49724, + "Ġtrin": 49725, + "ALITY": 49726, + "ĠFeO": 49727, + "Ġpolymerized": 49728, + "Encoding": 49729, + "Ġanalgesics": 49730, + "ĠLexical": 49731, + "Ġmarijuana": 49732, + "âĸĪâĸĪ": 49733, + "crops": 49734, + "entropic": 49735, + "olocation": 49736, + "ĠPomp": 49737, + "Ġcofactors": 49738, + "boxtimes": 49739, + "ĠArri": 49740, + "Angel": 49741, + "ĠRequirement": 49742, + "Ġmicrolensing": 49743, + "ĠTRANSF": 49744, + "åº": 49745, + "Ġdma": 49746, + "Ġrerio": 49747, + "undancy": 49748, + "antel": 49749, + "Ġradiometric": 49750, + "ĠSean": 49751, + "randn": 49752, + "ĠCRL": 49753, + "halos": 49754, + "ubertal": 49755, + "Ġquinone": 49756, + "TES": 49757, + "ĠdW": 49758, + "ĠCGM": 49759, + "Ġhealed": 49760, + "iane": 49761, + "Ġobtainable": 49762, + "ĠAdrian": 49763, + "Ġlikes": 49764, + "ĠMedication": 49765, + "Ġcognitively": 49766, + "Whether": 49767, + "Bob": 49768, + "did": 49769, + "alcohol": 49770, + "Ġnivolumab": 49771, + "ĠFY": 49772, + "Ġatresia": 49773, + "achs": 49774, + "ĠKip": 49775, + "Ġunigenes": 49776, + "ĠJaccard": 49777, + "ustri": 49778, + "Ġconfine": 49779, + "Ġautofluorescence": 49780, + "Ġpyg": 49781, + "Sea": 49782, + "Settings": 49783, + "Ġtruncatula": 49784, + "Literal": 49785, + "Fab": 49786, + "Mah": 49787, + "Ven": 49788, + "Ġtig": 49789, + "Ġcher": 49790, + "ĠCCI": 49791, + "ĠFunk": 49792, + "ĠBess": 49793, + "ĠNasal": 49794, + "iffer": 49795, + "Ġobsessive": 49796, + "ĠOpening": 49797, + "ochondral": 49798, + "ĠTRPA": 49799, + "ĠRabin": 49800, + "Ġtaper": 49801, + "Ġdeafness": 49802, + "DOS": 49803, + "isites": 49804, + "anite": 49805, + "leost": 49806, + "ĠSTP": 49807, + "ĠBACE": 49808, + "ĠHenn": 49809, + "ĠESM": 49810, + "Ġsuperfield": 49811, + "ĠOrland": 49812, + "ĠAMPs": 49813, + "ĠHemorrh": 49814, + "Ġrescues": 49815, + "Ġtourists": 49816, + "ĠVLBI": 49817, + "Ġneighbourhoods": 49818, + "communicable": 49819, + "gx": 49820, + "ratase": 49821, + "ĠNRT": 49822, + "Ġobstructions": 49823, + "Ġdeforestation": 49824, + "Ġqp": 49825, + "ĠPhan": 49826, + "ĠSTI": 49827, + "imento": 49828, + "ĠIRI": 49829, + "SVs": 49830, + "Ġstriped": 49831, + "Poinc": 49832, + "ĠBedford": 49833, + "ĠFragment": 49834, + "ĠReligion": 49835, + "Ġdrones": 49836, + "imulation": 49837, + "ĠCet": 49838, + "Ġgills": 49839, + "ĠNorton": 49840, + "ibatch": 49841, + "estructive": 49842, + "ĠJav": 49843, + "ĠϽ": 49844, + "Ġmica": 49845, + "AGB": 49846, + "RAW": 49847, + "ĠMyD": 49848, + "ctl": 49849, + "Ġreversibly": 49850, + "Ġsuppressors": 49851, + "ĠFAIL": 49852, + "ĠFukushima": 49853, + "Evidence": 49854, + "pink": 49855, + "asarray": 49856, + "ĠTann": 49857, + "Ġloved": 49858, + "Ġbiologists": 49859, + "Ġendothermic": 49860, + "Ġbroker": 49861, + "ĠPerkins": 49862, + "Ġcategorised": 49863, + "ĠSOME": 49864, + "hydroxyvitamin": 49865, + "rogates": 49866, + "ĠAgeing": 49867, + "Ġtournaments": 49868, + "ĠStromal": 49869, + "Ġdeferred": 49870, + "ĠSREBP": 49871, + "MAD": 49872, + "Say": 49873, + "cgi": 49874, + "phe": 49875, + "olini": 49876, + "ĠDü": 49877, + "Ġdehydro": 49878, + "apeptide": 49879, + "Ġhes": 49880, + "Ġdistally": 49881, + "versions": 49882, + "Ġmedals": 49883, + "Ġflaw": 49884, + "Ġduo": 49885, + "Ġimpairing": 49886, + "toplasts": 49887, + "ĠHFILL": 49888, + "Ġesculent": 49889, + "Classification": 49890, + "ĠGriffith": 49891, + "ĠWellington": 49892, + "Ġattorney": 49893, + "Ast": 49894, + "kA": 49895, + "Ġmilit": 49896, + "Ġnite": 49897, + "ĠCasp": 49898, + "ĠChester": 49899, + "ĠMok": 49900, + "ĠRAR": 49901, + "Ġchr": 49902, + "unctor": 49903, + "Ġabduction": 49904, + "Ġuniv": 49905, + "ovars": 49906, + "ouk": 49907, + "ERICAL": 49908, + "éri": 49909, + "orbance": 49910, + "ĠIdentifies": 49911, + "amento": 49912, + "Ġparenthesis": 49913, + "ĠMEFs": 49914, + "Ġabsorbs": 49915, + "ĠArrayList": 49916, + "Ġcaregiving": 49917, + "ĠFILE": 49918, + "Ġfeldspar": 49919, + "ochthonous": 49920, + "Sort": 49921, + "jal": 49922, + "Ġtantal": 49923, + "arabine": 49924, + "ĠSaid": 49925, + "ĠBCE": 49926, + "ĠNGO": 49927, + "ynure": 49928, + "doteq": 49929, + "ĠLeyd": 49930, + "modality": 49931, + "ĠGeometrical": 49932, + "Almost": 49933, + "Ġhardened": 49934, + "noea": 49935, + "news": 49936, + "Ġcleanup": 49937, + "ĠArmed": 49938, + "ĠSnake": 49939, + "multiply": 49940, + "ĠMillennium": 49941, + "ĠSmoothing": 49942, + "positely": 49943, + "enary": 49944, + "isse": 49945, + "ĠYuc": 49946, + "Ġgeneal": 49947, + "Ġsupers": 49948, + "Ġhandheld": 49949, + "Ġembark": 49950, + "ĠBla": 49951, + "horst": 49952, + "ĠPDGFR": 49953, + "Ġcitr": 49954, + "Ġcalorie": 49955, + "ĠBuddhist": 49956, + "Member": 49957, + "Ġfears": 49958, + "Ġfiscal": 49959, + "ĠAIF": 49960, + "LOAD": 49961, + "peare": 49962, + "Ġbitumen": 49963, + "Particip": 49964, + "ĠIndianapolis": 49965, + "ĠAlbum": 49966, + "Ġscrutiny": 49967, + "acylglycer": 49968, + "ĠSakai": 49969, + "Ġthermodynamical": 49970, + "ZB": 49971, + "Ġhpf": 49972, + "ĠLIP": 49973, + "Ġexpiration": 49974, + "tilt": 49975, + "Ġflax": 49976, + "ĠSelectivity": 49977, + "ĠSchol": 49978, + "anya": 49979, + "orbents": 49980, + "Ġincubations": 49981, + "Ġmarginals": 49982, + "involved": 49983, + "Ġenthalpies": 49984, + "macrophages": 49985, + "constructor": 49986, + "ĠRoland": 49987, + "ĠPm": 49988, + "ĠRY": 49989, + "Ġblobs": 49990, + "Ġannuli": 49991, + "Ġunstimulated": 49992, + "ĠPetroleum": 49993, + "Ġmerges": 49994, + "Ġenveloping": 49995, + "ĠInitialization": 49996, + "Ġsheds": 49997, + "Ġadvisable": 49998, + "ylethanolamine": 49999 + }, + "merges": [ + "Ġ t", + "i n", + "Ġ a", + "h e", + "o n", + "r e", + "a t", + "Ġt he", + "e r", + "Ġ s", + "Ġ o", + "e n", + "a l", + "Ġ c", + "t i", + "o r", + "e d", + "e s", + "i s", + "Ġ p", + "Ġo f", + "n d", + "Ġ in", + "Ġ f", + "Ġ w", + "Ġ Ġ", + "i t", + "a n", + "r o", + "a r", + "Ġ d", + "Ġ m", + "Ġ b", + "Ġa nd", + "i c", + "l e", + "in g", + "i on", + "a s", + "Ġ e", + "Ġ re", + "at ion", + "Ġt o", + "e l", + "en t", + "a c", + "e t", + "e c", + "ti on", + "o m", + "s t", + "Ġ T", + "Ġ n", + "Ġt h", + "o l", + "u l", + "i m", + "R E", + "i g", + "u s", + "RE F", + "Ġ l", + "Ġ h", + "u r", + "Ġ is", + "ĠĠ ĠĠ", + "Ġf or", + "i d", + "a m", + "Ġ S", + "v e", + "i l", + "Ġ A", + "Ġ C", + "Ġ g", + "o t", + "it h", + "l y", + "c e", + "Ġc on", + "o w", + "Ġs t", + "u t", + "o s", + "Ġw ith", + "o d", + "r a", + "Ġ v", + "Ġp ro", + "u m", + "Ġ I", + "i f", + "u c", + "t er", + "u n", + "A R", + "S T", + "re s", + "Ġ on", + "E N", + "e re", + "Ġ P", + "ĠT he", + "Ġ M", + "Ġa s", + "AR T", + "Ġa n", + "EN D", + "ST ART", + "Ġth at", + "q u", + "e m", + "Ġb e", + "Ġe x", + "r i", + "a b", + "it y", + "ti c", + "v er", + "Ġa l", + "p l", + "t s", + "Ġ F", + "Ġ â", + "u re", + "Ġb y", + "at e", + "a g", + "i r", + "o c", + "p er", + "Ġ B", + "a y", + "Ġ D", + "Ġc om", + "Ġ H", + "at ed", + "Ġ R", + "Ġa re", + "ro m", + "Ġ E", + "o p", + "a d", + "s e", + "Ġ L", + "ig h", + "Ġ N", + "m ent", + "he r", + "o g", + "a in", + "ec t", + "u d", + "Ġd e", + "Ġ r", + "Ġa t", + "Ġw as", + "Ġ us", + "Ġre s", + "el l", + "i z", + "in e", + "p h", + "Ġa c", + "es s", + "o re", + "ic al", + "t h", + "u nd", + "r ac", + "Ġw e", + "at h", + "Ġ G", + "Ġf rom", + "at i", + "u p", + "is t", + "an t", + "Ġo r", + "f f", + "Ġcom p", + "Ġw h", + "Ġ W", + "c h", + "er s", + "Ġs p", + "or m", + "Ġc h", + "ation s", + "r an", + "u b", + "t e", + "d i", + "Ġs h", + "g e", + "as e", + "Ġw ere", + "ĠĠĠĠ ĠĠĠĠ", + "Ġ Î", + "a p", + "ĠI n", + "a nd", + "Ġs e", + "v el", + "Ġ im", + "Ġâ Ī", + "en s", + "i es", + "ic h", + "igh t", + "d uc", + "Ġ O", + "Ġ it", + "tion s", + "en d", + "Ġc o", + "Ġth is", + "Ġc an", + "Ġ k", + "â Ģ", + "le c", + "t ed", + "Ġm od", + "m ath", + "Ġcon t", + "Ġn e", + "Ġp ar", + "i b", + "ĠĠ Ġ", + "Ġ le", + "i v", + "u g", + "en ce", + "ig n", + "o us", + "ent s", + "y s", + "a ve", + "re d", + "res s", + "ab le", + "p or", + "al l", + "if f", + "es t", + "Ġa p", + "Ġin c", + "n t", + "ar y", + "i ti", + "Ġwh ich", + "Ġn ot", + "f orm", + "Ġs y", + "Ġa d", + "l ow", + "a k", + "Ġp er", + "Ġ he", + "p ro", + "an ce", + "i al", + "u e", + "Ġ en", + "Ġc l", + "as s", + "i p", + "ran s", + "Ġo b", + "Ġg en", + "ti m", + "Ġd is", + "un c", + "Ġin t", + "e p", + "et w", + "Ġd iff", + "ac h", + "t her", + "im e", + "ag e", + "p le", + "il l", + "y p", + "Ġ K", + "ac t", + "ar i", + "Ġm et", + "or s", + "Ġh ave", + "Ġst ud", + "on g", + "Ġ U", + "Ġp l", + "id e", + "m a", + "he n", + "if ic", + "om e", + "Ġ i", + "ul ar", + "Ġ V", + "al ly", + "Ġsh ow", + "ri b", + "i a", + "en ti", + "Ġas s", + "on d", + "f t", + "Ġa b", + "Ġin ter", + "ĠT h", + "T he", + "st r", + "Ġc ell", + "c al", + "Ġmod el", + "at a", + "as t", + "Ġe ff", + "Ġt rans", + "at es", + "as ed", + "o st", + "v i", + "an g", + "o ur", + "Ġm e", + "ar d", + "Ġdiff ere", + "Ġp re", + "Ġd i", + "ĠâĪ Ĵ", + "ol og", + "u tion", + "o und", + "ac e", + "Ġres ul", + "er m", + "p os", + "he re", + "ti ve", + "or d", + "s o", + "st em", + "y l", + "Ġp h", + "Ġ y", + "am e", + "or k", + "ati ve", + "Ġ qu", + "r ic", + "S U", + "w o", + "Ġ un", + "Ġe v", + "a re", + "# #", + "d e", + "e en", + "ti v", + "Ġg ro", + "or y", + "Ġcon s", + "Ġs ub", + "t a", + "- -", + "Ġst r", + "b er", + "er v", + "etw een", + "en c", + "Ġan al", + "in t", + "Ġh as", + "uc h", + "Ġre g", + "Ġb etween", + "Ġd et", + "Ġal l", + "c ess", + "Ġex p", + "ec tion", + "Ġâ Ģ", + "in d", + "at er", + "Ġs ign", + "p t", + "ug h", + "it e", + "il ity", + "Ġus ing", + "Ġv al", + "Ġ ro", + "re e", + "Ġre l", + "o ut", + "Ġf unc", + "i tion", + "Ġc or", + "Ġal so", + "Ġt wo", + "n e", + "Ġ J", + "Ġsy stem", + "c l", + "uc t", + "Ġs im", + "t ain", + "u st", + "i ed", + "por t", + "Ġre c", + "Ġres p", + "Ġd ata", + "r m", + "res ent", + "ul d", + "x t", + "Ġ j", + "r y", + "ac k", + "Ġ ra", + "p ar", + "Ġfor m", + "Ġs c", + "f rac", + "ĠW e", + "at ing", + "ec h", + "h od", + "Ġf ol", + "in ed", + "ĠS t", + "u al", + "Ġus ed", + "Ġon e", + "Ġd es", + "Ġ Ï", + "Ġv ari", + "Ġd ist", + "Ġn um", + "y m", + "e w", + "re c", + "o b", + "Ġin f", + "Ġa r", + "lec t", + "l l", + "on s", + "ĠTh is", + "os e", + "i le", + "pl ay", + "e ar", + "o x", + "u res", + "on e", + "Ġstud y", + "ys is", + "Ġfol low", + "y le", + "rac t", + "d is", + "Ġp os", + "r ight", + "Ġth an", + "ro s", + "a v", + "F ig", + "Ġt ime", + "iz ation", + "ul ation", + "iz ed", + "Ġs ur", + "ot h", + "Ġo ut", + "Ġc ol", + "at ure", + "i ve", + "Ġs ol", + "Ġ x", + "el d", + "Ġo ther", + "pl ic", + "Ġde f", + "er g", + "Ġgen er", + "el y", + "Ġbe en", + "Ġinc re", + "Ġthe se", + "Ġn o", + "a x", + "st yle", + "ar g", + "i an", + "Ġin d", + "Ġs uch", + "Ġfunc tion", + "t ing", + "Ġe qu", + "a us", + "Ġ und", + "math b", + "tic al", + "Ġh igh", + "ra in", + "Ġa m", + "i eld", + "o un", + "ress ion", + "Ġsp ec", + "Ġo p", + "Ġd ec", + "Ġo ver", + "Ġmet hod", + "Ġs et", + "â Ī", + "Ġ if", + "di tion", + "u es", + "ec ts", + "dis play", + "he m", + "Ġp ati", + "Ġresul ts", + "ol d", + "an c", + "display style", + "Ġe ach", + "Ġm ore", + "l es", + "p r", + "ac ter", + "Ġthe ir", + "Ġac c", + "Ġap pro", + "is s", + "iz e", + "Ġin v", + "as es", + "Ġcell s", + "ir st", + "l u", + "a il", + "Ġme as", + "Ġl ow", + "o v", + "t he", + "i k", + "* *", + "e f", + "Ġb ut", + "he s", + "f ter", + "Ġdiffere nt", + "vel y", + "Ġex t", + "Ġthe re", + "oc i", + "Ġpro b", + "Ġit s", + "r on", + "ment s", + "Ġa g", + "N A", + "Ġp o", + "ic e", + "yp e", + "Ġgro up", + "âĢ ĵ", + "e ver", + "ul t", + "is m", + "ter n", + "ab ility", + "ion s", + "ar k", + "Ġn on", + "t o", + "ĠĠĠĠ ĠĠĠ", + "Ġob s", + "Ġt re", + "al s", + "le ft", + "ĠP ro", + "Ġon ly", + "Ġm an", + "d er", + "Ġp ol", + "ur ing", + "am et", + "ro l", + "I n", + "y n", + "Ġund er", + "ĠC h", + "Ġw here", + "o od", + "Ġ X", + "n ce", + "Ġpar tic", + "ect ed", + "ĠF ig", + "Ġe m", + "Ġf act", + "ĠA n", + "Ġper form", + "Ġs o", + "Ġanal ysis", + "st ract", + "he d", + "Ġm ay", + "at ic", + "Ġre p", + "te in", + "duc ed", + "Ġ up", + "Ġint o", + "Ġnum ber", + "Ġo ur", + "Ġe t", + "e g", + "it le", + "o ver", + "i x", + "at or", + "ul ti", + "Ġinc l", + "o uld", + "ic i", + "b stract", + "Ġcomp le", + "Ġpati ents", + "Ġd o", + "Ġex per", + "v id", + "an ge", + "Ġle vel", + "Ġpro cess", + "math cal", + "p s", + "Ġsign ific", + "Ġs am", + "T itle", + "Ġb l", + "Ġstr uct", + "et a", + "Ġobs erv", + "ra ph", + "g r", + "Ġac tiv", + "Ġf irst", + "vel op", + "g en", + "ib le", + "Ġs m", + "Ġw ill", + "Ġ Q", + "Ġmeas ure", + "p ut", + "Ġl oc", + "Ġm o", + "ver s", + "o f", + "t al", + "ere d", + "ow n", + "Ġm at", + "iti es", + "ti l", + "in al", + "Ġc ar", + "ph a", + "Ġb oth", + "Ġc ur", + "SU B", + "it s", + "re l", + "Ġw hen", + "Ġ z", + "Ġch ar", + "Ġb i", + "c ent", + "Ġthe n", + "is e", + "ow ever", + "Ġm in", + "ĠF or", + "Ġ Y", + "p tion", + "Ġ es", + "m un", + "Ġincl ud", + "is tic", + "c on", + "Ġob tain", + "a red", + "duc tion", + "Ġsignific ant", + "Ġ Z", + "Ġp resent", + "an n", + "Ġ id", + "enc y", + "Ġv er", + "v al", + "y d", + "ro ugh", + "SU P", + "f ore", + "Ġs ome", + "ĠA s", + "Ġs up", + "Ġa fter", + "olog ical", + "enti f", + "Ġc ase", + "Ġs ec", + "el f", + "Ġde p", + "k s", + "Ġc al", + "v ed", + "Ġt em", + "Ġus e", + "ĠC om", + "l am", + "in es", + "ay s", + "Ġg iv", + "Ġcons id", + "Ġe lect", + "ation al", + "ĠĠĠĠĠĠĠĠ ĠĠĠĠĠĠĠĠ", + "i qu", + "ti es", + "Ġl ine", + "Ġs u", + "A bstract", + "oun t", + "Ġde velop", + "ĠC on", + "olog y", + "al pha", + "an s", + "pr ime", + "c c", + "og en", + "Ġw ork", + "v en", + "i um", + "ec tive", + "Ġp a", + "t en", + "ĠA l", + "Ġ ï", + "Ġf e", + "âĢ Ļ", + "enti al", + "l ine", + "Ġpar amet", + "Ġpro tein", + "Ġdis c", + "f ace", + "c es", + "Ġw ell", + "ur al", + "en g", + "Ġd uring", + "ro w", + "an ts", + "Ġre m", + "form ation", + "Ġex am", + "Ġm ic", + "âĪ Ĵ", + "le m", + "erg y", + "Ġass oci", + "Ġ Ã", + "ro p", + "Ġf ield", + "t y", + "Ġcl ass", + "Ġ u", + "i e", + "Ġb ec", + "Ġexper im", + "s p", + "Ġp r", + "il ar", + "ti al", + "Ġcon st", + "ĠI t", + "Ġcont rol", + "d a", + "Ġm ulti", + "iti ve", + "ic s", + "ur n", + "Ġind ic", + "Ġf ound", + "te xt", + "Ġne w", + "Ġre f", + "g or", + "ra p", + "Ġdes c", + "Ġs ame", + "Ġfollow ing", + "Ġdist rib", + "Fig ure", + "il d", + "Ġan ti", + "etw ork", + "o ve", + "Ġth rough", + "Ġm ost", + "c er", + "Ġdet erm", + "h a", + "el ta", + "ar ge", + "Ġshow n", + "in ce", + "Ġan y", + "re n", + "d ot", + "r al", + "r ation", + "am ma", + "o id", + "Ġm ed", + "ens ion", + "ar t", + "Ġp red", + "m et", + "mathb b", + "ak e", + "Ġcal c", + "Ġh ig", + "Ġth ree", + "Ġb ased", + "m on", + "ar ch", + "-- --", + "pl es", + "ag es", + "aus e", + "is h", + "ti vely", + "qu i", + "res p", + "Ġchar acter", + "oc k", + "Ġtre at", + "Ġpro per", + "e x", + "Ġsm all", + "Ġt erm", + "b da", + "Ġk n", + "od e", + "ing s", + "Ġexp ression", + "Ġm on", + "em b", + "ut e", + "ech n", + "h ib", + "Ġdi rec", + "in ation", + "ith m", + "ul ated", + "Ġc y", + "Ġp ot", + "Ġor der", + "ot e", + "ical ly", + "Ġval ues", + "or t", + "ur ther", + "ce pt", + "yn am", + "o ugh", + "ech an", + "Ġâ ī", + "o k", + "em ent", + "ĠÎ ¼", + "Ġes tim", + "Ġeff ect", + "Ġp ath", + "Ġcon f", + "Ġap p", + "Ġgiv en", + "Ġ end", + "s et", + "Ġg l", + "Ġthe y", + "n ing", + "Ġt est", + "Ġtem per", + "v es", + "Ġval ue", + "it ed", + "al ity", + "Ġl im", + "Ġsp ect", + "ent ly", + "ti t", + "Ġse qu", + "Ġid entif", + "/ /", + "ig ma", + "Ġen ergy", + "in c", + "n ess", + "ens ity", + "Ġprob lem", + "yd ro", + "ag n", + "an e", + "re nt", + "c om", + "j ect", + "Ġim port", + "ĉ ĉ", + "Ġo per", + "ol ution", + "Ġa ut", + "ec tively", + "ĠH owever", + "h o", + "ent al", + "Ġs ing", + "e y", + "m u", + "ros s", + "ac tion", + "ep end", + "ĠE x", + "vi ous", + "Ġstud ies", + "s c", + "orm al", + "Ġh ad", + "Ġm ain", + "al th", + "gor ithm", + "Ġf l", + "om et", + "Ġ Â", + ". .", + "er r", + "Ġpos s", + "Ġdiffere n", + "Ġobserv ed", + "ra y", + "Ġpred ic", + "Ġgen e", + "Ġst ate", + "W e", + "Ġstruct ure", + "Ġre t", + "resp ond", + "re qu", + "il y", + "ĠâĪ Ī", + "Ġs er", + "Ġb ound", + "Ġrep resent", + "ph i", + "Ġtreat ment", + "h at", + "Ġre qui", + "ap p", + "um an", + "Ġhig her", + "Ġl arge", + "Ġt ra", + "w ard", + "Ġobtain ed", + "Ġco uld", + "ti g", + "ĠU n", + "Ġdesc rib", + "Ġsim ilar", + "por ted", + "in s", + "Ġad dition", + "os is", + "Ġn etwork", + "Ġe le", + "p i", + "ri x", + "Ġr ate", + "g an", + "ug g", + "us s", + "Ġm echan", + "Ġdis e", + "Ġeff ects", + "Ġmodel s", + "or ph", + "ik e", + "Ġsec ond", + "mathb f", + "Ġd ue", + "Ġ q", + "Ġp res", + "Ġt echn", + "el s", + "Ġcor respond", + "Ġassoci ated", + "pos ed", + "Ġm ass", + "ro und", + "vi ew", + "Ġin s", + "ĠâĢ ¢", + "di tions", + "Ġwh ile", + "o le", + "Ġl ong", + "al u", + "Ġc ap", + "Ġsur face", + "Ġcomple x", + "Ġc ent", + "Ġcomp ared", + "Ġf ind", + "arg et", + "at ory", + "f er", + "Ġs ize", + "Ġcont ain", + "us ion", + "u tions", + "Ġd em", + "E S", + "Ġdep end", + "at is", + "s um", + "ff ici", + "Ġb as", + "lam bda", + "i er", + "A T", + "Ġm ax", + "Ġim p", + "Ġev alu", + "Ġtemper ature", + "in k", + "ect or", + "Ġs cal", + "Ġgro w", + "ow er", + "Ġresp ectively", + "le ar", + "s h", + "ic k", + "Ġf il", + "ir c", + "il on", + "r am", + "ĠÎ ±", + "ific ation", + "Ġo cc", + "Ġy ear", + "Ġs ugg", + "Ġra di", + "if ied", + "ha vi", + "Ġwith in", + "Ġs ens", + "Ġin te", + "Ġw ould", + "Ġcon cent", + "Ġmic ro", + "Ġsing le", + "ĠS p", + "o u", + "Ġat t", + "Ġs elf", + "Ġab out", + "eng th", + "Ġe l", + "ĠR e", + "x im", + "Ġcon ditions", + "ud e", + "ĠA t", + "w here", + "m ed", + "Ġne ed", + "ir on", + "Ġp op", + "Ġresul t", + "Ġpo int", + "Ġl o", + "Ġal gorithm", + "Ġactiv ity", + "le q", + "ple ment", + "ĠR es", + "Ġsy m", + "on str", + "at ures", + "Ġim pro", + "f or", + "Ġgener al", + "it er", + "Ġex pl", + "## #", + "Ġd om", + "Ġt ri", + "m in", + "Ġdistrib ution", + "Ġt r", + "ĠThe re", + "os s", + "u ce", + "math rm", + "ul l", + "E R", + "re g", + "Ġp e", + "Ġto tal", + "Ġle ad", + "= =", + "i od", + "Ġass um", + "Ġch ang", + "Ġg ra", + "M I", + "Ġcomp ut", + "Ġcom b", + "Ġin formation", + "Ġdes ign", + "Ġin iti", + "Ġf requ", + "im ension", + "c op", + "Ġproper ties", + "Ġconsid er", + "Ġlevel s", + "en e", + "Ġt ype", + "iv ed", + "ĠH e", + "epend ent", + "Ġap plic", + "Ġinv es", + "Ġpre vious", + "a w", + "Ġsp ace", + "Ġpro vid", + "h yl", + "Ġinves tig", + "Ġappro ach", + "ater ial", + "on se", + "lec ular", + "Ġparamet ers", + "Ġph ase", + "ul ations", + "ub l", + "b eta", + "Ġa v", + "Ġf lu", + "Ġpot ential", + "ĠThe se", + "s igma", + "l o", + "tim es", + "Ġop tim", + "is ion", + "Ġa ff", + "Ġme an", + "Ġbe havi", + "Ġv ol", + "ore m", + "ag ne", + "Ġdec re", + "tion al", + "Ġsol ution", + "Ġh uman", + "g er", + "Ġpa per", + "Ġcomp ar", + "Ġlow er", + "and ard", + "Ġcor rel", + "c ri", + "Ġcur rent", + "Ġd er", + "iss ion", + "ĠFig ure", + "Ġpro duc", + "Ġw ater", + "ĠT o", + "Ġth ose", + "Ġac id", + "Ġcan cer", + "Ġloc al", + "t on", + "Ġf low", + "Ġreg ion", + "Ġhe alth", + "Ġimport ant", + "og raph", + "ab l", + "Ġse lec", + "Ġg re", + "Ġin di", + "ad e", + "r id", + "Ġsh ould", + "b ased", + "Ġab ove", + "l d", + "Ġsystem s", + "ic ation", + "Ġ ed", + "Ġt yp", + "Ġph ys", + "o per", + "Ġcomp on", + "O N", + "Ġsu per", + "g a", + "hem ical", + "is k", + "op h", + "Ġh y", + "Ġanal y", + "in u", + "Ġt arget", + "ĠA d", + "Ġp at", + "g amma", + "Ġsam ples", + "Ġs l", + "Ġpar t", + "old s", + "Ġb el", + "im um", + "ĠI m", + "Ġdise ase", + "I I", + "is ts", + "i ver", + "Ġperform ance", + "ĠĠĠĠĠĠĠĠ ĠĠĠ", + "g le", + "Ġo x", + "nd om", + "ĠĠĠĠ Ġ", + "Ġbec ause", + "ay er", + "Ġr ange", + "Ġco un", + "Ġincre ased", + "oc h", + "on al", + "Ġver y", + "Ġd ynam", + "an ti", + "Ġad d", + "Ġin hib", + "Ġmethod s", + "id ence", + "in ical", + "ere nce", + "iv al", + "u le", + "Ġfact or", + "Ġf in", + "in ts", + "v iron", + "Ġs our", + "ver age", + "e qu", + "Ġe ar", + "Ġshow ed", + "it es", + "Ġperform ed", + "Ġre se", + "ĠE n", + "Ġspec ies", + "A C", + "ĠC l", + "h ip", + "til de", + "i o", + "at ely", + "T h", + "od y", + "Ġincre ase", + "ĠP h", + "âĢ Ŀ", + "Ġshow s", + "ĠA c", + "Ġp ost", + "ord ing", + "enc es", + "o y", + "n er", + "Ġresp onse", + "Ġocc ur", + "r ho", + "Ġper iod", + "ar s", + "Ġre d", + "ĠO n", + "Ġd ensity", + "Ġexam ple", + "g et", + "Ġre al", + "ĠC ount", + "ac y", + "Ġp ower", + "Ġab s", + "it al", + "Ġpr im", + "âĢ IJ", + "Ġdef ined", + "Ġn ormal", + "a j", + "Ġin st", + "Ġal low", + "Ġposs ible", + "Ġv is", + "Ġre ported", + "Ġsign al", + "the ta", + "Ġd en", + "ab les", + "Ġde g", + "Ġindi vid", + "agne tic", + "Ġgroup s", + "a e", + "ar row", + "Ġst at", + "Ġmechan ism", + "os p", + "m er", + "ot her", + "Ġpro t", + "Ġc ases", + "Ġc r", + "Ġt e", + "Ġinte gr", + "et s", + "Ġdevelop ment", + "Ġra ndom", + "Ġinv ol", + "Ġinclud ing", + "Ġ err", + "gr am", + "Ġpartic ular", + "ep s", + "Ġst andard", + "pos ition", + "Ġcont rib", + "se qu", + "Ġman y", + "Ġf urther", + "Ġsignificant ly", + "at ors", + "ur b", + "Ġag ain", + "b ar", + "Ġwith out", + "Ġse ver", + "Ġto p", + "re t", + "l ed", + "Ġmat rix", + "Ġspec ific", + "ate g", + "Ĩ Ĵ", + "Ġdirec t", + "Ġsam ple", + "Ġthe m", + "S A", + "o int", + "Ġro le", + "Ġchang es", + "rac tion", + "Ġs um", + "Ġindivid ual", + "I N", + "Ġim mun", + "c ed", + "o h", + "Ġstr ong", + "Ġe p", + "Ġline ar", + "u ally", + "d elta", + "w ay", + "as ing", + "Ġt im", + "Ġv i", + "is on", + "Ġfunc tions", + "Ġam ong", + "Ġse e", + "ere st", + "Ġgrow th", + "Ġr ati", + "ĠS c", + "ix ed", + "R NA", + "e ed", + "ta u", + "Ġ ent", + "Ġd r", + "o res", + "Ġappro xim", + "f ul", + "Ġre le", + "Ġfact ors", + "Ġdisc uss", + "Ġph ot", + "Ġpro posed", + "er o", + "ome ga", + "Ġf our", + "as tic", + "Ġyear s", + "hes is", + "iqu e", + "Ġm aterial", + "Ġb re", + "Ġpro f", + "ĠA p", + "Ġne g", + "Ġb u", + "Ġass ess", + "ĠâĢ ľ", + "Ġv ir", + "at ter", + "Ġdescrib ed", + "istic s", + "Ġcomp os", + "a z", + "str uc", + "Ġt um", + "par tial", + "a f", + "Ġwh o", + "at al", + "Ġdem onstr", + "anc es", + "y t", + "Ġrem ain", + "Ġl ess", + "Ġpos itive", + "om ic", + "Ġs ince", + "og n", + "Ġcon dition", + ": :", + "Ġdo es", + "ti ce", + "os ph", + "Ġpro v", + "ĠC O", + "Ġr at", + "Ġterm s", + "b ox", + "Ġt ak", + "Ġpat tern", + "al e", + "Ġn an", + "ul es", + "Ġm ut", + "is hed", + "Ġrel ated", + "Ġthe ory", + "b ol", + "c dot", + "viron ment", + "a ir", + "i vers", + "ĠA r", + "Ġï £", + "ress ed", + "Ġâī ¤", + "ĠM et", + "I D", + "ul ts", + "ĠÎ ²", + "Ġd at", + "pos e", + "Ġor ig", + "Ġret urn", + "Ġch ange", + "Ġl arg", + "a u", + "ac es", + "Ġare a", + "Ġgen es", + "A S", + "Ġh ydro", + "Ġcons ist", + "m an", + "Ġrese arch", + "ĠD e", + "Ġor gan", + "as k", + "Ġb ack", + "Ġfollow s", + "un g", + "ro ll", + "Ġequ ation", + "pl ied", + "t r", + "Ġcorrespond ing", + "od es", + "es ted", + "Ġrel ations", + "n al", + "Ġf r", + "Ġlim it", + "m it", + "Ġof f", + "ut ed", + "Ġr isk", + "re ad", + "Ġkn own", + "pl it", + "tiv ity", + "Ġsequ ence", + "Ġconsid ered", + "x i", + "ĠM od", + "v ity", + "Ġn uc", + "c le", + "ic es", + "Ġl ength", + "Ġsever al", + "s ing", + "o ot", + "n ot", + "Ġst ress", + "ĠI f", + "C T", + "ro ph", + "Ġcom mun", + "Ġcl ust", + "ĠL e", + "m e", + "ant um", + "Ġm emb", + "Ġl ab", + "Ġev en", + "Ġinf lu", + "c k", + "Ġà Ĺ", + "Ġl og", + "v ing", + "es ts", + "Ġh is", + "an k", + "ĠI nd", + "ac tions", + "ft y", + "m od", + "Ġre view", + "th ough", + "Ġeff ici", + "Ġm ap", + "in fty", + "Ġbe ing", + "l and", + "Ġcl inical", + "Ġmeasure d", + "er ing", + "ĠT able", + "Ġs he", + "se e", + "Ġs ection", + "Ġav ail", + "om en", + "Ġv ers", + "Ġd el", + "it her", + "er ation", + "Ġh and", + "Ġcont inu", + "Ġcon n", + "h ors", + "ra d", + "Ġf am", + "Ġle ar", + "Ġiniti al", + "y stem", + "Ġg e", + "ĠâĢ ²", + "Ġc irc", + "Ġp ubl", + "ĠI s", + "Ġv ia", + "Ġcom mon", + "if e", + "Ġm ark", + "Ġe ver", + "ar c", + "b ig", + "er tain", + "\\ \\", + "v ar", + "A s", + "ros cop", + "Ġa ge", + "Ġh ow", + "ĠL et", + "str uct", + "Ġa verage", + "v ant", + "ĠS h", + "imension al", + "S C", + "ap e", + "n u", + "Ġl oss", + "as on", + "id es", + "Ġpop ulation", + "Ġdom ain", + "ind ing", + "w e", + "A L", + "Ġacc ur", + "et y", + "Ġc aus", + "D elta", + "rap y", + "Ġpro m", + "tim e", + "Ġint ro", + "Ġmulti ple", + "Ġconst ant", + "pl ing", + "in o", + "aj or", + "i or", + "ab ol", + "de f", + "Ġpo ints", + "ver se", + "n ame", + "ĠS e", + "it or", + "P ro", + "ar m", + "Ġt iss", + "Ġf ib", + "Ġg raph", + "Ġc all", + "atis f", + "Ġcon duc", + "de x", + "ĠN e", + "Ġp ers", + "er n", + "C R", + "ang le", + "Ġfrequ ency", + "A P", + "Ġpresent ed", + "am p", + "Ġbe fore", + "ord s", + "Ġin put", + "Ġâ ĨĴ", + "Ġpartic ip", + "O R", + "Ġch ild", + "Ġc re", + "ffici ent", + "Ġse par", + "ur ation", + "Î ±", + "Ġex ist", + "is ed", + "Ġl ight", + "im al", + "** **", + "ĠD NA", + "he l", + "Ġint erest", + "b f", + "k e", + "Ġcol lec", + "Ġt rain", + "a i", + "ĠP l", + "ĠÎ »", + "ĠC o", + "Ġim age", + "Ġh yp", + "om a", + "Ġwe ight", + "Ġc ross", + "r t", + "Ġdiffere nce", + "Ġfe atures", + "med i", + "t ype", + "Ġp ress", + "I C", + "Ġthe rm", + "Ġst ates", + "u str", + "ti ll", + "Ġh ist", + "Ġrati o", + "ag ing", + "ĠA ll", + "Ġhe l", + "b on", + "Ġbehavi or", + "Ġp ri", + "Ġsy nt", + "end ed", + "ĠIn t", + "t t", + "Ġvari ous", + "rec t", + "Ġpre c", + "Ġtim es", + "M S", + "Ġanaly z", + "Ġc are", + "m at", + "Ġal ong", + "Ġp ur", + "ati vely", + "Ġst ar", + "j ects", + "i i", + "ist ance", + "ĠThe n", + "A N", + "Ġparamet er", + "ul ate", + "Ġever y", + "Ġs atisf", + "Ġdeterm ined", + "in a", + "ran e", + "Ġpa ir", + "o ol", + "T able", + "Ġth us", + "ogen e", + "ĠÏ Ĩ", + "Ġpro gram", + "as c", + "Ġen vironment", + "M P", + "Ġre ad", + "Ġac h", + "Ġpres ence", + "Ġm ice", + "F or", + "Ġpro duction", + "Ġdifferen ces", + "Ġprov ide", + "st e", + "am es", + "ĉ Ġ", + "Ġ ±", + "ro up", + "Ġelect ron", + "Ġhy per", + "b it", + "ĠR ec", + "Ġv ector", + "ub le", + "ran gle", + "Ġw r", + "w ide", + "Ġâ Ĭ", + "rac k", + "ry st", + "Ġin j", + "eg a", + "Ġw he", + "ps ilon", + "Ġagain st", + "Ġdi agn", + "Ġh om", + "Ġach ie", + "n s", + "Ġre ce", + "---- ----", + "Ġavail able", + "in f", + "Ġs uc", + "Ġg u", + "Ġm ajor", + "ĠTh us", + "w are", + "Ġsup port", + "l or", + "Ġexperim ental", + "ĠM o", + "Ġconcent ration", + "tic s", + "Ġn ec", + "Ġp hen", + "s q", + "Ġcl os", + "s ub", + "Ġkn ow", + "Ġform ation", + "Ġd id", + "ous e", + "in ary", + "ic t", + "ĠC D", + "Th is", + "l ess", + "Ġne ar", + "Ġimpro ve", + "ab il", + "Ġre ve", + "Ġexperim ents", + "i ence", + "ul a", + "ore d", + "Ġ unc", + "_ _", + "Ġap plied", + "Ġre duced", + "Ġdet ail", + "st and", + "Ġch o", + "om y", + "Ġcalc ulated", + "Ġen h", + "L ES", + "it ro", + "Ġresp ons", + "Ġ est", + "Ġm i", + "Ġco e", + "ĠThere fore", + "ĠM ore", + "b l", + "anc ed", + "um e", + "Ġb and", + "Ġac t", + "Ġe ither", + "om es", + "ĠG en", + "v are", + "E T", + "re en", + "ĠP ar", + "ĠS im", + "Ġidentif ied", + "Ġinter action", + "Ġm ade", + "Ġsour ce", + "ti s", + "ot s", + "m ega", + "Ġs erv", + "m s", + "al ysis", + "v ent", + "en se", + "g l", + "Ġl ines", + "Ġapp ear", + "ti f", + "Ġf ree", + "om s", + "in ing", + "ere n", + "Ġch ann", + "vare psilon", + "s im", + "Ġco u", + " °", + "Ġerr or", + "Ġqu anti", + "ĠE q", + "b y", + "ĠI I", + "te x", + "ĠS ch", + "sq rt", + "oc us", + "Ġde v", + "qu ad", + "ter s", + "Ġrelations hip", + "ol l", + "Ġg o", + "Ġw ave", + "Ġle ft", + "w ays", + "h i", + "Ġr ight", + "ob al", + "Ġd own", + "u k", + "Ġcol l", + "Ġm agnetic", + "Ġpro g", + "dot s", + "Ġstr ateg", + "b s", + "unc tion", + "Ġen c", + "Ġc lear", + "Ġco st", + "ge b", + "et ter", + "MI LES", + "lam m", + "Ġm ust", + "Ġeff ective", + "Ġex c", + "Ġpl as", + "Ġsugg est", + "i tions", + "Ġle ast", + "y ing", + "ly ing", + "Ġl ik", + "O mega", + "ak ing", + "Ġmax imum", + "Ġrel ative", + "à ©", + "Ġacc ording", + "i ent", + "Ġw ay", + "Ġs em", + "at ural", + "l ike", + "res h", + "ĠM e", + "P s", + "ĠT rans", + "is c", + "Ġp rac", + "Ġr un", + "Ġcon ver", + "Ġs k", + "Ġy ield", + "ge q", + "ab ly", + "Ġanti b", + "iz ing", + "Î ²", + "m ission", + "Ġn ow", + "Ġdet ection", + "el oc", + "Ġg et", + "er t", + "Ġvari ables", + "Ġop en", + "Ġpress ure", + "Ġst rain", + "um ent", + "ĠF urther", + "Ġqu antum", + "Ġim plement", + "Ġear ly", + "Ġfr ame", + "Ġsh ort", + "Ġdr ug", + "Ġrequi red", + "P S", + "Ġm y", + "Ġm uch", + "Ġm em", + "C C", + "Ġqu ality", + "Ġprotein s", + "Ġl ayer", + "Ġqu es", + "Ġre cept", + "Ġhe re", + "Ġpro ced", + "ure d", + "Ġdevelop ed", + "Ġpos ition", + "r um", + "Ġl at", + "Ġincre asing", + "E M", + "Ġmeasure ments", + "Ġb en", + "Ġis ol", + "w h", + "T o", + "Ġval id", + "Ġfunction al", + "em ma", + ".. .", + "or ld", + "ri es", + "Ġprob ability", + "ĠN ew", + "Ġm m", + "O S", + "A D", + "ĠÎ ´", + "Ġscal e", + "ĠF e", + "ĠThe orem", + "ĠQ u", + "Ġcompon ents", + "Ġbl ood", + "ĠÏ ĥ", + "ac c", + "Ġb etter", + "Ġst ep", + "ĠÎ ³", + "Ġf ac", + "ane ous", + "Ġlo ad", + "Ġmet abol", + "Ġev olution", + "s on", + "re am", + "Ġe as", + "ir d", + "d imensional", + "b or", + "Ġm us", + "Ġequ ations", + "ps i", + "ord er", + "ol ar", + "Ġnum er", + "Ġk ey", + "or th", + "Ġsim ple", + "if t", + "cal e", + "Ġin dex", + "ĠâĢ ĵ", + "Ġconcent r", + "g es", + "Ġneg ative", + "Ġv eloc", + "Ġa x", + "ĠE ff", + "Ġfin ite", + "Ġ ill", + "ch ing", + "Ġpati ent", + "eps ilon", + "Ġm en", + "Ġc ri", + "I S", + "C l", + "Ġcon cl", + "ĠÎ ¸", + "ib ility", + "Ġsym met", + "ent er", + "Ġdist ance", + "Ġpol ym", + "igh ts", + "Ġc ult", + "Ġpe ak", + "Ġac ross", + "in ition", + "Ġle t", + "Ġcon struc", + "Ġinclud ed", + "Ġh owever", + "Ġreg ions", + "Ġlear ning", + "Ġev idence", + "in ally", + "Ġne ut", + "it ation", + "Ġwhe ther", + "Ġout put", + "ĠS ection", + "Ġg ood", + "I T", + "u ation", + "Ġtyp es", + "b m", + "c os", + "w ith", + "l im", + "o tic", + "Ġs till", + "Ġd ays", + "Ġstud ied", + "Ġim ages", + "b le", + "Ġar g", + "line ar", + "Ġprocess es", + "Ġw id", + "Ġtrain ing", + "Ġind ependent", + "pl ac", + "Ġres id", + "Ġsuc cess", + "Ġnuc le", + "G F", + "le t", + "pl oy", + "Ġtum or", + "G amma", + "Ġthere fore", + "r ast", + "Ġf ocus", + "as h", + "Ġbel ow", + "ial ly", + "Ġcompar ison", + "Ġad j", + "Ġl ike", + "Ġmo lecular", + "ri ed", + "Ġf it", + "ĠD i", + "l og", + "Ġpl ay", + "w ork", + "ec tions", + "Ġelect ro", + "u it", + "m ore", + "Ġm ight", + "Ġanal ys", + "Ġme ans", + "Ġcorrel ation", + "k n", + "Ġcont roll", + "I V", + "C h", + "p ec", + "ra g", + "Ġm agn", + "Ġphys ical", + "I ON", + "Ġreve al", + "Ġph osph", + "Ġr ates", + "Ġlarg er", + "Ġs tim", + "Ġso ft", + "Ġcomp ound", + "b e", + "ch i", + "ĠN o", + "Ġimp act", + "t or", + "Ġprim ary", + "oc ial", + "Ġapplic ation", + "Ġsol utions", + "d uce", + "Ġcharacter istics", + "Ġele ments", + "Ġvi ew", + "Ġl ater", + "ut ure", + "Ġfam ily", + "ri al", + "Ġtrans cri", + "or ption", + "Ġs w", + "C D", + "E D", + "Ġem b", + "Ġz ero", + "ol s", + "Ġl ife", + "ce p", + "ĠL i", + "th s", + "Ġser ies", + "Ġa round", + "Ġtrans ition", + "ĠC or", + "ĠâĪ Ĥ", + "Ġdat as", + "Ġ her", + "ĠB y", + "A M", + "sp ec", + "ol es", + "ograph y", + "t le", + "ĠC ar", + "al le", + "Ġest abl", + "ag ement", + "Ġsc hem", + "g round", + "Ġf ail", + "Ġexp ected", + "Ġrequi re", + "ar ray", + "Ġexperim ent", + "Ġele ment", + "Ġne u", + "Ġgener ated", + "Ġs ite", + "ĠCon t", + "ĠR NA", + "er al", + "Ġcont ent", + "Ġb acter", + "l er", + "Ġtrans fer", + "ul f", + "right arrow", + "an y", + "ĠS ince", + "in duced", + "Ġre action", + "he ck", + "Ġstruct ures", + "Ġcoun t", + "Ġdeterm ine", + "z ym", + "ĠB l", + "Ġunder stand", + "oc al", + "Ġsy n", + "Ġpol y", + "ur y", + "Ġb est", + "Ġf ixed", + "ren g", + "Ġc hemical", + "Ġtiss ue", + "Ġp ul", + "Ġbound ary", + "is ing", + "Ġb ro", + "atis tical", + "ic ity", + "s k", + "r ing", + "Ġl ast", + "Ġchild ren", + "r im", + "Ġre duction", + "Ġsp in", + "Ġb ody", + "oper ator", + "v ari", + "Ġd iv", + "ym bol", + "Ġm al", + "Ġsp ati", + "a h", + "ĠB i", + "b ack", + "s y", + "Ġse en", + "ĠW ith", + "id s", + "plic ations", + "Ġnec ess", + "Ġs ide", + "Ġb rain", + "Ġf ew", + "Ġapplic ations", + "ut es", + "ac hes", + "Ġac tive", + "var phi", + "ter m", + "Ġm om", + "ivers ity", + "Ġf inal", + "led ge", + "Ġdynam ics", + "av ing", + "er c", + "orph ism", + "on es", + "o ff", + "p m", + "Ġac tion", + "Ġn atural", + "ĠG e", + "Ġy ou", + "le x", + "ĠĠĠĠ ĠĠ", + "s tit", + "Ġg as", + "Ġm ake", + "Ġin duced", + "ĠA fter", + "ĠW h", + "Ġcompon ent", + "Ġinf ection", + "ĠĠĠĠĠĠĠĠ ĠĠĠĠĠĠĠ", + "Ġconf ir", + "ig en", + "ĠS ystem", + "tic le", + "Ġprovid ed", + "tern al", + "b ers", + "O D", + "ĠIn ter", + "ot t", + "av es", + "ĠSt ud", + "p y", + "Ġres istance", + "ĠS ur", + "at ch", + "Ġd im", + "Ġinter p", + "Ġcy cl", + "on t", + "it ing", + "A G", + "Ġequ ival", + "ot ype", + "Ġprevious ly", + "Ġaddition al", + "out h", + "Ġim pl", + "Ġ ion", + "Ġ ir", + "Ġc op", + "Ġh al", + "Ġactiv ation", + "l angle", + "Ġf ull", + "S S", + "ĠO p", + "id d", + "Ġpro of", + "Ġproblem s", + "Ġtrans form", + "Ġinter actions", + "Ġsup p", + "d es", + "ĠR eg", + "operator name", + "eg in", + "Ġc ryst", + "Ġincre ases", + "ron ic", + "Ġad ap", + "in ant", + "Ġveloc ity", + "ĠAs s", + "iqu es", + "Ġcontinu ous", + "ĠCom p", + "ĠPro per", + "Ġpri or", + "or b", + "Ġno vel", + "Ġbl ock", + "Ġvol ume", + "Ġreg ard", + "omet ry", + "E C", + "Ġresul ting", + "ĠO r", + "Ġcar bon", + "are nt", + "Ġb inding", + "i j", + "Ġac cess", + "Ġwe ak", + "Ġun it", + "Ġ ide", + "\" \"", + "Ġc m", + "Ġcri tical", + "Ġresp ect", + "t rans", + "Ġâī ¥", + "Ġs al", + "e ad", + "Ġsim ulation", + "Ġcap ac", + "iti vity", + "Ġrec ord", + "ra k", + "Ġne ur", + "on ic", + "op le", + "Ġm g", + "Ġst reng", + "er ve", + "Ġre duc", + "Ġp ass", + "ord in", + "ex p", + "j ective", + "ens or", + "Ġpartic les", + "Ġa ir", + "Ġl ink", + "ĠÏ Ħ", + "Ġl ist", + "c in", + "ĠO ur", + "p ri", + "ve re", + "ib r", + "if orm", + "Ġexpl ain", + "Ġf em", + "Ġu til", + "S t", + "over line", + "Ġof ten", + "er y", + "op e", + "ĠU sing", + "b egin", + "Ġdifferen ti", + "per s", + "s elf", + "iz es", + "Ġconcentr ations", + "I R", + "ĠS up", + "Ġbas is", + "Ġinclud e", + "ĠB ond", + "Ġext rac", + "ĠMet hod", + "ĠD ata", + "ĠD ef", + "w n", + "Ġnetwork s", + "ign ed", + "âĢ ¢", + "Ġexp ressed", + "Ġcont rast", + "es is", + "c ol", + "in ter", + "p id", + "Ġd ri", + "Ġdef ine", + "Ġinflu ence", + "Ġselec ted", + "E L", + "Ġcontain ing", + "Ġs il", + "geb ra", + "re at", + "b olds", + "Ġinvestig ated", + "ĠC ol", + "ym met", + "yt es", + "Ġmo lec", + "Ġinvol ved", + "Ġd ay", + "Ġch ain", + "ĠMore over", + "Ġdi ag", + "Ġan g", + "Ġlik ely", + "Ġspect rum", + "Ġder iv", + "bolds ymbol", + "Ġhel p", + "ĠA m", + "Ġtre ated", + "Ġvari able", + "ell ular", + "ĠD es", + "ap s", + "Ġn m", + "ĠÏ ģ", + "ĠW hen", + "Ġhigh ly", + "am in", + "Ġwh at", + "rel ated", + "Ġch rom", + "Ġsur v", + "ĠAn alysis", + "Ġs it", + "f act", + "od ing", + "Ġproduc t", + "Ġev ents", + "r as", + "ĠP er", + "ma x", + "ĠA g", + "con t", + "ic ro", + "Ġad v", + "Ġcall ed", + "Ġdeg ree", + "A B", + "T R", + "Ġse g", + "ĠC an", + "Ġdemonstr ated", + "w ise", + "Ġ ve", + "ĠC a", + "Ġdet ected", + "c o", + "Ġder ived", + "Ġex hib", + "Ġgl obal", + "al ax", + "ul ating", + "A l", + "ang u", + "b o", + "Ġrec om", + "Ġfe ature", + "d ependent", + "Ġro t", + "ven tion", + "Ġrem ov", + "Ġw ind", + "Ġaccur acy", + "s ize", + "Ġsum m", + "Ġmeasure ment", + "Ġfield s", + "ward s", + "Ġl iter", + "atal y", + "ĠSt r", + "Ġre port", + "Ġcent ral", + "Ġs qu", + "Ġthe rapy", + "he st", + "Ġfe ed", + "S MILES", + "ĠA N", + "Ġs ites", + "âĢ ²", + "our s", + "om al", + "Ġl ip", + "Ġanalyz ed", + "Ġ °", + "Ġwe e", + "t em", + "Ġan other", + "il es", + "Ġcomple te", + "Ġne xt", + "ĠO ne", + "b i", + "ri p", + "st ate", + "ĠMod el", + "Ġfind ings", + "ĠP re", + "Ġrec ent", + "asc ular", + "Ġestim ate", + "Ġmechanism s", + "ĠRes ults", + "Ġparticip ants", + "Ġen g", + "m ost", + "omet ric", + "Ġequ al", + "Ġro b", + "Ġpol ar", + "Ġgene tic", + "Ġb o", + "Ġre st", + "ĠÏ Ģ", + "Ġrel ation", + "Ġques tion", + "ep ti", + "Ġdiff ic", + "em s", + "Ġf uture", + "if y", + "Ġmod e", + "Ġmemb rane", + "Ġhe at", + "A ut", + "d ing", + "Ġox id", + "Ġconf ig", + "plic ation", + "ĠM on", + "alle l", + "id ed", + "Ġdirec tion", + "pl ed", + "Ġprovid es", + "Ġindic ate", + "Ġset s", + "Ġtechn ique", + "Ġm ac", + "Ġhyp ot", + "Ġat ten", + "Ġev ent", + "Ġst age", + "Ġn ode", + "Ġref erence", + "Ġup per", + "Ġtechn iques", + "Ġgre ater", + "Ġdirect ly", + "Ġare as", + "Ġdis s", + "h or", + "ĠP ol", + "Ġevalu ation", + "Ġpattern s", + "ĠA bstract", + "Ġvir us", + "ve y", + "P C", + "Ġw omen", + "ri ent", + "Ġplas ma", + "Ġpro duced", + "ĠÎ µ", + "Ġanalys es", + "ĠS ub", + "Ġset ting", + "Ġmom ent", + "Ġtherm al", + "Ġoptim al", + "Ġtak en", + "Ġrec ogn", + "Ġvari ation", + "ĠL emma", + "Ġs us", + "f rak", + "ĠI L", + "Ġproced ure", + "h ood", + "Ġa im", + "ar ies", + "math frak", + "Ġpl ant", + "b rid", + "e lect", + "Ġvis ual", + "ur s", + "c ence", + "Ġf ive", + "Ġspati al", + "Ġrecept or", + "Ġindic ated", + "Ġ ess", + "Ġconsist ent", + "Ġt urn", + "tic es", + "Ġex ists", + "ect ors", + "Ġen zym", + "mer ic", + "Ġno ise", + "Ġgro und", + "Ġestim ated", + "el ine", + "Ġchann el", + "ti tion", + "Ġdiscuss ed", + "om er", + "ot es", + "Ġex act", + "ĠS ec", + "Ġt ake", + "Ġknow ledge", + "Ġpro p", + "Ġinf lamm", + "Ġdo uble", + "I t", + "Ġcon text", + "ĠM ed", + "M A", + "Ġf at", + "am s", + "d ata", + "and s", + "Ġcar di", + "ĠFurther more", + "oc y", + "Ġobserv ations", + "app ing", + "ĠIn f", + "om ial", + "Ġpubl ic", + "Ġem ploy", + "Ġre ason", + "y gen", + "Ġfollow ed", + "Ġam ount", + "Ġc ertain", + "wh ich", + "ot yp", + "ĠC ell", + "Ġch all", + "Ġpartic le", + "am bda", + "Ġ ens", + "Ġpe ople", + "a ult", + "ĠU nd", + "ĠB e", + "um in", + "roscop y", + "M R", + "l ation", + "Ġrep e", + "Ġab le", + "ĠS o", + "ĠâĪ ŀ", + "Ġen ti", + "Ġmo ve", + "Ġt rac", + "C O", + "Ġhe ter", + "Ġsp eed", + "Ġeffici ency", + "Ġop tical", + "Ġcomb ination", + "en ess", + "Ġc hem", + "L E", + "app a", + "Ġdecre ase", + "Î ¼", + "p ed", + "n ote", + "ĠM ulti", + "Ġal tern", + "Ġassum e", + "ĠF orm", + "str ic", + "qu e", + "Ġis s", + "ur rent", + "Ġpr inc", + "Ġt ask", + "op s", + "Ġwhere as", + "C H", + "Ġreveal ed", + "Ġcan not", + "ac tive", + "en z", + "Ġf ore", + "Ġoper ator", + "Ġcol um", + "at in", + "Ġorig inal", + "Ġsmall er", + "Ġmaterial s", + "h ydro", + "Ġcur ve", + "Ġselec tion", + "ak es", + "Ġex pos", + "at s", + "ĠÏ ī", + "Ġp ack", + "Ġst ability", + "Ġover all", + "Ġm orph", + "Ġmet ric", + "Ġo l", + "Ġb ar", + "ĠI N", + "I M", + "c y", + "et hyl", + "S P", + "Ġrespons es", + "anc y", + "Ġl ay", + "spec ific", + "Ġv s", + "ag ed", + "Ġs ocial", + "Ġc ut", + "I P", + "Ġlim ited", + "enc ies", + "Ġprot oc", + "Ġcompos ition", + "ĠThe y", + "Ġnum bers", + "m box", + "Ġdecre ased", + "v ec", + "R O", + "Aut hors", + "Ġth ick", + "Ġco ordin", + "Ġm es", + "Ġaff ect", + "Ġcl ose", + "Ġtrans port", + "C A", + "re te", + "c ome", + "Ġcollec ted", + "ĠF rom", + "Ġcontain s", + "ch it", + "ĠD et", + "Ġflu x", + "over y", + "e u", + "a ff", + "Ġconduc ted", + "Ġcr iter", + "Ġliter ature", + "Ġmem ory", + "Ġsequ ences", + "Ġp an", + "plic it", + "Ġtr ue", + "Ġmed ium", + "Ġd am", + "i re", + "c ell", + "L et", + "ef ul", + "ĠA meric", + "Ġn odes", + "get her", + "Ġto gether", + "T P", + "Ġrat her", + "Ġaut hors", + "Ġs ch", + "Ġprocess ing", + "Ġspect ra", + "Ġevalu ated", + "al k", + "Ġred uce", + "ĠH igh", + "ĠC ons", + "Ġcy cle", + "or n", + "i ers", + "Ġpro por", + "or ies", + "r ate", + "Ġh ost", + "o oth", + "y nt", + "Ġsour ces", + "Ġindividual s", + "Ġacc ount", + "ĠAl though", + "Ġcor rec", + "Ġpl an", + "enti ally", + "Ġdist inc", + "Ġso il", + "Ġse arch", + "Ġman agement", + "Ġvers ion", + "âĢ Ķ", + "Ġf ig", + "ĠN ote", + "Ġhe ad", + "dition al", + "Ġbu ild", + "ĠG l", + "as is", + "g roup", + "Ġdis play", + "ĠUn iversity", + "oot note", + "amet er", + "min ist", + "o pl", + "ym ph", + "L ambda", + "Ġidentif y", + "ĠSt ere", + "Ġï Ģ", + "Ġpro l", + "our ce", + "ic ial", + "Ġsim ulations", + "Ġth resh", + "p oint", + "ear ch", + "ell ing", + "ĠA cc", + "Ġframe work", + "Ġstreng th", + "ĠA b", + "tic les", + "Ġc os", + "F ootnote", + "r u", + "osp ital", + "Ġst able", + "Ġmo tion", + "Ġt ested", + "Ġt ests", + "as ter", + "l dots", + "C L", + "in ite", + "Ġspec ial", + "== ==", + "Ġappro aches", + "p ing", + "Ġcons um", + "S D", + "Ġj ust", + "k appa", + "Ġth ough", + "f aces", + "Ġra pid", + "ens ive", + "Ġnecess ary", + "Ġt ub", + "Ġfor ce", + "Ġbl ack", + "v olution", + "ĠAt om", + "ĠH ere", + "it ude", + "ens ions", + "ff er", + "r ich", + "Ġgiv es", + "Ġsh ape", + "Ġh ard", + "om p", + "Ġrepresent ation", + "l ing", + "ĠD ec", + "Ġnumer ical", + "Ġpl ace", + "Ġlead ing", + "Ġben ef", + "Ġreg ular", + "Ġclust er", + "Ġrel atively", + "Ġper cent", + "Ġaut om", + "Ġsym pt", + "ib ri", + "c hes", + "hen yl", + "c ar", + "Ġill ustr", + "por ts", + "em ic", + "Ġg ive", + "Ġcon ven", + "lec tion", + "ĠĠĠĠĠĠĠĠ ĠĠĠĠ", + "ĠA nd", + "Ġf ood", + "m ic", + "ograph ic", + "Ġc heck", + "Ġab ility", + "iqu id", + "Ġsub str", + "ĠâĪ Ĩ", + "Ġed ge", + "ĠP D", + "Ġclass ification", + "Ġsurv ival", + "ĠC al", + "er ate", + "Ġus eful", + "Ġcar ried", + "Ġint ensity", + "H E", + "oc enter", + "Ġpath way", + "Ġdef inition", + "Ġschem e", + "Ġsub sequ", + "ĠF irst", + "Ġcon sequ", + "ĠD iff", + "Ġinhib it", + "Ġam plit", + "as er", + "ĠN etwork", + "n ormal", + "ĠS T", + "Ġsol id", + "per im", + "com es", + "Ġcy t", + "od ies", + "I F", + "ra di", + "Ġm or", + "Ġc ore", + "B S", + "**** ****", + "Ġsoft ware", + "ĠG u", + "i red", + "id ent", + "Ġdiffic ult", + "us e", + "Ġadd ed", + "le y", + "Ġcaus ed", + "g ence", + "Ġb ase", + "## ##", + "ogen ic", + "f rom", + "Ġstat us", + "Ġassoci ation", + "ĠStere ocenter", + "Ġg alax", + "N O", + "angu age", + "Ġd imension", + "ogen esis", + "Ġem ission", + "Ġde ath", + "ul in", + "Ġag re", + "t urb", + "n abl", + "por al", + "Ġp or", + "Ġcomb ined", + "Ġalgorithm s", + "C s", + "Ġsens itivity", + "Ġallow s", + "Ġcapac ity", + "vers ion", + "Ġre stric", + "rom e", + "Ġexpos ure", + "h y", + "ann ing", + "Ġob ject", + "Ġc ode", + "f l", + "ro duction", + "res ents", + "r up", + "Ġte xt", + "ĠM at", + "Ġlead s", + "Ġres on", + "Ġproduc ts", + "Ġwh ole", + "Ġmat ter", + "P hi", + "op t", + "enc ing", + "ffici ents", + "n a", + "pec ially", + "Ġh aving", + "rop y", + "Ġunc ertain", + "en ari", + "r ical", + "Ġmin im", + "Ġorig in", + "u per", + "ĠN on", + "Ġevalu ate", + "Pro of", + "c ap", + "Ġsignal ing", + "Ġpolym er", + "tic ally", + "it ten", + "an tit", + "Ġus er", + "le vel", + "Ġmeas ures", + "Ġdynam ic", + "Ġmon ths", + "o ti", + "ra nd", + "Ġun til", + "Ġden ote", + "Ġnot e", + "Ġmain tain", + "Ġk in", + "sc ill", + "Ġim aging", + "Ġp ain", + "av y", + "Ġm it", + "ot he", + "Ġreg ul", + "kn own", + "Ġpl ot", + "nabl a", + "Ġf raction", + "w er", + "Ġstrateg y", + "Ġgre at", + "Ġdatas et", + "Ġun ique", + "C M", + "Ġt w", + "h an", + "ĠE u", + "and id", + "Ġback ground", + "Ġbro ad", + "il t", + "Ġimpro ved", + "Ġdiagn osis", + "i ous", + "Ġd ig", + "re m", + "er a", + "Ġex cl", + "Ġmet al", + "Ġs ix", + "Ġmin imum", + "us ions", + "e e", + "Ġcompound s", + "Ġas p", + "Ġe th", + "Ġdet ect", + "f erence", + "ĠÎ ·", + "Ġst atistical", + "ati ves", + "r is", + "Ġthe orem", + "ĠO F", + "w w", + "ar ily", + "ce ption", + "iv ing", + "Ġtest ing", + "Ġdiagn os", + "Ġrep resents", + "S igma", + "on ical", + "Ġequival ent", + "Ġbi om", + "Ġsub st", + "rain ts", + "ĠR ef", + "Ġsc ore", + "Ġd oc", + "Ġimpl ies", + "et er", + "Ġsynt hesis", + "il ibri", + "atter ing", + "C S", + "al se", + "Ġneu ro", + "Ġal though", + "ir us", + "met hyl", + "Ġtranscri ption", + "Ï Ģ", + "ĠMo lecular", + "Ġc ause", + "m ut", + "ĠI d", + "Î »", + "ad d", + "Ġpl ac", + "Ġag g", + "t ure", + "Ġl ack", + "Ġpredic tion", + "ra w", + "A n", + "Ġ ult", + "yn omial", + "Ġimmun e", + "il i", + "Ġpre p", + "Î ³", + "cl ass", + "Ġm ach", + "am ple", + "Ġres olution", + "Ġcou pling", + "se ud", + "Ġindic ates", + "Ġgener ation", + "Ġh ar", + "Ġf und", + "s cale", + "Ġe igen", + "ĠR el", + "ab or", + "ĠC H", + "e xt", + "am m", + "Ġcor rect", + "Ġsc reen", + "Ġstruct ural", + "Ġp H", + "Ġrele vant", + "Ġan gle", + "I G", + "Ġal gebra", + "hel ial", + "Ġw orld", + "Ġcur ves", + "ĠInt roduction", + "Ġth ird", + "Ġintro duced", + "B ig", + "n o", + "aus s", + "sub set", + "Ġtrans mission", + "Ġprof ile", + "ĠÎ ½", + "Ġes pecially", + "Ġatt rib", + "uc tion", + "Ġcoe fficients", + "Ġremain s", + "Ġne igh", + "os en", + "Ġrel i", + "Ġhig hest", + "Ġun iform", + "Ġf ar", + "chit ect", + "| |", + "Ġappro pri", + "ple x", + "ĠM ass", + "ogene ous", + "al es", + "Ġref er", + "Ġneed ed", + "Ġdifferen tial", + "ce ed", + "$ $", + "ynam ic", + "Ġse x", + "Ġspect ral", + "ch ar", + "P E", + "T S", + "Ġapproxim ately", + "val ue", + "Ġhal f", + "end ing", + "Ġgra di", + "Ġcoe fficient", + "ĠPh ys", + "Ġcon cer", + "Ġlab el", + "ir al", + "Ġchar ge", + "Ġox ygen", + "Ġde vi", + "Ġinter nal", + "Ġexp ans", + "lo ad", + "ĠS m", + "ran g", + "C on", + "ĠN a", + "Ġk e", + "Ġdi ab", + "ac hed", + "Ġloc ation", + "Ġvol t", + "ĠD isc", + "-- -", + "oc ytes", + "ore tical", + "Ġg ain", + "Ġmed i", + "ym pt", + "ot ed", + "ĠV al", + "Ġcommun ity", + "plement ary", + "Ġt ree", + "ĠT wo", + "Ġwh ose", + "Ġd one", + "am ine", + "Ġbi ological", + "in ks", + "Ġal most", + "Ġsl ight", + "Ġre pro", + "ģ Ħ", + "Ġthe rap", + "oc ation", + "Ġg ly", + "ĠE qu", + "Ġcol or", + "Ġn am", + "s ection", + "ĠE m", + "read y", + "H z", + "P D", + "f unction", + "ch ange", + "Ġprinc ip", + "Ġbec ome", + "ĠâĢ ĺ", + "Ġco ur", + "Ġloc ated", + "Ġr ang", + "in ity", + "Ġinter val", + "g in", + "Ġinvestig ate", + "f ree", + "Ġv itro", + "Ġsub set", + "Ġm ov", + "Ġpro ve", + "Ġl iver", + "ate gor", + "et es", + "Ġl ymph", + "d om", + "ĠE lect", + "Ġser um", + "Ġsc enari", + "end s", + "ĠF inally", + "Ġfil ter", + "I L", + "Ġab und", + "ment ation", + "im als", + "n um", + "enc ed", + "Ġproper ty", + "mat rix", + "ĠCom par", + "Ġl and", + "ĠCh ar", + "ress ive", + "ul us", + "Ġb one", + "E x", + "Ġradi ation", + "Ġsugg ested", + "ĠCom put", + "Ġthresh old", + "ĠA D", + "Ġh or", + "Ġin duc", + "Ġapproxim ation", + "Ġad minist", + "Ġor d", + "Ġl ung", + "Ġrece ived", + "Ġn orm", + "Ġestim ates", + "Ġl aw", + "Ġout comes", + "ĠP r", + "Ġdep th", + "Ġel se", + "Ġcontrib ution", + "he tic", + "Ġcons erv", + "Ġup on", + "Ġde ep", + "M D", + "Ġm el", + "Ġfil m", + "ilibri um", + "Ġo scill", + "ol ved", + "Ġbre ast", + "C P", + "ĠD ist", + "ric es", + "in ated", + "Ġoptim ization", + "Ġpredic ted", + "s f", + "d im", + "ĠS N", + "Ġav oid", + "Ġne ural", + "Ġw a", + "rop e", + "Ġdistrib utions", + "ox id", + "Ġsm ooth", + "p ath", + "Ġflu id", + "Ġs af", + "Ġcho ice", + "A A", + "Ġmolec ules", + "U S", + "Ġal ways", + "iv o", + "Ġreg ression", + "Ġsuccess ful", + "Ġw all", + "oun g", + "Ġactiv ities", + "Ġdepend ence", + "Ġrequi res", + "Ġpl ane", + "Ġdesign ed", + "P I", + "d own", + "Ġpop ulations", + "c or", + "medi ate", + "Ġd ose", + "Ġb ond", + "C o", + "ĠM an", + "Ġdiag ram", + "g s", + "Ġto ol", + "Ġisol ated", + "Ġvers us", + "ne y", + "Ġem erg", + "ĠA ut", + "a im", + "f ield", + "Ġexam ined", + "Ġs at", + "S M", + "ĠSp ec", + "Ġpar allel", + "is ation", + "Ġdistinc t", + "Ġpredic t", + "Ġf er", + "Ġunderstand ing", + "ĠSim ilar", + "ĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ ĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ", + "ud es", + "Ġo rient", + "h ic", + "u z", + "Ġmod ified", + "ĠâĪ ¼", + "F F", + "The re", + "Ġtri al", + "x y", + "ger y", + "Ġal ready", + "def ine", + "m ing", + "ĠS D", + "Ġmon itor", + "Ġp sy", + "Ġbec omes", + "ist ry", + "ĠÎ ĵ", + "Ġh um", + "ri er", + "ess ion", + "Ġhist ory", + "à ¶", + "ĠÎ ¾", + "Ġestabl ished", + "Ġachie ved", + "es tern", + "Ï Ĩ", + "ĠH ence", + "Ġassess ment", + "ot or", + "Ġdescrib e", + "och ond", + "yl ation", + "st s", + "sp ace", + "Ġdise ases", + "j ection", + "Ġs low", + "Ġnon linear", + "p ly", + "m l", + "Ġemb ed", + "com p", + "Ġeffici ent", + "Ġoper ation", + "Ġcont act", + "o z", + "Ġinv ari", + "Ġcent er", + "Ġcon c", + "wide tilde", + "Ġbe am", + "Ġclos ed", + "ĠMethod s", + "Ġch ronic", + "al ing", + "Ġse vere", + "Ġform s", + "il it", + "s ide", + "p en", + "Ġb ran", + "o ud", + "tal ity", + "Ġmap s", + "ac ts", + "O L", + "P R", + "Ġ Í", + "s l", + "Ġinst ance", + "ul ly", + "Ġestim ation", + "Ġpl ate", + "Ġdev ice", + "ĠI II", + "s in", + "Ġpl ants", + "it tle", + "Ġpro duce", + "Ġhe nce", + "Ġn ature", + "Ġrele ase", + "ĠM in", + "ric t", + "Ġconn ected", + "ott om", + "ell ar", + "Ġform ed", + "Ġm ob", + "Ġcomput ed", + "Ġ RE", + "Ġpol ynomial", + "Ġl iquid", + "g n", + "Ġass ay", + "Ġman if", + "ĠS i", + "re nce", + "Ġax is", + "V ID", + "Ġsign als", + "Î ¸", + "to k", + "d s", + "Ġrat s", + "Ġt or", + "o lecular", + "c hed", + "Ġdesc ri", + "Ġexp on", + "Ġper turb", + "Ġgl uc", + "Ġcolum n", + "U L", + "Ġmain ly", + "Ġm ul", + "id er", + "ĠC R", + "Ġc ataly", + "Ġl aser", + "tion ed", + "d en", + "Ġsugg ests", + "f ig", + "Ġprop ag", + "or g", + "re p", + "Ġcharacter ized", + "olog ies", + "Ġacc um", + "Ġv ary", + "Ġcontroll ed", + "Ġup d", + "ĠB r", + "Ġenti re", + "Ġ @", + "â ģĦ", + "Ġ Ì", + "Ġdat ab", + "an o", + "am il", + "Ġadj ust", + "y e", + "p ression", + "eren ces", + "Ġess ential", + "ĠH ydro", + "ĠT r", + "Ġappropri ate", + "Ġform ula", + "Ġlat tice", + "Ġac ute", + "Ġus ually", + "it able", + "Ġm ar", + "Ġμ m", + "ĠU SA", + "Ġinc ub", + "oc ks", + "Ġp epti", + "idd le", + "Ġdec om", + "Ġdam age", + "Ġgen ome", + "Ġm ouse", + "c irc", + "Ġlay ers", + "Ġt rack", + "Ġto x", + "Ġre plac", + "Ġad vant", + "iz on", + "Ġrecord ed", + "Ġst art", + "Ġr ank", + "s er", + "ĠG ene", + "auss ian", + "ing u", + "Ġconst raints", + "f low", + "Ġm ig", + "P L", + "Ġinc or", + "ap pro", + "Ġf ast", + "Ġmus cle", + "Ġh ome", + "e q", + "ĠÏ Ī", + "Ġstrong ly", + "ĠEu rope", + "Ġsub jects", + "Ġob jects", + "t est", + "t ered", + "ĠWh ile", + "Ġsymmet ry", + "Ġquanti f", + "` `", + "Ġbre ak", + "ĠEx perim", + "Ġmi xt", + "< <", + "ĠCh ina", + "ĠId entif", + "Ġaff ected", + "Ġsecond ary", + "Ġin equ", + "in cl", + "E G", + "F T", + "Ġfail ure", + "ec tiv", + "Ġk m", + "Ġsam pling", + "Ġexpans ion", + "Ġprac tice", + "u ations", + "ogn itive", + "Ġdi et", + "Ġtemper atures", + "Ġcontrol s", + "Ġch osen", + "Ġgener ally", + "anc er", + "Ġdeg rad", + "ul i", + "s m", + "othe rapy", + "Ġto wards", + "ĠProper ties", + "Ġclust ers", + "Ġdel ay", + "Ġhe p", + "P A", + "ĠStud y", + "antit ative", + "Ġclass ical", + "ĠZ h", + "ĠÎ ©", + "ĠB o", + "Ġse ed", + "ĠStr uct", + "Ġtre nd", + "i ological", + "Ġconfir med", + "Ġdistrib uted", + "b ial", + "Ġn ame", + "C N", + "val ence", + "er ior", + "iv en", + "n ed", + "Ġbehavi our", + "as ks", + "g ra", + "m ark", + "Ġerr ors", + "ĠR ep", + "l ight", + "cri pt", + "I f", + "Ġc andid", + "Ġdepend s", + "ĠN ational", + "Ġh olds", + "Ġprotoc ol", + "ĠUn ited", + "Ġinter face", + "Ġexp ect", + "Ġï ģ", + "ĠH IV", + "Ġro ot", + "Ġsc attering", + "w ords", + "Ġobserv ation", + "ot op", + "Ġoccur s", + "our ces", + "p ite", + "ĠS te", + "Ġor th", + "Ġst ain", + "Ġst eps", + "Ġcomp are", + "Ġbas ic", + "Ġinhib ition", + "Ġsympt oms", + "ĠHe alth", + "Ġpubl ished", + "f old", + "Ġt un", + "Ġv ivo", + "Ġrec onstr", + "Ġm RNA", + "ic y", + "Ġhy brid", + "y r", + "Ġm ixed", + "v is", + "Ch I", + "Ġmed ical", + "Ġf rag", + "Ġan imals", + "Ġimport ance", + "Ġeng ine", + "ĠC T", + "Ġpair s", + "Ġb al", + "ĠE ar", + "her s", + "Ġsy nd", + "Ġar chitect", + "Ġidentif ication", + "Ġstrateg ies", + "Ġreg ulation", + "ĠL a", + "r or", + "Ġflu ores", + "ur ity", + "Ġcon cept", + "Ġatten tion", + "Ġtrans formation", + "uc le", + "ĠRes earch", + "Ġsim pl", + "Ġcult ure", + "ar ing", + "if ically", + "p ir", + "z e", + "P T", + "m osp", + "Ġsw it", + "Ġn or", + "Ġenh ance", + "Ġenvironment al", + "r ary", + "ĠM icro", + "Ġw ide", + "op ath", + "au ge", + "z eta", + "Ġst e", + "ĠE l", + "Ġw ords", + "Ġnuc lear", + "Ġl anguage", + "Ġdetail s", + "op ar", + "ĠR ed", + "w ater", + "Ġc ategor", + "Ġf ile", + "Ġco ver", + "Ġachie ve", + "à ¡", + "um m", + "Ġl ig", + "Ġsur vey", + "Ġext ended", + "l ab", + "ĠIn c", + "Ġdis pers", + "Ġrecom m", + "ĠB ased", + "Ġabs ence", + "Ġconstruc tion", + "Ġpo or", + "Ġvolt age", + "Ġcell ular", + "Ġmor tality", + "Ġshow ing", + "Ġprol if", + "m p", + "Ġneur ons", + "Ġsup ported", + "Ġpre vent", + "el i", + "ox y", + "ic a", + "Ġf ully", + "Ġen ough", + "o times", + "ĠM R", + "Ġb ul", + "Ġphen omen", + "F A", + "Ġdec ision", + "Ġd ual", + "Ġdec ay", + "Ġo wn", + "Ġus es", + "Ġchall eng", + "Ġadd ress", + "O C", + "tiv ation", + "Ġm ill", + "Ġmod es", + "at us", + "ic tion", + "Ġabs orption", + "Ġep it", + "Ġconst ra", + "Ġagre ement", + "ĠA f", + "Ġbi as", + "ud ed", + "Ġpar ts", + "Ġv an", + "Ġcol on", + "Ġex ternal", + "Ġthe oretical", + "as i", + "Ġl es", + "abil ities", + "L A", + "tt ps", + "Ġinst ead", + "Ġmemb ers", + "+ +", + "Ġrec ently", + "Ġprep ared", + "Ġar ticle", + "d ay", + "Ġext ract", + "Ġâ İ", + "Ġpath ways", + "Ï Ħ", + "m id", + "or age", + "Ġcommun ication", + "Ġacc el", + "Ġun its", + "iti s", + "ynt hesis", + "Ġamplit ude", + "ri e", + "ult aneous", + "ĠL ear", + "ec ause", + "d o", + "e ff", + "Ġex plicit", + "Ġcriter ia", + "b re", + "Ġex ec", + "Ġmechan ical", + "er os", + "ĠCon cl", + "ĠE xt", + "Ġclass es", + "Ġlong er", + "Ġcalc ulations", + "eu tic", + "oci ated", + "ar di", + "Ġcour se", + "Ġpar tial", + "Ġsens or", + "Ï ĥ", + "Ġoper ators", + "ĠAmeric an", + "Ġm M", + "Ġv acc", + "oc c", + "ic on", + "Ġout come", + "Ġanal og", + "Ġthick ness", + "Ġre ach", + "Ġassum ed", + "end er", + "Ġm ale", + "S E", + "Ġint ra", + "Ġimplement ation", + "em ia", + "Ġenh anced", + "b ility", + "Ġeas ily", + "um p", + "Ġcar cin", + "os a", + "Ġcorrespond s", + "ne g", + "Ġmagn itude", + "con st", + "Ġl atter", + "Ġrepresent ed", + "Ġs ed", + "Ġparticular ly", + "Ġwr itten", + "par t", + "Ġo il", + "ber g", + "ĠB ar", + "Ġd ys", + "ĠS ome", + "ĠM ar", + "Ġaltern ative", + "ĠG erm", + "Ġgener ate", + "Ġcon struct", + "ian s", + "st ream", + "Ġe c", + "oc hemical", + "ib ration", + "oper ative", + "is ter", + "Ġrob ust", + "t re", + "Ġmodel ing", + "or ing", + "es e", + "d ed", + "ide o", + "Ġhydro gen", + "um ents", + "Ġdemonstr ate", + "Ġcorrel ated", + "Ġsystem atic", + "Ġsur gery", + "Ġindic ating", + "Ġhypot hesis", + "y ear", + "mit ted", + "Ġst ars", + "Ġprof iles", + "Ġcons ists", + "t ri", + "Ġdepend ent", + "ish ing", + "t op", + "Ġhe art", + "at ically", + "Ġinj ury", + "Ġqu ad", + "Ġwee ks", + "ut ing", + "ĠT e", + "Ġid enti", + "Ġgradi ent", + "Ġcalc ulation", + "Ġ ur", + "R T", + "z ation", + "Ġed uc", + "en ing", + "P P", + "z ed", + "us h", + "Ġcharacter istic", + "Ġstrain s", + "et h", + "Ġdi vers", + "âĪ Ī", + "oid s", + "ol ic", + "Ġinterp ret", + "K ey", + "Ġatt ack", + "p ective", + "Ġlab or", + "Ġmet ast", + "N F", + "Ġtiss ues", + "Ġradi us", + "ĠE ach", + "Ġc at", + "Ġd on", + "Ġele v", + "Ġass emb", + "r ons", + "Ġar bit", + "Ġpan el", + "Ġg rid", + "Ġt able", + "roscop ic", + "Ġc le", + "ĠIn tern", + "ob acter", + "Ġassum ption", + "ĠCO VID", + "Ġbound ed", + "Ġother s", + "Ġsch ool", + "Ġh ospital", + "lec ted", + "ĠC u", + "à Ĺ", + "Ġcomple t", + "Ġwid th", + "Ġl inks", + "p o", + "ol low", + "Ġn ut", + "Ġappear s", + "row n", + "a ro", + "Ġus ers", + "Ġcl im", + "Ġslight ly", + "Ġbl ue", + "ra b", + "ĠS er", + "Ġfig ure", + "ĠR ad", + "Ġelect ric", + "m m", + "och astic", + "ri ef", + "Ġcollec tion", + "Ġst em", + "Ġg over", + "Ġb ur", + "Ġtyp ical", + "s up", + "Ġagg reg", + "ra z", + "ĉĉ ĉ", + "Ġst ation", + "Ġar ter", + "i vely", + "itro gen", + "Ġcons tit", + "em pt", + "ĠEff ect", + "Ġdescri ption", + "Ġsc ores", + "Ġmet hyl", + "ĠO b", + "ĠSt ates", + "Ġs plit", + "ĠV ari", + "ĠW ang", + "Ġc ere", + "ĠF ran", + "Ġneed s", + "ĠF our", + "Ġpro ject", + "Ġdev ices", + "Ġintegr al", + "ĠE s", + "ymmet ric", + "Ġm ess", + "Ġpl ays", + "ĠLear ning", + "Ġover l", + "H ere", + "ign ment", + "Ġdel iver", + "ap an", + "C E", + "Ġg auge", + "ĠJ oh", + "-------- --------", + "Ġunder lying", + "Ġth in", + "Ġassess ed", + "Ġdiff usion", + "Ġhe ight", + "ĠS w", + "Ġd ark", + "pr int", + "ran ge", + "ĠC I", + "is es", + "l ier", + "r ant", + "om orphism", + "Ġcomp act", + "ip s", + "ĠN ame", + "Ġtechn ology", + "ag en", + "Ġconfig uration", + "Ġd uration", + "ĠCl ass", + "Ġp ut", + "Ġm aking", + "Ġas ympt", + "a id", + "Ġco h", + "Ġcomplex ity", + "Ġsec tions", + "ĠM D", + "ĠĠĠĠĠĠĠĠ Ġ", + "Ġra d", + "Ġsubstr ate", + "d d", + "Ġan n", + "Ġorgan ic", + "Ġtak ing", + "Ġinclud es", + "Ġk ine", + "a res", + "Ġro w", + "ateg ory", + "Ġmit ochond", + "U T", + "Ġsynd rome", + "ĠPro b", + "re tion", + "Ġfl uct", + "ĠD is", + "Ġtrans l", + "pl as", + "Ġpsy ch", + "Ġsur faces", + "Ġdetail ed", + "amil ton", + "Ġh old", + "ĠâĬ Ĺ", + "ĠC N", + "Ġd il", + "ĠO ver", + "at form", + "Ġver tical", + "Ġcomput ation", + "Ġp ure", + "Ġm akes", + "Ġexist ing", + "Ġexam ples", + "S O", + "ord ers", + "Ġm ix", + "Ġincor por", + "Ġre qu", + "an tic", + "D NA", + "Î ´", + "Ġcl oud", + "ĠT echn", + "Ġï ĥ", + "em ents", + "Ġbas eline", + "ste in", + "Ġbel ong", + "Ġtri als", + "Ġhor izon", + "Ġphosph or", + "Ġan s", + "di x", + "ro id", + "Ġapp ly", + "u ed", + "ern el", + "Ġfem ale", + "ic acy", + "Ġv ectors", + "Ġmat rices", + "at ric", + "ĠM c", + "Ġp y", + "Ġch lor", + "l en", + "Ġclear ly", + "st atic", + "re f", + "ĠS outh", + "Ġmed ia", + "ĠS he", + "ĠB ay", + "Ġag ents", + "B y", + "Ġdifferenti ation", + "ist ant", + "orph ic", + "Ġvari ety", + "Ġserv ice", + "Ġm apping", + "vel ength", + "Ġchann els", + "Ġcomp ute", + "Ġst ream", + "ul s", + "am ide", + "ok ing", + "v it", + "Ġyield s", + "om b", + "ĠG aussian", + "Ġp en", + "un e", + "Ġexper ience", + "b and", + "ĠD o", + "math sf", + "Ġallow ed", + "A r", + "R A", + "Ġbacter ial", + "Ġm iss", + "Ġbacter ia", + "Ġmoment um", + "Ġh ours", + "uc k", + "ĠPro position", + "ber t", + "ot rop", + "Ġvari ance", + "Ġtr ig", + "Ġsh ift", + "Ġequ ilibrium", + "b u", + "IN G", + "Ġwh ite", + "Ġk ind", + "Ġj oint", + "Ġtem poral", + "ĠI V", + "ĠAf ric", + "Ġsub ject", + "ĠP o", + "he ad", + "id el", + "Ġantib ody", + "ĠEff ects", + "Ġsp e", + "Ġsu fficient", + "j ected", + "re es", + "ĠT op", + "Ġmut ations", + "is ions", + "B C", + "Ġin duction", + "Ġinterest ing", + "ell a", + "c an", + "Ġsus p", + "ĠG roup", + "Ġextrac ted", + "istic ally", + "c oh", + "m ap", + "Ġaccur ate", + "Ġto o", + "Ġdim ensions", + "te gr", + "Ġgre en", + "ĠR o", + "Ġw ild", + "Ġlo op", + "Ġmet a", + "Ġsub stit", + "os ome", + "Ġsuggest ing", + "Ġspec im", + "am ental", + "im ent", + "Ġi j", + "Ġcl aim", + "Ġaut hor", + "Ġfil ms", + "Ġcoun ter", + "Ġconven tional", + "r in", + "otyp es", + "Ġp ast", + "S ince", + "medi ated", + "reat ment", + "Ġext ension", + "Ġbi o", + "Ġs ent", + "h al", + "Ġob jective", + "Ġar ray", + "Ġsu itable", + "ĠB ut", + "ĠH uman", + "or gan", + "b ut", + "mod el", + "S I", + "Ġhealth y", + "Ġv ac", + "Ġl ate", + "Ġr ing", + "Ġl ittle", + "M T", + "Ġsqu are", + "Ġge ometry", + "ĠT HE", + "ĠS ing", + "j ug", + "Ġstud ents", + ", ,", + "Ġad ult", + "Ġcharacter ization", + "Ġat mosp", + "Ġmonitor ing", + "an i", + "n et", + "ĠP a", + "opt osis", + "Ġcont in", + "ĠS ol", + "Ġdatab ase", + "im port", + "m ann", + "ĠPro cess", + "ĠC hen", + "Ġg ap", + "Ġenzym e", + "O T", + "Ġsim ultaneous", + "Ġexist ence", + "B P", + "ĠJ apan", + "oun ts", + "Ġt urb", + "Ġsp aces", + "ĠWe ight", + "oph il", + "Ġa st", + "Ġwr ite", + "Ġdiab etes", + "ĠC A", + "Ġneut ral", + "Ġvari ations", + "ax on", + "Ġbe gin", + "und er", + "Ġext raction", + "ĠP ati", + "Ġf ron", + "ef ined", + "Ġacid s", + "Ġserv ices", + "Ġs ense", + "Ġag ent", + "hen s", + "elect ric", + "val ues", + "Ġimprove ment", + "here nt", + "ac tic", + "Ġac et", + "cdot s", + "Ġam ino", + "Ġro om", + "Ġexp ress", + "Ġex cept", + "Ġo ld", + "pl ant", + "cep ti", + "ĠP CR", + "ĠE R", + "ĠB oth", + "ve x", + "Ġad ults", + "Ġp seud", + "Ġal le", + "Ġwork s", + "Ġconsum ption", + "ip her", + "c m", + "c ast", + "Ġnan opar", + "Ï ī", + "Ġe con", + "ynam ics", + "Ġal ter", + "Ġsk in", + "Ġdi ameter", + "G C", + "ĠS ign", + "v ial", + "Ġgluc ose", + "ĠN orth", + "ot ox", + "Ġpro te", + "d x", + "ĠC r", + "Ġf ract", + "Ġins ide", + "Ġst atic", + "w id", + "Ġst orage", + "ĠA L", + "ĠM ark", + "ĠA T", + "Ġsens itive", + "Ġad s", + "Ġed ges", + "an a", + "R e", + "Ġsumm ar", + "ĠAN D", + "Ġremain ing", + "dition ally", + "Ġm id", + "ĠThe ory", + "M C", + "Ġf lex", + "ol y", + "Ġdegrad ation", + "Ġint r", + "ot a", + "ism s", + "Ġam pl", + "ĠA re", + "Ġwork ing", + "Ġdivers ity", + "Ġt ensor", + "Ġb inary", + "\"\" \"", + "v als", + "Ġhe m", + "M L", + "Ġμ g", + "ne q", + "ens ities", + "Ġtak es", + "Ġch arg", + "Ġinter vention", + "Ġal b", + "Ġqu al", + "Ġmen tioned", + "Ġon es", + "ĠAcc ording", + "ill ed", + "O H", + "S up", + "Ġgalax ies", + "ail y", + "Ġr ule", + "Ġc ognitive", + "her n", + "Ġrecogn ition", + "Ġbu ffer", + "Ġm arg", + "ĠN i", + "ĠâĪ ļ", + "Ġcl in", + "Ġintegr ation", + "Ġs in", + "ĠAl so", + "Ġmach ine", + "w r", + "id ity", + "Ġsubsequ ent", + "F e", + "Ġn ames", + "at her", + "ĠC y", + "Ġmetabol ism", + "Ġre actions", + "Ġit er", + "Ġnot ed", + "Ġcaus es", + "ĠH amilton", + "g o", + "Ġra re", + "V A", + "ĠM y", + "v ol", + "as ure", + "Ġsignific ance", + "ĠN one", + "Ġve hic", + "S R", + "Ġvari ability", + "ĠDe velop", + "are n", + "Ġprom ot", + "ard s", + "Ġcomput ational", + "Ġsh all", + "iz ations", + "ĠHydro gen", + "Ġprolif eration", + "Ġcou pled", + "ch ron", + "Ġconver gence", + "Ġg ast", + "Ġcalc ulate", + "ra ft", + "par ation", + "her ic", + "ĠP C", + "pl ate", + "p tions", + "ĠAl gorithm", + "Ġresul ted", + "D E", + "Ġinvestig ation", + "ĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ ĠĠĠ", + "ol ation", + "Ġt asks", + "Ġle g", + "in ess", + "Ġemploy ed", + "O n", + "Ġexper i", + "Ġtra ject", + "G A", + "Ġpur pose", + "ĠN um", + "Ġcomplet ely", + "th at", + "ĠOp tim", + "Ġform al", + "ec k", + "ĠPro tein", + "Ġgo al", + "Ġthrough out", + "Ġconsider ing", + "Ġref lect", + "tre ated", + "or ation", + "rib ution", + "Ġtherap eutic", + "Ġfind ing", + "U N", + "T hen", + "il ities", + "Ġun known", + "ove red", + "Ġver tex", + "Ġex change", + "Ġdrug s", + "ĠC P", + "Ġin str", + "Ġsymmet ric", + "ĠD ep", + "Ġconstruc ted", + "Ġpre valence", + "Ġdecre ases", + "Ġmi R", + "Ġy et", + "Ġb ox", + "g raph", + "wide hat", + "al ian", + "u fact", + "L R", + "cri ption", + "Ġn p", + "ĠChar acter", + "Ġep id", + "Î ½", + "Ġst ages", + "Ġs ay", + "ĠD uring", + "at ur", + "i entif", + "ab ric", + "à ¼", + "am ent", + "in ations", + "Ġsol ar", + "Ġdisc rete", + "ĠE r", + "ĠGen eral", + "b al", + "ĠC ent", + "u el", + "Ġmixt ure", + "Ġwid ely", + "ĠSec ond", + "Ġres ources", + "ĠAp pro", + "ĠI R", + "Ġstr ing", + "op ro", + "Ġin ner", + "ĠCom plex", + "O P", + "Ġat oms", + "Ġph ases", + "Ġdomain s", + "ad a", + "Ġcount ries", + "ac et", + "oci ation", + "iz er", + "Ġits elf", + "Ġmin imal", + "ĠCont rol", + "tt p", + "Ġb ottom", + "b all", + "ĠM ay", + "de v", + "n ow", + "em ber", + "Ġpercent age", + "ĠO ther", + "om as", + "Ġl ed", + "R es", + "ĠEn g", + "k g", + "Ġfrequ encies", + "k in", + "Ġinc idence", + "Ġan imal", + "Ġad op", + "Ġidenti ty", + "ĠR T", + "Ġy oung", + "ist ent", + "we ight", + "g u", + "Ġse ason", + "Ġexplain ed", + "ĠUnd er", + "io tic", + "w ell", + "Ġmetabol ic", + "g ical", + " ±", + "The orem", + "ad es", + "plic ated", + "Ġcontain ed", + "Ġs ulf", + "Ġco ol", + "Ġpers on", + "Ï ģ", + "Ġp ix", + "ĠS al", + "l ink", + "in i", + "t ual", + "S H", + "g ed", + "k y", + "as ts", + "erc ise", + "ĠH ar", + "Ġrel ax", + "equ iv", + "Ġy our", + "Ġund erg", + "Ġrec overy", + "Ġcom m", + "Ġden otes", + "form ed", + "ari a", + "e tic", + "Ġtum ors", + "ĠH y", + "Ġmark ers", + "Ġplac ed", + "ol ute", + "Ġw aves", + "Ġuncertain ty", + "Ġcontrib ute", + "ĠH ist", + "Ġa ver", + "Ġf av", + "Ġp ow", + "ĠSe e", + "Ġte am", + "Ġscal es", + "ientif ic", + "ier arch", + "Ġear lier", + "Ġsatisf ies", + "Ġcryst al", + "Ġpre gn", + "Ġobs erve", + "Ġon line", + "Ġcontrib utions", + "og ram", + "ĠM a", + "Ġf rac", + "Ġsp read", + "Ġon ce", + "d et", + "Ġresp ond", + "Ġpl atform", + "Ġinflamm atory", + "u tive", + "ĠS umm", + "pl ace", + "Ġ ions", + "Ġwind ow", + "ax is", + "est inal", + "Ġdepend ing", + "Ġsepar ation", + "Ġfor ward", + "ĠT i", + "Ġgl ass", + "Ġac cept", + "Ġfeed back", + "Ġon to", + "M E", + "mer c", + "unc tional", + "Ġap optosis", + "ĠProper ty", + "Ġintegr ated", + "Ġor b", + "Ġdevi ation", + "Ġantib odies", + "Ġremov ed", + "Ġlip id", + "arm ac", + "Ġarbit rary", + "ag ger", + "Ġemb ry", + "Ġg rain", + "Ġd rop", + "Ġstar ting", + "Ġrelationship s", + "ĠÏ ĩ", + "S F", + "Ġsim ply", + "Ġfac ilit", + "Ġz one", + "il s", + "Ps i", + "Ġinequ ality", + "Key words", + "Ġto ler", + "ed ge", + "Ġeas y", + "Ġal pha", + "Ġper f", + "wid th", + "in it", + "Ġimplement ed", + "C F", + "os ity", + "ocy te", + "Ġpropor tion", + "re st", + "ĠS uper", + "Ġpre f", + "Ġw ord", + "e v", + "Ġext ent", + "Ġinj ection", + "all ed", + "ĠAn ti", + "Ġb eta", + "ĠJ an", + "ĠG a", + "ĠZh ang", + "Ġ iron", + "Ġqu antitative", + "ro c", + "Ġf all", + "Ġregard ing", + "Ġf ix", + "Ġdatas ets", + "Ġt end", + "Ġscal ar", + "Ġresid ual", + "Ġrati os", + "ĠÎ ¦", + "k ing", + "Ġinflamm ation", + "Ġsing ular", + "ĠP ark", + "om atic", + "unc tions", + "Ġw ar", + "Í Ĵ", + "hem at", + "Ġf ace", + "ĠH u", + "Ġfund amental", + "Ġwa velength", + "el ing", + "ĠS uch", + "RNA s", + "c t", + "Ġid en", + "ce an", + "ne w", + "T ype", + "ĠForm ula", + "Ġmed ic", + "uss ion", + "Ġdist ingu", + "Ġreson ance", + "AT ION", + "ine ar", + "Ġh yd", + "l n", + "â ĨĴ", + "ĠU p", + "Ġact ual", + "Ġadap t", + "hen e", + "Ġm otor", + "l ist", + "ab it", + "I nd", + "ot al", + "Ġneigh bor", + "ĠP T", + "gen er", + "Ġposs ibility", + "erg ies", + "Ġse ems", + "ĠU S", + "Ġim m", + "Ġtyp ically", + "Ġsim ulated", + "ĠSystem s", + "ectiv eness", + "ry ing", + "Ġkin ase", + "Ġdecom position", + "ater al", + "Ġrot ation", + "pen dix", + "en n", + "at t", + "v ate", + "Ġtarget s", + "Ġsit uation", + "Ġinvol ve", + "Ġcre ated", + "hes ized", + "Ġal one", + "c i", + "Ġm L", + "Ġdiv ided", + "Ġbul k", + "o in", + "H C", + "Ġa rm", + "L O", + "ill s", + "Ġmed ian", + "h am", + "im er", + "f lu", + "Ġfib er", + "ĠS U", + "f ile", + "tiv ated", + "Ġradi o", + "ĠN ames", + "p e", + "Ġo ste", + "Ġel im", + "Ġsus cepti", + "re hens", + "Ġdiscuss ion", + "ĠS ep", + "Ġarchitect ure", + "Ġd est", + "t yp", + "r ame", + "Ġpar tition", + "Ġoccur red", + "Ġs izes", + "cl es", + "Ġsc hed", + "M olecular", + "ĠÎ º", + "Ġinv as", + "c up", + "P CR", + "ĠS MILES", + "ti ally", + "ox ide", + "ĠE d", + "Ġman ufact", + "ĠM aterial", + "Ġfl at", + "Ġmut ation", + "Ġintro duce", + "b ound", + "Ġdis orders", + "reg ulated", + "ĠM or", + "Ġf alse", + "ing er", + "ĠT R", + "Ġext rem", + "w ar", + "Ġsym bol", + "Ġan omal", + "ĠA R", + "Ġiss ues", + "Ġcoordin ates", + "Ġrecept ors", + "Ġprog ression", + "ĠF l", + "ubl ic", + "Ġelectron ic", + "Ġasp ects", + "Ġdoc ument", + "f lo", + "ĠP red", + "Ġgraph s", + "Ġtra ditional", + "D M", + "Ġsaf ety", + "ĠD r", + "ĠS equ", + "Ġcompos ite", + "ĠÎ Ľ", + "Ġrespons ible", + "Ġg ran", + "Ġinter mediate", + "od ium", + "pos ite", + "ph ase", + "d t", + "Ġwee k", + "Ġd os", + "Ġst abil", + "L C", + "ĠK ey", + "Ġver tices", + "Ġcomput er", + "ĠCan onical", + "Ġinvari ant", + "em ark", + "b enz", + "Ġ ice", + "ti le", + "z y", + "ĠO ut", + "Ġmove ment", + "Ġsh if", + "le ep", + "Ġd aily", + "Ġpos itions", + "Ġh im", + "Ġcre ate", + "O ur", + "Ġrese arc", + "Ġprog n", + "duc t", + "Ġscreen ing", + "Ġcho ose", + "pro cess", + "m al", + "Ġlabor atory", + "Ġoper ations", + "Ġto ols", + "olog ic", + "q quad", + "Ġcommon ly", + "Ġv oid", + "Ġocc up", + "ass ociated", + "Ġcorrel ations", + "Ġcarcin oma", + "l in", + "Ġv ideo", + "Ġhe avy", + "Ġlarg est", + "Ġm iddle", + "ĉĉ ĉĉ", + "ĠB as", + "as ons", + "id ing", + "Ġet c", + "ac he", + "ĠE val", + "i ra", + "rom agnetic", + "Ġco vari", + "L I", + "Ġde le", + "Ġst ra", + "am ples", + "od er", + "Ġc ategory", + "ĠIn stit", + "Ġpol icy", + "B ased", + "ib ly", + "Ġdeterm ination", + "Ġresp ir", + "otrop ic", + "Ġol der", + "ĠM al", + "Ġcy tok", + "Ġdeg rees", + "a ut", + "ill ing", + "et ing", + "Ġreduc es", + "Ġide al", + "b inding", + "ĠSp ect", + "un it", + "Ġdi ver", + "ĠW orld", + "Ġmark ed", + "al y", + "Ġcomplex es", + "ĠSumm ary", + "Ġpro pose", + "ĠA ustr", + "Ġmax im", + "Ġro und", + "Ġinhib itor", + "Ġeff icacy", + "act or", + "b ur", + "Ġtrans f", + "ĠG al", + "Ġpro ved", + "ĠDef ined", + "A t", + "Ġse lect", + "Ġnanopar ticles", + "W h", + "k en", + "ĠS P", + "en ge", + "Ġdeliver y", + "Ġdis order", + "ĠIn ChI", + "ĠCompar ison", + "if ying", + "ĠM echan", + "Ġconcl ude", + "Ġrepe ated", + "ell ow", + "Ġà Ģ", + "C I", + "ĠH z", + "an alysis", + "T r", + "à Ń", + "eli hood", + "Ġexp and", + "ĠDevelop ment", + "ĠSt ate", + "Ġt et", + "ff ic", + "Ġp arent", + "Ġscenari o", + "r s", + "ĠW hat", + "â ī", + "Ġstim ulation", + "ĠO bs", + "z ero", + "Ġman ner", + "as hed", + "ĠL og", + "Ġox ide", + "ph osph", + "Ġmig ration", + "Ġsub group", + "ros is", + "ip p", + "D R", + "d ec", + "os omal", + "Ġseg ment", + "ogen ous", + "F P", + "h and", + "ĠSur face", + "it z", + "Ġcryst all", + "th is", + "Ġbuild ing", + "t ag", + "Ġreduc ing", + "Ġun s", + "Ġrecom b", + "Ġc am", + "Ġlim its", + "oc ardi", + "& &", + "Ġsepar ate", + "Ġsup plement", + "ke le", + "Ġgra d", + "Ġiss ue", + "ĠQu antum", + "Ġcurrent ly", + "Ġqu ite", + "E P", + "Ġr ules", + "Ġwe ights", + "u ary", + "ill i", + "Ġbec ame", + "à ³", + "Ġnormal ized", + "ĠNetwork s", + "erv ed", + "Ġstat istics", + "ĠT ime", + "ĠU V", + "Ġc av", + "us ed", + "Ġf ish", + "Ġmajor ity", + "ĠP e", + "Ġcoh ort", + "Ġsem i", + "Ġg ame", + "mon ary", + "M M", + "od ed", + "Ġv ent", + "Ġaut o", + "Ġabund ance", + "n ov", + "Ġasympt otic", + "Ġtreat ments", + "ul y", + "Ġconstra int", + "Ġbe y", + "ĠS O", + "Ġst d", + "Ġdevelop ing", + "ĠN ot", + "L emma", + "Ġapp arent", + "Ġcirc uit", + "F rom", + "ĠEurope an", + "Ġsol ve", + "ĠÍ ij", + "u x", + "Ġbey ond", + "ep t", + "Ġapp e", + "requ ency", + "Ġvac u", + "ĠInd eed", + "ĠC hemical", + "ĠUnd efined", + "N ote", + "Ġn ull", + "Ġin verse", + "Ġnam ely", + "Ġshe ar", + "m L", + "A ll", + "R ec", + "Ġgeneral ized", + "ran es", + "ĠT est", + "il ing", + "Ġfluores cence", + "ĠÎ £", + "Ġind epend", + "d iff", + "Ġprovid ing", + "p henyl", + "h ing", + "Ġvir al", + "ĠB ecause", + "Ġint rac", + "ĠH ig", + "Ġw ant", + "Ġprincip le", + "an ol", + "Ġh a", + "ov ascular", + "Ġform er", + "Ġestabl ish", + "Ġadvant age", + "II I", + "Ġsequ encing", + "Ġproced ures", + "t ra", + "in dex", + "f e", + "Ġp i", + "Ġob vious", + "Ġreg ime", + "s ur", + "Ġpres ents", + "Ġdis plac", + "Ġdec l", + "ĠAp pendix", + "Ġinter act", + "land s", + "in ate", + "om orphic", + "Ġlow est", + "Ġar tif", + "Ġinvol ving", + "Ġcom merc", + "Ġd op", + "Ġcon form", + "ĠI g", + "rol og", + "v ised", + "Ġfl o", + "Ġcardi ac", + "p ts", + "r ig", + "Ġens ure", + "Ġaccum ulation", + "Ġent ropy", + "Ġide a", + "per ature", + "Ġques tions", + "ĠP R", + "Ġstat istically", + "d agger", + "Ġn itrogen", + "sc r", + "ĠDisc ussion", + "Ġre ports", + "Ġpul se", + "Ġrequire ments", + "Ġcompar ing", + "qui red", + "l ayer", + "Ġspect roscopy", + "viron ments", + "Ġscal ing", + "Ġex posed", + "M B", + "Î ¾", + "Ġh ole", + "Ġ á", + "Ġsimilar ity", + "Ġvari ants", + "b ody", + "Ġke ep", + "ĠC ancer", + "ed i", + "os omes", + "Ç «", + "A d", + "âĪ ŀ", + "mon ic", + "g ing", + "s plit", + "kn ow", + "Ġro ugh", + "hemat ical", + "v ision", + "Ġd ed", + "Ġcycl es", + "Ġfam il", + "Ġadminist ration", + "et al", + "Ġcor on", + "Ġinf ections", + "Ġmac roph", + "atic s", + "Ġpredic tions", + "is her", + "ere nt", + "re ted", + "incl ude", + "Ġclim ate", + "s ec", + "==== ====", + "ĠM S", + "Ġcomp e", + "r atic", + "l ig", + "pos es", + "Ġpolar ization", + "ll ip", + "der ived", + "Ġrele ased", + "Ġconn ection", + "l ic", + "Ġcol i", + "Ġout side", + "Ġabs olute", + "es ian", + "ĠE nd", + "ĠO f", + "Ġiden tical", + "Ġmod ule", + "Ġmitochond rial", + "Ġadv anced", + "ing ly", + "form ance", + "Ġto ward", + "ud ing", + "e k", + "Ġmean ing", + "c rib", + "ul ator", + "F N", + "k ey", + "c ons", + "Ġapp lying", + "is hes", + "Ġm amm", + "Ġderiv atives", + "Ġorient ation", + "Ġst ochastic", + "ĠA ug", + "Ġre nal", + "ĠG reen", + "Ġcomple ment", + "ob l", + "pir ical", + "or ts", + "B M", + "Ġex cess", + "Ġmorph ology", + "Ġs ound", + "if ier", + "Ġim plications", + "ĠDes ign", + "appro x", + "pro p", + "Ġcandid ate", + "Ġde pos", + "Ġequ ip", + "ust ain", + "ines e", + "et ry", + "Ġpot entially", + "Ġstra ight", + "Ġcr uc", + "i ology", + "Ġk ernel", + "Ġal coh", + "idd en", + "ret urn", + "Ġcorrec tion", + "ro t", + "Ġmic roscopy", + "Ġf oot", + "G L", + "ĠCell s", + "ir th", + "y g", + "ĠP ath", + "out hern", + "ĠL ong", + "Ġre vers", + "Î µ", + "ar se", + "Ġcere b", + "ist ed", + "Ġpul s", + "Ġdis k", + "it ud", + "Ġd u", + "Ġang ular", + "c hem", + "l ength", + "Ġexact ly", + "ro ke", + "ut h", + "Ġcon d", + "ins ic", + "Ġr ise", + "t ake", + "Ġtop ological", + "Ġrem ark", + "oll ary", + "Ġc er", + "T E", + "n ment", + "Ġbu ilt", + "Ġf re", + "Ġen ergies", + "ect ing", + "ĠT em", + "ra red", + "ĠN ow", + "ch arge", + "Ġloc ations", + "Ġbal ance", + "Ġl a", + "Ġre ached", + "lamm atory", + "Ġf abric", + "ific ations", + "Ġdiagnos tic", + "Ġmut ant", + "ĠN O", + "H D", + "ĠA B", + "Ġdisc rim", + "Ġprec ip", + "ĠTh ree", + "Ġins er", + "Ġinf ected", + "Ġconst ants", + "Î ©", + "neg ative", + "Ġconf idence", + "ĠPati ents", + "ollow ing", + "ad s", + "Ġhyper t", + "ĠIntern ational", + "D ef", + "ari ate", + "Ġinter vals", + "Ġex ercise", + "Ġeduc ation", + "Ġremov al", + "ther n", + "st er", + "Ġinte ger", + "ĠP A", + "Ġk id", + "Ġcategor ies", + "ĠG iven", + "Ġv ascular", + "here nce", + "math scr", + "ĠR et", + "Ġins ulin", + "tic ip", + "ĠC F", + "Ġlo ok", + "ymmet ry", + "Ġfor ces", + "ĠPhys ical", + "L S", + "c are", + "Ġh ouse", + "Ġind uce", + "Ġbel ie", + "ri a", + "ĠAs sum", + "Ġcomput ing", + "Ġb us", + "âĪ İ", + "Ġprac tical", + "t rain", + "T T", + "Ġpl astic", + "ĠN or", + "Ġfe as", + "ĠHamilton ian", + "Ġt ail", + "ĠZ n", + "Ġinterpret ation", + "duc ing", + "I s", + "Ġexam ine", + "ul ates", + "Ġmat ch", + "Ġ Ä", + "iv es", + "amet ers", + "Ġμ M", + "Ġexhib it", + "Ġn it", + "ot o", + "ĠCl inical", + "erv ation", + "ĠAd ditionally", + "ar ant", + "Ġel astic", + "D A", + "otop ic", + "Ġactiv ated", + "Ġt er", + "Ġconsequ ence", + "Ġend ot", + "oph ag", + "Ġcompar able", + "Ġdom inant", + "Î ·", + "Ġvalid ation", + "I m", + "Ġ Å", + "Ġle af", + "Ġf ung", + "tain ing", + "Ġun ivers", + "Ġph yl", + "Ġl ibr", + "Ġext ra", + "Ġpr int", + "medi ately", + "Ġmax imal", + "id ae", + "Ġor al", + "b in", + "Ġpepti de", + "ĠM ax", + "ar p", + "Ġconcl usion", + "Ġsatisf y", + "Ġanalyz e", + "o is", + "Ġinf er", + "Ġd raw", + "Ġdep ression", + "Ġmet all", + "Ġpost erior", + "Ġpeak s", + "s ol", + "Ġhorizon tal", + "Ġlater al", + "ĠO R", + "N N", + "Ġem o", + "P V", + "T A", + "Ġincub ated", + "Ġret rie", + "Ġhum ans", + "Ġ ri", + "Ġs oci", + "on ia", + "Ġinter ven", + "Ġvary ing", + "Ġs ti", + "ĠIm mun", + "Ġon set", + "Ġle aves", + "Ġother wise", + "Ġbl ocks", + "Ġass igned", + "SC s", + "Ġbi os", + "Ġmix ing", + "ar a", + "l i", + "Ġde formation", + "Ġcost s", + "Ġper ipher", + "ĠT ra", + "Ġat omic", + "Ġrandom ly", + "Ġarg ument", + "Ġit ems", + "Ġsu ff", + "Ġprob ably", + "n ers", + "Ġinhibit ors", + "Ġbe h", + "ĠDe ep", + "Ġp ig", + "ĠT ype", + "ĠM ost", + "ur a", + "itud inal", + "Ġderiv ative", + "Ġexpl ore", + "ĠIn formation", + "Ġg rap", + "ĠÎ Ķ", + "Ġprog ress", + "******** ********", + "Ġ ul", + "AR S", + "or al", + "os tic", + "C om", + "ĠEx ternal", + "ĠSt atistical", + "ĠR am", + "ĠL o", + "Ġelect rical", + "l ong", + "N et", + "EN T", + "v a", + "à ¤", + "ur ations", + "Ġdes ired", + "ir ing", + "Ġphys ics", + "Ġmass es", + "k i", + "Ġband s", + "Ġal k", + "ĠSimilar ly", + "Ġsur round", + "Ġcon vex", + "ost er", + "Ġlink ed", + "Ġfocus ed", + "Ġh ot", + "Ġmat ching", + "Ġoxid ation", + "Ġan ten", + "m iss", + "Ġm ental", + "il le", + "ici ency", + "ĠLi u", + "Ġprob e", + "ĠEs tim", + "Ġindic es", + "c he", + "ĠR ob", + "Ġcon v", + "ĠV er", + "ap se", + "S i", + "ph al", + "Ġles ions", + "Ġmolec ule", + "Ġa di", + "Ġd ate", + "Ġcompos ed", + "Ġa ud", + "struct ure", + "ot on", + "in for", + "Ġclust ering", + "ac ent", + "st ar", + "P O", + "ĠCh inese", + "Ġspec ifically", + "eren tial", + "Ġcap ture", + "ĠL ow", + "Ġf ine", + "Ġfem ales", + "ĠH ow", + "Ġa er", + "v ector", + "port un", + "form s", + "z o", + "Ġprec ision", + "yp t", + "Ġmin utes", + "Î º", + "Ġoxid ative", + "con n", + "ens us", + "Ġtrac e", + "Ġcon jug", + "Ġhigh light", + "s s", + "ĠExperim ental", + "ĠTh at", + "art ment", + "M O", + "' '", + "omet er", + "Ġst op", + "Ġ rib", + "Ġout er", + "r h", + "ri pt", + "Ġfluct uations", + "ob s", + "n on", + "Ġqu ark", + "Ġà °", + "ĠM ac", + "Ġperiod s", + "roll ed", + "A V", + "ĠO c", + "ĠIm age", + "ĠB el", + "Ġpropag ation", + "ĠD on", + "ww w", + "gl ish", + "Ġexhib ited", + "ogene ity", + "ĠB ack", + "Ġac tions", + "sk i", + "ĠAm ong", + "Ġb rief", + "ri ers", + "ĠN F", + "pos itive", + "sequ ently", + "ul ence", + "Ġen vironments", + "Ġcur v", + "om ics", + "Ġb it", + "Ġg el", + "Ġrepresent ations", + "Ġa way", + "ĠF ield", + "ob ic", + "C G", + "Ġcomp rehens", + "Ġh ierarch", + "Ġinduc es", + "B D", + "Ġh app", + "Ġe ight", + "Ġgra vity", + "Ġadap tive", + "B L", + "gen ic", + "Ġin struc", + "Ġanaly tical", + "ĠO x", + "ĠC ON", + "Ġsur gical", + "Ġd ip", + "at o", + "Ġrandom ized", + "Ġro les", + "d ep", + "ĠâĪ ĩ", + "ch ang", + "Ġdispers ion", + "Ġsepar ated", + "ĠOr gan", + "ĠV i", + "ĠJoh n", + "Ġan not", + "Ġres ource", + "en ergy", + "rel ation", + "me an", + "ĠB en", + "Ġconfir m", + "W ith", + "Ġinf inite", + "ĠSc ience", + "Ġsuccessful ly", + "Ġlocal ization", + "m ode", + "h ttps", + "geb ras", + "idel ines", + "Ġeff ectiveness", + "h yd", + "Ġs aid", + "ic o", + "Ġtrans itions", + "ed ing", + "Ġprogram s", + "Ġmob ile", + "Ġim mediately", + "ec tivity", + "ĠThe rm", + "ogene tic", + "Ġse ven", + "Ġem ph", + "G E", + "ne um", + "Ġf usion", + "lim its", + "Ġcalc ium", + "ra f", + "min us", + "Ġt rap", + "Ġspecim ens", + "anc ing", + "ĠM arch", + "Ġt en", + "Ġfamil ies", + "ĠH D", + "is ons", + "Ġpre paration", + "h old", + "et her", + "ĠV ol", + "ĠD ise", + "Ġrun ning", + "Ġqual it", + "Ġeff ectively", + "ffici ently", + "B I", + "Ġden oted", + "ĠEqu ation", + "Ġdem and", + "it ory", + "ach ing", + "Ġs odium", + "Ġrepro duc", + "ch o", + "Ġb il", + "P i", + "um b", + "Ġreconstr uction", + "for ward", + "O ne", + "Ġcon version", + "Ġform ulation", + "Ġnear ly", + "ĠL ag", + "S tr", + "ter ior", + "Ġoper ating", + "and om", + "Ġmov ing", + "ĠRe view", + "// //", + "n ai", + "p p", + "oti de", + "lab el", + "oc occ", + "Ġne ver", + "ak er", + "Ġdig ital", + "B l", + "U n", + "Ġmem ber", + "s el", + "Ġpot enti", + "Ġcop y", + "Ġelect rons", + "ch lor", + "ann el", + "yl ind", + "Ġm is", + "ĠS et", + "Ġnut ri", + "Ġdescrib es", + "Ġassum ptions", + "Ġvir tual", + "Ġcoordin ate", + "Ġv or", + "ĠA rab", + "ĠIm p", + "Ġde position", + "Ġins tit", + "Ġrepresent ative", + "ever al", + "Ġmill ion", + "ĠM A", + "Ġmal es", + "Ġcruc ial", + "Ġcol d", + "Ġload ing", + "Ġtrans lation", + "Ġst ead", + "ra ys", + "Ġchall enge", + "ac tivity", + "id al", + "u ff", + "Ġse em", + "Ġn ational", + "Ġf a", + "Ġmin or", + "Ġunderg o", + "c r", + "Ġcap t", + "e le", + "up le", + "ĠM g", + "le ge", + "G R", + "Ġr ig", + "Ġar ri", + "Ġdet ector", + "Ġst rict", + "Ġad hes", + "Ġse a", + "the less", + "Ġs leep", + "ĠCom mun", + "Ġanti oxid", + "Ġmark er", + "Ġflow s", + "anc re", + "ĠJan uary", + "in put", + "U P", + "Ġst ored", + "ad ing", + "iti vely", + "Ġsl ope", + "Ġshe ll", + "Ġelev ated", + "il k", + "Ġfrequ ently", + "Ġb all", + "urb an", + "Ġm l", + "us ive", + "ĠA nt", + "am ino", + "S im", + "Ġphys iological", + "reg ulation", + "es ity", + "Ġexpl an", + "Ġad en", + "re me", + "Ġdiff er", + "Ġmod ification", + "Ġir radi", + "H e", + "ac ial", + "Ġsupp ress", + "qu is", + "Ġd ry", + "er ated", + "Ġpro jection", + "Ġpo ol", + "ple te", + "Ġdirec tions", + "Ġchang ed", + "ĠI ts", + "Ġst er", + "Ġradi al", + "Ġg r", + "Ġperiod ic", + "Ġb in", + "Ġp ip", + "m en", + "t hen", + "p c", + "am ily", + "ĠD M", + "Ġsed iment", + "m i", + "Ġclos ely", + "Ġrep air", + "Ġrespir atory", + "Ġh orm", + "A ns", + "d r", + "l s", + "Ġhom ogeneous", + "et ric", + "D S", + "Ġresid ues", + "ĠVal ue", + "F s", + "Ġwh y", + "S p", + "Ġc a", + "Ġn arrow", + "g ent", + "Ġb r", + "Ġqu asi", + "Ġp ict", + "m o", + "Ġat om", + "Ġh abit", + "Ġlimit ations", + "con duc", + "Ġsh ock", + "cept or", + "ĠDet ection", + "S h", + "ub e", + "Ġe llip", + "U R", + "Ġstain ing", + "Ġrapid ly", + "ĠB ur", + "ĠB ro", + "Ġup take", + "Ġchalleng es", + "S N", + "Ġan is", + "Ġbound s", + "st ep", + "omer ic", + "ten tion", + "ĠEval uation", + "Ġrecomm end", + "M e", + "Ġmod erate", + "ell ed", + "Ġt it", + "ĠY ang", + "Ġph armac", + "inf lammatory", + "ĠJ une", + "Ġsens ors", + "ai red", + "Ġapproxim ate", + "S V", + "Ġb und", + "r c", + "om an", + "Ġvis ible", + "Ġmeas uring", + "og onal", + "ĠFour ier", + "Ġthe ories", + "Ġprof ession", + "tain ed", + "at as", + "ĠInt erest", + "par am", + "ĠStruct ure", + "Ġl iving", + "D ata", + "ĠS M", + "Ġn et", + "Ġsimultaneous ly", + "cont inu", + "Ġsh or", + "#### ####", + "Ġdecre asing", + "Ġrefer red", + "g g", + "Th us", + "Ġd ro", + "pr il", + "ĠP ers", + "Ġenc oding", + "Ġar c", + "Ġregul atory", + "Ġtra ined", + "cep ts", + "Ġro ut", + "ly s", + "P ar", + "ĠU l", + "ĠG raph", + "âĪ Ĥ", + "Ġir re", + "oid al", + "Ġex ceed", + "Ġmost ly", + "ĠP at", + "ater nal", + "Ġ er", + "Ġco verage", + "F S", + "ĠR ot", + "Ġclass ified", + "Ġexc itation", + "Ġconduc tivity", + "Ġcommerc ial", + "ĠD el", + "ĠP olar", + "H R", + "Ġtra ffic", + "z ing", + "Ġsetting s", + "Ġincl usion", + "Ans wer", + "Ġv it", + "vit ational", + "Ġb ind", + "Ġo c", + "ĠW estern", + "Ġpro sp", + "Ġn orth", + "it ch", + "ĠR iver", + "Ġvehic le", + "Ġlik elihood", + "L D", + "Ġin sp", + "âĪ Ĩ", + "Ġle uk", + "ĠB re", + "Ġsynt hetic", + "ĠGerm any", + "ĠThe ir", + "t arget", + "ĠEn glish", + "Ġnot ation", + "ĠA TP", + "ĠMod els", + "Ġab normal", + "ĠConcl usions", + "Ġoccur rence", + "Ġmicro bi", + "ĠW ar", + "tem ber", + "Ġloc ally", + "bor n", + "Ġbar rier", + "Ġexpression s", + "ov al", + "Ġfl av", + "emb le", + "Ġdynam ical", + "Ġphot on", + "app ed", + "Ġgl ut", + "Ġkine tic", + "Ġalcoh ol", + "Ġtrans plant", + "L P", + "Ġdef ault", + "Ġop portun", + "arg s", + "ĠD av", + "Ġfron t", + "h om", + "Ġw ays", + "ĠAss ociation", + "Ġkid ney", + "Ġpropor tional", + "W hen", + "Ġepit helial", + "Ġf resh", + "Ġrec all", + "Ġenzym es", + "b r", + "Ġmal ign", + "text rm", + "ĠU se", + "N ow", + "ĠL ie", + "Ġimp air", + "Ġgu arant", + "Ġin ver", + "Ġtranscri pt", + "Ġs ustain", + "Ġact ually", + "al ities", + "ĠM ic", + "ĠI C", + "ĠMe asure", + "Ġï£ ´", + "Ġd ensities", + "Ġgalax y", + "Ġsu fficiently", + "Ġor bit", + "f ord", + "Ġpar tially", + "ĠP y", + "Ġre verse", + "Ġsur ve", + "ĠW ork", + "Ġas k", + "H owever", + "Ġsit u", + "Ġvacu um", + "to ber", + "Ġsp ac", + "an th", + "O r", + "ag s", + "Ġb ig", + "her ical", + "er ge", + "ell ite", + "Ġinvol ves", + "ĠV is", + "Ġsumm ary", + "ĠSup plementary", + "ĠC oll", + "Ġadj acent", + "ont aneous", + "ab s", + "Ġresearc hers", + "k a", + "Ġinter n", + "Ġmon th", + "ĠNe ural", + "ap or", + "ĠN an", + "Ġst ri", + "E E", + "Ġconsist ing", + "Ġupd ate", + "Ġphot o", + "V al", + "s ens", + "Ġve get", + "B R", + "Ġco inc", + "ĠJ uly", + "til ity", + "ĠEx pression", + "Ġtop ology", + "Ġgrow ing", + "ap tic", + "uc ed", + "Ġperipher al", + "en es", + "Ġpl ots", + "Ġexpl o", + "Ġw or", + "b a", + "ati tis", + "i ef", + "w ave", + "Ġprot ection", + "Ġdef ects", + "Ġads orption", + "Ġsh ared", + "Ġst ellar", + "ĠB a", + "ĠEn ergy", + "que ous", + "ĠAug ust", + "Ġl ys", + "Ġpl us", + "i rel", + "ĠG P", + "ĠNe u", + "d ist", + "g ers", + "if er", + "is p", + "Ġstr at", + "ion e", + "ĠMaterial s", + "Ġl n", + "Ġpul monary", + "en ed", + "pl an", + "M od", + "Ġorgan ization", + "Ġrelax ation", + "Ġcor tex", + "Ġmod ulation", + "og l", + "sh ift", + "Ġsec urity", + "Ġfat ty", + "Ġm s", + "l ocal", + "erg ic", + "Ġinter ference", + "ins on", + "c f", + "Ġre asons", + "p red", + "Ġinterven tions", + "Ġj o", + "ĠI D", + "ĠAre a", + "ĠH a", + "u its", + "out put", + "L e", + "y cl", + "in ted", + "Ġnan o", + "N C", + "ĠC ap", + "Ġchang ing", + "Ġc ust", + "Ġappe ared", + "Ġgrow n", + "ĠU K", + "Ġrad ical", + "ĠP ot", + "ĠPro gram", + "ĠS R", + "Ġsh ap", + "os cop", + "ĠCh ang", + "Ġquanti ty", + "ĠT axon", + "id ation", + "Ġadd ing", + "ĠLe e", + "Ġam ounts", + "Ġdes pite", + "Ġremain ed", + "Ġscenari os", + "le ts", + "om ing", + "Ġcurv ature", + "Ġd imensional", + "Ġprom ising", + "ĠF il", + "str ing", + "Ġattrib uted", + "ym er", + "Ġneigh b", + "Ġinput s", + "Ġmagn et", + "Ġtre es", + "Ġent er", + "r uit", + "st able", + "to plas", + "Ġmess age", + "roph ic", + "Ġisol ates", + "t z", + "Ġdisplay ed", + "H A", + "oc l", + "Ġder ive", + "Ġsyn chron", + "Q U", + "à ŀ", + "Ġexam ination", + "Ġde b", + "Ġdef in", + "Ġf ault", + "Ġstead y", + "Ġphen otype", + "Ġpers pective", + "Ġstat ement", + "d f", + "v oid", + "Ġprom ote", + "ill ary", + "ĠE th", + "Ġw alk", + "Ġrepresent ing", + "Ġgen omic", + "ĠG r", + "sh ape", + "ĠP et", + "ĠL ocal", + "plic ity", + "ĠProb lem", + "G S", + "Ġcomple ted", + "ink ing", + "Ġread s", + "Ġin de", + "ce ived", + "ĠP L", + "ĠMe an", + "ĠSch ool", + "Ġbiom ark", + "irel ess", + "c ut", + "os ing", + "n el", + "ĠA pril", + "ĠB al", + "Ġadop ted", + "Ġcom plications", + "Ġassemb ly", + "f ort", + "h ar", + "Ġad oles", + "Ġans wer", + "Ġcommun ities", + "ĠInstit ute", + "Ġvari ant", + "F inally", + "mit te", + "Ġrestric ted", + "Ġman ip", + "at ers", + "E X", + "Ġd ust", + "Ġsupp ly", + "Ġper me", + "Ġreli able", + "ĠRes p", + "Ġsub t", + "o ks", + "Ġpol l", + "Ġcan c", + "ĠUn it", + "Ġendot helial", + "d y", + "ĠBl ack", + "Ġem pirical", + "Ġp ort", + "op y", + "Ġiniti ally", + "Ġcond ens", + "Ġe ye", + "Ġlist ed", + "ur rence", + "Ġreplac ed", + "Ġselec tive", + "Ġdist ances", + "Ġpar as", + "ĠP ost", + "ĠSep tember", + "Ġmiss ing", + "vere x", + "E r", + "Ġthough t", + "th al", + "Ġchrom at", + "Ġbenef it", + "ram es", + "ĠSup pose", + "Ġsub s", + "Ġang i", + "or i", + "Ġre plic", + "Ġschem es", + "p re", + "pl ane", + "Ġs outh", + "ag er", + "Ġbegin ning", + "v ents", + "on ent", + "i ples", + "ĠH er", + "Ġspect rom", + "Ġden se", + "Ġto ok", + "iver se", + "Ġdist urb", + "p ass", + "Ġillustr ated", + "Ġreve als", + "am a", + "Ġref lec", + "Ġallow ing", + "Ġexpon ential", + "ous tic", + "subset eq", + "Ġs n", + "Ġ urban", + "Ġext end", + "Ġass ays", + "ric e", + "Co V", + "quis ition", + "r ine", + "ĠIn tegr", + "f il", + "V D", + "Ġfib ro", + "Ġcomp ens", + "ĠIm pro", + "ĠĠĠĠĠĠĠĠ ĠĠ", + "ĠG R", + "Ï Ī", + "Ġbas al", + "Ġol ig", + "H T", + "Ġv ess", + "uz zy", + "Ġposs ibly", + "Ġtoler ance", + "The ta", + "Ġvi ol", + "uc lear", + "ĠL im", + "g el", + "Ġmetric s", + "ĠM us", + "am ination", + "Ġelectro de", + "Ġpers onal", + "Ġcool ing", + "Ġac quired", + "ĠF unction", + "ow s", + "oles ter", + "D P", + "Ġreli ability", + "Ġm uc", + "ĠOc tober", + "Ġg old", + "c a", + "Ġc ul", + "f it", + "Ġle m", + "Ġexc it", + "Ġnucle us", + "i ation", + "Ġpregn ancy", + "Ġsynt hesized", + "hem istry", + "Ġmemb ranes", + "ver t", + "ĠK im", + "ten ance", + "Ġquanti ties", + "Ġecon omic", + "Ġbenef its", + "Ġc ylind", + "pl er", + "ĠL arge", + "Ġengine ering", + "ĠE p", + "Ġco ating", + "ati v", + "Ġconduc t", + "Ġabs orb", + "ĠDec ember", + "Ġop posite", + "ĠGl obal", + "Ġl if", + "ĠD ue", + "Ġint ake", + "od ynamic", + "T M", + "Ġf ed", + "Ġspec ified", + "Ġge ometric", + "Ġresp ective", + "Ġb irth", + "ĠComp ound", + "Ġstar ted", + "Ġm other", + "ar r", + "Ġprim arily", + "Ġp aren", + "Ġtub e", + "Ġinter s", + "Ġgrap hene", + "iti al", + "ous ly", + "Ġcardi ovascular", + "Ġe V", + "Ġhe ating", + "Ġmat hematical", + "Ġindepend ently", + "B A", + "Ġaff ects", + "um or", + "ĠM P", + "ĠD em", + "ĠW est", + "ĠD om", + "it ter", + "Ġdis rup", + "op ed", + "Ġphenomen on", + "Ġl umin", + "A c", + "Ġpre fer", + "om ers", + "Ġg ender", + "ĠG L", + "F C", + "Ġinde ed", + "Ġr ational", + "ĠS C", + "Ġprincip al", + "Ġperf ect", + "Ġintro duction", + "t es", + "Ġpi ec", + "Ġc ity", + "Ġpop ular", + "Ġc oding", + "cl er", + "ag ue", + "ĠH R", + "Ġtrack ing", + "k er", + "Ġphosphor ylation", + "Ġpath s", + "Ġsol ving", + "Ġd y", + "Ġplay ed", + "Ġprec ise", + "ĠS l", + "ĠS em", + "Ġgener ating", + "ĠS un", + "Ġcriter ion", + "Ġbran ch", + "ĠÎ ¶", + "ti sh", + "S e", + "Ġanti gen", + "Ġcal ibration", + "E s", + "ĠI tal", + "Ġmass ive", + "E n", + "N o", + "Y P", + "y a", + "Ġsatisf ying", + "Ġqu ick", + "H O", + "Ġbehavi ors", + "icro bial", + "Ġam b", + "Ġpro ton", + "S L", + "Ġus ual", + "row s", + "en ch", + "U C", + "Ġweight ed", + "Ġrec ords", + "ĠA C", + "G T", + "in n", + "Ġe q", + "ĠW il", + "y roid", + "Ġset up", + "I A", + "p ress", + "is ely", + "Ġent ry", + "% %", + "ĠS il", + "e ast", + "ĠE volution", + "ĠR andom", + "Ġcav ity", + "Ġnam ed", + "know led", + "m ber", + "ues tion", + "ĠâĪ ©", + "g i", + "Ġdeterm ining", + "t in", + "Ġgen us", + "Ġtox icity", + "oc yt", + "Ġperturb ation", + "rough t", + "ĠB ri", + "Ġcar b", + "ĠG ra", + "ĠF lu", + "un s", + "Ġdri ven", + "Ġb atch", + "r if", + "P l", + "Ġdisplac ement", + "ĠC L", + "Ġdep ic", + "Ġpredic tive", + "I nt", + "hydro xy", + "ti d", + "d ri", + "Ġp ancre", + "Ġdiag onal", + "Ġsever ity", + "Ġlong itudinal", + "ĠE D", + "ati ble", + "d ir", + "ĠAn other", + "ĠH el", + "v an", + "Ġp neum", + "Ġspecific ity", + "s qu", + "Ġ ign", + "Ġb ed", + "ĠW T", + "aw a", + "es ter", + "Ġk g", + "Ġcomp ression", + "ever theless", + "Ġm ask", + "-------- ---", + "Ġt ens", + "row th", + "ĠG o", + "Ġf aster", + "Ġcan onical", + "Ġdeterm in", + "ust rial", + "ĠEar th", + "wh ile", + "our nal", + "Ġcount ry", + "Ġf erm", + "r ist", + "Ġpro xim", + "Ġmicro bial", + "Ġext ensive", + "Ġch am", + "Ġ §", + "s uch", + "w ent", + "Ġl ar", + "U sing", + "ĠP M", + "Ġoff set", + "ĠP I", + "ĠBay esian", + "H S", + "ĠAfric a", + "Ġsuscepti bility", + "ĠâĬ Ĥ", + "ococc us", + "ĠD ir", + "Ġb os", + "Ġdys function", + "ove mber", + "Ġunder st", + "Ġlarg ely", + "ĠC M", + "Ġmaintain ed", + "Ġposs ess", + "Ġexcl uded", + "ens is", + "ĠD C", + "ops is", + "Ġtor ch", + "id ine", + "Ġfore st", + "ĠEx act", + "ĠStud ies", + "iff iff", + "ĠC am", + "ang ular", + "Ġrem ove", + "o ir", + "av a", + "id a", + "Ġm ant", + "L og", + "Ġrang ing", + "ro g", + "Ġchain s", + "Ġ Ç«", + "ĠC ase", + "ĠA P", + "po ints", + "Ġtarget ing", + "Ġsc ience", + "Ġep is", + "ĠS oci", + "Ġphys ic", + "Ġpromot er", + "ĠEar ly", + "es tic", + "tiv es", + "Ġassum ing", + "ĠM i", + "Ġlem ma", + "Ġconfig urations", + "al ia", + "Ġp ay", + "r ino", + "e b", + "Ġvari ed", + "oun ted", + "Ġinter view", + "ĠGe V", + "O M", + "ogn ition", + "Ġenhance ment", + "ĠM ach", + "pl ies", + "O b", + "set minus", + "Ġintr insic", + "Ġcompar isons", + "b old", + "xi ety", + "Ġst roke", + "G B", + "anc ial", + "ste ad", + "Ġro ck", + "th on", + "ĠC urrent", + "c at", + "Ġgu idelines", + "cy cl", + "Ġintrac ellular", + "one y", + "k o", + "Ġdirec ted", + "rip ts", + "Ġtra vel", + "Ġl ens", + "id i", + "ĠAss ess", + "Ġd x", + "ĠP os", + "Ġmethod ology", + "Ġpred om", + "def ined", + "ĠP op", + "Ġgover nment", + "ell ig", + "ph yl", + "ol i", + "rop ical", + "Ġembed ded", + "ed om", + "crib ed", + "ĠDise ase", + "Ġmedi ated", + "Ġcirc ular", + "ĠTop ological", + "Ġear th", + "ri tis", + "g al", + "m ass", + "Ġcomprehens ive", + "ĠA ir", + "Ġn erve", + "Ġimpl ant", + "Ġextrem ely", + "ĠS E", + "Ġmark et", + "Ġconserv ed", + "emb rane", + "Ġsched ul", + "Ġrun s", + "P h", + "Ġtechn ical", + "T L", + "Ġregion al", + "Ġg erm", + "ĠPro t", + "Ġb right", + "Ġarter y", + "Ġmacroph ages", + "mitte e", + "ĠSing le", + "Ġcom e", + "w a", + "ac char", + "ple t", + "Ġsens ing", + "ros p", + "at om", + "Ġcomp r", + "ĠL u", + "Ġavail ability", + "pro t", + "Ġfit ting", + "sel ves", + "ĠP rim", + "re w", + "Ġwas te", + "ĠK ing", + "p ot", + "Ġinstr ument", + "ĠY ork", + "A F", + "an tial", + "stand ing", + "Ġpl anning", + "ust er", + "Ġâ Ĩ", + "N T", + "ic ular", + "Ġmel an", + "Ġexc ell", + "ill er", + "ĠL D", + "inf o", + "Ġsh are", + "v as", + "Ġl um", + "Ġa queous", + "Ġqu ery", + "Ġm ag", + "ult ure", + "ĠB er", + "Ġof fer", + "ĠN MR", + "ace ae", + "Ġmod ern", + "Ġcirc um", + "Ġcult ures", + "Ġd og", + "Ġc ir", + "Ġpol i", + "Ġchem otherapy", + "Ġpl ates", + "Ġrestric tion", + "st ack", + "ĠF low", + "ĠB u", + "ĠC enter", + "Ġpro ceed", + "tim icrobial", + "s he", + "Ġthere by", + "Ġkn ock", + "Ġdi verse", + "ustr y", + "Ġst ated", + "ĠH ol", + "M ore", + "Ġconserv ation", + "Ġpre vention", + "n orm", + "Ġp al", + "ĠCal c", + "Ġcle an", + "ĠPl as", + "`` `", + "per p", + "pro d", + "Ġâī ¡", + "por ter", + "Ġtrans ient", + "as p", + "Ġtarget ed", + "ĠP ri", + "Sup plementary", + "ĠT reatment", + "z en", + "ĠM art", + "ĠF erm", + "us cript", + "ĠS ynthesis", + "Ġcomb inations", + "UL L", + "Ġwe b", + "Ġth rom", + "Ġexplicit ly", + "an ks", + "Ġadapt ation", + "ĠSequ ence", + "Ġac ts", + "Ġrang es", + "f s", + "b ru", + "Ġsystem ic", + "Ġste el", + "Ġpri vate", + "Ġob esity", + "ĠP art", + "ment ed", + "bre ak", + "ER T", + "Ġfib ers", + "Ġis o", + "Ġtrans verse", + "CT ION", + "ĠR i", + "it in", + "ĠRep resent", + "oph ys", + "Ġco ast", + "Ġal ignment", + "AC T", + "es ides", + "op en", + "g ly", + "Ġsal t", + "unc ed", + "ia z", + "Ġcos m", + "Ġang les", + "ĠâĢ ł", + "ĠIdentif ication", + "he x", + "ĠH all", + "Ġhep at", + "Ġseg ments", + "ĠPh ase", + "ĠL and", + "form ing", + "h box", + "ic ations", + "Ġsubsequ ently", + "ĠC ur", + "Ġlab els", + "vid ence", + "ual ity", + "Ġhe ld", + "em ann", + "Ġcam era", + "c ing", + "ub ic", + "ĠS ARS", + "ul atory", + "kele tal", + "ĠInf lu", + "ĠInd ia", + "am ic", + "Ġs and", + "Ġcom es", + "Ġassoci ations", + "Ġcharg ed", + "Ġs per", + "opro tein", + "ii i", + "od al", + "Ġbound aries", + "ti zation", + "ĠHe avy", + "ĠRe al", + "ĠA F", + "Ġcontroll er", + "Ġantioxid ant", + "Ġb ars", + "Ġw et", + "en er", + "ĠComplex ity", + "Ġst ack", + "There fore", + "Ġre plication", + "Ġappear ance", + "Ġtraject ory", + "Ġunderst ood", + "Ġd ot", + "Ġim ag", + "Ġsc anning", + "T i", + "r uct", + "ĠL y", + "Ġsp ontaneous", + "l at", + "om on", + "Ġro ots", + "Ġl ive", + "Ġfin ally", + "¿ ½", + "Ġappro ved", + "ĠAp plications", + "ĠP an", + "Ġl ost", + "Ġsatisf ied", + "Ġg amma", + "ion al", + "Ġimpro ving", + "Ġmanif old", + "Ġc odes", + "b b", + "ĠN ovember", + "Ġr ich", + "N P", + "ĠE le", + "S B", + "Ġde al", + "Ġop tions", + "Ġcult ured", + "Ġv ul", + "> >", + "ar ithm", + "o ys", + "The se", + "ĠDet erm", + "Ġquad ratic", + "ĠCom b", + "iss on", + "ĠPer formance", + "Ġex ception", + "Ġnucle i", + "Ġad verse", + "k et", + "ĠP al", + "ĠM any", + "Ġdiff raction", + "Ġtrans mit", + "Ġphosph ate", + "olester ol", + "Ġquestion nai", + "ĠSe a", + "bru ary", + "Ġmod elling", + "ĠD R", + "ol in", + "ch mark", + "Ġprec isely", + "g ans", + "v in", + "rid ge", + "ĠInc re", + "Ġneur onal", + "Ġâī Ī", + "Ġexcell ent", + "et ary", + "Ġoverl ap", + "Ġstrong er", + "Ġfract ure", + "ĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ ĠĠĠĠĠĠĠ", + "Ġclin ic", + "ĠL ist", + "Ġhist or", + "gen eration", + "ric hed", + "ill us", + "Ġà ħ", + "ĠR ole", + "Ġlabel ed", + "Ġorth ogonal", + "Ġis chem", + "Ġinst ability", + "lo op", + "Ġplot ted", + "ĠProcess ing", + "ĠT a", + "ĠConcl usion", + "Ġm agne", + "Ġunivers al", + "Ġj et", + "Ġreg im", + "flo at", + "Ġc od", + "ad j", + "bold math", + "Ġar rang", + "Ġtrend s", + "Ġprecip itation", + "f requency", + "Ġcont rad", + "Ġtransfer red", + "Ġmain tenance", + "Î Ķ", + "n p", + "ist ence", + "he res", + "lec tive", + "ĠSur vey", + "Ġ Ð", + "Ġst and", + "Ġdisc overy", + "ain s", + "vers ely", + "Ġnumer ous", + "yl ated", + "Ġembed ding", + "Ġcoll abor", + "en ame", + "im mun", + "Ġadjust ed", + "i res", + "c ur", + "Ġvacc ine", + "Ġtra its", + "Ġmorph ological", + "Ġprec urs", + "roscop e", + "ad i", + "ec utive", + "u an", + "Ġt ract", + "ĠP res", + "Ġmy el", + "Ġad equ", + "Ġeth anol", + "i h", + "Ġmet h", + "Ġcoun ts", + "Ġqualit ative", + "Ġmus ic", + "Ġre infor", + "A fter", + "Ġac quisition", + "Ġh ttps", + "all ing", + "it a", + "ic ate", + "sc ript", + "Ġoptim ized", + "ĠH o", + "Ġm ild", + "opl as", + "Ġo verex", + "ĠâĪ §", + "Ġcol lect", + "ĠM ain", + "Ġextrac ellular", + "Ġan c", + "ra wn", + "Ġexpl ored", + "Ġres erv", + "ĠAp plication", + "c ase", + "Ġmar ine", + "ĠĠĠĠĠĠĠĠ ĠĠĠĠĠ", + "il ed", + "Ġmes h", + "ĠMon te", + "cl os", + "Ġperform ing", + "A g", + "reg ular", + "Ġc atal", + "Ġpotenti als", + "ant ly", + "U RE", + "Ġacc omp", + "Ġreason able", + "Ġpresent ation", + "abol ic", + "ĠOn ly", + "ann ed", + "Ġsubst antial", + "Ġdiet ary", + "Ġsubstr ates", + "ap ter", + "Ġint estinal", + "Ġproduc es", + "Pro position", + "ro gen", + "ĠSt at", + "bur g", + "ren ch", + "text bf", + "ystem s", + "at able", + "ĠV ir", + "Ġsol ved", + "ic ense", + "Ġs ong", + "Ġext reme", + "pt y", + "ĠC ity", + "ve red", + "ĠMR I", + "Ġtw ice", + "ĠM n", + "Ġm erg", + "ac tivation", + "Ġn g", + "Ġo dd", + "Ġatt rac", + "Ġatt empt", + "Ġsepar ately", + "Ġrob ot", + "ĠMulti ple", + "Ġsc ientific", + "ĠP P", + "Ġmin eral", + "Ġprotoc ols", + "Ġsuper ior", + "oc amp", + "box yl", + "Ġuniform ly", + "ĠS everal", + "Ġm ol", + "C or", + "under line", + "Ġinflu enced", + "Ġcur ren", + "us ing", + "rac e", + "ĠN evertheless", + "Ġacc om", + "Ġgra vitational", + "Ġindi rect", + "Ġcap able", + "Ġanalys ed", + "Ġdis charge", + "Ġv es", + "Ġlig and", + "l ik", + "Ġs i", + "Ġag ed", + "Ġcryst als", + "Ġspe ech", + "Ġcop per", + "ĠS an", + "ĠA rm", + "Ġman uscript", + "Ġsec retion", + "w edge", + " ·", + "Ġra w", + "Ġaim ed", + "Ġevolution ary", + "Ġconsequ ences", + "Ġit em", + "Ġw estern", + "Ġsol vent", + "Ġstim uli", + "Ġrequire ment", + "h ttp", + "ef ore", + "ĠAt l", + "Ġatmosp heric", + "Ġpack age", + "Ġmy ocardi", + "Ġd ashed", + "Ġver ify", + "ativ istic", + "Ġto m", + "av irus", + "ak en", + "ĠNum er", + "Ġadvant ages", + "F R", + "ĠS elf", + "rec ted", + "con fig", + "Ġit eration", + "Ġeigen values", + "Ġprob abilities", + "F IG", + "ĠW ater", + "ĠA u", + "Ġg ave", + "Ġv ar", + "ric ular", + "opath y", + "Ġr h", + "ord ance", + "Ġw in", + "ĠS cale", + "Ġann ual", + "atas et", + "Ġp el", + "ĠâĪ ª", + "ĠC C", + "it ors", + "Ġl ith", + "Ġchrom osome", + "Ġf uel", + "Ġmul tiv", + "Ġmanufact ure", + "l a", + "ĠS a", + "um es", + "ig m", + "Ġnan oc", + "E GF", + "Ġsign ature", + "N S", + "Ġme et", + "Ġf air", + "met h", + "Ġlocal ized", + "ĠCent ral", + "de g", + "Ġsurround ing", + "Ġn one", + "ĠM O", + "ĠInterest ingly", + "Ġmul tic", + "ĠK e", + "Ġinhib ited", + "ĠC are", + "ĠOp en", + "Ġgl ob", + "E A", + "ĠF ound", + "Ġpix el", + "ok e", + "R D", + "l oc", + "ti ous", + "Ġdistingu ish", + "Ġan terior", + "ur ch", + "Ġj ud", + "ĠP ower", + "Ġswit ch", + "ĠS yn", + "Ġinvolve ment", + "uc l", + "Ġlibr ary", + "ĠCon st", + "Ġsp herical", + "ĠT NF", + "Ġal tered", + "v ance", + "trans fer", + "M s", + "ĠO per", + "in ement", + "se q", + "C ons", + "ho le", + "ĠPh ot", + "Ġg ut", + "acter ial", + "ĠI P", + "un t", + "Ġn om", + "h as", + "ĠFe bruary", + "Ġpro state", + "ĠM L", + "h igh", + "ĠBack ground", + "ul ent", + "Ġo cean", + "a fter", + "ĠO ff", + "l oss", + "Ġfav or", + "Ġwork ers", + "Ġh idden", + "Ġextrac ts", + "raz il", + "s ign", + "N one", + "Ġcolum ns", + "Ġfrac tions", + "Ġco vered", + "ĠS erv", + "Ġin form", + "b ed", + "Ġatt em", + "rain ing", + "Ġneut ron", + "Ġr ice", + "Ġmo tif", + "Ġartif icial", + "Ġinhibit ory", + "Ġd t", + "AG E", + "Ġsam pled", + "Ġb atter", + "Ġsub jected", + "Ġgener ic", + "ĠN H", + "Ġcontin ue", + "ution al", + "Ġa ug", + "i us", + "Ġexec ution", + "ĠW illi", + "ĠDes pite", + "A MI", + "Ġcont ents", + "ĠS ens", + "og ens", + "C ol", + "Ġf o", + "Ġad di", + "u ated", + "Ġrecomm ended", + "ĠS W", + "Ġar ch", + "ĠY es", + "Ġh ol", + "atur ally", + "ti tive", + "Ġc he", + "Ġs ector", + "ĠDef inition", + "Ġcon cepts", + "or ous", + "sm all", + "ers on", + "in ator", + "ĠM T", + "Ġhypert ension", + "c ks", + "Ġn ative", + "Ġt ax", + "r yl", + "Ġre active", + "r b", + "duc ible", + "om m", + "Ġdiagnos ed", + "Ġdri ving", + "Ġbiom ass", + "u ate", + "Ġp il", + "c alled", + "Ġser ve", + "Ġinter fer", + "ipp ocamp", + "Ġalgebra ic", + "Ġbe gan", + "Ġpict ure", + "ind ependent", + "Ġutil ized", + "go ing", + "or a", + "n m", + "Ġdown stream", + "Ġorb ital", + "oun tain", + "ĠH is", + "Ġres ol", + "Ġcorrec tions", + "on ym", + "sc ripts", + "Ġsil icon", + "Ġc um", + "ĠT ri", + "Ġpepti des", + "Ġrece iving", + "Ġstation ary", + "Ġμ L", + "cler osis", + "Ġmod ules", + "em a", + "ĠAfric an", + "struc tion", + "Ġf arm", + "Ġlear n", + "n ode", + " ®", + "Ġsuper conduc", + "ĠL inear", + "Ġtechn ologies", + "Ġnecess arily", + "Ġcoron ary", + "ĠE ast", + "Ġf rames", + "Ġseg mentation", + "V s", + "Ġbehavior al", + "Î ĵ", + "Ġlog ic", + "Ġaccomp an", + "tif ied", + "han ol", + "ĠIn hib", + "il ation", + "and er", + "Ġeff ort", + "ĠD en", + "D I", + "op tim", + "term inal", + "Ġmob ility", + "Ġconsider ation", + "O VA", + "Ġpar ad", + "ox o", + "Ġdef iciency", + "ult ural", + "Ġvalid ity", + "Ġord ers", + "Ġloc us", + "Ġar th", + "em at", + "Ġfeed ing", + "Ġprogram ming", + "Ġtem plate", + "el ian", + "Ġop tion", + "ĠF ollowing", + "Ġen able", + "Ġass ign", + "Ġform ul", + "p u", + "Ġatmosp here", + "sl ant", + "ĠR uss", + "ĠE vidence", + "Ġsimilar ly", + "Ġc amp", + "Ġw ound", + "ĠCharacter ization", + "ĠP BS", + "e es", + "ĠDi rect", + "ĠS L", + "Ġfr uit", + "Ġg ate", + "it o", + "C hem", + "Ġcoll ision", + "or tic", + "Ġpolym orphism", + "enz a", + "w hat", + "Ġexperiment ally", + "Ġult ra", + "e z", + "Ġn erv", + "Ġess entially", + "ĠAustr alia", + "ĠSt andard", + "Ġmedic ine", + "ad ian", + "ĠHig gs", + "u ge", + "Ġsup ports", + "um a", + "Ġcom plicated", + "d ate", + "ophag y", + "ĠMark ov", + "Ġoccur ring", + "opl us", + "P ub", + "pro b", + "ur able", + "Ġk ept", + "Ġisol ation", + "Ġev ol", + "ili ary", + "Ġreg ist", + "Ġh oles", + "Ġcl ar", + "ip ar", + "Ġen rich", + "Ġro ute", + "ay ers", + "edi atric", + "Ġpolynomial s", + "Ġtri vial", + "ĠS am", + "vari ant", + "Ġfre edom", + "pos s", + "Ġinf erence", + "ol a", + "Ġinterp reted", + "C a", + "em ory", + "Ġcent ury", + "ĠR em", + "ĠW u", + "Ġsupp ression", + "Ġgener ator", + "ĠH om", + "Ġvis cos", + "Ġpseud o", + "ĠCh ild", + "ĠS A", + "ib er", + "Ġequival ence", + "if ies", + "ĠCons ider", + "ol ine", + "âī ¤", + "Ġde ple", + "Ġaver aged", + "Ġs outhern", + "Ġord ered", + "ĠB rown", + "Ġmethyl ation", + "ĠAd ap", + "Ġm aternal", + "ond ed", + "ĠBe havi", + "Ġidentif iers", + "Ġprocess ed", + "G G", + "V I", + "Ġch a", + "un k", + "ĠF unctional", + "Ġhydro ph", + "Ġfin ancial", + "ec ond", + "ĠÎ ¨", + "Ġemph as", + "Ġdef ect", + "m ar", + "Ġnor thern", + "c ore", + "Ġadhes ion", + "Ġte le", + "Ġw arm", + "rif ug", + "rang ian", + "res olution", + "Ġhe x", + "h bar", + "Ġhar monic", + "Ġcont rac", + "Ġread ing", + "Ġeff orts", + "ĠO l", + "Ġan xiety", + "b ul", + "T C", + "ip id", + "R emark", + "Ġform ing", + "il bert", + "am ond", + "Ġanaly tic", + "ore c", + "ch a", + "ĠCon sequently", + "ĠS u", + "for all", + "Ġà ŀ", + "Ġasp ect", + "Ġins ights", + "ati vity", + "io tics", + "he imer", + "ĠL abor", + "Ġa ware", + "ĠBri tish", + "c hemical", + "Ġâ ĭ", + "cl usion", + "ĠM ich", + "Ġgra de", + "ĠS EM", + "ĠC irc", + "hes es", + "W L", + "Ġen abl", + "Ġd end", + "Ġind ustry", + "Ġimpro ves", + "t et", + "Ġt el", + "Ġwas hed", + "Ġshor ter", + "Ġinc ident", + "ĠAc tivity", + "Ġdos es", + "ĠB razil", + "Ġtransform ations", + "Ġform at", + "ĠPro of", + "Ġl en", + "ul ative", + "Ġcycl ic", + "Ġrec ruit", + "pt r", + "T H", + "Ġrece ive", + "ĠNe xt", + "ĠEx p", + "i ant", + "in stein", + "S et", + "re ne", + "Ġge omet", + "Ġconsider able", + "S o", + "ugh t", + "Ġpaper s", + "ĠC S", + "z a", + "Ġis omorphism", + "ho u", + "Ġmut ants", + "Ġpor tion", + "Ġà ¾", + "Ġcontinu um", + "C u", + "ĠComput ed", + "Ġcomb ining", + "ov a", + "ĠN P", + "Ġc rack", + "Ġsome times", + "Ġcontinu ed", + "Def inition", + "arc in", + "ĠC d", + "ĠMed ical", + "i ences", + "ĠC ross", + "Ġtranscription al", + "ĠZ e", + "st d", + "if orn", + "Ġfail ed", + "Ġidentif ying", + "Ġm ir", + "Ġmetast asis", + "O F", + "n n", + "ĠC ID", + "Ġoscill ations", + "anc ies", + "wr ite", + "Ġband width", + "Ġtra de", + "Ġag ing", + "ĠModel ing", + "Ġass ert", + "Ġcurren ts", + "Ġf ire", + "ub iqu", + "Ġalb um", + "Ġfrequ ent", + "N ame", + "Ġpur ch", + "Ġpl ayer", + "ĠE sc", + "Ġno tion", + "Ġintern ational", + "ul um", + "o ic", + "Ġincub ation", + "Ġphenomen a", + "Ġser ver", + "ut er", + "Ġv en", + "qu in", + "Ġhyp ox", + "ĠR F", + "it on", + "Er ror", + "Ġhe mat", + "Ġthem selves", + "Ġper p", + "id ual", + "Ġpur poses", + "m es", + "w ing", + "ro v", + "Ġem iss", + "Ġexperi enced", + "qu es", + "ĠL C", + "ĠRec ent", + "bo ok", + "Ġalk al", + "id x", + "hy th", + "Ġconc rete", + "Ġswit ching", + "Ġexplan ation", + "ird s", + "Ġsign s", + "Ġob j", + "Ġcytok ines", + "ub ble", + "ad der", + "Ġuncertain ties", + "Ġprom otes", + "Ġcom pl", + "Ġsc an", + "Ġpr ime", + "P H", + "Ġheter ogeneous", + "ĠY ou", + "Al though", + "Ġser ious", + "Ġdri ve", + "Ġheter ogeneity", + "ryst all", + "Ġo d", + "Ġcon volution", + "ĠâĬ Ĩ", + "ĠSp ace", + "Ġgast ric", + "ĠSt re", + "ĠP V", + "b ase", + "M et", + "Ġloss es", + "Ġcyt otox", + "Ġcontroll ing", + "le ase", + "Ġreg ulated", + "ĠEng ine", + "ĠH ospital", + "B r", + "on om", + "hy de", + "st age", + "Ġgiv ing", + "ĠP en", + "ĠSoci ety", + "dri ven", + "i ang", + "Ġmod ifications", + "B V", + "Ġaccel eration", + "Ġm ilk", + "on omic", + "Ġth ink", + "ogl ob", + "Ġfeas ible", + "n am", + "Ġref lection", + "ĠPol y", + "Ġsummar ized", + "F L", + "Ġrec t", + "Ġpredom inant", + "Ġbl ot", + "de hyde", + "Ġtransform ed", + "Ġfacilit ate", + "ĠCar lo", + "Ġgreat ly", + "ĠS ocial", + "Ġparen ts", + "big g", + "ros pective", + "Ġprogn osis", + "Ġcharacter ize", + "Ġconn ectivity", + "Ġtraject ories", + "ĠS H", + "Ġl ies", + "Ġcandid ates", + "rom y", + "Ġs or", + "ĠIn s", + "Ġth or", + "Ġmet als", + "ĠS V", + "Ġtim ing", + "Ġutil ity", + "Ġnew ly", + "ĠI FN", + "Ġaffect ing", + "ce ment", + "ĠM el", + "ĠÌ ģ", + "typ es", + "lys is", + "erc ul", + "Ġdist or", + "act ors", + "ps y", + "Ġbo ok", + "ĠE ven", + "tem perature", + "Ġinvas ion", + "Ġrecogn ized", + "fact or", + "N e", + "Ġinter section", + "Ġcor tical", + "n g", + "Ġde ploy", + "Ġamplit udes", + "Ġd a", + "ĠG C", + "Ġchalleng ing", + "Ġpre lim", + "G M", + "A cc", + "Ġfour th", + "al c", + "ĠP S", + "ĠGene tic", + "l ock", + "err or", + "sk ip", + "s ime", + "Ġan a", + "sime q", + "Ġcereb ral", + "ĠE X", + "av ed", + "roph y", + "id opsis", + "Ġbeh ind", + "Ġen ables", + "Ġind ustrial", + "ĠP ac", + "Ġdefin itions", + "Ġcataly tic", + "Ġdiss ip", + "erv ical", + "Ġcom mut", + "Ġrepe at", + "Ġch iral", + "Ġp ron", + "p ol", + "Ġgo ing", + "Ġmic roscope", + "Ġhealth care", + "ĠClass ification", + "tit ude", + "ĠFerm i", + "Ġh ttp", + "are st", + "Ġsupport ing", + "Ġw ood", + "n ight", + "Ġkine tics", + "Ġsubset s", + "Ġsub unit", + "ĠCan ada", + "at on", + "Ġaccur ately", + "Ġres istant", + "ĠïĢ ½", + "ric tion", + "Ġcham ber", + "ig ue", + "ĠPh il", + "Ġrec over", + "c s", + "Ġsp here", + "ĠSpec ifically", + "Ġan ne", + "Ġiniti ation", + "ĠT H", + "Ġb ud", + "ord ered", + "Ġdi electric", + "ĠCol lege", + "Ġproduc ing", + "Ġanten na", + "B s", + "ĠF rench", + "O X", + "ĠAmeric a", + "ĠâĢ Ķ", + "oun ting", + "ful ly", + "Ġserv ed", + "Ġresid ue", + "Ġarg uments", + "Ġp and", + "Ġcomp any", + "Ġcondition al", + "m ia", + "ĠQ CD", + "Ġviscos ity", + "Ġprosp ective", + "as onal", + "Ġdom inated", + "Ġpen et", + "op o", + "Ġn ine", + "ĠI ll", + "ĠVis ual", + "Ġfil es", + "Ġy east", + "Ġthan k", + "G N", + "re al", + "Ġver ified", + "ĠInd ian", + "Ġsti ff", + "rolog ical", + "Ġd ram", + "Ġt ight", + "ĠGerm an", + "ĠTechn ology", + "ĠAppro ach", + "rom atic", + "Ġac oustic", + "ti an", + "os in", + "ĠDep artment", + "ot ropy", + "Ġem pty", + "tri vial", + "of il", + "Ġal gebras", + "tex ts", + "Ġwe bs", + "Ġp ore", + "Ġpack et", + "T ime", + "im g", + "on y", + "ri tic", + "Ġveloc ities", + "ĠD ynamics", + "Ġcanc ers", + "Ġtr unc", + "ĠForm ation", + "ĠDon or", + "ĠM it", + "I ST", + "Ġconcl uded", + "Ġan tag", + "ĠSo ft", + "app end", + "Ġfrag ments", + "ĠPro f", + "Ġflu or", + "ĠJ ac", + "ĠS n", + "Ġle pt", + "Ġsplit ting", + "Ġsex ual", + "ĠF ore", + "ĠGen er", + "Ġneighbor hood", + "Ġben chmark", + "ĠR A", + "Ġdiv ision", + "iforn ia", + "Tr ue", + "Ġf uzzy", + "Ġt ro", + "c ents", + "Ġconstit u", + "ati al", + "as tern", + "ĠT im", + "Ġper ception", + "Ġsubst anti", + "Ġmac ro", + "Ġout l", + "ĠObs erv", + "pr ising", + "ok ed", + "orec tal", + "ĠCh o", + "ĠDiff erent", + "Ġinvestig ations", + "Ġconsist ency", + "i ents", + "ĠF OR", + "AS S", + "ĠV an", + "Ġsit uations", + "ĠB R", + "Ġinf rared", + "ym al", + "Ġpix els", + "Ġcar rier", + "s en", + "IN T", + "Ġeffici ently", + "D T", + "ĠEx pl", + "ion ic", + "Ġn aturally", + "Ġpro pos", + "Ġgu ide", + "Ġconcl usions", + "o on", + "Ġg rant", + "Ġinst ances", + "Ġreview ed", + "Ġelect romagnetic", + "Ġth reat", + "ed ia", + "ĠOptim ization", + "ĠB io", + "Ġtrig ger", + "ici ent", + "otyp ic", + "Ġst ret", + "Ġan tic", + "Ġtox ic", + "Ġsp inal", + "UP AC", + "Ġover view", + "o tion", + "Ġstraight forward", + "Ġpos itively", + "as te", + "Ġref erences", + "ul ose", + "ĠG re", + "Ġantag on", + "Ġshif ts", + "Ġd rawn", + "ĠWh ite", + "Ġfrac tional", + "Ġbund le", + "Ġexhib its", + "Ġreserv oir", + "ĠA lex", + "Ġaggreg ation", + "Ġcirc le", + "Ġprac tices", + "ĠC oval", + "ĠDist ribution", + "Ġt ang", + "ĠM ut", + "Ġreg ulate", + "osp here", + "i ro", + "AMI NO", + "v est", + "Ġphot os", + "Ġev ident", + "Ġbus iness", + "cont rol", + "Ġw orth", + "ĠPo isson", + "ĠArab idopsis", + "ĠT arget", + "Ġregul ates", + "ĠI r", + "ĠAd v", + "Ġens emble", + "pr ing", + "Ġp rice", + "ĠF L", + "ĠImp act", + "Ġevent ually", + "in ating", + "Ġcent rifug", + "f rame", + "Ġdiagram s", + "Ġt ag", + "Ġt ry", + "sur face", + "ĠIdentif iers", + "ra ined", + "Ġs ides", + "Ġin n", + "Ġflex ible", + "Ġsat ellite", + "Ġaff inity", + "Ġsum mer", + "G P", + "am b", + "Ġa qu", + "Str ing", + "t reatment", + "ĠD ynamic", + "math op", + "Ġno tice", + "n es", + "row ave", + "ves tig", + "Ġoutput s", + "Ġco herent", + "Ġillustr ate", + "Ġvalid ated", + "ĠSc hem", + "Ġask ed", + "b atch", + "Ġpur ified", + "Ġminim ize", + "ĠD E", + "U M", + "c heck", + "vari an", + "ĠG old", + "yl ene", + "I O", + "Ġch olesterol", + "Pub Chem", + "ĠK ore", + "ĠCount y", + "Ġi i", + "ĠM AP", + "ect omy", + "Ġsem antic", + "Ġcoll agen", + "Ġper ceived", + "ich ia", + "Ġadminist ered", + "con taining", + "ran k", + "In ChI", + "Ġirradi ation", + "Ġlog arithm", + "Ġg ames", + "Ġinj ected", + "ĠM Hz", + "Ġd ors", + "Ġevalu ating", + "ĠHy per", + "Ġchromat ography", + "p hen", + "ĠK ar", + "Ġan timicrobial", + "ri end", + "Ġdescrib ing", + "Ġw t", + "Ġhorm one", + "A K", + "ĠI UPAC", + "G a", + "Ġvit amin", + "Ġconn ections", + "u ous", + "ĠL ine", + "Ġbenef icial", + "c ases", + "ic ated", + "is ks", + "p arent", + "I d", + "er ies", + "r un", + "Ġm ind", + "it t", + "s ulf", + "z heimer", + "Ġinter f", + "V ert", + "Ġan th", + "olog ous", + "ĠL ife", + "Ġm ur", + "Ġper mut", + "ot ing", + "Ġneut rino", + "Ġb orn", + "p matrix", + "ĠCal ifornia", + "ag ent", + "Ġcoll isions", + "ĠN S", + "Ġh ippocamp", + "Ġpow der", + "Ġv aries", + "Ġepid em", + "ĠWe b", + "ul er", + "Ġinterest ed", + "Ġdevelopment al", + "Ġlength s", + "Ġcol our", + "Ġqu as", + "ĠR ich", + "E q", + "Ġinf ants", + "ĠP H", + "ophil a", + "Ġcaus ing", + "G e", + "mod ule", + "I B", + "Ġcontrib uted", + "ro se", + "Ġcy toplas", + "---------------- ----------------", + "Ġro ad", + "s ymmetric", + "U s", + "Ġweak ly", + "ti te", + "Ġdef ines", + "ĠP E", + "Ġmetabol ites", + "Ġl ob", + "Ġterm inal", + "Ġdemonstr ates", + "ĠAc ceptor", + "ĠC lo", + "Ġinfer red", + "Ġv ill", + "F irst", + "Ġneg lig", + "Ġw ireless", + "A b", + "par ticle", + "ois otopic", + "Ġexc ited", + "P M", + "Ġcons ecutive", + "ĠIs otype", + "Ġstim ulus", + "ĠM C", + "tim ate", + "ĠCoval ently", + "B onded", + "Ġy ellow", + "Ġall oy", + "d ensity", + "Ġfil ters", + "Ġampl ification", + "Ġw on", + "h t", + "Ġimp acts", + "Ġst aff", + "ĠâĪ Ģ", + "ĠIs omeric", + "Ġsm oking", + "Q u", + "Ġcapt ured", + "h aps", + "ĠN ULL", + "Ġri ver", + "c ount", + "Ġmanif est", + "Ġdiab etic", + "Ġalter ations", + "ĠRot atable", + "ĠP RO", + "ĠMon oisotopic", + "Ġï Ĥ", + "sp ect", + "Ġcataly st", + "Ġmodel ed", + "Ġp age", + "ĠR OS", + "ĠCanonical ized", + "ĠT w", + "Ġa ux", + "av age", + "ĠRam an", + "st o", + "per f", + "Ġreplac ement", + "ĠEn vironment", + "Ġac ting", + "p ati", + "ific ant", + "th rough", + "Ġsat uration", + "Ġt ip", + "Ġrec urrence", + "ĠHist ory", + "Ġprot ective", + "Ġbur den", + "ad o", + "y es", + "in st", + "A p", + "ĠS y", + "Ġph on", + "ĠâĪ ij", + "Ġgen otype", + "Ġcovari ance", + "Ġquick ly", + "ĠD u", + "Ġs ug", + "Ġdec line", + "ĠT B", + "Ġstrict ly", + "Ġmo ist", + "und red", + "ĠC B", + "ati le", + "ĠH F", + "Ġar ticles", + "Ġp s", + "ĠEn h", + "ist ing", + "Ġbi ology", + "Ġb odies", + "ĠA k", + "ĠNumer ical", + "ĠLag rangian", + "Ġdisc overed", + "Ġv ic", + "op es", + "Ġfrag ment", + "Ġt y", + "ism ic", + "Ġhep atic", + "Ġen riched", + "p an", + "Ġinflu ences", + "ĠL ake", + "col or", + "Ġenrich ment", + "oc hemistry", + "Ġun stable", + "ĠIg G", + "der ly", + "Ġe cos", + "Ġconcer ning", + "ĠR isk", + "Ġmarg in", + "Ġpath ogenesis", + "Ġp ump", + "Ġprelim inary", + "Ġtum our", + "F urther", + "az ole", + "Ġelectro des", + "Ġd ial", + "ub es", + "ĠN atural", + "ĠM ul", + "ĠïĢ Ń", + "Ġn ic", + "Ġimp ed", + "on ly", + "Ġcompar ative", + "rec tion", + "ak i", + "Ġre nd", + "Ġsp arse", + "Ġindic ator", + "l ocation", + "tis m", + "ac tivated", + "ĠP b", + "epti de", + "Ġend ogenous", + "Ġcent ers", + "a o", + "s w", + "Ġcons ensus", + "Ġattrib utes", + "Ġsaf e", + "Ġbelie ve", + "ov irus", + "Ġimmun ity", + "Ġfit ted", + "Ġcontrib utes", + "i able", + "Ġvirus es", + "Ġins ight", + "ĠNo vel", + "ĠAl zheimer", + "cep ted", + "ĠP t", + "Ġcent re", + "n at", + "Ġbios ynthesis", + "m its", + "Ġchem istry", + "Ġj us", + "an ish", + "Ġre frac", + "ĠT or", + "Ġpan els", + "Ġimp ly", + "Ġmat ched", + "us c", + "w ord", + "v ae", + "ĠSt ar", + "s yn", + "M at", + "Ġapplic able", + "ĠP seud", + "amp ions", + "ĠR en", + "Ġus age", + "ĠL ight", + "p rec", + "Ġfib rosis", + "Ġreconstr uc", + "ĠO N", + "ĠG Hz", + "G D", + "al gebra", + "ig er", + "Ġdec isions", + "inf ected", + "knowled g", + "Ġexpress ing", + "Ġmyocardi al", + "ord ination", + "Ġprogn ostic", + "Ġfibro bl", + "Ġaccel er", + "ĠAssess ment", + "Ġconstra ined", + "Ġalle le", + "r ide", + "Ġrequ est", + "abil istic", + "te b", + "Ġg a", + "Ġrec overed", + "Ġpro min", + "urs es", + "ĠH C", + "ĠM ur", + "ĠEq s", + "Ġdef ining", + "Ġm er", + "im age", + "Ġorgan isms", + "g rad", + "Ġref lected", + "el astic", + "e ties", + "dim ethyl", + "EL O", + "ra ndom", + "ĠDi agn", + "ercul osis", + "ro b", + "Ġmom ents", + "ĠE C", + "Ġexperi ences", + "erv ing", + "ĠN C", + "Ġvor tex", + "g re", + "struct ures", + "el t", + "Ġcar ry", + "ĠTh rough", + "Ġpre ced", + "rast ruct", + "it us", + "Ġpsych ological", + "Ġlimit ing", + "t wo", + "ĠB ound", + "ĠC re", + "ĠSm ith", + "Ġc ast", + "Ġcompe tition", + "s ch", + "Ġcap ability", + "ach ment", + "Ġinhib its", + "à °", + "ĠDiff erential", + "Ġautom atically", + "Ġg est", + "Ġw aters", + "Ġun iqu", + "z er", + "E qu", + "Ġstudy ing", + "Ġdi ed", + "Ġo s", + "Ġrecomb ination", + "unc il", + "Ġpath ogen", + "GF R", + "U V", + "en eration", + "ĠS ta", + "Ġinst ant", + "Ġpro ven", + "Ġd s", + "Ġd amp", + "N ext", + "ĠY oung", + "Ġpower ful", + "Ġwr iting", + "k l", + "Ġcare er", + "ĠCor ollary", + "N s", + "Ġï ¿½", + "ĠM il", + "Ġb urn", + "tic ular", + "ond on", + "P r", + "ĠL in", + "ĠJapan ese", + "ĠL ab", + "Ġst rip", + "pro tein", + "Ġh our", + "angle ment", + "angu ages", + "r d", + "par se", + "Ġemiss ions", + "H ence", + "ĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ ĠĠĠĠ", + "Ġj ob", + "ĠA S", + "Ġax ial", + "ĠT ur", + "car bon", + "M F", + "ĠN E", + "Ġar ise", + "Ġlinear ly", + "Ġprol ong", + "Ġle ak", + "ĠĠĠĠĠĠĠĠ ĠĠĠĠĠĠ", + "Ġmov ed", + "orb idity", + "Ġprofession al", + "c ode", + "os ine", + "Ġpol ic", + "Ġbond s", + "m ask", + "Ġconver ted", + "v ille", + "ec tious", + "par allel", + "ĠH al", + "ĠT GF", + "ment al", + "Ġread er", + "Ġstandard s", + "ag o", + "Ġ EN", + "Ġst ations", + "Ġnormal ization", + "ĠÎ ĺ", + "ch ain", + "W hat", + "Ġtom ography", + "Ġent ries", + "bl ue", + "ĠPre vious", + "i as", + "Ġquestionnai re", + "Ġh az", + "Ġhom ology", + "ver y", + "Ġnucle otide", + "ĠGen ome", + "Ġμ l", + "Ġutil ization", + "Ġpolym ers", + "ro te", + "Ġsmall est", + "cal c", + "Ġs pl", + "Ġt ension", + "Ġdis continu", + "al a", + "h ol", + "Ġdeterm ines", + "Ġpro j", + "ĠOver all", + "Ġb le", + "f o", + "Ġprinc iples", + "Ġinteract ing", + "Ġhard ware", + "l ife", + "ail s", + "Ġdifficult y", + "Ġcho ices", + "Ġc ard", + "Ġl act", + "Ġro ll", + "Ġquantif ied", + "ĠSc ientific", + "Ġland sc", + "al igned", + "Ġcompos ites", + "her ichia", + "Ġen velop", + "iti g", + "S te", + "Ġcomp et", + "Ġimpair ment", + "Ġclos ure", + "Ġreturn ed", + "Ġrece iver", + "Ġpe er", + "Ġcons ent", + "Ġult ras", + "Ġphot ons", + "Ġsup pose", + "Ġpredic ting", + "ĠâĬ ķ", + "Ġcomp an", + "Ġneglig ible", + "c urrent", + "um ber", + "Ġcomp atible", + "i op", + "ĠStruct ural", + "R ef", + "Ġs on", + "Ġequ ality", + "Ġconsist ed", + "Ġv ibr", + "ou pling", + "v ation", + "Ġover come", + "s uper", + "l ict", + "Ġpancre atic", + "G s", + "ap ed", + "as al", + "w an", + "Ġlat ent", + "Ġcover ing", + "Ġles ion", + "i ance", + "ĠF T", + "wo od", + "ject ure", + "ĠB C", + "link ed", + "ĠL aw", + "Ġem it", + "Ġunc lear", + "Ġpre m", + "ac ted", + "p olar", + "c re", + "Ġmod ulus", + "rop ath", + "S ub", + "am i", + "Ġp ick", + "ER R", + "Ġmove ments", + "N i", + "Ġmechan ics", + "od ic", + "Ġg al", + "ĠMan agement", + "h ost", + "ew ise", + "ĠT otal", + "ĠInflu ence", + "Ġ ubiqu", + "roph ys", + "Ġcap s", + "Ġparticip ant", + "Ġpol yp", + "t d", + "Ġiter ations", + "dom inal", + "B B", + "Ġcharacter s", + "Ġdevi ations", + "res istant", + "Ġmal aria", + "Ġrem ote", + "h skip", + "Ġunder went", + "u til", + "bl ock", + "ucl ide", + "Î ¦", + "elect ron", + "Ġsens ory", + "ĠSim ulation", + "Ġre ward", + "Ġpand emic", + "Ġb or", + "ynt hetic", + "Ġinvas ive", + "R F", + "ĠSm all", + "ĠF isher", + "val ent", + "ĠM I", + "roc ytes", + "ĠT E", + "Ġst re", + "Ġperturb ations", + "Ġsim plicity", + "ĠG rowth", + "ĠÎ ł", + "Ġin oc", + "ard ing", + "at um", + "m ulti", + "ĠD iv", + "an es", + "ac illus", + "Ġlife time", + "ĠH ep", + "Ġa z", + "us p", + "ĠAssum e", + "Ġbre aking", + "ĠAt t", + "ticip ants", + "Ġlumin osity", + "Ġdon or", + "par ams", + "oh yd", + "Ġpro gen", + "ĠP O", + "G O", + "ĠL eg", + "Ġbiomark ers", + "Ġr ural", + "Ġne on", + "gl uc", + "ĠP B", + "Ġgu id", + "Ġc ervical", + "p ace", + "Ġc ord", + "um n", + "Ġsub space", + "Ġatt ached", + "Ġdepos ited", + "Ġindic ators", + "ĠS F", + "qui re", + "Ġdiss olved", + "r ite", + "ĠN A", + "Ġj u", + "Ġadd ressed", + "Ġsupp ressed", + "Ġpneum onia", + "Ġs ession", + "ĠC he", + "ĠF er", + "Ġacc ordance", + "D es", + "Ġqu ar", + "Ġfit ness", + "Ġvi ability", + "os h", + "Ġphyl ogenetic", + "ect in", + "p at", + "ĠFran ce", + "Ġmess ages", + "Ġl oci", + "Ġconf lict", + "Ġrele vance", + "Ġinstruc tions", + "Ġsome what", + "chang ed", + "Ġcorrect ly", + "oz yg", + "av ig", + "ĠL at", + "Ġo varian", + "ĠR emark", + "j oint", + "ain t", + "w est", + "s ample", + "Ġdiver gence", + "Ġh air", + "ag onal", + "Ġm im", + "Ġim mediate", + "ĠP ort", + "Ġoff ers", + "Ġdepic ted", + "Ġhydro x", + "ĠT ow", + "Ġemerg ing", + "ou pled", + "Ġh undred", + "Ġadap ted", + "ell er", + "ĠRel ations", + "et te", + "Ġgast ro", + "Ġm orphism", + "Ġequip ment", + "p op", + "un ately", + "Ġtransplant ation", + "if iers", + "Ġel derly", + "on ucle", + "Ġref ers", + "ar ial", + "ĠCom mittee", + "Ġmalign ant", + "omon as", + "Ġall ocation", + "og ether", + "Ġnan ot", + "pl ot", + "ĠM es", + "Ġplan ar", + "ell s", + "s ource", + "ow ski", + "Ġn a", + "Ġcl ock", + "Ġamb ient", + "oc ene", + "Ġfluores cent", + "Ġval u", + "ĠM agnetic", + "Ġde part", + "phosph ate", + "Ġrough ly", + "Ġne ither", + "ĠAl tern", + "Ġst ay", + "Ġsp ot", + "ĠE nt", + "Ġsecond s", + "h ard", + "Ġrec urrent", + "Ġpat ch", + "Ġlimit ation", + "ĠD er", + "Ġsh arp", + "Ġexpect ation", + "ĠL ore", + "d ict", + "R eg", + "Ġneut roph", + "Ġn ur", + "Ġstar ts", + "ost asis", + "Ġorgan ized", + "Ġc DNA", + "or ient", + "ĠEx ample", + "ĠF und", + "ay lor", + "id ering", + "Ġtri ple", + "n ic", + "Ġattack s", + "ĠD ros", + "à ¨", + "ĠE M", + "Ġoptim um", + "Ġp ull", + "Ġc e", + "ery th", + "Ġr ating", + "Ġreproduc tive", + "Ġdec ades", + "Ġre place", + "L ist", + "ĠF ast", + "Ġred shift", + "op sy", + "ill a", + "do uble", + "ter a", + "Ġgo als", + "ĠS k", + "IN E", + "Ġbi ochemical", + "u int", + "Ġf etal", + "ĠRi emann", + "ur ies", + "Ġp p", + "Ġsymbol s", + "ĠK a", + "D i", + "ĠG alax", + "ĠComp ared", + "Ġc asc", + "Ġb its", + "Ġsc aff", + "Ġestim ator", + "ĠAd ditional", + "Ġimprove ments", + "ectiv es", + "Ġh ous", + "ĠM agn", + "Ġmultiv ariate", + "Ġag ric", + "v o", + "ut ter", + "ĠAc knowledg", + "s u", + "Ġam mon", + "Ġaim s", + "Ġz inc", + "Ġel ong", + "ĠG O", + "Q uestion", + "incl uding", + "Log P", + "Ġint ellig", + "Ġcon e", + "ĠFound ation", + "Ġimp aired", + "Ġill ness", + "ĠEsc herichia", + "Ġabund ant", + "s cal", + "ens ively", + "Ġneg atively", + "par ameter", + "Ġperme ability", + "dom ain", + "r ated", + "Ġep och", + "Ġadoles cents", + "Ġdef ic", + "ĠEstim ation", + "Ġrout ine", + "P er", + "t ol", + "Ġellip tic", + "ĠH E", + "obl ast", + "Ġre aches", + "Ġflux es", + "Ġs un", + "ĠAn aly", + "âĢ ľ", + "ĠX LogP", + "Ġfilter ing", + "ri an", + "ĠS cal", + "Ġp in", + "ĠTi O", + "im ents", + "Ġmarg inal", + "Ġrecomb inant", + "Ġenc our", + "Ġal umin", + "Ġt f", + "ataly tic", + "Ġobserv ational", + "Ġgeneral ization", + "Ġï£ ¬", + "Ġantib iotic", + "Ġgener ates", + "Ġd B", + "S pec", + "r ically", + "Ġvalu able", + "Ġtop ic", + "Ġterm in", + "Ġsem icon", + "Ġquantif ication", + "ub b", + "Ġkin em", + "err ing", + "Ġa eros", + "p ack", + "Ġfew er", + "Ġfat igue", + "Ġgo es", + "Ġn ight", + "ĠU s", + "âĢ ¬", + "ĠPr inc", + "Ġsp ring", + "Ġconcer ns", + "Ġsm art", + "Ġsec ret", + "Ġmm ol", + "Ġbel ief", + "D C", + "Ġsubstanti ally", + "âĪ ĩ", + "Ġsubstit ution", + "map sto", + "sk y", + "ill ance", + "Ġstud ent", + "ok ine", + "Ġinter ior", + "Ġeigen value", + "m y", + "Ġclos er", + "eren ti", + "Ġec ological", + "ĠFig ures", + "oly tic", + "Ġar rays", + "ĠC as", + "Ġlo ops", + "Ġcorrec ted", + "Ġr he", + "Ġin version", + "Ġprefer red", + "um ab", + "ĠD I", + "Ġadequ ate", + "ir m", + "Ġim plicit", + "sh ip", + "Ġplay ers", + "Ġdelay ed", + "Ġw inter", + "Ġvul ner", + "Ġshap es", + "Ġstain ed", + "ĠM ajor", + "Ġhierarch ical", + "ĠD ig", + "ers ion", + "ĠE fficient", + "Ġwall s", + "d frac", + "Ġclass ifier", + "Ġmon ol", + "Ġupd ated", + "Ġm ature", + "ĠL I", + "ear ing", + "Ġf inger", + "oun ter", + "ank ton", + "Wh ile", + "Ġreal istic", + "ĠC amp", + "Ġf illed", + "Ġde ad", + "ĠPac ific", + "Ï ĩ", + "ĠDav id", + "Ġaddi tive", + "ench ymal", + "Ġobs er", + "Ġst ere", + "Ġultras ound", + "ĠPred ic", + "Ġend s", + "section al", + "m as", + "om at", + "iv ity", + "Ġhand le", + "Ġmetast atic", + "ole t", + "r yp", + "AC E", + "Ġpor ous", + "Ġconcer n", + "it ored", + "Ġcir cles", + "Ġemo tional", + "g ered", + "Ġf riction", + "f irst", + "oph y", + "es cop", + "ad ed", + "Ġres olved", + "ER S", + "Ġpath ogens", + "Ġgrad ually", + "ĠB rain", + "x f", + "an ium", + "a el", + "N ew", + "Ġcytok ine", + "ĠB P", + "Ġspecim en", + "ole an", + "Ġt axon", + "Ġsequ ential", + "κ B", + "ad emic", + "pl ings", + "~ ~", + "erm al", + "t ree", + "Ġcaus al", + "ari an", + "Ġc rop", + "op ol", + "ch annel", + "ĠM ex", + "Ġcl on", + "ĠRec ently", + "ĠIn vestig", + "Ġrecommend ations", + "form at", + "ĠM ET", + "Ġsent ence", + "Ġb p", + "ĠG W", + "Ġrec ording", + "Ġp le", + "to tic", + "Ġï£ ·", + "Ġrang ed", + "en tion", + "obacter ia", + "cep tions", + "ĠIm port", + "d ynamic", + "por ary", + "G iven", + "Ġturb ulence", + "Ġg ram", + "Ġequ ally", + "c d", + "ĠO s", + "Ġturn s", + "Ġdetect ing", + "ati o", + "gen erate", + "gra de", + "Ġcirc ulation", + "Ġmanufacture r", + "L a", + "ĠH ilbert", + "T s", + "in tegr", + "Ġbelong s", + "ĠIntern et", + "ang l", + "ĠâĬ ¥", + "ĠDros ophila", + "uclide an", + "t an", + "Ġext ends", + "Ġexpand ed", + "ill in", + "squ are", + "ys acchar", + "Ġquantif y", + "Ġpuls es", + "Ġves ic", + "ĠN K", + "ores cence", + "ĠPh osph", + "Ġv ision", + "ĠHu ang", + "ĠResp onse", + "h ouse", + "ear s", + "Ġe g", + "Ġac cepted", + "ĠT M", + "amet ric", + "Ġpath ological", + "Ġrecruit ment", + "AT A", + "Ġfig ures", + "ĠP ress", + "Ġal igned", + "Ġpost operative", + "ĠMe V", + "Ġconsider ably", + "Ġconform al", + "ĠIs land", + "num ber", + "Ġautom atic", + "Ġs plic", + "Ġcyt os", + "Ġdesc rip", + "ĠS ant", + "l ies", + "u ity", + "it one", + "E CT", + "ĠB on", + "Ġdis app", + "bo ard", + "or rh", + "Ġcalc ulating", + "ne e", + "ĠMe as", + "Ġgen omes", + "Ġphot oc", + "Ġread ily", + "ov ine", + "ĠDe v", + "Ġsat ur", + "Ġkind s", + "ĠP K", + "Ġro d", + "Ġj unction", + "ĠH A", + "Ġdesign s", + "h n", + "Ġorder ing", + "Ġcosm ological", + "Ġpil ot", + "Ġcol orectal", + "ĠL ondon", + "ĠDir ac", + "C ont", + "ĠW ind", + "ĠT re", + "id in", + "ĠïĢ «", + "ilt ration", + "More over", + "Ġre tention", + "tim ately", + "hydro gen", + "d el", + "bol ic", + "ĠQu anti", + "per iod", + "Ġretrie val", + "at ase", + "end icular", + "ulti es", + "R S", + "N H", + "Ġin formed", + "Ġfil tered", + "m embrane", + "Ġstiff ness", + "ĠO cean", + "ĠS Y", + "Ġl ot", + "ĠFig s", + "Ġans w", + "ĠEng land", + "ĠAtl antic", + "process ing", + "Ġdog s", + "Ġl ie", + "Ġun ion", + "ĠT an", + "Ġhal o", + "Ġcontinuous ly", + "B u", + "A MP", + "ĠAp p", + "Ġmoist ure", + "Ġth yroid", + "Ġaccompan ied", + "Ġfol d", + "Ġorig inally", + "Ġsp an", + "ĠF A", + "conn ected", + "Ġrec urs", + "vi an", + "ĠEqu ations", + "en a", + "arcin oma", + ".. ..", + "Ġdisc rep", + "U H", + "Ð ¾", + "ang er", + "Ġmon itored", + "Ġinflu enza", + "Ġs ure", + "bl ack", + "o e", + "Ġall oc", + "Ġhabit at", + "op henyl", + "Ġvent ricular", + "Ġpolic ies", + "am ate", + "Ġreport ing", + "Ġsol uble", + "======== ========", + "Ġdip ole", + "Ġirre ducible", + "ĠP rec", + "acet yl", + "Ġth read", + "ĠAppro xim", + "Ġm apped", + "i pro", + "Ġt ropical", + "S ch", + "ĠAN OVA", + "Ġl anguages", + "ic ine", + "ĠF amily", + "f unctions", + "E F", + "Ġnut rient", + "Ġanalyz ing", + "ines cence", + "Ġthrom b", + "Ġk it", + "Ġmamm alian", + "op totic", + "Ġequip ped", + "on a", + "Ġqu e", + "Ġc ame", + "Ġsimpl ified", + "Ġdec ays", + "Ġpass ive", + "Ġdele tion", + "Ġobtain ing", + "Ġmixt ures", + "Ġprim ers", + "ĠP sy", + "os c", + "om ent", + "Ġchlor ide", + "ĠPa ul", + "st art", + "int estinal", + "hel ium", + "ar th", + "od ot", + "Ġf its", + "Ġsqu ares", + "ĠCar di", + "ak a", + "rib uted", + "Ġinequ alities", + "omet hing", + "hed ral", + "ĠF uture", + "Ġgl i", + "Ġmetall ic", + "Ġfac ilities", + "Ġob st", + "poss ible", + "Ġz ones", + "uc id", + "Ġdr ift", + "d epend", + "val ued", + "Ġn ons", + "Ġworld wide", + "Ġtr ust", + "Ġso le", + "ĠLe vel", + "ĠS ha", + "Ġregard less", + "Ġspectrom etry", + "duc tor", + "le uk", + "Ġsk ills", + "Ġincorpor ated", + "Ġlear ned", + "Ġ ure", + "Ġext inc", + "OD U", + "Ġgrain s", + "ater n", + "ĠInd ex", + "com put", + "u a", + "Ġcont amination", + "ĠA ff", + "un ing", + "Ġas ymmetric", + "Ġopen ing", + "Ġb at", + "Ġag ree", + "IT Y", + "ĠChang es", + "organ ic", + "ĠR ay", + "ĠH and", + "n i", + "in ic", + "Ġr isks", + "Ġst ock", + "Ġnec k", + "Ġvol umes", + "ĠP rac", + "Ġincreasing ly", + "S c", + "os es", + "GF P", + "Ġass ignment", + "ĠF ed", + "osp it", + "Ġoverex pression", + "Ġm aster", + "Ġo pt", + "il er", + "in variant", + "Ġconver ges", + "Sim ilar", + "n y", + "Ġst ore", + "Ġelev ation", + "Ġco al", + "he t", + "it em", + "PL C", + "oh ist", + "G en", + "ĠC hem", + "ĠC ost", + "p air", + "Ġnumer ically", + "Ġpre ference", + "ĠN ucle", + "ĠB D", + "T I", + "ĠH yp", + "ro y", + "T e", + "ĠF in", + "Ġclaim s", + "ib ilities", + "Ġlar vae", + "im a", + "emb ly", + "Ġc it", + "L L", + "Ġsil ica", + "ĠV I", + "Ġreach ing", + "O f", + "ĠAustr alian", + "t ub", + "w orld", + "on i", + "ĠF P", + "Ġbrief ly", + "ĠDes cription", + "Î ¶", + "ch arg", + "Ġc is", + "ĠC at", + "Ġrec ip", + "Ġemerg ency", + "Ġst rand", + "Ġreal ized", + "pos ing", + "ot ope", + "Ġmaintain ing", + "ĠCh rist", + "Ġcre ating", + "Ġembry os", + "Ġs keletal", + "Ġag es", + "rep resent", + "C r", + "Ġestim ating", + "Ġre ar", + "ĠY u", + "ĠP i", + "m g", + "Ġflo at", + "ĠR oy", + "p us", + "Ġch ick", + "Ġmicrobi ota", + "vas ive", + "ĠB ern", + "ĠPat tern", + "l ines", + "Ġfl ood", + "ĠL ou", + "ilit ary", + "ros ion", + "Ġsurve ys", + "F I", + "ia e", + "Ġse arc", + "m ol", + "Ġt itle", + "ĠMach ine", + "Ġcirc uits", + "ĠNum ber", + "z i", + "ĠB MI", + "Ġautom ated", + "plic ate", + "ĠL PS", + "Ġelectro chemical", + "Ġwebs ite", + "Ġanis otropy", + "Ġr ings", + "Ġin nov", + "b its", + "w in", + "ĠN AD", + "Acc ording", + "ĠCon n", + "ure us", + "ĠFe ature", + "ĠIn stead", + "C omp", + "it udes", + "M o", + "Ġsc ope", + "tif ication", + "ĠI S", + "ĠNe ut", + "Ġreg ulating", + "c oding", + "Ġrow s", + "h l", + "ĠK n", + "ist or", + "ampions hip", + "Ġpromin ent", + "Ġr s", + "um atic", + "A m", + "Ġdifferenti ally", + "ug in", + "Ġadv ance", + "ph ys", + "Ġsh aring", + "Ġar t", + "v acy", + "ti tions", + "Ġst yle", + "Fig ures", + "Ġg lu", + "Ġvacc ination", + "ĠOp tical", + "flu id", + "ĠF re", + "Ġgradi ents", + "op hyl", + "ĠP ubl", + "Ġacc retion", + "Ġâ̲ â̲", + "ress ing", + "Ġtrans mitted", + "Ġnerv ous", + "um ar", + "Ġreview s", + "Ġgen otypes", + "low er", + "ĠE V", + "Ġcont ract", + "ati bility", + "Ġchild hood", + "Ġon c", + "Ġbi ofil", + "Ġaut ophagy", + "Ġads orb", + "ĠSup port", + "Ġlig ands", + "p ower", + "rec tional", + "ĠR ap", + "sim ilar", + "Ġinf arc", + "Ġelectro ly", + "Ġinc ome", + "ar ity", + "ĠA v", + "er ic", + "Ġclin ically", + "un ch", + "Ġattrib ute", + "Ġcomm and", + "rib utions", + "Ġgly c", + "Ġtranscri pts", + "ogram s", + "Ġassess ing", + "F O", + "script style", + "j i", + "ric k", + "en vironment", + "Ġlaw s", + "Ġnorm ally", + "Ġdeple tion", + "ĠR O", + "Ġenc oded", + "h ma", + "Ġbran ches", + "Ġarg s", + "oun ger", + "or ge", + "um ps", + "Ġview ed", + "Ġult r", + "R R", + "uls ion", + "ĠH or", + "Ġf ro", + "ĠMeasure ment", + "x x", + "erm an", + "ĠO nce", + "Ġorient ed", + "ĠP oint", + "Ġto wn", + "Ġformul as", + "S Y", + "ĠA M", + "Ġconsider ations", + "ĠT C", + "ĠK it", + "Ġact in", + "Ġplas mid", + "Ġhistor ical", + "Ġd ye", + "Ġhe ur", + "ĠLe ague", + "ĠM ad", + "Ġgra ft", + "Ġsil ver", + "O ver", + "ĠC os", + "ograph ical", + "Ġprecurs or", + "r us", + "Ġregard ed", + "ĠH am", + "f unctional", + "iv eness", + "ffici ency", + "ig ene", + "oc ol", + "Ġcum ulative", + "Ġse asonal", + "Ġm u", + "ĠB an", + "omy cin", + "Ġb ool", + "ĠM ag", + "ĠAn al", + "enti a", + "a ign", + "Ġfoot ball", + "act ing", + "Ġreturn s", + "ĠT om", + "sh aped", + "it ance", + "ĠExperim ent", + "ĠO S", + "Ġabs ent", + "ran ial", + "Ġtherap ies", + "O p", + "o unced", + "AT E", + "Val ue", + "g reen", + "Ġveget ation", + "D s", + "Ġinc om", + "à §", + "Ġm arrow", + "ĠCo uncil", + "Ġinv est", + "Ġcl ub", + "T rans", + "dev ice", + "Ġv ibration", + "ĠX u", + "//// ////", + "ĠH en", + "vi er", + "Ġanalog ous", + "Ġd elta", + "Ġsal ine", + "Ġrequ iring", + "Ġneur on", + "o o", + "ĠQ uality", + "Ġte ac", + "ĠE c", + "L i", + "Ġpubl ication", + "ĠPhys ics", + "Ġp pm", + "th ase", + "Ġcre ation", + "ĠA ge", + "Ġbelong ing", + "Ġion ic", + "ĠS I", + "u ating", + "end if", + "ĠC our", + "Ð °", + "Ġd ots", + "Ġe ast", + "ar com", + "Ġâ ĩ", + "Ġr ights", + "ess ions", + "Ġvers ions", + "ĠF ree", + "ĠSt ress", + "Ġsed iments", + "Ġm itig", + "Ġb ow", + "ĠAc t", + "ĠCar bon", + "t here", + "te en", + "Ġphen otypes", + "Ġne arest", + "ĠPot ential", + "Ġde form", + "Ġreflec ts", + "Ġpart ners", + "Ġan est", + "Ġad vers", + "ĠF actor", + "Ġconven ient", + "ul os", + "ĠP ur", + "ĠM er", + "Ġfl ag", + "Ġtri ang", + "Ġseed s", + "Ġf if", + "ob il", + "ĠC K", + "men tioned", + "Ġv apor", + "og ue", + "Ġpredic tor", + "O ut", + "Ġcomple tion", + "ĠS eg", + "Ġdiff use", + "Ġra ised", + "Ġco ordination", + "Ġsyn aptic", + "ĠB or", + "ĠB ol", + "Ġpolymer ase", + "Ġwhe at", + "Ġinser tion", + "Ġes c", + "ĠW al", + "Ġdist al", + "transfer ase", + "Ġinter faces", + "Ġins u", + "Ġpoor ly", + "Ġa ureus", + "Ġben z", + "Ġun iverse", + "ĠInter action", + "ĠF rame", + "ĠIm aging", + "Ġexpl oration", + "ĠEngine ering", + "ĠB esides", + "ti a", + "Ġen um", + "an ine", + "Ġto t", + "ĠE duc", + "Ġderiv ation", + "Ar ray", + "yl oid", + "ĠAr ch", + "is en", + "ac ity", + "ak ers", + "Ġshe et", + "ĠE st", + "Ġwe ar", + "Ġ eryth", + "EC K", + "hem atics", + "Ġarter ial", + "cript style", + "scripts criptstyle", + "echan ical", + "Ġparticip ation", + "c her", + "ur ance", + "ĠF R", + "ĠC V", + "Ġcomplement ary", + "ain e", + "empt y", + "Ġdig es", + "Ġexpon ent", + "Ġsim ulate", + "U E", + "Ġantib iotics", + "ĠUn ivers", + "Ġpath ology", + "ther mal", + "p a", + "Ġstress es", + "ĠLabor atory", + "N ode", + "Ġle ave", + "ash ing", + "Ġdisc re", + "Ġsusp ension", + "ree k", + "Ġschedul ing", + "ĠD A", + "ary n", + "ĠNa Cl", + "st rain", + "ST R", + "ĠC ong", + "ol f", + "Ġcal ibr", + "ĠOptim al", + "Ġ ó", + "G l", + "ĠR h", + "Ġdiffic ulties", + "Ġvess els", + "Ġas ymmetry", + "Ġco herence", + "ĠTaxon omy", + "Ġp ed", + "ĠH ouse", + "tit udes", + "ĠF ar", + "O Y", + "Ġconcentr ated", + "Ġsign alling", + "Ġfung al", + "Ġconsist ently", + "Ġenh ances", + "Ġfore cast", + "Ġc ubic", + "ĠE P", + "Ġparticip ate", + "ĠPl ant", + "r isk", + "A nd", + "ad ic", + "of lu", + "Ġsper m", + "ĠCh ris", + "N D", + "col on", + "Ġf aces", + "Ġtub erculosis", + "ryst al", + "flo or", + "up s", + "Ġg ray", + "ĠP ublic", + "t ensor", + "Ġrig id", + "Ġeas tern", + "ĠItal y", + "Ġsign atures", + "Ġshall ow", + "ó n", + "ĠC e", + "Ġpro jects", + "Ġro uting", + "Ġpredic ts", + "ĠFe atures", + "ĠDist rict", + "Ġcar rying", + "ĉ ĠĠĠĠ", + "ĠT O", + "H M", + "d ings", + "Ġre normal", + "Ġb ring", + "p in", + "al ed", + "Ġcloud s", + "n ames", + "ox in", + "Ġperp endicular", + "W T", + "ers hip", + "Ġrec on", + "Ġwork ed", + "ĠâĢ «", + "rastruct ure", + "Ġpo inted", + "E V", + "ĠT aylor", + "Ġhep atitis", + "Ġorb its", + "ĠF actors", + "c ellular", + "Ġf ocal", + "Ġbo ost", + "Ġmic rowave", + "ĠPro ject", + "B F", + "Ġpoli tical", + "Ġsupplement ed", + "Ġillustr ates", + "Ġide as", + "ĠDr ug", + "ob ile", + "ĠH O", + "Ġrobust ness", + "ros ine", + "ĠN ormal", + "Ġstim ulated", + "Ġimped ance", + "fort unately", + "zym e", + "Ġbar riers", + "act ory", + "lear ly", + "Ġpre print", + "sens itive", + "Ġturb ulent", + "th ing", + "Ġbo ard", + "Ġp it", + "Ġintegr ity", + "Ġrot ating", + "ud a", + "Ġv enti", + "ĠSN Ps", + "Ġcorrespond ence", + "Ġvisual ization", + "av ail", + "Ġbe ams", + "ĠCont inu", + "Ġpers istent", + "Ġb ath", + "Ġmi RNAs", + "Ġcust om", + "Ġord inary", + "Ġgener ators", + "Ġb ridge", + "Ġdom in", + "am y", + "Ġlo oking", + "t able", + "F alse", + "Ġsoil s", + "Ġmat ches", + "Ġprog ressive", + "st ates", + "ĠSh ort", + "Ġco res", + "Ġintro ducing", + "Ġar rest", + "Ġtext ure", + "Ġdors al", + "Ġd rain", + "iz oph", + "ĠQ ue", + "à ±", + "dis c", + "Ind ex", + "Ġext ensively", + "Ġplastic ity", + "Ġre ally", + "ĠEr ror", + "Ġsugg es", + "Ġconsequ ently", + "Ġperform s", + "lik ely", + "ive red", + "Ġtherm odynamic", + "Ġk er", + "Ġacet ate", + "Ġg ets", + "leq slant", + "Ġpredict ors", + "ĠSw ed", + "n an", + "he ter", + "Ġanomal y", + "Ġoper ational", + "Ġret rospective", + "Ġt ends", + "ad en", + "Ġb order", + "Ġmet hanol", + "ĠEn ter", + "Ġcoll apse", + "Ġpurch ased", + "D a", + "ĠH T", + "Ġf ulf", + "Ġcr ust", + "st one", + "Ġpen al", + "Ġtun n", + "ĠTem perature", + "Ġpot ent", + "lec ule", + "Ġco vers", + "Ġbatter y", + "Ġbe g", + "Ġor gans", + "ĠTh omas", + "Ġsol ub", + "oc rine", + "ĠSp in", + "Ġinterest s", + "d oc", + "Ġundergo ing", + "u i", + "Ġin herent", + "Ġintegr als", + "ira ble", + "as hi", + "Ġreg eneration", + "Ġinf lation", + "man if", + "ĠRec ognition", + "Ġdisplay s", + "An other", + "Ġcont amin", + "j unction", + "Ġcop ies", + "MR I", + "Ġvehic les", + "G et", + "Ġper haps", + "Ġw est", + "Ġint ensive", + "Ġs omething", + "Ġhypox ia", + "Ġcou plings", + "Ġfeas ibility", + "az ine", + "un ic", + "in er", + "ĠI T", + "Ġdist rict", + "ĠJ ames", + "e val", + "Ġplace bo", + "a que", + "Ġel ucid", + "ĠJac ob", + "Ġcoun ting", + "Ġflex ibility", + "Ġper man", + "Ġadv ances", + "ul ph", + "Ġent anglement", + "Ġinte gers", + "Ġfocus ing", + "k ov", + "Ġh ospit", + "Ġap plies", + "Ġc ot", + "S m", + "ass ium", + "Ġdocument ed", + "Ġload ed", + "Ġre ly", + "Ġinf ectious", + "Ġprob es", + "Ġhighlight ed", + "Ġp ediatric", + "Ġwe ather", + "Ġman ual", + "Ġc ation", + "Ġinterp olation", + "ĠSte p", + "ĠK al", + "D H", + "d b", + "izoph ren", + "ad er", + "car b", + "Ġag on", + "orph ous", + "t ors", + "at z", + "Ġb if", + "Ġcharg es", + "ĠAg ain", + "Ġb ron", + "ĠG over", + "Ġmin ing", + "a ver", + "Ġearth qu", + "Ġview s", + "Ġsc ene", + "par ameters", + "Ġbro ken", + "T est", + "ĠS um", + "ĠP rom", + "Î Ľ", + "Ġcut off", + "Ġb irds", + "Ġar ising", + "ĠA I", + "ĠC E", + "Ġpron ounced", + "asp ase", + "Ġint ended", + "Ġaff ine", + "Ġur ine", + "Ġbelie ved", + "ĠPrim ary", + "ĠCon f", + "Ġab dominal", + "sp in", + "un iform", + "ĠSt ochastic", + "ĠPro v", + "Ġmi RNA", + "ĠB ell", + "B O", + "ĠSoft ware", + "ĠT s", + "ut ri", + "ick ing", + "i en", + "Ġmic ros", + "ĠN R", + "Ġleuk emia", + "Ġsuper nat", + "f amily", + "Ġall oys", + "ĠP ET", + "ĠA bs", + "ĠG A", + "ĠQu antitative", + "L o", + "Ġis land", + "sec ond", + "pec tives", + "Ġlat ency", + "ang i", + "Ġfl ight", + "ĠE uclidean", + "em y", + "ĠBl ood", + "leuk in", + "L T", + "en h", + "Ġs we", + "Ġunit ary", + "ĠRep ublic", + "Ġstructure d", + "ĠS en", + "M n", + "cent ric", + "Ġtrans genic", + "Ġhelp ful", + "py x", + "Ġhome ostasis", + "N a", + "Ġpass ed", + "Ġe yes", + "Ġab stract", + "ul se", + "Ġmir ror", + "Ġregul ator", + "Ġmur ine", + "load ed", + "Ġmod ular", + "Ġlandsc ape", + "ic ks", + "Ġs now", + "Ġb ovine", + "ell i", + "Ġdatab ases", + "Ġout break", + "l arg", + "ĠR un", + "B E", + "Ġsur prising", + "Ġaccept able", + "Ġrot ational", + "p g", + "F E", + "w ik", + "Ġy ounger", + "ash ion", + "Ġmic roscopic", + "reg ation", + "Ġfib r", + "ĠPl an", + "Ġha pl", + "Ġmanif olds", + "Ġout per", + "Ġcho osing", + "e per", + "Ġke V", + "ĠT yp", + "p read", + "nt z", + "ĠRe port", + "ĠMat rix", + "Ġint u", + "Ġproper ly", + "og ly", + "oscop ic", + "ĠA MP", + "ĠB M", + "Ġelement ary", + "kele ton", + "Ġsyn thase", + "Ġion ization", + "b es", + "oph age", + "duc es", + "acc o", + "Ġprot ect", + "ĠCo ul", + "Ġsp ent", + "Ġm and", + "Ġh ind", + "flu or", + "ĠG ood", + "Ġdo ing", + "Ob ject", + "duc ts", + "o yl", + "chi atric", + "Ġo v", + "c el", + "Ġb ases", + "Ġmitochond ria", + "p ted", + "art z", + "Ġb rown", + "Ġequ als", + "ti ble", + "Ġopportun ity", + "az ol", + "Ġoff icial", + "ail ed", + "Ġur inary", + "ĠH an", + "B e", + "res ult", + "un its", + "Ġb ad", + "ĠSt ring", + "iz able", + "con dition", + "ĠElect ron", + "immun e", + "ĠM E", + "ha o", + "Î £", + "ĠM AT", + "Ġad opt", + "Ġel ic", + "Ġsh r", + "Ġproxim al", + "F D", + "ĠS S", + "Ġentire ly", + "es ium", + "ĠE EG", + "Ġpa ired", + "ĠT P", + "ĠD O", + "NA L", + "ides pread", + "Ġmov es", + "s ite", + "Ġra in", + "Ġl ap", + "ĠF u", + "ĠM eta", + "irc raft", + "Ġmagne tization", + "oper ation", + "Ġpro st", + "Ste p", + "Ġsubgroup s", + "ĠS outhern", + "Ġat he", + "lu or", + "ĠTaxon omic", + "ĠE instein", + "Ġr ace", + "ĠK en", + "Ġattem pts", + "Ġcos mic", + "ĠD op", + "Ġfix ation", + "Ġremov ing", + "B T", + "Ġlim b", + "Ġal ign", + "Ġd ried", + "d u", + "Ġput ative", + "uc cess", + "per t", + "Ġslow ly", + "al so", + "ol ip", + "Ġcl ient", + "Ġbas in", + "Ġsuscepti ble", + "Ġcom ing", + "ns on", + "ĠN GC", + "ass ert", + "Ġtens ile", + "Ġar ises", + "cut aneous", + "Ġc aro", + "B i", + "Ġdiscuss ions", + "Ġabnormal ities", + "Ġpoll ution", + "ĠA x", + "Ġload s", + "D o", + "ia o", + "Ġmed ication", + "Ġint act", + "ĠC X", + "Ġbre eding", + "ĠUn ion", + "ĠB at", + "ĠPar ticipants", + "ĠReg ulation", + "Ġcontrad iction", + "Ġint ensities", + "ence phal", + "ri le", + "ĠT LR", + "Ġred und", + "Ġpers ons", + "ĠAr c", + "sol id", + "l aw", + "Res ults", + "il ic", + "z one", + "ocyt osis", + "Ġtri angle", + "ST M", + "ĠV irus", + "Ġa id", + "so ft", + "Ġso on", + "exp ected", + "Ġan ch", + "ĠM u", + "ĠS r", + "ĠL O", + "Ġc ry", + "Ġup stream", + "ox ic", + "math it", + "ĠK le", + "Ġis otropic", + "Ġspati ally", + "ĠH ard", + "Ġext r", + "b as", + "e or", + "iv il", + "y an", + "Ġshif ted", + "Ġbi opsy", + "Ġfe el", + "gl ut", + "S ize", + "Ġ erg", + "ĠT er", + "Ġdeath s", + "bor ne", + "Ġrel ativistic", + "ĠV EGF", + "at ab", + "s pring", + "res tim", + "ĠS earch", + "yp henyl", + "ec al", + "ur c", + "Ġl amin", + "Ġser ial", + "l as", + "ĠPro duction", + "Ġsoci o", + "Ġmod ify", + "ĠServ ice", + "Ġb ary", + "Ġradi ative", + "big l", + "Ġparad igm", + "pati ent", + "Ġsp p", + "ph one", + "Ġ î", + "Ġro cks", + "ĠMart in", + "m n", + "Ġflu ids", + "ĠIN TR", + "od s", + "Ġdiv is", + "Cons ider", + "comp onent", + "Ġanomal ies", + "Ġk nee", + "ĠRelations hip", + "a ud", + "Ġover night", + "Ġra inf", + "Ġanne aling", + "Ġtre ating", + "Ġco arse", + "Mod el", + "Ġp ose", + "Ġocc as", + "ĠWilli am", + "o or", + "Ġadjust ment", + "ĠF unctions", + "im eter", + "Ġdet ectors", + "Ġinstit utional", + "Ġthrough put", + "iv idual", + "Ġenti ties", + "Ġprolong ed", + "Ġsh ip", + "Ġpres erved", + "ODU CTION", + "Ġlog istic", + "ĠPred iction", + "ti zed", + "ĠOr ig", + "ĠH em", + "onom ous", + "######## ########", + "ĠGen eration", + "b ottom", + "ĠK now", + "cl inical", + "Ġtra uma", + "Ġiter ative", + "Ġfac ility", + "ron t", + "ĠB us", + "Ġret inal", + "Ġcon duction", + "Ġcheck ed", + "Ġcall s", + "olog ists", + "C ON", + "ĠSc iences", + "Ġnon zero", + "Ġb rack", + "Ġmel ting", + "Ġas c", + "Ġmen tion", + "ĠB L", + "Ġver ification", + "uk ary", + "ĠSp atial", + "ĠG ram", + "Ġpl aces", + "Ġnec rosis", + "ĠChild ren", + "Ġdel ivered", + "Ġres ection", + "Ġdetermin istic", + "S ection", + "Ġmul tim", + "D F", + "Ġhypot heses", + "Ġra ise", + "Ġse ismic", + "Ġl am", + "ĠH CC", + "big r", + "Ġhe aling", + "is y", + "Ġoptim ize", + "obacter ium", + "ed y", + "Ġtr uth", + "Ġspace time", + "Ġchrom atin", + "Ġdom estic", + "Ġrec ru", + "ĠJ ose", + "ĠTherm al", + "Ġenvelop e", + "v able", + "Ġinc ons", + "Ġn od", + "Ð ¸", + "Ġcontrib uting", + "Ġguarant ee", + "ĠP hen", + "Ġra b", + "M an", + "Ġsurve illance", + "Ġth ings", + "Ġpre v", + "ĠNon linear", + "Ġg aps", + "ay a", + "ĠC ri", + "Ġcrystall ine", + "str ict", + "Ġcomput ations", + "Ġun able", + "h abil", + "um ina", + "Ġpromot ing", + "eg rad", + "Ġreg ister", + "Ġcross ing", + "ul ators", + "ĠL anguage", + "ĠA A", + "Ġin er", + "ĠL V", + "os an", + "Ġcoast al", + "Ġbi od", + "ĠM OD", + "Ġneighb our", + "Ġpredominant ly", + "ĠNew ton", + "ĠStr ateg", + "be ing", + "Ġ ì", + "Ġcap abilities", + "Ġun less", + "form al", + "Ġvess el", + "b matrix", + "ES S", + "Ġrainf all", + "à £", + "Ġpre par", + "ax ial", + "Ġd ental", + "ĠPro te", + "Ġwor se", + "d oped", + "hen tic", + "Ġvalid ate", + "Z n", + "Ġspec ification", + "s i", + "ĠAn g", + "Ġtub es", + "ul ic", + "ĠAn y", + "ĠM ap", + "Ġfabric ated", + "Ġfor ced", + "ĠWil son", + "ol ysis", + "ĠW ave", + "ĠC ast", + "Ġast hma", + "Ġper i", + "ĠC yt", + "ast y", + "Ġsk y", + "rup t", + "D ec", + "Ġmelan oma", + "P ER", + "Ġcontinu ity", + "B ox", + "s ystem", + "Ġn avig", + "Ġcirc ulating", + "Ġcolon y", + "less sim", + "ad ium", + "Ġtet ra", + "Ġacc ounts", + "Ġpresent ing", + "ĠL ik", + "Ġres is", + "Ġdamp ing", + "ĠG ly", + "ĠNeu ro", + "us er", + "Ġcap ital", + "ur ate", + "ĠM W", + "Ġcorrel ates", + "ĠG ib", + "Ġhapp ens", + "Ġg all", + "ĠWith in", + "Ġcomb ine", + "Ġsin us", + "ĠK in", + "**************** ****************", + "M ap", + "Ġmat uration", + "Ġblock ing", + "ĠClo ud", + "Ġcont acts", + "Ġs ac", + "AL L", + "ĠR ab", + "z z", + "ut ch", + "Ġcar riers", + "ĠSN R", + "er b", + "Ġprot ected", + "rack ing", + "radi ent", + "Ġattrac tive", + "Ġl ag", + "Ġop in", + "ĠG i", + "Ġdef ense", + "Ġtun ing", + "Ġelect roph", + "Ġgreat est", + "Ġreconstruc ted", + "ĠPop ulation", + "M AP", + "Ġw rote", + "AN D", + "ec onomic", + "ĠMich ael", + "ĠBl ock", + "Ġv o", + "op rop", + "Ġprof iling", + "oot st", + "ĠAs ian", + "Ġoscill ation", + "ĠâĨ IJ", + "U D", + "Ġsign ed", + "ĠE uler", + "ĠCompar ative", + "ĠW here", + "ĠJ ack", + "Ġpass ing", + "Ġvill age", + "Ġa u", + "ĠNor thern", + "ess age", + "m atic", + "Ġaff ili", + "ĠF ac", + "Ġoverl apping", + "she ll", + "Ġobst ac", + "Ġbec oming", + "enti ve", + "Ġeas ier", + "init ely", + "Ġcent ered", + "Ġac ademic", + "ann els", + "Ġir regular", + "Ġproj ections", + "Ġpro position", + "Ġdiscrim ination", + "Ġrem od", + "Ġsh oot", + "mon th", + "ess or", + "Ġdiff ers", + "ĠT V", + "ĠZ hou", + "Ġin her", + "Ġmach ines", + "Ġm ell", + "Ġconjug ate", + "Ġc oc", + "un a", + "an yl", + "Ġoff ic", + "Ġopportun ities", + "Ġve in", + "ĠCharacter istics", + "Ġpath ogenic", + "OY SA", + "ĠPark inson", + "ĠGal actic", + "FF FA", + "ys es", + "UH FFFA", + "UHFFFA OYSA", + "act in", + "Ġun us", + "hes ia", + "ace u", + "ad ow", + "os ide", + "Ġgly cos", + "Ġdil uted", + "ĠS ource", + "ol ated", + "arm aceu", + "ant om", + "Ġmus c", + "Ġaver aging", + "Ġvis it", + "Ġc atch", + "Ġsatisf action", + "Ġv on", + "val id", + "Ġyield ed", + "Ġpack ets", + "Ġreson ant", + "p ret", + "ĠG FP", + "Ġcut ting", + "Ġreplac ing", + "az e", + "P a", + "Ġto day", + "Ġdec ided", + "il ateral", + "im ate", + "l ings", + "ĠRob ust", + "ĠA st", + "od ynamics", + "Ġlack ing", + "izophren ia", + "Ġcont raction", + "um ann", + "ĠS ample", + "Ġdi amond", + "met hod", + "T OR", + "Ġcom ments", + "se y", + "Ġmanufact uring", + "ĠD a", + "N R", + "Ġoper ated", + "r ates", + "Ġextinc tion", + "u vant", + "ĠF inite", + "Ġlymph ocytes", + "b ro", + "om ology", + "Ġinstr uments", + "b ec", + "og le", + "Ġqu oti", + "Ġhyper bolic", + "Ġtr im", + "Ġp ap", + "atur ated", + "h aus", + "Ġs essions", + "Ġcamp aign", + "Ġvari eties", + "Ġpro jected", + "Ġr id", + "b one", + "Ġanc est", + "ĠE T", + "ma il", + "ĠTrans port", + "// /", + "ĠAn n", + "Ġcompos itions", + "ĠINTR ODUCTION", + "ĠâĪĴ âĨĴ", + "Ġwhen ever", + "ĠL ip", + "par ts", + "Ġis omorphic", + "Ġsulf ate", + "Ġh op", + "Ġg on", + "ĠOb ject", + "Ġpip eline", + "Ġm a", + "ĠG as", + "Ġtend ency", + "ob ject", + "Ġparamet ric", + "ĠRet urn", + "Ġd war", + "Ġpress ures", + "ĠBi os", + "Ġmulti plication", + "Ġdim in", + "Ġcol ors", + "ĠTr ue", + "M ax", + "ĠD epend", + "Ġpair wise", + "Ġl ake", + "Ġhierarch y", + "Ġthresh olds", + "ĠAdap tive", + "m aking", + "Ġcataly sts", + "ip al", + "Ġeg gs", + "Ġw ire", + "ophyl l", + "ict or", + "label ed", + "Ġmus cles", + "ĠUnder standing", + "Ġfib re", + "cont rolled", + "Ġinvari ance", + "Ġc ache", + "Ġbos on", + "Ġnear by", + "ĠW omen", + "ĠIn itial", + "Ġprob abilistic", + "Ġembry onic", + "ĠB etween", + "Ġcon jecture", + "i enti", + "t x", + "g ens", + "anc k", + "Ġg ir", + "ĠL ower", + "Ġhospit als", + "brid ge", + "Met hod", + "Ġthe ta", + "j a", + "Ġconcept ual", + "Ġcol le", + "ĠS af", + "d ic", + "Ġp et", + "Ġprim er", + "ĠO h", + "Ġun treated", + "long rightarrow", + "Ġl icense", + "Ġhel ps", + "Ġcle avage", + "Ġampl ified", + "Ð µ", + "Ġaccess ible", + "ĠSe lection", + "ĠLore ntz", + "P y", + "Ġpolar ized", + "ĠST AT", + "mit t", + "U p", + "Ġon going", + "Ġne ph", + "e fficient", + "ac tiv", + "ĠR R", + "Ġfunction ing", + "ot in", + "Ġl ists", + "Ġformal ism", + "Ġoscill ator", + "Ġgastro intestinal", + "ootst rap", + "ĠAs ia", + "ĠD ay", + "Ġcomp eting", + "ival ent", + "Ġbl adder", + "Ġh it", + "Ġapproxim ations", + "ĠE g", + "ĠCl ust", + "Ġrel ies", + "N E", + "cop ro", + "Ġb ank", + "Ġintegr ating", + "ĠH ear", + "Ġiniti ated", + "ac ryl", + "ĠB H", + "rac ted", + "y c", + "ĠR a", + "Ġremark able", + "Ġ Ë", + "ten ess", + "Ġemploy ing", + "ste ine", + "Ġï£ Ń", + "Ġtransf ected", + "Ġinj uries", + "ĠB rief", + "Ġw idespread", + "ĠA K", + "IV E", + "Ġh arm", + "Ġpo le", + "Ġanis otropic", + "at en", + "gen e", + "iv ariate", + "In ter", + "duct ors", + "Ġaccom pl", + "oglob in", + "c ong", + "Ġqu eries", + "escop e", + "ĠH op", + "Ġenti ty", + "Ġoff ered", + "St ate", + "ĠExperim ents", + "ann er", + "ĠW ood", + "ard ed", + "ag on", + "Ġfibrobl asts", + "Ġnan os", + "Ġper oxid", + "Ġev id", + "Ġï£ ¸", + "Ġre tained", + "os qu", + "Ġle aving", + "Ġf ashion", + "Ġn M", + "Ġmut ual", + "appro xim", + "Ġwalk ing", + "Ġim possible", + "Ġdemonstr ating", + "Ġde gener", + "ĠA V", + "Ġcont rary", + "us tion", + "ocl onal", + "A nal", + "Ġperform ances", + "Ġcomp rom", + "orm s", + "Ġbud get", + "ĠH aw", + "Ġarth ritis", + "ob j", + "no ise", + "Ti O", + "och rome", + "Ġge odes", + "be an", + "Ġselec tivity", + "ĠF ood", + "ugh ter", + "Ġpermut ation", + "ĠR P", + "os al", + "Ġadi p", + "armaceu tical", + "w hen", + "ĠT ext", + "we ek", + "Ġbond ing", + "ar b", + "oc or", + "Ġv oc", + "Ġup regulated", + "Ġneighb ors", + "Ġtra it", + "Ġthe ore", + "Ġc f", + "ĠB erg", + "ĠL A", + "Ġl as", + "un te", + "cept ual", + "AS E", + "Ġischem ic", + "Ġb ending", + "d ataset", + "Ġkeep ing", + "Ġar rows", + "Ġsubst ances", + "Ġn s", + "Ġext ending", + "ĠR u", + "Ġsupplement ation", + "cri tical", + "ĠT raining", + "bul let", + "Ġpar a", + "ta il", + "ĠRef erence", + "Ġï£ ¶", + "Ġdissip ation", + "Ġaux iliary", + "ĠCy cl", + "s tim", + "Ġdil ution", + "bu f", + "ĠM iss", + "Ġul timately", + "Ġpow ers", + "Ġst ands", + "ust ed", + "ĠO H", + "habil itation", + "an aly", + "ĠB ra", + "ad ding", + "Cor ollary", + "Ġd rought", + "qu ality", + "Ġstandard ized", + "ĠJ e", + "ĠAc id", + "Ġm ism", + "ĠCh rom", + "d raw", + "ĠBi om", + "ĠSt ability", + "Further more", + "l ast", + "v ic", + "Ġab st", + "Ġb is", + "Ġemerg ence", + "Ġg iant", + "D e", + "ĠS amples", + "AB A", + "n as", + "Ġon t", + "Ġev ap", + "le vant", + "m ain", + "ĠR od", + "Ġc ros", + "it ary", + "Ġdo ub", + "r ö", + "igene tic", + "Ġincom plete", + "dep th", + "ï ģ", + "Ġsatur ated", + "Ġaeros ol", + "As sum", + "Ġimmun os", + "Ġlip ids", + "itone al", + "Ġbe aring", + "ĠIm plications", + "Ġsustain ed", + "Ġcompe titive", + "Ġmo tivation", + "Ġdisturb ance", + "rystall ine", + "Ġtax a", + "Ġdem entia", + "Ġconcer ned", + "PI O", + "hom ogeneous", + "ĠE v", + "ĠGe orge", + "ĠAlgorithm s", + "ick el", + "us ively", + "Ġcor ner", + "ĠR est", + "Ġinf inity", + "ĠTrans form", + "hen g", + "Ġneuro de", + "ol im", + "Í ij", + "Ġsk ew", + "ĠB S", + "sc ore", + "Y PE", + "em an", + "el le", + "ĠCor relation", + "Ġcult ural", + "oph osph", + "Ġatten uation", + "Ġaggreg ate", + "Ġam big", + "Ġanomal ous", + "Ġt ors", + "Ġplan et", + "ĠN Ps", + "h r", + "ĠDiv ision", + "ĠEduc ation", + "lec tic", + "Ġb rought", + "ĠM orph", + "Ġplan es", + "Ġsug ar", + "Ġdend ritic", + "Ġcont our", + "Ġcylind er", + "p ost", + "Ġw ent", + "R L", + "Ġad mission", + "MS E", + "I X", + "Ġdis joint", + "Ġannot ation", + "Ġis otope", + "Ġμ ν", + "Ġelim inate", + "Ġre actor", + "on ents", + "Ġreason ing", + "Ġm orbidity", + "Ġcor rosion", + "other mal", + "arc tic", + "ĠM B", + "ĠZ hao", + "Ġhist ological", + "Ġsuperconduc ting", + "at tered", + "Ġhouse hold", + "ĠPro p", + "Ġass er", + "he red", + "Ġte ams", + "Ġvan ishes", + "P re", + "am ents", + "Ġam orphous", + "ĠDeterm ination", + "miss ions", + "Ġover head", + "det erm", + "Ġutil izing", + "f a", + "ip olar", + "Ġform ulated", + "Ġext rap", + "gr id", + "Ġhum idity", + "ub er", + "t umor", + "ro us", + "Ġdistor tion", + "d ynamics", + "ĠL oss", + "Ġscal ed", + "Ġischem ia", + "Ġax es", + "Ġqu antit", + "n it", + "ĠReg ion", + "ain ed", + "Ġf ill", + "Ġbran ching", + "ĠT iss", + "c ross", + "Ġplate let", + "iffiff iffiff", + "ro ps", + "lu x", + "j oin", + "ur acy", + "ic ide", + "ĠLou is", + "Ġï£ «", + "Ġstr ings", + "ys et", + "Ġfac ial", + "ĠM MP", + "RE S", + "Ġhydro lysis", + "ĠCan adian", + "Ġpro jective", + "Ġsc atter", + "ur on", + "ĠPsy ch", + "com plex", + "ĠN am", + "Ġconc urrent", + "ION S", + "Ġth ous", + "Ġch ance", + "Ġplac ement", + "Ġaware ness", + "Ġt rib", + "ĠT ex", + "ĠTh ird", + "Ġlabel ing", + "cer ol", + "Ġs aw", + "ĠB and", + "ĠP ear", + "Ġpregn ant", + "ĠD own", + "pl atin", + "S eq", + "x e", + "ethyl ene", + "ĠHig her", + "Ġre ality", + "ur is", + "ĠP AR", + "l b", + "d ose", + "sh if", + "ili ar", + "t otal", + "S W", + "Ġval ve", + "nd er", + "Ð ½", + "am ous", + "Ġend omet", + "LI SA", + "Ġfract ures", + "Ġfil t", + "ro le", + "Ġmicro structure", + "ĠSN P", + "T ER", + "ĠZn O", + "ov ing", + "al i", + "ĠG M", + "unc t", + "Ġext ensions", + "exp ression", + "Ġesc ape", + "ĠM as", + "ĠSp anish", + "Ġflo or", + "ĠCom mon", + "otop y", + "plement ation", + "Ġr hyth", + "Ġserv es", + "y to", + "Ġwavelength s", + "empt yset", + "ĠH ill", + "n or", + "ĠElect ro", + "Ġde hydrogen", + "Ġwh om", + "im etric", + "ĠR oman", + "ĠV e", + "âī ¥", + "ĠK u", + "ĠTrans fer", + "Ä ĩ", + "ĠT F", + "b rain", + "copro tein", + "ĠG reat", + "av en", + "ĠInd ividual", + "ur i", + "Ġfung i", + "Ġpar am", + "pt on", + "s ymmetry", + "Ġloc k", + "me as", + "Ġha em", + "Ġh ip", + "As s", + "eng er", + "Ġpot assium", + "an al", + "ibr ary", + "Ġschool s", + "n atal", + "Ġalle les", + "ĠH LA", + "ox ygen", + "ĠC up", + "Ġpure ly", + "D O", + "Ġch ip", + "ô ı", + "C ar", + "s il", + "Ġun likely", + "cor respond", + "ĠD P", + "Ġint ense", + "Ġfor cing", + "ĠJ ournal", + "Ġar row", + "ocy an", + "Ġcul tiv", + "Ġbl ind", + "Ġselect ing", + "oc arcinoma", + "ran ce", + "Ġhydroph obic", + "clos ed", + "Ġens ures", + "Ġprom oted", + "Ġdetect able", + "rane an", + "Ġsched ule", + "Ġpart ly", + "Ġgl and", + "Ġco uple", + "ĠEm erg", + "Ġtrac es", + "p oly", + "Ġprote ase", + "ys tic", + "Ġdoc uments", + "pos itions", + "Ġdri ver", + "ti um", + "ĠC YP", + "cl ose", + "ĠRec ep", + "Ġper mit", + "Ġblock ed", + "Ġinvestig ating", + "ĠT umor", + "ĠB ig", + "Ġwave gu", + "Ġsubst ance", + "Ġweak er", + "ĠM ont", + "ro vers", + "ĠMex ico", + "p res", + "ĠAc ute", + "Ġmicro gl", + "ĠE S", + "itor ing", + "ĠSer ies", + "l ights", + "Ġhypot hesized", + "Ġconstruc ts", + "Ġfilt ration", + "Bl ack", + "Ġun changed", + "Ġobserv able", + "Ġra y", + "b etween", + "Ġï£ ¯", + "ĠPos ition", + "Ġth i", + "ĠSystem atic", + "Cl ass", + "k m", + "ĠT ak", + "Ġrespond ents", + "Ġinn ate", + "Ġan t", + "Ġconn ecting", + "R el", + "Ġmanip ulation", + "ĠN eg", + "N Ps", + "ĠDi ab", + "ĠAc tive", + "ĠG all", + "ĠCoul omb", + "Ġspac ing", + "ĠF lor", + "Ġconduct ance", + "Ġtrac ks", + "ĠZh u", + "weight ed", + "ro cy", + "Ġfat her", + "id ium", + "struct ured", + "ĠT el", + "Ġst rom", + "ith ub", + "cer tain", + "B ut", + "ĠAc cess", + "Ġprevent ing", + "rest rial", + "ĠCons idering", + "tr ue", + "Ġhost s", + "Ġwor st", + "ĠP d", + "gre di", + "Ġgly col", + "Ġst ory", + "osqu ito", + "par atus", + "Ġme eting", + "Ġepis ode", + "n c", + "ĠS and", + "Ġu int", + "ynam ical", + "ur t", + "Ġeduc ational", + "Ġfocus es", + "g t", + "ĠH S", + "Ġdeterm inant", + "Ġlith ium", + "ĠDig ital", + "Ġguid ance", + "Ġprior ity", + "Ġpar ty", + "or ial", + "T wo", + "ĠProblem s", + "Ġsem an", + "ĠCN N", + "ĠE pid", + "Ġplay ing", + "Ġelim ination", + "ĠS at", + "Ġobj ectives", + "p lectic", + "Ġcircum st", + "ĠG S", + "oc ellular", + "ot rans", + "Ġfind s", + "Ġa romatic", + "iz ers", + "Ġfavor able", + "st andard", + "ich lor", + "mod els", + "otyp ing", + "Ġstabil ization", + "Ġhand ling", + "Ġco ated", + "e ven", + "Ġlet ter", + "Z E", + "Ġultr ason", + "Ġf riend", + "Ġsens iti", + "Ġatt achment", + "Ġap art", + "Ġgre y", + "Ġa ircraft", + "Ġr RNA", + "Ġenabl ed", + "Ġbu ff", + "Ġred ox", + "ass isted", + "Ġgener ality", + "PS S", + "Ġe lection", + "resp onse", + "Ġded icated", + "Ġdem ographic", + "Ġim posed", + "ĠK ir", + "ĠRad io", + "ĠE LISA", + "ga e", + "Ġres c", + "ĠR ic", + "raph ic", + "Ġra il", + "Ġj ournal", + "ol er", + "W S", + "Ġincorpor ation", + "w ind", + "Ġaud itory", + "A E", + "t ask", + "Ġp c", + "w all", + "Ġapp rec", + "aterial s", + "Ġpart ner", + "Ġcollec tive", + "Ġsc oring", + "ĠFran k", + "Ġperman ent", + "ĠI ran", + "um ination", + "M ed", + "ĠHy brid", + "Ġphen otypic", + "Ġdisrup tion", + "vi olet", + "osp heric", + "Ġregim es", + "ĠCol or", + "ĠPati ent", + "Ġf ever", + "Ġn n", + "Ġvari ational", + "ke ys", + "Ġdis till", + "Ġspect roscopic", + "ĠAr chitect", + "ac ing", + "Ġpro ves", + "Ġver teb", + "ĠComput er", + "Ġexp ensive", + "Ġfro zen", + "arcom a", + "N K", + "Ġhist one", + "Ġpolymer ization", + "Ġto b", + "Ġturn ed", + "eff ective", + "ĠAut hor", + "AP I", + "Ġdec ade", + "ĠRo bert", + "Ex ample", + "over set", + "AB LE", + "ĠBehavi or", + "f eed", + "ĠT ai", + "Ġï£ º", + "Ġe gg", + "Ġc ath", + "au x", + "ĠJoh nson", + "Ġtor que", + "Ġpur ification", + "Wh ite", + "c ious", + "ĠS ong", + "Ġprecip it", + "resh old", + "Ġm ilitary", + "Ġconv ection", + "ĠM iddle", + "ĠW he", + "Ġ ôı", + "al and", + "ar ation", + "fig ure", + "Ġded uce", + "chlor o", + "c ost", + "ithm etic", + "ĠItal ian", + "miss ible", + "ĠCommun ity", + "ĠN ature", + "Ġdi oxide", + "Ġbal anced", + "et t", + "ST AT", + "ild ing", + "Ġev olved", + "Ġmon ot", + "p ur", + "Ġpref erences", + "ding er", + "Ġarg ue", + "Ġmo tions", + "Ġinf ant", + "Ġaccel erated", + "Ġobser ver", + "Ġfabric ation", + "ĠMechan isms", + "Ġfunc tor", + "Ġhar ves", + "r ase", + "ĠSpec ial", + "Ġdepos its", + "Ġr ub", + "à ¸", + "ĠCP U", + "ĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġ", + "atom ical", + "Ġfin it", + "Ġsec ure", + "Ġnutri tional", + "ren al", + "ĠF alse", + "Ġshe l", + "Ġrecru ited", + "am big", + "ĠSign aling", + "K O", + "organ isms", + "ĠL T", + "el en", + "ĠM arc", + "ab atic", + "Ġt ables", + "Ġconf ined", + "ĠA z", + "Ġproduc tivity", + "Ġad herence", + "Ġreplic ates", + "Ġvir t", + "f in", + "Ġagric ultural", + "ĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ ĠĠĠĠĠĠĠĠ", + "ĠCh ampionship", + "and a", + "ĠCh urch", + "D uring", + "Ġinser ted", + "igh ter", + "Ġx en", + "Ġs ave", + "Ġtang ent", + "ven ous", + "Ġconver ge", + "Ġdistingu ished", + "Ġexpl os", + "Ġa ortic", + "Ġj ump", + "Ġneon atal", + "ud den", + "Ġslow er", + "Ġinfarc tion", + "Ġpre vents", + "u er", + "Ġ eros", + "R P", + "Ġcontin ues", + "OR T", + "Ġconsid ers", + "ĠN uclear", + "ly mp", + "Ġacc ounted", + "ores is", + "Ġneighbor ing", + "ĠRich ard", + "Ġen for", + "ĠCh ronic", + "Ġdisc over", + "ĠH ong", + "cell s", + "ĠCh all", + "Ġhom ogen", + "Ġathe ros", + "Ġisol ate", + "ĠPlas ma", + "ĠD L", + "par ametric", + "ĠUp per", + "H P", + "Ġintro duces", + "Ġmother s", + "Ġatt ract", + "Ġexcl usion", + "gra vity", + "ĠK r", + "Ġsp ike", + "ĠHe at", + "v ival", + "ĠRNA s", + "b ach", + "ator ial", + "ĠL td", + "on omy", + "in vasive", + "l ass", + "Ġwell s", + "Ġimag inary", + "Ġcarb ohyd", + "od a", + "Ġactiv ate", + "µ Ħ", + "Ġenzym atic", + "p es", + "Ġstat ements", + "Ġapproxim ated", + "ĠSal mon", + "ophage al", + "ĠH PV", + "con f", + "um at", + "Ġsulf ur", + "ĠRec all", + "Ġch ond", + "Ġvi able", + "por ation", + "Ġcare fully", + "tet ra", + "Ġlymph oma", + "st at", + "Ġconserv ative", + "atab ase", + "m and", + "Ġsc ored", + "Ġv as", + "Ġpri vacy", + "onym ous", + "Ġlogarithm ic", + "ĠE con", + "Ġachie ves", + "Ġabund ances", + "c am", + "Ġcy an", + "ĠE L", + "idel ity", + "j o", + "Ġan ticip", + "re ported", + "Ġarrang ement", + "iter ranean", + "ps is", + "ich i", + "Ġt a", + "um ping", + "ĠAc tivation", + "Ġmel t", + "Ġan no", + "og e", + "ĠD am", + "optim al", + "Ġneu rological", + "s a", + "ĠPar ameters", + "off set", + "Ġc ement", + "Ġinhib iting", + "Ġch ose", + "itz er", + "at tr", + "Ġmod er", + "ator ies", + "Ġte aching", + "ĠC ore", + "ph thal", + "ĠL uc", + "Ġin gredi", + "Ġclear ance", + "Ġachie ving", + "t age", + "Ġbur st", + "vi e", + "ĠSp ain", + "pt o", + "Ġtrans membrane", + "Ġsup plementary", + "Ġto ken", + "Ġobvious ly", + "ĠV ector", + "Ġdest r", + "H OD", + "Ġassum es", + "Ġpenet ration", + "Ġsub jective", + "h olds", + "ã o", + "Ġmo tiv", + "Ġprovid ers", + "v ascular", + "Ġdepart ment", + "ock et", + "F ile", + "Ġbre ath", + "ĠB est", + "gra ble", + "Ġl iqu", + "ĠAr g", + "ĠB ob", + "Ġfrag mentation", + "ec tic", + "Ġv ital", + "s ince", + "all oc", + "ox yphenyl", + "Ġradi otherapy", + "ĠSD S", + "Ġcyt ometry", + "n ucle", + "ĠI M", + "ĠTe V", + "raf ish", + "ĠKore a", + "Ġstreng then", + "Ġb are", + "Ġw oman", + "Ġrad ar", + "Ġplatform s", + "ozyg ous", + "ĠA h", + "Ġsub types", + "py rid", + "ĠTrans cription", + "Ġá º", + "ĠMeasure ments", + "Ġsurv iv", + "ĠN ear", + "Ġcasc ade", + "out he", + "B U", + "Ġexpon entially", + "Ġhaz ard", + "Ġsi RNA", + "Ġcell ulose", + "Fig s", + "Ġdifferenti ated", + "Ġim plicated", + "met ric", + "Ġcorrel ate", + "Ġm ission", + "Ġmant le", + "ĠP hyl", + "ĠH art", + "Ġg ases", + "Ġun ity", + "Ġexper t", + "Ġchar t", + "Ġd ict", + "Ġep ile", + "Ġoff spring", + "Ġemerg ed", + "Ġdem ands", + "Ġpres um", + "orb id", + "ĠMed icine", + "Ġstream s", + "tic ed", + "ĠN ic", + "Ġf illing", + "ĠC ro", + "Ġrestric tions", + "S ee", + "ĠM ill", + "Ġparent al", + "Ġdetermin ants", + "Ġecos ystem", + "ĠW all", + "ĠM emory", + "ple ts", + "Ġaggreg ates", + "per turb", + "Ġresid ents", + "AC K", + "v ectors", + "Ġman ually", + "Ġï ĺ", + "ĠFrame work", + "Ġv ag", + "eb rafish", + "l ib", + "ĠHear t", + "ĠAn imal", + "Ġwid er", + "G ene", + "ĠR os", + "Ġoper ate", + "Ġposs ibilities", + "ĠStr ong", + "Ġpy ro", + "resp ectively", + "Ġhybrid ization", + "ip edia", + "x in", + "Ġst om", + "f ish", + "ĠFor ce", + "Ġdim er", + "SU L", + "el se", + "Ġund e", + "g ar", + "con v", + "Ġarri val", + "Ġmon oclonal", + "I AL", + "Ġl y", + "Ġsymmet ries", + "Ġnur sing", + "rac h", + "Ġó µĦ", + "Ġbi ased", + "Ġc ues", + "Ġbiomark er", + "d ers", + "Ġc row", + "ern els", + "Ġbil ateral", + "Ġphys ically", + "Ġpat ches", + "Ġunc on", + "ĠB efore", + "def ault", + "est yle", + "t frac", + "ĠC ox", + "Ġinf iltration", + "Ġconver t", + "Ġstreng ths", + "ĠS ar", + "ig ible", + "oc omp", + "Ġsti r", + "Ġsch izophrenia", + "w as", + "Ġo w", + "et erm", + "ĠOr der", + "Ġf oss", + "Ġline age", + "Ġrab bit", + "Ġregular ization", + "ran ch", + "opl astic", + "T O", + "Ġmeas urable", + "Ġm ang", + "in itial", + "Ġbuild ings", + "Ġsystem atically", + "Ġferm ions", + "Ġlibr aries", + "Ġab lation", + "ide os", + "ĠW i", + "ph oton", + "ĠTest ing", + "ĠComput ing", + "ti er", + "in et", + "Ġprim itive", + "Ġcap illary", + "Ġsl ip", + "ver gence", + "rap eutic", + "ĠBl ue", + "ĠAc ad", + "ha i", + "ĠL ew", + "Ġtri angular", + "MS O", + "Ġsal inity", + "Ġnanoc om", + "o a", + "Ġhom omorphism", + "ĠM M", + "Ġres in", + "D B", + "um inescence", + "d ashed", + "ĠK h", + "qu ark", + "emb les", + "Ġidentif ies", + "Ġfol lic", + "Ġmet am", + "ĠH erm", + "Ġtob acco", + "Ġreal ization", + "hydro x", + "ĠB et", + "B ecause", + "Ġpiec es", + "Ġt alk", + "Ġopen ed", + "as ome", + "Ġsur ge", + "Ġfluct uation", + "g ithub", + "ĠB acter", + "Ġbind s", + "ĠRap id", + "au er", + "p H", + "emb ed", + "ĠD oc", + "uch i", + "ĠC andid", + "Ġrare ly", + "Ġm ountain", + "ĠF at", + "Ġs end", + "ov sk", + "ĠOrgan ization", + "ĠFran c", + "ĠO P", + "âĪ ¼", + "ok es", + "ec e", + "def icient", + "Ġlink age", + "od on", + "Ġf ly", + "Ġt idal", + "ĠEx amples", + "ĠR out", + "Ġaccom mod", + "Sup pose", + "ad ap", + "Ġdi e", + "ro ot", + "Ġh on", + "Ġminim izing", + "Ġrough ness", + "Ġgr ass", + "ent a", + "ĠL ang", + "ed u", + "ĠSim ple", + "en ic", + "Ġinduc ing", + "t f", + "Ġcon texts", + "ĠGeneral ized", + "ĠW nt", + "P b", + "at omic", + "d em", + "ĠPre paration", + "Ġinsu fficient", + "s am", + "ĠSpec ies", + "ĠS olar", + "Ġuns igned", + "ĠH ER", + "â Ĭ", + "Ġpar ity", + "Ġnit rate", + "ĠC er", + "p tic", + "id entif", + "ge al", + "Ġemo tion", + "ĠL P", + "Ġenh ancing", + "Ġmeaning ful", + "st ation", + "Ġrel ig", + "y o", + "Ġpers pectives", + "Ġsc ans", + "ugin osa", + "Ġsummar ize", + "rel ations", + "Ġdist ant", + "Ġfunction ality", + "Ġde eper", + "ol ate", + "ĠP or", + "graph s", + "ĠW a", + "ophil ic", + "CL US", + "ropath y", + "Ġc red", + "Ġun iversity", + "se g", + "ve e", + "O G", + "ĠM en", + "ĠCri tical", + "ã ģ", + "Ġex it", + "var theta", + "ĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ ĠĠĠĠĠĠĠĠĠĠĠ", + "Ġun f", + "Ġpropos al", + "Ġty rosine", + "oti des", + "Ġproxim ity", + "Ġbox es", + "cat en", + "ĠEnvironment al", + "bound ed", + "down arrow", + "Ġfall s", + "Ġfer til", + "Ġcompr ised", + "Ġmell itus", + "Ġleak age", + "ut y", + "Ġchrom osomes", + "ĠStat istics", + "%% %%", + "Ġcomb inator", + "Ġk et", + "ad vant", + "T her", + "Ġtop ics", + "fl at", + "n ia", + "ĠSpect ral", + "Ġsynchron ization", + "var rho", + "Ġcolon ies", + "ĠF ive", + "ag ues", + "ĠF C", + "ID S", + "Ġa ward", + "Ġyield ing", + "Ġarchitect ures", + "ashing ton", + "chit z", + "per ty", + "Ġmod uli", + "m oment", + "sp eed", + "Ġmes enchymal", + "op tera", + "Ġinc omp", + "C ell", + "ĠM ice", + "Ġg ot", + "te ger", + "Ġt au", + "ĠAd S", + "Ġb ill", + "Ġdr inking", + "uls ive", + "Ġknock down", + "Ġarm s", + "ĠAut om", + "ĠIncre ased", + "H F", + "Ġglob ally", + "Ġdop ing", + "Ġat h", + "ĠC op", + "Ġsuccess ive", + "UL T", + "el ess", + "Ġble eding", + "Ġfood s", + "Ġimmun ohist", + "Ġdef inite", + "ĠJ ones", + "ĠT S", + "Ġjo ined", + "ĠTow ards", + "ĠC s", + "Ġun like", + "Ġval ence", + "d or", + "o S", + "Ġp ush", + "Ġoff ice", + "Ġalumin um", + "id yl", + "idi rectional", + "wr itten", + "Ġb ubble", + "H I", + "Ġmarked ly", + "ĠT ok", + "Ġvesic les", + "Ġquoti ent", + "Ġrepro duce", + "Ġelse where", + "ĠMy c", + "Ġinf rastructure", + "Ġgain ed", + "ab el", + "ĠS ex", + "ĠT ables", + "et in", + "Ġhom olog", + "Ġleg al", + "he a", + "Ġsoci ety", + "Ġman aged", + "id ase", + "ĠInhib ition", + "Ġparas ite", + "Ġvol unte", + "AT P", + "i os", + "Ġse psis", + "Ġrib osomal", + "Ġconf ound", + "ĠSta phyl", + "aryn geal", + "ï Ģ", + "com b", + "ĠOb jective", + "SUL TS", + "Ġthor ough", + "m t", + "Ġc hest", + "V ector", + "ele ment", + "Ġvir ulence", + "Ġhem isp", + "Ġso ught", + "ĠK o", + "Ġnutri tion", + "ul ing", + "ian a", + "Ġprot otype", + "ĠO nt", + "c ine", + "Ġdot ted", + "Ġob ese", + "ount ered", + "Ġphysic ians", + "Ġmin i", + "Ľ ľ", + "sp aces", + "Ġexcl usively", + "ĠCon volution", + "Ġc aspase", + "ĠL ink", + "di v", + "ĠRoy al", + "h ist", + "it ness", + "Ġes ter", + "Ġconduc ting", + "Ġparticip ated", + "Ġair way", + "Ġaer uginosa", + "E xt", + "arg ument", + "ock ing", + "Ġintegr ate", + "Ġcont rovers", + "ap es", + "train ing", + "ĠPre valence", + "tem p", + "b oth", + "Ġre activity", + "Ġrank ing", + "Ġtunn eling", + "OD E", + "ĠMed iterranean", + "Ġreson ances", + "M g", + "Ġl ib", + "ĠH eter", + "Ġnot hing", + "Ġindic ation", + "ĠH M", + "ocy tic", + "st rand", + "Ġcollabor ation", + "Ġelectro static", + "Ġindepend ence", + "h ab", + "Ġconf lic", + "Ġi od", + "in us", + "Ġdepend ency", + "ĠL am", + "Ġexam ining", + "Ġoccup ied", + "Ġque ue", + "ĠB ul", + "Ġregist ered", + "Ġindivid ually", + "R x", + "aus al", + "V E", + "Ġbright ness", + "resp ons", + "bal ance", + "Ġcytotox ic", + "f all", + "com mut", + "IC AL", + "ur an", + "ain ing", + "ra ulic", + "res ults", + "Ġepis odes", + "Y S", + "ĠG ar", + "Ġsur fact", + "dr ug", + "Ġc ities", + "ĠCh ange", + "os ition", + "Ġtrig gered", + "Ġcytoplas mic", + "erv es", + "Ġle x", + "Ġasymptotic ally", + "ph y", + "Ġfron tal", + "ĠD ensity", + "Ġsyn erg", + "cy cle", + "ĠImpro ved", + "à ¸", + "Ġmon o", + "Ġaccum ulated", + "orient ed", + "b our", + "Ġtun nel", + "com ing", + "Ġap paratus", + "Ġenc ountered", + "C re", + "Ġlet ters", + "et ch", + "Ġexcess ive", + "Ġbiofil m", + "Ġrear rang", + "Ġpolymorphism s", + "er obic", + "Ġconn ect", + "res olved", + "ĠN N", + "Ġret ro", + "ĠIn iti", + "ĠQuanti f", + "Ġp up", + "T ensor", + "Ġsent ences", + "l ay", + "ran ts", + "pl oid", + "ĠAnd erson", + "Ġdes irable", + "st ud", + "i ability", + "Ġd rying", + "ec ess", + "Ġd ens", + "Ġdescri pt", + "ĠË Ĩ", + "Ġcl ones", + "Ġju ven", + "b p", + "Ġk il", + "H L", + "Ġhem orrh", + "ĠK i", + "H ow", + "Ġen erge", + "Ġsub section", + "ĠS ac", + "di al", + "Ġcardi omy", + "Ġto uch", + "d m", + "Ġsc ienti", + "oid es", + "Ġà Ĥ", + "ysacchar ide", + "Ġs clerosis", + "ĠZe aland", + "in ine", + "Ġunus ual", + "ĠB A", + "ips chitz", + "g ap", + "ĠDiff erences", + "Ġdual ity", + "ed ical", + "Ġl ign", + "Ġfail s", + "Ġ lect", + "Ġrel ate", + "Ġincor rect", + "Ġspec ify", + "Ġcylind rical", + "ĠP F", + "ĠL ind", + "Ġdet erior", + "Ġher b", + "d z", + "Ġw eld", + "Ġnom inal", + "cop y", + "Ġacet yl", + "ht ml", + "Ġrecogn ize", + "** *", + "iti an", + "W A", + "ĠM N", + "ĠF ind", + "Ġaut hentic", + "per ture", + "Ġcytotox icity", + "of l", + "ĠG et", + "Ġcoh omology", + "Ġremain der", + "Ġexpand ing", + "Ġhe av", + "oster one", + "R ight", + "Ġcop ol", + "Ġs hed", + "Ġcompl iance", + "Ġacid ic", + "or ic", + "Ġam yloid", + "Ġevap oration", + "d l", + "Ġdel ays", + "P o", + "ĠCH ECK", + "tain s", + "Ġrevers ed", + "ĠMP a", + "Ġprocess or", + "Ġh all", + "ĠL ast", + "Ġplas m", + "ĠAss ociated", + "ĠBas ic", + "in os", + "Ġsympt om", + "ã Ģ", + "Ġanth rop", + "Ġjud g", + "Ġe ti", + "k le", + "Ġwr ong", + "ro om", + "Ġdevelop ments", + "ĠMax imum", + "Ġcoating s", + "Ġheur istic", + "ron tal", + "S ome", + "Ġutil ize", + "ĠâĪ ħ", + "c oll", + "ĠRel ated", + "Ġde generation", + "tem plate", + "Ġmod ulated", + "Ġparamet ri", + "Ġsal iv", + "ĠPseud omonas", + "Ġanti gens", + "Ġhar mon", + "ĠL HC", + "do i", + "ens itive", + "ĠNo tice", + "ĠM oh", + "til age", + "AC S", + "Ġdiscrep ancy", + "Ġsp ik", + "Ġre strict", + "it rile", + "le g", + "ĠB ase", + "Ġconvolution al", + "ĠRes istance", + "Ġappear ing", + "ĠIm ages", + "ĠM ann", + "Ġre act", + "Ġmacroph age", + "Ġwave let", + "och rom", + "Ġfair ly", + "Ġpreced ing", + "Ġsp ir", + "n etwork", + "ĠN ak", + "IF T", + "Ġag o", + "Ġenc ryp", + "al d", + "ens in", + "Ġs ulph", + "ĠPol ymer", + "ĠAr t", + "Ġsub units", + "sh ot", + "Ġbeg ins", + "Ġex er", + "pro pto", + "Ġn urses", + "Ġsuff ices", + "Ġgra ded", + "ĠR ock", + "Ġuniqu ely", + "it ol", + "Ġsp iral", + "Ġthan ks", + "char acter", + "ĠDist ributed", + "ĠC art", + "F orm", + "Ġform ulations", + "iction ary", + "Ġspread ing", + "Ġsingular ity", + "Ġpig s", + "it u", + "ot rophic", + "Ñ Ģ", + "Ġsemicon ductor", + "Ġd rag", + "ne xt", + "ma xim", + "un n", + "Ġarg ued", + "pl astic", + "Ġdehydrogen ase", + "Ġreinfor cement", + "ent ral", + "ĠD S", + "Ġcompan ies", + "Ġquanti zation", + "ĠD ri", + "Ġsimpl er", + "Ġradi i", + "ĠEth ics", + "ĠElect ronic", + "t aken", + "Ġpharmac ological", + "ps on", + "Ġpair ing", + "Ġn est", + "ĠR S", + "Ġl ic", + "oc on", + "Ġobserv ing", + "ĠF M", + "I ES", + "Ġsub mitted", + "ĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ ĠĠ", + "Ġno isy", + "Ġvan ishing", + "ĠTechn ologies", + "il st", + "ag ic", + "Ġembed dings", + "Ġpl ans", + "re ak", + "oc t", + "Ġepit helium", + "Ġrevers ible", + "Ġrequ ests", + "V i", + "ĠPro g", + "meth oxy", + "ur ia", + "Ġsl ice", + "Ġmetast ases", + "ĠM ary", + "Ġprior i", + "Ġexplain s", + "ĠS igma", + "ĠArm y", + "Ġpre y", + "K L", + "ĠP ass", + "Ġrepro duction", + "Ġfer mentation", + "ul o", + "Ġproof s", + "ĠAccording ly", + "ti st", + "ĠïĢ ©", + "Ġme at", + "Ġpl anned", + "Ġangi ogenesis", + "W R", + "ĠA ust", + "Similar ly", + "ĠW ashington", + "Ġref inement", + "Ġembry o", + "Ġdiss ociation", + "á n", + "plas ia", + "ĠG ro", + "Ġsimilar ities", + "Ġsolub ility", + "Ġimm obil", + "ĠSc ot", + "ĠSub sequently", + "di vid", + "Ġclos est", + "ĠW at", + "Ġâ Į", + "ĠA GN", + "Ġpres cribed", + "Ġm osquito", + "Ġf irm", + "Ġde generate", + "Ġeth yl", + "Ġhar vest", + "ĠSpec ific", + "Ġcomp artment", + "p ublic", + "ĠBi ological", + "Ġpiec e", + "Ġat titudes", + "Ġsp ray", + "ĠS ix", + "Ġprofession als", + "Ġsl ot", + "Ġretrie ved", + "ve ment", + "Ġexec uted", + "se ed", + "Ġout flow", + "d istance", + "ĠT erm", + "ad y", + "ĠProv ince", + "ĠCent re", + "ĠD FT", + "Ġs udden", + "Ġse iz", + "r at", + "rom o", + "ot echn", + "Ġhigh lights", + "Ġelectroly te", + "ĠAdv anced", + "all ow", + "p x", + "os ed", + "sub array", + "rac ks", + "P RO", + "ogen y", + "Ġpool ed", + "Ġd type", + "Ġop posed", + "ĠG rand", + "Ġdesign ing", + "b el", + "it ability", + "Ġminim ization", + "Ġdram atically", + "Ġso y", + "ag ents", + "ĠMet al", + "ĠM V", + "rib ute", + "D D", + "it an", + "Ġspeed s", + "Ġmar ried", + "Ġevalu ations", + "ĠKing dom", + "Ġcl ay", + "ĠTiss ue", + "left arrow", + "Ġcompens ation", + "ch ild", + "p ool", + "up arrow", + "ĠDom ain", + "spec ies", + "Ġmeth ane", + "ĠE GFR", + "Ġpar ser", + "h ave", + "Ġneg lected", + "f unc", + "aps ed", + "Ġs ays", + "ad ata", + "bin om", + "C ase", + "Ġre porter", + "S n", + "Ġmaxim ize", + "Ġbif urc", + "ĠCN S", + "ĠO lymp", + "Ġdecl are", + "Ġenc oder", + "Ġab elian", + "Ġsingular ities", + "Ġe ch", + "Î ¨", + "Ġpro to", + "Ġph ag", + "Ġpoly g", + "Ġb ott", + "Ġadi pose", + "u ing", + "j k", + "uch y", + "ĠStud ent", + "Ġnan ow", + "Ġth ym", + "E d", + "E nd", + "Ġtransform s", + "ĠP CA", + "k ern", + "reg n", + "Ġcom ment", + "ĠL L", + "ell es", + "Ġeng agement", + "ĠP eter", + "IS PR", + "ĠCh annel", + "in y", + "Ġbund les", + "A ld", + "Ġpublic ations", + "T G", + "st ra", + "Ġf ear", + "Ġre tic", + "ple ments", + "Ġcor pus", + "ĠCl uster", + "ĠR ate", + "Ġsimpl est", + "ac ic", + "rb rack", + "Ġb low", + "Ġcomp ress", + "ĠD ark", + "Ġpsy chiatric", + "ĠCon versely", + "Ġo wing", + "Ġabs or", + "ĠH P", + "Ġcr ude", + "equ al", + "ĠAr ray", + "ĠRel ative", + "Ġcomb ustion", + "R ed", + "k t", + "Ġm A", + "Ġt ex", + "por ters", + "Ġdiffere d", + "Ġaud io", + "z on", + "od i", + "Ġmac roscopic", + "ac in", + "Ġz eros", + "Ġfore ign", + "Ġd uct", + "b ow", + "w orth", + "ĠRo ad", + "re y", + "ace ous", + "Ġbl ast", + "Ġgran ul", + "Ġw ing", + "Ġannot ated", + "ĠF ull", + "Ġinflu encing", + "v y", + "iaz ol", + "Ġp itch", + "Ġre habilitation", + "ĠPri or", + "com it", + "math tt", + "di a", + "ĠI on", + "Ġab use", + "Ġharves ted", + "Ġepid emic", + "Ġfil ament", + "Ġnucle ation", + "ĠKnow ledge", + "rin os", + "Ġb ent", + "Ġsqu ared", + "Ġhippocamp al", + "ĠT G", + "AN T", + "mod ified", + "ari o", + "ĠF ace", + "Ġgrow s", + "Ġfa ults", + "v irus", + "Ġpartition ing", + "air s", + "Ġhe aring", + "Ġcon gen", + "Ġ rip", + "ĠColl abor", + "Ġinterview s", + "Ġh uge", + "Ġbreak down", + "Ġmonth ly", + "ĠCON CLUS", + "E ach", + "D iff", + "Ġrel ay", + "ĠM use", + "oscop y", + "Ġre new", + "g b", + "Ġb rid", + "Ġoutl ined", + "or ig", + "e at", + "ĠWith out", + "Ġsp or", + "ĠT N", + "ĠJ o", + "ĠA U", + "N ot", + "Ġret in", + "ĠAn gel", + "Ġtri ed", + "ey ond", + "j e", + "ĠRuss ian", + "ĠUn fortunately", + "ĠMean while", + "ograph s", + "Ġacc ounting", + "ĠA β", + "m b", + "Ġdop amine", + "ĠBrief ly", + "ĠF requency", + "Mat rix", + "ĠJose ph", + "Ġexper ts", + "Ġdro ps", + "ĠRE SULTS", + "Ġrect angular", + "ath ione", + "cent er", + "ĠLe ft", + "in form", + "k ins", + "Ġm il", + "ĠM ah", + "Ġmed ial", + "ĠComp any", + "Ġpass age", + "Ġlead er", + "Ġscreen ed", + "er i", + "pos ites", + "r arily", + "Ġph one", + "ie tic", + "Ġexpect ations", + "ĠPar ticle", + "ĠM ountain", + "Ġinter leukin", + "Ġfif th", + "Ġv ast", + "Ġlog ical", + "Ġt err", + "Ġcre ates", + "Ġfinit ely", + "Ġsw im", + "Ġsupernat ant", + "opath ological", + "ĠUl tra", + "ĠT y", + "Ġgra nd", + "Ġconstit ute", + "olog ist", + "ĠBro ad", + "aw are", + "Ġvic inity", + "ag ulation", + "uns igned", + "ĠS ize", + "ĠC ognitive", + "Ġsusp ected", + "Ġu pl", + "Ġauto immune", + "ĠS K", + "C B", + "Ġsl ices", + "ĠCh i", + "Ġobserv ables", + "Ġhippocamp us", + "so ver", + "Ġfund ing", + "Ġcon formation", + "ĠQ uestion", + "ĠS qu", + "ĠW ill", + "Ġsc attered", + "ir ty", + "Ġpl aus", + "cor relation", + "Ġventi lation", + "ĠGen es", + "Ġben ign", + "Ġheter o", + "St atus", + "ang led", + "Ġb ootstrap", + "Ġvacc ines", + "Ġmicro organisms", + "Ġvis its", + "Ġtheorem s", + "d rop", + "ĠT A", + "Ġcycl ing", + "Ġspectrom eter", + "Ġground water", + "Ġnanot ubes", + "Ġjo ints", + "ĠE ll", + "Ġcons ult", + "Ġwindow s", + "Ġdis ability", + "Ġgain s", + "Ġdis charg", + "Ġhe ated", + "Ġa fore", + "ary ing", + "inc re", + "Ġagg ressive", + "Ġhe mod", + "ari um", + "ĠIn st", + "v m", + "Ġdro plet", + "p tive", + "vious ly", + "Ġst arch", + "Ġd f", + "os yl", + "Ġdon ors", + "ĠUn like", + "Ġalkal ine", + "Ġintellig ence", + "a a", + "Ġaccept ance", + "Ġsl iding", + "aps es", + "ĠD iss", + "ist an", + "a uc", + "Ġb ins", + "Ġmod ulate", + "Ġman age", + "out s", + "Ġs enes", + "Ġdifferenti ate", + "Ġcoun ted", + "AS K", + "Ġantib acterial", + "Ġent ered", + "Ġdis advant", + "ĠSalmon ella", + "Ġis otopic", + "Ġanno unced", + "ĠBo ard", + "Ġrest oration", + "Ġalle vi", + "Ġprogram me", + "Ġalb umin", + "Ġcatal og", + "est ine", + "Ġdifferent ly", + "Ġm olar", + "rö dinger", + "ĠE vent", + "minist ration", + "ĠSer um", + "RO M", + "k w", + "b ot", + "Ġj ets", + "ĠDo uble", + "el er", + "Ġinf usion", + "Ġconsum ed", + "ĠI ron", + "ĠProcess es", + "Ġad mits", + "Ġj uris", + "ĠPer iod", + "Ġremod eling", + "alle y", + "Ġenabl ing", + "Ġback ward", + "ĠM id", + "bre vi", + "Ġclass ify", + "Ġcr ypt", + "Ġhel ix", + "ĠJ iang", + "Ġh oney", + "ges tion", + "x c", + "Ġcoinc ides", + "ĠD N", + "Ġap optotic", + "Ġinst all", + "ĠR ever", + "ĠDop pler", + "ic ago", + "er als", + "Ġp ie", + "ĠM ars", + "ĠStaphyl ococcus", + "Ġnot ing", + "Ġgener a", + "ĠI o", + "Ġh ope", + "Ġpres erve", + "MA X", + "yn chron", + "Ġr up", + "Ġcompr ising", + "ĠW ay", + "Ġvi olation", + "Q R", + "Ġreflect ing", + "Ġregular ity", + "ĠSi O", + "ĠJ un", + "Ġcommun ications", + "r ating", + "Ġfam iliar", + "Ġinstant aneous", + "Ġcor tic", + "Ġapparent ly", + "X X", + "Ġexcit ations", + "ĠA ward", + "N um", + "ĠU N", + "Ġqu bit", + "ĠAc tion", + "ĠF ried", + "Ġelim inated", + "Ġasp ir", + "h ler", + "Ġdec oding", + "un ov", + "Ġanalog ue", + "ul monary", + "Ġge ographic", + "Ġs ort", + "ĠCR C", + "Ald rich", + "Ġk Da", + "ĠN D", + "Ġset tle", + "ex ists", + "Ġstat istic", + "ĠB ow", + "ĠC G", + "Ġorgan izations", + "ĠM obile", + "Ġinv ent", + "Ġincorpor ate", + "ĠF ib", + "ord an", + "Ġcolle agues", + "ĠSt ation", + "Ġs en", + "Ġenc aps", + "ĠR H", + "rel im", + "Ġcarbon ate", + "ĠN ether", + "m em", + "EE E", + "Ġafore mentioned", + "Ġp ent", + "ĠSign al", + "Ġsusp ended", + "Col or", + "Ġsp ins", + "Ġpropor tions", + "ult y", + "Ġen rolled", + "ĠT EM", + "ĠRecep tor", + "Ġpre valent", + "l arge", + "v s", + "Ġtrunc ated", + "Ġâĭ ħ", + "l m", + "an il", + "Ġann ih", + "ĠGalax y", + "er as", + "Ġep igenetic", + "Ġto oth", + "Ġcondens ation", + "ĠT ensor", + "Ġin organic", + "ym ers", + "u f", + "an ese", + "are t", + "Ġar ithmetic", + "â Ĩ", + "Ġt rying", + "Ġimplement ing", + "x d", + "Ġill umination", + "el a", + "Ġdefic its", + "Ġsp ots", + "Ġdoes n", + "Ġrest ing", + "tra ined", + "Ġeros ion", + "Ġgran ular", + "Ġsc ar", + "Ġpol len", + "l ie", + "Ġcon vers", + "Ġdisturb ances", + "ĠG od", + "Ġen larg", + "ĠL ate", + "yl ase", + "Ġfac ts", + "ent y", + "ĠStre et", + "sequ ence", + "Ġven ous", + "ĠC heck", + "ag g", + "Ġabsorb ed", + "Ġcom mit", + "set s", + "Ġdest roy", + "Ġbow el", + "Ġfin ished", + "ĠF eed", + "Ġdop ed", + "ĠAl b", + "ĠMit ochond", + "Ġtheore tically", + "R I", + "Ġmet eor", + "ĠM G", + "Ġn ation", + "ĠBas in", + "n ik", + "Ġdep ths", + "ĠMechan ism", + "Ġmotif s", + "ĠH ay", + "Ġmo tivated", + "ĠC opy", + "ĠE astern", + "Ġpers istence", + "Ġra ys", + "F B", + "and em", + "l ayers", + "ey er", + "ĠStre pt", + "Ġregist ration", + "ĠAnt arctic", + "C V", + "ĠP ap", + "ĠSp e", + "Ġsplic ing", + "per formance", + "Ġseman tics", + "Ġloc om", + "oblast oma", + "Ġm oney", + "Ġtrans parent", + "Ġh r", + "ĠInter actions", + "Ġs ap", + "Ġbi ases", + "Ġte eth", + "yn olds", + "omet hyl", + "Ġm V", + "Ġsole ly", + "Ġor ange", + "bl ast", + "ATION S", + "c all", + "opo ietic", + "s ided", + "ĠF ox", + "ĠV ideo", + "Ġinsp ection", + "Ġb uck", + "hes ize", + "p resent", + "ĠAnti b", + "Ġh am", + "al am", + "ĠP G", + "ĠA E", + "Ġj oin", + "Ġmon ocytes", + "es tiv", + "Ġrandom ised", + "Ġtransl ocation", + "Ġincorpor ating", + "Ġprolif er", + "Ġod ds", + "IT H", + "Ġr an", + "Ġinstruc tion", + "Ġresol ve", + "Ġf t", + "ĠHe ad", + "Ġre agent", + "Ġad mitted", + "h uman", + "pos ure", + "ĠCh a", + "ĠF r", + "Ġbroad cast", + "Ġnutri ents", + "n ob", + "Ġnot able", + "ĠI GF", + "ĠC learly", + "Ġquark s", + "Ġe ukary", + "ĠAd d", + "it osan", + "Ġinter active", + "it ting", + "ĠComput ational", + "Ġdiss olution", + "ist ribution", + "pro duct", + "ĠA BC", + "olim its", + "bi ased", + "Ġtrap ped", + "P K", + "ĠH PLC", + "roph ot", + "z es", + "our se", + "ĠH ot", + "Ġrec ipro", + "n olimits", + "ell o", + "Ġassess ments", + "EN TS", + "Ġalter ation", + "t w", + "Ġcha otic", + "ĠL oc", + "Ġcat tle", + "R ay", + "Ġform ally", + "le ave", + "text style", + "Ġvent ral", + "ĠWilli ams", + "ĠPe ople", + "ix ing", + "ĠThe rapy", + "Ġi ii", + "ĠD T", + "Ġb ic", + "Ġsp heres", + "Ġvis c", + "Ġestablish ment", + "Ġdescrip tions", + "ĠA verage", + "Ġto ur", + "ĠInf ection", + "ĠL icense", + "Ġprep are", + "H s", + "f inite", + "ri um", + "ore g", + "ent ry", + "Ġdis ks", + "Ġelong ation", + "c pu", + "ĠChar les", + "FIG URE", + "st on", + "ĠObserv ations", + "A dd", + "ĠT ask", + "at omy", + "ig ration", + "ĠD atabase", + "ĠTex as", + "Ġph yt", + "ll er", + "con jug", + "onal d", + "Ġheav ily", + "Ġsp le", + "Ġass ist", + "ĠC p", + "Ġhapp en", + "u v", + "ĠUn iverse", + "ĠG PS", + "W E", + "X i", + "Ġadminist r", + "str ong", + "Ġmagn itudes", + "Ġsimpl ify", + "Ġele gans", + "es h", + "ĠB ody", + "ĠNether lands", + "à ¯", + "omet ers", + "B o", + "F M", + "ĠN iger", + "pl us", + "inst ance", + "Ġdist ress", + "Or gan", + "C as", + "Ġsym plectic", + "Ġbreak s", + "Ñ Ĥ", + "Ġferm ion", + "em poral", + "Ġs omatic", + "e vent", + "ne ut", + "lamm ation", + "ĠL ibrary", + "Ġmulti plic", + "ĠIn str", + "et hel", + "ur ys", + "Ġhelp ed", + "Ġcol lege", + "Ġcar tilage", + "Ġr pm", + "w estern", + "res is", + "Ġlob e", + "Q L", + "In put", + "Ġemph asis", + "b est", + "Ġtot ally", + "ĠMET HOD", + "ĠF a", + "ĠRed uction", + "ici ous", + "Ġimplant ation", + "pot ential", + "prob lem", + "Ġobtain s", + "ur ons", + "Ġconstruct ing", + "ĠMus ic", + "Ġcan cell", + "Ġnew s", + "ĠCh apter", + "Ġlab elled", + "Ġz ebrafish", + "ĠSol id", + "Ġglut amate", + "ĉĉ ĉĉĉ", + "Ġch apter", + "ĠPres ident", + "M in", + "Ġat rial", + "c p", + "f i", + "f inal", + "Ġto k", + "Ġeffect or", + "Ġsp ine", + "Ġidenti ties", + "isc o", + "ol is", + "ĠC le", + "Ġinvari ants", + "P ath", + "ĠG on", + "fact ory", + "Ġex ogenous", + "ĠMAP K", + "Ġansw ers", + "Ġget ting", + "R s", + "I H", + "ĠDef ine", + "ĠConvolution al", + "Ġgeomet rical", + "ĠIn put", + "Ġ à", + "Ġatten uated", + "Ġradical s", + "ĠAcad emy", + "ã ĥ", + "ich let", + "Ġtor us", + "ĠThe oretical", + "ĠT D", + "Ġan tiv", + "on ge", + "Ġintra venous", + "Ġhyp oth", + "Ġwaste water", + "ĠF lo", + "Ġpor osity", + "Ġp all", + "ac i", + "Ġrecord ings", + "Ġe ating", + "ĠD W", + "un ting", + "ĠD im", + "Ġemit ted", + "ĠJ oint", + "of ib", + "Ġearthqu ake", + "Ġm unic", + "Ġreduc tions", + "Ġcon junction", + "ĠL ocation", + "Ġestabl ishing", + "ĠMat hematical", + "ĠS olution", + "bu ffer", + "ar in", + "ile y", + "ĠCom mission", + "ĠG ABA", + "ĠMuse um", + "Ġver b", + "lec ules", + "inf ection", + "Ġins ect", + "is er", + "Ġprov ision", + "Ġagre ed", + "Ġaff ord", + "the ory", + "know ledge", + "Pro tein", + "Ġk ernels", + "Ġd erm", + "Ġw ish", + "Ġv ox", + "S cale", + "h u", + "Ġcounter parts", + "ĠR oss", + "Ġun p", + "ĠOn line", + "Ġtrans porter", + "G raph", + "Ġ uter", + "Ġmin ute", + "Ġautom orphism", + "il tr", + "ĠResp ons", + "ĠS ym", + "Ġfactor ization", + "s em", + "Ġmedi ates", + "Ġun expected", + "Ġorgan ism", + "Ġattem pted", + "ar an", + "ven ue", + "ethel ess", + "Ġno ticed", + "ĠInvestig ation", + "Ġcare g", + "Ġgroup ed", + "or bit", + "Ġshort est", + "Ġbroad er", + "ĠM IM", + "ris es", + "vel oper", + "ĠH i", + "Ġk Hz", + "Ġbe ads", + "Ġph yto", + "ĠDo es", + "Ġmamm als", + "Ġref ined", + "vol ume", + "S er", + "Ġresis tivity", + "Ġter restrial", + "Ġa xi", + "if luor", + "Ġ £", + "Ġv ice", + "ĠK el", + "V M", + "ĠT own", + "ad m", + "pl ates", + "Ġhol omorphic", + "ĠR ib", + "ĠS B", + "ĠTem poral", + "s rc", + "Ġupd ates", + "Ġsee k", + "en dix", + "ore tic", + "war z", + "Ġro utes", + "Ġstand ing", + "Ġà ģ", + "Ġclass ic", + "Ġp ale", + "lec tions", + "Ġclass ifiers", + "Ġpath ophys", + "Ġm ounted", + "Ġdesign ated", + "Ġv ideos", + "Ġinc oming", + "Ġguarant ees", + "Ġparas ites", + "ĠB acillus", + "f our", + "ĠâĪ ¨", + "Ġcommut ative", + "stack rel", + "ĠBan ach", + "Ġde aling", + "em porary", + "M ulti", + "ot omy", + "re ting", + "Ġn ond", + "ĠCon ference", + "tz mann", + "Ġphosphor us", + "Ġchemical s", + "Ġdis par", + "deg ree", + "Ġarbit rarily", + "rocy te", + "Ġpar abolic", + "Ġdimension less", + "Ġo sm", + "Ġphon on", + "ti ary", + "ĠS ect", + "ophys ical", + "ĠM apping", + "b is", + "ĠCommun ication", + "Ġmim ic", + "Ġregul ators", + "Ġneutroph ils", + "f n", + "ĠImport antly", + "Ġm ere", + "Ġconfir ms", + "ag ram", + "Ġatt end", + "ung al", + "ĠGroup s", + "Ġz o", + "Ġm outh", + "Ġste ep", + "Ġprevent ed", + "Ġdep ressive", + "ac ies", + "ĠL S", + "Ġnit ric", + "Ġvisual ized", + "Ġtranscript ome", + "Ġga it", + "erc ury", + "Ġsh ot", + "ĠV en", + "Ġex chang", + "Ġint ention", + "ĠT ang", + "Ġfav our", + "ve olar", + "Ġper mission", + "Ġhabit ats", + "Ġma ize", + "inc t", + "Ġtele vision", + "ryst als", + "ĠRad i", + "Ġflav on", + "Ġcan n", + "i ota", + "ĠO T", + "p ic", + "R ad", + "ti tial", + "ĠOr th", + "st ellar", + "ĠK ine", + "Ġnavig ation", + "f ast", + "ĠCR ISPR", + "Ġkinem atic", + "Ġsearch ing", + "Ġmic rom", + "Ġinst alled", + "ĠTai wan", + "il a", + "r f", + "ri age", + "pl inary", + "Ġe cho", + "ra v", + "ĠL es", + "cre ate", + "Ġubiqu it", + "Ġprecurs ors", + "K E", + "Ġdiv ide", + "Ġln c", + "ĠCon struction", + "an ic", + "es tim", + "is ters", + "Ġfe et", + "ari ant", + "ĠSch w", + "Ġexcl ude", + "Ġvol can", + "ĠOver view", + "Ġy r", + "ol k", + "Ġ ©", + "ĠF E", + "Ġsper mat", + "Ġcapac itance", + "ĠSch rödinger", + "ĠG E", + "Ġcalibr ated", + "S EM", + "Ġlat tices", + "pl ier", + "Ar g", + "ĠN T", + "ĠEnh anced", + "Ġb rom", + "Ġmulti p", + "Ġcer tified", + "Ġis lands", + "Ġcy st", + "Ġal titude", + "ed ef", + "Ġconst rain", + "Ġsatisf actory", + "Ġspecial ized", + "Ġj unctions", + "Ġcoron avirus", + "ud ge", + "ex c", + "Ġal t", + "ĠB acterial", + "Ġse asons", + "ĠL M", + "Ġhist ogram", + "Ġsol vents", + "a verage", + "Ġcard inal", + "ch rom", + "py thon", + "d ered", + "en ia", + "ĠG H", + "ĠEs s", + "__ __", + "ĠP ak", + "s ized", + "ĠH g", + "Ġel if", + "ĠSchem atic", + "Ġcytoplas m", + "ĠFor t", + "an ia", + "Ġcare ful", + "ĠD ual", + "Ġtransl ated", + "Ġn asal", + "In v", + "Ġda ughter", + "Ġemphas ize", + "mod ules", + "Ġl ives", + "Ġhom otopy", + "Ġb ot", + "Ġdis ordered", + "mat o", + "S econd", + "Ġclaim ed", + "add le", + "Ġinterf acial", + "Ġvisc ous", + "Ġdest ination", + "ĠPl anck", + "Ġabsorb ance", + "Ġvol atile", + "Ġst orm", + "Ġcar boxyl", + "ĠB ank", + "ĠP ack", + "Ġscaff old", + "te br", + "ip ot", + "Ġtum ours", + "ĠG ol", + "Ġelectroph oresis", + "Ġreal ize", + "Ġconstitu ents", + "S ol", + "ĠE very", + "Ġmedi ate", + "Ġcoinc ide", + "Ġexplo it", + "Ġmon oton", + "me asure", + "Ġsup plied", + "rac ellular", + "Ġfer ro", + "Ġpur s", + "eren tially", + "tr ast", + "ĠR B", + "Ġdiss em", + "as y", + "Ġrel ating", + "n ull", + "u ates", + "const ant", + "ĠContinu ous", + "Ġgeomet ries", + "r ust", + "ĠS TR", + "cl uster", + "Ġprogen itor", + "ĠC SF", + "ĠY am", + "ĠRe ynolds", + "ĠM Y", + "ĠK O", + "ĠW alk", + "ari able", + "ind er", + "ĠR ight", + "ĠAl gebra", + "ĠW ik", + "Ġin activation", + "t mp", + "ac cess", + "ĠL ater", + "Ġmicrobi ome", + "Ġgeodes ic", + "Ġre jection", + "us es", + "Ġhard ness", + "Ġhydro dynamic", + "Ġvan ish", + "Ġpoll ut", + "amy cin", + "ĠÏ Ń", + "ip itation", + "Ġaug mented", + "ĠT T", + "av al", + "Ġenc ode", + "Ġtox in", + "et o", + "igh bor", + "add r", + "Ġdam aged", + "o i", + "Ġtrans duction", + "Ġinter acts", + "ÃŃ a", + "ĠC all", + "ri ends", + "ĠMon itoring", + "ĠVari ation", + "Ġôı ¼", + "Ġd ich", + "Ġsp ars", + "al ign", + "Ġan atomical", + "Ġcentrifug ed", + "ur ally", + "ĠZ r", + "ĠCar l", + "Rec all", + "Ġopin ion", + "Ġ era", + "Ġdrain age", + "Ġmicro array", + "st atus", + "um ental", + "Ġcomp rises", + "press ure", + "Ġprac tition", + "m ac", + "Ġcon gr", + "urn al", + "ĠA PI", + "ĠL R", + "Ġtransf ection", + "Ġsl opes", + "ĠC ode", + "Ġph il", + "b ool", + "W s", + "Ġâ Ļ", + "Ġassoci ate", + "otox icity", + "ra de", + "ĠM iller", + "ĠÏ ª", + "Ġshor ten", + "Ġaddition ally", + "ĠEff ective", + "Ġsuper vised", + "Ġel abor", + "ĠC ellular", + "Ġt ell", + "ĠR C", + "s ave", + "im id", + "Ġrating s", + "ĠT aking", + "Ġappro val", + "Ġpenal ty", + "K K", + "con text", + "ak s", + "pec ific", + "Ġtem por", + "Ġup regulation", + "V AL", + "Ġenc odes", + "in in", + "Ġnot es", + "ĠFore st", + "Ġcombinator ial", + "ympt otic", + "Ġsqu amous", + "ĠAs h", + "our n", + "Ġmyel oid", + "el ines", + "B io", + "Ġbre ed", + "ĠR ub", + "uz z", + "Ġsingle t", + "en na", + "Ġcri tically", + "d ig", + "dis ci", + "Ġdrop ped", + "Ġlip oprotein", + "ĠE t", + "Ġno v", + "op hen", + "Ġanc ient", + "B ase", + "Ġsmooth ing", + "iti ves", + "p ine", + "Ġsol ver", + "per m", + "ĠH ome", + "Ġaz im", + "l Vert", + "Ġtransport ation", + "Ġde x", + "ĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ ĠĠĠĠĠ", + "opath ic", + "ex perim", + "âĢ¢ âĢ¢", + "perf usion", + "Ġdo i", + "ĠL act", + "Ġhepat ocellular", + "Ġmism atch", + "Ġaden ocarcinoma", + "ĠP ain", + "Ġsp r", + "Ġconf inement", + "Ġexceed s", + "Ġhas h", + "ĠCompar ing", + "ĠS ensor", + "Ġf iring", + "k es", + "v ir", + "ine a", + "aff ected", + "Ġmod elled", + "Ġe ther", + "Ġsu ffer", + "â̲ â̲", + "о Ð", + "ĠB ir", + "Ä ģ", + "Ġsec reted", + "Ġcat heter", + "Ġy outh", + "ex pl", + "ĠD ar", + "ĠW HO", + "Ġfound ation", + "Ġhyd raulic", + "ĠCa rol", + "SS ION", + "Ġá ¹", + "f eld", + "av or", + "Ġpass es", + "vis iae", + "Ġapplic ability", + "Ġn ested", + "F l", + "ĠC atal", + "Ġmicro environment", + "lab els", + "Ġcrystall ization", + "In fo", + "Ġposition ing", + "Ġtriang les", + "Ġtr yp", + "ĠTrans ition", + "Ġset t", + "Ġneuro t", + "M on", + "Ġdro plets", + "ĠA RT", + "Ġcor ne", + "Ġmulti plicity", + "Ġec centric", + "Ġ iv", + "ĠM atter", + "lear ning", + "elect ro", + "ĠWe yl", + "Ġdec ide", + "ĠW r", + "ĠH ierarch", + "Ġap ical", + "Ġfail ures", + "Ġdiges tion", + "MI C", + "Ġge ographical", + "ĠEle ment", + "ĠTh ough", + "Ġch ron", + "lim ited", + "ĠDI SC", + "ĠArchitect ure", + "Ġvibr ational", + "ĠVari ous", + "Ġdynam ically", + "ak ed", + "Ġconven ience", + "ĠIs ra", + "ĠMD A", + "i tic", + "A u", + "Ġass istance", + "ven tional", + "mid t", + "os por", + "F ollowing", + "Ġinf erior", + "Ġn ickel", + "ra ine", + "p aren", + "Ġtit anium", + "F ield", + "Ġh oc", + "ĠCa uchy", + "ĠMc C", + "ĠSc reen", + "Ġneg lect", + "class es", + "ĠI F", + "Ġstrat ified", + "ens es", + "ĠPl ate", + "oz oic", + "Ġinstit utions", + "ĠTh ose", + "Ġgener ations", + "trans form", + "Ġpar titions", + "Rx iv", + "ent h", + "Ġs tic", + "ol ith", + "ĠF em", + "Ġag ar", + "be am", + "Ġprot ons", + "L U", + "Ġwork load", + "Ġmin erals", + "Ġm t", + "ll a", + "ĠPh armac", + "Ġconver ter", + "ĠMechan ical", + "Ġflav or", + "Ġphosph atase", + "Ġsum s", + "PC s", + "Ġiso forms", + "ig roup", + "py r", + "fe atures", + "Ġper c", + "Ġcomple teness", + "Ġfore sts", + "Ġdiv iding", + "ĠL ipschitz", + "period ic", + "Ġrec ycl", + "ĠN ag", + "Ġtw in", + "epti des", + "Ġco hor", + "Ġsearc hes", + "e ated", + "H g", + "ĠP U", + "ĠT ree", + "all ic", + "P F", + "Ġapp endix", + "ĠC ov", + "Ġcheck ing", + "Ġback bone", + "Ther mo", + "Ġactiv ating", + "ĠV ictor", + "Ġcri tic", + "ĠL em", + "group s", + "RE G", + "ĠO cc", + "SC C", + "ĠX RD", + "ĠVal ues", + "Ġsub type", + "Ġstret ching", + "OR M", + "s ome", + "Ġfl ip", + "Ġphen olic", + "Ġk illed", + "Ġsequ enced", + "usc ular", + "ab in", + "Ġquad r", + "Ġtransl ational", + "Ġsol ids", + "di rect", + "Ġprom otion", + "Ġcohor ts", + "ĠCl imate", + "ĠO ld", + "ĠS ir", + "g ue", + "str ate", + "ĠP oss", + "Ġrece ives", + "ĠVal idation", + "uc tive", + "Ġcere visiae", + "G u", + "is is", + "ce il", + "ĠPear son", + "ĠP relim", + "ĠG ran", + "CS F", + "Ġster ile", + "oflu orescence", + "b ad", + "Ġcol ored", + "comp ass", + "equ ation", + "j an", + "Ġcondition ing", + "Ġvo ice", + "Ġmen ing", + "Ġgrant ed", + "Ġrenormal ization", + "ĠLim it", + "th i", + "Ġa perture", + "Ġdos age", + "di rected", + "ĠBre ast", + "oc ular", + "b earing", + "s al", + "asc ul", + "uper vised", + "Ġmonol ayer", + "Ġmembers hip", + "ĠW ireless", + "sh ow", + "ĠMed ia", + "ĠV L", + "ess el", + "Ġdec oder", + "ĠM F", + "ĠCom position", + "ĠCl ark", + "P oint", + "ĠN ano", + "ĠD eg", + "N L", + "ĠB ox", + "Ġexpl oring", + "m olecular", + "O ther", + "ĠDiab etes", + "he ight", + "Ġkin ases", + "Ġadjust ing", + "Ġsp orts", + "off s", + "ĠI EEE", + "Ġt il", + "ĠInt ra", + "Ġplan ets", + "ĠEpid em", + "Ġto mato", + "Ġscaff olds", + "ĠMet abol", + "ĠGe ometry", + "im etry", + "ĠT en", + "th read", + "o hex", + "Ġpro poses", + "pr im", + "ĠPar ty", + "Ġquar ter", + "ĠSh i", + "Ġab err", + "ĠIn tr", + "Ġdirect or", + "aff e", + "ĠS us", + "ens ors", + "E le", + "Ġpol es", + "Ad ditional", + "Ġby pass", + "caten in", + "Ġunder taken", + "im ation", + "op or", + "Ġpres erving", + "Ġmultiple x", + "ĠRepresent ative", + "s is", + "ĠA G", + "ach y", + "Ġfr uits", + "Ġreconstr uct", + "ens en", + "Ġstrong est", + "Ġsc av", + "ĠChen g", + "ĠCor on", + "ĠObs ervation", + "ĠA ch", + "ĠGe org", + "ĠSV M", + "ĠC hern", + "Ġrevers al", + "v ia", + "im p", + "Ġdeploy ment", + "ĠH ad", + "Ġcircumst ances", + "ob i", + "Ġcur ved", + "Ind uced", + "ĠPos itive", + "im b", + "ĠPar is", + "ĠS tein", + "ic z", + "ĠC ath", + "Ġdraw ing", + "t ory", + "Ġcontin ental", + "Ġquantit atively", + "ac erb", + "Ġnorm s", + "ĠB E", + "S everal", + "do or", + "Ġplate au", + "G al", + "Ġc ivil", + "ĠF ix", + "L AB", + "oc cal", + "Ġsor ted", + "ĠâĢ Ŀ", + "Ġed iting", + "ĠChris tian", + "Ġclar ify", + "Ġwavegu ide", + "b ell", + "Ġded uced", + "od ec", + "utri tion", + "Ġcomp ressive", + "ĠE U", + "ĠReg ression", + "Ġrank ed", + "Ġestim ators", + "Ġab ilities", + "Ġbelief s", + "th ree", + "Ġâĩ Ĵ", + "rolog y", + "Ġaut onomous", + "ĠS z", + "sc hem", + "ĠAL T", + "ĠPattern s", + "Ġex on", + "Ġlif estyle", + "f ill", + "ĠC AR", + "ĠDom ains", + "Ġpa id", + "Ġt ab", + "ĠCo hen", + "air y", + "Ġshe ep", + "Ġse aw", + "ĠK ong", + "g as", + "Ġres erved", + "Ġres il", + "Ġob l", + "car box", + "ĠGover nment", + "up per", + "ract ing", + "Ġg angl", + "ĠR V", + "Ġbron ch", + "Method s", + "ĠL iver", + "Ġgu ess", + "cha romy", + "IC E", + "Ġcongen ital", + "Ġk a", + "Ġsp anning", + "ĠRec omm", + "e a", + "Ġcon vention", + "Ġshe ets", + "Ġtherm o", + "Ġqualit atively", + "Ġox ides", + "Ġcongr u", + "ĠJ er", + "Ġpres ervation", + "ĠB T", + "ĠD MSO", + "Ġcom plication", + "Ġsurviv ors", + "Ġreduc t", + "Ġdes cent", + "Ġsuc rose", + "ĠCour t", + "Ġmetabol ite", + "ĠM ath", + "ĠSec urity", + "ĠNot ably", + "ĠSt em", + "Ġdwar f", + "b c", + "Ġre vis", + "ĠK l", + "ĠG h", + "Ġman ager", + "Ġinvest ment", + "Ġmo tility", + "E m", + "ĠM r", + "as ic", + "ĠB os", + "Ġinsp ired", + "plac ian", + "Ġe ase", + "Ġtors ion", + "ĠDir ichlet", + "Ġsple en", + "ag ation", + "on ate", + "ĠT rial", + "Ġturn over", + "Ġselec tively", + "ĠÍ Ĵ", + "ian o", + "Ġnon trivial", + "i asis", + "Ñ ģ", + "ĠGu o", + "Ġaddress es", + "Ġuniqu eness", + "Ġwith draw", + "ri z", + "Ġcomputation ally", + "Ġperson ality", + "A X", + "went y", + "Ġgover n", + "ber ts", + "Ġrob ots", + "Ġread y", + "Ġdi ets", + "l it", + "M y", + "ĠRe ve", + "ĠL os", + "inf rared", + "Ġint ram", + "l ated", + "pl ankton", + "ĠG rant", + "pi per", + "Ġanten nas", + "Ġb ol", + "f p", + "ĠV it", + "Com par", + "ok en", + "Ġke ys", + "ĠCl ub", + "in ery", + "ĠF oot", + "Ġwarm ing", + "m ond", + "Ġm iles", + "Ġspe aking", + "ĠI v", + "Ġconform ational", + "ĠO k", + "Ġun ified", + "Ġassemb led", + "Ġinver ted", + "Ġf elt", + "correspond ing", + "ĠE CM", + "ĠN SC", + "Ġind oor", + "g ov", + "Ġantagon ist", + "unc hed", + "ĠJ ava", + "ĠComb ined", + "tiv ities", + "Ġaltern ating", + "ã Ĥ", + "ĠDiagn osis", + "Ġdistinc tion", + "le igh", + "ĠT ogether", + "Ġparticip ating", + "Ġgl omer", + "oc he", + "Ġcopy right", + "ĠG TP", + "ĠV ar", + "Ġammon ium", + "Ġfacilit ates", + "Ġperf usion", + "ĠL B", + "f ull", + "Ġre ti", + "ifer ase", + "Ġimmunos up", + "ĠIm plementation", + "Ġpo res", + "ĠB B", + "ĠB ud", + "ĠV O", + "ĠV o", + "Ġphysic ian", + "ĠA UC", + "Ġcertain ly", + "μ m", + "ĠK ol", + "Ġw rap", + "m iddle", + "Ġsil encing", + "Ġfresh water", + "ig an", + "are a", + "A I", + "Ġmicro tub", + "Ġarrang ed", + "struc tive", + "ĠReg ular", + "ĠF ile", + "al ks", + "Ġpl ain", + "Ġinte grable", + "ĠM embrane", + "ist ors", + "Ġaqu atic", + "Ġwork flow", + "ĠG er", + "ul ant", + "Ġactiv ates", + "T erm", + "ĠUp on", + "ĠP ut", + "V ar", + "ĠO D", + "hal f", + "Ġul cer", + "ĠB O", + "ĠG y", + "ren ces", + "Ġpur ity", + "Ġarri ve", + "ĠSign ificant", + "ĠM AC", + "ĠOther wise", + "o ured", + "Ġt an", + "ĠR L", + "ĠQ TL", + "Ġammon ia", + "v mode", + "Ġmagn esium", + "Ġac knowled", + "Ġaltern atives", + "id ents", + "r Vert", + "ĠCom plete", + "ĠB one", + "y er", + "ĠB ab", + "Ġe ut", + "Ġno vo", + "disci plinary", + "Ġsevere ly", + "uk i", + "ĠP N", + "leave vmode", + "cl ip", + "ĠSy nd", + "ĠMIM O", + "ade qu", + "ĠArc tic", + "ly cer", + "RE T", + "ens ed", + "co ated", + "V P", + "Ġl akes", + "Ġch urch", + "Ġhom ologous", + "Ġoxid ase", + "ĠA ud", + "Ġincre ment", + "Ġneut rinos", + "ar bon", + "T YPE", + "iz umab", + "ut able", + "Ġimp lying", + "ĠMo tion", + "Ġâī ĥ", + "Ġp ages", + "Ġplaus ible", + "ĠN L", + "Ġis otop", + "ĠH yd", + "A tt", + "lat tice", + "sh ore", + "Ġsuc ceed", + "Ġsup posed", + "ĠTrans mission", + "D imensional", + "ingu istic", + "Ġcont ours", + "Ġcon comit", + "Ġagre es", + "ĠD ani", + "qu ar", + "Ġsh ield", + "Ġo zone", + "ĠT et", + "lb rack", + "Ġw at", + "Ġcyt ochrome", + "ta iled", + "p ix", + "Ġco ex", + "ĠVi ew", + "od ef", + "ĠW ild", + "ĠL E", + "h op", + "Ġpoint ing", + "unc ture", + "Ġec ology", + "Ġb ab", + "re a", + "eg o", + "Ġviol ence", + "Ġt RNA", + "ĠR N", + "p ent", + "ore l", + "ĠPar allel", + "Ġdri ves", + "nob reak", + "Ġh olog", + "Ġprob able", + "Ġent ering", + "Ġs ink", + "Ġsw elling", + "pro ducing", + "âĨĴ âĪŀ", + "ĠSaf ety", + "Ġanaly se", + "ser ies", + "Ġdri vers", + "K S", + "ĠR MS", + "Ġgene tics", + "ĠF red", + "Ġsub m", + "Ġscienti sts", + "ĠF D", + "ĠSol utions", + "ĠF ab", + "Ġen compass", + "commut ative", + "Ġadi abatic", + "but yl", + "PE G", + "Ġα β", + "ĠSt an", + "Ġclust ered", + "Ġhold ing", + "ĠB eck", + "ĠY an", + "Ġast er", + "Ġecon om", + "Ġign ored", + "u ro", + "yl es", + "ubb les", + "Ġf ate", + "Ġper ceptions", + "Ġl in", + "é n", + "Ġact u", + "Ġar sen", + "Ġb a", + "ep och", + "ĠS tim", + "Ġmedic ations", + "EC s", + "ĠMin istry", + "ĠPubl isher", + "Ġdep ri", + "Ġob struction", + "ĠmRNA s", + "Ġbro ther", + "Ġcros sover", + "ĠT urb", + "t ation", + "Ġt ank", + "ĠM em", + "Ġint estine", + "Ġmicrogl ia", + "ĠMax well", + "Ġjuris dic", + "Ġphen yl", + "hy per", + "um s", + "ĠH IF", + "ĠS hen", + "Ġcheck point", + "ĠBrown ian", + "Ġâĭ Ĩ", + "ĠSt rain", + "ĠExt raction", + "Ġbatter ies", + "ĠP le", + "ĠCon ditions", + "Ġincons istent", + "ĠH ost", + "yp ical", + "Ġc rops", + "al g", + "ĠF I", + "ant a", + "Ġfound ed", + "Ġmark s", + "dist ribution", + "ĠÎ ¹", + "Ġh ors", + "Ġsn ap", + "W M", + "Ġmanifest ations", + "em pl", + "Ġprov ing", + "le ading", + "ĠA CE", + "ĠL ED", + "ch annels", + "Ġl ift", + "F unction", + "in ase", + "super vised", + "ĠU ser", + "Ġphys iology", + "Ġlink ing", + "p ressed", + "Ġ iff", + "ĠJ im", + "Ġglut athione", + "ĠT I", + "Ġan e", + "en osis", + "Ġcollec tions", + "Ġgenetic ally", + "ĠFil ter", + "ĠCh icago", + "ĠServ ices", + "Ġsuper symmetric", + "Ġstri king", + "Ġir rig", + "oc occal", + "Ġfib res", + "Ġecos ystems", + "um ing", + "f ly", + "Ġlung s", + "Ġcovari ates", + "Ġlay out", + "ĠR aj", + "Ġsumm ation", + "abl ed", + "Ġfre ely", + "Ġre vised", + "Ġcut s", + "ĠIntegr ated", + "Ġph armaceutical", + "Ġrespir ation", + "ĠB ill", + "Ġest rogen", + "ra int", + "Ġpercent ages", + "ĠP f", + "ĠG F", + "methyl ene", + "Ġorig ins", + "tr im", + "mat ch", + "it ney", + "ĠY e", + "Ġalloc ated", + "manif old", + "ĠT ris", + "ĠL ys", + "Ġcomp ressed", + "ore r", + "Ġhim self", + "Ġqu in", + "ĠAss embly", + "sing le", + "tem poral", + "Ġs oph", + "Ġepidem iological", + "Ġknock out", + "Ġcomp ares", + "ĠSens itivity", + "Ġgir ls", + "ĠV alley", + "al id", + "ĠSchem e", + "ĠCO MP", + "Ġrefrac tive", + "ĠOff ice", + "Ġlat est", + "Ġp rices", + "car boxyl", + "Ġecon omy", + "Ġbo oks", + "ĠD D", + "Ġne oplas", + "app ings", + "Ġfol ding", + "moment um", + "pot ent", + "Ġpref ix", + "ĠRiemann ian", + "ĠER K", + "ĠPath way", + "Ġlar val", + "ol or", + "Ġat titude", + "geq slant", + "Ġg ates", + "Ġagon ist", + "ĠïĢ ¨", + "ĠM CF", + "ost atic", + "m icro", + "Ġdo ubl", + "ĠPar ameter", + "Ġequival ently", + "Ġs rc", + "M ost", + "ĉ ĠĠĠ", + "Ġrhe umat", + "ĠH um", + "reg ion", + "Ġwind s", + "Ġquad rup", + "cal es", + "ulf ide", + "bal anced", + "U nder", + "gener ated", + "oplas mic", + "Ġweight ing", + "ĠN ov", + "vel oc", + "util s", + "ĠA CT", + "Ġvulner able", + "d c", + "Ġstrom al", + "Ġex acerb", + "H V", + "Ġperfect ly", + "t xt", + "di rection", + "og on", + "Ġb im", + "ĠM arg", + "it ons", + "Ġterm ination", + "ed a", + "Ġpre treatment", + "Ġimportant ly", + "Ġd uc", + "Ġartif acts", + "St ud", + "ot ensin", + "rel and", + "ah n", + "Ġdeploy ed", + "ĠE F", + "ens ing", + "ĠC ard", + "ĠJ ordan", + "ap unov", + "Ġanest hesia", + "Ġatheros clerosis", + "in ner", + "struct ural", + "ĠAs p", + "through put", + "ur ities", + "Ġin set", + "with out", + "Ġac quire", + "Ġcomb ines", + "ĠSh ar", + "M ASK", + "ĠL iter", + "Ġcons cious", + "isc ell", + "cons istent", + "y st", + "Ġfil aments", + "ĠAl ice", + "ĠG round", + "Ġm TOR", + "vers al", + "Ġline ages", + "par ticles", + "a roscopic", + "ĠPro ced", + "Ġorient ations", + "ĠM ouse", + "Ġaccording ly", + "Ġsuppress or", + "Ġdestr uction", + "O V", + "ĠProtein s", + "PE CT", + "Ġc up", + "Ġmon omer", + "plement al", + "Ġneutroph il", + "Ġer up", + "Ġt ac", + "Ġasympt omatic", + "ĠEm bed", + "ĠRad iation", + "ĠG ame", + "Ġneed le", + "Ġre use", + "ĠD utch", + "Ġjuven ile", + "Ġmoment a", + "ĠB ose", + "Ġde veloper", + "Ġresidual s", + "Å ¡", + "Ġc ognition", + "ĠReg ional", + "Y ou", + "ĠCon cent", + "oc in", + "ĠPar tial", + "Ġcomplet es", + "ĠSing h", + "ĠEx c", + "ĠIs olation", + "ĠStruct ures", + "Ġinter mitt", + "Ex ception", + "Ġanaly tically", + "Ġelectric ity", + "â ĭ", + "Ä į", + "Ġprote ome", + "Ġ ic", + "k al", + "inu x", + "ĠB eyond", + "Ġim plied", + "AS H", + "Ġcl one", + "ĠRuss ia", + "ĠH od", + "tebr ates", + "Ġpro xy", + "hold er", + "el ve", + "Ġval ley", + "ut ely", + "Ġj obs", + "rup tion", + "ro ids", + "ĠWh y", + "ep ing", + "ĠY et", + "Ġp yl", + "Ġb ra", + "il ization", + "et ers", + "Ġad ver", + "Ġo ve", + "k ernel", + "s amples", + "ordin ate", + "ĠAssum ing", + "Ġcontamin ated", + "Ġb ipolar", + "Ġl ac", + "Ġl uc", + "Ġcentrifug ation", + "B oth", + "Ġ nd", + "Ġt ib", + "B efore", + "ĠImmun e", + "Ġas h", + "Ġcondition ed", + "ĠR ank", + "N OS", + "Ġnanopar ticle", + "Ġdepend encies", + "Ġhouse holds", + "ag ers", + "Ġspect rophot", + "Ġb ile", + "ĠH ans", + "ĠAcknowledg ements", + "r atio", + "ĠSecond ary", + "Ġdown regulated", + "f ixed", + "O bs", + "ĠH L", + "Ġs ends", + "ting s", + "Ġf i", + "ĠPa per", + "Ġultra violet", + "ĠB all", + "Ġdr astic", + "ail ure", + "o il", + "ex change", + "ĠD an", + "ĠAut o", + "Ġarch ae", + "ĠCol lection", + "Ġantiv iral", + "ĠC hemistry", + "Ġf err", + "cho ice", + "v ac", + "ol ipid", + "Ġd anger", + "ĠL ittle", + "Ġde hyd", + "Ġoccas ion", + "oprop yl", + "ab e", + "Ġinterfer on", + "Ġex port", + "on itrile", + "p d", + "ĠCon text", + "ru z", + "ĠD ys", + "Ġassemb l", + "Ġoil s", + "Im age", + "row ing", + "Ġane urys", + "Ġliqu ids", + "Ġac tively", + "Ġev apor", + "ĠP resent", + "Ġconstit utive", + "ĠS ite", + "Ġsc ript", + "Ġrepe ats", + "ĠS IR", + "ĠFil m", + "ĠSant a", + "ĠRepresent ation", + "ĠA ma", + "ord on", + "ĠMo lecule", + "Ġgover ning", + "ĠSo il", + "V er", + "Ġphot onic", + "tif y", + "ĠLew is", + "at hered", + "Ġcategor ical", + "iscell aneous", + "up date", + "Ġdefic it", + "Ġadj uvant", + "ĠHen ry", + "G roup", + "ist ency", + "ag raph", + "ĠImpro ving", + "E l", + "Ġfl ame", + "rog ate", + "om orph", + "Ġqu bits", + "Ġillustr ation", + "ĠFlor ida", + "ĠD G", + "big cup", + "Ġprov ince", + "egrad ation", + "ĠLand au", + "Ġgr ating", + "Ġins ects", + "Ġd raft", + "ĠH b", + "Ġs s", + "ĠR as", + "Ġmuc osa", + "Ġhydrox yl", + "Ġmod est", + "Ġconfir ming", + "ĠGalax ies", + "G aussian", + "ĠRet rie", + "Ġrest ored", + "m emory", + "Ġreinfor ced", + "r ific", + "Ġass isted", + "Ġaffili ations", + "R C", + "duc er", + "ĠInt ellig", + "ĠA SD", + "mod ium", + "Ġo mitted", + "ok ers", + "Ġgu ided", + "Ġgraph ical", + "ĠQ ual", + "D ue", + "Ġn emat", + "vari able", + "Ġsenes cence", + "Ġpip e", + "Ġsustain able", + "Ġteac her", + "Ġth ing", + "ĠGP U", + "T B", + "Ġre form", + "Ġref lex", + "Ġindic ative", + "ab out", + "Ġop i", + "eff ect", + "Ġdispers ed", + "k h", + "it helial", + "ĠT reg", + "i pl", + "ĠAut omatic", + "Ġn itro", + "com plete", + "Ġbos ons", + "Ġp ac", + "Ġavoid ing", + "is l", + "pl asty", + "respons ive", + "d est", + "ĠB rad", + "ĠDec ision", + "ĠDisc overy", + "Ġchick en", + "m us", + "ĠW ITH", + "Ġt ric", + "Ġqu artz", + "onstr uction", + "ĠField s", + "Ġass im", + "opro t", + "Ġguarant eed", + "f at", + "ic ts", + "Ġch ol", + "id o", + "ĠK L", + "Ġch itosan", + "ĠN d", + "ĠO scill", + "Ġevol ve", + "c u", + "Ġm ast", + "Ġam ph", + "tor ch", + "V is", + "enti ty", + "ĠAd am", + "Ġdev oted", + "Ġeth ical", + "Ġprem ature", + "Ġconsum er", + "Ġrecurs ive", + "Ġglu on", + "Ġmoder ately", + "Ġmod alities", + "Ġcan al", + "for ce", + "ĠCh lor", + "sl ash", + "st en", + "Ġcommerc ially", + "ong s", + "Ġstim ulate", + "atin um", + "ĠR ail", + "Ġconv ective", + "Ġarter ies", + "in v", + "ĠW ol", + "ĠL ung", + "let es", + "raph y", + "ĠH I", + "Ġgraph ite", + "Ġhous ing", + "e ach", + "Ġcal or", + "acet amide", + "roc hemical", + "Ġhand s", + "Ġelucid ate", + "ĠCh and", + "ro ad", + "nov a", + "ĠLine age", + "Ġr am", + "Ġf ight", + "Ġrecommend ation", + "Ġamong st", + "Ġswit ches", + "ber ry", + "Ġthere in", + "al gebras", + "ĠT aken", + "az z", + "Ġf urn", + "Ġam el", + "Ġteac hers", + "ar n", + "Ġavoid ed", + "Ġaver ages", + "am er", + "ĠCon dition", + "Ġdis location", + "ir con", + "Ġadoles cent", + "Ġt ur", + "en v", + "Ġz e", + "D L", + "load ing", + "ic idal", + "c ategory", + "ĠD B", + "Ġmuc osal", + "ĠR G", + "Ġtaxon omic", + "Ġmut agen", + "ĠSt age", + "n ecess", + "ĠP erm", + "Ġoc clusion", + "Ġexplo ited", + "Ġana erobic", + "ul ed", + "Ġwant ed", + "ĠComb ining", + "Ġsub cutaneous", + "Rec omm", + "Ġdiscuss es", + "Ġcounter part", + "ĠF B", + "Ġadsorb ed", + "d on", + "M any", + "ĠSwed en", + "ĠAnd rew", + "enh anced", + "Ġdoc tor", + "ĠKore an", + "ĠS AR", + "Ġm ating", + "at uration", + "ĠL atin", + "Ġsor ting", + "Ġsk ip", + "O s", + "Ġw ife", + "Ġcom mittee", + "l vert", + "ĠA CC", + "ĠCom m", + "Ġsub tle", + "ĠSur vival", + "b ecause", + "Ġfe at", + "ĠPort ug", + "AR Y", + "ĠI SB", + "it ron", + "Ġs ectors", + "Ġadj oint", + "ĠAlex ander", + "Ġimp urity", + "ĠMar ine", + "l act", + "Ġtrap ping", + "Ġgeneral ize", + "fil ter", + "Ġpolar ity", + "Al so", + "Ġstabil ized", + "ĠVir gin", + "Ġst ores", + "P AGE", + "Ġdraw back", + "Ġâİ ª", + "j et", + "Ġsubstit uted", + "L INE", + "Ġoutper forms", + "Ġterm ed", + "Ġweek ly", + "Ġpoly c", + "Ġf used", + "Ġfer romagnetic", + "l r", + "ell ites", + "ĠT urn", + "ĠC ulture", + "pr ise", + "Å Ĥ", + "om position", + "elf are", + "ĠGo ogle", + "o arth", + "Ġ ë", + "Ġm ist", + "ĠMat hematics", + "S ET", + "Ġepoch s", + "Ġcont ras", + "ish ment", + "ĠFirst ly", + "Ġdecl ared", + "a ur", + "ĠP ed", + "Ġreplic ate", + "Ġel igible", + "Ġconc aten", + "Ġc ig", + "Ġtri plet", + "f ound", + "ĠC z", + "Ġaccompl ished", + "Ġgover ned", + "on uclear", + "ĠN Y", + "ĠEth iop", + "Ġin ject", + "Ġe osin", + "ann on", + "ol o", + "ĠM HC", + "Ġpre operative", + "Ġd ates", + "Ġs igma", + "L ong", + "ĠRes on", + "Ġsympt omatic", + "Ġvolunte ers", + "Ġco operation", + "Ġar r", + "Ġclon ed", + "Ġd ent", + "ĠS ob", + "Ġcath ode", + "ct x", + "Ġ encephal", + "Ġp iv", + "vi ve", + "um etric", + "ĠF F", + "Ġunde restim", + "Ġc oded", + "Ġanal ges", + "spect ral", + "Ġattrac ted", + "Ġtw enty", + "Ġin active", + "Ġvic tim", + "Ġhold er", + "ogen es", + "Ġsuff ering", + "re x", + "Ġpro phyl", + "ĠUnivers al", + "Ġden om", + "st olic", + "ans ion", + "SI ZE", + "ĠHC V", + "Ġtechn ological", + "CN N", + "en ching", + "Ġdeb ris", + "ĠBound ary", + "link ing", + "Ġstop ped", + "ĠD ie", + "ĠCos m", + "Ġturn ing", + "Ġgly coprotein", + "ĠK umar", + "Ġp g", + "ĠB Y", + "Ġr ising", + "ĠR OC", + "Des pite", + "ĠBo olean", + "il der", + "Ġexpon ents", + "in ters", + "print f", + "Ġl it", + "t rack", + "Ġf idelity", + "Ġsm oke", + "ot emporal", + "Ġad missible", + "ĠBol tzmann", + "T F", + "ol ite", + "li ament", + "Ġcalc ulus", + "iti zed", + "Ġdiver gent", + "Ġcolon ization", + "Ġconver gent", + "ĠH as", + "Ġconsum ers", + "Ġmy c", + "Ġcon tig", + "Ġepidem iology", + "é s", + "ĠAss oci", + "g iven", + "Ġwh ilst", + "ĠK ur", + "Ġreason ably", + "Ġaer obic", + "se par", + "Ġche cks", + "ĠSem antic", + "Ġserv ing", + "ĠAt mosp", + "Ġoxid ized", + "c oupled", + "Ġbio Rxiv", + "Ġtun ed", + "usp ended", + "Ġindirect ly", + "ĠC AD", + "ĠCurrent ly", + "Ġbehavi ours", + "ĠPP AR", + "r ors", + "ere b", + "Ġwid ths", + "di agonal", + "erv ice", + "Ġo le", + "me ans", + "IM E", + "ĠT racking", + "Ġac knowledge", + "ĠH on", + "ĠTechn iques", + "ĠOx id", + "bl ind", + "Ġdi ast", + "nam ed", + "asi tic", + "Ġprepar ations", + "ĠAr th", + "Ġpres erves", + "Ġf asc", + "Ġwave form", + "ĠC rystal", + "Ġunc om", + "Ġel ast", + "Ġfunction ally", + "H om", + "ĠCo ast", + "Ġop tic", + "ĠAltern atively", + "on yl", + "ĠL ig", + "al dehyde", + "Ġsim ulator", + "Ġdram atic", + "if era", + "Ġexhib iting", + "Ġbehaviour al", + "th ick", + "xt ure", + "Ġexec utive", + "Ġcondens ate", + "ĠOut comes", + "T ext", + "oin ted", + "ĠCopy right", + "Ġd c", + "od d", + "ĠD iversity", + "ch ip", + "ĠBu ilding", + "Ġpuls ed", + "har monic", + "Ġclinic ians", + "d p", + "Ġq PCR", + "mark s", + "Ġapprec i", + "ĠL aser", + "Ġsize of", + "y rene", + "Ġco operative", + "gener ative", + "ĠL ib", + "Ġdispers al", + "Ġevol ving", + "ĠSt atus", + "Ġsuper con", + "ĠM amm", + "Ġinters titial", + "isen berg", + "Ġâ ľ", + "Ġconf ocal", + "Ġmod ulates", + "h our", + "Ġper oxide", + "depend ence", + "Ġperturb ed", + "ill ation", + "Ġpl aque", + "ĠNe umann", + "Ġtrig gers", + "om ain", + "ĠAd ministration", + "ol ia", + "ĠM IC", + "osa ic", + "ĠG B", + "text normal", + "Ġdomin ance", + "ĠEx per", + "C AM", + "ĠAb out", + "ĠG arc", + "Ġsummar izes", + "A pp", + "charomy ces", + "tif icial", + "Ġgly cerol", + "ĠAssum ption", + "Ġt ect", + "ĠF W", + "Ġcot ton", + "gen eral", + "ĠF ern", + "P t", + "Ġwork er", + "Ġan ion", + "gram s", + "re q", + "Ġlo oks", + "Ġimplement ations", + "ĠCol umb", + "ag i", + "ĠAt tention", + "ĠTe am", + "on ing", + "on ential", + "tin y", + "ĠHigh ly", + "text up", + "Ġinver tible", + "oc ortic", + "In f", + "ĠOff icial", + "ĠMod elling", + "Ġincl usions", + "Ġbl ank", + "Ġs ight", + "ĠG amma", + "Ġlept on", + "Ġpneumonia e", + "Ġro tor", + "Ġeth nic", + "Ġre tain", + "v arying", + "ĠE B", + "Ġast rocytes", + "ĠN orm", + "Ġnan om", + "class ical", + "Ġsh adow", + "ĠRef erences", + "ĠF S", + "Ġnon negative", + "b ond", + "ĠC oh", + "Ġnum py", + "Ġo ct", + "sp an", + "rac ts", + "Ġnot ably", + "Ġsoph istic", + "P AR", + "Ġhorm ones", + "Ġtens ors", + "ĠÌ Ħ", + "ĠConst raints", + "Ġâ IJ", + "Ġtrans it", + "Ġrun time", + "aut hor", + "Ġprom pt", + "ĠS G", + "Ġg rate", + "ce mia", + "ĠLy apunov", + "con vex", + "Ġforecast ing", + "p ush", + "Ġjurisdic tional", + "à Ģ", + "Ġbiom edical", + "Ġepile psy", + "fe ature", + "wik i", + "Vi ew", + "Ġless er", + "Ġconjug ated", + "Ġwa iting", + "ĠW ord", + "I Z", + "Ġhydro xy", + "Ġdis p", + "Ġseed ed", + "fit ting", + "Ġstrat ification", + "Ġend point", + "Ġmedi ators", + "duc tive", + "Ġinj ections", + "ĠMicro bi", + "Ġins ert", + "ĠEm b", + "Ġstop ping", + "w elling", + "Ġirradi ated", + "Ġmetall icity", + "vin yl", + "Ġplasm ids", + "R ep", + "ĠDiff erenti", + "ĠSm art", + "ĠIdentif ier", + "ĠB F", + "rop ic", + "Ġkinem atics", + "Ġinoc ulated", + "C K", + "aus es", + "ĠReturn s", + "re ement", + "Ġantic ancer", + "Ġspecific ations", + "Ġadd s", + "Ġst ake", + "Ġwhe el", + "ü ller", + "ĠS on", + "Ġrup ture", + "Ġsol d", + "th an", + "Ġinter medi", + "ĠN ik", + "Ġt uple", + "est abl", + "Ġnor the", + "Ġsuppress es", + "Ġf et", + "Ġwas hing", + "Ġinter play", + "Ġregular ly", + "EX T", + "Ġemploy ees", + "y z", + "rup ted", + "et ts", + "ĠU AV", + "Ġdifferenti able", + "ing e", + "MD A", + "Ġh o", + "Ġt ags", + "Ġcomp atibility", + "Ġà ĥ", + "b us", + "ĠU C", + "Ġtok ens", + "Ġcl ients", + "Ġpres cription", + "ĠÌ Ī", + "ĠRe action", + "veloc ity", + "ĠN LR", + "ĠG ast", + "ĠPlas modium", + "ĠC ut", + "Ġn as", + "gra ined", + "Ġchrom osomal", + "Ġpossess es", + "Ġm ath", + "Ġe lected", + "plac ement", + "Ġcollect ing", + "Ġg els", + "ai re", + "Ġdeform ations", + "ra ise", + "Ġfl ank", + "sulf anyl", + "z ens", + "pri ate", + "Ġchlor ophyll", + "ab i", + "avail able", + "Ø §", + "Ġt ack", + "field s", + "Ġrich ness", + "Ġimpl ants", + "ob enz", + "id ential", + "Ġbill ion", + "ut or", + "ĠISB N", + "Ġins urance", + "N ET", + "Ġin adequ", + "Ġmerg ed", + "ĠR ange", + "Ġavoid ance", + "ĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ ĠĠĠĠĠĠ", + "ric s", + "Ġexcl usive", + "L V", + "Ġï£ ²", + "Ġcategor ized", + "Ġultrason ic", + "ip e", + "ic ans", + "ĠA PP", + "Ġtra umatic", + "B a", + "ĠAss ay", + "ĠG rid", + "ĠClass ical", + "ĠD ES", + "Ġsoy bean", + "Ġtop ography", + "ĠCont roll", + "Ġemo tions", + "Ġcarbohyd rate", + "Ġcons ol", + "ox yl", + "Ġbifurc ation", + "Ġco il", + "f ind", + "Ġw itness", + "ĠL F", + "th reshold", + "Ġaddress ing", + "Ġsc rew", + "Ġact or", + "ĠW ell", + "Ġï£ °", + "ï ĺ", + "ĠD F", + "ĠCor poration", + "ĠMitochond rial", + "Ġk pc", + "und ers", + "Ġfib rin", + "ax el", + "Ġpol yt", + "Ġshap ed", + "re z", + "ste resis", + "ĠComp rehens", + "Ġï£ ³", + "d h", + "Ġsem ic", + "Ġm ot", + "ĠDav is", + "sk a", + "ĠL H", + "Ġexpans ions", + "ack s", + "Ġoptim izing", + "e ak", + "ĠQ i", + "m ul", + "og raft", + "Ġsu icide", + "cal ar", + "ĠSc ott", + "Ġth inking", + "Ġdirec tional", + "Ġsurfact ant", + "Ġdegrad ed", + "Ġregim en", + "it ative", + "ĠV ersion", + "ĠM aster", + "ĠSim ulations", + "NC BI", + "l ip", + "Ġre agents", + "Ġpost ed", + "os us", + "Ġlay ered", + "ĠSpect rum", + "ĠGraph s", + "bur st", + "Ġl ived", + "Ġelement al", + "Ġï£ »", + "ĠDisc rete", + "Ġexcl uding", + "Ġorigin ating", + "ĠG ames", + "continu ous", + "AT ED", + "Ġpy ram", + "lu ent", + "Ġtw isted", + "ĠN b", + "ox icity", + "Ġsc r", + "Ġf un", + "ĠSeg mentation", + "Ġphen ol", + "Ġmet ers", + "ĠE igen", + "ĠWe ak", + "Ġschem atic", + "r one", + "Ġphil os", + "ti tis", + "ĠI reland", + "Ġg y", + "ĠPT M", + "Ġpack ing", + "il inear", + "z eros", + "Ġubiqu itin", + "ĠPress ure", + "Ġinf iltr", + "EN S", + "val idation", + "Ġpr one", + "Ġout line", + "h s", + "reng th", + "Ġat tain", + "Ġt we", + "Ġt andem", + "C an", + "Ġlat itude", + "uit ary", + "Ġvolt ages", + "ĠGa o", + "Ġpharmac okine", + "Ġcontext ual", + "Ġx yl", + "els on", + "ĠMet abolic", + "od en", + "ti les", + "ff icking", + "Ġdistill ed", + "Ġal ph", + "Ġpie zo", + "g rowth", + "Ġb ore", + "Ġredund ant", + "Ġdemonstr ation", + "Ġi k", + "Ġround s", + "ĠS ri", + "fig uration", + "ĠRay leigh", + "L ine", + "ov ol", + "Ġobstac le", + "c n", + "Ġbio active", + "ĠO A", + "phys ical", + "at idyl", + "AC C", + "h ow", + "Ġresult ant", + "ĠH ubble", + "ĠV or", + "Ġens uring", + "Ġannot ations", + "ac yl", + "stit uted", + "ĠAm b", + "feed ing", + "Ġpresum ably", + "Ġblock ade", + "Ġs oc", + "ĠU rb", + "Ġmulti plied", + "Ġdiff e", + "Ġreflect ance", + "ĠKey words", + "ĠBay es", + "odef iciency", + "ĠB inding", + "in ely", + "ex cept", + "ĠUl tr", + "ĠBrazil ian", + "N umber", + "Ġmass less", + "ĠCons istent", + "Ġcr isis", + "og s", + "Ġres idence", + "Ġim per", + "f ts", + "Ġcapt ures", + "ĠSynd rome", + "Ġdimension ality", + "j un", + "Ġex haus", + "ĠMod ern", + "Ġperc enti", + "Le vel", + "ĠRespons es", + "Ġla unched", + "Ġre pos", + "ĠK am", + "at ility", + "Ġcaro tid", + "ro tic", + "ĠM and", + "U B", + "ĠM ixed", + "Ġindex es", + "Ġcis platin", + "ic an", + "ion ine", + "Ġh ab", + "ĠI ce", + "ĠG T", + "ĠAg g", + "ĠLD L", + "Ġvolcan ic", + "d B", + "ĠElect ric", + "Ġt mp", + "Ġgrid s", + "l iquid", + "p rom", + "ĠG AL", + "Ġp estic", + "Ġhel ium", + "Ġï£ ¹", + "ĠD ong", + "Ġmagn ification", + "k ip", + "ĠG rad", + "ĠWe i", + "ĠPD F", + "ĠGl uc", + "P ol", + "Ġtumor igen", + "yr in", + "Ġshel f", + "ad her", + "enti als", + "s n", + "Ġcultiv ars", + "Ġorbit als", + "ĠP EG", + "ĠAn ne", + "en o", + "Ġatt ended", + "oph ore", + "ish op", + "Ġf riends", + "pos able", + "Ġim pose", + "Ġend emic", + "Ġs ick", + "shif ts", + "ĠOut put", + "L M", + "ĠM iscellaneous", + "Ġthous ands", + "ĠD ataset", + "Ġperturb ative", + "op rec", + "Ġb ene", + "Ġre ef", + "Ġfoss il", + "Ġc ited", + "plic ates", + "Ġrel ates", + "ĠV II", + "Ġanti fer", + "Ġglass es", + "clos ure", + "Ġrub ber", + "Ġb ird", + "Ġsuper symmetry", + "Ġmes on", + "he ll", + "Ġpar ties", + "k ar", + "ĠH ur", + "ĠE A", + "ĠSt ars", + "oth ing", + "h ot", + "ill ar", + "AS P", + "he v", + "ï ĥ", + "a ques", + "Ġcoordin ated", + "ĠIs lands", + "en able", + "Si O", + "Ġexception al", + "C omb", + "ĠL ike", + "Ġbroad ly", + "ĠB ac", + "Ġn il", + "ipar tite", + "r ations", + "Ġre write", + "Ġsal ts", + "d imension", + "ĠVe hic", + "Ġhundred s", + "ĠU r", + "Ġend points", + "ĠMOD EL", + "ĠH BV", + "ĠVir tual", + "ĠCon fl", + "ĠPrac tice", + "ĠAF M", + "Ġadvers arial", + "Ġdi ameters", + "Ġtrans ported", + "RE M", + "ĠB art", + "Ġed ition", + "Ġturb ine", + "Ġmin us", + "otechn ology", + "I g", + "Ġbig ger", + "ab ul", + "Ġperoxid ase", + "wh ite", + "ĠS ed", + "di hydro", + "Ġseg regation", + "Ġreduct ase", + "Ġhor iz", + "Ġinf initely", + "avail ability", + "Ġactiv ator", + "Ġc ensus", + "press ing", + "Ġspir it", + "con ver", + "ĠQuantif ication", + "omer ase", + "Ġrel apse", + "ĠF inal", + "Ġover weight", + "a per", + "Ġformul ae", + "r r", + "Ġfem oral", + "Ġfo am", + "o tics", + "Ġprovid er", + "Ġinstr umental", + "Ġadv ice", + "Ġoccup ation", + "ass embly", + "bi as", + "ĠN OT", + "re stric", + "ĠProt ocol", + "ĠCandid a", + "ĠR hod", + "ard en", + "f under", + "os ens", + "Ġpar ams", + "f ront", + "Ġex erc", + "Ġgal actic", + "r vert", + "Ġim balance", + "Ġk illing", + "ĠGen omic", + "Ġ ip", + "Ġc ave", + "Ġf alc", + "ĠR M", + "Ġcar ries", + "gl obal", + "Ġc ube", + "Ġrig orous", + "Ġcomput es", + "Q P", + "Ġexpos ures", + "c over", + "ological ly", + "O per", + "Ġp ec", + "Ġin homogeneous", + "Ġser vers", + "alian a", + "n b", + "Ġexplain ing", + "Ġshr ink", + "Ġcom orbid", + "eth oxy", + "outhe ast", + "Ġco urses", + "ĠN M", + "ĠSh ape", + "Ġfl ies", + "ĠM ir", + "Ġpublic ly", + "Ġphot ometric", + "vers ible", + "ole v", + "Ġvulner ability", + "Ġc ations", + "Ġsee king", + "U TR", + "Ġdecom posed", + "Ġh us", + "Ġdisapp ear", + "Ġenc ounter", + "Ġtransform ing", + "Ġpolymer ic", + "Ġdiscre tization", + "otox ic", + "ĠI ter", + "ĠM ari", + "Ġun fold", + "ĠAd ult", + "ob acillus", + "met al", + "ber ger", + "rap hene", + "resp ective", + "Ġsur vive", + "ov ich", + "Ġprot ects", + "ĠR og", + "Ġimmun otherapy", + "ĠD SM", + "Ġanalog y", + "ĠP ER", + "ĠPy thon", + "h um", + "ĠAd j", + "ĠLik ewise", + "Ġï£ ®", + "Ġstom ach", + "Ġin it", + "Ġw ires", + "Ġingredi ents", + "Ġper ceptual", + "H and", + "B ack", + "Ġm ood", + "Ġde formed", + "ĠRe ad", + "Ġrh iz", + "ĠOrgan ism", + "ĠInd ones", + "ann ot", + "ict ory", + "Ġt ended", + "ĠS ound", + "ia x", + "S r", + "ĠT ab", + "ĠLa placian", + "ol uminescence", + "back slash", + "i ologic", + "Ġtyp ename", + "ĠY ear", + "D ependent", + "Ġsl ides", + "Ġsac rific", + "Ġconcomit ant", + "ops ies", + "Big g", + "pe ak", + "ĠApp lying", + "Ġcod on", + "ĠSim ultaneous", + "ti se", + "Ġter tiary", + "ĠP oll", + "Ġre vision", + "RA F", + "x mm", + "Ġsu ited", + "ĠRecomm end", + "ĠR y", + "Ġs ake", + "Ġstret ch", + "ĠSam pling", + "Ġtub ular", + "Ġpar k", + "Ġul timate", + "Ġl ands", + "ĠCr iter", + "ass ay", + "m or", + "Ġd ocking", + "Ġgrad ual", + "Ġed itor", + "Ġpol ice", + "aff in", + "ĠDe ath", + "Ġpromot ers", + "ass ic", + "Ġwr iter", + "ĠVol ume", + "is o", + "Ġdis ag", + "tok en", + "Ġster oid", + "N on", + "ĠMet hyl", + "A meric", + "d ue", + "ĠL ess", + "Ġdy st", + "ĠStat ement", + "ĠT wenty", + "Ġaccess ed", + "Ġblot ting", + "ĠCO PD", + "Ġste am", + "Ġdescrip tive", + "ĠV ery", + "Ġcapac ities", + "ĠPers onal", + "ac id", + "ä hler", + "estiv al", + "Con text", + "Ġa str", + "Anal ysis", + "Ġse pt", + "Ġpr inted", + "d ual", + "am an", + "ere r", + "Ġweak ness", + "ì Ŀ", + "ĠTrans lation", + "Ġpropag ating", + "ĠS ections", + "ac a", + "Ġconf usion", + "I K", + "Ġframework s", + "Ġsitu ated", + "Ġst ays", + "n odes", + "c hen", + "art ments", + "Ġfree zing", + "w s", + "net t", + "Ġcontroll ers", + "Ġsil ic", + "LA ST", + "f oot", + "ĠDISC U", + "R H", + "rid ine", + "ĠRe v", + "per g", + "py rim", + "fl ags", + "ĠGu ide", + "Ġspe aker", + "tis ol", + "re ll", + "ĠD EG", + "Ġf u", + "ĠG ut", + "Ġsh ar", + "Ġgro ss", + "Ġcross es", + "wa velength", + "ĠAp plied", + "ï ve", + "ĠH B", + "ĠEd ge", + "Ġiner tial", + "Ġv ocal", + "pro duction", + "pat hetic", + "Ġplan etary", + "Ġs ister", + "Ġminim a", + "Ġlong est", + "Ġfl ash", + "Ġperiod on", + "Ġepid ermal", + "Ġflo ating", + "G ET", + "ĠT ake", + "p df", + "ĠL iquid", + "Ġremark ably", + "S ign", + "Ġshell s", + "oglob ulin", + "qu ilibrium", + "ĠMo ore", + "ĠAd vers", + "ĠMyc obacterium", + "Inv itrogen", + "Ġth aliana", + "B Y", + "ĠB it", + "Ġt s", + "Ġsynchron ous", + "y x", + "Ġpropag ator", + "ĠIncre asing", + "ipar um", + "Ġfree ze", + "ĠSe lective", + "af e", + "Ġstre pt", + "ph antom", + "ĠGener ally", + "Ġaltern ate", + "ĠCon vergence", + "//////// ////////", + "eng ing", + "ĠRandom ized", + "de velop", + "pred ict", + "ress or", + "Ġmat hematics", + "f r", + "ĠComput ation", + "ĠMal ays", + "Ġbreath ing", + "Th rough", + "ĠS IM", + "Ġan ode", + "o ad", + "ĠAT CC", + "Ġconstitu ent", + "ĠMeas uring", + "Ġf MRI", + "Ġan emia", + "lies t", + "Ġhemisp here", + "Ġmaxim a", + "Ġtem porary", + "Ġd z", + "otox in", + "C ount", + "on ed", + "à º", + "Ġcollabor ative", + "Ġk b", + "Ġvers a", + "ĠSwed ish", + "ik a", + "Ġdial ysis", + "Ġper ovsk", + "Ġwill ing", + "ĠG reek", + "Out put", + "Ġsem igroup", + "Ġbott len", + "ĠGib bs", + "d ark", + "Ġrheumat oid", + "ur ring", + "mat ched", + "Ġsophistic ated", + "Ġcust omer", + "tetra hydro", + "X Y", + "b ug", + "Ġmor ning", + "ĠC VD", + "Ġm appings", + "ĠM SCs", + "ĠD H", + "Ġqu atern", + "he alth", + "Ä ±", + "Ġtem p", + "ĠJ ew", + "ĠI l", + "Ġvor tices", + "Ġser ine", + "ĠOx ygen", + "w eg", + "Ġexplan ations", + "P G", + "Ġc iti", + "Ġloc ality", + "== =", + "ĠTh om", + "Ġd airy", + "Bl ock", + "or dial", + "ak ov", + "Ġgli oma", + "Ġtrans action", + "Ġincre mental", + "anc he", + "R et", + "m agnetic", + "pyr rol", + "ĠP ic", + "Ġamel ior", + "oxid ant", + "rov iral", + "or atory", + "Ġs av", + "ĠSt ream", + "Ġsuper f", + "ĠIC U", + "Ġevid enced", + "Ġrepeated ly", + "Ġr ated", + "ĠP it", + "FA ULT", + "Ġh at", + "ĠCont ent", + "Ġiso form", + "V ER", + "Ġn odal", + "Ġschedul ed", + "Ġshould er", + "Ġt ap", + "Ġpor tal", + "Ġtra ps", + "ae v", + "ĠS OD", + "em atic", + "Ġen j", + "Ġretic ulum", + "ĠMin ister", + "ĠS el", + "Ġfall ing", + "ro st", + "N G", + "f d", + "n itro", + "ĠM ove", + "rel ativistic", + "eng es", + "ĠS ST", + "ĠIn v", + "Ġfin ish", + "ĠPol and", + "os econd", + "ĠB AL", + "oarth ritis", + "Ġop tics", + "ĠS ky", + "Ġadv oc", + "Ġhemorrh age", + "Ġmod ulating", + "n is", + "Ġmach inery", + "Ġupd ating", + "Ġcharacter izing", + "ish man", + "Ġtem plates", + "ĠLa place", + "ĠEn s", + "Rec ently", + "or us", + "ar ts", + "diff usion", + "ĠLevel s", + "ag a", + "ĠIn j", + "ĠL ayer", + "Ġrem n", + "Ġelastic ity", + "Ġmere ly", + "Ġf ission", + "eng ue", + "m ake", + "Ġmon op", + "Ġure a", + "ĠSim on", + "mi R", + "ĠSecond ly", + "ur ic", + "ĠVari able", + "il is", + "Ġmultiplic ative", + "ĠNo ise", + "Ġswit ched", + "Ġnic ot", + "Ġeffici encies", + "he ma", + "Ġapp ointed", + "gu ided", + "Ġwin ning", + "ĠMechan ics", + "Ġne o", + "ĠBR CA", + "ud i", + "Ġcontain er", + "sh op", + "Ġsugges tions", + "K B", + "Ġsubstit ute", + "O x", + "V C", + "Ġst one", + "ann a", + "ĠDep ression", + "Ġcont emporary", + "Ġoutl iers", + "qu et", + "ĠZ heng", + "Ġoc cl", + "Ġal veolar", + "exp ressing", + "Ġcom fort", + "Ġign ore", + "Am ong", + "ĠKle in", + "Ġrhyth m", + "Ġimm ers", + "Ġfa ith", + "bl ing", + "Ġaug mentation", + "ĠPre vention", + "Ġhe par", + "Ġnot ations", + "Ġhemat opoietic", + "perf ect", + "Ġsh ares", + "not in", + "Ġpict ures", + "ĠAcknowledg ments", + "Ġt ick", + "Ġun related", + "ĠTo ol", + "Ġm as", + "os ocial", + "g est", + "us hed", + "Ġphosphor ylated", + "Ġcer amic", + "c ool", + "or ylation", + "Ġdef icient", + "Ġrelax ed", + "ĠAnal yses", + "ec raft", + "Ġret ina", + "ĠIn ternal", + "Ġsp ite", + "Ġrecip ients", + "Ġsh ut", + "Ġeth ylene", + "ĠG ulf", + "Ġun affected", + "ĠRes ource", + "ĠN et", + "Ġperp et", + "Ġsl ab", + "re port", + "Ġμm ol", + "Ġid x", + "Ġsk ill", + "ĠInd uction", + "Ġmalign ancy", + "Ġc v", + "Ġdiff ering", + "Ġappropri ately", + "ij ing", + "Ġwar rant", + "r ally", + "Ġal gae", + "we ights", + "c asts", + "Ġoc ular", + "rac ycl", + "Ġdomin ates", + "Ġle uc", + "W here", + "ph on", + "Ġsocio economic", + "itzer land", + "Ġresil ience", + "Ġneighbour hood", + "Ġt one", + "psy ch", + "ĠOrgan ic", + "Ġg ather", + "Ġfalc iparum", + "Ġengine ered", + "ĠAv ail", + "inter ing", + "Ġclim atic", + "ĠEvolution ary", + "N MR", + "Ġre v", + "cent ral", + "ĠS in", + "Ġdecl ined", + "op ausal", + "Ġal arm", + "Right arrow", + "se x", + "Ġenerge tic", + "ï Ĥ", + "Ġdisc s", + "Ġol factory", + "ur ipot", + "spect rum", + "sp ot", + "Ġhem oglobin", + "M ark", + "c ov", + "ar boxyl", + "Ġindic ations", + "Ġsal mon", + "Ġsearc hed", + "Ġend ed", + "rolog ic", + "r floor", + "Ġau tism", + "Ġs elen", + "ĠH ung", + "ĠInf erence", + "Ġmamm ary", + "l floor", + "Ġser oton", + "Ġfund ed", + "ĠVi et", + "Ġri vers", + "ĠRe infor", + "ur g", + "Ġalb icans", + "ĠTherm o", + "ERR OR", + "Ġmut ually", + "Ġir r", + "ĠR at", + "Ġim g", + "Ġlymph ocyte", + "ĠRef s", + "ĠS parse", + "hold ers", + "F ree", + "RE D", + "ĠG auss", + "Ġcirc adian", + "ĠJ in", + "Ġconstit utes", + "Ġw ors", + "Ġfeature d", + "oc ent", + "le te", + "Ġont ology", + "Ġbil ayer", + "ĠCam bridge", + "Ġencryp tion", + "rot ron", + "et ti", + "ĠA er", + "Ġcou ples", + "ra il", + "Ġtw ist", + "Ġrid ge", + "G AN", + "id ers", + "SH IFT", + "Ġdiff us", + "Ġme ant", + "ĠSch warz", + "S b", + "Ġarc s", + "No tice", + "i y", + "Ġem erge", + "kw args", + "E ff", + "E nt", + "ion ization", + "ch oline", + "ust ries", + "ac her", + "s pl", + "pop ulation", + "f ol", + "Ġquestionnai res", + "Ġall ergic", + "w ich", + "ĠV acc", + "Ġat tained", + "ĠAn imals", + "am ics", + "ĠReg arding", + "ĠSem i", + "Ġgl ac", + "ĠEff icacy", + "Ġsynerg istic", + "IS H", + "Ġmaintain s", + "Ġsong s", + "ĠNeg ative", + "am oto", + "ĠMod ified", + "Ġsepar able", + "Ġbin aries", + "Ġaccess ibility", + "I ter", + "d in", + "ĠB inary", + "equ ilibrium", + "Ġc ue", + "m agn", + "Ġed ema", + "ï ¿½", + "Ġposition ed", + "Ġcharg ing", + "Ġun ivariate", + "he p", + "Ġcl ade", + "Ġcy steine", + "rac le", + "Ġresc ue", + "h abit", + "ĠDISCU SSION", + "Ġdepic ts", + "p ole", + "Ġst enosis", + "Ġv eter", + "pr inger", + "ĠP ow", + "Ġcovari ant", + "Ġmod ifying", + "Al gorithm", + "aver aged", + "al o", + "res on", + "Ġcharacter ised", + "Ġn i", + "Ġseem ed", + "ĠR om", + "sh ort", + "N V", + "Ġfer tility", + "ĠM emb", + "Ġl ying", + "Ġinstit ution", + "im ages", + "ĠB orel", + "fs ys", + "c ataly", + "Ġsepar ating", + "b iotic", + "m el", + "pg fsys", + "ĠJack son", + "Ġb ag", + "og rap", + "prop yl", + "ĠProgram ming", + "oc ratic", + "Ġp ion", + "ĠG radient", + "Ġsp he", + "Ġin line", + "Ġdom inate", + "Ġsuff ered", + "ĠDise ases", + "igen ous", + "w ill", + "Ġam in", + "adher in", + "ĠT ro", + "adj usted", + "E W", + "Ġde but", + "ne a", + "ĠD un", + "Ġd ictionary", + "oper atively", + "K A", + "be it", + "Ġperson nel", + "ĠÅ ½", + "re view", + "int o", + "ĠTok yo", + "Ġt rop", + "Ġvent ric", + "ĠMETHOD S", + "Ġim plication", + "ak is", + "ĠC MB", + "Ġtransmit ter", + "o ichi", + "ĠNiger ia", + "ĠK on", + "Ġbe ar", + "ĠK an", + "ĠPl ot", + "ĠS PSS", + "ĠBi ology", + "Ġbary on", + "Ġmicro RNA", + "Ġreproduc ibility", + "Ġlact ate", + "Ġpolyp hen", + "ĠM t", + "ĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ ĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ", + "end it", + "Ġhydro thermal", + "Ġwe alth", + "Ġhad ron", + "Ġwhere by", + "ell um", + "ĠDiff usion", + "ĠOrig in", + "Ġnonlinear ity", + "Ġinform ative", + "Ġvis ited", + "Ġvirt ually", + "ĠT un", + "Ġres et", + "ĠElect rical", + "ĠG lu", + "ĠS AM", + "ĠI sing", + "ĠSt ra", + "ond er", + "Ġd ies", + "Ġrecipro cal", + "C heck", + "ĠGu idelines", + "hest er", + "Ġproblem atic", + "ĠAt omic", + "Ġconcentr ate", + "st eps", + "j son", + "Recomm ended", + "ĠScreen ing", + "Ġna ive", + "Ġpractition ers", + "Ġfast ing", + "Ġmechan istic", + "op tions", + "P tr", + "IT E", + "W ork", + "âĢ ĺ", + "raf ts", + "Ġun w", + "Ġannih ilation", + "ob jective", + "ĠD ynamical", + "ad ec", + "ĠL ith", + "Ġextract ing", + "Ġcor al", + "ĠSt able", + "Ġbackground s", + "omorphism s", + "ĠâĪ «", + "Ġgre w", + "In st", + "g els", + "Ġin hal", + "d am", + "he im", + "benz yl", + "Ġpel vic", + "Ġdi arr", + "Ġdi ode", + "Ġem pir", + "ĠAl f", + "ĠUn certain", + "ĠH Cl", + "Ġjoint ly", + "Ġde par", + "Ġmerg ing", + "Ġch i", + "ap t", + "Ġpl t", + "Ġid i", + "Ġper for", + "stit uting", + "p age", + "ar é", + "ind ices", + "put ation", + "diff erent", + "b urn", + "Ġsurround ed", + "ĠT L", + "unt ary", + "st rip", + "l an", + "Ġc ow", + "ĠS ab", + "ĠGa As", + "p f", + "Ġes ophageal", + "ĠAl t", + "Ġhospital ization", + "ĠApproxim ation", + "Organ ism", + "ĠF air", + "Ġtrac ing", + "Ġpref erentially", + "Ġlower ing", + "uli ar", + "ĠDer iv", + "Ġphyto plankton", + "omy c", + "T hat", + "ĠIsra el", + "Ġminim ized", + "Ġany thing", + "r ule", + "p ow", + "Ġfam ous", + "ĠAcc uracy", + "Ġphotoc atalytic", + "ĠNon etheless", + "Ġdivis or", + "v b", + "Ġcam eras", + "ĠW ales", + "ĠCont ributions", + "Ġdisplac ements", + "ĠT am", + "Ġvol umetric", + "ession al", + "Ġcompens ate", + "Ġa ce", + "tri angle", + "bu ff", + "Ġnames pace", + "Ġbound ing", + "ynchron ous", + "m d", + "Ġimag ery", + "it ated", + "Ġorigin ated", + "ĠBel g", + "ĠE CG", + "ex isting", + "ĠSt okes", + "sens itivity", + "tid ine", + "ĠW M", + "Ġmonot one", + "Ġproceed s", + "ĠClust ering", + "ĠIo T", + "ern ary", + "al amic", + "ĠCollabor ation", + "ĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ ĠĠĠĠĠĠĠĠĠĠĠĠ", + "OL D", + "Î ĺ", + "ĠNan opar", + "ĠMul tiv", + "Ġc ystic", + "pi re", + "Ġoper ates", + "Ġmedi ating", + "Ġbene ath", + "ob e", + "g ate", + "Ġo ocytes", + "Ġmarg ins", + "ymmet ries", + "Ġrelig ious", + "ĠN it", + "Ġcut aneous", + "AN S", + "Ġdevelop s", + "as ia", + "ĠRo berts", + "a vier", + "Ġsim plic", + "Ġreveal ing", + "UN D", + "Ġte a", + "Ġl ysis", + "Ġaggreg ated", + "ĠR GB", + "Ġcor ro", + "Ġb ir", + "in ae", + "v d", + "Ġcour t", + "Ġcontrovers ial", + "Ġto w", + "Ġhy steresis", + "en berg", + "Ġent ers", + "p ng", + "ĠF lex", + "Assum e", + "ĠB ad", + "ĠSimilar ities", + "Ex perim", + "AT H", + "Ġ ut", + "ter ms", + "ĠM ol", + "Ġvis ually", + "Ġadop tion", + "Ġprint ing", + "Ġequ iv", + "ĠP ert", + "Ġper col", + "Ġsome one", + "abul ary", + "Ġle ver", + "ĠH aus", + "ic illin", + "it ar", + "Ġto urn", + "Al tern", + "Ex p", + "~~ ~~", + "ĠF o", + "Ġab ol", + "med ian", + "Ġroll ing", + "h m", + "Ġtel escope", + "ĠC av", + "Ġseed lings", + "in hib", + "Ġd in", + "Ġimp urities", + "Ġampl ifier", + "ĠK er", + "Ġdimin ished", + "P B", + "f ib", + "ro ck", + "ĠB in", + "Ġphotos ynthetic", + "ĠCr ypt", + "Ġpre term", + "Ġh its", + "Ġfract al", + "Ġdisc arded", + "Ġend ocrine", + "os hi", + "Ġmod ulo", + "w t", + "Ġqu enching", + "Ġsound s", + "ĠED TA", + "re active", + "Ġres ist", + "ang hai", + "Ġn arr", + "Ġiniti ate", + "ĠS aint", + "X R", + "Ge V", + "ĠInd ependent", + "Ġinj ective", + "up us", + "Ġl inguistic", + "Ġanalog ues", + "Ġdiss ection", + "Ġlas ers", + "di ab", + "ĠTe le", + "Ġc racks", + "Ġb rane", + "V O", + "ĠExt ended", + "Ġt ells", + "Ġremark s", + "ul ting", + "ĠB urn", + "d L", + "ress ible", + "ĠCh ap", + "Ġs q", + "Ġrepro duced", + "ĠB cl", + "Ġsw arm", + "opath ology", + "ch rotron", + "Ġm ine", + "Ġhad ronic", + "ĠLocal ization", + "ĠM otor", + "Ġvisual ize", + "Ġc ats", + "Ġbal ancing", + "ĠSc hed", + "Co A", + "Ġtherm odynamics", + "ĠDiagn ostic", + "Ġreli ef", + "Ġpos itivity", + "Ġh ub", + "ĠInf rared", + "S ur", + "om ed", + "Ġop tically", + "Ġv ascul", + "is ations", + "enc oder", + "Ġcopol ymer", + "Ġrest ore", + "Ġiner tia", + "ubic in", + "Ġeti ology", + "ĠSec ret", + "ĠC W", + "Con st", + "ĠBr it", + "ĠConst ant", + "ĠD IS", + "Ġdisc ipl", + "b ra", + "ĠO ral", + "ĠU L", + "Ġdel ine", + "Ġnucle on", + "Ġemploy ment", + "ĠR D", + "q q", + "ĠCarol ina", + "ĠG ab", + "Ġasser tion", + "CM C", + "r gb", + "F rame", + "ĠJ ust", + "Ġinoc ulation", + "cl uding", + "Ġoscill atory", + "Ġcanc el", + "ĠPo inc", + "por a", + "ĠJ ul", + "ru vate", + "Ġpoli tic", + "ur us", + "ĠAdv ances", + "ĠR oot", + "th ood", + "oxygen ase", + "ms g", + "Ġk V", + "Ġad mit", + "Ġrefrac tory", + "Ġclon ing", + "Ġf atal", + "plant ation", + "ĠG ir", + "Ġt es", + "ĠR ho", + "oh n", + "Ġinnov ation", + "Ġs ending", + "Ġc able", + "Ġnic he", + "Ġres erve", + "Ġat rophy", + "ath an", + "Ġà ij", + "iti zation", + "Ġf an", + "Ġb ubbles", + "ĠTheorem s", + "ĠSw itzerland", + "ĠHe isenberg", + "ĠRed uced", + "R a", + "Z r", + "ĠPoss ible", + "U psilon", + "ĠAg ric", + "el lect", + "nd s", + "math ds", + "at re", + "Ġfor aging", + "Ġup ward", + "id ene", + "Ġgl ands", + "f ed", + "uccess ful", + "ĠW olf", + "Ġuseful ness", + "op orous", + "Ġp unct", + "ard o", + "Ġsy stolic", + "ĠTarget ing", + "Ġill umin", + "Ġpig ment", + "Ġsim ulating", + "Ġpor tions", + "ĠPrinc iples", + "ĠHop f", + "l ipid", + "ĠL U", + "ub ation", + "ĠAr tificial", + "Ġpr ison", + "an ing", + "ĠG N", + "ĠStrateg ies", + "ĠP as", + "T a", + "ĠProb ability", + "or um", + "Ġs keleton", + "Ġcomp artments", + "R ead", + "Ġco ach", + "Ġmod ality", + "ĠReg ister", + "Ġj e", + "Ġhe ights", + "in yl", + "Ġsub spaces", + "ti p", + "Ġá ¸", + "ĠG I", + "Ch ar", + "ro genic", + "ret t", + "eu tics", + "Ġadhes ive", + "ĠP ier", + "Le ft", + "id ental", + "NA c", + "Ġconjug ation", + "or ov", + "id ge", + "im aging", + "ĠT W", + "Ġpres ident", + "ĠO ste", + "ass emb", + "Ġinter net", + "Ġde als", + "ĠG AP", + "Ġform ulate", + "ĠUp date", + "ĠRNA i", + "cl ero", + "Ġpermut ations", + "Ġisotop es", + "op ic", + "ĠQ U", + "rom es", + "ĠPol icy", + "ĠC reek", + "ĠWind ows", + "Ġm erge", + "Ġacc ident", + "Ġsuper position", + "Ġdeb ate", + "Ġdocument ation", + "Ġeigen vectors", + "s or", + "ĠPh oto", + "Ġdepos it", + "Ġgerm ination", + "Ġsub graph", + "ĠRec ords", + "Ġchem ically", + "ĠPredic ting", + "ĠK y", + "se lective", + "yn man", + "dis pers", + "Ġlum bar", + "Ġmus ical", + "in ates", + "Ġinher ited", + "j u", + "Ġtrac er", + "Ġend ing", + "Ġeng aged", + "hand ed", + "Ġproduc er", + "Ġent angled", + "ĠD elta", + "Ġpiec ewise", + "NA ME", + "st op", + "Ġmut ated", + "Ġre cess", + "Ġimmun o", + "c ancer", + "ĠAk t", + "it ers", + "ĠB MP", + "Ġcompan ion", + "Ġcommun icate", + "Ġh ollow", + "Ġp ad", + "Ġs ph", + "om od", + "Ġpar ton", + "Ġspontaneous ly", + "e ared", + "Ġrot ations", + "Ġcosm ology", + "Ġmore over", + "pr inc", + "Ġevery where", + "b rane", + "l ational", + "em e", + "Ġbeh ave", + "um en", + "ost on", + "ov es", + "Ġg ar", + "Ġad renal", + "ĠEstim ating", + "N b", + "Ġech ocardi", + "Ġemphas ized", + "Ġeng ines", + "Ġbrack ets", + "Ġlead ers", + "Ġdistinc tive", + "ĠL ymph", + "Ġex ert", + "Ġinnov ative", + "c oupling", + "ĠSign ific", + "she et", + "ĠC over", + "ĠC CD", + "ĠF all", + "stim ulated", + "Ġsuper oxide", + "Ġpollut ants", + "Ġby tes", + "ĠL ipid", + "Ġtra fficking", + "Ġlead ership", + "inform atics", + "Ġbiod iversity", + "ad or", + "Ġinter conn", + "Ġharmon ics", + "Ġseaw ater", + "ĠIll umina", + "necess ary", + "ĠAnt on", + "Ġprocess ors", + "typ ename", + "D et", + "pro ton", + "Ġsubt raction", + "Ġshif ting", + "Ġcust omers", + "K e", + "ĠO B", + "aton in", + "at ellite", + "ĠS US", + "ĠCol on", + "ĠTim es", + "T V", + "ĠM ink", + "ĠIntegr ation", + "Ġprof ound", + "IT C", + "Ġg ras", + "ĠNA SA", + "ĠAC K", + "radi ol", + "ĠM ale", + "ĠWork ing", + "tic ity", + "ilibri a", + "bound ary", + "ĠR I", + "ĠAl i", + "car di", + "ĠF GF", + "b ranes", + "Ġbe et", + "Ġmiss ed", + "S ource", + "ĠB ot", + "ie ve", + "Ġis other", + "ne ys", + "n l", + "or tion", + "Ġcool ed", + "M V", + "Ġo mit", + "Ġver bal", + "aret te", + "Ġconf erence", + "Ġtransform er", + "Ġre jected", + "Ġprogress ively", + "ĠTur key", + "Ġath letes", + "Ġan atomy", + "E Q", + "Ġdeterior ation", + "ĠDi etary", + "Ġcor n", + "Ġcaps ule", + "Ġvibr ations", + "Ġoccup ational", + "Ġex osomes", + "Ġre written", + "Ġlign in", + "Ġbi opsies", + "ĠAdvers arial", + "Ġm ercury", + "Ġpl atinum", + "Ġirre levant", + "Ġker atin", + "ĠE mission", + "Ġeukary otic", + "Ġinte g", + "Ġkn ot", + "Ġser a", + "Ġcav ities", + "ĠMed i", + "Ind eed", + "E u", + "Ġâ Ł", + "Ġsc enes", + "Ġlap aroscopic", + "Ġsen ior", + "ĠD istance", + "pred ic", + "Ġear liest", + "Ġor g", + "ĠTh or", + "b ury", + "obl asts", + "Ġp umping", + "target ed", + "Ġra p", + "ĠP il", + "Î ł", + "Ġneu rom", + "o ft", + "ost at", + "Ġp adding", + "Ġconflic ts", + "Ġst ems", + "ĠSac charomyces", + "eng ine", + "Ġalk yl", + "Ġt ill", + "ĠQu ad", + "g ood", + "ro x", + "ĠF uzzy", + "Ġrob otic", + "ĠDen ote", + "ĠN IR", + "ĠY uk", + "paren cy", + "Ġle gs", + "yl van", + "Ġtight ly", + "Ġdec or", + "ĠV P", + "ĠM un", + "at oms", + "ĠSil ver", + "Ġneurode generative", + "Ġrespond ed", + "Ġrec ons", + "G EN", + "ĠF ine", + "f c", + "Ġpar agraph", + "Ġint ens", + "Ġalong side", + "Ġb rand", + "mon ium", + "Ġp m", + "Ġsimple x", + "ĠPrelim inary", + "Ġdown regulation", + "Ġx y", + "ĠM ak", + "op ter", + "ush ing", + "ĠB og", + "ox ia", + "================ ================", + "com mon", + "ĠA SS", + "ĠHD L", + "alam us", + "Ġirrig ation", + "N M", + "Ġf ading", + "Ġprev entive", + "Ġreli ably", + "ĠEthiop ia", + "ot hesis", + "iz ability", + "O B", + "Ġtrig lycer", + "Ġgest ational", + "Ġb esides", + "ĠI ii", + "ĠZ one", + "Ġcop ing", + "Ġminor ity", + "Ġdepri vation", + "Ġhex agonal", + "chlor ophenyl", + "ĠóµĦ ¨", + "Ġg yr", + "Ġview ing", + "New ton", + "ĠHierarch ical", + "o L", + "ec es", + "Ġconcl udes", + "Ġfung us", + "Ġpyl ori", + "Ġobstac les", + "th iazol", + "conjug ated", + "r ass", + "Ġl ose", + "Ġfor th", + "ĠAll en", + "opl ast", + "ĠProt ection", + "Ġintermitt ent", + "Ġluc iferase", + "ĠM K", + "Ġga ug", + "ĠF an", + "Ġmod al", + "ĠEx ercise", + "sc attering", + "ĠSh im", + "Ġexc retion", + "Ġat ypical", + "Ġmalign ancies", + "angl ades", + "ĠSpect roscopy", + "Ġaden osine", + "l if", + "Ġnucle ic", + "Ġincl ination", + "ĠC ass", + "Ġeth n", + "Ġex empl", + "ĠD y", + "Ġl ambda", + "Ġj ac", + "ĠP RE", + "Ġrail way", + "Ġf le", + "Ġreflec tions", + "Ġnano structures", + "ti sts", + "pr ints", + "ĠC AT", + "Ġs ib", + "Ġchlor o", + "Ġrecip ient", + "op tic", + "Ġcoun ty", + "Ġnucle otides", + "Ġz ircon", + "Ġhors es", + "ĠM ental", + "in line", + "ĠNor way", + "The y", + "Ġmusc ular", + "ace tic", + "ĠJ u", + "Ġcommun ic", + "f iles", + "f illed", + "H B", + "Ġreg ulations", + "Ġaccum ulate", + "ĠPan el", + "C y", + "ö l", + "ĠPak istan", + "Ġthor acic", + "ĠM PI", + "por tion", + "Ġinduc tive", + "ĠCong ress", + "Ġfibrobl ast", + "cl ust", + "Ġcent res", + "ad el", + "Ġsubstit utions", + "Ġtrunc ation", + "r ification", + "ok a", + "F low", + "ĠRed uc", + "polar ized", + "ib ular", + "P e", + "ĠA ML", + "ĠAg ency", + "Ġt ilt", + "ubl ished", + "Ġdep olar", + "Ġbel t", + "Ġoptim izer", + "EL L", + "ĠHand book", + "ĠVirgin ia", + "s ense", + "ĠD ur", + "Ġpiezo electric", + "Ġaward ed", + "ail ing", + "P os", + "p ref", + "ĠSum mer", + "ed o", + "ĠI de", + "ĠB SA", + "Ġmon omers", + "Ġco agulation", + "Ġg am", + "Ġhom es", + "Ġhead s", + "adm ium", + "ĠO C", + "Ġoccup ancy", + "ĠEm pirical", + "ĠI i", + "Ġch ir", + "Ġdegener acy", + "Ġflow ers", + "Ġsuperconduc tivity", + "Ġin versely", + "op tical", + "w ere", + "ĠAs ymptotic", + "S ec", + "tit le", + "pos al", + "ĠPro gn", + "Ġpos es", + "ĠB orn", + "Ġcontinu ation", + "Ġcul tivated", + "enti ment", + "Ġman aging", + "Ġthromb osis", + "a ug", + "CN T", + "ure a", + "Ġsp ind", + "ĠWhere as", + "ĠPers on", + "Ġb ipartite", + "Ġres cal", + "Ġmark ets", + "ph an", + "per ties", + "Ġferm ionic", + "Ġmunic ip", + "Ġachie vable", + "t ab", + "Å į", + "ĠRel ation", + "T otal", + "x ia", + "Ġintellig ent", + "ĠU T", + "ĠD al", + "Ġmedic inal", + "Ġinadequ ate", + "i ently", + "ers en", + "Ġpre condition", + "Ġmethod ological", + "Ġcan opy", + "Ġbacter ium", + "col umn", + "C al", + "ĠDi ego", + "ĠS ak", + "ĠComprehens ive", + "Ġanti tumor", + "Ġflow er", + "ĠK han", + "Ġmet adata", + "Ġphot ore", + "ogen icity", + "Ġle ague", + "ol ating", + "Ġprom ise", + "ĠP ere", + "Ġper mits", + "Ġthread s", + "ĠD Cs", + "ĠCh am", + "raz ol", + "B ank", + "Ġwithdraw al", + "Ġapp end", + "ot helial", + "ĠMeas ures", + "Ġguid eline", + "Ġmitig ate", + "adj oint", + "Ġbrack et", + "P ad", + "M ills", + "Bu ffer", + "Ġc ass", + "h oc", + "manif olds", + "her ry", + "Ġfacilit ated", + "E vent", + "Ġ È", + "ĠC ruz", + "ĠB rand", + "Ġnecess ity", + "burg h", + "Ġme V", + "Ġc AMP", + "O ff", + "se lected", + "Ġeng age", + "Ġredund ancy", + "Ġnanocom posites", + "s olution", + "ons et", + "ĠEx posure", + "Ġrepe titive", + "à ł", + "ĠR AD", + "ĠTur k", + "Ġcorne al", + "Ġexplo iting", + "Ġob structive", + "gram ming", + "ĠM ED", + "Ġmat hem", + "Ġconduc tive", + "Ġphotos ynthesis", + "E instein", + "ĠP eng", + "M W", + "ĠSch midt", + "Ġrepe tition", + "identif ied", + "Ġinj ured", + "Ġdef ective", + "ĠP el", + "Ġcul tivation", + "Ġfirst ly", + "Ġanalyz er", + "Ġstain less", + "Ġjo ining", + "ĠOxid ative", + "Ġph age", + "Ġexp endit", + "Ġhom ogeneity", + "ip le", + "ov ic", + "Ġcross ed", + "ĠTr ust", + "ĠF ract", + "rophys iological", + "Ġbas ically", + "Ġco ales", + "Ġgra vit", + "ful ness", + "c ano", + "Ġcol itis", + "Ġcha os", + "carb ons", + "O nce", + "ĠTow ard", + "or f", + "top ic", + "ĠPl ay", + "ĠCor respond", + "ĠS leep", + "ticular ly", + "c umin", + "v dots", + "ĠR he", + "Ġult raf", + "Ġtimes cale", + "ĠDet ails", + "ang les", + "Ġsur rogate", + "ĠFlu id", + "c z", + "Ġinitial ization", + "ĠTel escope", + "r ases", + "ĠSt ock", + "ĠC ond", + "Ġimmun odeficiency", + "B el", + "os er", + "sh own", + "Ġk cal", + "Equ ation", + "prot ective", + "Ġcall ing", + "Ġanticip ated", + "Ġambig uity", + "ĠN ode", + "ĠG D", + "Ġin let", + "Ġbre ad", + "Ġexceed ed", + "Ġimmun ization", + "Ġpro hib", + "y tic", + "Ġbo ys", + "t u", + "Ġto wer", + "L ike", + "ĠAn omal", + "â Į", + "ĠSh ow", + "Ġim aged", + "Ġequ il", + "Ġrend ering", + "ob ility", + "Ġge ological", + "f riend", + "ö r", + "carbox amide", + "ovol ta", + "C urrent", + "ĠS ti", + "ĠM U", + "Ġval ued", + "Ġpo ison", + "Ġprac tically", + "Ġrequ ested", + "C ode", + "Ġbr ings", + "Ġdim ethyl", + "h yp", + "ce mic", + "V ol", + "qu anti", + "Ġex ha", + "Ġrespons ibility", + "ĠCont rolled", + "Ġf ur", + "Ġres emb", + "ĠK aw", + "Ġev oked", + "Ġuter ine", + "Ð »", + "Ġan onymous", + "ĠChall enges", + "Ġanch or", + "ĠAb d", + "D er", + "Ġtherm ally", + "ĠC AP", + "obl ot", + "ĠF ire", + "Ġdiagnos tics", + "Ġexec ute", + "al is", + "ron i", + "ĠHar ris", + "ĠGon z", + "Ġv ig", + "ĠProf essor", + "Ġinvent ory", + "int ensity", + "ĠNSC LC", + "Ġinterf ere", + "ysacchar ides", + "Ġreg ener", + "ĠAut hors", + "Ġtransl ate", + "ĠT ests", + "ĠL ove", + "ĠInd uced", + "enn is", + "ĠG EN", + "Ġolig onucle", + "Ġmet er", + "s atisf", + "hes ion", + "Ġtrans porters", + "B IT", + "ĠCon c", + "Ġgl auc", + "sc ores", + "Ġmerg er", + "G H", + "Ġst oichi", + "ĠX ia", + "eff ects", + "ĠExpl oring", + "dor ff", + "Ġcardinal ity", + "ĠK az", + "f alse", + "ĠH SP", + "Ġuns upervised", + "ingu ish", + "isc her", + "Ġrel ativity", + "on ormal", + "oot hed", + "ed ges", + "ĠI MP", + "Ġimp ulse", + "ĠColumb ia", + "Ġpartic ulate", + "ĠSupport ing", + "ĠSD SS", + "vol tage", + "ĠAma zon", + "Ġep oxy", + "C all", + "Big l", + "Ġme ets", + "Ġequ atorial", + "Ġneu ros", + "Ġper itoneal", + "des c", + "input s", + "Ġex terior", + "ac o", + "Ġme al", + "ĠDani el", + "Ġintu itive", + "Ġcoun s", + "dep ress", + "in is", + "ph ot", + "ĠA min", + "Ġreservoir s", + "ĠW hole", + "Ġca ud", + "Ġbos onic", + "Ġread ers", + "Ġcr im", + "Ġpathophys iology", + "arg o", + "the se", + "inc ome", + "Ġiss ued", + "Ġhepat ocytes", + "ĠC i", + "der iv", + "up ta", + "t uple", + "ĠCh an", + "Ġauthentic ation", + "yg d", + "Ġinf in", + "Ġaccel erate", + "ep tive", + "Ġhydro gel", + "ask a", + "ON E", + "Ġfed eral", + "ograph ics", + "Ġmu on", + "Ġsl ide", + "Ġellip tical", + "at ite", + "Ġc c", + "ET s", + "Ġclar ity", + "ocy cl", + "is al", + "rec tions", + "ay an", + "row eak", + "ĠS OC", + "od erm", + "t un", + "as m", + "ĠH ir", + "lik elihood", + "Ġad ul", + "t l", + "H igh", + "Ġal ters", + "plit ude", + "ĠRe lease", + "Ġharm ful", + "l ate", + "ound s", + "ĠFed eral", + "ĠEcon omic", + "Ġra bb", + "Ġaccommod ate", + "em ission", + "ĠB ah", + "c ox", + "ĠMod ulation", + "Ġconstruc tions", + "ign er", + "ĠUrb an", + "Ġw ake", + "Ġadvers ary", + "wik ipedia", + "Ġsu ite", + "w ick", + "exp ressed", + "ro d", + "K D", + "Ġcomput ers", + "ĠB anglades", + "Ġpers ist", + "Ġburn ing", + "Ġadministr ative", + "Ġpl ug", + "ĠRepresent ations", + "ĠSc attering", + "Ġendomet rial", + "Ġdescript ors", + "Ġcom mission", + "B ar", + "igh th", + "ĠMar sh", + "sam pling", + "Ġh ull", + "ic in", + "Pro b", + "Ġnur se", + "Ġsh am", + "ĠK err", + "Ġpref rontal", + "Ġfix ing", + "O K", + "Ġb old", + "Ġcor ollary", + "cf g", + "ĠOx ford", + "Ġbor on", + "R B", + "ĠC ab", + "Big r", + "ĠPred ict", + "Ġpec uliar", + "h idden", + "is a", + "id en", + "appro priate", + "or h", + "ellect ual", + "Ġseiz ures", + "ass er", + "til is", + "hand le", + "iax ial", + "s ym", + "Ġcarcin omas", + "se a", + "sp ired", + "Ġab rupt", + "t ests", + "Ġw elfare", + "ĠO il", + "ĠLo ad", + "FL AG", + "ut hal", + "Ġfac ing", + "Americ an", + "L AS", + "Ġir respective", + "Ġrout inely", + "w al", + "Ġsettle ment", + "ĠA qu", + "Ġelectron ics", + "Ġhand led", + "Ġbiological ly", + "sm ooth", + "ĠBel ongs", + "ti b", + "Ġtra v", + "p ressive", + "ourn als", + "Ð º", + "fil ename", + "Ġhel ical", + "Ġbacter i", + "Ġsat ellites", + "B H", + "ent ed", + "ĠFoot ball", + "Ġï£ ±", + "ĠH V", + "Ġtri p", + "ĠCK D", + "ran i", + "Ġclean ing", + "lim it", + "ĠT CP", + "Ġsc in", + "Ġsl udge", + "Ġsymbol ic", + "ĠSequ encing", + "ad al", + "ĠPhil ipp", + "IC S", + "Ġvag inal", + "Ġcommit ment", + "ĠA wards", + "tr ig", + "Ġgu itar", + "acet ate", + "Ġb et", + "Cl N", + "Ġagric ulture", + "Ġch ief", + "Ġem bol", + "bu ild", + "Ġtex ts", + "ĠCo oper", + "l ived", + "ĠDel ay", + "ĠM ode", + "y al", + "B N", + "Ġindex ed", + "ex pr", + "ER N", + "v ens", + "Ġpo inter", + "c v", + "ac on", + "t ance", + "ĠâĪ Ŀ", + "Ġlow ered", + "Ġmit otic", + "rh osis", + "ĠP age", + "ü r", + "im m", + "ĠThe rapeutic", + "Ġoste opor", + "Ġbil inear", + "ĠCath olic", + "ĠAltern ative", + "oxid ation", + "Ġiniti o", + "benz o", + "ĠA di", + "per son", + "per itoneal", + "ĉĉ Ġ", + "Ġatt raction", + "Ġdiarr hea", + "Ġre n", + "ĠI SO", + "im ir", + "Ġtermin ology", + "uk ey", + "Ġreson ator", + "Ġsubstit uting", + "Ġhar bor", + "pro vid", + "dec ay", + "ĠHD AC", + "ĠAnaly tical", + "Ġpost natal", + "Ġund es", + "Spec ific", + "d ichlor", + "AR I", + "t ot", + "Ġdig it", + "op ing", + "ĠZ inc", + "Ġle thal", + "Wh itney", + "F i", + "qu antum", + "ĠF ailure", + "Ġsol ves", + "ĠSp aces", + "ear man", + "Ġgo at", + "Ġsyn apses", + "Ġres uspended", + "Ġresid ent", + "Ġcomp ac", + "Ġcor tisol", + "Ġphot ometry", + "W P", + "se lect", + "Ġc ele", + "or ubicin", + "ĠMul tic", + "ĠJe an", + "Ġcl ip", + "Ġs a", + "oc o", + "ge ometric", + "Ġhel ic", + "Ġempir ically", + "Ġmicro fluid", + "id is", + "Ġaut ocor", + "W F", + "ĠRes pir", + "radi ation", + "Ġthem es", + "Ġt aste", + "ric ing", + "Ġexam inations", + "ĠSens ing", + "s ame", + "DE FAULT", + "Ġphyl ogeny", + "h ig", + "Ġplate lets", + "ĠHist or", + "ab a", + "Ġresid ential", + "Ġun bounded", + "and ing", + "hed ron", + "r ys", + "ĠC CR", + "Ġcon ce", + "Ġpar asitic", + "c b", + "ĠFe ynman", + "ĠKe pler", + "à ´", + "ĠG il", + "ĠMAT LAB", + "b en", + "sc ope", + "Ġdiscrim in", + "Ġjus tified", + "plas ma", + "ĠCho i", + "Ġro of", + "PC A", + "ĠT CR", + "Ġvox el", + "ĠW ard", + "Ġunc or", + "S tok", + "Ġsp ur", + "TR A", + "Ġdiagnos es", + "rophys ical", + "ategor ies", + "Ġove restim", + "Ġstream ing", + "ĠRec overy", + "Ġevery thing", + "LO W", + "G ener", + "Ġun biased", + "Ġvari ances", + "comp act", + "es pan", + "in j", + "Ġend oscopic", + "Ġide als", + "ĠR ice", + "ĠKa plan", + "Ġf ecal", + "fer red", + "ĠCy cle", + "Ġimplant ed", + "Ġw ine", + "P ET", + "Ġassign ments", + "Ġabs ol", + "X T", + "Ġswim ming", + "M N", + "ĠGe ometric", + "ĠHealth care", + "Ġpow ders", + "ĠG el", + "Ġdown ward", + "Ġexceed ing", + "ĠHE K", + "ly m", + "ĠB V", + "Ġvis co", + "i et", + "ĠCO X", + "ploy ment", + "ins ki", + "Ġout door", + "ĠLiter ature", + "ant ed", + "meth oxyphenyl", + "ĠMed ium", + "Ġd ia", + "ail and", + "vari ance", + "ĠEval uating", + "ox acin", + "Ġanti f", + "Ġpul p", + "Ġcorro bor", + "ĠO t", + "Ġrabb its", + "R u", + "Ġfunction als", + "â ĩ", + "Ġimm ersion", + "Ġcre atin", + "Ġq RT", + "Ġcondens ed", + "n r", + "ĠV A", + "h ad", + "Ġk ing", + "ob le", + "Ġexist ed", + "Ġthe sis", + "ubb ard", + "ap optotic", + "Ġflow ering", + "ĠAdap tation", + "ĠKal man", + "tr l", + "Ġm ent", + "ut ation", + "ĠCon v", + "Ġhist ories", + "Ġen anti", + "n ell", + "on ian", + "ĠF abric", + "Ġx x", + "Ġf ell", + "Ġcytos olic", + "Ġm ud", + "Ġsusp ensions", + "ĠMicro bial", + "meas ured", + "Ġdown load", + "Ġinv alid", + "Ġcapt uring", + "ĠH H", + "ĠG ray", + "ĠA Z", + "ĠN ash", + "vi ation", + "nai re", + "or tium", + "yn ch", + "amin ergic", + "Ġwa it", + "S chem", + "t race", + "ĠV ill", + "Ġpo ols", + "Ġhypox ic", + "x p", + "Ġsh aded", + "OR Y", + "t urn", + "inter acting", + "Ġdestroy ed", + "ak h", + "ĠCp G", + "dot ted", + "ĠTrans cript", + "plan ar", + "Ġpre clinical", + "ĠRe pro", + "ĠSur gery", + "Stok es", + "if def", + "Ġdiscrim inate", + "ĠG ross", + "Ġfl ags", + "i ety", + "umm y", + "Ġtransf ers", + "S G", + "ĠSc i", + "Ġhead er", + "ĠFund ing", + "Ġdet rim", + "Ġinst abilities", + "ĠPhyl ogenetic", + "ym ethyl", + "ĠAssess ing", + "RO C", + "els en", + "Equ al", + "Ġc as", + "Ġver tically", + "Ġvis ibility", + "ĠFT IR", + "sc rib", + "Ġbur sts", + "ĠDo ug", + "ĠFranc isco", + "ĠM SC", + "Ġpred is", + "establ ished", + "Ġfac ed", + "ĠW I", + "S l", + "Ġchar ts", + "orth y", + "izon tal", + "ial ysis", + "Ġtun able", + "Ġexplos ion", + "S w", + "T NF", + "Ġdiscontinu ous", + "ect ure", + "ci ences", + "mathbb m", + "lo ok", + "Ġt achy", + "Ġb row", + "obs erved", + "Ġana est", + "S al", + "q PCR", + "Ġse es", + "Ġspac ecraft", + "Ġsal es", + "ĠT rac", + "T em", + "iv est", + "ĠF c", + "ĠNew s", + "Ġharvest ing", + "ĠE G", + "p ad", + "Ġnanow ires", + "Ġpot ato", + "pl iers", + "on in", + "Ġw orm", + "s ue", + "ti e", + "Ġm asks", + "Ġth row", + "! !", + "be havi", + "Ġp ine", + "og y", + "TE ST", + "on to", + "Ġcreatin ine", + "ĠB oston", + "Ġch air", + "pl oys", + "ov en", + "Ġent rance", + "Ġc och", + "Ġdy es", + "T or", + "ĠPD E", + "unders et", + "atas ets", + "Ġt ernary", + "cho ose", + "f ive", + "chlor ide", + "on ium", + "Pro perty", + "Ġt u", + "Ġadequ ately", + "romy cin", + "Ġco oper", + "ï Ľľ", + "Ġpap ill", + "ĠStrept ococcus", + "ĠC Y", + "Ġgroup ing", + "Ġbi oc", + "ĠCardi ac", + "ĠBo ok", + "re ference", + "Ġconfirm ation", + "iver y", + "Ġwar ning", + "pret ation", + "Ġl ove", + "Ġoscill ators", + "s ed", + "ĠT X", + "il ent", + "ĠV as", + "Ġcl amp", + "Ġa head", + "ac s", + "Ġdeple ted", + "Ġmethod ologies", + "m ay", + "Ġc affe", + "Ġsequ entially", + "os acchar", + "Ġcompr ise", + "Ġc hel", + "Ġin acc", + "Ġtend on", + "S equ", + "ough t", + "ser ver", + "ĠPert urb", + "Ġter rain", + "cur ve", + "ĠAr gent", + "T ABLE", + "Ġimplicit ly", + "Ġenj oy", + "ĠS itter", + "Ġmic ron", + "ĠEv ans", + "ns ylvan", + "Ġlook ed", + "sp e", + "vol ving", + "ĠL STM", + "agne tism", + "ĠNot ch", + "ĠT al", + "ĠDEG s", + "lem an", + "Ġbo olean", + "Ġob ey", + "organ ization", + "se en", + "ĠEn c", + "sch ild", + "ĠOnt ario", + "Ele ment", + "ĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ ĠĠĠĠĠĠĠĠĠ", + "m ouse", + "Ġpoly ethylene", + "Ġace tic", + "s ections", + "ur onal", + "ĠD ick", + "Ġk ill", + "Ġbroad ening", + "Ġfluor ide", + "Ġs aved", + "Ġde em", + "St ream", + "ac ed", + "ĠJ eff", + "Q A", + "Ġscal able", + "ĠF if", + "ĠMin i", + "Ġsuper gravity", + "Ġcoll oidal", + "L Y", + "O A", + "Ġper ic", + "Ġshort ly", + "Ġv ap", + "Ġspl its", + "m ove", + "Ġstim ulating", + "ĠBe ijing", + "Ġp yr", + "Ï Ń", + "Ġlex ical", + "âĢ ł", + "Å Ħ", + "itor ies", + "oler ance", + "Ġins ulator", + "ĠLe on", + "Ġpropag ate", + "ĠEle ments", + "y en", + "Mod ule", + "ĠWhe ther", + "Ġa ph", + "ĠLa ure", + "ĠMut ations", + "Ġhypert rophy", + "Ġocean ic", + "ograph ically", + "pati ents", + "ĠAngel es", + "Ġp he", + "Ġsqu ee", + "Ġcaro ten", + "f ine", + "Ġsk etch", + "Ġans atz", + "tit ution", + "ĠF us", + "ĠS ug", + "obacter ial", + "Ħ ĥ", + "Rel ated", + "Ġar tist", + "Ġac ryl", + "l ined", + "raf ted", + "ĠQ oS", + "ĠF eng", + "se arch", + "Ġnanot ube", + "ĠV M", + "ah l", + "Ġstr ide", + "ĠT ag", + "ĠL ar", + "Ġdes orption", + "d type", + "Ġb ug", + "Ġcareg ivers", + "ĠH un", + "ĠPrac tical", + "Ġob lig", + "re r", + "ĠK ang", + "ĠPro ducts", + "omet h", + "ĠHe La", + "Ġlabor atories", + "n atural", + "Ġf ul", + "Ġm old", + "ab ine", + "ĠS pring", + "Ġco bal", + "Ġhighlight ing", + "ĠPre f", + "cycl ic", + "ĠCONCLUS ION", + "ĠS ources", + "Ġap ex", + "par ser", + "ĠLog ic", + "Ġp ond", + "Ġto ld", + "ĠSh ap", + "perg illus", + "Ġsay ing", + "Ġmutagen esis", + "Ġmm Hg", + "ĠP AN", + "Ġsm okers", + "od ay", + "Ġhere in", + "CM V", + "ĠP W", + "Ġred shifts", + "ĠMin im", + "ym an", + "ull i", + "d ense", + "Ġarsen ic", + "ĠE MT", + "og aster", + "carboxyl ate", + "s ys", + "R o", + "an ch", + "ĠAl pha", + "ĠTechn ical", + "s v", + "Ġb ones", + "Ġaccept or", + "Ġnew born", + "pri vate", + "Ġnan or", + "ĠSw iss", + "a round", + "Ġsynt ax", + "ĠK ähler", + "Ġaer ial", + "ĠP ale", + "typ edef", + "names pace", + "Ġconfound ing", + "vi Äĩ", + "Ġret ard", + "Ġz eta", + "ĠT um", + "is ch", + "Ġsulf ide", + "ĠT ian", + "u y", + "Ġintu ition", + "Ġphosph olip", + "ĠS her", + "ric ts", + "-------------------------------- --------------------------------", + "ok ines", + "gluc ose", + "tol er", + "ifer ative", + "ĠFlu or", + "Ġencour age", + "Ġrespons ive", + "perturb ative", + "Ġs addle", + "l ers", + "nd ez", + "ĠZ ero", + "ĠDi et", + "Ġdeveloper s", + "S yn", + "Ġconf er", + "Ġorigin ate", + "rop ol", + "ha w", + "le tion", + "ms kip", + "Ġb er", + "Ġpe at", + "v ially", + "Ġgran ules", + "ĠÌ ĥ", + "Ġpl uripot", + "Ġassim ilation", + "Ġdenom inator", + "abil ization", + "ĠEpidem iology", + "MI N", + "eed s", + "ĠV R", + "E val", + "st ore", + "ĠBas eline", + "Ġc u", + "ĠSpect ra", + "Ġfraction ation", + "Ġplac ing", + "Ġbur ied", + "el eration", + "Ġalkal i", + "ĠI U", + "C alc", + "we ak", + "Ġmorphism s", + "Ġlig ase", + "Ġf s", + "Ġutil izes", + "Com put", + "à ¢", + "Ġs tig", + "rel ative", + "Ġimm ature", + "ĠF rac", + "ap i", + "Ġout patient", + "Ġachieve ment", + "Ġstack ing", + "Ġnod ules", + "IN D", + "ĠGP a", + "Ġpercol ation", + "m space", + "Ġbrain s", + "uff le", + "ent ropy", + "L ab", + "Ġstabil ize", + "ĠRic ci", + "ĠAn timicrobial", + "pers onal", + "Ġfarm s", + "ĠP in", + "Ġpor cine", + "Ġoccasion ally", + "w he", + "Ġundergo es", + "Ġregim ens", + "Ġbl ade", + "Ġlinear ized", + "Ġdec on", + "Ġpack ed", + "Ġf ishes", + "ĠM end", + "Ġapproach ing", + "Ġball s", + "Ġpro inflammatory", + "imer ic", + "ĠDirect or", + "Ġsol iton", + "Ġm osaic", + "vi et", + "Me an", + "ĠP ad", + "Ġtri plicate", + "sup ported", + "Ġcar t", + "<< <<", + "Ġrem ission", + "ase ous", + "astic ity", + "ĠM ik", + "ĠStrateg y", + "ram er", + "ĠPol ish", + "Ġent hal", + "Ġheter ozygous", + "ĠGra vity", + "A x", + "Ġorganization al", + "Ġmo vie", + "Ġexpl oratory", + "WL ED", + "Ġmo iety", + "dec re", + "ĠS till", + "Ġ ¡", + "Ġgreen house", + "Ġsupercon ductors", + "en um", + "el in", + "Ġoffer ing", + "st ad", + "ĠT rich", + "Ġre pl", + "Ġrecycl ing", + "ph or", + "Ġin elastic", + "ock ey", + "ĠâĢ Ļ", + "Ġsequ el", + "E B", + "ĠCh ile", + "Ġfibr illation", + "Ġdis ulfide", + "ob tained", + "ub in", + "Ĥ ¬", + "Ġfacilit ating", + "Ġhop ping", + "Ġmedi ator", + "Ġhyd ration", + "Ġspars ity", + "Ġs ati", + "Ġis othermal", + "Ġreturn ing", + "Ġtravel ing", + "Ġin g", + "Ġst ent", + "Ġcapac itor", + "Ġcomprom ise", + "ĠS ud", + "ĠV ision", + "Ġtop ologies", + "opol ysaccharide", + "ĠPro file", + "ĠR ing", + "Ġdiscrep ancies", + "D is", + "AR D", + "cc cc", + "Ġdirect ory", + "ĠCM OS", + "ow ed", + "ill o", + "ĠIns ights", + "ĠT ib", + "Ġab and", + "aro se", + "Or der", + "Ġ ¬", + "Ġintrac ranial", + "Ġintermedi ates", + "Ġhab its", + "Ġcar p", + "pro perty", + "IM AGE", + "ĠU k", + "Ġhydroph ilic", + "W id", + "Ġab iotic", + "Ġobser vers", + "Ġch or", + "ĠCons ervation", + "ĠEnh ance", + "ĠAutom ated", + "ĠGl ut", + "ir atory", + "Ġsp aw", + "ĠE fficiency", + "v ast", + "in iti", + "Ġop tional", + "ĠScal ing", + "if old", + "Ġmt DNA", + "ĠRec onstruction", + "Ġcount able", + "ĠGr ass", + "D en", + "ĠCh ain", + "en zyme", + "Ġwave forms", + "Ġpancre as", + "ĠDet ailed", + "cm d", + "Ġâİ ľ", + "Ġmagnet o", + "ĠFP GA", + "Ġabsol utely", + "Ġstim ulates", + "ach us", + "ĠAr n", + "m essage", + "ocomp atibility", + "H Cl", + "ĠF ish", + "Ġphenomen ological", + "Ġsaliv ary", + "ond o", + "Ġno tions", + "f ur", + "U CT", + "Ġw ww", + "ab et", + "ĠS ulf", + "F il", + "dom inated", + "ars er", + "Ġpack ages", + "Ġsplic e", + "F lo", + "NO WLED", + "x a", + "ĠY uan", + "Ġacet one", + "ĠVit amin", + "ĠÎ ŀ", + "Ġobs c", + "Ġcha per", + "Ġm ort", + "M AN", + "Ġsub tilis", + "Ġoptim ality", + "Ġcontinu ing", + "Ġdu plication", + "Ġmultip lying", + "Ġimmun ological", + "Ġcir rhosis", + "h ospital", + "ĠProb abilistic", + "Ġdele tions", + "Ġca ution", + "Ġow ner", + "ox orubicin", + "Ġla unch", + "Ġc ure", + "th us", + "ĠHerm itian", + "can onical", + "Ġimmun ore", + "form in", + "Ġbroad band", + "part um", + "op he", + "ĠB eta", + "ĠB I", + "Ġïĺ º", + "Ġj umps", + "Ġparad ox", + "um ped", + "Ġdoc tors", + "Ġhospital ized", + "Ġwas h", + "prec ision", + "Ġr uled", + "Ġdu plicate", + "ant e", + "Ġneuro trans", + "Ġïĥ §", + "Ġthem e", + "T aking", + "ĠPl ants", + "f ollowing", + "Ġage ing", + "Ġcon gestion", + "os arcoma", + "Ġrepos itory", + "ĠH ess", + "ĠC atalytic", + "ĠD V", + "IN K", + "pri v", + "ĠAn a", + "ĠS LE", + "ĠTh ailand", + "í ķ", + "Ġd uty", + "loc ations", + "ot er", + "Ġlys ine", + "Ġind ist", + "Ġagon ists", + "A ck", + "Ġminim ally", + "Ġet ching", + "ugg ing", + "c uda", + "nd ef", + "Ġref erring", + "Ġlys ates", + "Ġseroton in", + "crib ing", + "ĠInter face", + "d V", + "Ġd urations", + "Ġphot od", + "Ġd ating", + "Ġirre versible", + "os idase", + "ĠF ROM", + "with in", + "SN R", + "Ġarr hyth", + "ĠR atio", + "ĠTh in", + "cent ered", + "Ġsh ocks", + "ĠV ers", + "Ġnotice able", + "Ġf oci", + "Ġorth onormal", + "Ġâİ Ł", + "Ġlum inescence", + "ĠSUS Y", + "in ternal", + "ĠT our", + "Ġab brevi", + "ĠM AL", + "ver tex", + "Ġem ploys", + "IN S", + "Ġimmunohist ochemistry", + "Ġhepar in", + "Ġidi opathic", + "Ġimmobil ized", + "is he", + "ph th", + "th in", + "ĠSt orage", + "Ġperovsk ite", + "Pro t", + "ĠDepend ing", + "Ġbl ends", + "Ġpred ator", + "Ġdisplay ing", + "Ġvesic le", + "ĠK ra", + "Ġl ane", + "Ġmulti layer", + "Ġhom ozygous", + "cos h", + "Ġsuperf icial", + "Ġ il", + "ĠK R", + "ĠBr un", + "ĠE W", + "op a", + "ĠCart esian", + "ĠCy toplas", + "ĠPen nsylvan", + "b ands", + "Ġangi otensin", + "ĠLat tice", + "G I", + "j ee", + "Ġenlarg ed", + "en ius", + "ĠI a", + "ou x", + "Ġg ent", + "Ġcarbon yl", + "c hers", + "Ġhypot he", + "Ġmic rosp", + "Ġaff ective", + "Ġax ons", + "e i", + "ypt oph", + "ĠJ on", + "que ue", + "ĠG auge", + "men opausal", + "ĠD as", + "ĠEss ential", + "ĠF ault", + "ĠB il", + "Ġtest osterone", + "Ġcham bers", + "d ione", + "Ġelic ited", + "IG N", + "Ġantioxid ants", + "pop ulations", + "Ġov ary", + "Ġâ ĸ", + "Ġabst raction", + "Ġhydro carbons", + "Ġrec tal", + "Ġtrigger ing", + "Ġthorough ly", + "R un", + "acter ia", + "in formation", + "ĠB ed", + "Ġqu enc", + "Ġund ers", + "ĠScot land", + "Ġre volution", + "Ġpit uitary", + "Ġanthrop ogenic", + "f ocus", + "Ġmet han", + "Ġinf low", + "Ġdef lection", + "ĠC ape", + "Ġmulti dimensional", + "Ġarri ved", + "ĠS par", + "d v", + "Ġc ows", + "ĠB h", + "Ġj k", + "tol yl", + "Ġeigen states", + "Ġpre processing", + "ĠR ain", + "ä ¸", + "in z", + "Ġm n", + "RE E", + "atric k", + "D ev", + "Ġfulf illed", + "Ġar tic", + "Ġreal izations", + "ĠComp onent", + "ĠW S", + "Ġinf o", + "pr inted", + "at osis", + "c ache", + "an ov", + "ĠT g", + "cont ent", + "j unc", + "ĠCD K", + "Ġbeh aves", + "ĠK id", + "diff erence", + "ĠP s", + "ĠU g", + "Ġstruct urally", + "ereb ral", + "ĠSur ve", + "he al", + "on ite", + "Ġdele ted", + "iti m", + "St ar", + "ĠSpe ech", + "ĠA str", + "g radient", + "Ġf ellow", + "Ġsy ring", + "N B", + "ĠN B", + "Ġcre ep", + "Ġlog ging", + "Ġint en", + "scal ar", + "ĠAtmosp heric", + "Ġl upus", + "Ġiden tically", + "process ed", + "sign al", + "ĠClo str", + "anc ers", + "Ġd b", + "Ġsubs ystem", + "s itu", + "Ġferro electric", + "Ġï Ľľ", + "Ġo re", + "ĠR b", + "ĠMicro soft", + "ĠC och", + "ĠAc tin", + "Ġnerv es", + "Ġexper tise", + "o tive", + "ĠPoinc aré", + "ĠR ig", + "Ġpsych osocial", + "Ġprogen itors", + "ĠM yr", + "ĠH ug", + "Ġbi ogenesis", + "Ġincorpor ates", + "Ġnever theless", + "ĠDec l", + "obs erv", + "Ġmulti plier", + "Ġrespond ing", + "ho ff", + "Ġimp acted", + "Ġsynd romes", + "k el", + "ĠS ynt", + "ĠCon cer", + "ĠAmeric ans", + "Ġspac ed", + "um ption", + "ĠThom pson", + "ĠJacob ian", + "T ra", + "e volution", + "Ġdid n", + "Ġpercenti le", + "Ġl id", + "equ ivalent", + "Ġantic o", + "Ġmulti ply", + "Ġpen icillin", + "Ġrespons iveness", + "Ġrun off", + "al anine", + "squ ares", + "ĠIns ulin", + "re le", + "ĠL if", + "ĠMink owski", + "Ġbl end", + "ĠP and", + "Ġtw elve", + "Ġhybrid s", + "Ġb ass", + "inter action", + "ĠBanglades h", + "Ġop ens", + "ĠAr ts", + "Ġconc ave", + "Ġped est", + "Ġf ist", + "ĠAd ults", + "open ia", + "EN CE", + "ĠF usion", + "Ġmicro c", + "ĠSur gical", + "yl ate", + "Ġpack aging", + "OC K", + "Q C", + "T ri", + "sc an", + "Ġregard s", + "Ġdiscrim inant", + "Ġind ustries", + "ic us", + "ĠWalk er", + "Ġpe ers", + "sy nt", + "Ġhor se", + "Ġflow ing", + "ur red", + "ĠCR P", + "ĠCare er", + "iffiffiffiff iffiffiffiff", + "ĠM SE", + "han a", + "ĠMor tality", + "Ġtumorigen esis", + "ĠIs lam", + "Ġazim uthal", + "w en", + "Ġs ys", + "az in", + "ne ighbor", + "Con fig", + "the y", + "Ġs orption", + "Ġsp anned", + "Ġview point", + "M OD", + "Ġth rust", + "up lex", + "Ġhist ograms", + "Ġprogram med", + "Ġeth ics", + "ect able", + "represent ation", + "um ns", + "Ġstre et", + "ĠSob olev", + "Ġexc ision", + "ĠR ud", + "qui res", + "Ġown ed", + "Ġthous and", + "Ġantagon ists", + "U ST", + "Ġdrastic ally", + "ĠóµĦ ©", + "ĠD or", + "ĠM OS", + "p n", + "ĠDec re", + "D ep", + "Ġs intering", + "Ġpur ple", + "et hanol", + "Ġhydro carbon", + "ĠF O", + "left rightarrow", + "Ġimmun ofluorescence", + "ĠO M", + "Ġmat urity", + "Ġearthqu akes", + "Ġax on", + "Ġprob ed", + "OR D", + "ĠAD P", + "s g", + "om ere", + "Ġtrans cribed", + "M ar", + "ĠU til", + "ĠI A", + "Ġcomp iled", + "Ġsuper vision", + "ĠX en", + "ĠJ ur", + "com par", + "Ġhypert ensive", + "il ized", + "ra e", + "Con clusion", + "'' '", + "Do uble", + "ĠF as", + "Ġins ectic", + "ĠPre m", + "P ri", + "ĠCa o", + "ĠQuestion naire", + "Ġg athered", + "G W", + "ĠN V", + "ĠLact obacillus", + "Ġcycl in", + "Ġre ject", + "Ġsk ull", + "Ġa w", + "ĠC old", + "Ġmes ons", + "b d", + "Ġdetrim ental", + "ap ore", + "now led", + "ĠCX CL", + "Ġspik es", + "Ġt ent", + "ĠL ength", + "Ġdo or", + "Ġfl our", + "ustr ation", + "He alth", + "Ġtrans parency", + "Ġdisrup ted", + "H y", + "over l", + "ĠReinfor cement", + "cept ors", + "ĠK os", + "ret roviral", + "ĠIN T", + "ĠS or", + "Ġadop ting", + "Ġend oplasmic", + "Ġsu it", + "Ġopi oid", + "Ġintegr in", + "aw ay", + "Ġtail ored", + "ĠS oc", + "Ġqu ies", + "Ġhus band", + "Ġ umb", + "ĠC ai", + "ĠAs pergillus", + "ĠGa N", + "Ġdistingu ishing", + "Ġextrap olation", + "Ġc age", + "Ġscav enging", + "K F", + "T ree", + "ĠConfl ict", + "UN C", + "Ġmang anese", + "d ays", + "à Ł", + "ĠL ive", + "s d", + "ract or", + "Ġl ute", + "Ġdis similar", + "Ġ ib", + "ĠV eg", + "Ġoccur rences", + "Ġbin omial", + "Schem e", + "Ġt ape", + "ĠC ant", + "Ġelect rosp", + "C d", + "m ade", + "Ġse vent", + "sh ared", + "Ġaccess ion", + "or p", + "D ATA", + "le ted", + "V ari", + "Ġro se", + "tag ged", + "ĠA th", + "Ġed dy", + "est one", + "Ġes ters", + "Ġtyp ing", + "ĠStud ents", + "y i", + "ores istance", + "ino is", + "Ġgluc ocortic", + "i osis", + "Ġcor onal", + "Ġshe ath", + "ĠT rack", + "Ġequ ilibria", + "amm ing", + "Ġp ione", + "Ġsc iences", + "Ġsuppress ing", + "Ġdec o", + "if ndef", + "H is", + "Ġpel let", + "L inear", + "orb ent", + "Ġflat ten", + "Ġst raw", + "Ġal beit", + "ĠPredic tive", + "Ġg aze", + "Ġhydro ly", + "ut her", + "od ers", + "Ġfl ap", + "Ġsimplic ial", + "S ystem", + "Ġst ressed", + "Ġimmun oglobulin", + "il ia", + "Ġconsum ing", + "Ġà ©", + "gal act", + "Ġadul thood", + "Ġvor ticity", + "ycl ic", + "ovolta ic", + "ivest ock", + "Ġbed s", + "ĠPl anning", + "Ġparameter ized", + "Ġg host", + "maxim um", + "Ġsuper im", + "Ġphysic ochemical", + "g p", + "ong ue", + "Ġprim ordial", + "x ff", + "ins ula", + "M c", + "Ġminim izes", + "ĠGra vitational", + "os oma", + "ign ificant", + "Ġelucid ated", + "Ġsub surface", + "sign ificant", + "Ġrel atives", + "fer roni", + "trans f", + "Ġtail s", + "b eck", + "om agnetic", + "Ġun necessary", + "Ġmon omial", + "del ay", + "Ġst a", + "ĠS uz", + "Ġalter ing", + "LO G", + "ĠL ac", + "Ġr anks", + "h w", + "ĠN ep", + "Ġneu ropath", + "ĠComp e", + "G r", + "P ati", + "red uce", + "ĠMalays ia", + "cer al", + "Ġmicro bes", + "Ġlens ing", + "ĠCalc ium", + "ĠDeterm in", + "ĠCost a", + "Ġke eps", + "print ing", + "ĉĉĉĉ ĉĉ", + "ch in", + "ex posed", + "Ġperiod ically", + "Ġrend er", + "ĠCardi ovascular", + "ent in", + "Ġbio availability", + "Ġinterpret ations", + "ĠC U", + "Ġneg oti", + "Ġan tim", + "Ġdeem ed", + "Ġa e", + "Ġhal os", + "ĠMich igan", + "Ġoste oarthritis", + "di ag", + "ĠB eng", + "Ġmet agen", + "Ġparameter ization", + "di agn", + "ĠMat ching", + "Ġcatal ysis", + "ut s", + "Ġdissem ination", + "Ġout let", + "ĠMo on", + "ĠG ST", + "sp here", + "Ġresearc her", + "ambig uation", + "Ġra ises", + "Ġflavon oids", + "ĠMultiv ariate", + "Ġac cl", + "W I", + "Ġn u", + "Ġerg odic", + "un ique", + "atin ib", + "Ġresol utions", + "Ġhous es", + "D EC", + "ig hed", + "Ġsix th", + "Ġpolitic ian", + "ap ache", + "Ġsol ute", + "Ġaug ment", + "st ress", + "H IV", + "ĠS ets", + "Ġtrans istors", + "qu bit", + "am ines", + "Ġfarm ers", + "Ġn t", + "ĠLag range", + "Ġveget ables", + "Ġpre t", + "ĠS ynthetic", + "Ġcon es", + "Ġmedic ines", + "Ġgen omics", + "Ġexperi encing", + "ag land", + "Ġgen ital", + "ĠObserv atory", + "ĠS kin", + "ĠR osen", + "ĠBrit ain", + "gen ome", + "ĠEnt ropy", + "Ġr ac", + "G o", + "Ġw alks", + "cript or", + "ĠB aker", + "ok er", + "Ġprop ensity", + "Ġpopular ity", + "restric ted", + "ĠB ert", + "b efore", + "ĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ ĠĠĠĠĠĠĠĠĠĠ", + "aut o", + "R ank", + "ĠR CT", + "Ġp ocket", + "ob ut", + "Ġbenz ene", + "ĠCN T", + "yptoph an", + "all is", + "ĠRes ources", + "ĠBer lin", + "Ġsch olar", + "gl ob", + "ĠSp eed", + "ĠX iao", + "big gl", + "AN CE", + "ĠPr ime", + "Ph ys", + "id ia", + "Ġmon oc", + "ĠCommun ications", + "ĠPrec ision", + "ĠPa uli", + "Ġinvestig ators", + "ĠLi ang", + "Ġmeteor ological", + "m og", + "re ens", + "ub ric", + "Ġrearrang ement", + "or ta", + "E lect", + "ĠT ukey", + "ĠM is", + "Ġepid erm", + "ĠACK NOWLED", + "w art", + "Ġexcit on", + "Ġassoci ative", + "st yrene", + "Ġl osing", + "ĠO d", + "p rep", + "ess ation", + "Ġattrib utable", + "ĠNa vier", + "an z", + "Ġcorrect ness", + "o ints", + "ĠR ather", + "Ġassemb lies", + "Ġbrid ges", + "OS S", + "M ET", + "Ġper m", + "Ġauthor ities", + "Ġiod ine", + "sh ire", + "inter val", + "epti d", + "Ġpot ency", + "Ġrenew able", + "v ard", + "Ġsur jective", + "Ġsubsequ ence", + "ĠE Vs", + "it ching", + "Ġgen otyping", + "ĠAcc urate", + "iop hene", + "G ly", + "pl ified", + "ĠDist inct", + "AC H", + "Ġspe akers", + "hol m", + "Ġpro s", + "ĠDev ice", + "m c", + "ĠD ense", + "ĠV a", + "r ison", + "Ġac yl", + "ĠPrinc ipal", + "ĠV iral", + "Ġcos ine", + "ĠRes idual", + "Ġeff lux", + "ĠSub jects", + "Ġrect angle", + "work ers", + "Ġrot ated", + "Ġb omb", + "ĠRes olution", + "ne ar", + "Ġ ®", + "Ġestabl ishes", + "am ed", + "Ġcompet ence", + "G lu", + "ĠD end", + "ĠH sp", + "ens ation", + "ĠLe ad", + "Ġlog ger", + "sin h", + "Ġint ellectual", + "form er", + "C e", + "Ġmon ocyte", + "ho res", + "Ġdiast olic", + "Ġlif espan", + "ĠSil va", + "ar um", + "Ġtrans ducer", + "Ġout going", + "ent ation", + "Ġabsorb ing", + "it age", + "Ġsynt hesize", + "Ġfe eling", + "as ian", + "Ġcer amics", + "i ph", + "Ġnon local", + "P art", + "Ġimmers ed", + "station ary", + "lect ing", + "Ġweld ing", + "Ġres embles", + "ĠK at", + "m aster", + "Ġinters ect", + "ĠO lig", + "ĠTre nds", + "ag h", + "ĠN av", + "ĠT u", + "Ġep ist", + "Ġclin ics", + "Ġrepresent atives", + "Ġgrate ful", + "G PIO", + "H H", + "Ġun ambig", + "t uning", + "Ġnew sp", + "coh ol", + "################ ################", + "%%%% %%%%", + "represent ed", + "oc ic", + "ĠF uk", + "ĠS und", + "has one", + "M ode", + "ol one", + "ĠS b", + "Th ree", + "L ink", + "ce phal", + "ĠK ap", + "Ġelim inating", + "Ġmelan ogaster", + "â Ł", + "ĠB MD", + "IS E", + "ĠBat tle", + "Ġshrink age", + "ĠSe ven", + "ĠGl ass", + "rom agn", + "Ġk l", + "ĠOb viously", + "pres erving", + "ĠPl atform", + "ĠÌ ĩ", + "om avirus", + "ĠE ight", + "Ġall erg", + "ĠNanopar ticles", + "ary l", + "Ġpri ors", + "pat tern", + "Ġlinear ity", + "Ġtr uly", + "Pro cess", + "Ġdesc ending", + "ĠVictor ia", + "c ond", + "ĠI CP", + "ores cent", + "Ġauthor ity", + "Ġm ock", + "igm oid", + "Ġcomorbid ities", + "sim ple", + "Ġbl o", + "ĠComput e", + "Ġgest ation", + "achus etts", + "Ġph antom", + "ĠEd ward", + "ĠF BS", + "fact ors", + "ĠEstim ates", + "c lear", + "W B", + "pro ducts", + "num py", + "b rief", + "Ġsh op", + "ĠPol i", + "ĠRespir atory", + "Ġsurprising ly", + "Ġnanocom posite", + "divid ual", + "Ġholog raphic", + "ygd ala", + "ro plasty", + "ot actic", + "ĠPennsylvan ia", + "ĠSc ore", + "Ob j", + "Ġst ories", + "Ġmaxim izing", + "Ġgel atin", + "r ites", + "ĠT au", + "Ġtryp sin", + "Ġ ith", + "Ġf aint", + "Ġprim ing", + "ew orthy", + "ĠIn verse", + "Ġkn ots", + "sh arp", + "Ġtrain s", + "Ġcred it", + "ĠBel ow", + "pix el", + "Ġspind le", + "ĠP ast", + "Ġenum erate", + "ol ateral", + "Ġatt ending", + "Ġquanti zed", + "Ġhapl otypes", + "enc l", + "Ġw aven", + "Ġfurther more", + "Ġchalleng ed", + "Ġmanufact ured", + "ipher al", + "Ġinfin ites", + "ĠR and", + "Ġst aging", + "ag an", + "Ġper ox", + "tr ifluor", + "ĠMc K", + "ĠF OX", + "ĠL ank", + "ĠLu o", + "ĠAn th", + "ibri o", + "y el", + "ĠJ i", + "ĠI O", + "ĠB ridge", + "ĠR ow", + "Ġcompens ated", + "ats u", + "Ġhypothe tical", + "Ġtermin als", + "Ġcobal t", + "m ers", + "ĠM ang", + "N I", + "ĠR ac", + "AL S", + "f en", + "ĠU b", + "Ġpred ation", + "c adherin", + "ĠSh anghai", + "Ġtri es", + "Ġsp ort", + "acryl ate", + "ĠAlgebra ic", + "ain ts", + "Ex pr", + "Ġand rogen", + "Ġw edge", + "dis p", + "Ġstir red", + "ĠA le", + "Ġc ock", + "F our", + "Ġsc anner", + "Ġplas mon", + "ĠG ender", + "ĠRec ord", + "ĠInj ury", + "obl astic", + "ĠFlu orescence", + "Ġanti depress", + "Ġdefin itive", + "Ġrep ression", + "ordin ates", + "Ġangi ography", + "ĠHel ical", + "Ġcancell ation", + "re lease", + "Ġrel ational", + "ĠAnd re", + "mo lecule", + "Ġshap ing", + "ĠDen mark", + "ĠAL S", + "ĠN W", + "over rightarrow", + "Ġcomb at", + "box es", + "sub ject", + "Ġnanos cale", + "Ġcan ine", + "Ġs aving", + "Ġstrateg ic", + "St at", + "ĠD ub", + "Ġper mitted", + "ĠTw itter", + "â Ķ", + "Ġmem ories", + "ĠBus iness", + "ad ays", + "Ġpool ing", + "ĠClust ers", + "im ide", + "oun ters", + "frac tion", + "ĠCl iff", + "C am", + "E ven", + "K Y", + "k it", + "ibr ated", + "Ġaccompan ying", + "an us", + "Ġbu oy", + "Ġprolifer ative", + "Ġpro c", + "Ġstabil izing", + "ĠNam ely", + "pos p", + "so on", + "Ġaberr ant", + "Ġinter stellar", + "Over all", + "ĠG n", + "ĠFeed back", + "Ġo racle", + "Ġpre natal", + "com mun", + "Ġoutbreak s", + "Ġfertil ization", + "ĠM AG", + "Ġsing er", + "ĠMic rowave", + "ĠPar liament", + "cast ing", + "Gen eral", + "al gorithm", + "Ġph rase", + "Ġa vian", + "ĠP LA", + "Ġhard ly", + "approxim ately", + "AR CH", + "Ġtrans c", + "Ġdec omp", + "cont in", + "ĠMil ky", + "Ġher pes", + "R ange", + "O FF", + "prising ly", + "l x", + "ĠAB A", + "Ġsh ore", + "Ġderiv ing", + "Ġpel lets", + "nowled g", + "I tem", + "strand ed", + "bu ilt", + "Gl c", + "qu ist", + "ĠSub strate", + "Ġtra ditionally", + "ĠM ount", + "ival ence", + "ax ation", + "Ġloc ate", + "Ġg un", + "Ġvoc abulary", + "ĠPol ym", + "Ġec t", + "Ġm ult", + "Ġsediment ary", + "Ġautocor relation", + "ĠS ympt", + "Ġterr itory", + "Ġexcit atory", + "Ġv ote", + "Ġhe red", + "ace a", + "ĠF ocus", + "am pling", + "ff ee", + "Ġprim es", + "ĠM aking", + "ir s", + "MP s", + "Ġl itter", + "amet hasone", + "Ġk J", + "Ġsecret ory", + "Ġcost ly", + "Ġpartners hip", + "ĠBacter ia", + "Ġperoxid ation", + "st roke", + "ĠS av", + "ĠB W", + "Ġconn ects", + "Ġam ine", + "r il", + "Ġbat tle", + "ĠN otes", + "ĠPro vid", + "ĠInstit utional", + "Ġpro pri", + "f an", + "Ġp un", + "rom b", + "v ities", + "ĠC AM", + "ĠI sh", + "ĠH N", + "ĠRec omb", + "sc he", + "Ġsyn chrotron", + "ri k", + "syn aptic", + "ĠGeorg ia", + "? ?", + "C Y", + "Ġcorrespond ed", + "kin ase", + "ĠI TS", + "Ġpropos als", + "Ġbi oge", + "ĠE SR", + "ĠW en", + "ĠJ a", + "ĠSe vere", + "ĠAd en", + "ĠC CL", + "Ġse at", + "ĠK re", + "Ġhelp ing", + "Ġn ets", + "ĠL ep", + "hed ra", + "opo ulos", + "ĠB ak", + "ans as", + "Ġref rig", + "Ġubiquit ous", + "Ġmat ters", + "Ġsil icate", + "ĠLast ly", + "ĠThe ories", + "Ġag arose", + "big gr", + "trans ition", + "ĠDec omposition", + "b romo", + "Ġstake holders", + "ĠE E", + "On ly", + "ĠKen ya", + "Ġarg on", + "ĠIdentif ying", + "Ġtourn ament", + "cl ock", + "ĠCF U", + "ĠBehavi oral", + "Ġp od", + "Ġtaxon omy", + "ĠPro duct", + "ĠAl ong", + "Ġfamil ial", + "Ġdescript or", + "v ated", + "ĠVari ables", + "t p", + "Ġgood s", + "ĠA ST", + "ĠAn is", + "Ġspin or", + "at tention", + "Ġbas ket", + "Str uct", + "Ġimmunohist ochemical", + "eng ers", + "C AT", + "Ġtang ential", + "C ap", + "ĠP air", + "Ġvisco elastic", + "ĠAd s", + "Ġglycos ylation", + "Ġd ur", + "ĠMin imum", + "Ġrig idity", + "st ats", + "till ation", + "ĠDisc rim", + "ĠLeg end", + "Pre vious", + "fil m", + "Ġalumin ium", + "M icro", + "in ia", + "eg el", + "ĠSub cellular", + "Ġbottlen eck", + "Ġsy ll", + "ic le", + "Ġshe af", + "che ll", + "ex ample", + "ĠSe lected", + "Ġpred ators", + "Ġre per", + "Ġstr ugg", + "ĠM aria", + "ly l", + "L F", + "Ġexerc ises", + "ob ium", + "IL ITY", + "cor rected", + "Ġbenchmark s", + "ĠT ol", + "Ġinter cept", + "ĠCalc ulation", + "ĠIndones ia", + "Ġgli oblastoma", + "K M", + "ĠSup plemental", + "Ġciti zens", + "ad ren", + "Ġmultim odal", + "Ġmosquito es", + "iv a", + "ĠFind ings", + "ĠP ub", + "ĠMac roph", + "Ack nowledg", + "Ġbas ins", + "ex act", + "Ġgra des", + "Ġf ir", + "ig a", + "ĠPol ynomial", + "ĠLong itudinal", + "Ġsemicon ductors", + "T op", + "ip tera", + "Ġlack s", + "ro graph", + "Ġselec ts", + "Ġswe et", + "Ġb ac", + "Ġdown loaded", + "ap onic", + "ij k", + "ot onic", + "normal ized", + "ĠVari ability", + "di vision", + "ĠSu pers", + "il ab", + "H uman", + "Ġlept in", + "Ġosm otic", + "Ġh ur", + "ĠSing apore", + "ĠO PT", + "ĠSo viet", + "lit axel", + "ret aceous", + "ĠOn c", + "ĠI X", + "ul as", + "u ent", + "Ġlymph oid", + "T c", + "Ġrational e", + "L ayer", + "os ities", + "Ġdes ire", + "ĠAnn ual", + "ub a", + "ĠCompound s", + "Ġantif ungal", + "Ġcation ic", + "it ems", + "acter ium", + "amil ies", + "Ġelong ated", + "ĠMass achusetts", + "ĠIr ish", + "ass o", + "az o", + "ĠBur k", + "rob enius", + "Ġis instance", + "b ion", + "Ġgre edy", + "Ġnicot ine", + "Ġretrie ve", + "Ġsym pathetic", + "que e", + "Ġfol i", + "Ġsp utter", + "Ġgra ding", + "determ ined", + "Ġab norm", + "Ġman agers", + "Ġtop ical", + "Ġimm ig", + "ĠD NN", + "g tr", + "Ġdet ections", + "ĠOb esity", + "s uc", + "ĠSc he", + "Ġtr unk", + "Ġto ugh", + "ĠB N", + "Ġr u", + "ox if", + "Ġaim ing", + "ĠExt racellular", + "Ġhapl otype", + "D u", + "ĠD ing", + "ĠD ol", + "Ġhum id", + "b rom", + "Ġoff line", + "Comb ining", + "Ġpuls ar", + "Ġpar i", + "part ate", + "im ated", + "Ġwaters hed", + "acryl amide", + "ex ec", + "ĠCom posite", + "Ġdispers ive", + "Ġt ons", + "rom etry", + "ĠJ ud", + "az a", + "Ġchick ens", + "reg ister", + "n z", + "U til", + "ĠV es", + "e V", + "ĠR ule", + "sub stituted", + "Con v", + "qu ery", + "M ac", + "ĠT ar", + "im plies", + "ĠR ates", + "Ġr ins", + "Ġtimes cales", + "ĠCz ech", + "S uch", + "res timate", + "ĠM b", + "ĠFu j", + "ĠI MD", + "c it", + "Ġra ising", + ".... ....", + "h ome", + "as ted", + "Ġoc ta", + "Ġc admium", + "Ġps ori", + "role um", + "ĠSt ellar", + "ĠKin ase", + "ĠG ard", + "ie u", + "ĠMo S", + "M G", + "ĠG SH", + "Ġhaz ards", + "Ġn ice", + "he ating", + "Ġreproduc ible", + "gen esis", + "ĠIg M", + "Ġbe at", + "onucle ase", + "entral ized", + "ĠL é", + "Ġd ol", + "Ġdeep ly", + "rac tive", + "Ġgl ial", + "i ella", + "Ġinitial ized", + "ĠMethod ology", + "Ġbent hic", + "om i", + "ĠAl ter", + "Or dered", + "ĠL IN", + "Ġun ilateral", + "Ġcortic oster", + "L EN", + "Ġdil ute", + "Ġmetall oprotein", + "ab eth", + "amp ion", + "Ġmor al", + "ĠSi C", + "Ġquadr ature", + "Ġsediment ation", + "et e", + "ĠF rag", + "Ġpeak ed", + "Ġmitig ation", + "Ġsol di", + "Ġdoub ly", + "Ġellip so", + "Ġlnc RNAs", + "Ġâİ ¢", + "ĠS ame", + "ĠS ustain", + "ĠCap acity", + "Ġs omat", + "Ġtrans istor", + "Ġassay ed", + "ĠN ur", + "to ols", + "S ing", + "Ġlig ament", + "ate ver", + "Ġper ce", + "hen ce", + "U X", + "s ent", + "EG G", + "th ird", + "end ers", + "the oretic", + "Ġre wards", + "ut o", + "Ġinstall ation", + "ĠKine tic", + "ĠIn nov", + "ĠSol ving", + "ĠS ymmetry", + "Ġr amp", + "Ġneu ropathy", + "omer ization", + "Ġcat ech", + "P red", + "ĠB oh", + "EM ENT", + "Ġarm y", + "ĠYuk awa", + "Ġalign ments", + "ĠDepend ence", + "Ġen v", + "e an", + "s r", + "Ġinterp reting", + "eloc ity", + "Ġpsych ology", + "Ġbiofil ms", + "Ġeccentric ity", + "l ot", + "analy tic", + "Ġperiod icity", + "n ings", + "ĠK ent", + "fl ag", + "Ġm p", + "ĠN ich", + "hi re", + "Ġfl are", + "Ġcit rate", + "Ġp aste", + "Ġdele te", + "zym es", + "orient ation", + "ĠH Y", + "Ġcomm ands", + "Ġstri ke", + "s ymbol", + "ĠM ind", + "Ġoptim isation", + "Ġosteopor osis", + "ĠInf lammation", + "ĠIntellig ence", + "e h", + "ut um", + "Ġv ec", + "ell ation", + "ĠBl och", + "ĠMajor ana", + "en or", + "ĠN gu", + "Ġde uter", + "oped ia", + "Ġ utter", + "Ġrib osome", + "Ġact ors", + "elect ronic", + "é e", + "Ġfeat uring", + "ag le", + "Ġper in", + "ĠC ivil", + "Ġpred efined", + "l ag", + "ĠJ AK", + "j amin", + "in dividual", + "on c", + "Ġf ishing", + "di tive", + "N orm", + "ĠSc anning", + "van ishing", + "Ġc essation", + "ĠH ole", + "rib utes", + "I E", + "ĠM pc", + "weg ian", + "M a", + "Ġrevis ited", + "ĠPl us", + "abil ized", + "Ġsc anned", + "ĠEx change", + "Ġbrom ide", + "L ife", + "ot roph", + "AD S", + "âĭ ħ", + "Ġoper ative", + "ĠB ERT", + "Ġpl ume", + "Ġpo orer", + "Ġtro ut", + "Ġmicrotub ule", + "Ġphosph atidyl", + "radi us", + "ĠMus cle", + "Ġcarcin ogenesis", + "Ġsee ing", + "ucle in", + "f ollow", + "Ġsup plements", + "ol ars", + "spec ially", + "Ġcomple ting", + "Ġna ïve", + "ĠÏ ©", + "clero tic", + "D isc", + "ĠF estival", + "Ġcl ick", + "cl usive", + "Ġcatal ogue", + "Ġap ps", + "ĠS ED", + "Ġstack ed", + "Ġtun e", + "ĠDM EM", + "Ġaeros ols", + "Ġg ear", + "ant ine", + "ĠSt one", + "Ġpos itives", + "tri ang", + "prob ability", + "Ġdec oupling", + "ĠÍ ĵ", + "ĠV in", + "Ġsurv ived", + "Ġre plicated", + "ut rient", + "Ġtemper ate", + "Ġens embles", + "Ġmultic enter", + "Ġg aseous", + "ide a", + "class ification", + "ĠOut come", + "cl onal", + "Ġdiscontinu ity", + "Ġadvantage ous", + "Ġdist ricts", + "ĠI BM", + "inguish able", + "Ġcar s", + "c ult", + "en riched", + "arg in", + "nov ae", + "stead y", + "Ġbu y", + "pir ation", + "Ġpartition ed", + "Ġin ability", + "p q", + "Ġb ull", + "od end", + "Ġass istant", + "Ġlum en", + "Ġconver ting", + "P Y", + "z ol", + "ut ors", + "ĠNLR P", + "app ly", + "ĠBon ferroni", + "L s", + "Ġt ips", + "ĠL N", + "rol ase", + "Ġadv is", + "ĠMet ast", + "Ġsaliv a", + "Ġin habit", + "Ġr im", + "de bug", + "An y", + "Ġfor b", + "Ġvers atile", + "ĠComp act", + "v oc", + "ĠI so", + "ĠJ us", + "b odies", + "AR M", + "ĠGW AS", + "he tized", + "Ġmicrofluid ic", + "Ġacet onitrile", + "Ġin hom", + "Ġparen ch", + "Ġins ensitive", + "Ġag ency", + "po or", + "ĠAn gi", + "Ġappro ached", + "Ġem ulsion", + "Ġvol untary", + "ut t", + "ĠRec urrent", + "ric ulum", + "à ª", + "Ġt all", + "ĠDep th", + "Ġf f", + "ĠInc idence", + "Ġmanifest ation", + "Ġcomprom ised", + "i aceae", + "ĠM IT", + "otrans fer", + "ĠW D", + "m ov", + "ĠMan ual", + "M edi", + "Ġinterfer ing", + "ĠJacob i", + "K T", + "Ġs arcoma", + "Ġkid neys", + "Ġod or", + "Ġt i", + "yd ay", + "alth ough", + "vis ible", + "Ġd engue", + "ĠC AL", + "str at", + "ĠVari ations", + "in ib", + "comp onents", + "ĠT ob", + "ĠAnti oxidant", + "Í Ķ", + "Ġk iller", + "Ġsubt racted", + "ĠE vents", + "Ġim plements", + "ĠG AN", + "Ġprophyl axis", + "Ġno zz", + "Ġsm oothed", + "Ġdecay ing", + "ĠIniti ally", + "Ġuncom mon", + "Ġconduc tor", + "ĠW OR", + "av ity", + "ĠX ie", + "ĠAc et", + "Ġin e", + "ĠBe am", + "opol ymer", + "ĠX ML", + "ĠW ide", + "Ñ ĥ", + "Ġe jection", + "B MI", + "t c", + "ue z", + "Ġcereb ellar", + "Ġcatch ment", + "cox on", + "ĠSh annon", + "Ġcentral ity", + "Ġsaf ely", + "pro be", + "ĠLabor atories", + "Ġn c", + "Ġsp her", + "Ġprob ing", + "ĠLe v", + "Ġa f", + "ĠM ig", + "ĠV ascular", + "Ġprogram mes", + "Ġcontamin ants", + "sequ ent", + "Ġbond ed", + "integr ation", + "b os", + "ĠF ew", + "ĠIll inois", + "S he", + "W C", + "ĠG PIO", + "o C", + "ĠM aternal", + "erc etin", + "ĠMass ive", + "Ġen orm", + "img ur", + "Ġb idirectional", + "ĠG raphene", + "ins ky", + "ĠObs erve", + "Ġst ops", + "b io", + "ĠL ines", + "ĠG ill", + "Ġeigen vector", + "Sp ace", + "ĠM ining", + "Ġmel atonin", + "ĠS ET", + "onse qu", + "os cale", + "ĠR aw", + "Ġreview ers", + "Ġnan ofib", + "t aking", + "amm ad", + "Ġrecurs ion", + "us al", + "Ġpos itron", + "ĠN IH", + "ĠIN TER", + "ĠDoc ument", + "Ġconstant ly", + "Ġunderg one", + "Ġelect roweak", + "Ġiter atively", + "fol io", + "Ġsub family", + "Ġâİ ¥", + "P age", + "f erm", + "av ir", + "Ġag encies", + "Ġpol ys", + "ĠSqu are", + "ym m", + "Ġhydro gels", + "al most", + "ar ter", + "Ġan kle", + "Ġr ises", + "Ġmed ull", + "g ated", + "Ġmon onuclear", + "Ġdiscuss ing", + "Ġprof essor", + "trans formed", + "Ġcol ours", + "rag g", + "emic on", + "Ġsymmet rical", + "Ġplac ental", + "Ġl i", + "Ġstud io", + "sequ ences", + "Ġt am", + "ĠL ap", + "ĠCriter ia", + "Ġhapp ened", + "Ġantifer romagnetic", + "ĠHaus dorff", + "ĠCONCLUS IONS", + "H ER", + "V R", + "ĠK or", + "ĠA PO", + "Ġprot ecting", + "ĠS OL", + "ĠB uck", + "ph ia", + "ĠMul tim", + "on ine", + "uls ions", + "Ġg p", + "benz amide", + "ĠNAD PH", + "ĠOh io", + "ĠM EG", + "CO VID", + "Ġdisplac ed", + "ĠAb b", + "Ġbran ched", + "ĠN avy", + "ĠN rf", + "ĠO DE", + "ach i", + "ĠTrans ient", + "Ġcircum ference", + "Ġbe es", + "ir ation", + "Ġfac ulty", + "IG HT", + "ĠMetabol ism", + "M K", + "ĠIn equ", + "ĠQual itative", + "P BS", + "ter minus", + "k ary", + "o vian", + "ĠT Hz", + "ĠRel iability", + "f uran", + "Ġcor ners", + "Ġattack er", + "Ġmar riage", + "oprec ipitation", + "ĠC ry", + "ĠâĬ Ļ", + "Ġevol ves", + "Ġb an", + "Ġdi urnal", + "oun ce", + "Ġover w", + "ĠH off", + "Ġextr insic", + "am ps", + "UL AR", + "op her", + "Ġlight ing", + "Ġarchitect ural", + "hes ive", + "Ġsav ings", + "Ġglauc oma", + "oz oa", + "ĠO ption", + "cont roll", + "eck er", + "Ġoste ocl", + "Ġglyc ine", + "anal yses", + "ĠAl d", + "ĠS yd", + "ĠC x", + "Ġscal ars", + "Ġknow ing", + "Ġrem ember", + "ĠEmb ry", + "T EM", + "ĠB ran", + "F ORM", + "Ġsurv iving", + "Ġglob ular", + "Ġincl usive", + "sc hed", + "UT ION", + "Ġquadrup ole", + "ĠH ubbard", + "Ġax onal", + "ĠCos mic", + "Ġsl ots", + "ĠProced ure", + "ag in", + "ĠLo op", + "are r", + "Ġbut ter", + "Ġhist opathological", + "f usion", + "AN OVA", + "Ġclos ing", + "ĠL ord", + "ĠB is", + "ĠR AM", + "ID E", + "Ġj ournals", + "Ġmon keys", + "Ġatten uates", + "Ġsegment ed", + "T OF", + "o tional", + "pol ymer", + "ĠSha h", + "A kt", + "W r", + "l ov", + "Ġpolym orphic", + "Ġarrang ements", + "U F", + "l on", + "Ġdep ressed", + "NA T", + "ĠOper ation", + "Î ¹", + "ĠR an", + "â IJ", + "Ġthere after", + "Ġmyel oma", + "j or", + "à ¥", + "ĠW inter", + "pt osis", + "D ir", + "ver ty", + "ĠF inn", + "Ġorth olog", + "Ġmonoton ically", + "Ġtect onic", + "ĠG BM", + "ĠA O", + "Ġgener ative", + "C learly", + "Ġt ile", + "ĠR NN", + "Ġground s", + "Ga As", + "Ġbe e", + "ĠB oy", + "ĠTranscription al", + "ur in", + "ot om", + "Ġsinus oidal", + "ĠA y", + "ĠCl inic", + "ut orial", + "ĠAD C", + "ER IAL", + "c ation", + "ĠAD HD", + "cycl ohex", + "ĠHaw ai", + "ast om", + "Ġmorph ologies", + "Ġrod ents", + "Ġscal ability", + "R OS", + "a emia", + "Ġdecom pose", + "Ġpiv otal", + "Ġdiffus ivity", + "Ġco valent", + "ĠK D", + "ataly st", + "Ġold est", + "Ġsu itability", + "Ġw ants", + "if ts", + "ĠDist ributions", + "ĠQue en", + "l ich", + "Ġpar se", + "ĠM HD", + "Ġrec re", + "Ġhydrox ide", + "e um", + "Ġle v", + "Ġrefer ral", + "plan es", + "ĠEg ypt", + "Ġl enti", + "Ġtrans actions", + "Ġexp ense", + "Ġcy sts", + "Ġabs cess", + "Ġmicro RNAs", + "eff ectiveness", + "ĠDifferenti ation", + "Ġcer tif", + "c ience", + "ĠRE L", + "Ġread out", + "ĠQu asi", + "Ġround ed", + "ot ti", + "e fficients", + "Ġsynchron ized", + "Ġsil ico", + "Ġfore casts", + "Ġd μ", + "Ġex otic", + "ĠO CT", + "x b", + "Ġas ynchronous", + "ne z", + "chi at", + "Ġha emat", + "Ġfulf ill", + "ĠM ix", + "ib li", + "f m", + "Ġj ava", + "sol uble", + "Ġincomp ressible", + "âĨ ij", + "CD M", + "Ġdil ation", + "L YP", + "as hes", + "ĠS ports", + "Ġfund ament", + "ĠSa udi", + "Ġen roll", + "ĠNa OH", + "Ġcrust al", + "ĠInstr uments", + "Ġïģ ¡", + "Res ult", + "Ġpref erential", + "Ġsug ars", + "Ġdim ers", + "ĠEmerg ing", + "è re", + "diab etic", + "Ġstrengthen ing", + "ep i", + "ĠM eg", + "ĠY our", + "ĠSet ting", + "le z", + "ĠB ou", + "Ġhist ology", + "Ġol ive", + "ĠDis orders", + "Ġdistor ted", + "Ġcompet e", + "c ens", + "ĠA e", + "ĠG G", + "Ġquantif ying", + "Ġa ur", + "ĠW right", + "Ġsuperconduc tor", + "ed s", + "st alk", + "con cent", + "ĠLim ited", + "Ġst yles", + "des ign", + "ĠE llip", + "PL A", + "mog orov", + "ĠR idge", + "Ġrandom ization", + "a ft", + "ic ially", + "ĠBi otechnology", + "Ġseiz ure", + "K I", + "AV E", + "re ceptor", + "Ġgram mar", + "Ġcr ime", + "n ection", + "in ces", + "ĠCom pton", + "Ġventric le", + "Ġred istribution", + "yn aptic", + "Par ameter", + "N ormal", + "P ack", + "erm ann", + "ul ants", + "de generate", + "ĠNewton ian", + "Ġancest ral", + "ph rag", + "Ġimp ression", + "Ġnormal ize", + "Ġambig uous", + "Ġingredi ent", + "ĠCl aim", + "Ġcle aved", + "ĠAppro aches", + "ĠS PECT", + "cs v", + "ĠReve als", + "ĠW aves", + "Ġdwar fs", + "ĠProg ress", + "Ġa orta", + "Ġn ig", + "ĠAd ams", + "ĠM üller", + "ĠY ellow", + "ĠC ord", + "ĠPh ill", + "ĠF ormal", + "bes gue", + "ter min", + "r n", + "b n", + "k ine", + "r it", + "q i", + "ĠRout e", + "en ol", + "ĠA SC", + "ĠP u", + "m ill", + "um er", + "Ġsuper nova", + "i ative", + "diff erenti", + "Ġto lu", + "op us", + "R M", + "Ġpo verty", + "ĠX X", + "ĠïĤ ¶", + "ult ry", + "Op tim", + "Ġgl acial", + "ĠDis pers", + "Ġdifferenti ating", + "á ndez", + "pro ject", + "ĠEl iz", + "scal ing", + "ĠT oll", + "Ġnon empty", + "Ġpredic ate", + "Ġgyr us", + "min ute", + "â ĸ", + "ĠH ind", + "ĠL iving", + "V S", + "pri or", + "ĠVer tical", + "ark s", + "ĠS FR", + "ĠViet nam", + "comp are", + ">> >", + "Ġb anks", + "Ġse ptic", + "ĠB if", + "ĠE PS", + "ĠInt el", + "ĠDis order", + "P N", + "ĠN ord", + "tiv eness", + "Ġdr illing", + "ĠSub ject", + "enari o", + "Ġr ms", + "ph ones", + "h ang", + "ĠTechn ique", + "Ġcl ot", + "Ġinters ections", + "Ġan ions", + "ab ove", + "Ġcl ause", + "Ġgen u", + "oz o", + "rh iz", + "Ġlob es", + "ĠB ian", + "Ġexer ted", + "ure th", + "rom a", + "ĠCh arge", + "ĠSyn chron", + "Ġcont ing", + "othe rapeutic", + "gtr sim", + "ĠReson ance", + "ĠF al", + "und le", + "Ġdrop out", + "ĠPers pective", + "OL OG", + "at ches", + "ĠSequ ences", + "Cons idering", + "Ġprosp ects", + "Ġal iqu", + "Ġstr ata", + "Ġanalog s", + "Ġencour aged", + "ĠP ulmonary", + "Ġch im", + "ĠC FT", + "un ar", + "iz z", + "end ocrine", + "ĠC RE", + "ĠSt roke", + "âĩ Ĵ", + "up uncture", + "trans lational", + "ĠGr iff", + "ĠS ter", + "erg ed", + "ph rine", + "Ġl ivestock", + "ĠH ash", + "Ġdos ing", + "Ġplas mas", + "ĠCompar isons", + "Ġencour aging", + "Ġcompar atively", + "Ġcharacter isation", + "Ġasc ending", + "ĠF ixed", + "Ġveget able", + "es pecially", + "ĠL ange", + "ĠC oding", + "Ġverteb rate", + "F W", + "ĠOR F", + "ĠT ub", + "le e", + "Ġtim ely", + "E p", + "ĠâĪĴ âĪŀ", + "Ġlip osomes", + "Ġextrem al", + "ropol itan", + "ĠC ay", + "ĠB iod", + "o ulli", + "D ri", + "ĠR ats", + "Ġcent roid", + "osp in", + "rosp inal", + "Ġsol itons", + "por tive", + "ĠMc G", + "B bb", + "Ġpar affin", + "lec tively", + "Ġmetast able", + "Ġdissip ative", + "Ġassembl ages", + "Ġcol onic", + "Ġs ized", + "Ġcr yp", + "process or", + "ç ão", + "Ġacknowled ged", + "ĠUncertain ty", + "ĠInd ustrial", + "Ġunc ont", + "Ġref ere", + "ĠN itrogen", + "Ġlif ting", + "Ġfor get", + "Ġfeel ings", + "Ġdig its", + "Ġstrat ig", + "yp es", + "Ġcomposition al", + "Ġsupernat ants", + "Ġconflic ting", + "Ġdisadvant age", + "adel phia", + "P d", + "ĠC oupling", + "Ġexpendit ure", + "ik i", + "des cribed", + "ĠRN ase", + "ĠCon vex", + "ĠB ax", + "ung sten", + "Ġbo iling", + "Ġbas ement", + "ocardi al", + "hist ory", + "int on", + "trim ethyl", + "Ġgraft ing", + "ĠHard y", + "ĠFem ale", + "ĠF ollow", + "ĠE ST", + "tis tic", + "O pen", + "Ġref lux", + "ele ments", + "Ġpol ysaccharide", + "dim s", + "ac ency", + "Ġbi ore", + "cap ac", + "Ġoverex pressed", + "e ither", + "Ġl aid", + "Ġinc ision", + "Ġass ets", + "inf lammation", + "Ġreconstruc tions", + "Ġglomer ular", + "Ġcon vey", + "ĠCX CR", + "or o", + "Ġclass ifying", + "Ġcop e", + "Ġp d", + "lin ic", + "Ġch ord", + "ĠAg ing", + "Ġpal m", + "Ġpermit tivity", + "ĠRever se", + "Ġoff shore", + "Ġdoub t", + "im oto", + "ĠCol omb", + "Ġrod ent", + "ĠElect rochemical", + "ĠImpro vement", + "ines cent", + "ĠTr iton", + "Ġtransf usion", + "Ġlocom otion", + "Ġdanger ous", + "Ġwe ighed", + "ĠH SV", + "t echn", + "ĠDi agram", + "Ġpari etal", + "s ix", + "Ġtit les", + "yl on", + "Ġheur istics", + "Ġj aponic", + "Ġtransl ations", + "Ġtit ers", + "Ġw orms", + "ĠD PP", + "Ġcytos keleton", + "Medi ated", + "ari ance", + "the l", + "à ħ", + "ĠInf lammatory", + "Ġoscill ating", + "Ġavoid s", + "Def ine", + "ĠOlymp ics", + "og el", + "Ġhe me", + "Ġmic rop", + "Ġthreat s", + "Q CD", + "X RD", + "ĠC oupled", + "Ġl m", + "ĠHel ic", + "Ġdischarg ed", + "Ġro oted", + "Ġallevi ate", + "Ġcaus ality", + "ĠC row", + "ĠM ack", + "ĠAir port", + "Ġchem okine", + "Ġl l", + "ĠN ar", + "omy ces", + "eth oxyphenyl", + "ĠD aily", + "ĠFin land", + "Ġh orn", + "ĠO rient", + "Ġion ized", + "ĠY ears", + "Ġquas ipar", + "Ġper cutaneous", + "Ph ase", + "Ġfore ground", + "ĠA NAL", + "Ġincre ments", + "st an", + "Ġspec ulate", + "T X", + "Ġp ile", + "Ġd ic", + "ip y", + "wind ow", + "neut ral", + "ĠAtl as", + "ĠM TT", + "ĠN y", + "ĠV III", + "ĠFil ms", + "sing ular", + "rem ove", + "L ength", + "ĠRec e", + "wa it", + "Ġpurch ase", + "ĠWik ipedia", + "ĠL ars", + "Ġsynt actic", + "Ġactu ator", + "ĠAK T", + "ĠB ry", + "ĠRes ult", + "ĠVari ational", + "Ġjudg ment", + "J ECT", + "xim ab", + "Ġtrac ed", + "Ġcardiomy opathy", + "W N", + "ĠRod rig", + "b t", + "Ġb id", + "ac le", + "am ura", + "Ġep ic", + "Ġp uzz", + "ĠS ox", + "Ġinflu x", + "ÃŃ n", + "ulos keletal", + "D im", + "ĠS CC", + "ĠR AS", + "m r", + "U I", + "Ġj un", + "ĠSp earman", + "Ġfair ness", + "et z", + "ĠP PI", + "in ance", + "en ko", + "Ġgal act", + "ö m", + "Ġex ceptions", + "ĠC retaceous", + "M Y", + "Res p", + "Ġp ep", + "ĠOr d", + "ST E", + "Ġhel icity", + "Ġoffic er", + "T arget", + "ĠNor wegian", + "Ġdehyd ration", + "ĠSIR T", + "ĠRob inson", + "ĠBen chmark", + "v iral", + "Re al", + "Ġd oxorubicin", + "Ġestim ations", + "ĠCa uc", + "Ġaddi tives", + "m odes", + "ĠH end", + "Ġacceler ating", + "ĠG ordon", + "ĠMagn et", + "Ġgon ad", + "Ġpyro lysis", + "coh olic", + "ĠPK C", + "S AR", + "Ġw inding", + "ter ious", + "ĠMountain s", + "ĠS ymbol", + "ĠMat the", + "ĠSh in", + "S cript", + "r ug", + "Ġm W", + "ĠI SM", + "ĠN g", + "Ġapp oint", + "ĠA IDS", + "Ġpor ts", + "diff erential", + "ĠJ es", + "ĠN eed", + "Ġlens es", + "ĠAMP K", + "à ¤", + "le af", + "ĠB ron", + "Ġprof it", + "L ocal", + "ĠEx amination", + "ĠCh ief", + "Ġopin ions", + "ĠR ound", + "form ations", + "Ġcol linear", + "Ġdig ested", + "lass ical", + "erv ative", + "Ġce phal", + "Ġdisadvant ages", + "Ġïĥ ·", + "Ġsubt racting", + "Ġwe igh", + "B ound", + "D G", + "Ġinflu ential", + "Ġtox ins", + "ĠBen jamin", + "ĠNum bers", + "c rystal", + "Ġst ocks", + "ĠB our", + "ĠComp eting", + "Ġac qu", + "t RNA", + "ĠSep aration", + "Ġtag ged", + "Ġcon ject", + "ĠPr ince", + "Ġgra zing", + "Ġrele ases", + "ĠChall enge", + "ATP ase", + "Ġe mail", + "ins ically", + "ĠReg ulatory", + "M essage", + "Ġsl it", + "Ġpolyg on", + "Ġdoubl ing", + "Ġrece ivers", + "Ġtrack ed", + "Ġengine er", + "st ained", + "ĠD anish", + "sh ock", + "ĠM az", + "Ġco ugh", + "ĠImmun ohist", + "C onsequ", + "arm acy", + "Ġchem o", + "ĠM H", + "Ġemerg es", + "Ġanne aled", + "Ġhypot hesize", + "ĠTyp ically", + "ĠB ang", + "ĠP uls", + "Ġgir l", + "Ġherb iv", + "ĠAN N", + "Ġse ism", + "ĠCy tok", + "ĠThrough out", + "Ġadapt ations", + "l ang", + "Ġcl onal", + "um ulation", + "ĠUn iform", + "Ġh i", + "op ent", + "Ġbut ton", + "ten e", + "Ġprote asome", + "b red", + "ĠN elson", + "racycl ine", + "ĠD Y", + "Ġimmun oblot", + "pro l", + "Ġp ic", + "Ġcomp ilation", + "ĠDev ices", + "eterm ined", + "ĠFranc is", + "not ation", + "wr iting", + "ter ase", + "ĠSte phen", + "am el", + "ĠCh u", + "al one", + "Ġexha ust", + "re levant", + "ĠStr at", + "Ġparametri zation", + "ĠB ull", + "ĠRem ote", + "incre asing", + "Ġd d", + "âĢ °", + "yroid ism", + "il in", + "ĠH ip", + "IC A", + "ĠAp optosis", + "Ġmach ining", + "LD L", + "Ġg em", + "ĠF FT", + "ĠGu ang", + "Ġorigin ates", + "d at", + "c one", + "ĠAd oles", + "uc ci", + "av oid", + "ul pt", + "ur ium", + "Ġliter acy", + "Rec ent", + "av g", + "Ġinv ited", + "ĠPe ak", + "ĠEnter obacter", + "Ġaneurys m", + "ĠMor ris", + "ti da", + "ĠS ER", + "ĠMic hel", + "ĠI BD", + "ĠN G", + "Ġscar ce", + "we b", + "Ġexpress es", + "Ġsc hema", + "Ġless ons", + "Ġarg inine", + "Ġphot ographs", + "ĠNe urons", + "ĠATP ase", + "Ġf iller", + "rap ped", + "Ġrandom ness", + "Ġve ins", + "Ġwound s", + "ĠA part", + "Ġr acial", + "Ġnot eworthy", + "Ġremov es", + "Ġgangl ion", + "Ġlamin ar", + "ĠS SR", + "Ġpol ysaccharides", + "Ġbu f", + "Ġendot helium", + "ĠC AS", + "ĠGol gi", + "Ġinher itance", + "is ite", + "CO MP", + "Ġp t", + "Ġmes hes", + "Ġtherap eutics", + "O ST", + "olin ergic", + "U G", + "squ ared", + "Ġdeg rade", + "u um", + "Ġret rosp", + "L oc", + "ĠJ NK", + "O ptions", + "Ġins ulating", + "Ġspec ifies", + "Ġo ven", + "y y", + "ĠCon ver", + "Ġdisapp eared", + "ĠProgn ostic", + "ĠNgu yen", + "Ġperipher y", + "b ank", + "Ġim id", + "Ġassign ing", + "ĠM ess", + "prop an", + "i oma", + "oly b", + "Ġepit ope", + "Ġemit ting", + "D IR", + "yn c", + "Ġimpair ments", + "ĠMic roscopy", + "ĠFW HM", + "g ray", + "Ġf ing", + "uc ial", + "plement ed", + "e as", + "est amp", + "Ġcre st", + "ĠM os", + "Ġneut rons", + "Ġbro th", + "Ġhead ache", + "onge vity", + "Ġre ass", + "ĠP SF", + "ĠB uch", + "vis or", + "Ġden oting", + "in teger", + "ou in", + "eff icacy", + "Ġglut amine", + "Ġpick ed", + "ĠCamp bell", + "ĠK ernel", + "Ġsh ips", + "l t", + "ond yl", + "Ġcre di", + "Ġpepti d", + "ĠEst abl", + "b ons", + "Ġag gl", + "US E", + "sup p", + "ups ilon", + "character ized", + "ishe ries", + "M ay", + "AR C", + "Ġro ads", + "Ġdepar ture", + "ĠMA X", + "ĠT RA", + "im od", + "ĠAl ber", + "Ġterm inated", + "öl der", + "S calar", + "h ash", + "ĠM SS", + "Ġsmooth ness", + "Ġres emble", + "ĠEff ectiveness", + "r x", + "ĠE ye", + "Ġfa ecal", + "à ¾", + "ĠClostr idium", + "ach ine", + "ĠBD NF", + "Ġc ab", + "ĠW ong", + "ĠDoug las", + "Ġre perfusion", + "ĠX i", + "Ġconf used", + "ĠPhil adelphia", + "Ġap ple", + "Ġi le", + "Ġfav ored", + "Ġpl aques", + "Ġtri vially", + "ĠTyp ical", + "Ġcentral ized", + "ĠFace book", + "Ġnorthe ast", + "Ġnorm ality", + "ĠT b", + "Ġap t", + "Ġfac et", + "ĠRen al", + "cl k", + "Ġlig ation", + "iff erenti", + "Ġput ting", + "Ġintr ig", + "w alled", + "E t", + "ĠC ow", + "ĠN ations", + "Ġcamp us", + "ĠKine tics", + "ĠMex ican", + "ER K", + "Ġlat itudes", + "ĠR oll", + "ĠQ D", + "adap tive", + "Ġquenc hed", + "Ġf ram", + "Q i", + "Ġt ongue", + "ed es", + "Ġasc orb", + "ĠGluc ose", + "our i", + "Ġdef eated", + "ophil us", + "ral ateral", + "x rightarrow", + "ĠJ up", + "ax es", + "eg er", + "MI T", + "ĠM ember", + "ĠN u", + "Ġtransl oc", + "ĠFlu x", + "ĠColor ado", + "Ġre lying", + "at rol", + "Ġcontras ts", + "cent age", + "Ġleuk ocyte", + "Ġcoinc idence", + "Ġcontrac tions", + "og a", + "AN N", + "ĠAbs orption", + "Ret urn", + "rep rene", + "ba um", + "tra umatic", + "inc ial", + "Ġaut ophag", + "Ġalgorithm ic", + "rim p", + "Ġdiv ides", + "ĠR ose", + "ĠE ric", + "Ġadd iction", + "pl ification", + "Ġdiff usive", + "ĠVehic le", + "en erate", + "ti sing", + "Ġstar vation", + "abs orption", + "ĠA ra", + "Ġgra v", + "ĠSub unit", + "Ġam ide", + "Ġenh ancer", + "Ġmer id", + "erm ost", + "Ġal gal", + "ĠQue ens", + "ĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ ĠĠĠ", + "Ġjud ge", + "ĠGreen land", + "b race", + "ĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ ĠĠĠĠĠĠĠĠĠĠĠĠĠ", + "Ġhyper gly", + "Ġemerg ent", + "F isher", + "ĠL as", + "Ġsex es", + "S ep", + "Ġph rases", + "ĠSequ ential", + "ink i", + "Ġaxi oms", + "stud y", + "Ġt iny", + "Ġc d", + "cataly zed", + "as aki", + "ĠW R", + "ĠMin imal", + "Ġsub cellular", + "Ġphosph o", + "ES I", + "Ġv ow", + "Ġsup plies", + "oper and", + "F ix", + "an ian", + "wr iter", + "âĪ ¶", + "Ġwin ner", + "ĠP ID", + "ĠLe besgue", + "Ġsimpl ification", + "ĠRelationship s", + "Ġautom ata", + "ĠCont ribution", + "Ġhered itary", + "err in", + "ĠB LAST", + "ae a", + "yle th", + "ĠT c", + "ade h", + "adj uvant", + "W ave", + "c ounter", + "ĠG upta", + "ĠG hana", + "C ho", + "Ġour selves", + "Ġeven ly", + "lym ph", + "Ġcereb ellum", + "Ġcopol ymers", + "mod ular", + "Ġhard er", + "Ġp lease", + "ĠP SD", + "Ġlim bs", + "Ġexplo itation", + "ir y", + "Ġperiodon tal", + "AT CH", + "Ġmal icious", + "ĠSl ov", + "H Y", + "Consequ ently", + "ore n", + "ĠP are", + "ag ine", + "ĠRO I", + "ĠWh ich", + "ĠN ative", + "am en", + "resh ape", + "opl ankton", + "Ġartif act", + "Ġrh in", + "g pu", + "Ġund et", + "Ġspor adic", + "Ġor ally", + "Ġstep wise", + "ĠCoh ort", + "Ġr hod", + "c yt", + "Ġi err", + "Ġmot ors", + "ĠIg E", + "calc ulated", + "ĠChampionship s", + "p el", + "ĠF err", + "Ġis ometric", + "n utrition", + "Ġuns aturated", + "Ġd oll", + "ĠR MSE", + "Ġsol itary", + "approxim ation", + "Ġreper to", + "s ight", + "Ġc ranial", + "il ical", + "ĠO st", + "o ul", + "Ġd g", + "ĠPro ceed", + "Ġmill ing", + "s z", + "Ġmineral ization", + "Ġcig arette", + "Ġp orph", + "Ġsp ons", + "ĠGre ece", + "ip ore", + "ac cept", + "ĠPT SD", + "Å «", + "Ġc ipher", + "Ġfunctional ized", + "P oly", + "Ġab d", + "fl ight", + "ĠSyd ney", + "Ġdis aster", + "ĠH aving", + "Ġdies el", + "ĠG reg", + "Ġsp ans", + "ĠSe asonal", + "ST EM", + "i err", + "ĠI B", + "Ġle mm", + "an um", + "ĠB ottom", + "Ġse al", + "bo ost", + "Ġleg end", + "b ing", + "ab is", + "Ġch itin", + "Ġmaxim ally", + "Ġimmunosup pressive", + "âĪĴ âĪĴ", + "Ġabol ished", + "ig e", + "Ġes ophag", + "Ġlas ted", + "Ġcarbohyd rates", + "Ġch ips", + "ĠFern and", + "f ar", + "ĠPo ints", + "cal ation", + "ĠReg ions", + "CH K", + "ver atrol", + "tr uth", + "Ġst range", + "Int erest", + "s ho", + "ĠInd uc", + "Ġmig raine", + "ĠV ac", + "op hores", + "Ġerr one", + "scripts ize", + "ĠNeut ron", + "Ġindist inguishable", + "ist ine", + "Ġhel per", + "spec ified", + "Ġju ice", + "ox al", + "ĠJ ung", + "Ġmag azine", + "Ġtele phone", + "ĠPh yt", + "Ġ um", + "ĠAvail ability", + "ĠT ropical", + "ĠC ases", + "Ġdesc end", + "H ar", + "âĪ Ĺ", + "ĠâĨ ĵ", + "K s", + "Ġ ê", + "ol uble", + "Ġch ampionship", + "ĠMove ment", + "ĠX Y", + "kappa B", + "year s", + "m emb", + "qu ine", + "Ġlet ting", + "Ġbig gest", + "Ġc ards", + "Ġbi otin", + "ĠA ur", + "mod al", + "Ġvacc inated", + "Ġtransl ates", + "ĠP AC", + "ll i", + "re onine", + "Ġcur cumin", + "ĠCon struct", + "Ġconv inc", + "ĠN at", + "Ġam ygdala", + "Ġprot r", + "ĠSing ular", + "ĠCont act", + "k ind", + "ĠD aw", + "og roup", + "ĠK Cl", + "Ġhy gi", + "eren ced", + "Ġsurvey ed", + "ĠM ull", + "est hetic", + "Ġms g", + "ĠRe quire", + "Ġdistor tions", + "Cont rol", + "B ERT", + "Ġaut onomic", + "Ġhorm onal", + "Ġstri ps", + "Ġt rophic", + "if ting", + "op od", + "ĠSp ontaneous", + "Ġlog s", + "O PT", + "ĠM ot", + "ĠG mb", + "ah aran", + "ĠP OL", + "Ġvis ceral", + "bl ocks", + "Ġsit ting", + "Ġs ine", + "Ġonc ogenic", + "ERR Q", + "quin one", + "Ġsmart phone", + "ĠTan z", + "lact am", + "ĠSignific ance", + "Ġe u", + "ĠI SS", + "ĠTr ig", + "ĠM aj", + "ting ale", + "Ġdil at", + "enn es", + "ĠBelg ium", + "le v", + "ĠCon tr", + "ĠGal ois", + "ĠComb ination", + "ĠTh i", + "ĠAust ria", + "P rom", + "Ġelic it", + "bi osis", + "Ġlymph atic", + "ĠMur ray", + "ĠX PS", + "Ġcon g", + "sc reen", + "ti de", + "am oyl", + "ĠMc D", + "Ġreti red", + "m ixed", + "EL D", + "ĠM aps", + "ĠV E", + "cess ion", + "num er", + "id ated", + "ĠB ishop", + "Ġneon ates", + "Ġlands l", + "ĠFrac tional", + "Ġspec ifying", + "ĠJ r", + "Ġnanow ire", + "Ġconsult ation", + "l anguage", + "Ġp ricing", + "ĠLimit ations", + "ĠP ediatric", + "ĠD imension", + "Ġprepar ing", + "L ag", + "seg ment", + "Ġsp end", + "at he", + "Ġwe ap", + "ĠJ os", + "tex tit", + "output s", + "ord ering", + "Ġplac enta", + "ation ally", + "ĠK un", + "Ġout standing", + "Ġthickness es", + "ĠCh IP", + "de oxy", + "ĠZ o", + "ĠDevelop ing", + "Ġstring ent", + "i ency", + "per se", + "Ġp end", + "ĠDevelopment al", + "Ġex tern", + "Ġinver ter", + "ĠD API", + "lec tivity", + "Ġtable ts", + "Ġprog ester", + "Ġïģ Ń", + "Ġansw ered", + "ent ary", + "OR S", + "Ġd ir", + "Ġdele terious", + "Ġdop aminergic", + "R andom", + "dis s", + "Ġmonol ayers", + "Ġinteg rand", + "ĠComp onents", + "ĠP erc", + "ag it", + "AR N", + "es ophageal", + "iv an", + "ne ider", + "ĠStar ting", + "P ORT", + "y ellow", + "Ġreg isters", + "pair s", + "Ġethn icity", + "Ġb oy", + "au ti", + "Ġchrom ium", + "P OS", + "v ature", + "ay ashi", + "Ġin appropriate", + "ĠS NA", + "D omain", + "ĠP rice", + "Ġmac ular", + "Ġover load", + "ĠUn ified", + "Ġatt ach", + "ĠScot tish", + "m aps", + "ag l", + "em i", + "Ġse am", + "ĠAnal og", + "d ated", + "u o", + "Ġpl ated", + "Ġass et", + "Ġsc reens", + "Ġspur ious", + "B esides", + "Ġbas elines", + "head s", + "Ġco at", + "ĠRem oval", + "Ġinfinites imal", + "ĠTrans formation", + "Ġcomm ens", + "Flo at", + "A UC", + "ĠL ay", + "Ġint ron", + "ĠDet ecting", + "ĠHere in", + "ĠAssoci ations", + "Ġprogester one", + "B acteria", + "Ġs entiment", + "ĠPhen omen", + "m atter", + "Ġcylind ers", + "Ġtolu ene", + "Ġspati otemporal", + "Ġland ing", + "ĠCoron avirus", + "ĠBer ry", + "ĠB ragg", + "Ġreg istry", + "Ġenthal py", + "tic a", + "raz ine", + "Ġc argo", + "ot ation", + "Ġcontrad icts", + "Ġpestic ides", + "ĠF ischer", + "Ġmechan ically", + "ĠInter fer", + "ĠC yp", + "ĠK as", + "Ġmet res", + "Ġanti retroviral", + "Ġtra vers", + "se lection", + "ĠW A", + "Ġdouble t", + "m eta", + "EN TR", + "son ic", + "Ġmark ing", + "ĠO verex", + "Ġpy ruvate", + "Ġextr usion", + "Ġin gestion", + "Ġcoc aine", + "ĠF ellow", + "CN Ts", + "B G", + "ĠMorph ological", + "Ġdef ence", + "ĠY osh", + "mit ter", + "rystall ization", + "STR ACT", + "Ġinflamm asome", + "ĠG d", + "Ġsh aft", + "Ġerup tion", + "ĠOx ide", + "if olds", + "ĠG am", + "ĠG ap", + "com mand", + "ĠIg A", + "Ġshorten ing", + "assemb led", + "is opropyl", + "Ġal umina", + "ĠAT M", + "Ġc t", + "Ġspin ning", + "ĠPet sc", + "pref ix", + "Ġperpet uity", + "P RE", + "Ġfr uct", + "G Hz", + "el ike", + "en yl", + "Ġwhere in", + "U K", + "vis ual", + "lipid emia", + "re duction", + "an in", + "ol as", + "Ġam plic", + "ĠS AT", + "Ġmod ulator", + "for th", + "r l", + "Ġcre w", + "Ġi P", + "Ġx i", + "AD D", + "ĠAlex and", + "const rained", + "r atory", + "Ġk W", + "ĠMD R", + "Ġlnc RNA", + "M ill", + "ĠMg O", + "circ uit", + "Ġpersonal ized", + "ĠOper ator", + "st ock", + "ĠP SA", + "ens able", + "Ġle an", + "y ield", + "Ġop acity", + "ĠComm ons", + "Ġsum med", + "uck er", + "ec ke", + "ep ithelial", + "Ġas king", + "ues e", + "ĠFl av", + "Ġl actic", + "Ġl ubric", + "Ġis n", + "reg ions", + "sup port", + "Bel ow", + "ĠN om", + "Ġhy al", + "ik h", + "b an", + "ĠB G", + "rom eter", + "ind ic", + "oph aryngeal", + "IT ION", + "ĠProp agation", + "ĠPl ace", + "ĠCirc uit", + "ĠCO L", + "G reen", + "I r", + "l av", + "Ġd S", + "ĠM oment", + "Ġinduc ible", + "Ġdischarg es", + "hab di", + "ĠExper ience", + "Ġs g", + "Ġout ward", + "Ġport able", + "ĠOper ators", + "A v", + "ĠD Q", + "ost atin", + "Ġeosin ophil", + "Ġstri atum", + "ĠCons ensus", + "Ġim perfect", + "NO T", + "ĠDem ocratic", + "; ;", + "B ody", + "di i", + "H o", + "ĠRail way", + "ĠUg anda", + "Ġunp aired", + "friend ly", + "Ġrepro gramming", + "Altern ative", + "R G", + "im et", + "ene z", + "ĠHyp othesis", + "Ġt on", + "ĠCom bin", + "ĠDel ivery", + "L ast", + "Ġown ers", + "raz ole", + "ĠK ob", + "Ġform ats", + "Ġpoly clonal", + "Ġidentif ier", + "IL L", + "Ġsurge on", + "Ġpost p", + "ĠGener ative", + "ĠM all", + "ab c", + "ĠH az", + "Ġsmooth ly", + "Ġcrystall ographic", + "ĠF DA", + "Ġcoex istence", + "ion ized", + "Ġcomp iler", + "ĠAr ter", + "Ġappear ances", + "amilton ian", + "Ġencaps ulated", + "ati a", + "w i", + "re b", + "Ġwa fer", + "ub s", + "ĠU E", + "ĠGS K", + "Ġv iv", + "Ġflood ing", + "ĠG yr", + "Ġst ably", + "Ġdis locations", + "Ġes cap", + "ĠPhys iological", + "tid al", + "ym e", + "ĠMax im", + "iter ator", + "ord ant", + "Ġatten tional", + "Ġcataly zed", + "ĠTr yp", + "P IN", + "ĠCor relations", + "Ġhyd rological", + "Ġn ose", + "ex port", + "Ġde xt", + "ĠBen ef", + "ĠBios ystems", + "ĠP ars", + "Ġread ings", + "Ġinstrument ation", + "ĠI Q", + "R IC", + "Ġgra fts", + "over s", + "ĠMed ic", + "Ġmon od", + "Ġuniform ity", + "ĠAT LAS", + "Ġmask ed", + "R i", + "ĠPhys ic", + "Ġim posing", + "ĠPar ad", + "ime tic", + "Ġdemand ing", + "un ks", + "Ġfol ds", + "ĠAn c", + "Ġvol atility", + "Ġbring ing", + "ac il", + "ĠN MDA", + "re duced", + "ti i", + "Ġnorth west", + "ĠB essel", + "ven tions", + "Ġconsol idation", + "Me ier", + "Ġmicro f", + "Ġqual ified", + "Ġins ignificant", + "ĠMorph ology", + "Ġpoint wise", + "Ġlear ns", + "Ġgu ard", + "CH ECK", + "phon on", + "ĠEnhance ment", + "Ġz onal", + "ER G", + "St art", + "Ġhistor ic", + "ĠP ure", + "ĠGmb H", + "g lu", + "Ġpattern ing", + "Ġstic k", + "umin osity", + "D ataset", + "Ġover ride", + "ĠSte el", + "Ġfu els", + "m echanical", + "Ġaut ologous", + "Ġdepart ments", + "ĠB lo", + "Ġim ported", + "Ġrestric tive", + "e igen", + "ĠR ome", + "ĠÌ Ĭ", + "Ġepit opes", + "Ġlab elling", + "Ġown ership", + "ĠE specially", + "Ġco ffee", + "ĠGR B", + "H ead", + "ĠV ent", + "es are", + "ĠPar ticles", + "UN CTION", + "j j", + "u ents", + "el ic", + "ĠT at", + "ĠF le", + "Ġg ating", + "Ġref uge", + "Ad ditionally", + "Ġrh s", + "Ġmay be", + "ĠF ang", + "Ġad vent", + "otransfer ase", + "sh ould", + "Ġprote omic", + "Ġleg itim", + "PER IM", + "ĠG iant", + "Ġgraph ics", + "onom ical", + "sc atter", + "Ġsugges tive", + "pl ots", + "Ġmulti drug", + "Ġabsor ber", + "X S", + "cons uming", + "Ġsustain ability", + "op re", + "f ix", + "Ġvol cano", + "ĠTyp es", + "ĠCre ate", + "Ġcho oses", + "Ġstir ring", + "Ġsurge ons", + "d S", + "Ġcharacter izes", + "Ġadjust ments", + "text tt", + "et ra", + "Ġclass ifications", + "sp ots", + "ĠâĻ ¯", + "ere x", + "de hyd", + "ĠBr ig", + "ĠSuper conduc", + "Ġgran ts", + "ĠC en", + "ĠY in", + "ĠRe actions", + "des cription", + "trans cription", + "import ant", + "Ġhemod ynamic", + "ĠY i", + "ĠGold en", + "k k", + "al b", + "Ġro oms", + "Ġseg reg", + "Ġsumm ing", + "Ġsuccess ion", + "Ġfollic ular", + "Ġtack le", + "D own", + "Ġevalu ates", + "atic a", + "ann ual", + "ĠAl bert", + "Ġt al", + "orb ital", + "f ted", + "vari ables", + "Ġwet land", + "outhe astern", + "M EM", + "ĠBr ill", + "ĠS odium", + "ĠAlex a", + "um ed", + "BU G", + "ar ine", + "Ġre venue", + "habdi tis", + "Ġdiss ol", + "am plitude", + "Ġar tists", + "Ġnormal ised", + "Ġfluct uating", + "Ġas par", + "ĠF i", + "ol ates", + "isp anic", + "Ġacet ylation", + "ĠConcent ration", + "Ġth ro", + "sh ots", + "Ġnarr ative", + "ĠWa als", + "am monium", + "ure au", + "-------- ----", + "Ġresearc hes", + "Ġbab y", + "Ġshar ply", + "Ù Ħ", + "ĠC el", + "C X", + "um inal", + "Ġgerm line", + "ĠTransform er", + "p seud", + "H G", + "K a", + "ĠS MC", + "ĠN utrition", + "Ġb arc", + "ĠW rite", + "Ġprote ases", + "Ġswe ep", + "ĠKol mogorov", + "m orph", + "in ducible", + "Ġexc iting", + "le in", + "ĠH ass", + "Ġproduc tive", + "mes h", + "ĠC MS", + "Ġhe avier", + "Ġmeet ings", + "ĠCop per", + "Ġvirt ue", + "as ant", + "ĠD EN", + "Ġinherent ly", + "ri o", + "Ġhous ed", + "Ġintra operative", + "Ġc rown", + "con ditions", + "AN G", + "YS IS", + "im an", + "Ġnm ol", + "ĠRetrie val", + "al gae", + "Ġk appa", + "de ep", + "in ence", + "ĠC arcinoma", + "Ġchromat ographic", + "Ġas cribed", + "Ġle verage", + "ĠK K", + "omy el", + "p et", + "ĠN J", + "com m", + "Ġann ually", + "g ran", + "Ġa val", + "ĠN ish", + "Ġev ac", + "Ġmulti f", + "Ġfund s", + "enn y", + "ĠM ong", + "ĠEx ception", + "path s", + "ym en", + "h pp", + "Ġrestric ting", + "s aturated", + "â Ļ", + "Ġlear ners", + "ĠLank a", + "in ities", + "ĠG DP", + "Ġspec iation", + "Ġens ured", + "Ġneutral izing", + "Ġball oon", + "Compar ison", + "ĠCal ibration", + "ĠInflu enza", + "Ġvap our", + "X A", + "t racking", + "ĠI CD", + "fluor o", + "ĠDam age", + "Ġp ra", + "Ġcon ceived", + "ĠCosm ological", + "Ġlo ose", + "inos itol", + "ĠCliff ord", + "ow a", + "Ġoffset s", + "doc ument", + "Ġenorm ous", + "Ġphoto electron", + "rec ord", + "estic ular", + "Ġvoc als", + "Ġconscious ness", + "Ġtre m", + "Ġlandsc apes", + "ĠFund amental", + "teb rate", + "Ġverteb ral", + "Ġregener ative", + "Ġtro posp", + "In tegr", + "Ġassoci ates", + "ov ed", + "uss ed", + "aw s", + "ĠS ide", + "Ġinter connected", + "Ġsuper family", + "ĠCo ok", + "load er", + "Ġpy thon", + "ĠC ounter", + "bo oks", + "Ġïģ ²", + "bre aking", + "g y", + "Ġcar box", + "Ġed ited", + "otyp ed", + "Ġdu oden", + "an ne", + "Ġan astom", + "gin ate", + "ĠBios ciences", + "ra ge", + "ĠCh iral", + "Ġsimpl ifies", + "Ġtes tis", + "str öm", + "ial s", + "Ġmic elles", + "cor rect", + "ĠGene tics", + "al ong", + "R em", + "res istance", + "Ġdr ink", + "orb ed", + "ĠT reat", + "ĠS ho", + "sh ows", + "é r", + "Ġmim ics", + "occ up", + "ec lam", + "ON G", + "Ġmark eting", + "ĠF inding", + "Ġendomet ri", + "âĶ Ģ", + "st rained", + "ĠM uch", + "Ġex ons", + "ĠH il", + "T D", + "ĠW W", + "ĠV ic", + "end a", + "Ġfact ory", + "ĠHep G", + "ĠSt atic", + "blast oma", + "w d", + "ra isal", + "ĠB asis", + "In s", + "ĠUn supervised", + "el o", + "ose lective", + "Ġaccompl ish", + "ĠP rospective", + "Ġuncor related", + "ĠG ate", + "icy cl", + "Ġur gent", + "ĠPath ways", + "Ġobl ique", + "ĠIndividual s", + "Ġiniti ative", + "Ġcat ast", + "j ections", + "Ġaut osomal", + "ĠPhil ip", + "Ġcomprehens ion", + "m M", + "p ain", + "Ġmicro M", + "Ġenc ounters", + "g oto", + "Ġl adder", + "Ġoccup y", + "ĠSur faces", + "D oc", + "ug by", + "Ġexam ines", + "os ynthesis", + "ĠK EGG", + "gl ass", + "sl ice", + "prop agation", + "str y", + "Ġillustr ating", + "im i", + "Ġsp ores", + "Ġast rophysical", + "Ġen closed", + "Ġinf erences", + "Ġbi jection", + "Ġever yday", + "Ġaltern atively", + "re action", + "ian ts", + "cont act", + "Ġg ing", + "ĠBi as", + "Ġautom aton", + "back ground", + "Ġneighbour ing", + "Ġdet ects", + "por ate", + "ĠShar ma", + "H ydro", + "Ġs acc", + "ĠF iber", + "ĠCh lam", + "Ġbuff ers", + "App lying", + "l ceil", + "em ph", + "ĠG SE", + "met ry", + "Ġimmun ost", + "ĠHistor ical", + "ĠD rag", + "Ġtransplant ed", + "Ġf rail", + "Ġanth ocyan", + "in te", + "ĠB hat", + "ĠO g", + "Ġste ering", + "benz ene", + "******************************** ********************************", + "Ġsynt het", + "A ct", + "Ġc in", + "Ġher bal", + "Ġd yn", + "Ġhyper plasia", + "head er", + "Ġcalc ulates", + "ĠDiff erence", + "Ġb ats", + "duc tivity", + "Ġconform ations", + "c ity", + "Ġsepar ates", + "ĠCD C", + "ĠPr ism", + "ĠBehavi our", + "ĠKel ly", + "ĠS ey", + "Ġà ł", + "LE X", + "g kin", + "st rom", + "Ġv om", + "ĠW in", + "ĠW igner", + "Ġcont ralateral", + "ĠMin or", + "Ġstere o", + "ĠApproxim ately", + "L ED", + "s ay", + "ĠJ S", + "Ġalcoh ols", + "Ġs an", + "Ġhard ening", + "IF N", + "Ġretrosp ectively", + "Ġgeneral ised", + "Ġtib ial", + "ĠWe ek", + "Ġar yl", + "ĠPen insula", + "Ġdeterm inations", + "Ġphot ovoltaic", + "Ġsugges tion", + "J ac", + "ĠV itro", + "Ġcycl o", + "Ġfibro us", + "dis ambiguation", + "pro gram", + "Ġgu est", + "ĠD ust", + "r ceil", + "Ġpow ered", + "Ġcardiomy ocytes", + "he at", + "yl ic", + "Ġpresent ations", + "Ġtransmit ting", + "W D", + "add ed", + "In itial", + "D el", + "ĠV elocity", + "Ġmo le", + "Ġo val", + "Ġpl ankton", + "the ir", + "ĠQ ED", + "vol utions", + "Ġmand atory", + "Ġrep ulsive", + "ĉ ĠĠ", + "Ġpost ulated", + "ĠCor tex", + "ĠCar b", + "CHK ERRQ", + "Ġoverl ay", + "ĠF arm", + "enor habditis", + "Ġpos ed", + "Ġinst anti", + "Z T", + "ĠVisual ization", + "ĠGAP DH", + "lec om", + "och ron", + "ĠB j", + "ĠT rib", + "Ġby te", + "Ġsuperim posed", + "Ġund i", + "Ġacceler ator", + "cri ptions", + "ĠSm ooth", + "Ġz ip", + "nes ota", + "ĠE FF", + "ĠC ole", + "ĠB ru", + "re nd", + "ut z", + "Ġdiagn ose", + "b asis", + "di amond", + "ĠIn n", + "ĠMed ian", + "Ġmarg inally", + "Ġlemm as", + "rect omy", + "Ġdial ogue", + "ĠB rid", + "Ġ å", + "ox ane", + "ar is", + "Ġmunicip ality", + "Ġproduc ers", + "Reg arding", + "ĠF V", + "ide al", + "exp onential", + "L abel", + "ĠF robenius", + "Ġe ll", + "ĠL TE", + "Ġlip ase", + "r p", + "Ġd m", + "ot ri", + "cl oud", + "ĠAg ent", + "M SCs", + "os om", + "hyd ropy", + "ne urons", + "Ġsol vable", + "duc ting", + "Ġrend ered", + "Ġattract or", + "Ġb rac", + "à ģ", + "Ġhost ed", + "ĠO ct", + "Ġgu iding", + "Ġdiges tive", + "j s", + "Ġint ent", + "flu x", + "Ġbios ynthetic", + "Ġe lections", + "ĠWil coxon", + "Ġspectrophot ometer", + "Ġimpair s", + "Ġabd omen", + "k b", + "ĠW ho", + "ASS ERT", + "Ġel uted", + "Ġmaxim ization", + "Ġcollect or", + "ĠPrevious ly", + "a q", + "am bo", + "ĠO z", + "C ur", + "Ġcaffe ine", + "M ass", + "p al", + "pi ece", + "ou ville", + "ĠM eyer", + "ut a", + "ch an", + "ĠK S", + "om otor", + "ĠG PR", + "Ġev al", + "ĠCo operative", + "ogly can", + "Ġnozz le", + "ĠS hel", + "Ġinter change", + "Ġunderg rad", + "Ġexplan atory", + "Ġphag ocytosis", + "Ġc tx", + "hes s", + "Ġunivers ality", + "ĠK illing", + "ons in", + "Ġlast ing", + "ĠIm m", + "Ġconc ordance", + "y ma", + "Ġaut umn", + "Ġbar ley", + "Ġconsequ ent", + "is i", + "Ġconjug ates", + "Ġta ught", + "Ġcovari ate", + "Ġadoles cence", + "Ġvill ages", + "Ġeigen functions", + "Ġtempor ally", + "ĠMin nesota", + "y rate", + "ies is", + "def inite", + "Ġalph abet", + "ĠY un", + "ĠM AR", + "Ġse aled", + "ron ectin", + "ĠSep ar", + "n x", + "CA A", + "Ġrece ption", + "uck y", + "ĠPT EN", + "ĠM organ", + "Ġdi odes", + "Ġmet formin", + "Ġsynt hes", + "ĠPar ticip", + "ĠJer sey", + "Ġamph ib", + "c hel", + "Ġl amp", + "ĠH els", + "ĠF N", + "Ġexc av", + "is econd", + "int ro", + "Ġnon commutative", + "Ġsubs ystems", + "sum m", + "Ġcontrast ing", + "ĠSil icon", + "ĠPar tition", + "Glc NAc", + "Ġdisc ern", + "ĠBound s", + "ĠR ah", + "Ġapproxim ating", + "ĠHyper t", + "ĠD il", + "Ġcompact ness", + "Ġca ught", + "ĠImpro ve", + "ĠTor onto", + "ĠBiom ark", + "ĠB ag", + "ĠIn vent", + "Ġelabor ate", + "ĠM ott", + "AB C", + "ĠGra ham", + "Ġpo ultry", + "ĠCon jecture", + "ĠAl gebras", + "ĠN LO", + "ap sing", + "path y", + "ĠEliz abeth", + "ĠT it", + "ĠS CI", + "ant on", + "Ġv oting", + "math rel", + "ĠF ord", + "ig ibility", + "Ġall ergy", + "ac oustic", + "ĠD yn", + "ĠD SC", + "ĠG RO", + "ĠTh irty", + "Ġanalys ing", + "ĠEm pire", + "f ire", + "Ġpath ologic", + "Ġpat ent", + "Ġhe ard", + "ĠF ront", + "isc onsin", + "hy pert", + "uz umab", + "ĠMut ation", + "Ġb iliary", + "Ġsuper fluid", + "ĠW C", + "ust om", + "ĠAc tivities", + "Ġpolyp eptide", + "he ets", + "Ġb orders", + "ear ly", + "Ġorth ogon", + "Ġbul ge", + "ï £", + "Ġcon ical", + "ĠL ept", + "Ġelectroly tes", + "Ġ «", + "reg ulating", + "Ġviol ated", + "â ĺ", + "AL T", + "ĠWork s", + "ĠHep at", + "ur gical", + "ob ar", + "ĠRe active", + "poss ibly", + "ĠAds orption", + "ĠR io", + "ano ic", + "ĠâĨ ij", + "Ġintrig uing", + "Ġo m", + "her tz", + "ĠApproxim ate", + "ĠP arent", + "Ġco in", + "exp and", + "Ð ²", + "Ġnon parametric", + "ex tern", + "ae us", + "gly cerol", + "Ġc p", + "Ġbat ches", + "Ġnanom aterials", + "U se", + "ĠV ivo", + "R h", + "Ġt iles", + "Ġdep ict", + "Ġsouth west", + "ĠCas imir", + "lay ered", + "ĠLe af", + "f em", + "b ered", + "Ġsub algebra", + "Ġdet achment", + "ĠLe uk", + "ol us", + "ĠR ick", + "Ġab ortion", + "Ġclar ified", + "Ġgangl ia", + "Q S", + "o ising", + "ĠFor ward", + "ĠPer ipheral", + "shif ted", + "b ula", + "ram olecular", + "ĠF EM", + "ĠPro ton", + "AM E", + "Ġsched ules", + "Ġa a", + "ĠU DP", + "st ere", + "Ġmorph ine", + "Ġspecial ist", + "ĠAnd roid", + "Id entif", + "Ġun expl", + "Ġheter ozyg", + "Ġf id", + "pyrid yl", + "ĠW y", + "phosph or", + "Ġfriend ly", + "Ġmic rol", + "ĠS plit", + "agn er", + "crib e", + "Ġm oth", + "ĠEu ro", + "ig s", + "ĠCon ditional", + "ĠSte wart", + "pro perties", + "AS C", + "ĠTra ditional", + "ĠPortug al", + "Ġear ned", + "Ġcat he", + "Cre ate", + "ici encies", + "Ġsph ing", + "x ml", + "Ġimmun omod", + "Ġcomm ute", + "Ġselen ium", + "ang es", + "ho ok", + "den oted", + "Ġjus tify", + "ĠP ool", + "Ġgu inea", + "Ġcont ra", + "Ġfol ded", + "Ġlist ing", + "ĠL G", + "ĠL ane", + "Ġsure ly", + "v et", + "fluor ophenyl", + "Ġcoron a", + "ĠAb und", + "ĠOb jects", + "Ġt rough", + "ch t", + "Ġdis h", + "ith i", + "ĠMat lab", + "w orm", + "Ġprote omics", + "Ġinter molecular", + "ĠPet ers", + "Ġmir rors", + "quin oline", + "art ens", + "ĠJew ish", + "k B", + "ĠD egradation", + "Ġrele asing", + "V EGF", + "Ġsub populations", + "ĠTra ffic", + "Ġpro line", + "ĠH f", + "Ġad ren", + "b irth", + "Ġs ender", + "Ġat las", + "Ġwork place", + "Ġreflec tivity", + "ĠEx istence", + "cl s", + "Ġfin er", + "Ġbreast feeding", + "on ectin", + "Ġc ogn", + "ell ate", + "ĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ ĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ", + "by te", + "Ġsk et", + "N ULL", + "s ystems", + "ĠB ranch", + "ĠPro posed", + "lear n", + "Ġtoler ant", + "Ġver tebrates", + "Ġmulti level", + "ĠPA H", + "Ġaud ience", + "ĠW L", + "nit rop", + "ĠC t", + "Ġsati va", + "e ight", + "Ġme g", + "oc ell", + "Ġst ating", + "dom inant", + "b ytes", + "Ġp u", + "ĠB atter", + "ot axis", + "ĠE BV", + "Ġnanoc rystals", + "Ġmonop ole", + "Ġdia phrag", + "ĠV el", + "Ap pendix", + "at ten", + "im pl", + "Ġland mark", + "encl ature", + "ĠST AR", + "Ġprost agland", + "oprot ective", + "Ġload ings", + "ĠPres ence", + "ĠN SF", + "ress es", + "F U", + "il ers", + "Ġeryth rocytes", + "t rac", + "is lation", + "ĠN ight", + "Ġster oids", + "ti z", + "ĠD MA", + "Ġr ic", + "Ġsal ient", + "ĠF ur", + "spec ial", + "Ġbio informatics", + "ign ant", + "ĠEX PERIM", + "avor able", + "dis k", + "Ġcur riculum", + "imid azol", + "hig her", + "Ġdesign er", + "ĠSt rength", + "Ġcytos ol", + "ĠCh annels", + "L and", + "s par", + "Ex pression", + "Ġday time", + "merc ial", + "v box", + "in ar", + "ie ving", + "ce in", + "ĠNC BI", + "R AN", + "¸ Ģ", + "H ig", + "ĠD HA", + "Ġsub script", + "Ġ ¢", + "or ange", + "Ġknow s", + "ĠN AF", + "pro duced", + "ep id", + "Ġdex amethasone", + "Ġformal dehyde", + "yl l", + "Ġec topic", + "ĠVer ification", + "activ ating", + "ĠI G", + "ĠP av", + "Ġtra ding", + "Ġgrad uate", + "ĠF IR", + "enc il", + "ever y", + "Ġradi ological", + "ĠMamm alian", + "M ES", + "in ium", + "ĠS AS", + "ĠW H", + "Over ride", + "ĠSched uling", + "ĠB es", + "ĠY ao", + "Ġgl ad", + "ĠStandard s", + "Ġprov inces", + "en ers", + "Ġn r", + "Ġtrans pos", + "ĠCar ib", + "Ġfa una", + "um i", + "res et", + "Ġsup ra", + "Ġdiv isions", + "Ġbiod egrad", + "metric s", + "og rafts", + "Ġfunc tors", + "Ġsup portive", + "Ġcaud al", + "Ġexer ts", + "Ġc ub", + "od imer", + "Ġair borne", + "Ġdeliver ing", + "Ġmultiv ariable", + "Ġfurn ace", + "Ġremn ant", + "Ġinc o", + "ĠElect romagnetic", + "m apping", + "Ġdecl ines", + "c old", + "ĠS eed", + "con version", + "Ġglyc ogen", + "d T", + "aw i", + "AP P", + "H ol", + "ataly sts", + "ĠSat ellite", + "gar is", + "c ard", + "ĠBre ak", + "ĠAgain st", + "d dot", + "Ġpr uning", + "ĠCa enorhabditis", + "Ġsucceed ed", + "ub ert", + "ĠÏ ħ", + "ID s", + "Ġasympt otics", + "Ġauto anti", + "ĠScal ar", + "Ġnemat ode", + "h d", + "Ġg yn", + "ist ocene", + "Ġunderg round", + "ĠEth ical", + "Ġs ial", + "ĠM igration", + "cop e", + "Ġstig ma", + "Ġele ven", + "Ġcolor ing", + "in itions", + "ĠJ ay", + "ob a", + "ĠL DA", + "Ġbuild s", + "g ences", + "ĠEc ology", + "schem e", + "ĠUltr as", + "Ġmedi ation", + "ĠTa q", + "Ġf lying", + "ĠEqu ilibrium", + "ophosph ate", + "ĠArgent ina", + "ps ia", + "tt es", + "Ġdispar ity", + "Ġadver tis", + "agg reg", + "I SA", + "od em", + "ĠR ational", + "Ġsil ent", + "divid ed", + "P an", + "J A", + "cl aim", + "Ġradio active", + "Ġp ink", + "Ġcon verse", + "ĠM ell", + "en ib", + "rus kal", + "sl ope", + "hen ol", + "ĠP on", + "par tition", + "SM GR", + "tit led", + "ĠInter ference", + "t osecond", + "Ġse q", + "Ġtrans itive", + "ĠW id", + "review ed", + "× ¥", + "ĠV C", + "rec all", + "ogene ic", + "ĠOverex pression", + "Ġcom mitted", + "Ġsyn apse", + "Sh ort", + "ĠNeut ral", + "ic les", + "IS M", + "Ġintr insically", + "Ġmicros atellite", + "R N", + "ĠâĪ ĥ", + "det ection", + "Ġcod imension", + "Ġdrawback s", + "ĠTurn er", + "Ġsputter ing", + "Ġdis mut", + "Ġhyp ogly", + "Ġspe ak", + "J D", + "Ġs ul", + "Ġperin atal", + "Ġin k", + "ies t", + "Ġoffic ers", + "tic k", + "Ġre taining", + "ĠN ET", + "Ġexchang es", + "Ġany one", + "ĠEnd othelial", + "s end", + "in jection", + "ĠPer u", + "Ġcl ades", + "uct uations", + "Ġsulph ate", + "pi o", + "Ġphys i", + "ĠMi y", + "ĠB AS", + "ari us", + "Ġlip opolysaccharide", + "Ġneurode generation", + "ĠTurk ish", + "Ġo phthal", + "Ġac ted", + "ent re", + "Ġsh aking", + "Ġchlor oplast", + "ĠS id", + "regn ancy", + "as ion", + "ĠH s", + "Ġiniti ating", + "Ġflex ural", + "Ï ª", + "Ġpar ac", + "Ġinter layer", + "ĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ ĠĠĠĠĠĠĠĠĠĠĠĠĠĠ", + "c ause", + "rac tions", + "Ġval uation", + "SY SMGR", + "ĠGarc ia", + "ar rays", + "Ġcast ing", + "ĠP FN", + "ĠL anc", + "ĠGl ob", + "Ġd enti", + "Ġport folio", + "ĠHol ocene", + "ĠMAT ERIAL", + "Ġs arc", + "L ear", + "Ġt in", + "ĠC lear", + "bel ow", + "Ġadv ection", + "Ġoverl aps", + "Ġarth roplasty", + "comput e", + "Ġglycol ysis", + "he pt", + "lor a", + "f rames", + "ĠH ern", + "pro to", + "Ġsw ine", + "Ġje jun", + "Ġrepe ating", + "ancre atic", + "ĠColl ins", + "ĠPrinc iple", + "Ġnan of", + "Ġadj acency", + "Ġsyn ov", + "che t", + "ĠAl most", + "Ġintr usion", + "Ġechocardi ography", + "lif eration", + "Ġquies cent", + "ĠM uk", + "Ġlife times", + "grad ed", + "Ġoverw hel", + "z el", + "Ġnit ride", + "Ġdisturb ed", + "Ġfast est", + "gra bility", + "Ġtoler ated", + "f rag", + "ĠExt ension", + "ano ate", + "ifer ous", + "Ġhydro dynamics", + "IO NAL", + "ĠT oday", + "ĠExp ansion", + "Ġven om", + "ĠHep atitis", + "ñ o", + "on ation", + "syn uclein", + "Ġbasket ball", + "cl usions", + "Ġsett led", + "I QR", + "ĠC ra", + "Ġautom ation", + "ĠHealth y", + "ĠPortug uese", + "ĠAb elian", + "Ġg ad", + "ĠH G", + "ĠR oth", + "Ġcons ume", + "F G", + "in als", + "ĠM CMC", + "Ġpregn ancies", + "D ES", + "por tional", + "ĠBi ochemical", + "Ġmiss ions", + "ĠAnti body", + "ĠB CG", + "ĠL AS", + "mar ine", + "D MA", + "Ġl ongevity", + "ĠD ry", + "ĠR ao", + "Ġinterfer ometer", + "Ġdiscre tized", + "osens ory", + "s it", + "et ta", + "tain er", + "other wise", + "AK T", + "ĠFac ulty", + "Ġas certain", + "ĠSim ulated", + "Ġpay load", + "O UT", + "Ġsuff ers", + "Ġt ungsten", + "ĠAn xiety", + "ĠHeter ogeneous", + "ling ual", + "Ġphe rom", + "b ors", + "l inux", + "Ġmon key", + " £", + "ur l", + "ĠAc ross", + "ĠAK I", + "Ġop p", + "ocal ization", + "Ġmorph ogenesis", + "g ic", + "ĠP CM", + "Ġolig omers", + "Ġexhaus tive", + "ĠG IS", + "Ġpr istine", + "ĠAc tiv", + "ĠSc ilab", + "ĠAc oustic", + "ĠP ick", + "integr al", + "Ġphilos ophy", + "ĠD eng", + "ĠH ab", + "sc ape", + "ĠEmerg ency", + "Ġe pi", + "ĠB ET", + "ric ket", + "Ġann ulus", + "Ġlys osomal", + "Ġstrand s", + "C AP", + "ĠAmin o", + "ĠSt ri", + "epend ence", + "Ġfoot print", + "ĠFat ty", + "ĠN az", + "n est", + "ĠEx plicit", + "plan etary", + "le ad", + "Ġg rip", + "ne ed", + "AT T", + "ER V", + "ĠTarget ed", + "CR P", + "Ġparam agnetic", + "ĠT yr", + "ĠMicro RNA", + "h line", + "g h", + "p it", + "ĠIs olated", + "ject ory", + "Ġclean ed", + "ost e", + "Ġpath ologies", + "prop ylene", + "ĠRe ason", + "ĠIN FO", + "RA Y", + "Val ues", + "Ġal ive", + "Ġbi of", + "ew icz", + "Ġcrack ing", + "go ogle", + "lock ed", + "c rop", + "ec a", + "ur ane", + "SV M", + "ut ta", + "ĠMet ric", + "ĠEn cycl", + "ĠMod ule", + "Ġwarrant ed", + "Ġmulti disciplinary", + "ĠEl astic", + "lab elled", + "ĠSchwarz schild", + "ĠP CC", + "ma jor", + "v ideo", + "Ġst oring", + "ĠM ake", + "ak o", + "ĠJ ia", + "Ġtor oidal", + "ĠH MM", + "Ġmask ing", + "Ag ain", + "Ġneph ropathy", + "g f", + "Ġdom inating", + "er kin", + "ĠFabric ation", + "ĠF el", + "DE F", + "c ulture", + "ĠI ra", + "ĠRE G", + "iling ual", + "Ġm uss", + "pl ain", + "z h", + "ist on", + "ĠÎ ¥", + "min imal", + "c mp", + "Ga N", + "Ġmonot onic", + "Ġinv olution", + "Ġwh atever", + "ĠInstr ument", + "im ple", + "ĠPC I", + "ĠNe uronal", + "Ġfac ets", + "Ġhemod ialysis", + "ap atite", + "ĠK il", + "ont ally", + "Ġinser ting", + "ĠR IP", + "Ġconn ective", + "ĠFed eration", + "n ut", + "ĠG un", + "inu ous", + "M or", + "ĠW isconsin", + "Ġmus h", + "IT S", + "Ġe ject", + "ĠB PS", + "ĠH orn", + "ĠEmbed ding", + "Ġr aces", + "ĠJ am", + "Ġpost ure", + "ĠIn vol", + "ĠIMD b", + "ĠP lease", + "pro portion", + "ĠInter leukin", + "Ġar te", + "Ġsub sp", + "oder ma", + "F ind", + "im it", + "ĠCl in", + "H el", + "FI LE", + "orig inal", + "erv oir", + "Ġple ural", + "clip se", + "enc er", + "in aries", + "Ġv ictory", + "Ġinvestig ates", + "ĠImport ance", + "ĠM IN", + "Ġphon ons", + "integr ated", + "Ġex changed", + "ys tis", + "Ġmig rate", + "R ob", + "el and", + "pro of", + "ĠIntegr al", + "Ġmerg ers", + "Ġpolyphen ols", + "ĠF ully", + "Ġu ro", + "Ġhom ogenous", + "Ġrecogn izing", + "ĠSign als", + "v at", + "ig ms", + "Ġaccur acies", + "Sub stituting", + "Ġpoison ing", + "Ġsh rimp", + "ĠH ölder", + "ĠTanz ania", + "J S", + "M ENT", + "ĠTop ology", + "Ġin vers", + "ĠD U", + "Ġun iaxial", + "ĠS EC", + "par ty", + "Ġcontroll able", + "Ġf um", + "os tics", + "Ġmanif ested", + "Ġpropag ated", + "Ġsuff ix", + "ĠC AN", + "ĠP ret", + "ke eping", + "Assum ing", + "Ġs uture", + "Ġp est", + "Ġg amet", + "ĠAl ignment", + "esare an", + "t um", + "Ġref ine", + "Ġpop ulated", + "Ġest u", + "ĠDef ense", + "ĠPri vacy", + "ĠWe in", + "ĠSen ate", + "Ġazim uth", + "ĠProf essional", + "Ġlab our", + "Ġsem inal", + "ĠInter vention", + "ĠOl der", + "A U", + "W ind", + "d ynamical", + "ĠV eter", + "aci ón", + "Ġco oking", + "Ġâī ª", + "Ġbe ad", + "Ġdens ely", + "Ġpall iative", + "m ort", + "ĠA AV", + "ĠR yan", + "P rim", + "g alax", + "mu ir", + "st ers", + "ĠSal t", + "quee ze", + "ĠPlate au", + "Ġ í", + "Ġl ighter", + "ord inary", + "formal dehyde", + "ĠW er", + "Ġb ark", + "Ġhomogen ized", + "Ġpyram idal", + "Ġin ert", + "ĠA PC", + "ĠMic ros", + "ĠProte obacteria", + "ĠPur ification", + "Ġparametri zed", + "Ġ ille", + "acc uracy", + "embed ding", + "Ġtough ness", + "Ġis ometry", + "back s", + "ĠF IG", + "ĠR on", + "ĠE SP", + "Ġmicrogl ial", + "inter p", + "ĠIntegr ating", + "ĠReduc ing", + "Ġhe arts", + "Ġserious ly", + "Ġspec ially", + "CT RL", + "ĠSur prisingly", + "Ġhyper plane", + "pol ynomial", + "Ġrecon c", + "Ġpharmacokine tic", + "M art", + "ĠB right", + "m able", + "Ġion izing", + "Ġtr ich", + "zym atic", + "Ġlept ons", + "et ting", + "ĠH ex", + "Ġneu rop", + "Ġadip ocytes", + "Ġro ds", + "Ġsuper critical", + "Ġsuc cin", + "Ġan ter", + "ĠN AC", + "ĠSub sequent", + "IG H", + "Ġs outheast", + "Ġend owed", + "Ġconver ging", + "Ġspati o", + "Ġcele br", + "hel ix", + "Ġaccess ions", + "Ġimmobil ization", + "ĠE Q", + "sp atial", + "Ġinform al", + "Ġd ere", + "ĠEn zyme", + "ĠB BC", + "ĠE PR", + "Ġelect rically", + "Ġleuk ocytes", + "Ġal anine", + "Ġmit ogen", + "Ġintram olecular", + "ĠN I", + "Ġpro kary", + "IS O", + "Ġd odec", + "ĠTra de", + "ĠD ai", + "cc c", + "ĠWal ter", + "ĠNe ither", + "Ġvul garis", + "Ġlong itude", + "ĠInt ro", + "op tion", + "ĠQ C", + "Ġâ Ŀ", + "prot ection", + "ĠI MF", + "ap rote", + "Ġlink er", + "Ġfound er", + "Ġaspir ation", + "clust ers", + "ĠP ay", + "ĠR oles", + "Ġac yclic", + "over ing", + "Ġrem ind", + "ĠT ong", + "ĠAt ten", + "Ġengine ers", + "Ġdys regulation", + "ĠFour th", + "Ġfil ename", + "ĠCo ol", + "prot ected", + "Ġnil potent", + "ĠH K", + "cl one", + "ĠSt adium", + "a is", + "os amine", + "AB ILITY", + "rov ascular", + "ĠA H", + "ĠCon cept", + "Ġcereb rospinal", + "ow itz", + "Ġresol ving", + "Ġw ings", + "ĠE GF", + "ĠCom mand", + "iaz ep", + "Ġbe ef", + "Ġsp ines", + "Ġprior ities", + "Ġattempt ing", + "Ġtel omere", + "B QU", + "Ġviol ations", + "L B", + "om nia", + "os m", + "ir q", + "Ġdivers ification", + "al t", + "ĠB RAF", + "Ġorgan isation", + "di e", + "Ġaut oreg", + "ick ed", + "ĠEc ological", + "ĠT rain", + "ĠP Y", + "Ġmusc uloskeletal", + "Ġhoriz ons", + "Ġo mega", + "Ġquas ars", + "ep tion", + "Ġer ad", + "Ġlum inal", + "Interest ingly", + "Ġpay ment", + "c nt", + "Ġdi pl", + "Ġrecogn ised", + "C at", + "ĠCh l", + "Ġmill ions", + "Ġdisappear ance", + "G AP", + "Ġradi ographic", + "Ġpost partum", + "develop ed", + "x ual", + "Ġhe d", + "id ered", + "ĠC ertain", + "Ġdys plasia", + "____ ____", + "ĠHal f", + "Ġas ymmetries", + "ĠAl cohol", + "S um", + "Ġf m", + "Ġch ap", + "Ġpre treated", + "ĠGall ery", + "Ġoutper form", + "Ġbreed s", + "Ġt ied", + "Ġdiffe omorphism", + "Ġcaus ative", + "Ġcollec tively", + "Ġsub optimal", + "Ġins ulation", + "Ġmanip ulate", + "Ġkil omet", + "Ġrep ulsion", + "Ġchloro form", + "Ġbe an", + "Ġhe ro", + "rophys ics", + "ĠP eptide", + "Ġout lier", + "Der ived", + "iss er", + "ĠInf ant", + "sulf onyl", + "Ġrecurs ively", + "H u", + "ĠK oh", + "pyrid ine", + "Ġs quad", + "Ġth irty", + "Ġsp oken", + "ĠZ ar", + "other mic", + "Ġcalc ification", + "ĠHels inki", + "Ġbe ach", + "ĠF DR", + "Ġprob iotic", + "Ġfin ishing", + "ymmet rical", + "Ġvac ancy", + "Ġthrom bo", + "Comp ared", + "A ST", + "st ed", + "othe rap", + "Ġiod ide", + "Ġt t", + "al ignment", + "Ġmicro vascular", + "Ġinitial ize", + "ĠANAL YSIS", + "Ġtop ographic", + "ĠReport ing", + "Ġunderestim ated", + "put ed", + "Ġatheros clerotic", + "Qi agen", + "g ut", + "ĠCor tical", + "Ġdisrup t", + "es te", + "Ġgl ue", + "Ġnarrow er", + "Ġin patient", + "Ġsch olars", + "Ġb c", + "ĠPsych ological", + "ĠHamilton ians", + "Ġhon or", + "tib ular", + "Ġinser tions", + "oscop e", + "Ġpharmacokine tics", + "Ġmathem atically", + "Ġfor k", + "ip ital", + "ĠAr gs", + "abol ism", + "Ġâİ ł", + "ĠRob ot", + "ĠC asc", + "Ġle aching", + "ĠL ack", + "Ġend ocytosis", + "Ġtr is", + "Ġsensiti vities", + "Ġlic ensed", + "Ġsp onge", + "carbon yl", + "fe at", + "Ġpre cl", + "Ġwa ist", + "tif ications", + "Ġol iv", + "b inary", + "at ri", + "ĠBi ot", + "T Z", + "Ġf ake", + "ĠM osc", + "ĠH PS", + "ĠVol tage", + "Ġâİ Ŀ", + "ĠAh med", + "ĠSex ual", + "dehyd es", + "ĠC ot", + "Ġmag ma", + "oxyl in", + "Ð Ī", + "amet hyl", + "ĠL OS", + "di phenyl", + "experim ental", + "Ġpluripot ent", + "agit tal", + "w alk", + "Ġplas monic", + "Ġcontrac ts", + "Ġexp ed", + "ĠArab ia", + "Ġshoot s", + "ĠR AN", + "ustr ated", + "Ġconvex ity", + "Ġm J", + "ĠAbs olute", + "ĠS EL", + "MI P", + "ĠAct ually", + "so le", + "Q I", + "ĠTGF β", + "Ġâİ ŀ", + "Ġrearrang ements", + "Ġc uring", + "exp ensive", + "cepti bility", + "Ġour s", + "ĠKid ney", + "Ġassign s", + "Ġvox els", + "ore al", + "Ġeven ing", + "h us", + "Ġ ãĢ", + "or adi", + "ĠCor rection", + "Ġnanofib ers", + "Ġcan tile", + "big oplus", + "umin ous", + "eclam psia", + "ĠC ult", + "EC H", + "at ology", + "Ġj i", + "cr yp", + "ĠAsp ects", + "en i", + "Ġsem is", + "IR S", + "ĠP ho", + "enc oding", + "ĠJus tice", + "ococc i", + "Ġhypoth alamic", + "ract able", + "ĠOr b", + "Sim ons", + "Ġmanip ulated", + "att ribute", + "on ov", + "or ously", + "end ar", + "ud er", + "ins ert", + "Ġlys ed", + "ĠHod ge", + "Ġfootball er", + "Dev ice", + "ĠLe ast", + "Ġstrat um", + "Ġmit ral", + "Ġs ell", + "ĠM uc", + "gly cer", + "o j", + "Ġpathogen icity", + "ĠDecl aration", + "op ause", + "ĠAr ticle", + "Ġrins ed", + "ĠLé vy", + "re ment", + "Ġan ts", + "ĠD ic", + "Ġk Pa", + "ur ry", + "mo tion", + "cl ient", + "Ġaccess ory", + "Ġdepolar ization", + "nam ely", + "Ġdispar ities", + "Ġfavour able", + "ĠTib et", + "Ġo ocyte", + "ist ration", + "Ġun resolved", + "cri ptive", + "phys ics", + "Ġben zo", + "Ġcrystall inity", + "Ġpay off", + "Ġumb ilical", + "os il", + "ĠSystem ic", + "ĠST M", + "Ġstabil izer", + "U SA", + "ĠJ ensen", + "A ug", + "ĠH at", + "AG G", + "under brace", + "Ġmanip ulations", + "ĠM anc", + "ned y", + "Ġscr atch", + "C herry", + "osacchar ides", + "Ġprecipit ate", + "quar ters", + "ic ul", + "Ġoptim ally", + "man y", + "Ġneoplas ms", + "Ġin ward", + "ary ng", + "Ġm oll", + "ĠW el", + "ĠW iley", + "Ġnewsp aper", + "Ġinhabit ants", + "ĠS uccess", + "Ġbrid ging", + "Ġdis connected", + "Ġhygi ene", + "D ist", + "Ġsc ripts", + "Ġmes oporous", + "Ġrestric ts", + "act one", + "Ġaqu ifer", + "ĠïĤ ·", + "Ġp lex", + "Ġpresum ed", + "Ġ ips", + "ĠM ilitary", + "Ġjud ged", + "Ġal d", + "Ġsequ est", + "comp ared", + "UL ATION", + "adap ted", + "Ġinstruc ted", + "p ulse", + "Ġc usp", + "mat ching", + "car rier", + "Ġenfor ce", + "ĠInter view", + "ometric s", + "Ġnull ptr", + "Ġflav our", + "ĠPare to", + "ĠB ER", + "Ġu v", + "Ġcr ash", + "ĠC ann", + "ĠMin eral", + "ĠOlymp ic", + "Ġpolyc rystalline", + "le tt", + "T ables", + "requ ent", + "Ġsed entary", + "uns aturated", + "ĠBern oulli", + "Ġad missions", + "itor ial", + "ac ute", + "Ġad ditions", + "we et", + "AL E", + "ĠMan ip", + "tok ens", + "prec ed", + "d k", + "cons ider", + "Ġïĺ ¹", + "Ġwr ites", + "car dia", + "ct omy", + "omat ous", + "S ymbol", + "ust en", + "Ġprote olytic", + "c ategories", + "Ġf ic", + "Ġsw ing", + "Ġpass enger", + "Ġoverl apped", + "if i", + "Ġmut ational", + "ĠJoseph son", + "Ġreg ret", + "ĠAr k", + "ĠCF D", + "Ġman eu", + "enc oded", + "texts c", + "Ġdecom positions", + "ĠDe b", + "Ġmand ibular", + "d U", + "ĠP IC", + "Ġtranscript omic", + "Ġtel escop", + "ĠSant os", + "o E", + "ĠM CP", + "Ġind igenous", + "Ġmicrosp heres", + "Ġcod ew", + "z ip", + "Ġfing ers", + "Ġcampaign s", + "¸Ģ ł", + "Ġacc idents", + "ĠTo ols", + "Pl anck", + " »", + "ed er", + "ing ham", + "oxid ase", + "Ġancest or", + "wh ose", + "Ġphosph olipid", + "Ġconvers ation", + "ĠH of", + "cor tical", + "gly cos", + "Ġmanufacture rs", + "op ulmonary", + "Ġincl ined", + "ĠBet he", + "Ġsp ending", + "ĠFus arium", + "u itively", + "Ġfem ur", + "ĠL inks", + "Ġnit rite", + "M ain", + "Ġfl ora", + "ĠPh D", + "ĠWr iting", + "ĠHess ian", + "Ġμ s", + "ool s", + "Ġvictim s", + "ĠR ew", + "ans en", + "E ar", + "Ġor n", + "Ġthermo electric", + "EN SE", + "ĠWeight ed", + "h oles", + "Ġc en", + "Ġac uity", + "Ġvac ancies", + "ĠDu ke", + "Ġpac litaxel", + "Ġconver ts", + "bour ne", + "ĠA CS", + "os i", + "Ġcrim inal", + "ĠI b", + "un es", + "ĠNan oc", + "P ost", + "ĠMD S", + "Ġecon omics", + "Ġthough ts", + "Ġneuro protective", + "Ġinters ects", + "c ers", + "at id", + "us a", + "ĠAn s", + "Ġafter wards", + "ĠOF DM", + "ĠCM V", + "ĠC um", + "AT G", + "ĠImage Net", + "ĠAtt ack", + "ogene ities", + "Ġcouns eling", + "ĠCON TR", + "á lez", + "ĠD h", + "ĠG V", + "Ġposition al", + "Ġg ang", + "ĠInter active", + "w ig", + "ĠT race", + "ĠD SS", + "Ġsynthet ase", + "ĠGal ile", + "us ually", + "ĠB ass", + "ard less", + "Ġexec uting", + "K P", + "ĠNep al", + "RE AD", + "ĠL ock", + "oh ydro", + "rot ation", + "d il", + "roscop ically", + "re perfusion", + "Ġdis hes", + "ĠProceed ings", + "ĠN PC", + "Ġmon soon", + "ĠLem mas", + "ĠChand ra", + "Ġre actors", + "Ġtr yptophan", + "ĠV T", + "ĠD EM", + "Ġleg islation", + "m k", + "Ġtor ic", + "ĠProgram s", + "ĠPub Med", + "Ġr DNA", + "Ġpost s", + "Ġâİ Ľ", + "Ġshed ding", + "toler ant", + "Ġv oids", + "ĠCarib bean", + "C ODE", + "T ube", + "AL SE", + "Ġchlor ine", + "Ġco erc", + "ĠRh iz", + "ĠKir k", + "Ġà ĸ", + "ro ut", + "ic ides", + "ag u", + "ĠK w", + "Ġcr u", + "Obs erve", + "ĠRe vis", + "Ġan onym", + "Ġpre requ", + "ocor tical", + "Ġrest aur", + "ĠPop ulations", + "d st", + "Ġfor t", + "reg s", + "ĠPolar ization", + "Ġpancre atitis", + "a ph", + "th reat", + "ft en", + "ĠAl aska", + "ĠFlex ible", + "Ġreperto ire", + "k an", + "math choice", + "Ġmit osis", + "Ġe at", + "ut in", + "Ġr t", + "Ġd ummy", + "ĠC ys", + "ĠG or", + "ear chers", + "H PLC", + "Ġb ay", + "ĠNi elsen", + "ĠR oc", + "ian i", + "ic it", + "rag ue", + "Ġcour ts", + "test ing", + "Ġampl ify", + "Ġtu ples", + "prol iferative", + "ĠPar as", + "Ġmagn ets", + "Ġchem okines", + "ĠMit chell", + "ĠPet ri", + "hol tz", + "y ch", + "mat rices", + "Ġcorrec ting", + "ĠPC a", + "ynam ically", + "ĠNAF LD", + "Ġeff luent", + "it um", + "Ġth rows", + "ĠGu id", + "och romatic", + "ĠF ro", + "id ad", + "rom agnetism", + "H erm", + "ĠS pi", + "ĠQu as", + "dom ains", + "Ġquad rant", + "ĠSO X", + "ĠGover nor", + "Ġam enable", + "he ld", + "ĠC ul", + "Ġunder water", + "ĠK ron", + "ĠSp ati", + "ano yl", + "C U", + "ov ir", + "Ġdem ographics", + "With in", + "ĠM é", + "texts f", + "ĠLab el", + "Ġgenu ine", + "Ġh ill", + "ĠL az", + "Ġt esticular", + "ĠB row", + "IC ATION", + " ¡", + "ĠA IC", + "anc omycin", + "str ual", + "Ġarrest ed", + "ĠS om", + "ĠI HC", + "ĠP ose", + "ĠM ö", + "ist ar", + "ĠP AM", + "ĠH CT", + "Ġtyp edef", + "ĠMor se", + "ĠLe ishman", + "lim b", + "Ġsphe roid", + "os ely", + "ĠGu inea", + "re new", + "Ġpsori asis", + "ist a", + "ĠCh ung", + "orth ogonal", + "ĠShe ar", + "ĠMus lim", + "ĠP ict", + "In teger", + "Ġspac er", + "L y", + "Ġd ermal", + "Ġonc ology", + "Ġd p", + "Ġphot oluminescence", + "reg on", + "amin ase", + "ĠẠĭ", + "Inst ance", + "ver b", + "Ġmethyl ated", + "ĠG em", + "ist ently", + "ĠMg Cl", + "ĠEle vated", + "⣠©", + "onstr uct", + "Ġsnap shot", + "en em", + "ĠD isk", + "Ġhydro static", + "Ġïĥ ª", + "v or", + "ĠI E", + "ĠL Y", + "OR F", + "Ġfo il", + "m ale", + "Ġdepend ed", + "s parse", + "Ġmet as", + "Ġtext ures", + "Ġstack s", + "M Hz", + "Ġf n", + "Ġult rac", + "ĠSh ould", + "V ec", + "n ine", + "inf inite", + "ĠLaw rence", + "ĠInvent ory", + "ĠPro state", + "Ġgest ure", + "ĠSuz uki", + "A bs", + "ric ane", + "ĠPeriod ic", + "M yc", + "if iable", + "Ġin efficient", + "Ġcoll apsed", + "Ġtopological ly", + "Ġprefer able", + "Ġbronch ial", + "ust on", + "Ġflex ion", + "our ney", + "trans lation", + "Ġepit axial", + "Ġirradi ance", + "Ġneighb ours", + "sw itch", + "Ġactu ators", + "S OD", + "m ir", + "di es", + "ik awa", + "ĠAL L", + "ĠR SV", + "ĠH EP", + "Ġend urance", + "conn ection", + "Ġgest ures", + "odon tic", + "ĠUn c", + "Ġdismut ase", + "H aving", + "m ix", + "Ġneuro genesis", + "Ġmyocardi um", + "ĠRuss ell", + "H ist", + "ĠS PI", + "tri azol", + "ag ulant", + "ĠRe quired", + "Ġsh RNA", + "ĠArth ur", + "Ġspaw ning", + "d ried", + "Ġrec tif", + "Ġà ī", + "Ġoste ogenic", + "re place", + "Ġgain ing", + "Ġneutral ization", + "ĠHart ree", + "Ġfollic les", + "Ġrelig ion", + "Ġd uplex", + "Ġtrans ients", + "amp ed", + "Ġmicrotub ules", + "int erest", + "Ġste els", + "B atch", + "Ġden aturation", + "ĠPhill ips", + "Ġqu iet", + "ĠB ureau", + "ĠR are", + "Ġqu ercetin", + "a ults", + "Ġel ution", + "uk a", + "ĠInter pretation", + "R V", + "ĠE SC", + "ĠK om", + "are ttes", + "Ġï ģĦ", + "Ġtra dition", + "Ġdiss ected", + "Ne igh", + "Ġshe aves", + "Ġbelong ed", + "ĠHist oric", + "ĠO E", + "Ġj son", + "lem ma", + "ĠY AP", + "ode xt", + "inter face", + "Ġextrem ity", + "cross ing", + "preced ented", + "acc ording", + "Ġconstruc tive", + "ĠStim ulation", + "ĠHF D", + "Ġwaven umber", + "Ġh rs", + "Ġpapill omavirus", + "Ġvom iting", + "Ġre activation", + "omet rically", + "ĠDim ensions", + "ob jects", + "ort on", + "ĠMat hem", + "ĠOl ive", + "Ġcros stalk", + "par tite", + "opath ies", + "ĠCN Ts", + "rous al", + "Ġcrow d", + "ĠLang muir", + "ĠT ox", + "echan ics", + "im us", + "ĠSh ock", + "tan h", + "ĠBrill ouin", + "Ġtransf erring", + "Ġellip se", + "ĠAd dition", + "ĠR ural", + "Ġgeodes ics", + "G EM", + "ĠP OS", + "ĠM ission", + "oc arp", + "ĠJ ane", + "L ie", + "f req", + "op ot", + "ĠV ibrio", + "ĠOb j", + "er ts", + "ĠTri als", + "C FT", + "ĠC odes", + "μ g", + "Ref erence", + "ĠF ung", + "ĠSup pression", + "h og", + "Ġresis tive", + "C hi", + "int ered", + "Ġpost menopausal", + "St atistical", + "ĠEd wards", + "Ġs es", + "Ġfarm ing", + "quar tile", + "cool ed", + "Ġnan op", + "ĠProb ing", + "ĠBern ard", + "un i", + "ie ties", + "ĠMark et", + "os um", + "ĠM essage", + "Ġaxi om", + "c g", + "ĠM oving", + "Res olution", + "Ġadsorb ent", + "Ġmult in", + "Ġin effective", + "prop ag", + "hard t", + "S aharan", + "W il", + "ĠI van", + "ir ubin", + "Ġtra bec", + "all i", + "ĠCD Cl", + "Ġse w", + "ĠIs s", + "Ġagg ression", + "ĠJ uan", + "Ġdispers ions", + "Ġaux in", + "F ET", + "l p", + "re ach", + "ĠP GE", + "che str", + "Ġlect ure", + "ĠD onald", + "sl ip", + "ĠHb A", + "ĠSec ure", + "ĠBe h", + "Ġdam ages", + "W H", + "alk yl", + "H a", + "ĠTh anks", + "Ġsensiti zation", + "Ġwat erm", + "Ġtw ins", + "Ġcultiv ar", + "Ġze olite", + "V ariable", + "ĠB ent", + "Ġanti sense", + "ĠHans en", + "reprene ur", + "ĠSN e", + "ĠEM G", + "Ġre acted", + "Ġover flow", + "Ġformal in", + "ĠUs ually", + "olyb den", + "Ġac ad", + "AT URE", + "Ġwavegu ides", + "Ġch unk", + "Ġmod ifies", + "Ġer yt", + "ĠZh ong", + "Ġgran ule", + "Ġc s", + "ĠGra de", + "Ġland marks", + "ur istic", + "Ġam ines", + "ĠIntr insic", + "Ġerrone ous", + "Ġlock down", + "yp ti", + "Ch ild", + "Ġunivers ities", + "Ġparas it", + "Ġign ition", + "T im", + "ar aj", + "ra vel", + "ĠL ands", + "ĠCirc ular", + "Ġrot ate", + "Pati ents", + "ĠW B", + "Ġmyel in", + "ĠWe iss", + "Ġdip olar", + "Ġfollic le", + "ĠWat son", + "ĠIn cor", + "Ġfound ations", + "ĠP ip", + "Ġpress ing", + "Ġforb idden", + "av an", + "Ġm Ab", + "un ion", + "ĠF resh", + "ĠCor p", + "fl oxacin", + "co ordinate", + "Ġsh unt", + "Ġconstit uted", + "anil ine", + "Ġtwe ets", + "ĠCh ow", + "Ġmob ilization", + "zy k", + "E ST", + "ne igh", + "ĠM eng", + "ĠRes Net", + "ĠJ et", + "Ġlumin ous", + "Ġstress ors", + "do es", + "trifluor omethyl", + "Ġconcer t", + "ĠCho ice", + "ph im", + "al coholic", + "oc hem", + "ilt ered", + "Ġpredict able", + "Ġt ran", + "ĠP ra", + "Ġval ves", + "Ġaut onomy", + "reg ulate", + "ĠBe ach", + "ĠOnt ology", + "Ġis ofl", + "Ġqu oted", + "ĠL ex", + "th y", + "Ġcompl aints", + "ĠT rees", + "Ġop posing", + "ĠAcc eler", + "con trast", + "Ġcompet ed", + "O E", + "ĠR oche", + "iss ance", + "Ġpe ace", + "ĠA im", + "Ġinfer tility", + "ĠAntarctic a", + "th ien", + "S umm", + "Ġjudg ments", + "am ides", + "Ġsp ill", + "Ġhere after", + "ĠCons tit", + "comput er", + "Ġbeg un", + "ocent ric", + "Ġp umps", + "med ium", + "ch ol", + "met allic", + "Ġfl ares", + "Ġpet roleum", + "Ġwith d", + "ĠThe atre", + "Ġun labeled", + "Ġregular ized", + "oster ic", + "ĠP FS", + "Ġun em", + "Ġpresent ly", + "Ġbuff ered", + "aff inity", + "ĠDem ographic", + "ĠK ondo", + "Ġcent uries", + "Ġmig ratory", + "aryn x", + "Ass ociated", + "anil ino", + "g rown", + "ĠEx ecutive", + "ĠE k", + "ĠH emat", + "ĠPl ayer", + "ĠCH D", + "f lex", + "ĠS ever", + "alth am", + "im pro", + "an et", + "ocy st", + "ĠA ster", + "CO L", + "ĠSimilar ity", + "ĠHow ard", + "Ġmultic ast", + "ĠEns emble", + "ì Ĺ", + "ol ys", + "ĠGen omics", + "Ġreson ators", + "Ġfist ula", + "on en", + "us ers", + "Ġhyp o", + "rog ens", + "Ġmed al", + "ĠM IP", + "Ġvolt am", + "Ġappreci ated", + "ĠP é", + "ĠGa ia", + "Ġbuck ling", + "Ġcongru ence", + "fur yl", + "ĠEp stein", + "Ġcasc ades", + "g old", + "Ġan hyd", + "Ġgrad uated", + "M emory", + "ĠInd ustry", + "ĠSch neider", + "Ġemploy ee", + "ĠCor n", + "M AC", + "ro ve", + "rop od", + "s ervice", + "ĠOx idation", + "Ġenum eration", + "m ad", + "ĠCl ose", + "ĠMod ular", + "Ġprogen y", + "Ġg t", + "read ing", + "ĠInd ic", + "opath ologic", + "ĠPFN GL", + "X L", + "c is", + "ĠM ike", + "ĠB BB", + "ĠExt reme", + "ĠCho ose", + "Ġhoriz ontally", + "ĠASS ERT", + "Ġglucocortic oid", + "B ay", + "Ġp df", + "Ġcontain ers", + "ĠL OC", + "ĠY ield", + "opro te", + "Ġfruct ose", + "ĠI CC", + "Ġdec id", + "rim idine", + "Ġfrag mented", + "Ġisomorphism s", + "Ð ¼", + "Ġintegr ates", + "Ġfib ration", + "ĠâĬ ¤", + "Ġxen ograft", + "nucle on", + "ĠC SP", + "Ġs ut", + "ĠSp ir", + "Ġdiss oci", + "ĠT BI", + "ĠFor ces", + "Ġhyper surface", + "Ġmy osin", + "ĠQueens land", + "N eg", + "ĠU RL", + "b ind", + "Ap plied", + "ĠD ob", + "ĠK E", + "Ġmem or", + "ĠArab ic", + "ĠL ateral", + "ĠSt art", + "n ose", + "ti bility", + "as ters", + "Ġus ability", + "Ġinc enti", + "ym n", + "ĠAnaly tic", + "P et", + "ĠM ask", + "W orld", + "b rand", + "Ġelim inates", + "Ġmer it", + "ĠPhilipp ines", + "ĠB CL", + "ĠO ri", + "Ġparad igms", + "ĠIn ters", + "riz ona", + "Ġcon ception", + "Ġrel ied", + "ĠJ oe", + "ĠAp ple", + "Ġlight weight", + "mort em", + "ol ig", + "Ġv iz", + "Ġst ones", + "Ġkey words", + "ĠSecret ary", + "T N", + "old er", + "ĠInt estinal", + "Ġpossess ed", + "Ġmonoton icity", + "em itting", + "ĠDef ining", + "ĠPar ticularly", + "Ġautomorphism s", + "Ġeryt hemat", + "ĠW aters", + "ĠCycl ic", + "maxim al", + "xt y", + "ĠS ad", + "Ġur anium", + "Ġhypoth alamus", + "ĠSU MO", + "Ġdeal t", + "Ġk its", + "Ġpain ting", + "ĠS ier", + "ch ool", + "OD O", + "sur faces", + "ĠP neum", + "organ ized", + "ĠC PT", + "Ġins oluble", + "ĠCo herent", + "Ġrecess ive", + "Ġb ivariate", + "Ġed it", + "Ġnation wide", + "M ODE", + "c hest", + "ĠS LC", + "Ġintra peritoneal", + "ĠDis ordered", + "Ġinsu fficiency", + "ie v", + "iaz ole", + "W rite", + "ĠD ATA", + "tor al", + "Ġqual ities", + "Ġpossess ing", + "ĠM ats", + "Ġretin opathy", + "ĠB K", + "Ġnovel ty", + "ce ans", + "Ġreserv es", + "ĠNAD H", + "Ġisother m", + "Ġsoldi ers", + "p b", + "iter pen", + "ĠAg ents", + "z u", + "Ġunw anted", + "Ġhyper parameters", + "ec an", + "ĠS ES", + "ĠF G", + "ĠN avig", + "Ġtriang ulation", + "Ġnetwork ing", + "Ġpoly styrene", + "Ġinduc tively", + "brevi ations", + "Ġneurom uscular", + "ĠL inux", + "stud ied", + "ĠBe ing", + "Ġdef iciencies", + "ĠMat rices", + "Ġwe aring", + "Ġhad rons", + "am yl", + "Ġdisc ourse", + "och lor", + "ĠMel an", + "ĠL an", + "V L", + "Ġmunic ipal", + "Ġenroll ment", + "ĠS ymmetric", + "Ġdiscipl ines", + "ĠBar on", + "Res earch", + "Ġmagne tite", + "om ide", + "polar ization", + "le ys", + "Ġseem ingly", + "hep atic", + "Ġcl o", + "ĠQu atern", + "Ġcompe tit", + "R equ", + "ga uge", + "Ġhydro chloride", + "drop out", + "pan el", + "Ġaspir in", + "ĠR UN", + "Ġrib bon", + "Ġinacc urate", + "ĠP all", + "duc ers", + "Through out", + "Ġcell ul", + "Ġsusp ect", + "Ġalle lic", + "Ġsn ake", + "ordin ated", + "ĠAut ophagy", + "Ġe ig", + "Ġr if", + "ĠKen nedy", + "Ġbot tle", + "ĠY outh", + "aw ed", + "linear ity", + "uk er", + "ĠO X", + "ext ension", + "Ġw ard", + "ĠComplex es", + "Ġbios ensor", + "ĠCart an", + "d n", + "Ġs onic", + "Ġindex ing", + "Ġd v", + "rel iable", + "p k", + "RE NT", + "Ġt anks", + "ĠH et", + "ĠW ing", + "ĠCu O", + "Ġprint f", + "Ġlumin osities", + "c ourse", + "Ġsc ram", + "Ġsam pler", + "Ġmulti pliers", + "Def ault", + "od il", + "int r", + "sequ encing", + "Ġtrans missions", + "ĠWh it", + "ĠOp portun", + "Ġintern ally", + "Ġacknowled ges", + "ĠE dition", + "Ġarter i", + "Ġalb edo", + "ĠNucle otide", + "Ġy es", + "ĠRel ativistic", + "Ġv otes", + "ĠForm ulation", + "usc itation", + "Ġconcurrent ly", + "u in", + "Ġnon invasive", + "Ġprim ates", + "μ l", + "Ġsubt ropical", + "g un", + "ĠS outheast", + "ö n", + "Ġequ ator", + "Ġwork shop", + "Ġsch ist", + "und ant", + "ĠMOD IS", + "t ar", + "Ġa eg", + "Ġplot ting", + "ĠD ET", + "Man ager", + "un ed", + "oxif en", + "ĠIn ver", + "Ġx anth", + "ĠSer ver", + "Ġstret ched", + "Gl obal", + "C ore", + "ĠWe ber", + "y ard", + "Ġexpl ores", + "ĠBi ography", + "SN P", + "ĠNeut rino", + "Ġkilomet res", + "Ġcomm utes", + "Ġaccept ability", + "ĠAntib odies", + "ic ol", + "Ġmus eum", + "Ġden it", + "Ġextrap olated", + "Ġacetyl choline", + "T oken", + "ĠF ock", + "ond e", + "Ġdiscrimin ative", + "ĠM ant", + "Ġess ence", + "cel and", + "ĠCh air", + "Ġintegr ative", + "ĠS PD", + "hen ium", + "arbon ate", + "B ASE", + "reg ulates", + "p atch", + "Ġd ib", + "Ġanti symmetric", + "Ġwear able", + "Ed ge", + "re ts", + "Ġperce ive", + "ĠMagn esium", + "ad ows", + "Ġdis posal", + "Ġair port", + "ause a", + "f its", + "Ġnec ro", + "ĠS IN", + "ĠD uc", + "ĠRe ading", + "b ys", + "Ġreflec tive", + "h is", + "omet ries", + "Ġvi rial", + "Ġartif icially", + "child ren", + "ĠUltras ound", + "VI EW", + "Ġsc ulpt", + "Ġsur f", + "Ġsex ually", + "Ġgeomet rically", + "Ġdivis ors", + "Ġiniti atives", + "acc i", + "Ġkeratin ocytes", + "a R", + "aro t", + "Ġïĥ ¨", + "comput ed", + "ĠTC GA", + "psych ological", + "ĠM AN", + "ĠM PC", + "tic ing", + "lim iting", + "am ins", + "Ġsurfact ants", + "ĠSer b", + "Ġrhyth ms", + "ĠRout ing", + "w ang", + "Ġmicro structures", + "oph ytes", + "Ġanalges ic", + "F OR", + "qu al", + "Ġpubl ish", + "ĠTim ing", + "por ous", + "rang ing", + "er on", + "ĠZ i", + "ĠMarsh all", + "Wid th", + "Ġis omers", + "Ġ ·", + "phen oxy", + "Ġure th", + "ro bl", + "Ġmention ing", + "o zyme", + "ĠL ud", + "Ġop position", + "Ġaband oned", + "Ġrout ines", + "ĠH ST", + "mut ex", + "c oded", + "e ating", + "ter t", + "emicon ductor", + "d w", + "Ġbary ons", + "Ġleuc ine", + "ot ron", + "Ġend os", + "Ġreproduc es", + "Ġanalges ia", + "Ġimmunore activity", + "ĠPre p", + "ĠGarc ÃŃa", + "Ġinco herent", + "an ed", + "le pton", + "and ra", + "ul ae", + "ĠH idden", + "F V", + "Ġgeneral izes", + "ĠSte vens", + "ĠF oster", + "Ġfresh ly", + "Ġh f", + "Den ote", + "o es", + "ĠD in", + "Ġdet ox", + "Ġdec oupled", + "Ġsepar ations", + "ucle otide", + "Ġelect rophysiological", + "ĠBAL B", + "Q TL", + "ĠA Ch", + "ĠRe le", + "que z", + "Mn O", + "ect ures", + "Ġis cha", + "Ġins ulators", + "cell ulose", + "ĠFL AG", + "omb ic", + "ĠUs ed", + "j iang", + "exp ansion", + "ĠRep eat", + "ĠRes erve", + "ab elian", + "ĠH unting", + "G RO", + "ly te", + "ĠB ark", + "Ġcre ative", + "Ġb end", + "el erated", + "dis h", + "Ġhigh way", + "Ġcross ings", + "j ust", + "on o", + "ull ivan", + "ĠDe ad", + "Ġtrade off", + "e on", + "og ical", + "experim ent", + "Ġconf ers", + "ĠD ot", + "Ġco ils", + "Ġax ion", + "ĠIR S", + "ĠÅ ©", + "Ġglac ier", + "ĠMosc ow", + "ĠS pringer", + "Ġinv is", + "ĠArn old", + "Un iversity", + "at tern", + "per or", + "ĠLim its", + "Ġincomp atible", + "r ather", + "ĠT es", + "Ġfail ing", + "Ġthick ening", + "Ġest radiol", + "as se", + "Ġnecess it", + "Ġsacrific ed", + "ĠS ear", + "ĠNor the", + "raise box", + "ĠS low", + "ĠM unic", + "Ġlear ner", + "igen ic", + "Ġderm atitis", + "ut en", + "Ġde er", + "Ġhist amine", + "L at", + "M al", + "il ly", + "Ġge ochemical", + "Ġspermat ozoa", + "Ġv inyl", + "em et", + "Ġeffect ors", + "ĠEncycl opedia", + "Ġord inal", + "Ġcontrovers y", + "ĠPers pectives", + "ovirus es", + "mark ed", + "ĠS PE", + "ĠN utri", + "Ġad here", + "ĠHigh way", + "Ġdistill ation", + "MR T", + "ple tion", + "Ġannih il", + "Ġwave function", + "Ġconfig ured", + "Ġmeth ionine", + "L ow", + "s ensor", + "ĠS now", + "S ample", + "Ġdef initely", + "ĠMet h", + "r ypt", + "Ġprom pted", + "Ġmonol ith", + "ĠEn vironments", + "t m", + "ĠCO D", + "or is", + "equ ations", + "âĺ Ĩ", + "ĠNe ighbor", + "Ġimag ine", + "ĠUs ers", + "ĠCam era", + "ĠMod ification", + "ĠAtt acks", + "Ġinhal ation", + "á º", + "Ġventi l", + "ĠN U", + "ĠCon trast", + "Ġconf ining", + "S ervice", + "W allis", + "ĠA TR", + "Ġsub duction", + "Ġïģ ¢", + "Ġtit ration", + "R oche", + "v iv", + "Ġbe ars", + "bol a", + "Ġblind ed", + "meas ures", + "ĠSt ack", + "occ urrence", + "Ġperme ation", + "l ar", + "ept ors", + "ĠD IF", + "cor rhiz", + "ĠV isc", + "fig urable", + "Ġschedul er", + "Ġoccas ions", + "ambo o", + "Ġam p", + "g ain", + "ĠC it", + "Ġpreced ed", + "Ġtac tile", + "Ġïĥ ¦", + "gener ic", + "Ġretro grade", + "Ġf ans", + "Ġf isher", + "Ġl ights", + "ee per", + "Ġundes irable", + "w ald", + "emb ol", + "Ġwr ist", + "Ġauthor ized", + "Ġchond rocytes", + "ĠE PA", + "ne u", + "ĠOper ations", + "Ġche ap", + "Ġan ionic", + "ĠO regon", + "c ot", + "re ason", + "ex istence", + "ĠFin ancial", + "olybden um", + "c us", + "ĠN ON", + "Ġlock ed", + "B it", + "S il", + "m ixing", + "ĠS ites", + "aprote obacteria", + "ĠIn ner", + "Ġcar c", + "Ġbi otic", + "ĠFl ag", + "Ġmag ic", + "kine tic", + "ic ted", + "Ġbul b", + "sup set", + "pe z", + "deriv ative", + "Ġe IF", + "ĠR ough", + "di rectional", + "ex it", + "ax y", + "xt ures", + "phim urium", + "ĠT Fs", + "ath in", + "Ġor ch", + "Ġspect ro", + "duct ase", + "quin olin", + "Ġgras p", + "Ġpar sing", + "Ġdiffic ile", + "ĠLD H", + "ĠJup iter", + "ĠF IF", + "ĠPri ze", + "Ġinten tions", + "s ession", + "pow ered", + "ĠB am", + "ph asic", + "Ġign oring", + "ĠRichard son", + "princ iples", + "Ġoffic ially", + "C t", + "Ġinc on", + "ĠReg ulates", + "Ġm isc", + "ĠE Z", + "Ġsyn onym", + "Ġunfold ing", + "ĠD EC", + "ĠR X", + "PD F", + "Ġbran es", + "typ ically", + "Ġc ages", + "if olia", + "ug u", + "oll en", + "Ġtable t", + "ĠS ah", + "ĠP VD", + "Ġal ert", + "Ġformer ly", + "ĠKR AS", + "s un", + "Ġde acetyl", + "M er", + "Ġskew ed", + "ĠPle istocene", + "ĠB etter", + "ĠH ud", + "ĠBro ok", + "Ġp ts", + "ĠH U", + "om o", + "ag rass", + "Ġenvironment ally", + "Ġhon est", + "ĠN ine", + "Ġpig ments", + "l inks", + "ĠT OP", + "ĠCytoplas m", + "G ib", + "Ġaccess ing", + "mi as", + "Ġexplos ive", + "Ġres ide", + "art an", + "Ġtransition al", + "Ġun precedented", + "Ġ rom", + "ĠTNF α", + "Ġprecipit ated", + "Ġt ie", + "IS S", + "Ġthick er", + "ĠLat ent", + "ĠValue Error", + "d q", + "d ma", + "Ġchrom atic", + "ĠSub section", + "ĠF ACS", + "Ġrenormal ized", + "P rop", + "m TOR", + "ĠH CO", + "Ġover lo", + "bs iella", + "yl obacter", + "Ġneuro imaging", + "Ġassembl age", + "Ġexp ands", + "Ġî Ī", + "ĠF un", + "Ġc itation", + "IK V", + "Ġtro ops", + "in istic", + "Ġc ubes", + "Ġf ont", + "ĠH os", + "ger ies", + "Ġsuccess ively", + "Ġdeco herence", + "S pringer", + "h in", + "at ine", + "ĠâĪ ¥", + "SA S", + "é t", + "ĠSed iment", + "u ously", + "ĠW ars", + "ind icated", + "Ġfl ask", + "A IDS", + "Ġc ra", + "ĠL ot", + "Ġprim al", + "Ġjus tice", + "z ag", + "Ġmax illary", + "Ġgeneral izations", + "uel a", + "Ġtag ging", + "Ġpup il", + "Ġin expensive", + "Ġw atch", + "ĠA MD", + "ĠF ir", + "Ġneuro blastoma", + "Ġmaxim izes", + "ĠObs erved", + "mi xture", + "Ġopportun istic", + "t rial", + "ah an", + "Ġïģ ¬", + "Ġcat ar", + "ĠControl s", + "ĠNew man", + "Ġmicro structural", + "bor ns", + "Ġoxygen ation", + "ĠMac ro", + "ĠJ ak", + "plic ating", + "Ġolig odend", + "Ġres orption", + "Ġd orm", + "Ġsol vers", + "ĠK ruskal", + "ĠRe volution", + "ĠGast ro", + "Dri ven", + "Ġt iter", + "Ġo ri", + "ĠP CL", + "Ġwet lands", + "Ġar ticular", + "CC A", + "en oic", + "Ġt rick", + "oper iod", + "ĠCoch rane", + "ad ay", + "ĠC erebral", + "Ġmod ulators", + "ĠS SC", + "Ġactiv ations", + "Ġadap ting", + "ĠScal able", + "n one", + "p ip", + "Ġpri vi", + "ĠPseud o", + "Ġdisapp ears", + "ĠE ur", + "Ġuncon strained", + "Ġsub mit", + "Ġrep utation", + "at ar", + "ĠB ai", + "ari ans", + "ĠInt racellular", + "tre es", + "Ġwet ting", + "ĠFran ces", + "Ġel igibility", + "fold er", + "ĠSta ff", + "ok i", + "Ġstrengthen ed", + "ĠC ob", + "ter al", + "ĠY east", + "by e", + "dec oder", + "Ġrain bow", + "perturb ed", + "v c", + "Ġsupplement al", + "Ġbir ths", + "W O", + "con c", + "stit ution", + "hy brid", + "Ġk i", + "Ġhyp ere", + "ĠS MA", + "form ula", + "Ġund efined", + "na phth", + "Ġdecl ining", + "Ġshield ing", + "Y au", + "Ġre ver", + "ĠW ilk", + "Ġdec imal", + "H CO", + "ang ered", + "Ġeryth rocyte", + "ĉĉ ĠĠĠ", + "n uclear", + "Ġabnorm ality", + "P res", + "Par ticipants", + "ĠW agner", + "Ġfibr ils", + "Ġfet us", + "ĠEx press", + "requ est", + "min imum", + "ĠBo oks", + "het amine", + "us hes", + "ĠB ach", + "ĠD OS", + "lect ric", + "ĠTw een", + "ĠHug hes", + "Ġm artens", + "Ġn ematic", + "Ġexperiment ation", + "ĠPark er", + "Ġepis odic", + "Ġte lem", + "AD E", + "col umns", + "Ġfundament ally", + "en et", + "ĠV l", + "ear th", + "Ġquanti le", + "ĠRe plication", + "Ġcle ared", + "En ergy", + "Sm ith", + "Ġantidepress ant", + "m x", + "p mod", + "am id", + "Ġser otype", + "Ġundergrad uate", + "ĠA rizona", + "Ġp ushed", + "ul u", + "ĠN IC", + "Ġrhe ological", + "ome gal", + "ĠQ ing", + "or ch", + "ir med", + "ĠQu ery", + "Ġsand wich", + "Ġclinic ian", + "ĠEllip tic", + "ĠMe h", + "DE V", + "ĠDeterm ining", + "alc ogen", + "b ench", + "az ep", + "ĠMiss iss", + "ti zing", + "ĠR BC", + "Ġofficial s", + "T ag", + "k T", + "lu ence", + "ĠRo om", + "Ġlect in", + "bar a", + "k yl", + "ON D", + "ĠD ose", + "Ġpr ism", + "Ġreduc tive", + "ĠSpect roscopic", + "od ied", + "col one", + "ĠCON FIG", + "Ġbr ittle", + "in verse", + "ĠB uff", + "yt ocin", + "Ġform ations", + "ĠCon ventional", + "pre v", + "Ġferr ite", + "ĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġ", + "Ġadop ts", + "ĠMi ocene", + "man agement", + "ĠCR F", + "ĠHel m", + "Ġdoubl ed", + "ĠEFF ECT", + "Ġd ance", + "struc tions", + "ra it", + "if ers", + "ell ip", + "ut ting", + "pro f", + "ĠQ in", + "Ġab sc", + "Ġexplo its", + "Ġcy ber", + "def inition", + "ĠCoron ary", + "Ġdet erg", + "ĠPer ception", + "ĠCur ves", + "Ġnemat odes", + "Ġlist ening", + "Ġcatal ase", + "C oll", + "r é", + "isl ative", + "Ġarri ving", + "Ġviol ating", + "Ð ´", + "he tics", + "ĠJ ar", + "con cept", + "Ġbr ush", + "immun ity", + "Ġfinger print", + "res id", + "Ġelev ations", + "ock ets", + "Ġcatech ol", + "и Ñ", + "Ġprecipit ates", + "Ġsoc cer", + "ins ulin", + "Ġpurs ue", + "ĠI CA", + "ĠPol ice", + "ĠMur phy", + "T ask", + "ĠC oc", + "ĠH abit", + "ĠK P", + "Ġfl oral", + "Ġh un", + "Ġhydrogen ation", + "Ġsp ong", + "Ġch imeric", + "ĠK och", + "g on", + "ĠSch ur", + "ĠGre ater", + "R X", + "Ġc ing", + "ĠW altham", + "ang ling", + "Ġcoun ties", + "Ġlam ina", + "Ġco uncil", + "s ort", + "ĠB arc", + "ĠD ow", + "ĠZ eng", + "Ġdev ised", + "uit able", + "Ġmethyl ene", + "Ġsuperior ity", + "Ġepiderm is", + "Ġp rag", + "ĠP ED", + "threat ening", + "ish i", + "Ġe psilon", + "add ress", + "ENT AL", + "ĠB le", + "ĠAnton io", + "o other", + "ĠAg ar", + "Ġneighborhood s", + "Ġshorten ed", + "ST ATE", + "ĠSer ial", + "M AR", + "O U", + "Ġencaps ulation", + "ĠCons ortium", + "D r", + "pro file", + "Ġem itter", + "Ġnec rotic", + "ĠAut onomous", + "ĠPhosph orylation", + "min im", + "anth in", + "ĠS ph", + "ĠG ur", + "di hydroxy", + "dist ributed", + "ĠRP MI", + "st ones", + "Ġhyper fine", + "Ġis let", + "ĠS lo", + "plet ely", + "Ġin activated", + "ĠAgric ulture", + "Ġtrem end", + "Ġevery one", + "omp onent", + "Zn O", + "MP I", + "ĠDi amond", + "Ġ⣠¨", + "C ost", + "Ġdis abilities", + "in ver", + "ĠC ensus", + "ech o", + "Ġveget ative", + "Ġwilling ness", + "Ġrec ap", + "ĠConst raint", + "ĠP atrick", + "Ġover t", + "Ġmo ieties", + "or ax", + "ip pi", + "Di rect", + "Ġcar ies", + "Ġlocal ities", + "lat tices", + "ĠExpl oration", + "ĠA W", + "Ġloc king", + "Ġcoinc ident", + "Ġmultim edia", + "Ġtempor arily", + "ĠC aus", + "enc ia", + "Ġweather ing", + "ĠHelic obacter", + "ĠTh ings", + "hip s", + "m oving", + "Ġs igmoid", + "is in", + "ĠB ec", + "Ġmicro grams", + "bound s", + "ĠCol umn", + "Ġcommut ing", + "ĠJ en", + "Ġhour ly", + "M SC", + "Ġattend ance", + "ĠâIJ £", + "ĠE O", + "pro g", + "Ġrap amycin", + "ĠPredict ors", + "ĠRetrie ved", + "Ġsub species", + "Ġderiv es", + "ĠÄ ¤", + "ĠGener ating", + "ann ers", + "Ġvol at", + "Ġvis iting", + "ĠCalc ulations", + "ñ a", + "Ġdes ert", + "Ġexpect ancy", + "BM Cs", + "ĠExpl o", + "Ġtrav elling", + "ic um", + "Ġsub division", + "Ġcross linking", + "benz oth", + "ĠT on", + "RE N", + "Ġle th", + "rab bit", + "ĠAb ove", + "ul ted", + "Ġcon stric", + "J ones", + "z hou", + "ver n", + "ĠL ady", + "ĠBu ffer", + "ĠControll ing", + "Ġmulti scale", + "nik ov", + "acy cl", + "Ġprost hesis", + "A f", + "ĠCor ps", + "struc ted", + "G rid", + "in ning", + "old ing", + "Ġthi ol", + "ik ov", + "âĢ¢âĢ¢ âĢ¢", + "Ġgovern ments", + "rap ping", + "Ġthromb ocyt", + "L eg", + "R Y", + "ĠI celand", + "ocy cle", + "ĠMem orial", + "g ot", + "Ġid em", + "ĠBu ild", + "olip oprotein", + "D V", + "Ġph thal", + "rich ment", + "ĠHa em", + "Ġansw ering", + "ĠI J", + "Ġtrans gene", + "Ġre named", + "ĠImage J", + "Ġcass ette", + "Ġcoales cence", + "Ġcomp action", + "Ġwild life", + "Ġw ins", + "Ġsuper novae", + "enter ic", + "isp here", + "Ġtrack er", + "Ġevid ences", + "Ġcom orbidity", + "ĠR ules", + "ph asing", + "ĠLange vin", + "ĠF it", + "Ġpsy chiat", + "Ġbreak through", + "Ġch olinergic", + "ĠMet all", + "bre eding", + "itin ib", + "Ġsol o", + "abl ing", + "eli ef", + "osc ill", + "re v", + "ary a", + "Ġgood ness", + "ĠPB E", + "Ġa wards", + "Ġc rani", + "Ġphot ograp", + "aren ts", + "Ġfix es", + "r ÃŃ", + "ass uming", + "Ġcongru ent", + "ĠM other", + "ĠN ap", + "ĠPro c", + "Ġcategor ization", + "in ch", + "ĠH orm", + "ĠInter ventions", + "Ġnone quilibrium", + "Ġencryp ted", + "prim ary", + "i ens", + "l ac", + "ram s", + "Ġbo ards", + "ĠH ell", + "charg ed", + "Ġperi operative", + "em p", + "ĠInvol vement", + "R uss", + "un ivers", + "ĠD J", + "Ġdisag reement", + "Ġper t", + "Ġstrom a", + "Ġcalc ite", + "Ġrot ary", + "Ġmethyl transferase", + "Ġancest ry", + "ĠW itten", + "CR C", + "ure tic", + "ophy ta", + "provid ed", + "Ġcorresponding ly", + "big cap", + "ĠAg ilent", + "à «", + "ro oms", + "Ġdis ent", + "Ġdil utions", + "ĠMy el", + "Ġquas ar", + "Ġtil ted", + "Ġinternal ization", + "ĠPri vate", + "ĠFried man", + "Ġsevent h", + "ĠCl osed", + "CT C", + "g ren", + "ĠColomb ia", + "od el", + "Ġpoli tics", + "ĠMSS M", + "Ġm ate", + "Ġcom mod", + "ĠR us", + "Ġanest hetized", + "t ogether", + "ĠB CS", + "ew ski", + "romagn et", + "ĠC un", + "Ġcur ative", + "Ġim putation", + "Ġcarb ide", + "D FT", + "ns ic", + "be e", + "Ġspl en", + "ĠMary land", + "Ġoligonucle otide", + "ĠVeg et", + "buff ered", + "N ational", + "le tic", + "ĠS yl", + "Ġse ab", + "ardi al", + "Ġport ray", + "Ġaberr ations", + "Ġst orms", + "ĠSh an", + "ĠGen Bank", + "iss a", + "Ġc et", + "Ġben ch", + "ĠRecommend ations", + "Ġtri ples", + "Ġïĥ ¥", + "ĠNeu ros", + "Ġdisc om", + "se ason", + "ĠEx ec", + "chang ing", + "Ġarri ves", + "H ash", + "m RNA", + "Ġf ric", + "as a", + "ob ia", + "Ġpost synaptic", + "optim izer", + "ĠCloud s", + "Ġhyper sensitivity", + "v acc", + "ĠS ig", + "ph ilic", + "Ġground ed", + "ĠW an", + "ĠCal abi", + "ĠMach ines", + "Ġaxis ymmetric", + "ĠSte ve", + "Ġpull ed", + "ĠEx cel", + "Ġdiamond s", + "K R", + "W est", + "ĠD est", + "Ġann ular", + "Ġarch ive", + "Ġparench yma", + "ĠE H", + "ó pez", + "Ġunp ublished", + "Ġs outheastern", + "Ġn ests", + "dim ensions", + "lat itude", + "O rig", + "ec ed", + "ĠD raw", + "red shift", + "Ġam yl", + "omyel itis", + "W hy", + "c aro", + "i q", + "ass ess", + "ĠCont in", + "Ġchir ality", + "mat ical", + "Ġchaper one", + "Ġendometri osis", + "re lu", + "Ġconver ged", + "bro ad", + "ĠIter ative", + "Ġvascul ature", + "f und", + "ĠF ly", + "Ġanti genic", + "Ġmening itis", + "Ġent ails", + "hor n", + "Ġlocom otor", + "iz ard", + "Ġun even", + "par ity", + "pack et", + "tub ulin", + "Ġsew age", + "Ġdec entralized", + "Ġgra fted", + "Ġse p", + "ĠExt ensive", + "Ġspl ine", + "qu er", + "arch it", + "Ġprim ate", + "Ġïģ ±", + "pyrim idin", + "ĠS AP", + "Ġunder lie", + "Ġanalyz es", + "ĠC CA", + "rec ogn", + "IP T", + "Diff erent", + "ĠTE ST", + "Ġunf avorable", + "ed ic", + "ĠAb normal", + "pyrim idine", + "ur ine", + "embed ded", + "var ies", + "otrop in", + "Ġsem en", + "Ġtransmit tance", + "Ġab ras", + "Ġó ¸Ģł", + "Ġtriglycer ide", + "b undle", + "ĠY b", + "ĠCar r", + "Ġnam ing", + "We ight", + "Ġcondens ates", + "Ġn os", + "am ard", + "ver tices", + "EL S", + "id one", + "Ġcont est", + "Ġhead ing", + "ĠGal erkin", + "G V", + "ĠGl i", + "Ġfer mented", + "Ġb ilingual", + "Ġt icks", + "Ġk ary", + "rag al", + "ĠA ber", + "ĠYou Tube", + "UCT URE", + "b ranch", + "Ø ±", + "ĠF H", + "on oi", + "im otor", + "Ġver ifying", + "ĠCon ceptual", + "ĠDetermin ants", + "ur m", + "ur onic", + "ĠK au", + "ĠCon formal", + "Ġdrop ping", + "ĠFlow s", + "glu on", + "ag ain", + "ĠMR SA", + "war f", + "Ġemphas izes", + "Ent ry", + "ĠA SP", + "res ol", + "vent ricular", + "ĠâĨ Ķ", + "Ġoverex pressing", + "omegal ovirus", + "in oc", + "SC O", + "ĠPAR P", + "ĠSch ul", + "ĠCam b", + "ĠP od", + "ĠP un", + "ĠCompe tition", + "ĠG ATA", + "Ġmo on", + "Ġput s", + "angi ogenic", + "ĠRepublic an", + "ĠUb iqu", + "e ys", + "ĠG ong", + "arg er", + "ĠInter mediate", + "Ġinterp olated", + "Ġenlarg ement", + "Ġin struct", + "Ġr c", + "di oxo", + "ey e", + "ĠCar ls", + "ĠMeas ured", + "ir cles", + "ĠR af", + "Ġar b", + "ex amples", + "M i", + "ĠS tern", + "ĠF K", + "Ġmill isecond", + "ĠIR F", + "ĠEp ithelial", + "ed icine", + "el es", + "s ig", + "âĪ Ģ", + "ĠWi ener", + "b auer", + "ous es", + "Ġcol oured", + "ĠIncre ase", + "Ġtriglycer ides", + "Ġaeg ypti", + "ĠNumer ous", + "Ġretard ation", + "Ġinter cellular", + "ĠKle bsiella", + "ĠD ra", + "ĠD IC", + "ĠTh reshold", + "rain ment", + "Ġrepro ducing", + "Ġul cers", + "Ġa rousal", + "ĠH ills", + "Ġcal ves", + "ĠRes ervoir", + "ĠRad ar", + "Ġpsych osis", + "ĠFOR M", + "d uration", + "ĠAc ademic", + "c atal", + "oll a", + "ol ol", + "ĠC ron", + "ik o", + "Ġextrem es", + "ĠTryp an", + "Ġb ip", + "Ġal ginate", + "ĠH och", + "ĠBen nett", + "ĠH ippocamp", + "ĠGe ological", + "N evertheless", + "ĠH es", + "ĠAd ding", + "Ġextern ally", + "Ġsl ag", + "Ġte ach", + "ĠStan ley", + "controll er", + "ĠUn its", + "ĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ ĠĠĠĠ", + "Ġaer odynamic", + "oval ent", + "c ube", + "Å Ł", + "re quire", + "romo lecules", + "ir teen", + "Ġcl auses", + "Ġdef eat", + "pol icy", + "Ġfaith ful", + "Ġp q", + "ĠTan aka", + "ĠE ver", + "Ġun predict", + "aut y", + "ĠGAL AX", + "Ġt ide", + "ĠFilter ing", + "Ġeut han", + "mer ce", + "DE X", + "Ġnest ing", + "D N", + "IR T", + "ĠTh r", + "tis sue", + "Ġpal ae", + "Ï ©", + "Ġdil ated", + "Ġpin ning", + "R b", + "ĠS ap", + "rag onal", + "ĠS PR", + "ĠD ial", + "Ġac upuncture", + "di ameter", + "ĠPC B", + "Par ameters", + "ĠProf iles", + "transf ected", + "l iter", + "ĠR ights", + "Ġcontrib utor", + "ĠCor rel", + "Ġregression s", + "Ġsegment al", + "Sh ape", + "I AN", + "ec om", + "com ings", + "Ġhemorrh agic", + "op os", + "Ġrefrac tion", + "P FC", + "pro j", + "ov o", + "ĠDer ived", + "Ġundi rected", + "Ġl os", + "Ġeng aging", + "c ans", + "Ġdestr uctive", + "P op", + "Ġm akers", + "ĠW or", + "ĠAre as", + "vas ion", + "Ġpara formaldehyde", + "abin oid", + "c py", + "pro xim", + "Ġen amel", + "Ġpa ediatric", + "ĠChild hood", + "Ġp ectin", + "ofil m", + "Ġcarboxyl ic", + "Ġa usten", + "Ġun equal", + "ĠCount ry", + "Ġiter ated", + "Ġflank ing", + "Ġt raction", + "ans on", + "isc us", + "ĠDav ies", + "ra ham", + "ter ozoic", + "ĠBr ass", + "O c", + "Ġun ification", + "met er", + "ĠNe on", + "bu ilding", + "ic ting", + "Ġjus tification", + "Pri or", + "Ġfir ms", + "Ġeduc ated", + "Ġinters ecting", + "Ġboost ing", + "P ass", + "m ember", + "con tains", + "ran o", + "rel ax", + "ĠCollabor ative", + "Ġp x", + "Ġseed ing", + "cri pts", + "ine z", + "ome res", + "Ġsib lings", + "ang ing", + "fer t", + "Ġrecover ing", + "p ure", + "Ġs d", + "ĠV ul", + "ped ance", + "Ġfight ing", + "S uper", + "ĠI to", + "Ġper imeter", + "ĠInhib itors", + "electro de", + "en abled", + "f b", + "ĠP Cs", + "Ġn ausea", + "ĠCon version", + "Ġsl a", + "Ġinver tebrates", + "ĠBri an", + "Ġcontig uous", + "ĠACKNOWLED GM", + "ur face", + "Ġco ars", + "ĠLe h", + "ĠComp ression", + "cy cles", + "Ġsin h", + "ĠOcc up", + "st rength", + "Ġcon str", + "Ġpestic ide", + "Ġb isp", + "ĠT n", + "Ġparent heses", + "deg rad", + "Ġhypergly cemia", + "P W", + "k j", + "ec ological", + "Ġth y", + "Ġele g", + "ĠSyn aptic", + "scal ed", + "ti ty", + "Ġequ ity", + "Ġblock chain", + "ĠLith ium", + "Ġsp ark", + "Ġen titled", + "Ġconven tions", + "Arg ument", + "Ġre tail", + "Ġne oplastic", + "Ġdamp ed", + "ĠSurve illance", + "ĠAn na", + "Ġspace times", + "ing es", + "ah ashi", + "ĠInf ections", + "Ġneglect ing", + "Ġevapor ated", + "vast atin", + "Ġg h", + "ĠN LP", + "Ġph ones", + "Ġlif ted", + "Ġdivis ible", + "Ġdur ability", + "os ited", + "Ġexcit ability", + "Ġbuoy ancy", + "Ġuncont rolled", + "b ran", + "ĠP he", + "Ġimmun ocomp", + "Ġevent ual", + "Ġclass room", + "Ġmicro graphs", + "Ġre charge", + "et tes", + "ĠD iver", + "ĠD all", + "Ġmet ac", + "Ġneuro endocrine", + "top ology", + "ĠHaw king", + "oms on", + "ĠHar ry", + "m outh", + "Ġdec iding", + "Ġunc overed", + "Ġgold en", + "ĠCast le", + "Ġfid ucial", + "A ware", + "ĠG an", + "era hertz", + "ĠSat urn", + "L N", + "Un it", + "ĥ Ĺ", + "Ġbind er", + "IN FO", + "ĠTem per", + "ip el", + "Ġnumer ator", + "Ġwebs ites", + "Ġthreat ened", + "Ġremn ants", + "ĠFinn ish", + "h of", + "med ia", + "concent ration", + "ĠRe ed", + "ĠLeishman ia", + "Ġmulti functional", + "rac y", + "Ġdistrib ute", + "ĠDec ay", + "Ġgr inding", + "L oss", + "MP L", + "ĠL akes", + "ĠQ R", + "ĠStruct ured", + "ĠMal aria", + "Ġflavon oid", + "Ġtow ns", + "op ia", + "ĠV ec", + "oth y", + "Ġsing les", + "Ġpenet rate", + "ĠP ig", + "ie ved", + "Ġderiv ations", + "Ġdiscom fort", + "af enib", + "ĠLegend re", + "ĠP ax", + "ĠM X", + "ĠExt rem", + "ĠFore ign", + "ĠCour se", + "ĠH it", + "v age", + "Ġcl ique", + "Ġcompens atory", + "U ser", + "Ġdraw s", + "ĠProt ective", + "Ġalloc ate", + "ĠP ant", + "Ġd ash", + "Ġpar al", + "ĠCirc ulating", + "ĠHist one", + "ĠÅ «", + "Ġproj ec", + "ĠA AA", + "ĠP MS", + "gl acial", + "ĠMe eting", + "ĠAntib iotic", + "ategor ical", + "Ġatten uate", + "P ower", + "ow icz", + "ĠDef ault", + "Ġmar sh", + "plas m", + "ĠPath ology", + "ĠE f", + "L ys", + "fl ies", + "Ġinterview ed", + "ĠQ A", + "Ġimp uls", + "Ġpap illary", + "d R", + "u h", + "ĠJ ing", + "Ġrescal ed", + "e fficiency", + "Ġe f", + "ĠE isen", + "Ġattack ed", + "Ġopt o", + "Ġspec ulated", + "ha z", + "Ġide ally", + "ymen optera", + "Ġl r", + "ĠI z", + "res ource", + "ĠFac ility", + "ĠAc quisition", + "Ġpost ural", + "auti ful", + "Ġging ival", + "Ġper taining", + "ĠExt ra", + "ĠProgram me", + "hes us", + "ferm ion", + "Ġstead ily", + "Ġtermin us", + "P arser", + "ĠIn clusion", + "ĠWu han", + "Ġrepe titions", + "d one", + "ĠC ep", + "Ġun structured", + "ĠCol lectively", + "Ġsett ling", + "Ġj aw", + "ĠUn i", + "Ġrest oring", + "urt les", + "F ull", + "Ġdynam o", + "IG O", + "ĠB AT", + "ov á", + "ven ues", + "ĠPer haps", + "sens ing", + "ĠI schem", + "odem ographic", + "S s", + "ĠL und", + "Ġel ite", + "prot ocol", + "ĠChrist opher", + "bas ic", + "Ġp uber", + "Ġmagne tism", + "v ars", + "in ducing", + "Ġd ated", + "Ġen emy", + "ĠSt op", + "s ocial", + "Ġd ÏĦ", + "ĠB un", + "Sm all", + "pur pose", + "Ġh unting", + "CP U", + "ĠJun ior", + "RE L", + "Ġcontrac tile", + "Ġsilic one", + "adren ergic", + "b z", + "Ġf us", + "if ted", + "se p", + "âĪĴ âĪŀ", + "Ġdr um", + "-------- --", + "ĠTreg s", + "it arian", + "cent ury", + "âĬ ¥", + "Num er", + "ĠB enz", + "Ġcommunic ating", + "Ġp aternal", + "ĠF GFR", + "Ġâ Ĥ¬", + "Ġdevi ate", + "f re", + "Ġmol ten", + "Ġstandard ization", + "Ġfunctional ities", + "ĠPaul o", + "Ġbuck et", + "ĠConcent rations", + "ĠK um", + "Ġmim icking", + "D rop", + "zo a", + "ĠNucle i", + "b rack", + "ec olor", + "Ġcar n", + "Ġveter inary", + "Ġchem otherapeutic", + "Ġfer ment", + "last ing", + "ĠRog ers", + "ier i", + "Ġconver ters", + "ĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ ĠĠ", + "ĠRep air", + "Eu rope", + "T IME", + "Ġt ies", + "ĠP IN", + "Ġtrib ut", + "Ġhomogen ization", + "exc itation", + "at ization", + "ĠR ash", + "Ġpre cession", + "á s", + "Ġspik ing", + "ĠGrass mann", + "min ister", + "Ġfactor ial", + "ĠDe ut", + "sam pled", + "Ġeukary otes", + "overl apping", + "ag glut", + "Ġpres cribing", + "Ġc ro", + "om echanical", + "iz a", + "ĠMan ufact", + "n ative", + "urs ive", + "ĠIss ues", + "Ġstrept omycin", + "en di", + "ĠS pr", + "ce q", + "arg inine", + "ix on", + "ĠFound ations", + "Sing le", + "Ġox al", + "Ġhyd rate", + "Iter ator", + "k ii", + "amin ated", + "Ġspr ings", + "ol n", + "ĠSet up", + "Ġrip ening", + "Ġtheore tic", + "Ġcf g", + "μ L", + "G ordon", + "S K", + "Ġn ations", + "Qu ery", + "Ù ħ", + "Ġf ores", + "requ encies", + "ĠPh armaceutical", + "ĠAll ocation", + "otyp ical", + "ĠPil ot", + "th ora", + "ĠV and", + "Ġsyring e", + "ĠR AP", + "rom etric", + "Ġïģ ´", + "Ġcit ations", + "wo uld", + "Ġnorthe astern", + "compar ison", + "l ocus", + "et he", + "ĠK B", + "Ġhomolog s", + "Ġencephal itis", + "Ġz ig", + "Ġinc entive", + "Ġconf idential", + "Ġves tibular", + "ĠOT Us", + "Ġsynov ial", + "ĠRel ativity", + "Ġsub divided", + "che z", + "Ġlik ewise", + "ĠPD MS", + "ĠÅ ł", + "Ġsoci eties", + "ocyan ate", + "g ia", + "Ġlocal ize", + "Ġlact ation", + "Ġnod ule", + "ĠCO R", + "Ġharbor ing", + "ĠE QU", + "har vest", + "Ġband gap", + "r k", + "Ġres istor", + "Ġy e", + "ĠAs ymmetric", + "Ġpropag ators", + "Ġdiagnos ing", + "ĠAff airs", + "Ġeject a", + "Ġis omer", + "Ġi x", + "Ġfol iation", + "Ġcapac itors", + "Ġc ad", + "ĠNeut roph", + "pl iance", + "Ġcompress ible", + "ĠHun ter", + "ĠM Z", + "ĠWe ib", + "Ġnon coding", + "Ġmountain s", + "Ġadver tising", + "ale z", + "b right", + "lim sup", + "C i", + "ĠNe v", + "ĠStrain s", + "ost omy", + "op al", + "Ġconcaten ated", + "ĠPer f", + "CH O", + "Ġt urtles", + "ĠF ra", + "Ġall ogeneic", + "Ġuns uccessful", + "Y M", + "er ver", + "Ġc uc", + "Ġf ires", + "ch art", + "Ġinter rupted", + "Ġdec ides", + "Ġa uction", + "ĠUn til", + "ĠAT G", + "Ġdi am", + "magn itude", + "Ġd l", + "Ver tex", + "mon t", + "Ġfem tosecond", + "Par ams", + "Ġlys ate", + "is hers", + "ĠP AT", + "ĠK ev", + "ĠKn ock", + "Ġgro ove", + "L u", + "ĠJoh ann", + "Ġreplic a", + "ĠMATERIAL S", + "Ġl ots", + "Ġgener ically", + "ĠAl tered", + "ĠId entity", + "Ġunfold ed", + "C ES", + "ing ular", + "ĠF raction", + "ĠPro liferation", + "ĠVi enna", + "ac ia", + "pl ess", + "ĠSe vent", + "Ġturb ines", + "lys ine", + "Ġperox is", + "AR P", + "ĠEp is", + "ĠSY BR", + "Bu ilder", + "Ġspher ically", + "Ġdef end", + "Per formance", + "Ġmort ar", + "ĠCon cepts", + "work s", + "Ġreinfor ce", + "á ¹", + "Ġc us", + "ĠC IF", + "ĠAgric ultural", + "c rystalline", + "r ish", + "Ġref erenced", + "Ġact ress", + "Ġbounded ness", + "Si C", + "Ġà ¢", + "Ġj ack", + "Ġterm inate", + "ĠJ A", + "ĠKr ish", + "M MP", + "k x", + "ĠP SR", + "end l", + "W HO", + "ĠS ão", + "ĠC ultural", + "ĠE h", + "ul is", + "vi k", + "pr ises", + "ix el", + "ĠMet rics", + "Ġdiscontinu ities", + "ĠU ne", + "SC R", + "Ġproject ing", + "ĠOrig inal", + "ĠHum ans", + "transcription al", + "H K", + "ĠJ ain", + "atisf action", + "mes enchymal", + "Ġpyram id", + "Ġascorb ic", + "g ame", + "Ġno un", + "otox ins", + "p eptide", + "Ġglass y", + "Ġtalk ing", + "D em", + "ĠSch ro", + "ĠAssum ptions", + "Ġð x", + "Ġaneurys ms", + "M ASS", + "ĠH ou", + "ex posure", + "ĠL LC", + "Ġno ises", + "CT G", + "ĠElement ary", + "fl ip", + "Ġdys p", + "Ġmess enger", + "ĠImport ant", + "Ġim poses", + "Ġorgan elles", + "assert Equal", + "Ġjus tif", + "uc ine", + "Ġform ic", + "ormal ization", + "ĠRad ial", + "ĠCur ve", + "ĠCro hn", + "Ġbrow ser", + "Ġeff usion", + "Ġhand les", + "var sigma", + "Ġspecial ists", + "Ġpain ful", + "Ġerythemat osus", + "Ġf en", + "nitrop henyl", + "Ġleg acy", + "ĠQ Ds", + "rap per", + "Ġmon otherapy", + "ĠBel t", + "Z Z", + "Ġs intered", + "en edi", + "H b", + "t v", + "ĠN as", + "ov is", + "Ġmuc in", + "Ġacceler ates", + "Ġacqu iring", + "l uc", + "Ġdil aton", + "ĠPit ts", + "Ġequiv ariant", + "ĠL yman", + "ĠY a", + "Ġprog ressed", + "ĠAfter wards", + "ĠCH AR", + "D on", + "Ġhist ologic", + "Ġcircuit ry", + "p ene", + "op res", + "ĠSte fan", + "Ġsemic lassical", + "m und", + "ĠW aste", + "B Q", + "Ġadip onectin", + "Ġun seen", + "Ġbiom echanical", + "Ġhazard ous", + "r uctive", + "x yl", + "op f", + "Ġpr ion", + "ĠInf inite", + "Ġtrac ers", + "ĠHar rison", + "Ġfibrin ogen", + "Ġhydro lys", + "Ġis lets", + "Ġparallel ism", + "Sp ect", + "Ġimper ative", + "Ġc ured", + "ĠD SB", + "ide finite", + "ick er", + "Ġdiver gences", + "ĠShap iro", + "ab d", + "ĠL um", + "ĠV D", + "Ġf isheries", + "ge on", + "cop enia", + "ĠCl ay", + "Ġmaxim ized", + "ĠGre y", + "ĠB atch", + "Ġinf est", + "Ġam ple", + "Ġest ate", + "ĠSup reme", + "A O", + "is ia", + "ĠSm ad", + "Car lo", + "ĠSub st", + "Ġmon oidal", + "Ġnumer ic", + "Pl ot", + "Ġdyst rophy", + "hypert ensive", + "Ġst ool", + "als y", + "Ġche ese", + "n ih", + "Ġb ought", + "ĠS Q", + "Ġcl ues", + "Ġme iotic", + "Ġgo ats", + "ĠGTP ase", + "Ġrescal ing", + "N UM", + "ic ing", + "ĠÄ Ģ", + "Ġpret ty", + "lig and", + "En glish", + "ĠIntellig ent", + "E very", + "ĠPoli tical", + "ent on", + "Ġpass ages", + "ĠRemark s", + "s b", + "N etwork", + "ĠL RR", + "Ġcur l", + "urs ion", + "ĠA ver", + "ĠG LP", + "here n", + "at an", + "IC ENSE", + "Ġlate x", + "E MI", + "qu asi", + "ĠO m", + "Ġreview ing", + "Back ground", + "Ġs om", + "Ġsnap shots", + "b row", + "w ho", + "ĠT ail", + "ĠM SM", + "ĠG m", + "Ġph i", + "ren cy", + "separ ated", + "Ġg ig", + "os ides", + "Ġpe an", + "Ġappe aling", + "P U", + "n k", + "Ġqu er", + "ĠCh arg", + "ĠMo lecules", + "local ization", + "I dx", + "l ap", + "ĠT ax", + "ĠExp onential", + "ĠInhib itor", + "ĠBiom edical", + "ureth ane", + "le rene", + "rogen esis", + "ĠL ai", + "ĠAgg regation", + "ĠCa Cl", + "Ġsens ible", + "Ġcon junc", + "pa per", + "ĠCov id", + "ĠProced ures", + "Ġk new", + "Ġset ae", + "ĠAl le", + "ĠEx cept", + "Ġpres ynaptic", + "flow er", + "Ġultrason ography", + "Ġent ertain", + "i ors", + "ĠE ry", + "ĠIn teger", + "Ġrep ressor", + "Ġlater ally", + "Ġcomplement ed", + "T AG", + "ĠA round", + "ĠL ister", + "bit rary", + "back ward", + "Me V", + "Ġwh isk", + "AM s", + "ĠBul k", + "Ġqu iver", + "Ġdam aging", + "ĠQuantif ying", + "Ġsup rem", + "t el", + "Ġt ear", + "ot ers", + "vid in", + "Ġtub ules", + "Ġips ilateral", + "is ive", + "Ġsuit ably", + "ri el", + "Ġtub er", + "Ġfav ors", + "Ġcen tim", + "Ġtrans versal", + "ĠCH O", + "Ġtrim ester", + "C AC", + "c ognitive", + "ĠU TC", + "put e", + "Ġmid line", + "am ers", + "eval uation", + "D av", + "Ġb ags", + "tim er", + "Ġshort comings", + "ĠEr d", + "Ġdiscrim inator", + "A nt", + "s izes", + "Ġb ist", + "ing ual", + "ĠC ategory", + "Ġpuls ars", + "ĠSchw artz", + "ĠD rop", + "Sequ ence", + "Ġt ann", + "ĠSympt oms", + "D ict", + "ĠB lu", + "Sup plemental", + "Ġdis abled", + "ĠK oz", + "Ġinv oked", + "ĠC Q", + "ĠConn ectivity", + "Ġtelescop es", + "os o", + "Ġphyt ochemical", + "Ġorthogon ality", + "Ġinvis ible", + "ĠS CF", + "ĠA void", + "ĠH us", + "mic ron", + "atern ity", + "Pro ject", + "Ġadv ancing", + "ĠLorentz ian", + "S a", + "t Ãŀ", + "ĠU P", + "Ġar ts", + "Ġz er", + "ask et", + "Ġappe al", + "n ick", + "ĠCl oning", + "Ġsw ap", + "Ġphospholip ids", + "b g", + "ot hel", + "asc o", + "T rack", + "Ġsub manifold", + "Off set", + "ĠB ird", + "problem s", + "D Cs", + "Ġd ow", + "Ġde ionized", + "Ġsub class", + "Ġpubl ishing", + "ĠCar ter", + "Ġsyn ergy", + "Ġweak ened", + "ĠGl as", + "ĠP ie", + "hen ko", + "Ġsetup s", + "ĠBern stein", + "Ġà ¿", + "ĠSh u", + "ĠChang ing", + "os ov", + "ĠMet eor", + "in th", + "ra h", + "par amet", + "ren a", + "Ġnew borns", + "isc he", + "rot ating", + "Ġconf ident", + "f ac", + "ĠT err", + "Ġline width", + "IC P", + "thon y", + "Ġl anes", + "Ġsm oother", + "mon y", + "ĠCN Ns", + "P ort", + "Ġtrans iently", + "Ġsur geries", + "Ġsubm erged", + "Ġp uncture", + "Ġd ichlor", + "Ġsystematic s", + "Ġcontig s", + "Ġresid ing", + "B W", + "E O", + "G old", + "ion ate", + "voc ab", + "d W", + "ST AR", + "ĠP LC", + "ath i", + "ĠInf ectious", + "L ight", + "á »", + "ĠR al", + "Ġpropag ates", + "ĠLik elihood", + "h ill", + "cur l", + "check point", + "ra x", + "Ġv ancomycin", + "ĠU SD", + "ophe les", + "Ġfil tr", + "Ġstoichi ometry", + "âĶĢ âĶĢ", + "ĠN ad", + "access ible", + "Ġto y", + "Ġn ude", + "ĠS ut", + "ess ential", + "ĠO L", + "Ġper tin", + "Ġrec ur", + "Ġcap ill", + "Ġcomput able", + "Ġsuc tion", + "Ġsoft ening", + "ĠE SI", + "Ġmon itors", + "Ġpy ridine", + "ĠSens ors", + "ĠCombin atorial", + "at ta", + "ĠA MS", + "ĠD ul", + "ple teness", + "E th", + "Ġà »", + "Ġexc ised", + "ĠDiab etic", + "ĠI owa", + "Ġimmunost aining", + "Ġillness es", + "Ġenum er", + "ĠIran ian", + "Ġth umb", + "orphism s", + "Ġlegitim ate", + "l g", + "ĠS VD", + "Ġdes k", + "Form at", + "B on", + "Ġg arden", + "Ġinter personal", + "Ġel bow", + "ĠDem onstr", + "Ġnons pecific", + "F erm", + "ival ently", + "phthal ene", + "AR GET", + "Val id", + "Ġsun light", + "Ġresc ued", + "D AR", + "ĠIn variant", + "Ġid le", + "Ġalkal oids", + "scal es", + "s es", + "ob icity", + "be at", + "Ġcentrifug al", + "analy tical", + "p v", + "Ġt utorial", + "ĠN ation", + "gener ator", + "Ġcollision al", + "ĠC ME", + "Ġsc rap", + "ĠQ SO", + "Ġw ax", + "ĠSc enario", + "Ġminim izer", + "ĠMD PI", + "Ġprostagland in", + "ol ites", + "ocy steine", + "Ġcompac tification", + "Ġfrail ty", + "ops in", + "Ġjun ior", + "lo ud", + "Ġtit led", + "Ġeconom ically", + "th iophene", + "ĠInvestig ating", + "ĠE sp", + "Ġel usive", + "Ġmal ware", + "ĠTH P", + "imid azole", + "Ġre tains", + "ĠM IR", + "ff l", + "j ac", + "ĠP ART", + "ĠD CM", + "trans port", + "MAP K", + "Prob lem", + "S u", + "Ġdel im", + "Ġpsych ometric", + "vit ably", + "Ġhyper geometric", + "Ġuter us", + "Ġanaest hesia", + "ĠA venue", + "Ġmean ings", + "Ġrapid ity", + "Ġdend rites", + "g rain", + "ĠN ile", + "Ġfac ies", + "Ġpip elines", + "ĠCamp ylobacter", + "ĠMemb ers", + "benzo ate", + "Requ est", + "Ġp k", + "Ġref used", + "c aus", + "ĠS ay", + "l ane", + "ĠP SO", + "Ġgather ing", + "Ġrefrig er", + "R CC", + "Ġfib ronectin", + "hel p", + "ĠInt ensity", + "CL C", + "Q ue", + "el ly", + "Ġillumin ated", + "Ġpedest rian", + "ĠM ercury", + "Ġafford ed", + "Ġpathophys iological", + "ĠN GS", + "ass a", + "Ġend ors", + "Ġsens ation", + "Ġstream flow", + "av in", + "ĠGABA ergic", + "Ġreti rement", + "C ells", + "oc a", + "Ġoptim izations", + "Ġdig raph", + "ĠAu tism", + "oct urnal", + "osc ience", + "ĠEll is", + "ĠA j", + "ĠW SN", + "Ġshoot ing", + "i per", + "î Ħĥ", + "ĠWe ather", + "Ġrece ptive", + "Ġquar tic", + "ocycl ic", + "P ATH", + "size of", + "Ġmel ts", + "Ġdip oles", + "Ġbim odal", + "summ ary", + "Ġins omnia", + "opy ran", + "Ġwrap ped", + "ĠJos é", + "A H", + "c ia", + "Ġob eys", + "ĠK ay", + "inter vention", + "Ġrout er", + "ĠDrug s", + "ow ska", + "ĠAr r", + "ĠCap tain", + "ĠT MS", + "ad v", + "Ġbo at", + "Ġtrust ed", + "se ver", + "ill ars", + "ĠMiss ouri", + "Ġequival ents", + "ĠHar vard", + "ĠClark e", + "reson ant", + "rad y", + "trig gered", + "Ġc left", + "Ġun ic", + "Ġbrain stem", + "Ġthrom bin", + "ĠF light", + "Ġsection al", + "Ġconcaten ation", + "Ġcantile ver", + "et on", + "Ġdec ode", + "of acial", + "Ac tion", + "ĠIll ustration", + "ver tical", + "ch all", + "ĠReg istry", + "M AT", + "Ġcons on", + "Ġneo adjuvant", + "ĠW istar", + "ĠIm per", + "Ġal titudes", + "Ġsub population", + "ĠSc ene", + "tensor flow", + "s low", + "Ġh int", + "Ġbeam forming", + "e in", + "Ġimp regn", + "ĠRF ID", + "ĠAnaly zing", + "ĠP ent", + "ĠD NS", + "ĠG ilbert", + "Ġcr ater", + "Compar ing", + "Ġb f", + "Ġfl ights", + "Ġmal nutrition", + "SM C", + "Ġeryth rop", + "ĠTum ors", + "T x", + "Ġis ospin", + "ĠK ub", + "ik ing", + "Ġcorticoster oids", + "urs or", + "ĠBur g", + "in spired", + "ĠI gn", + "Ġmy cel", + "pred iction", + "method s", + "C opy", + "ĠR W", + "ĠK night", + "Ġdem ethyl", + "ì Ħ", + "Ġc ili", + "Ġb es", + "ĠE ck", + "Ġdilat ation", + "Ġan imation", + "ab stract", + "Ġcircum vent", + "Ġinoc ulum", + "S eg", + "ĠC aps", + "ere rs", + "PL S", + "ĠPe er", + "Ġver ifies", + "ateg y", + "ogene tics", + "Ġoligonucle otides", + "rac tical", + "Ġdiver ges", + "ĠStan ford", + "ĠA i", + "Ġweigh ing", + "T g", + "re infor", + "ĠA lam", + "qu iry", + "ĠN ob", + "Ġlinear ization", + "ĠV enez", + "ne xin", + "level s", + "L ip", + "ĠPat el", + "ĠMagn itude", + "e titive", + "ĠE agle", + "Ġsp utum", + "ĠCO S", + "Ġincub ator", + "U l", + "ĠRec eptors", + "ĠSch ott", + "GC G", + "ĠZe iss", + "ĠEnt anglement", + "ĠVacc ine", + "or ted", + "Ġn b", + "ĠS j", + "ĠM rs", + "Ġcal f", + "Ġinte grability", + "ĠPh oton", + "Ġgon dii", + "ĠM IL", + "Ġal iph", + "ĠD ip", + "fall s", + "c trl", + "k u", + "et ent", + "pl t", + "Ġpers isted", + "ĠMan ager", + "Ġprerequ isite", + "f illing", + "ĠM EA", + "S ym", + "ĠG rain", + "Ġduct al", + "ĠT ODO", + "Ġaff inities", + "Ġdegener ative", + "ĠF itz", + "ov ar", + "ĠTri ple", + "Ġdend rim", + "ĠFrank lin", + "m ag", + "ot ely", + "Ġstabil izes", + "Ġc ash", + "ĠS quad", + "Ġch ampion", + "PD B", + "Ġur g", + "Ġalcoh olic", + "Ġt ar", + "yl ed", + "V ersion", + "Ġs ale", + "ĠM LP", + "out er", + "Ġsimpl ifying", + "ĠExt ract", + "Par am", + "ĠRest ric", + "Ġtract able", + "ĠArch ive", + "Resp onse", + "AD DR", + "Ġcommut ation", + "R ich", + "ĠAndrew s", + "Ġosteocl ast", + "rom ic", + "ĠSh ift", + "Ġacceler ometer", + "ĠS ent", + "Ġch ances", + "ost ing", + "Ġmeth acrylate", + "Ġglu ons", + "Ġôı ½", + "Ġpolyg ons", + "ĠRCT s", + "Ġinf ancy", + "Ġproceed ed", + "ĠHor izontal", + "C OR", + "Ġc aching", + "ĠN HS", + "ph obic", + "ĠX MM", + "Ġmicrobi ological", + "G MP", + "Ù Ĩ", + "ĠT SS", + "ĠS ul", + "ĠF act", + "ĠW E", + "Ġcertain ty", + "ens itivity", + "Ġdecon volution", + "ĠG ain", + "Ġbl ots", + "Ġsee ks", + "Ġcos h", + "ennes see", + "Ġsl ave", + "ĠT ran", + "Ġtrans pose", + "re ated", + "Ġsh ading", + "ĠB U", + "ĠO V", + "ĠLo ok", + "Ġcomprehens ively", + "ĠFred er", + "Hand ler", + "f ibr", + "Ġmiss ense", + "target s", + "prom oting", + "ĠP ep", + "var pi", + "ĠHar monic", + "ĠA IS", + "Ġmon ocyt", + "Ġthin ning", + "Ġpherom one", + "W ater", + "an ase", + "ĠS ang", + "Ġsub structure", + "w p", + "ĠK ansas", + "DE BUG", + "ĠPro be", + "Ġpattern ed", + "cle an", + "Ġbro iler", + "odext rin", + "a ided", + "op rol", + "ubl in", + "in um", + "Ġan atomic", + "Ġpl ating", + "ar ro", + "uc al", + "Ġspeed up", + "Ġhaem orrh", + "eptid ase", + "Ġs agittal", + "Ġin tim", + "ĠF ISH", + "Ġsc arc", + "AT CC", + "inc or", + "Ġser ological", + "ent e", + "Ġsh ale", + "Ġover fitting", + "ĠEx cess", + "ĠAL P", + "P ool", + "d ry", + "y u", + "ĠPM MA", + "ĠHyp oxia", + "n othing", + "chest ra", + "colone qq", + "Ġb ibli", + "ĠEX PECT", + "B AL", + "et han", + "ĠâĪ ĺ", + "Ġj ourney", + "Ġbi ocompatibility", + "P AN", + "Ġb on", + "ĠR oh", + "Ġpolar isation", + "Sp in", + "id ences", + "ĠB CR", + "ĠH IP", + "ĠTh ick", + "Ġrecogn izes", + "Ġs ar", + "Ġam end", + "ques tions", + "Ġcareg iver", + "ĠMari e", + "Ġmetalloprotein ase", + "Ġal dehydes", + "Ġinter neurons", + "Ġtetra hedral", + "gue z", + "Ġquasipar ticle", + "Ġo t", + "decre asing", + "st re", + "Ġphot operiod", + "Ġprior iti", + "Ġap o", + "Ġimmunosup pression", + "ĠPier re", + "L PS", + "Ġcl umps", + "ĠPl ane", + "Ġturb idity", + "Ġpollut ant", + "Ġbi och", + "ĠT RE", + "Ġdesign ers", + "Ġrend ers", + "Ġrepl aces", + "ĠP LS", + "Ġhum oral", + "B as", + "re ira", + "ĠA edes", + "vit amin", + "cur ves", + "ocic eptive", + "Ġin disp", + "Ġox y", + "Ġed ible", + "ĠMes enchymal", + "ĠDeg ree", + "Å ¾", + "ĠO ak", + "ĠBhat t", + "on so", + "ĠS BP", + "ĠA ux", + "Ġmar tingale", + "ĠMicrobi ota", + "g low", + "Ġex ud", + "ap olis", + "Ġsome how", + "Ġcent red", + "Ch annel", + "ĠNormal ized", + "il itation", + "Ġtranscript ase", + "Ġcry o", + "predic ted", + "ĠD AG", + "Ġr f", + "end or", + "INT ER", + "ĠMes h", + "ĠFund ament", + "y cle", + "Ġprim itives", + "radi ated", + "Ġr ho", + "enes ulf", + "ĠF SH", + "ĠE cos", + "local ized", + "Ġenter prise", + "cephal us", + "Ġcarc ass", + "A Y", + "ec urity", + "ĠT MD", + "Ġl b", + "ĠA eros", + "ĠM ER", + "At tr", + "ĠA CL", + "ĠBar b", + "c out", + "Ġde oxy", + "ati os", + "Ġpers ists", + "Ġviol ent", + "Ab elian", + "Ġell ips", + "ion g", + "Ġsuccess or", + "ĠGonz ález", + "l iving", + "ĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ ĠĠĠĠĠĠĠ", + "iment in", + "Ġcaps ules", + "V IS", + "ĠP OP", + "arithm ic", + "O O", + "w l", + "ino ic", + "ĠCent ers", + "robl asts", + "th ose", + "ĠM J", + "Ġfron ts", + "Ġun int", + "Ġfac ile", + "co herent", + "av our", + "cep tive", + "ta h", + "Ġrelated ness", + "d E", + "un gen", + "## ###", + "Ġam phi", + "ĠGu y", + "st ars", + "ect om", + "Ġlay ing", + "Ġsp ider", + "AC s", + "Ġseed ling", + "Ġdu plicated", + "ic he", + "ĠM ST", + "gr ass", + "Ġprophyl actic", + "e ks", + "Ġl aryngeal", + "ĠS per", + "ĠW als", + "Ġcho lec", + "ĠPlan et", + "ĠHEP ES", + "Ġdi ploid", + "const raint", + "Py x", + "AC h", + "ĠCu i", + "ĠSh ared", + "ĠC and", + "ĠG ö", + "Ġdet ached", + "Ġpass engers", + "Ġaliph atic", + "Ġp our", + "Ġaccess es", + "ĠWal d", + "Ġdecor ated", + "Ġcaroten oids", + "ues tions", + "ĠImp acts", + "S AT", + "ar u", + "ĠP ir", + "ĠCon figuration", + "ĠCong o", + "ĠL ing", + "Ġdes ic", + "Ġmac rom", + "Ġlack ed", + "Ġencompass es", + "Ġp umped", + "ĠFor ty", + "rex ate", + "ifferenti ated", + "Ġn oble", + "Ġrad ion", + "Ġimmig rants", + "Ġbiodegrad able", + "Ġmig rating", + "arg v", + "CO M", + "ĠObserv ational", + "Ġcann abis", + "y ama", + "Ġconcent ric", + "Con n", + "tal ion", + "Ġrespond ers", + "uten ant", + "ĠT rim", + "Ġcontrib utors", + "Ġcontrac ted", + "ĠXen opus", + "Ġlo ud", + "ĠEnh ancing", + "Ġinfarc t", + "Ġo k", + "Ġas ks", + "rel in", + "Ġillustr ative", + "vd ash", + "d g", + "Ġf oc", + "Ġl ivers", + "ĠO tt", + "ĠT SP", + "log ger", + "depend ing", + "Ġdis proportion", + "Ġint ric", + "Ġimmun ized", + "vare z", + "Ġsal ic", + "ĠInstit utes", + "KE Y", + "Ġend oscopy", + "er k", + "el iness", + "ĠS ag", + "ath yroid", + "Ġacid ity", + "aro v", + "ĠVor onoi", + "Experim ental", + "Ġg ently", + "Me asure", + "ïĺ º", + "Ġw onder", + "ĠP ancreatic", + "ĠH ispanic", + "ĠE ug", + "re ducing", + "tain ment", + "Ġsur prise", + "Ġ æ", + "cr iter", + "ĠHypert ension", + "ti que", + "ĠC ris", + "comp atible", + "ens on", + "Ġdistribution al", + "ĠN AT", + "wid ths", + "Ġisother ms", + "ĠP rad", + "Ġbi odies", + "Ġorb ifold", + "ĠE OS", + "Ġat ax", + "ĠB od", + "ĠN MD", + "Ġmon oxide", + "ĠUk raine", + "f oli", + "ĠD ro", + "Ġun available", + "Ġbr ighter", + "âĬ Ĺ", + "ometh ane", + "Ġd ream", + "Ġsp o", + "ĠMa ur", + "Ġoccas ional", + "Ġincons istency", + "ĠT ac", + "op ts", + "ĠG AB", + "ĠTa o", + "ĠMatthe w", + "à ½", + "Ġp iano", + "ĠR CC", + "ĠO K", + "ĠK ul", + "met han", + "ĠPRO C", + "Ġconvers ations", + "ĠC SI", + "ang ent", + "ĠX ue", + "Ġgraph ic", + "den ing", + "health y", + "Ġf p", + "az one", + "Ġdiscipl ine", + "Ġprogress es", + "Ġb amboo", + "Ġchar m", + "ĠAc tivated", + "ĠSh arp", + "yn es", + "Ġtool box", + "Ġhetero structures", + "piper azin", + "Ġa rose", + "ĠInter val", + "Ġstrip e", + "ĠCh ak", + "Ġc uff", + "RE SS", + "Ġnon uniform", + "Ġbeet le", + "P rec", + "z c", + "Th read", + "b et", + "Ġe e", + "ĠOption al", + "Ġt roph", + "ĠP uer", + "ĠF ron", + "Ġmultiple t", + "Ġcalor imetry", + "Ġmonocyt ogenes", + "ĠH imal", + "Ġdr ill", + "AG A", + "Ġferr itin", + "Ġd pi", + "ĠC arm", + "Ġg one", + "Ġun idirectional", + "Ġrem inis", + "Ġadjust able", + "ĠAust in", + "S ARS", + "d al", + "Ġc ef", + "equiv ariant", + "bas eline", + "Ġspin ors", + "ĠPr int", + "Ġm ile", + "ĠL inc", + "mut ation", + "Ġmuc us", + "ĠH SC", + "Ġtherm od", + "Ġpain t", + "Ġdistinct ly", + "ath y", + "Ġph armacy", + "ĠBul g", + "ĠG ang", + "hic le", + "og an", + "ĠJ ian", + "ĠIndian a", + "Ġinstant on", + "Ġpall adium", + "f iber", + "n py", + "ĠU A", + "ĠQ T", + "cepti ble", + "et ine", + "ĠH oles", + "Ġdepend ences", + "Ġthreshold ing", + "ĠMain tenance", + "Ġparticip ates", + "ĠGen omes", + "factor ial", + "ĠL iber", + "ĠTherm odynamic", + "Ġe lective", + "uc her", + "Ġhyper ther", + "Ġstom atal", + "ĠB irth", + "ch olesterol", + "Ġnot ch", + "Ġsym biotic", + "Ġbusiness es", + "Ġapprec iable", + "Ġspecial ization", + "á r", + "act yl", + "ĠGraph Pad", + "os per", + "Ġor chestr", + "Ġdi hydro", + "Ġconcl uding", + "CL K", + "Ġeq s", + "ĠProg ression", + "Ġclub s", + "ak u", + "ev ents", + "Ġspl enic", + "Ġb unch", + "ĠT m", + "ĠM obility", + "Ġtwo fold", + "Ġradi ally", + "L STM", + "M H", + "ĠCo al", + "Ġfron tier", + "J an", + "J un", + "ĠSim pson", + "Ġabst racts", + "P al", + "Ġun im", + "Ġro bo", + "ĠII B", + "dep leted", + "Ġmorphological ly", + "Ġenfor cement", + "Ġd well", + "Ġst agn", + "Ġlim estone", + "Ġmicro v", + "Ġïĥ ¸", + "L uc", + "p acs", + "cy ano", + "Ġintra ocular", + "ĠCalc ulate", + "Sup port", + "SY S", + "ĠV S", + "CM s", + "Const ant", + "ĠD j", + "Ġun balanced", + "Ġrepeat ability", + "g ins", + "i rect", + "ĠM OR", + "ĠBa iley", + "Ġadvance ment", + "Ġpurs uit", + "Ġa rom", + "pro ced", + "ĠIniti ative", + "Ġincenti ves", + "Ġsur pass", + "gen es", + "ĠIN D", + "L H", + "Ġsu icidal", + "Ġbiodies el", + "x z", + "Ù Ĭ", + "le a", + "ĠAn thony", + "Lear ning", + "Ġund o", + "Ġïĥ º", + "ĠCommun ities", + "h ua", + "iti me", + "ĠDe an", + "Ġplas min", + "ÃŃ nez", + "ohyd rate", + "Ġneurode velop", + "Ġstoichi ometric", + "ĠOnc ology", + "Ġshow er", + "ĠD MS", + "W OR", + "ĠP IP", + "Ġster ic", + "mitte es", + "ist ol", + "ox ins", + "no on", + "FF T", + "Ġá »", + "opo iesis", + "Ġresemb ling", + "ĠB ord", + "Ġprob iotics", + "ocy sts", + "gre y", + "ĠCatal og", + "IZ ATION", + "ill es", + "ĠAl an", + "ĠÅ ·", + "ĠLe ib", + "ĠReason ing", + "bi ological", + "uter ine", + "vac izumab", + "lecom mun", + "ĠW arm", + "ep age", + "vari ants", + "B SA", + "Ġïĥ ¶", + "Ġhepat ocyte", + "ket ch", + "Ġstrip ping", + "ĠAd verse", + "ĠFe as", + "Ġïĥ ¯", + "P ac", + "Ġind entation", + "Ġsec ular", + "Ġidentif iable", + "run ning", + "Ġr d", + "Ġz yg", + "ĠD ictionary", + "Ġres veratrol", + "ines terase", + "Ġtet racycline", + "ub les", + "Ġthro at", + "ĠL amb", + "ary on", + "ĠS QL", + "Ġà ľ", + "Ġgly cemic", + "Ġcompet ent", + "ĠAg reement", + "oic ed", + "Ġconstitu tively", + "Ġelectro cardi", + "oplas ma", + "Ġî Ħĥ", + "an ide", + "Ġre organization", + "Ġun infected", + "UT E", + "Ġro yal", + "ĠS it", + "Ġmar ital", + "ĠKob ayashi", + "B arr", + "ĠT ennessee", + "ĠChrom at", + "ĠD erm", + "pro jection", + "ĠJ ob", + "Ġâī ł", + "ĠT rip", + "Ġis op", + "Ġproject or", + "Ġatmosp heres", + "Ġperfor ation", + "st orage", + "ith s", + "Ġmon omeric", + "ĠUS B", + "ĠE ve", + "Ġsp ore", + "Ġm T", + "ox azole", + "ĠDe formation", + "Ġtext ual", + "Ġwar f", + "Ġneuropath ic", + "prep ared", + "Ġbl ended", + "ĠHo uston", + "**************************************************************** ********", + "es ters", + "Equ als", + "Ġallerg en", + "Ġpertin ent", + "f acts", + "uc tions", + "Ġcl ocks", + "ĠV ia", + "ĠCD F", + "Ġest uary", + "Ġphenomen ology", + "ar us", + "AP H", + "Ġarg ues", + "Ġinser ts", + "g ow", + "h art", + "Ġchem otaxis", + "Ġp v", + "Ġre in", + "ĠG rim", + "ĠV F", + "Ġeff ic", + "ĠProf iling", + "Ġan odic", + "ĠDEN V", + "ĠW it", + "ĠSY STEM", + "ĠCay ley", + "En g", + "ĠA QP", + "inter actions", + "ili arity", + "ĠProm otes", + "Ġd ams", + "ing ton", + "ff ff", + "Ġint ran", + "ĠTurb ulence", + "ĠBian chi", + "C RE", + "ĠN OD", + "ap ine", + "ĠK ane", + "ĠPD GF", + "ĠAx is", + "ĠC ausal", + "ĠPo or", + "ĠW ords", + "ĠHR V", + "Ġcyan obacteria", + "Ġreminis cent", + "ĠRemark ably", + "he et", + "@ @", + "b il", + "Ġdiscrim inating", + "ĠBal tic", + "ĠQue bec", + "Ġdef ensive", + "âĪ ©", + "k r", + "ĠR PE", + "see king", + "ĠMo vie", + "Ġinnov ations", + "le pt", + "Ġk w", + "Ġtib ia", + "Ġne at", + "yt est", + "Ġthin ner", + "Ġoste oblasts", + "ĠNorth west", + "M OS", + "ĠP Q", + "Ġsp i", + "Ġrespond s", + "Ġhistor ically", + "ĠPack age", + "ĠCoast al", + "ĠMississ ippi", + "ĠP VA", + "per ing", + "ind ole", + "Ġprosp ectively", + "ĠHem isphere", + "Ġbare ly", + "án chez", + "ag gered", + "yp tian", + "ĠG est", + "yl ine", + "Ġphot ochemical", + "os calar", + "por ated", + "Ġmetabol omics", + "Ġoste oblast", + "EGF P", + "eri atric", + "D W", + "qu est", + "ĠH ave", + "Ġsp ondyl", + "ĠPrim er", + "Ġs inks", + "Ġg aussian", + "ĠK hal", + "En c", + "ĠAn opheles", + "Th anks", + "Ġconstr ued", + "ĠU SS", + "ĠZe eman", + "Ġex ported", + "ĠLe vi", + "Ġcomm ander", + "conn ect", + "Ġnom enclature", + "there fore", + "ul ata", + "Ġent repreneur", + "Ġneuros cience", + "z an", + "Ġext ant", + "AT IVE", + "ope z", + "Ġenfor ced", + "ĠInnov ation", + "ear ance", + "Ġimp ressive", + "ĠPl ac", + "ĠMo z", + "ĠSt ark", + "Ġri val", + "ĠCap ital", + "Ġgranular ity", + "Ġdiaphrag m", + "ut aneous", + "ind s", + "Ġphot ograph", + "Ġrect angles", + "T GF", + "Ġse af", + "Ġm aze", + "ĠH W", + "Ġcorrel ators", + "Ġdistinguish able", + "Ġconfound ers", + "Ġlandsl ide", + "Ġto ll", + "Ġwas tes", + "ĠW F", + "Ġend oc", + "Ġcaps id", + "ec und", + "ĠR BD", + "ps in", + "Ġobst etric", + "Ġnanos heets", + "oc ols", + "ren s", + "ĠSub stituting", + "Ġcustom ized", + "Ġres uscitation", + "Ġtub ulin", + "ophy te", + "~~~~ ~~~~", + "pl ants", + "hic illin", + "hal o", + "ruit ment", + "ĠConc rete", + "Ġnanor ods", + "ĠForm s", + "Ġd ying", + "dis charge", + "Ġwell being", + "Ġwar mer", + "ĠS SD", + "ĠA UT", + "ĠCon jug", + "Ġjuven iles", + "Ġine vitably", + "ĠM CS", + "appro ach", + "ĠM ason", + "ĠG ust", + "ĠTherm odynamics", + "Ġpe el", + "ĠTranscript ome", + "Ġindisp ensable", + "ur gery", + "pos ity", + "Ġpolar izations", + "ĠOther s", + "Ġsand y", + "Ġgli omas", + "Ġpurs ued", + "V EL", + "Ġr st", + "pos ium", + "ne arest", + "Ġdissem inated", + "ĠMY C", + "Ġal dehyde", + "ĠDiagn ostics", + "m ans", + "Ġas phal", + "ĠSe lect", + "ĠRec on", + "and ro", + "D IM", + "Ġf eces", + "ill on", + "ĠMAL DI", + "n f", + "ĠE lim", + "Ġhapp y", + "ĠKar l", + "ĠIn ser", + "Ġinter rog", + "In tern", + "Ġtensor flow", + "Ġhalo es", + "Ġanticip ate", + "ĠDPP H", + "rÃŃ guez", + "H er", + "an ate", + "Ġd ressing", + "ĠH oly", + "Ġnew er", + "rid es", + "plac ed", + "inet obacter", + "ĠOcc urrence", + "ed ema", + "ĠI k", + "ab ad", + "ĠTrans itions", + "Ġoutl ines", + "Ġcoch lear", + "G y", + "s uccess", + "ĠM EM", + "ast ype", + "Ġnormal izing", + "Ġtermin ates", + "Ġsudden ly", + "b box", + "ĠP ul", + "ĠP TP", + "ag inal", + "Ġpre trained", + "Ġun reliable", + "ĠGraph ical", + "ĠSey fert", + "Ġcharacter izations", + "Ġt x", + "Ġbic arbonate", + "math ord", + "Ġher itability", + "stack exchange", + "i ri", + "âĢ ĸ", + "ip it", + "at tle", + "Ġare na", + "ib a", + "ĠA X", + "ĠG Ps", + "ophil ia", + "S EL", + "os ystem", + "ĠâĬ ¢", + "ĠNucle us", + "red ited", + "AC R", + "ĠAnt enna", + "ĠCd c", + "or ie", + "Ġequil ibration", + "el ong", + "st ability", + "ĠSch ist", + "Ġinject ing", + "h p", + "Ġvit amins", + "Po isson", + "or tal", + "Ġà Ĭ", + "ĠÄ ı", + "I ll", + "Ġutil s", + "оР²", + "ĠG rom", + ":: ::", + "ĠGn RH", + "ĠSier ra", + "Ġd rafted", + "Ġcap ita", + "sh ips", + "Ġtim estamp", + "Ġsubstit uents", + "ĠNot able", + "ĠPur pose", + "in ol", + "Ġa i", + "Ġf og", + "ot one", + "ĠPl aces", + "bys hev", + "ti ology", + "ri ption", + "Ġy ards", + "ĠX I", + "Ġtechn ically", + "G AM", + "ĠA BS", + "pl atform", + "ĠW O", + "PRO C", + "Ġrecons tit", + "ĠAnomal ous", + "ĠBi ol", + "St age", + "ĠReview s", + "Ġrecall ing", + "Ġille gal", + "l und", + " ¬", + "ut henium", + "ĠP es", + "Ġo varies", + "sol utions", + "mass ive", + "ĠRA W", + "Ġrecon nection", + "ĠSus ceptibility", + "Ġeconom ical", + "cult ured", + "ĠSh am", + "sq cup", + "Ġp ear", + "dep osition", + "uch s", + "ĠS aw", + "Ġembol ism", + "B ur", + "n ar", + "ou le", + "Ġtex tile", + "se ven", + "th io", + "Ġden oising", + "CE P", + "Ġubiquit ination", + "ĠCarl os", + "a P", + "Ġfol der", + "Ġhemat ological", + "il uminescence", + "ĠF uel", + "ic ion", + "ac ulture", + "AR B", + "ĠTra vel", + "F unc", + "ac les", + "ĠIn te", + "Ġvacu a", + "Ġcock tail", + "ĠIn sp", + "Ġcor porate", + "Ġdepic ting", + "Ġspr int", + "ĠmTOR C", + "Ġc img", + "oc arbon", + "ĠD ave", + "ĠG b", + "ij i", + "target ing", + "Ġsequest ration", + "B ri", + "I GF", + "Ġanaly tics", + "ĠAc inetobacter", + "get s", + "MP S", + "ogl uc", + "C ent", + "Ġver bs", + "Ġinduc tance", + "di agram", + "Ġrec alled", + "Ġcos me", + "Ġautom otive", + "ĠPD Es", + "ĠRe id", + "Ġadap ter", + "ĠOl iver", + "Ġaval anche", + "V ir", + "ĠT oxicity", + "ĠLe u", + "Con clusions", + "Ġtet ragonal", + "ĠDM F", + "umann ii", + "ĠRequire ments", + "t oc", + "it é", + "Ġcontin ent", + "ĠH ank", + "ĠDef initions", + "GP U", + "orig in", + "Ġdich ro", + "M us", + "Ġb ival", + "Ġimp ulsive", + "Ġassemb le", + "Ġpip es", + "doc s", + "Ġexchang er", + "Ġall ograft", + "lo yd", + "ĠÌ ĭ", + "Ġanten atal", + "Ġgrass land", + "Ġhy stere", + "ĠAnti gen", + "ĠGener ic", + "ĠT uring", + "ĠEx cell", + "ĠHe in", + "aj a", + "umin um", + "cit abine", + "f acial", + "iter ation", + "Ġsl urry", + "AM L", + "erge tic", + "ĠTH F", + "Ġkil ometers", + "f g", + "ed uc", + "id ian", + "Ġpredic ates", + "Ġradi os", + "ĠPer i", + "ĠShe ll", + "Ġarc sec", + "Ġstri atal", + "Ġce iling", + "olith ic", + "Ġexhaus tion", + "P UT", + "ther s", + "ym p", + "ĠQ ian", + "ĠProg ressive", + "Ġw el", + "ĠCon vention", + "ĠCur ie", + "ĠM ans", + "ĠN ova", + "ĠW ells", + "de w", + "St andard", + "real istic", + "trans pose", + "ser ial", + "ĠT x", + "ĠA MR", + "Ġind eterm", + "ĠLi ouville", + "hook rightarrow", + "AR s", + "Ġbase ball", + "ac ious", + "agne tization", + "es timate", + "ĠP AS", + "Ġme als", + "multi ple", + "ĠBiomark ers", + "W ide", + "ĠTom ography", + "//////////////// ////////////////", + "Ġres ins", + "Ġany where", + "IN C", + "ĠTe aching", + "ĠSam uel", + "Ġhall mark", + "ĠTh yroid", + "oth i", + "Ġconst raining", + "ĠBar rett", + "ĠEr rors", + "C ole", + "sh aring", + "HD L", + "Eff ect", + "ĠT olerance", + "Ġstress ful", + "ĠBal ance", + "ĠT ech", + "Ġval leys", + "set up", + "ĠRad ical", + "ĠMacroph ages", + "Ġinter rupt", + "Ġdi atom", + "col ored", + "Ġpy rid", + "FD G", + "à ¦", + "Ġre ared", + "ĠR ating", + "Ġop aque", + "pack age", + "Ġnas opharyngeal", + "Ġprecondition ing", + "D iptera", + "ĠM ing", + "ĠCa ro", + "ĠImmun ity", + "rif uge", + "ĠObj ectives", + "g han", + "uc cin", + "ĠF ors", + "ĠF ITC", + "Ġse ats", + "ĠImp aired", + "Ġreef s", + "em aker", + "Ġoff ices", + "Ġaccept ing", + "ĠTR AN", + "ĠTarget s", + "Ġcorrel ator", + "Ġsuper capac", + "in burgh", + "Ġcoll ider", + "Ġenter ic", + "ĠSTR UCTURE", + "Ġmin ister", + "ĠArch ae", + "Lo op", + "ĠA SA", + "Ġcont acted", + "Ġhis tidine", + "fold ed", + "S earch", + "Ġresp ects", + "ĠAT F", + "Ġtro uble", + "Ġprev ailing", + "C p", + "ĠT CM", + "ĠSp inal", + "Ġgu ides", + "ev itable", + "Ġb rick", + "str ings", + "ĠHung ary", + "Ġe ps", + "ent ricular", + "Spec ifically", + "and o", + "iss ues", + "osom iasis", + "k Da", + "Ġas ide", + "Ġaden ine", + "Ġmotiv ate", + "strat ig", + "B LE", + "ĠDep osition", + "m otor", + "ĠH ers", + "Ġne bul", + "ĠBar rier", + "Un like", + "Ġball istic", + "Ġsouth western", + "ĠMont real", + "S can", + "Ġm ould", + "Ġinter rup", + "small matrix", + "Ġelabor ated", + "uc ks", + "AP S", + "ĠCons umption", + "cap acity", + "inn itus", + "Ġgovern ance", + "Ġp alsy", + "Ġsub mission", + "Ġtem ple", + "ĠII A", + "meth ionine", + "Ġker at", + "Ġrid ges", + "Prom ega", + "c ols", + "IS P", + "Ġap nea", + "ĠFl at", + "ĠEp igenetic", + "Ġpar ish", + "ĠPar ametric", + "d ash", + "f uture", + "r ise", + "Ġcontract ing", + "alg ia", + "Ġg oto", + "stad t", + "Ġfabric ate", + "Ġdimer ization", + "d ump", + "ĠL yn", + "Ġrecycl ed", + "posed ness", + "ĠSens ory", + "ï Ŀ", + "ĠW et", + "Ġdi ethyl", + "Ġbl ades", + "Ġtim ed", + "Ġkey word", + "Ġpolyt ope", + "ĠG ot", + "Ġapproxim ates", + "With out", + "ĠB ere", + "ĠL p", + "opl asty", + "ĠF ibr", + "mod ulated", + "ĠAR M", + "Ġunde restimate", + "ĠC BS", + "ĠL ectures", + "unc an", + "ĠSe ismic", + "So ft", + "Ġzo oplankton", + "Ġencephal opathy", + "ĠS SA", + "ĠC ros", + "ĠH ann", + "Ġsh uffle", + "sc ription", + "ĠRever s", + "Stud ies", + "Ġsoc ially", + "Ġsub cl", + "ĠY ong", + "og h", + "Ġïģ ³", + "UD Y", + "ĠHa ar", + "ĠDoc tor", + "Ġint akes", + "Ġbar rel", + "ĠTR PV", + "ĠAgg reg", + "ny i", + "tun ed", + "ac quired", + "Ġho ok", + "F GF", + " «", + "ĠIn jection", + "Ġgra vel", + "Ġmicro g", + "Ġmen strual", + "Fe ature", + "I RE", + "u u", + "ĠS rc", + "ĠSt ore", + "Ġiniti ator", + "PS O", + "Ġepile ptic", + "Ġcing ulate", + "I J", + "R ow", + "Ġsing ing", + "ĠMet han", + "ĠAld rich", + "Ġtremend ous", + "am ining", + "Ġtrac ts", + "Ġâİ £", + "kl ah", + "D iv", + "ind ol", + "Ġind ole", + "ex per", + "Ġgly cer", + "Ġbenz yl", + "Ġwors ening", + "Ġunambig uous", + "u art", + "Ġpar sim", + "ric ks", + "Ġtra il", + "ĠBl anc", + "Ġamin otransferase", + "ĠD OC", + "Ġfum ig", + "id ic", + "ĠCon sequences", + "Ġacid ification", + "ĠCIF AR", + "ĠD atasets", + "ĠA MI", + "Ġexpl ants", + "ĠD iverse", + "Ġde phasing", + "Ġpar liament", + "ip ient", + "Ġhoney comb", + "he avy", + "Ġwaterm ark", + "M ED", + "d atasets", + "w aters", + "Pro vid", + "inter pret", + "rov irus", + "I o", + "R AD", + "Ġl unar", + "Ġwe aning", + "Ġsensor imotor", + "uc a", + "Ġinf ect", + "ĠUn ique", + "GR P", + "Q oL", + "osp ec", + "Ġforward ing", + "Es tim", + "ÅĦ ski", + "ĠM s", + "ach n", + "Ġro ta", + "Ġappoint ment", + "ĠMed al", + "Ġaden ovirus", + "quin ol", + "Ġdeuter ium", + "te p", + "ĠSt yle", + "N d", + "ay ama", + "ĠH amm", + "ĠSpec ification", + "v ability", + "th a", + "Ġj itter", + "Ġâİ ¦", + "a qu", + "w ire", + "Ġclass ically", + "Ġsuper potential", + "ĠSpec im", + "ĠVari ance", + "Ġalbum s", + "ĠSen ior", + "Ġneurotrans mitter", + "ĠRecomb inant", + "D CS", + "v l", + "Ġp f", + "Ġin evitable", + "ĠN ick", + "Ġmanip ulating", + "itu ximab", + "ce iver", + "ĠB ren", + "ĠR ace", + "Ġret arded", + "mod ulin", + "Cl inical", + "Ġneu rologic", + "ĠReg iment", + "Ġzo om", + "ĠOrth ogonal", + "ĠConcer ning", + "ĠJur assic", + "ĠAr tem", + "ĠMel bourne", + "b ins", + "j l", + "Ġin hab", + "Ġsq rt", + "Ġsemis imple", + "ast ric", + "ĠPro xim", + "ĠVari ants", + "Ġa esthetic", + "Ġsummar ised", + "ĠBeck er", + "O CH", + "d ale", + "Ġm ounting", + "and ering", + "Ġsoft max", + "Ġneuro inflammation", + "Ġesophag us", + "oper ators", + "ĠAD AM", + "Ġviol ate", + "ĠPH Y", + "ed e", + "ĠC her", + "ors al", + "Ġmetam orphic", + "ĠI CM", + "ĠAb cam", + "sl ot", + "ser ine", + "Ġdu plicates", + "ĠME MS", + "ĠA bl", + "ĠC hel", + "ĠAuthor ity", + "Ġge o", + "Ġhome omorphism", + "Ġimmunomod ulatory", + "ĠT U", + "ĠK T", + "ater ally", + "ox ides", + "teb ral", + "Ġcatar act", + "le aved", + "ig u", + "ate ur", + "ĠR é", + "Ġdiscover ies", + "bos on", + "oc ated", + "j pg", + "ĠS ato", + "ĠPRO P", + "ĠIm plement", + "EL ISA", + "iqu eness", + "Ġsym bion", + "ĠFar aday", + "ĠPPAR γ", + "w itz", + "re ward", + "ĠB ush", + "st ressed", + "ĠA bor", + "Ġair ways", + "Ġinterfer ometry", + "C irc", + "Ġimmun oprecipitation", + "ĠAp ache", + "roph osph", + "Ġo C", + "Ġf rog", + "ĠG U", + "ff e", + "ĠSt ro", + "Ġdodec yl", + "d an", + "f olds", + "ĠM ust", + "Ġsurround ings", + "Ġcod ons", + "ond a", + "t b", + "od ge", + "av as", + "ĠSe ason", + "t ude", + "ĠPl asticity", + "ĠHawai i", + "D EG", + "ĠC MD", + "Ġsingle ton", + "ke ley", + "Ġalgebra ically", + "Ġnano structured", + "eas ible", + "Ġoverlo oked", + "ĠP ulse", + "rom echanical", + "ĠEl se", + "Ġexcit ons", + "ĠConst rained", + "Ġco hesion", + "Ġreal izing", + "ĠRadi ative", + "Ġtryp an", + "x s", + "ĠT as", + "Ġmain stream", + "Ġcompact ly", + "g rowing", + "es c", + "Ġd N", + "ĠSign atures", + "ĠFundament als", + "Ġex pose", + "ĠR ang", + "Ġhand ed", + "Ġfunctional ization", + "Ġpass iv", + "al tern", + "ag ul", + "Ġschem atically", + "O W", + "Ġ Ö", + "ĠP OD", + "Ġhe ar", + "ym ore", + "ĠPrem ier", + "S outh", + "Ä «", + "ĠO BS", + "ĠAl g", + "gl ia", + "ĠTrans membrane", + "Ġsphe roids", + "ĠR HS", + "Ġinc hes", + "ĠK ato", + "Ġi e", + "ĠCom mercial", + "Ġanaly tes", + "Ġrisk y", + "Ġp iston", + "ĠMark ovian", + "Ġdram a", + "Ġc i", + "ĠHist ological", + "Ġact uation", + "disc rete", + "carb amoyl", + "S MA", + "Ġfeed s", + "Ġneoplas ia", + "ĠControll er", + "b een", + "glut amine", + "in jected", + "Ġc rab", + "ĠC auses", + "ĠSt ory", + "Ġvan adium", + "ĠT itan", + "en ix", + "ass ign", + "Ġimmun ogenicity", + "ĠAp parent", + "Ġenh ancers", + "ĠS ou", + "all oy", + "mathb in", + "Ġsed ation", + "ĠWork shop", + "g over", + "l st", + "Ġup welling", + "me z", + "Ġpoly propylene", + "ĠCol orectal", + "ĠRel axation", + "Ġfrag ile", + "Ä ĥ", + "Ġsub graphs", + "the oretical", + "Oper ator", + "ly wood", + "aw n", + "ĠPer centage", + "methyl ation", + "corrhiz al", + "G rad", + "d ens", + "ĠH α", + "Ġup coming", + "Ġvir gin", + "N ames", + "ĠR yd", + "Ġâİ ¤", + "phosph orylation", + "renew al", + "Y ear", + "In it", + "Ġs elling", + "ĠM ASS", + "roph in", + "ij n", + "Con versely", + "Ġunivers ally", + "orh ombic", + "Ġunpredict able", + "F ock", + "ch air", + "iv as", + "network s", + "Ġterr itories", + "th ia", + "ĠAm plification", + "M arch", + "Ġf lam", + "ĠCh art", + "Ġshort age", + "AM ET", + "Ġgrap e", + "Ġvoltam metry", + "Ø ¯", + "ĠS CH", + "Ġepit hel", + "ĠChrom osome", + "ĠX L", + "ĠPers istent", + "Ġtravel ed", + "Ġmerid ional", + "Ġf printf", + "Ġg um", + "vis ory", + "Un fortunately", + "Ġant eced", + "Ġfric tional", + "D AT", + "ac l", + "ĠP regnancy", + "ĠB Z", + "reg ulatory", + "stim ulating", + "J apan", + "m achine", + "u ti", + "ĠL er", + "Ġnan oflu", + "prot otype", + "identif ication", + "klah oma", + "ĠEm ploy", + "Sch warz", + "Ġincorrect ly", + "at to", + "ri zation", + "ism uth", + "Ġir is", + "iment ary", + "Ġinflation ary", + "Ġoutflow s", + "ĠL ic", + "ore ductase", + "Ġproceed ing", + "ĠT AC", + "ĠH TL", + "Ġres ides", + "str al", + "ĠTrans f", + "Ġdich otom", + "Fil ter", + "J une", + "is ure", + "ĠA de", + "Ġij k", + "ĠPhil os", + "Ġstay ed", + "Ġtam oxifen", + "Ġaspar agine", + "ex ception", + "Ġaccum ulating", + "ast ro", + "Ch ange", + "uz i", + "Ġl on", + "In stead", + "Ġcent rally", + "ĠD ental", + "class ified", + "ĠEg yptian", + "Add ress", + "ĠQuatern ary", + "ĠU SP", + "co in", + "Ġembry ogenesis", + "ïĢ ¨", + "N ull", + "ĠM ixing", + "int ensive", + "Ġnorm ative", + "ĠL ef", + "Ġr umen", + "ĠTh ai", + "Ġsw allow", + "Comp onent", + "Ġrobo tics", + "ĠC ad", + "ĠC IP", + "ĠAc ids", + "ĠO ffic", + "ure r", + "ĠW ick", + "Ġk ink", + "ĠSch a", + "ĠCharacter istic", + "f amilies", + "ĠG Cs", + "ĠOptim izing", + "Ġtim er", + "é l", + "j in", + "re versal", + "Ġsand stone", + "H N", + "t k", + "Ġp tr", + "Ġmon ochromatic", + "Ġfeed forward", + "ding ton", + "Ġcritic ism", + "Ġs ig", + "Ġp ace", + "ĠT K", + "ĠW as", + "Ġcertif icate", + "Ġst uck", + "Ġcor rid", + "Ġlocal isation", + "Ġsil k", + "Ġdig est", + "ĠTem ple", + "ĠPost erior", + "Ġcommut ator", + "ts ch", + "per me", + "ys ed", + "Ġmen u", + "Ġmid w", + "oc atalytic", + "Ġpp b", + "T ypes", + "ar ri", + "ĠL OD", + "Ġlo an", + "sec ret", + "Ġcarb ons", + "ĠH olog", + "olip ids", + "Ġupl o", + "ĠDN ase", + "Ġpuzz le", + "Ġst ance", + "ĠManc hester", + "ĠDet ector", + "im s", + "ĠTerm s", + "ĠP GC", + "Ġinc idents", + "ie h", + "ĠID s", + "ĠAh mad", + "Ġn ights", + "Ġbiom o", + "ĠMethyl ation", + "u ator", + "res ize", + "ĠF inger", + "ĠW o", + "Ġpost er", + "Ġsolid ification", + "ĠVal idity", + "ĠDend ritic", + "Ġad herent", + "iss ions", + "inc tion", + "Ġantagon istic", + "ĠPrelim inaries", + "Ġco val", + "Ġmov ies", + "Ġbud ding", + "K n", + "ĠG it", + "ĠThere after", + "Ġcapac itive", + "A z", + "ĠT LS", + "Ġiniti ates", + "ĠD MR", + "Ġâī «", + "ĠMy ocardial", + "ĠRot ation", + "CON FIG", + "Ġvow el", + "Ġoliv ine", + "H amiltonian", + "Ġst alk", + "N eu", + "R est", + "an ical", + "Ġd st", + "Ġres h", + "Ġexp ressive", + "Ġinf ectivity", + "ok u", + "CT L", + "F requency", + "Ġprem ise", + "W alk", + "Ġâ Ĺ", + "Ġrel apsed", + "t ured", + "ĠU ML", + "ov an", + "ĠRes earchers", + "Ġconven iently", + "us k", + "IN IT", + "Eq s", + "F actory", + "Ġun steady", + "ĠAn sw", + "Al a", + "nit ine", + "q p", + "ul ous", + "res earch", + "ĠB rom", + "ĠDem oc", + "config uration", + "ulos ic", + "Ġf ra", + "Ġg ift", + "Th ird", + "Cl aim", + "Ä Ł", + "od iazep", + "Ġpro x", + "oc ystis", + "ĠR PA", + "ĠLik ert", + "R MS", + "t ech", + "Ġac ous", + "T LR", + "b uck", + "ĠThe rap", + "uss ions", + "hel or", + "ĠEm otion", + "b ird", + "Ġth io", + "Ġquantit ation", + "brack et", + "Ġper cept", + "Ġsub category", + "Ġlight ning", + "Ġher nia", + "Ġneurot rophic", + "SD S", + "ĠAnd ers", + "Ġslow ing", + "strong ly", + "ĠC ounting", + "ĠIn cluding", + "duc tions", + "ub ated", + "ĠSt orm", + "cor related", + "Ġautoanti bodies", + "ĠM erg", + "oc er", + "mic utes", + "Ġnonlinear ities", + "ĠCent ury", + "ĠLand scape", + "ĠDeriv atives", + "ĠContr ary", + "Ġcomp ile", + "ĠHep atic", + "Ġpond s", + "Ġorgan ize", + "D MSO", + "P osition", + "Ġb rach", + "Ġinf lat", + "osp ace", + "Ġskew ness", + "Ġag itation", + "ĠHO MO", + "E U", + "Ġcom mented", + "Ġcor pora", + "Ġmal t", + "Herm itian", + "id ay", + "ĠHelm holtz", + "ro blast", + "ĠC TR", + "un ching", + "ĠM ond", + "ĠCom ment", + "Ġoste osarcoma", + "post erior", + "Ġthym us", + "Ġcig arettes", + "N W", + "o lem", + "ĠH ox", + "ĠNF L", + "ĠAvail able", + "ĠS iber", + "ĠF eld", + "Ġborder line", + "Ġbe ats", + "Ġorgan ised", + "Ġdistingu ishes", + "Ġdial og", + "ĠBerg er", + "ole ic", + "Ġnum bered", + "Ġreach able", + "ĠRoberts on", + "ĠCham ber", + "nd array", + "Ġcytos keletal", + "Ġbl ending", + "bl ood", + "Im port", + "Ġoverwhel ming", + "Ġi o", + "Ġout age", + "ĠSch olar", + "plac ing", + "ĠPol yp", + "Dec l", + "ĠMED LINE", + "ĠK M", + "ĠD AP", + "err ors", + "ĠS HR", + "ĠD ex", + "ĠG AS", + "ĠG ian", + "Ġclinic opathological", + "Ġïģ ·", + "ĠPredic tions", + "ĠQuad ratic", + "Ġarrhyth mias", + "ar id", + "Ġcl othing", + "ĠFract ure", + "ĉ ĠĠĠĠĠ", + "add y", + "ĠAlber ta", + "ĠW ed", + "phi re", + "ĠEn cryp", + "ĠL AB", + "ĠF ano", + "CT T", + "Ġor yz", + "ili ac", + "ĠL iao", + "vers us", + "Ġmes o", + "Ġmid point", + "Ġst ator", + "ĠJ enn", + "ov sky", + "Ġunc over", + "eren n", + "ĠMc M", + "âī Ī", + "ĠCirc uits", + "Ġfet uses", + "Ġaggl omer", + "Ġf b", + "Ġy y", + "at ech", + "AR G", + "Ġba umannii", + "Ġellipso id", + "Ġl oses", + "Ġun ve", + "Ġbut t", + "Ġmultic entre", + "il ine", + "Ġres ort", + "Ġcereb rovascular", + "ĠDecre ased", + "j ud", + "s us", + "am ol", + "const raints", + "Ġt een", + "ĠPass ive", + "ĠCauc asian", + "Ġc ran", + "ĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ ĠĠĠĠĠĠĠĠ", + "ü n", + "ĠDN MT", + "Ġt error", + "ad renal", + "Ġangi ogenic", + "ĠInhib itory", + "p rag", + "Ġco b", + "els h", + "Ġenhance ments", + "ĠSha w", + "ĠTak ahashi", + "Ġsulph ur", + "Ġgrav itation", + "ĠPVD F", + "m ust", + " ¢", + "as ymptotic", + "el man", + "ĠP ros", + "ĠM AD", + "ĠL en", + "the rapy", + "eful ly", + "sulf ur", + "ĠT CA", + "ad ditive", + "tal k", + "Ġpig lets", + "Ġprosp ect", + "ecund ity", + "ĠX iang", + "hand ler", + "Ġcl ath", + "Ġmill imeter", + "j ar", + "Ġbi ophysical", + "Ġcomplex ities", + "ĠHer b", + "Ġrecover s", + "ĠVin cent", + "ĠPuer to", + "E arth", + "R AM", + "Ġc ables", + "des igned", + "ĠOscill ation", + "Ġme iosis", + "Ġfle et", + "ĠHunting ton", + "ĠB eg", + "ĠE Cs", + "ĠAn tic", + "Ġpractition er", + "c ultural", + "k at", + "Ġrec oil", + "ĠIm plicit", + "Ġsumm aries", + "Ġdiscontinu ed", + "Ġencompass ing", + "ĠAlt ogether", + "ĠD IST", + "Ġconst ellation", + "ĠEx isting", + "Ġconduct ors", + "oplas m", + "ĠCosm ology", + "Z ero", + "ĠIn form", + "Ġend angered", + "Ġweap ons", + "at ype", + "ĠAs c", + "Ġflu ence", + "Ġfer ric", + "ĠLaure nt", + "Ear ly", + "Ġs gn", + "ĠHad amard", + "Ġastr on", + "C ys", + "ĠTh m", + "Ġdec e", + "eren cing", + "ĠMe ans", + "Ġhyd rated", + "Ù Ī", + "Ġrig orously", + "Ġamb ulatory", + "ĠDO I", + "Hand le", + "ĠEnterobacter iaceae", + "ĠR Q", + "ĠG FR", + "pro te", + "Ġmig rated", + "then ing", + "ĠHop kins", + "ĠPsych ology", + "ig l", + "ĠE DS", + "ĠâĪ ¶", + "Ġrem otely", + "Ġ ¥", + "Ġinsp iration", + "ĠâĮ ¬", + "ol ian", + "Ġsal iency", + "ĠD og", + "ĠR osa", + "oy a", + "Ġoccup ies", + "cam era", + "Ġdecomp ression", + "Ġsc att", + "Ġinvestig ator", + "Ġcount erex", + "ĠIFN γ", + "ĠPitts burgh", + "Ġad minister", + "ne gl", + "uss is", + "MP C", + "ĠSw itching", + "Ġcool er", + "Ġbron chi", + "Ġpar alle", + "Ġspec kle", + "Ġphys iologic", + "IN VAL", + "Ġheter ologous", + "|| |", + "org hum", + "G AL", + "Ġmal formations", + "Ġweak ening", + "Ġpsych o", + "ĠI H", + "Ġcontrad ictory", + "Ġphon ological", + "ĠPerturb ation", + "b B", + "ĠN os", + "TR UE", + "fold ing", + "phen ol", + "ĠL SM", + "ĠâĪ Ĺ", + "ĠAn gle", + "Ġprov incial", + "Fe O", + "Å Ľ", + "ĠI ber", + "ress ors", + "Ġprolifer ating", + "z ers", + "organ ism", + "âĨ ĵ", + "Z O", + "c img", + "Ġun perturbed", + "Ġj j", + "Ġelectro dynamics", + "ĠEp it", + "NT s", + "ĠBlo om", + "Ġl anth", + "am inant", + "ĠSw ift", + "Europe an", + "Ġaff erent", + "Red uce", + "p ublished", + "ĠF itting", + "ĠF ungal", + "Ġtrib e", + "rec ting", + "Ġconjug acy", + "im eters", + "ĠC ec", + "ĠK H", + "cast le", + "Ġsept al", + "rele asing", + "Ġo ss", + "Ġ ¦", + "ĠMiss ing", + "ĠFat igue", + "ĠBase ball", + "Ġimmunoblot ting", + "Ġo h", + "or ations", + "Ġv ine", + "az y", + "ser um", + "Ġlook up", + "Ġne ovascular", + "ia h", + "so il", + "Ġair flow", + "ĠSlo an", + "h im", + "ç ļ", + "loc ated", + "z antine", + "ĠS uccessful", + "em inal", + "ĠD imensional", + "ĠN SA", + "ĠLog istic", + "emet ery", + "Ġb rak", + "ant al", + "so uth", + "Ġprot otypes", + "Ġadv ised", + "Ġideal ized", + "ophy tic", + "nb sp", + "B inary", + "H yp", + "J oh", + "p olation", + "Ġpoly vinyl", + "estim ated", + "Ġox ytocin", + "ĠLet ter", + "ĠImp air", + "Ġenvelop es", + "main ly", + "Ġm ys", + "Ġint ras", + "Ġbi ogenic", + "cy steine", + "Ġur ic", + "ĠCy an", + "ryp tion", + "Ġphotore ceptor", + "ĠT oxic", + "ĠG amm", + "Ġcontain ment", + "Ig G", + "S qu", + "Ġperf used", + "Ġbios ensors", + "Ġmag matic", + "R ate", + "ĠT f", + "Ġsec rete", + "Ġcritical ity", + "Ġcomposition ally", + "ĠBr uce", + "S Z", + "ĠS port", + "ĠE I", + "Ġdise ased", + "Ġpres chool", + "ĠHar vey", + "ĠPT H", + "Ġbil ayers", + "ĠOscill ations", + "ĠHon or", + "ĠC CN", + "ĠM OT", + "ĠL loyd", + "Ġtrap ez", + "Ġbud s", + "OFF SET", + "Ġmac romolecules", + "Ġbil irubin", + "ol ly", + "Ġutil ities", + "minist ered", + "Ġglob e", + "OLOG Y", + "rop ods", + "ĠMD M", + "ĠPy Object", + "mac roph", + "ĠP BMCs", + "osp heres", + "Ġcatast rophic", + "ĠNavig ation", + "ĠL SD", + "Ġcre am", + "Ġdere g", + "b onded", + "ren ts", + "Ġpotenti ation", + "Ġst ro", + "Ġst eeper", + "ulin um", + "Ġperiodon titis", + "ar ization", + "âĪ ª", + "amic in", + "Ġmagne tized", + "ĠNutri tional", + "Ġacc ord", + "ga ard", + "FT IR", + "r amethyl", + "ĠG le", + "M el", + "ĠCT L", + "Ġtransl ating", + "Ġauto immunity", + "oler ant", + "triang leq", + "am o", + "Ġv el", + "ĠH CN", + "ĠH amming", + "ĠVen us", + "ĠG ad", + "ĠO wing", + "In formation", + "ĠSchem es", + "caro tene", + "I ts", + "an is", + "Ġre play", + "Ġto uc", + "LE CT", + "ĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ ĠĠĠĠĠ", + "Ġtab ulated", + "ĠSchott ky", + "F ar", + "am ation", + "ĠR ies", + "Ġexp ects", + "ĠInst ability", + "Ġs ons", + "Ġdec k", + "Ġïģ ¥", + "ĠSign ature", + "Ġlith osphere", + "W W", + "m akers", + "ugh ters", + "Ġâİ ¡", + "ardi an", + "à ¦", + "Ġac cepts", + "ĠO SA", + "Ġγ δ", + "non umber", + "S elect", + "l ite", + "ĠA queous", + "ag awa", + "ĠEd inburgh", + "ĠMemb ranes", + "ĠS IG", + "ak ia", + "Ġtest es", + "Ġhel i", + "++ ++", + "Ġultraf ast", + "Ġmaneu ver", + "ĠD ate", + "ph in", + "ĠK ad", + "Ġtransfer ase", + "P ers", + "Ġt ones", + "ĠS GD", + "ant o", + "ĠO range", + "ĠGe ography", + "ĠAcc umulation", + "at y", + "Ġbe ating", + "Ġover lying", + "ĠND VI", + "ĠTown ship", + "j ing", + "ĠN OS", + "play er", + "ĠMD D", + "ĠHung arian", + "Ġd w", + "ĠH in", + "Ġvalid ating", + "Ġcolor imetric", + "ĠSupers ymmetric", + "F UNC", + "g ically", + "of uran", + "---- ---", + "Ġimp ing", + "similar ity", + "ĠD OX", + "ĠG lo", + "iv irus", + "list ed", + "Ġbus y", + "ipro floxacin", + "Ġan xi", + "Ġbl unt", + "Ġproced ural", + "Ġunknown s", + "Ad S", + "thick ness", + "follow s", + "cl osing", + "environment al", + "ĠFeed ing", + "un ami", + "end e", + "ip ine", + "Ġimpact ing", + "Ġpenet rating", + "amb ia", + "ĠWave let", + "Ġfilament ous", + "Ġl eng", + "ĠS CA", + "ĠE ther", + "met all", + "Ġfr inge", + "ĠAdj ust", + "us z", + "ĠR ey", + "ĠBo yd", + "Ġburn out", + "Ġco ok", + "Ġnow adays", + "ĠDispers ion", + "ĠRodrig uez", + "F actor", + "ĠO klahoma", + "Ġun ital", + "Ġpredict ability", + "Ġlith ography", + "è s", + "W illi", + "un al", + "ast ing", + "cor rection", + "ĠD ed", + "ĠSoci o", + "ĠChap man", + "ĠE co", + "Ġonc ogene", + "ĠDri ve", + "Ġfun nel", + "u is", + "ĠGEN ER", + "ĠA CR", + "Ġworkload s", + "Ġocta hedral", + "v ich", + "en burg", + "Ġimpro per", + "dec oded", + "Ġimmunos orbent", + "Ġinhom ogeneity", + "R K", + "on ically", + "Ġglycoprotein s", + "on ics", + "ĠF ok", + "ĠB ras", + "ĠCalc ulus", + "ĠM oss", + "ĠR K", + "Ġvi olet", + "Ġlymph omas", + "ens pace", + "ĠPal ae", + "Ġren in", + "ph ant", + "ĠRE D", + "Ġfault y", + "Ri emann", + "à ī", + "ĠEll i", + "B ol", + "T n", + "Y ang", + "g ender", + "Ġdet uning", + "Ġoper on", + "Ġinsectic ide", + "es i", + "am on", + "ĠS CD", + "ĠB ath", + "ĠâĢ ĸ", + "ĠGe ographic", + "Ġcycl ohex", + "ĠConf idence", + "Ġcom et", + "Ġfol ate", + "ob server", + "Ġvis itors", + "ext ra", + "at eness", + "ĠS PT", + "arc ane", + "Ġhol istic", + "sem i", + "ĠM ild", + "Ġsm ear", + "Ġcycl ase", + "Ġan ymore", + "Ġse agrass", + "Ġcons ortium", + "Ġfin ishes", + "cy an", + "duct ance", + "f rost", + "here after", + "Ġpres criptions", + "Ġcm d", + "ĠPer ceived", + "co ordinates", + "Ġst yl", + "ĠB ard", + "ĠH oll", + "Ġsi RNAs", + "s ugg", + "Ġth r", + "Ġmain land", + "SC H", + "Ġasser tions", + "Ġbab ies", + "Ġrecap it", + "T ok", + "Ġres ected", + "con struct", + "B er", + "Ġch oline", + "Ġunit arity", + "Ġcataly zes", + "det ector", + "ĠS MB", + "ter y", + "cl uded", + "ĠAb breviations", + "ĠOlive ira", + "L OC", + "z in", + "ĠLore nz", + "K ernel", + "ly n", + "ĠL EP", + "son i", + "Ġsept um", + "T MS", + "Ġun modified", + "bor ough", + "ĠAud io", + "Ġdoll ars", + "CM D", + "Ġnorth western", + "Ġpal mit", + "ragal actic", + "ĠM iz", + "F H", + "conf idence", + "N EXT", + "ĠA GE", + "ĠEq n", + "ĠClass es", + "Ġmis leading", + "ĠPK A", + "Ġanch ored", + "ĠR ip", + "ph ag", + "Ġint ubation", + "ĠAng ular", + "ĠB EC", + "Th r", + "Ġorgan isations", + "Ġcomfort able", + "Ġcommission ed", + "p oll", + "y dia", + "in stead", + "Ġpass word", + "Ġcompl iant", + "ĠPrec ipitation", + "ophosph amide", + "ust ers", + "Ġpneum ococcal", + "Ġtom ographic", + "tida e", + "ĠFir micutes", + "b w", + "ĠPD B", + "ĠGP Us", + "ĠPlan ar", + "Ġverb ose", + "Summ ary", + "l ance", + "ĠE GFP", + "ong ru", + "Com plex", + "ĠWhe at", + "uc he", + "ĠM CA", + "ĠPro jection", + "Ġstat s", + "Ġsumm and", + "dim ethoxyphenyl", + "ĠAB STRACT", + "Ġcaroten oid", + "Ġbro ke", + "ĠDesign ing", + "ĠHet ero", + "ĠCarls bad", + "C ov", + "in eral", + "Ġanaly te", + "ĠCo leman", + "Ġeigen state", + "ĠHol land", + "ERS ION", + "ĠD ak", + "ell ers", + "Ġà ĺ", + "miss ing", + "dep osited", + "ĠLinc oln", + "an ion", + "ĠSP EC", + "Ġfertil izer", + "ĠC PS", + "Ġco factor", + "Ġtre n", + "Ġcal endar", + "Ġyoung est", + "STAT US", + "ĠEXPERIM ENTAL", + "Ġs r", + "Ġn l", + "ĠM ES", + "Stud y", + "p adding", + "Ġat opic", + "ĠO G", + "Ġent rainment", + "AF M", + "ĠC ou", + "We b", + "ĠMic roscopic", + "Ġunambig uously", + "D ay", + "y otrophic", + "re ous", + "Ġs arcom", + "ĠV AL", + "Ġhind ered", + "ĠRE M", + "ot rexate", + "oc arcin", + "ĠAl k", + "Ġbre vity", + "fact ual", + "C er", + "di ox", + "oph ical", + "Ġly tic", + "T ake", + "Ġint end", + "ĠCl a", + "Ġaster oid", + "ĠS EP", + "ap enem", + "univers al", + "Ġo ceans", + "Ġmon oid", + "Ġsepar ator", + "ĠP orous", + "Ġpost operatively", + "Ġsem in", + "ĠDis play", + "Ġhyd rolase", + "transfer ases", + "Ġthromb us", + "ĠO v", + "ĠDie lectric", + "Ġcomp elling", + "ass ing", + "ĠM AS", + "ull ary", + "ĠMor i", + "ĠPath ogenesis", + "ĠBre aking", + "ĠPL GA", + "cool ing", + " §", + "Ġfe e", + "Ġreduc ible", + "Ġdiver ge", + "Ġque ues", + "Ġmush room", + "Ġdeacetyl ase", + "Y FP", + "Ġdis reg", + "ĠAr rays", + "process es", + "ĠTransport ation", + "Ġundet ectable", + "bur sts", + "Ġphospholip ase", + "O ption", + "as in", + "Ġn octurnal", + "te z", + "ĠDis ruption", + "oser ine", + "behavi or", + "ĠT ony", + "ĠK ot", + "ie val", + "Ġmy ofib", + "Ġhal ogen", + "ĠC PR", + "ploy ed", + "ĠPol ymers", + "Ġaden oma", + "Ġquar tile", + "Ġquatern ary", + "ĠIra q", + "Ġs ieve", + "Ġint ractable", + "Ġfabric s", + "continu um", + "ĠEmerg ence", + "P ot", + "iti sm", + "ven ess", + "ho e", + "Ġred es", + "ĠHR P", + "ploid y", + "pic uous", + "og o", + "ĠG ag", + "Ġnom inated", + "occup ied", + "Ġqu ench", + "rop olis", + "nucle otide", + "ĠEvent ually", + "Ñ ı", + "ĠCl ock", + "ĠSte ady", + "opol ymers", + "ĠA RE", + "ir nov", + "hel f", + "bl ob", + "down load", + "PL L", + "UN T", + "predic tions", + "Ġocc ipital", + "t oxic", + "ĠV ice", + "Ġang io", + "Cu O", + "Ġresist ances", + "ffl ffl", + "D istribution", + "G re", + "on amide", + "ĠI OP", + "UN EL", + "Ġa ids", + "ĠH UV", + "EC M", + "ĠP AD", + "ĠAg NPs", + "Pr int", + "Ġlam ellar", + "ĠUltr ason", + "se vere", + "ĠAn notation", + "N IR", + "s gn", + "ĠO ften", + "Ġit erate", + "Ġcar riage", + "sp herical", + "ĠF rid", + "Ġdiff ract", + "ĠBas al", + "Ġuns atisf", + "ĠDys function", + "arboxyl ic", + "ĠCol lective", + "Ġdegrad ing", + "Ġadi posity", + "Ġfif ty", + "Ġpar s", + "ĠOptim ized", + "oc aine", + "Ġb b", + "ĠS hip", + "ĠL W", + "Ġtre mor", + "Ġà £", + "Ġnucle ons", + "Ġscienti st", + "ĠM ish", + "g ression", + "ĠM erc", + "ĠF lem", + "Ġcor als", + "In cre", + "ĠD SP", + "Ġdef enses", + "dim er", + "ather ine", + "ot ubes", + "str ide", + "ĠAlter ations", + "Ġo est", + "ĠB IC", + "Ġradi ated", + "Ġket amine", + "Ġdissimilar ity", + "ĠAnc ient", + "ĠH ed", + "Ġatt r", + "ĠIs a", + "Ġion ospheric", + "Ġgover nor", + "ĠEstim ated", + "Ġultr athin", + "Up date", + "Ġimmuno assay", + "Ġconject ured", + "Ġ REF", + "ĠSi egel", + "Ad v", + "M em", + "Ġp ups", + "ĠAP PL", + "ecom posable", + "j ournal", + "ĠR ol", + "ĠL ob", + "ring ton", + "Ġnons ingular", + "Ġcit ric", + "ion es", + "os itis", + "AL Y", + "Ġmen tions", + "ĠMark ers", + "algebra ic", + "Ġflatten ed", + "Ġm ail", + "ĠT GA", + "ĠP MA", + "ĠN aval", + "Ġfac ilitation", + "Ġun identified", + "Ġem pathy", + "ject ories", + "log its", + "Ġperman ently", + "Ġbott les", + "ĠBeng al", + "Ġpean ut", + "Ġcapill aries", + "eren ts", + "ĠLo oking", + "chang es", + "ĠMag ell", + "ĠC MC", + "ĠV erm", + "Ġsubs cales", + "dem and", + "ore xia", + "Ġachieve ments", + "ĠRobust ness", + "ĠWall ace", + "ĠD TT", + "og els", + "ock er", + "ĠSp ike", + "Ġpain ter", + "Ġbus es", + "Ġpoll uted", + "Ġt ort", + "ĠP PP", + "ne x", + "ext ended", + "ucal ypt", + "Ġpro static", + "ĠF CC", + "Ġk ick", + "oy al", + "epoch s", + "h ss", + "y on", + "Ġd ans", + "ĠA w", + "Ġad versely", + "Ġalt ogether", + "Ġophthal m", + "Ġc pu", + "ĠF RET", + "Ġfore nsic", + "Ġhot spots", + "Ġpain tings", + "Ġo mn", + "Ġp S", + "og lu", + "of ol", + "FT s", + "Ġderm at", + "prag ma", + "Ġb ump", + "ĠC ir", + "a S", + "Ġn aked", + "ĠN LS", + "ĠSp itzer", + "Ġsal vage", + "Ġintu itively", + "Ġcas ual", + "Ġf ired", + "ver ages", + "ĠBur den", + "W ang", + "yle m", + "Ġradi ographs", + "ĠSch iff", + "OL UTION", + "C ross", + "Ġh ints", + "ow ing", + "ĠSt reng", + "ĠAN Y", + "Ġwor ry", + "ĠRog er", + "Ġtrabec ular", + "B and", + "ĠN ec", + "ip es", + "to ol", + "ĠIL C", + "i Äĩ", + "o cean", + "ĠA ri", + "AM A", + "ĠVer tex", + "activ ate", + "L ocation", + "on ts", + "Ġh s", + "Ġsl ender", + "ref ring", + "ĠEnd ogenous", + "adi abatic", + "Ġcryp tic", + "Ġerad ication", + "ĠKev in", + "Ġm c", + "Ġcardi o", + "Ġphosphor yl", + "W itten", + "Ġs cl", + "ĠI w", + "ĠM ade", + "Ġfound ing", + "ofl ag", + "al ine", + "hor izontal", + "ĠGeneral ization", + "psy chiatric", + "ĠD uncan", + "ĠSn O", + "ĠA ar", + "Ġg g", + "Ġpre mi", + "ĠSt rom", + "ĠEx plan", + "Ġleth ality", + "Ï Ĥ", + "od o", + "Ġsub scrib", + "ĠST UDY", + "Ġoutper formed", + "Ġcoval ently", + "M HC", + "f ail", + "ĠK ac", + "EG R", + "ĠTR I", + "rob ot", + "ĠCandid ate", + "ĠTN BC", + "Ġarchae ological", + "E ukary", + "Ġl ava", + "di pole", + "Ġunc ons", + "An ti", + "Ġpred nis", + "ĠRob in", + "Ġstratig raphic", + "Ġ ¤", + "Ġfin ance", + "ĠStud io", + "re nder", + "Ġre aring", + "Ġg er", + "ĠO pt", + "ĠMan ifolds", + "Ġdest abil", + "Ġtel omerase", + "Ġpick ing", + "Ġamplic on", + "Ġyear ly", + "ĠN CC", + "ins er", + "ĠEn richment", + "ĠMicro structure", + "ĠWar ren", + "ophys ics", + "Ġfif teen", + "Å ij", + "Ġreview er", + "Ġsk illed", + "Ġmagnet oresistance", + "Ġrecon figuration", + "Ġpo et", + "Ġpred etermined", + "Ġcry opres", + "Ġattract ors", + "Ġprojec tile", + "ĠC rystals", + "ĠM CM", + "ĠX anth", + "Ġclock wise", + "regn ant", + "Ġg ated", + "ry za", + "ĠP rosp", + "ad in", + "Ġm olybdenum", + "ĠAl ps", + "ĠBal d", + "Ġhall uc", + "ud o", + "Ġmon t", + "ĠFl ash", + "Ġpull ing", + "ĠL Q", + "ĠWals h", + "ĠTh omson", + "mes on", + "Ġinter cal", + "Ġel apsed", + "FF FF", + "ĠFore casting", + "à ¯", + "ĠL SP", + "end orf", + "Ġx ml", + "sub strate", + "M u", + "d uring", + "oc onstr", + "EM A", + "Ġïĥ «", + "ĠD FS", + "ĠV on", + "Ġfat hers", + "Ġunc o", + "ĠUnd erg", + "Ġmultiplex ing", + "at ra", + "Ġco hesive", + "ĠU I", + "ĠPre v", + "çļ Ħ", + "c um", + "h f", + "ĠS CN", + "atal ysis", + "ĠAr sen", + "amp ing", + "ĠPl astic", + "ĠMad ison", + "Ġsuprem um", + "ĠC ited", + "Ġare n", + "isk i", + "in el", + "st ro", + "Ġcor rupted", + "Ġgl ab", + "Ġcardi opulmonary", + "Ġprag matic", + "C AG", + "St ack", + "thi oxo", + "ĠRepro ductive", + "Ġste atosis", + "B est", + "ĠB ars", + "Ġr acing", + "ĠU tah", + "equ ivalence", + "ĠFif ty", + "ĠCytok ine", + "Ġutil ised", + "hor izon", + "our acil", + "ivers ary", + "em er", + "ĠQ uestions", + "Ġlink ages", + "anche z", + "V V", + "Ġphotod et", + "k owski", + "RE ST", + "Ġhost ing", + "Ġpush ing", + "Ġneurot oxicity", + "S Q", + "r st", + "Ġh ockey", + "Ġtri ps", + "ĠInd oor", + "em atics", + "Ġtrans ect", + "ĠAB I", + "ag ar", + "âĪ ļ", + "eg enerate", + "ĠQ P", + "MI D", + "ĠAc cept", + "ĠCy ber", + "N orth", + "Ġd θ", + "all a", + "Ġbra id", + "f inding", + "al in", + "ĠL ST", + "ĠL ax", + "ud in", + "Ġi NOS", + "con vert", + "AC A", + "ĠGu an", + "Ġlymph ocytic", + "Ġsyll able", + "ĠT OR", + "ĠS CR", + "ĠA J", + "Ġout burst", + "bl adder", + "OT A", + "aud io", + "chrom en", + "Ñģ ÑĤ", + "Ġgrate fully", + "Ġt iling", + "Ġqu it", + "sh an", + "ĠAcc retion", + "Ġnarrow ing", + "ĠInduc es", + "M ic", + "Ġf uc", + "Ġth alamus", + "AN ES", + "Ġquatern ion", + "ĠLister ia", + "d uality", + "he nd", + "and e", + "Ġpa ro", + "Ġinsp ected", + "ques tion", + "ĠH oney", + "Ġch unks", + "Ġfore arm", + "radi ents", + "ific antly", + "ob ank", + "Ġsome where", + "Ġmon etary", + "ĠLouis iana", + "Ġem ulsions", + "Ġprogram mable", + "Ġmanif ests", + "ĠMart inez", + "Ġt ed", + "em en", + "ann i", + "Ġoverl aid", + "Ġvir ulent", + "M ask", + "ĠU tility", + "Ġw k", + "ose xual", + "ĠEar l", + "d ar", + "h dr", + "ract ors", + "Ġconstruct or", + "Ġnas cent", + "inz burg", + "ĠCra ig", + "Ġplex us", + "re verse", + "og rav", + "tag s", + "Ġcalibr ate", + "à ®", + "Ġh ide", + "ĠF ol", + "Ġinter acted", + "Ġconf ron", + "mark et", + "Ġsoci odemographic", + "ĠLuc as", + "ĠM CT", + "ĠR SS", + "Ġmicro plate", + "under st", + "I tal", + "ĠC MR", + "rec y", + "ĠPC OS", + "Ġdetox ification", + "Ġsubt ree", + "Ġsubs ections", + "Ġpropos itions", + "Acknowledg ements", + "reinfor ced", + "l is", + "ĠC IR", + "Ġim printed", + "vi um", + "af ic", + "Ġcheck list", + "ĠR x", + "ĠE ph", + "Ġsol der", + "trans formation", + "ĠStra it", + "az ar", + "Ġhand ler", + "ke let", + "B CL", + "M ath", + "Ġw ishes", + "um inescent", + "ĠP EC", + "ir t", + "yl idene", + "Ġlo osely", + "na issance", + "IL s", + "fo il", + "ĠGN U", + "ĠK et", + "vi x", + "ĠPl ain", + "ĠRE S", + "Ġparent ing", + "ĠConn ection", + "Ġrhiz osphere", + "opre valence", + "i atic", + "Ġp A", + "ĠV il", + "set ting", + "ĠRe LU", + "ĠBO OST", + "Ġappreci ate", + "b x", + "ore st", + "olog ie", + "Ġpal p", + "fo o", + "us ual", + "Ġquestion ed", + "Ġtrig on", + "ĠGF AP", + "ĠKy oto", + "dis e", + "anti le", + "ü ck", + "ĠQuanti zation", + "Ġs cler", + "Ġbe half", + "ĠD uality", + "Ġmagnetic ally", + "Ġeleg ant", + "U A", + "ep is", + "Ġsub clinical", + "ont rol", + "ĠChemical s", + "Util s", + "Ġlow ers", + "ext raction", + "Ġampl ifiers", + "ĠEnt ry", + "ĠWOR K", + "Ġthrombocyt openia", + "M il", + "id us", + "emb ry", + "man ager", + "ĠCo ordination", + "ĠPhen otypic", + "ch unk", + "Ġhypot ension", + "Ġcry ogenic", + "Ġreact ants", + "ĠM MSE", + "Ġcent ros", + "ĠBut ler", + "Ġcav itation", + "ĠLess ons", + "es tion", + "ĠM IS", + "ass oci", + "AP E", + "ĠEuler ian", + "Ġrecre ational", + "ĠNe o", + "ĠCD M", + "rep eat", + "det ails", + "B al", + "ST A", + "Ġâī º", + "ĠCam ero", + "ĠTele vision", + "Ġwork force", + "Ġcomputer ized", + "Ġextra ordinary", + "Ġrib onucle", + "Ġhydroph obicity", + "ĠFeas ibility", + "O l", + "T w", + "ĠM am", + "ĠF AC", + "pro fit", + "negl igible", + "ĠF ruit", + "Ġear s", + "Ġshe aring", + "ĠCorrespond ing", + "f un", + "i eck", + "m os", + "ĠE MI", + "ĠSome times", + "Ġfluor ine", + "Ġdeterg ent", + "Ġal g", + "rac es", + "iv able", + "CO MM", + "ĠSw itch", + "Ġstra ined", + "vir tual", + "Tem perature", + "Ġcredi ble", + "ĠG PCR", + "ĠDe bye", + "ĠL it", + "Ġhe mic", + "Ġtrans ducers", + "met ast", + "adi ene", + "Ġoryz ae", + "t n", + "Ġafter noon", + "ĠArab ian", + "ĠChrom atin", + "Ġxen ografts", + "Ġcrypt ographic", + "Ġax illary", + "Ġvolunte er", + "ĠNev ada", + "Ġp ions", + "un known", + "ĠF U", + "ven ously", + "radi o", + "ĠLab our", + "ĠVill age", + "R ic", + "Ġmet at", + "Ġser otypes", + "reg ression", + "s aturation", + "re ra", + "Ġfar ther", + "Ġround ing", + "Ġlib itum", + "Ġsh uff", + "ĠO w", + "Ġlocal ised", + "ĠAL G", + "Ġhypert rophic", + "p pm", + "im ine", + "ĠA the", + "Ġan hydro", + "Ġsup ramolecular", + "Ġmac ros", + "acet ed", + "ĠOl iv", + "Ġmotiv ational", + "ĠC ave", + "enz ie", + "Ġaffili ated", + "Ferm i", + "Ġequal ities", + "ĠMil an", + "Ġd ressed", + "Ġan ger", + "ad os", + "Ġav g", + "ĠPh on", + "Ġradio activity", + "ĠE ch", + "Ġorgan oids", + "Ġïģ §", + "ĠAnth rop", + "l ateral", + "Ġal pine", + "Ġaud it", + "W ER", + "ĠC SC", + "Ġrank ings", + "ĠER R", + "GL ER", + "Ob viously", + "ĠMad rid", + "obenz ene", + "other mia", + "Ġrespons ibilities", + "omes tic", + "ĠInf lation", + "Ġepidem ics", + "Ġt aut", + "ph os", + "ĠUn less", + "Ġge omagnetic", + "ĠCF TR", + "vel d", + "ari etal", + "Ġend otoxin", + "AD P", + "Ġsupp ressive", + "rand ial", + "Ġïĥ ©", + "exc ited", + "ĠInn ate", + "ĠL ópez", + "omyc etes", + "Ġbe autiful", + "ir k", + "ĠH wang", + "ĠU SE", + "ÏĢ i", + "Rec ord", + "Att ribute", + "Ġre acts", + "ĠB und", + "Ġcow ork", + "Ġconf luence", + "ĠReg ardless", + "Ġmetagen omic", + "M AL", + "Ġa ided", + "ang a", + "Ġam n", + "ĠI CI", + "ĠP ML", + "Ġdel ivers", + "Ġke yp", + "Ġbeet les", + "Ġoxid ant", + "Im mun", + "Ġrhyth mic", + "fem ale", + "J C", + "P AD", + "gen itor", + "A MS", + "c atalytic", + "ĠM om", + "ĠH ert", + "ad ish", + "Ġcont ention", + "Ġy olk", + "Ġdem yel", + "Ġsuc c", + "Ġtravel s", + "V e", + "ĠF ul", + "ĠR if", + "Ġint rons", + "enc aps", + "col our", + "Ġhot el", + "Ac cess", + "ado op", + "Ġcoal ition", + "ĠMu h", + "ĠL TP", + "aut om", + "ĠL ak", + "Ġrem edi", + "Ġtra iling", + "ins ulator", + "ĠRel ig", + "ĠHud son", + "em ics", + "O Ac", + "our t", + "Ġrel ic", + "ĠMi xture", + "Ġcalor imeter", + "ĠR DF", + "ĠHod gkin", + "Newton ian", + "ĠDelay ed", + "ĠNorthe ast", + "her ing", + "Ġhel ices", + "Ġprincip ally", + "Ġsusp icion", + "Ġextrem ities", + "Ġdead line", + "ĠEnter ococcus", + "m j", + "Ġh p", + "ĠN AS", + "ous s", + "Ġintram uscular", + "L IN", + "Ġch icks", + "S core", + "Ġf ür", + "ĠR SA", + "Ġk r", + "Ġphot ography", + "Ġclear ing", + "hol omorphic", + "t hem", + "Ġp om", + "ĠL is", + "Ġdisc ard", + "Ġgu an", + "c x", + "ub ov", + "ĠCons istency", + "Ġple i", + "ĠUr inary", + "Ġbread th", + "E I", + "m echan", + "Ġd q", + "ĠBl ast", + "co eff", + "IL D", + "Ġunem ployment", + "A rm", + "ĠC n", + "mod erate", + "Ġagg ress", + "Ġcircum f", + "l os", + "Ġb aro", + "velop e", + "Ġulcer ative", + "Ġhelic ase", + "H W", + "K G", + "r ion", + "Ġgen otyped", + "Ġar id", + "ĠAndre as", + "Ġthere of", + "ĠOper ating", + "ĠNE W", + "ĠAntib acterial", + "ĠDar win", + "Ġrefere e", + "Ġd ome", + "ag us", + "ĠD MD", + "AT OR", + "Current ly", + "ĠInequ alities", + "d N", + "ol ymer", + "em pirical", + "ĠBra un", + "F IN", + "ĠO ber", + "pr one", + "Ġdimin ish", + "ĠGrad uate", + "ĠT SH", + "ĠH su", + "oid osis", + "Ġepid ural", + "Ġreinfor cing", + "Ġthe atre", + "Ġv ib", + "ĠH ob", + "col lection", + "MAN GLER", + "ĠH ecke", + "Ġtr uck", + "Ġmotiv ates", + "ĠV OC", + "Ġun bound", + "ram id", + "ious ly", + "ĠFern ández", + "ĠF acial", + "ox azol", + "Ġtre adm", + "ĠRes id", + "Lo ader", + "ĠRun ning", + "otin ib", + "P AC", + "V II", + "i u", + "Ġc ite", + "ĠH ockey", + "ES C", + "rho ea", + "Ġmac aques", + "Ġmedi ast", + "at im", + "ĠT MP", + "ĠA GB", + "ĠR up", + "ug a", + "Ġass urance", + "p ay", + "en ergies", + "ĠK end", + "till ery", + "Ġanest hetic", + "Wind ow", + "Ġbe verages", + "ag uchi", + "ĠFL T", + "ĠBound ed", + "ĠPolymer ase", + "S am", + "ĠOr bit", + "Ġseason ality", + "Ġtachy cardia", + "este em", + "ĠPerf ect", + "S EC", + "l ater", + "tal e", + "ĠForm ally", + "L G", + "z yn", + "Ġmicro algae", + "Ġindi um", + "erenn ial", + "ĠI PT", + "Ġk j", + "ĠPD A", + "Ġassim il", + "whe el", + "ĠS OS", + "ĠP FC", + "Ġdec oded", + "AT S", + "Ġsoci etal", + "Ġdiffe omorphisms", + "Ġtra verse", + "Ġcoll ateral", + "g ives", + "ĠC EN", + "Ġra nd", + "Ġher self", + "Ġpay ments", + "Ġps i", + "âIJ £", + "ĠGrom ov", + "Ġacc idental", + "ĠRe ality", + "Ġlog istics", + "Ġrobust ly", + "ĠSar ah", + "N U", + "d ates", + "ĠC UR", + "ĠD ream", + "Ġdegrad es", + "ĠGE O", + "Ġbutter fly", + "Ġpend ulum", + "q a", + "Ġas partate", + "pseud o", + "Ġall osteric", + "der r", + "ĠQ oL", + "Ag ilent", + "ĠHard ware", + "ĠCum ulative", + "Ġp n", + "qu antitative", + "Ġapp raisal", + "Ġpoly acrylamide", + "Ġmild ly", + "Ġcontrac eptive", + "ĠPubl ished", + "Ġupl ift", + "be h", + "Ġadap tor", + "ĠEqu al", + "thien yl", + "at ched", + "Ġrep ly", + "Ġup wards", + "Ġaut opsy", + "sim ulation", + "Ġgran ite", + "Ġpel vis", + "Ġhat ching", + "ĠS PS", + "ĠG EM", + "illi ard", + "ĠRet rospective", + "ĠEarth qu", + "ĠInvestig ations", + "ĠMer ck", + "Ġchol angi", + "Ġinfiltr ating", + "Ġoverestim ated", + "focus ed", + "A min", + "Ġpre eclampsia", + "osp atial", + "ĠTRA IL", + "P air", + "Ġsub marine", + "Ġprote olysis", + "Ġcomple ments", + "ĠKir ch", + "Ġcent rom", + "Ġn ap", + "ĠWe ar", + "Ġpun ishment", + "Ġautoreg ressive", + "Ġcompos er", + "ĠEng el", + "Ġana emia", + "ĠKron ecker", + "ĠD id", + "ĠCar p", + "pe er", + "Ġbug s", + "ĠIslam ic", + "ith romycin", + "Ġcons ec", + "Ġfam iliarity", + "et axel", + "Ġint ensively", + "ĠU pt", + "Ġindic a", + "AD A", + "ĠChe byshev", + "Ġhierarch ies", + "Ġworth while", + "Ġburn ed", + "ĠHM GB", + "Ġpolyg onal", + "b rile", + "Ġz oon", + "war ning", + "Eukary ota", + "d A", + "ĠRep eated", + "ĠCast ro", + "Ġmet ropolitan", + "ont inuous", + "ĠBar nes", + "ĠPost operative", + "Ġcyt ology", + "Ġspot ted", + "vers ity", + "aff ine", + "sor ted", + "ĠPro to", + "ĠDes criptive", + "Ġhit ting", + "Ġanalog ously", + "feed back", + "Ġspirit ual", + "ĠL INE", + "ress in", + "oph thal", + "Ġpoly unsaturated", + "Ġpi per", + "observ ations", + "ĭ ¤", + "ir re", + "ĠW NT", + "Ġund ifferentiated", + "eral d", + "ĠCT C", + "Ġhomomorphism s", + "ĠNeon atal", + "F in", + "ro zen", + "ĠL ux", + "Ġmod ifier", + "ĠK A", + "osa ur", + "Ġinterven tional", + "ĠHa pl", + "Ġlumin ance", + "Ġun fortunately", + "Ġsleep ing", + "Ġcit rus", + "reson ance", + "Ġm oss", + "ul ay", + "ĠP enn", + "ad ministration", + "ĠN GF", + "Ġsec ured", + "ĠA Es", + "ĠP WM", + "oc co", + "ob uf", + "Ġphotoc urrent", + "ĠScilab Double", + "A pril", + "Ġfor amin", + "Ġpar alysis", + "ĠQu ark", + "eq ref", + "ĠBro oks", + "ĠColl ision", + "W ar", + "Ġ ig", + "am ylase", + "ist ered", + "Ġret raction", + "ĠMulti plex", + "ĠMa o", + "Com mon", + "ĠEcon omics", + "ĠCriter ion", + "ĠC CC", + "ĠLe i", + "Ġorth orhombic", + "Ġaliqu ots", + "Ġst ric", + "ĠL enn", + "Ġdis closure", + "amet h", + "Ġnormal isation", + "Ġphyl ogen", + "ĠQTL s", + "ĠVers us", + "ĠUtil ization", + "y ne", + "un ted", + "ĠD uff", + "ĠG J", + "Ġoptim ised", + "iform is", + "ĠIncre ases", + "ĠFD G", + "ĠBatter y", + "P he", + "ĠC CS", + "Ġch rys", + "of en", + "Ġmultic omponent", + "disc ussed", + "bond ing", + "ore tically", + "ĠAll iance", + "Ġhead quarters", + "ĠGlas gow", + "Ġb out", + "Ġe ighth", + "Ġinc urred", + "ĠBar ry", + "Ġquad ric", + "Ġdu ties", + "Ġmind fulness", + "rastruct ural", + "T rain", + "sh itz", + "CD C", + "Ġdys lipidemia", + "Ġalle ged", + "Ġbron ze", + "Ġattain ment", + "Q D", + "rom bin", + "Ġap olipoprotein", + "own ed", + "Ġge ographically", + "work ing", + "ĠBl ind", + "Ġdon ation", + "ĠSer ge", + "Ġspread s", + "ĠHeter ogeneity", + "ĠFr é", + "Ġdef er", + "Ġlif ts", + "EGF R", + "ĠPort land", + "Ġbrother s", + "ĠTrypan osoma", + "in ian", + "Ġp ressed", + "Ġtrans duced", + "Ġpol yn", + "Ġlist eners", + "bo ards", + "ĠSustain able", + "al an", + "ĠS ullivan", + "Assum ption", + "oft en", + "j p", + "or ative", + "pl ers", + "Ġmodular ity", + "ĠHerm ite", + "Ġhydroxy apatite", + "ĠHir sch", + "D eterm", + "f acing", + "ir radiated", + "Ġhar sh", + "Ġtoler ate", + "ĠT rap", + "ĠA ware", + "ot ax", + "AT ING", + "Ġhist opathology", + "ĠIsra eli", + "clock wise", + "z ig", + "ĠJ C", + "ĠQu ick", + "ĠSL AM", + "Ġf ox", + "ĠR av", + "gener ating", + "Ġhemat oxylin", + "yl transferase", + "Ġcorrobor ated", + "F DR", + "o ard", + "Ġequ id", + "Ġ »", + "Ġneuro psychological", + "Ġbreak up", + "Ġemphas izing", + "Ġemiss ivity", + "block ing", + "Ġpar all", + "Ġtil ting", + "Ġp eng", + "ĠSc an", + "Ġion osphere", + "Ġm ount", + "fore st", + "Ġcall us", + "α β", + "ĠChrist mas", + "ĠMag azine", + "eval uate", + "ĠP ag", + "ĠBe at", + "Ġaccum ulates", + "Ġcrow ding", + "unn eling", + "Ġ Ñ", + "ĠA CP", + "ge ometry", + "MP T", + "Ġpharmac ists", + "Ġpull back", + "Ġduc tility", + "S upervised", + "Ġlymph oblastic", + "pe a", + "typ ical", + "bro ken", + "F c", + "Ġl ining", + "ĠD um", + "Ġmulti ples", + "ó w", + "Ġmer its", + "Ġextinc t", + "ĠNur sing", + "ĠExplo iting", + "ĠBhatt ach", + "J uly", + "t ze", + "th romb", + "te enth", + "Ġtoxic ities", + "Ġdenit rification", + "Ġex position", + "Ġim perf", + "Ġsur name", + "po inter", + "ĠEr n", + "ĠAbund ance", + "ĠD unn", + "oph ora", + "Ġtool kit", + "Lo ad", + "ĠDeriv ation", + "c ould", + "ĠC aspase", + "ĠSp rague", + "ĠTr p", + "Ġbright est", + "ill ard", + "Ġinter disciplinary", + "Ġqu arant", + "Ġhyper surfaces", + "eli ac", + "ĠAL MA", + "Ġacryl ic", + "Ġgent le", + "De ep", + "ĠPand emic", + "Ġinf easible", + "Ġradi ol", + "AB P", + "Ġmes enteric", + "ylind er", + "pack ed", + "Ġsomat osensory", + "Ġp ave", + "ĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ ĠĠĠĠĠĠ", + "Ġpharmac ology", + "Ġtan h", + "ĠMt b", + "Ġchim pan", + "Ġautophag ic", + "Ġwithd rawn", + "ĠM CC", + "Z F", + "ĠS pl", + "ĠL au", + "Ġbi ologic", + "elect rons", + "Ġunderestim ation", + "Ġcharacter ise", + "circ ular", + "ĠTHE ORY", + "B rown", + "F BS", + "J o", + "d G", + "m ars", + "ar ticular", + "ĠP ren", + "ĠM SA", + "ĠIt em", + "Ġsem idefinite", + "ĠGib son", + "Ġtour ism", + "ĠK ok", + "Ġexpos ing", + "Ġintra venously", + "dri ver", + "ĠFort unately", + "ĠS ach", + "Ġcont aminant", + "Ġab rog", + "ĠEm otional", + "VAL UE", + "dispers ion", + "Jac obi", + "ĠImper ial", + "I on", + "L in", + "f idelity", + "ĠB irds", + "ĠCon current", + "mat ism", + "co al", + "Ġt q", + "ĠMn O", + "Ġfoss ils", + "Ġt ender", + "Ġr hesus", + "Ġblo om", + "ab dominal", + "Ġscal p", + "Ġhome ostatic", + "ĠH unt", + "ĠPharmac okine", + "b rown", + "ĠH YP", + "Ġdiss ociated", + "ĠSoc cer", + "ĠInequ ality", + "m aker", + "Ġsh ade", + "ĠZ ur", + "obs ervation", + "al tered", + "U U", + "Ġthe or", + "ep it", + "Ġphyl um", + "Ġvig orous", + "ĠA CM", + "Ġmeth otrexate", + "dem ographic", + "Ġsing ly", + "ĠPhys iology", + "Ġremod elling", + "ĠK rist", + "rop ies", + "flow s", + "hard ness", + "igh teen", + "bre ve", + "ĠRet inal", + "Ġscin till", + "Ġutter ance", + "Ġmonolith ic", + "ĠVl ad", + "ĠL MC", + "ip t", + "arrow s", + "ĠPubl ishing", + "ĠStrept omyces", + "f al", + "Ġtroposp here", + "B en", + "c andid", + "ĠS ic", + "tim ore", + "L en", + "in en", + "amp ered", + "ĠMon th", + "Ġopp onent", + "Aug ust", + "Ġst aggered", + "cent re", + "exp ect", + "Ġred dening", + "ĠT l", + "hib ition", + "Ġmicro particles", + "ĠInt rac", + "ĠInitial ize", + "Ġdict ated", + "D ig", + "ä º", + "he aling", + "Ġd V", + "Ġappe tite", + "Ġunus ually", + "ĠAstr onomy", + "Ġw are", + "Ġover coming", + "Ġcoll iders", + "ĠUS ING", + "ocardi tis", + "P ick", + "Ġd ub", + "ĠJ ason", + "ĠEd itor", + "ê ³", + "Ġl ags", + "Ġcl s", + "Ġsur gically", + "ĠPV C", + "par ticularly", + "Ġred ist", + "Ġlog ics", + "sk ii", + "ĠD VD", + "Ġcomp ly", + "az i", + "ĠInter acts", + "bo olean", + "ĠER P", + "ĠEr r", + "otrans piration", + "ĠPé rez", + "A sp", + "am iliar", + "ĠF etal", + "Ġdecl aration", + "k inson", + "t ube", + "Ġphysiological ly", + "c ue", + "ĠE ri", + "Ġen vision", + "ex ternal", + "inter mediate", + "Ġshop ping", + "ĠF ras", + "ĠH aj", + "ĠAl ger", + "Ġanthrop ometric", + "Ġcancell ed", + "H PV", + "k ers", + "af a", + "Ġvulner abilities", + "electro lyte", + "ĠGonz alez", + "íķ ĺ", + "q v", + "Ġde af", + "Ġbut yrate", + "ĠCo efficient", + "Ġstar burst", + "Ġpolym orph", + "ĠE RA", + "ĠMax imal", + "ĠMu eller", + "Ġabsor bers", + "Ġa rab", + "re tions", + "Ġne bula", + "Ġmin es", + "е н", + "%%%%%%%% %%%%%%%%", + "Ġband pass", + "Ġpoly urethane", + "Re LU", + "ĠFer ro", + "pic illin", + "C AD", + "T y", + "ĠP CD", + "ĠB AC", + "Ġplankton ic", + "F er", + "Ġc ricket", + "Ġman ure", + "oun s", + "âĪ §", + "Ġtor ques", + "m itian", + "Ġt ion", + "ĠG arden", + "Ġfol k", + "Ġsusp icious", + "à Ĥ", + "od ia", + "ist encies", + "ãĢ ī", + "ĠInv itrogen", + "ĠS UN", + "ĠSuper ior", + "Ġdiscontinu ation", + "c ock", + "k not", + "Ġext ens", + "ĠWh itney", + "Ġhar bour", + "P ID", + "Ġp mol", + "ol ymph", + "Ġg ard", + "ĠO varian", + "Ġrep ressed", + "ĠAl ab", + "Ġà Ħ", + "ule x", + "ĠAust rian", + "Ġa flat", + "Ġpar athyroid", + "Ġgroup oid", + "Ġdev ast", + "ĠK v", + "Ġbor row", + "Ġuncon ventional", + "Ġbore hole", + "Ñ Į", + "ĠD ays", + "Ġlex ic", + "N or", + "ĠH erc", + "ass ays", + "Ġdraw ings", + "def in", + "ev oked", + "ĠÈ ³", + "ĠSund ay", + "ĠC hes", + "cons idered", + "oped ic", + "larg er", + "om inant", + "ĠB omb", + "Ġf iss", + "Ġh inge", + "ĠI onic", + "Ġdest ro", + "Ġcomplement arity", + "Hig gs", + "or ia", + "our cing", + "ĠX in", + "Ġwork space", + "ĠLig and", + "Ġstrugg le", + "ĠImmunohist ochemical", + "Ġn ick", + "ĠGu ard", + "rig id", + "Ġaqu aculture", + "Experim ent", + "Ë Ī", + "ti r", + "ĠS MS", + "Ġbe vacizumab", + "Ġmod ulations", + "Ġge ophysical", + "Pro perties", + "Ġpain ted", + "Ġs anc", + "Ġin timate", + "Ġn ail", + "id entity", + "Ġdat um", + "anth us", + "Ġdy adic", + "Ġconvinc ing", + "e lem", + "Ġh iding", + "Ġr ugby", + "ĠX e", + "ĠIs sue", + "Ġves icular", + "ĠKel vin", + "Ġdist ancing", + "echn ology", + "af ers", + "ĠAut hentic", + "Pub Med", + "Ġdeform ity", + "ĠCha os", + "ĠSh ield", + "ox etine", + "ĠWork ers", + "ĠMO I", + "Ġdehyd rated", + "ĠGast ric", + "Ġmonomial s", + "od ox", + "ĠD ublin", + "Ġle ishman", + "Ġpl anner", + "circ le", + "Ġfract ured", + "ĠLoc ally", + "ĠAc tions", + "Ġlic hen", + "h annel", + "ĠT AG", + "Ġdec isive", + "ĠQ M", + "Ġbiom aterials", + "ĠVirus es", + "hydrox yphenyl", + "ĠI AA", + "ĠR U", + "vi olating", + "Ġp ockets", + "ch ant", + "ib erg", + "lect omy", + "oler ae", + "Ġattract ing", + "Ġket one", + "ĠC od", + "Ġmicro arrays", + "ĠMet als", + "benz oyl", + "Ġsemigroup s", + "Ġreconstit uted", + "s ites", + "an abe", + "ĠCom posites", + "Ġwild type", + "Ġleuk aemia", + "Ġmur der", + "Ġdent in", + "H ub", + "O rient", + "on n", + "syn chron", + "Ġchron ically", + "methylene amino", + "Ġdop ant", + "Ġf ecundity", + "de lete", + "rem ia", + "ĠNH L", + "iti dis", + "Ġcop ep", + "X I", + "Ġloc ating", + "ĠZ IKV", + "hex a", + "ĠFactor ization", + "ynch us", + "M ethyl", + "h agen", + "ĠP aw", + "ne ath", + "bs ite", + "Ġtrac he", + "B re", + "u w", + "ro it", + "Ġre acting", + "ĠB ae", + "Ġquoti ents", + "Ġp ins", + "ĠV ARI", + "Ġequ ine", + "ĠRun ge", + "Ġcolon ial", + "measure ment", + "ĠAbb ott", + "Ġorth o", + "Ġmeta phor", + "benz oic", + "ĠTransform ers", + "L ower", + "ĠO VA", + "radi al", + "Fl ag", + "author bs", + "Ġtreadm ill", + "Ġenter ica", + "ĠJul ia", + "Ġpl umes", + "Ġinv oke", + "chlor ic", + "ol ino", + "Ġinter ruption", + "sub unit", + "ĠMD P", + "Ġmanip ulator", + "ĠScal es", + "ĠHT ML", + "ĠFreder ick", + "G arc", + "Ġb ell", + "ĠR ect", + "rom ised", + "W ord", + "o ples", + "oper ated", + "Ġcollec ts", + "ĠHor izon", + "Ġsa fer", + "d up", + "ĠM ills", + "AL P", + "Ġex opl", + "AT TR", + "war a", + "ĉĉĉĉ ĉĉĉ", + "Ġdeb ug", + "Des criptor", + "stat istics", + "ĠC ub", + "ST ER", + "ĠSt abilization", + "ĠIR AS", + "Ġconform ally", + "Ad ap", + " Ń", + "ĠQ S", + "Ġmicro strip", + "Ġdel icate", + "Ġpubl isher", + "Ġh os", + "ĠS v", + "ĠDes ert", + "ĠGu er", + "ĠCap ture", + "E BP", + "d ust", + "å ¤", + "ĠO ls", + "Ġsuper script", + "ĠFl uctuations", + "ill ium", + "Ġcap tion", + "Ġconc ur", + "Ġquantif ies", + "ster dam", + "Ġspik ed", + "N an", + "us in", + "ĠL AN", + "Ġobserv es", + "ĠAl a", + "ĠInt uitively", + "cur r", + "Ġshr inking", + "Ġcompress ibility", + "orp oreal", + "Ġdeb t", + "ç Ķ", + "ĠT il", + "ĠW AT", + "ody ne", + "Ġgate way", + "Ġduc tile", + "ĠJes us", + "os itol", + "ĠM ales", + "Ġsol vation", + "Ġdisag ree", + "Ġortholog s", + "S an", + "ig o", + "Ġph ages", + "Ġneg atives", + "Ġinterp re", + "AA A", + "Ġgrating s", + "ĠM oll", + "ĠR ivers", + "Ġcr uzi", + "ĠGen erate", + "ĠBar bara", + "ĠHer itage", + "ĠFlu orescent", + "ĠLaw s", + "Array Expr", + "Ġmultip ole", + "Ġsquee zing", + "S PSS", + "l f", + "n lm", + "Ġw orn", + "ĠK uz", + "Ġgenes is", + "ĠEm peror", + "vol atile", + "Ġsib ling", + "iv ir", + "o en", + "Ġprot ost", + "Ġtransform ers", + "enn ium", + "Ġpropos ing", + "Ġbroadcast ing", + "Q M", + "ĠD ependent", + "Ġdis able", + "ĠU AS", + "Ġwar nings", + "Ġarm ed", + "Ġjournal ist", + "Ġmonoc linic", + "ol ium", + "ap ing", + "to on", + "Ġorth odontic", + "ĠNormal ization", + "Ġmand ible", + "ab an", + "ĠW ak", + "ext end", + "Multi ple", + "in vestig", + "is cal", + "ut tered", + "Ġbur g", + "dec ode", + "em por", + "ĠD uration", + "ann y", + "opro st", + "ĠRen ormalization", + "ĠF UNCTION", + "yt orch", + "Ġsyn apt", + "ĠForm at", + "ĠCR T", + "ĠJon athan", + "ĠOF F", + "or r", + "Ġres ur", + "Ġcor ruption", + "d welling", + "Ġback up", + "AG T", + "ĠSaf e", + "dor fer", + "Ġatax ia", + "Ġpar v", + "read er", + "Ġsubt ract", + "embol ism", + "Ġt innitus", + "Ġcyt omegalovirus", + "Ġdele ting", + "T ex", + "ĠC SS", + "ard t", + "Ġout growth", + "Ġmy ocytes", + "dig ital", + "Ġsub scale", + "usp ension", + "Ġham ster", + "Ġinflat on", + "h ara", + "ur ches", + "ĠC LE", + "ĠY as", + "ĠEn coding", + "ĠAug er", + "Ġanastom osis", + "A gent", + "ĠS IL", + "ĠC CT", + "Ġbr ine", + "Ġolig o", + "Ġfluor o", + "Ġgall ery", + "d dots", + "Ġc ilia", + "ĠP PV", + "ĠU TR", + "Ġinter tidal", + "ocal ized", + "Ġcrow ds", + "od or", + "Ġco v", + "Ġnon etheless", + "Ġïģ ¤", + "Ġboost ed", + "ĠChak ra", + "H al", + "P ear", + "Ġimp rec", + "ĠSup plement", + "go al", + "Ġôı¼ ģ", + "Ġst all", + "Ġher d", + "small er", + "Ġreconstruct ing", + "Ġarte facts", + "Ġt eg", + "con ventional", + "rad ical", + "Ġliter al", + "frame work", + "ipro cal", + "E EG", + "Ġg ins", + "od ermal", + "ĠAg u", + "ĠTw elve", + "M ul", + "Ø ¨", + "ir l", + "ĠB elief", + "Ġinc ont", + "IC C", + "hex ane", + "Ġe jected", + "ĠP SC", + "ĠH PC", + "ĠV H", + "Ġequival ences", + "plot lib", + "en ital", + "ri ans", + "pro v", + "ĠV ibr", + "Ġgram matical", + "bach ia", + "accept able", + "od icity", + "ab b", + "Ġher bs", + "Ġpredom inance", + "ĠOrient ation", + "Ġinver tebrate", + "Ġpel agic", + "count ry", + "ĠOrig ins", + "ĠAdoles cents", + "ĠT uning", + "rain ian", + "ĠSc ar", + "Ġlight est", + "Ġemit ters", + "ĠTs ai", + "ri tical", + "ĠEx pert", + "aut hors", + "E CTION", + "ĠSever ity", + "N am", + "p ubl", + "ĠA be", + "Ġnanoc rystalline", + "ĠNak amura", + "ĠP ec", + "ĠB ug", + "Ġsens ed", + "ON S", + "IC s", + "Ġelectro chem", + "ĠR OM", + "ĠRec ruitment", + "Ġ⣠©", + "Ġbiomo lecules", + "ĠB rac", + "Ġtrans position", + "ĠW P", + "ĠO mega", + "Ġdiag on", + "plate let", + "J M", + "ac re", + "ĠA SR", + "ĠK ath", + "Ġpri v", + "opl asts", + "S amples", + "d F", + "at ti", + "ĠS anger", + "ip itated", + "Ġric her", + "ĠG RA", + "Ġplant ar", + "Ġfo ams", + "Ġmathem atic", + "Ġsta phyl", + "ĠUpt ake", + "Ġc ant", + "ĠS Z", + "Ġdis miss", + "Ġselec tions", + "plit z", + "Ġexempl ified", + "Ġtors ional", + "E v", + "Ġv oters", + "ĠN est", + "ys cale", + "Ġspec i", + "Ġpol ished", + "Ġlat encies", + "q ing", + "Ġon wards", + "ll vm", + "the orem", + "log ging", + "ĠAL K", + "ĠBa um", + "ĠGh osh", + "Ġchair man", + "p aired", + "ĠP AP", + "not es", + "olester olem", + "Ġestu arine", + "ĠTibet an", + "ĠV ER", + "Ġcheck er", + "FLAG S", + "rol imus", + "ĠMut ant", + "Ġspray ing", + "ĠC hest", + "olin ium", + "ĠTri assic", + "Ġlid ar", + "A rt", + "ĠM ilk", + "Ġind ecomposable", + "Ġrock et", + "ĠPart ners", + "Ġseman tically", + "entin el", + "L arge", + "P en", + "ĠT ru", + "Ġher itage", + "ĠMut ual", + "ĠChem otherapy", + "Ġdoub les", + "ĠEmbed ded", + "it ual", + "ĠB PA", + "Ġch olerae", + "ĠIn side", + "ĠK atz", + "con vergence", + "Ġindividual ized", + "kin je", + "Ġdiscover ing", + "Ġintric ate", + "Ġin land", + "RE CT", + "ĠCh ick", + "ĠSU R", + "Ġye asts", + "l uminosity", + "Ġf ain", + "ion i", + "ĠT ig", + "ound er", + "Ġdel iber", + "ĠCons ervative", + "ĠDel hi", + "B ER", + "ĠY B", + "ole y", + "ĠBe au", + "TE XT", + "Ġsquee zed", + "Ġs ocket", + "Ġp T", + "py razol", + "co efficients", + "Ġrecruit ing", + "Ġduc ts", + "Ġf oster", + "om eration", + "ĠP SI", + "ĠD up", + "Ġk s", + "ĠOp tics", + "Ġliter ary", + "ĠNi O", + "ĠVEGF R", + "Ġgravit on", + "Ġutter ances", + "Ġb rady", + "Ġfor ty", + "ĠTrans plantation", + "Ġagre ements", + "Left rightarrow", + "w aves", + "Ġacid osis", + "Ġwood en", + "ĠCytoplas mic", + "s afe", + "Ġj umping", + "enn ial", + "Vari ous", + "ĠEry th", + "ul ins", + "un lock", + "methyl ated", + "asser stein", + "Ġheterozyg osity", + "oxy cycl", + "Ġcre ativity", + "MP LE", + "in ative", + "Ġcon volutions", + "Ġno uns", + "eg an", + "ĠAb raham", + "Ġdens er", + "C he", + "l c", + "ĉĉ ĉĠ", + "Ġsem im", + "ĠOut er", + "Ġc and", + "od ule", + "est hesia", + "ĠJ oy", + "ĠProt ocols", + "ĠCalc ulated", + "at op", + "ĠF ALSE", + "Ġref in", + "Ġmig rants", + "ĠïĤ ´", + "ĠSpecific ity", + "ĠFellow ship", + "ĠP MT", + "Ġdis close", + "unc hes", + "Ġdi atoms", + "cor r", + "Ġsky rm", + "Ġrenew al", + "g cd", + "ce reb", + "Ġup right", + "Ġmes oscopic", + "hyd raz", + "B AS", + "F LO", + "H CC", + "M ouse", + "Ġpos et", + "Ġprotein uria", + "Ġre app", + "ĠN ickel", + "Ġstrip es", + "Ġrip ple", + "Sep tember", + "od omain", + "ĠP ope", + "ĠN ons", + "Ġtechn ic", + "Ġneut rop", + "des criptor", + "Ġdissip ated", + "Ġglac iers", + "ĠH IGH", + "ĠL av", + "ret ely", + "Ġback wards", + "Ġcri tics", + "ĠExt ending", + "b ic", + "ĠCh ao", + "of ibr", + "Ġcoun ters", + "Ġstre ets", + "Ġprost hetic", + "Ġbiod egradation", + "complex ity", + "ĠS PL", + "ĠC AC", + "Ġad ducts", + "Ġmorph ometric", + "ĠMat t", + "Ġinduc er", + "Ġast rocyte", + "Ġtriple ts", + "Ġpert ussis", + "P ES", + "id y", + "unc ertain", + "Ġhyper parameter", + "ĠInf rastructure", + "ìĿ ĺ", + "Z W", + "Ġadd r", + "Ġdisrup ts", + "Ġove restimate", + "ĠDY NA", + "Ġvolat iles", + "em erg", + "iss ue", + "c pp", + "Ä ħ", + "ĠV IP", + "Ġu ve", + "ĠCN V", + "yleth yl", + "on azole", + "ĠH iro", + "Ġc n", + "ti k", + "ub ted", + "ĠJac obs", + "Ġadvoc ated", + "ĠBif id", + "m aterial", + "Ġst yrene", + "ĠK eller", + "rocy tic", + "pine phrine", + "ĠWr itten", + "ĠRecommend ation", + "b led", + "ĠB ootstrap", + "th irds", + "Ġcap tain", + "equ als", + "SR C", + "ĠKent ucky", + "Ġeosinophil s", + "A verage", + "H i", + "W he", + "ĠD AT", + "ĠU M", + "Ġtend encies", + "ĠPet erson", + "Ġocc ult", + "Ġexhib ition", + "ĠIN S", + "Ġadip ocyte", + "J ust", + "h ift", + "t ensors", + "Ġc iliary", + "ip ation", + "Ġmotiv ations", + "Ġwitness ed", + "it ches", + "ĠS oy", + "Ġg ib", + "ep tic", + "ĠK OH", + "Ġïģ ¨", + "ĠTor res", + "Î ¿", + "ar po", + "ok inase", + "ĠBud d", + "ĠG MM", + "Ġunder pin", + "Ġoptim istic", + "oge ography", + "numer ical", + "og g", + "Ġdise quilibrium", + "Ġsw ab", + "ED S", + "ĠPD Fs", + "ĠSuper nova", + "phosph o", + "Ġlys osomes", + "gal actic", + "ĠPerm e", + "Ġfisher y", + "ĠB OLD", + "Ġun ravel", + "ĠEncryp tion", + "J P", + "h ur", + "Ġdisc ount", + "ĠWat anabe", + "ĠRhe umat", + "F ITC", + "Ġt erahertz", + "ĠF ont", + "ian ces", + "ĠAd ditive", + "ĠE ither", + "met adata", + "amp hetamine", + "ĠPal mer", + "Ġlever aging", + "J ohn", + "O CT", + "in fer", + "ĠM SD", + "ĠâĪ ĵ", + "ou ver", + "ĠAnd ersen", + "Ġworld s", + "Ġtor i", + "Ġïģ °", + "engine ering", + "ĠSquad ron", + "A ff", + "å ı", + "ox el", + "yle tic", + "ĠCharacter izing", + "V T", + "r ational", + "ere mia", + "Ġcomplex ation", + "ĠER α", + "carboxyl ic", + "ïĤ ·", + "Ġgalact ose", + "ĠAur ora", + "Ġplasmin ogen", + "ure n", + "ign e", + "Ġrep aired", + "Ġblock ers", + "ĠMN IST", + "Ï ħ", + "ĠA xi", + "Ġst adium", + "di ethyl", + "âĢ İ", + "Ġcycl otron", + "Ġlymph aden", + "Ġv in", + "ĠM ayer", + "Ġendomet rium", + "ĠSp herical", + "Ġpers u", + "Ġimm ortal", + "benz enesulf", + "ĠÅ ľ", + "Ġb ite", + "ugg ed", + "ĠDiff raction", + "GT G", + "i ate", + "Ġt p", + "Ġab er", + "ĠRe in", + "Pro gram", + "St yle", + "ĠRegular ization", + "ĠLeuk emia", + "Ġprokary otic", + "oc omial", + "sk b", + "Ġdevi ates", + "Ġf use", + "ĠN ull", + "Ġïĥ Ĺ", + "ĠOper ational", + "Ġcompress or", + "ĠRyd berg", + "Ġf ought", + "Ġe co", + "ĠS SP", + "CD s", + "ĠME K", + "ĠAnis otropic", + "ĠDi rection", + "ĠSpect rometry", + "Ġglut en", + "ĠPow ell", + "recogn ized", + "Ġpsych otic", + "Ġhind er", + "Ġaccommod ation", + "ĠNorm an", + "Q x", + "Ġper iv", + "ĠUn known", + "Ġjo ins", + "ĠMinim ization", + "ĠS ons", + "ĠC in", + "Ġun avoid", + "ĠPT X", + "Ġc ada", + "ĠL uk", + "Ġr uling", + "Ġbi phasic", + "ĠCom plications", + "ĠDef ects", + "Cont ent", + "ĠGreg ory", + "ĠWer ner", + "ĠWeib ull", + "eld om", + "Ġactiv ators", + "GL API", + "math ring", + "Ġhe ns", + "N SC", + "h owever", + "ĠT ME", + "ma frost", + "co efficient", + "ĠIns ect", + "ĠRO Is", + "ĠBor rel", + "ĠQi u", + "Ġinhal ed", + "id ate", + "Ġanti hypertensive", + "Ġtreat s", + "ĠNear ly", + "suc c", + "ĠOrb ital", + "er adish", + "ad ministered", + "ĠÏ Ĥ", + "ĠCol ony", + "ĠâĮ Ĭ", + "ĠIndones ian", + "ĠB auer", + "ĠK od", + "mann ed", + "Res istant", + "Ġda ughters", + "ĠPredic ted", + "Ġvoc ab", + "Ġcontras ted", + "m argin", + "ĠDi rected", + "ED TA", + "Ġsynchron y", + "ick i", + "ĠSal v", + "t reat", + "in cess", + "var nothing", + "Ġhex ane", + "Em pty", + "Ġgem citabine", + "om ib", + "ore pinephrine", + "pro c", + "ĠMet S", + "ĠDR AM", + "Ġantico agulant", + "n om", + "am ater", + "ĠLi DAR", + "Ġmob il", + "Ġamelior ates", + "n iz", + "Ġj a", + "Ġem uls", + "ĠZ a", + "Ġastr onomical", + "ĠAlf red", + "H ilbert", + "ĠK F", + "CR T", + "quad ratic", + "Ġdifferenti als", + "rob acterium", + "ĠHippocamp al", + "p ull", + "Ä Ļ", + "Ġs ad", + "ally l", + "Ġhot spot", + "ĠElectron ics", + "Ġconstit ution", + "iton in", + "ا ÙĦ", + "P c", + "Ġre vascular", + "Ġus able", + "ĠSc atter", + "Ġgraph ically", + "lim inf", + "Ġrestaur ant", + "ucalypt us", + "AC G", + "Anal y", + "ĠMill ipore", + "Ġmunicip alities", + "ĠMacroph age", + "Ġmacrom olecular", + "L icense", + "g c", + "Ġl avage", + "ĠA ES", + "ĠF CS", + "per itone", + "Ġmeas les", + "TE X", + "ĠVir ulence", + "Ġhemat oma", + "ĠF res", + "ĠN utrient", + "ap ar", + "ĠSp ot", + "co plasma", + "ĠExp ect", + "Ġc iprofloxacin", + "phyl axis", + "ĠAtl anta", + "ro uting", + "ar ate", + "ĠC is", + "ens ure", + "car riers", + "ĠVari ant", + "sur gical", + "ĠEstim ate", + "à ¹", + "ĠL iqu", + "Ġam alg", + "Ġbl a", + "Ġthem atic", + "IR Q", + "ACT ION", + "ĠChris ti", + "æ ľ", + "Ġn py", + "de ath", + "Ġhair pin", + "Ġmultiplic ities", + "Gib co", + "he ated", + "af ety", + "mut able", + "quark s", + "S un", + "q l", + "Ġproduc tions", + "Ġge ology", + "Ġt ides", + "at rix", + "Ġad mixture", + "trans lated", + "ĠAb u", + "nucle us", + "Ġweakness es", + "Ġflav ors", + "ĠLu is", + "ĠPut ative", + "sent ence", + "ĠM ast", + "ĠM PS", + "ĠE SS", + "Ġcomp ose", + "Ġbi refring", + "ĠRam sey", + "ĠCL L", + "Ġlign ocell", + "ĠL amin", + "ĠW elsh", + "v on", + "Ġp ests", + "Ġf iction", + "ĠH RT", + "Ġass ure", + "CT s", + "ĠPA Hs", + "Ġcrypt ography", + "en erated", + "Ġop s", + "ĠSyn erg", + "ig inal", + "ĠC raw", + "Ġk ne", + "Ġcurv atures", + "Ġl ux", + "ĠK enn", + "ĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ ĠĠĠĠĠĠĠĠĠĠĠ", + "print ln", + "Ġverteb rae", + "Ġru tile", + "ĠAeros ol", + "re ferred", + "lactam ase", + "ve hicle", + "ad ir", + "iz ards", + "Ġcall back", + "Cl uster", + "Ġsil t", + "Ġresearc hed", + "ĠGener ator", + "ĠRest oration", + "ĠCh in", + "omet rical", + "ĠCo efficients", + "rach id", + "F ace", + "M en", + "c ounts", + "Ġp eg", + "Ġe cl", + "Ġcom edy", + "ĠL n", + "ob uty", + "ĠSh aring", + "Ġadequ acy", + "urt osis", + "ĠPic ard", + "Ġf estival", + "Ġdis position", + "ĠCom plement", + "ĠEx clusion", + "Ġdext ran", + "m ons", + "ĠInter polation", + "ĠSte ven", + "Ġcelebr ated", + "Ġh Pa", + "of requency", + "Ġexception ally", + "Ġenerge tically", + "psych otic", + "Land au", + "T uple", + "dist ributions", + "ĠRich ards", + "Ġpolyp s", + "ĠAbs ence", + "Ġcele b", + "X G", + "Ġsim ulates", + "mit ters", + "Ġheat map", + "ĠSD N", + "ĠSte ps", + "Ġshall ower", + "ĠTurb ulent", + "Y T", + "Ġn al", + "plic ative", + "pha e", + "ĠLe ica", + "ĠAPP RO", + "Ġarrhyth mia", + "Ġre writing", + "Ġuns afe", + "Ġcowork ers", + "ĠG AD", + "iv ol", + "Ġdisrup ting", + "ĠUltra violet", + "ere e", + "ĠL opez", + "Ġneg ation", + "Ġjaponic a", + "ec essor", + "ĠP atch", + "Ġso ap", + "ĠY ing", + "MS K", + "Ġtrac heal", + "ic os", + "Ġv p", + "FA IL", + "Ġcat abolism", + "sol ver", + "f ont", + "es p", + "ĠZ ou", + "Ġdark er", + "Ġlys ozyme", + "c overed", + "Ġmulti tude", + "requ ently", + "Ġmetam orph", + "Ġchap ters", + "h h", + "ch l", + "red undant", + "ack ing", + "Ġent ail", + "ĠPack et", + "ĠHabit at", + "im edia", + "ĠC of", + "ph rase", + "Ġcl oth", + "ars al", + "Ġdr ums", + "TP UT", + "Ar gs", + "duct ory", + "ĠUl timately", + "ic ates", + "anti gen", + "Th ough", + "ĠFl ore", + "pro bs", + "Ġcirc ulatory", + "ĠCont emporary", + "e plitz", + "Ġh atch", + "ri zed", + "ĠK op", + "mit ting", + "Ġhyper spectral", + "ĠAb st", + "S IM", + "Ġfruit ful", + "Ġrecip e", + "Ġimid azole", + "Ġsyn onymous", + "Ġattrib ution", + "ĠMart ÃŃnez", + "ĠRod rÃŃguez", + "par ticular", + "ĠInter acting", + "Con f", + "O RE", + "ĠT MA", + "uc idation", + "Ġbi ochemistry", + "ĠLe vy", + "Ġconcentr ates", + "Ġinduc tor", + "Ġpy rophosph", + "Ġrespond ent", + "Z hang", + "Ġro pe", + "Ġdesign ation", + "ĠCl im", + "Ġconstrain s", + "s helf", + "Ġd Ïĥ", + "ĠT LC", + "ĠA har", + "ĠM atch", + "ĠM OL", + "Ġfe es", + "we alth", + "Ġhyper activity", + "ĠBr uker", + "ĠFre und", + "dichlor ophenyl", + "re ro", + "ĠF ear", + "dot sc", + "Ġhy g", + "ĠText ure", + "T ak", + "am pled", + "Ġal geb", + "sub t", + "Ġdocument ary", + "ĠJ E", + "CN S", + "Ġdecl ar", + "He ight", + "K i", + "en oid", + "ĠC ervical", + "frac tory", + "Ġplant ed", + "IF I", + "Ġconcept ually", + "Ġfill ers", + "ic ola", + "le an", + "Ġcl ump", + "Ġwr iters", + "Gener ally", + "Ġo st", + "op ening", + "CL ASS", + "Ġherpes virus", + "In stit", + "Ġdr inks", + "ĠInt ensive", + "Ġmusic ian", + "Ġanch ors", + "S eries", + "ĠF AM", + "ĠB ott", + "ĠE CC", + "Ġinvers ions", + "Ġac res", + "Ġsw abs", + "ĠÍ ī", + "ĠBer keley", + "Ġpl um", + "Ġem power", + "Ġphoto emission", + "ĠRab i", + "E ast", + "T aylor", + "OS E", + "Ġden ied", + "ĠHT TP", + "M U", + "he w", + "Ġth ri", + "ĠC ERN", + "Ġsuff ice", + "functional ized", + "Ġcra bs", + "Ġidem potent", + "Ġpost ulate", + "ĠCB F", + "disc rim", + "Char acter", + "ĠRecomb ination", + "C ache", + "om it", + "ĠA da", + "Ġcur sor", + "EM T", + "Ġmes oscale", + "gu ide", + "Hy per", + "Ġh t", + "ren es", + "uss en", + "where as", + "Ġintegr ator", + "Ġsyn cy", + "aro us", + "Ġcounter act", + "hal ose", + "ĠNot ation", + "ĠRele vance", + "v f", + "Ġin bred", + "Ġrec irc", + "Ġend e", + "Ġpres idential", + "Ġlact ose", + "ac ional", + "os pi", + "ĠV GG", + "ose lectivity", + "ĠCon fig", + "Ġfinger prints", + "Inter face", + "pur ple", + "et us", + "ĠN in", + "ĠK ras", + "ĠRe ports", + "ĠSe attle", + "AD C", + "Ġlipoprotein s", + "cyclohex yl", + "op ressin", + "Ġwave front", + "tet razol", + "th ys", + "Ġdiv or", + "amin ophen", + "ĠPer ry", + "ĠConsider ations", + "ĠHal o", + "Ġreflex ive", + "thiazol idin", + "oxycycl ine", + "C W", + "od im", + "ĠCh ong", + "Ġequil ibrated", + "r ime", + "ym ology", + "Ġdev oid", + "rig el", + "amater gic", + "Ġidentif ications", + "Ġcontroll ability", + "ectic ut", + "ĠSynchron ization", + "ul atus", + "Ġcorrel ating", + "Ġmu ons", + "Ġcompartment al", + "Ġinhom ogeneities", + "Ġevac uation", + "resp iratory", + "dim ethoxy", + "Ġinterfer ometric", + "Ġastr onomy", + "Z D", + "Ħ Ħ", + "el ia", + "bl er", + "Ġpione ering", + "Ġp its", + "Ġman soni", + "ĠCON D", + "Ġcodew ord", + "im ura", + "ĠDop amine", + "ĠGi ov", + "ĠCamero on", + "S em", + "d ong", + "ot to", + "em ies", + "Ġinter quartile", + "ll bracket", + "otrop ies", + "Ġhapp ening", + "ĠPal m", + "Ġst uff", + "Ġpar king", + "eg al", + "ĠCO P", + "Ġorgan izing", + "Ġpoly hedral", + "Ġproven ance", + "J s", + "ch ains", + "eg u", + "mer cap", + "level and", + "Ġeryth roid", + "ympt omatic", + "Ġzig zag", + "Ġinf erring", + "Ġappro x", + "Ġdown link", + "ĠDef iciency", + "rbrack et", + "ĠT IM", + "ST S", + "ain en", + "Ġun loading", + "ĠX P", + "ĠWh ilst", + "ĠID H", + "ĠTI MP", + "r rbracket", + "ac ities", + "Ġwh ale", + "ĠW AR", + "Ġinf l", + "ĠPresent ation", + "authorbs nm", + "Ġbacter icidal", + "SP EC", + "Ġdys regulated", + "ĠIC AM", + "n ano", + "Ġw afers", + "ĠM UC", + "Ġal ien", + "ch ke", + "Ġsl abs", + "Ġback ing", + "ns is", + "Ġbal ances", + "eth ane", + "Link ed", + "C hen", + "H ymenoptera", + "it ations", + "ĠO UT", + "trans plant", + "condition ed", + "ĠBenef its", + "T yr", + "at mosp", + "ĠAd hesion", + "Ġthor ac", + "activ ator", + "Ġphosphatidyl inositol", + "Ġreported ly", + "ĠCL ASS", + "Ġrenew ed", + "ĠPharmac ological", + "Ġminim ise", + "gluc osidase", + "aden osyl", + "Ġov ip", + "initial izer", + "Ġfor age", + "rm s", + "ĠIm ag", + "ĠAnne xin", + "ĠVehic les", + "Ġf les", + "st a", + "ĠG BS", + "ĠCh at", + "measure ments", + "ĠAud itory", + "C ut", + "F v", + "Ġm aker", + "ap plication", + "Ġrevers ing", + "Ġsti p", + "Ġfaecal is", + "icy cle", + "Ġtrim med", + "Ġexacerb ation", + "Ġtransc ranial", + "ĠMoment um", + "Ġf c", + "ĠF OV", + "Ġang ina", + "Ġnano structure", + "Ġantagon ism", + "ĠLED s", + "ìĹ IJ", + "Ġf als", + "ap oration", + "ĠIn vasive", + "ĠK m", + "ert ation", + "Ġhar ness", + "Ġfer tile", + "ĠTR UE", + "Ġshel ter", + "ĠWol bachia", + "sho ot", + "Ġs ess", + "ĠH ous", + "ĠA ce", + "ĠC ML", + "Ġpro active", + "Ġsh ots", + "Ġco up", + "rest ling", + "uniform ly", + "y am", + "ol ase", + "ĠI CS", + "ĠE bola", + "roll ing", + "tr unc", + "ĠRepresent atives", + "Ġgras ping", + "ĠAnomal y", + "ĠM ine", + "ĠM PO", + "ler ight", + "Ġinstit ute", + "Ġsug arcane", + "ÑĢ Ð°", + "Ġoccl uded", + "ĠMagell anic", + "B EC", + "W i", + "o A", + "Ġg apped", + "ĠPR C", + "ĠMA E", + "Ġmusic ians", + "ĠSignific antly", + "Ġforth coming", + "Ġaccl imation", + "re quired", + "ver bal", + "ĠF X", + "ĠM LE", + "Ġcomp ass", + "ĠMultim odal", + "G rant", + "Ġt vb", + "In struction", + "Ġsens es", + "urb ed", + "ham n", + "Ġfram ed", + "Ġuro thel", + "or in", + "se al", + "Ġfl asks", + "sh ops", + "Ġwhe els", + "ĠRad on", + "ĠPlan etary", + "Ġhed ge", + "Ġd k", + "Ġevid ently", + "thread s", + "Ġt ad", + "el im", + "im ov", + "ist em", + "and i", + "Ġle isure", + "ost om", + "Ġcar ing", + "ĠSm oking", + "Ġcompetit ors", + "A FS", + "x l", + "ĠS atur", + "ĠF erg", + "Ġch in", + "ĠCD R", + "ĠSO M", + "osacchar ide", + "MOD EL", + "E CC", + "Ġd as", + "agon ist", + "st ery", + "Ġrel ays", + "ze k", + "Ġneoplas m", + "C hip", + "Ġg ill", + "lam ed", + "cer ning", + "Ġincons istencies", + "ace ans", + "ĠAd ri", + "ĠAf ghan", + "Ġnic hes", + "Ġtunn elling", + "g us", + "ĠI an", + "Ġbur ial", + "Trans form", + "ocomp atible", + "Ġs eldom", + "Ġdis closed", + "âĪ ķ", + "Ġref ining", + "Ġty ph", + "Ġcooper ate", + "Ġasphal t", + "ĠCons titution", + "fl avor", + "Ġwar p", + "Å ¼", + "Ġc raw", + "ĠInd igenous", + "ĠPre vent", + "Ġtrig eminal", + "ĠFried rich", + "ĠInterfer on", + "i osity", + "w arm", + "us on", + "Ġunder lies", + "Ġmultiple ts", + "ĠSU PER", + "ĠManufact uring", + "Ġv imentin", + "ram ine", + "Ġeffic acious", + "ic ed", + "ĠV all", + "oth orax", + "Ġaud i", + "Q s", + "ĠP AL", + "ĠH old", + "hat tan", + "idd ing", + "w ana", + "Ġp ending", + "Ġp erennial", + "Ġtouch ing", + "xp ected", + "D istance", + "n av", + "Ġis omeric", + "ĠM CI", + "num bers", + "Ġrevers es", + "Ġpolyc ystic", + "H em", + "u ities", + "op tional", + "Ġsub cortical", + "ĠSup ply", + "ĠCal der", + "Ġmang rove", + "Ġp ads", + "ur faces", + "ĠF aster", + "Ġunder neath", + "Ġprol actin", + "Ġcle arer", + "Ġscin tillation", + "Ġhumid ified", + "ĠW ound", + "ĠH PA", + "Ġcoll apsing", + "Ġbary onic", + "ĠMEA SU", + "ĠG ü", + "Ġdet r", + "Ġsubstit uent", + "ĠRoman ia", + "ĠInv olved", + "Ġduoden al", + "ĠAm p", + "ĠS IS", + "sc her", + "aut h", + "ĠResp ond", + "ĠRank ing", + "t rip", + "x F", + "ist in", + "Ġpa uc", + "ref lection", + "Ġcorne a", + "Ġbol us", + "Ġpiv ot", + "Oc tober", + "ĠS ERS", + "ĠX ing", + "AN ET", + "Ch inese", + "ĠMus c", + "D ynamic", + "M esh", + "Ġdi phosphate", + "Ġcons pecific", + "lect or", + "ĠEc u", + "ĠCover age", + "ĠãĢ Ī", + "C OD", + "am ong", + "Ġpos it", + "imum ab", + "Ġp N", + "Ġco aching", + "ex ports", + "Ġreal m", + "ĠFer reira", + "Ġnation ally", + "Ġtur tle", + "ubted ly", + "ĠD raft", + "Ġend l", + "ĠContinu um", + "embed dings", + "Ġá¹ ½", + "ĠCr ime", + "Ġimm igration", + "ĠFil ip", + "Ġgar net", + "Ġobsc ure", + "ĠT YPE", + "Ġult rastructural", + "ca emia", + "ĠSem an", + "r ink", + "ti ff", + "uc cal", + "ke e", + "itud inally", + "ĠAll oy", + "ĠAnaly zer", + "contin ue", + "ĠAlab ama", + "Q OL", + "Ġpol lin", + "Ġcorrespond ences", + "ĠRes ol", + "F IR", + "ul are", + "ta wa", + "UR CE", + "Ġurban ization", + "z d", + "Ġgl oss", + "ER A", + "ĠDeterm ine", + "D ate", + "ĠP SP", + "ĠSh ig", + "rep ta", + "ĠGa it", + "neut rino", + "Ġper vasive", + "ĠâĢ¢ âĢ¢âĢ¢", + "Ġhom ozyg", + "Ġadap tively", + "graph ic", + "ĠJohn ston", + "z t", + "ex plicit", + "Ġhel min", + "Ġp es", + "AR F", + "ĠF ram", + "ĠAm sterdam", + "Ġlogarithm s", + "ĠCre ative", + "Page Index", + "Ġp acing", + "ĠP CS", + "Ġfore brain", + "ĠCT CF", + "dec omposition", + "Ġbear ings", + "Ġanhydro us", + "Ġc b", + "ĠM ON", + "ĠN odes", + "str um", + "ĠJ ans", + "Ġdeline ate", + "Ġdichro ism", + "con formal", + "Ġret reat", + "gl ial", + "Ġnucle ase", + "ĠBal timore", + "Ġpay ing", + "Ġbore al", + "tip ation", + "R oot", + "S QL", + "s ources", + "end o", + "ĠOr ion", + "Pl us", + "ĠD EL", + "ĠTh an", + "Ġmon oph", + "Ġreflect or", + "Z e", + "ĠL inking", + "syn c", + "ĠCRE B", + "n ational", + "ur ized", + "ĠP eptides", + "ĠB egin", + "bor g", + "piper idyl", + "Ġoverestim ation", + "R GB", + "T K", + "Ġbe ings", + "Ġat tains", + "Ġres ervation", + "ĠMo tivation", + "Ġtrim ethyl", + "ĠTerm inal", + "Ġinten tional", + "Neg ative", + "ĠCron bach", + "dorfer i", + "D aw", + "V AR", + "d P", + "im ath", + "ove rex", + "Ġfibro tic", + "Ġsmart phones", + "Ġont ologies", + "G ood", + "u tively", + "ĠV B", + "SP E", + "ĠMcD onald", + "galax ies", + "Ġbioch ar", + "ĠE MS", + "ĠN f", + "ors hip", + "Ġback scattering", + "ĠÐ ¿", + "Ġanthocyan in", + "ĠPho enix", + "con tained", + "ĠPS II", + "hl ung", + "ĠLA I", + "Ġlect ures", + "Ġdisp atch", + "V F", + "ĠM EC", + "ĠW es", + "Ġback scatter", + "oti te", + "ĠSR C", + "Ġcurren cy", + "onym s", + "as partate", + "Ġcos et", + "ĠC PP", + "orb ing", + "ĠEmbed dings", + "ĠSurve ys", + "Ġneurodevelop mental", + "ĠS RE", + "ĠInter ior", + "ĠAR DS", + "experim ents", + "brom ophenyl", + "ĠE CL", + "ĠO PE", + "medi ation", + "Ġtherm oc", + "Ġinterpret able", + "ĠMicrobi ome", + "e astern", + " ¿", + "ĠT DP", + "ath on", + "ĠBy zantine", + "any on", + "Ġepit axy", + "Ġcritic ized", + "Mill ipore", + "ĠD EP", + "ĠFre edom", + "j unctions", + "ĠA SM", + "ĠG ren", + "Ġsign ing", + "Ġconstit uting", + "opro terozoic", + "ĠSyn ech", + "ĠVo ice", + "Ġcholec yst", + "b ilities", + "on line", + "ĠE dd", + "ĠK up", + "ĠLet t", + "ĠMar in", + "ĠGo al", + "ĠSY M", + "intro duced", + "naphth yl", + "ĠL ü", + "Ġm x", + "Ġb lu", + "Ġr m", + "ĠDe letion", + "ĠConn ecticut", + "Cole optera", + "t ry", + "Ġso ot", + "ĠCount ries", + "Ġsick le", + "M eta", + "ĠS ib", + "ĠH NO", + "ĠU D", + "Ġexp r", + "Ġallow able", + "ĠInd irect", + "tis ation", + "Ġaden omas", + "electron ics", + "R NN", + "ĠT CF", + "Ġgluc agon", + "ĠC itation", + "Ġg amb", + "and ez", + "Ġtrans mits", + "aj ima", + "Ġhol onomy", + "ì ł", + "act am", + "ĠTh reat", + "ĠPear l", + "Ġerup tions", + "ĠImmunohist ochemistry", + "Y es", + "p atrick", + "Ġa ma", + "Ġd rew", + "ĠT asks", + "ĠP IM", + "Ġdis pat", + "ĠDet roit", + "Ġcoex ist", + "arboxyl ase", + "I BM", + "ĠT UNEL", + "ĠU F", + "ĠAN G", + "Ġsar copenia", + "Ġh aptic", + "Ġcarbon ates", + "Ġmit ophagy", + "Ġciti zen", + "ĠCONTR OL", + "f if", + "Ġw i", + "ĠG LO", + "ens ored", + "ĠPar a", + "ĠAb del", + "oi etin", + "Ġto e", + "ĠS QU", + "ĠR ag", + "Ġx ylem", + "Ġlib eral", + "ĠMarg aret", + "W a", + "k p", + "ĠP EM", + "ĠD DR", + "Ġgen otypic", + "ĠY M", + "ING S", + "ker as", + "ĠEduc ational", + "ĠCult ures", + "in str", + "ĠF uchs", + "ag asc", + "fact ant", + "Ġt enth", + "AB L", + "Ġperme able", + "ĠCam eron", + "Br N", + "ĠMull er", + "ĠRevers ible", + "w ild", + "Ġf usions", + "os ulf", + "ĠE oS", + "ĠK ö", + "det ected", + "ĠColl agen", + "Ġdescend ants", + "e lection", + "ar ange", + "Ġb ounce", + "Ġcont ag", + "In valid", + "ĠCo ating", + "t asks", + "ar ma", + "ĠK C", + "Ġdi ar", + "ĠSup press", + "Ġfraction ated", + "Ġsn ail", + "Ġmicro phone", + "ĠSc ienti", + "Ġchem iluminescence", + "soft ware", + "Ġburg dorferi", + "Ġb oot", + "ĠC SCs", + "ĠM SI", + "ts ev", + "Ġhe ater", + "frac tal", + "Ġend osomes", + "Un iform", + "Ġath lete", + "ĠDri ven", + "Ġviv ax", + "K ind", + "satisf ies", + "Ġcorticoster oid", + "ĠEstabl ishment", + "cal ibration", + "Ġdim eric", + "Ġcere al", + "ĠSuper vised", + "ĠSP M", + "MB ER", + "Ġhemisp heres", + "Ġpercenti les", + "L eu", + "M ajor", + "Ġex agger", + "Ġds RNA", + "Dec ember", + "ĠZr O", + "Ġas ymmetrical", + "ĠV AS", + "ĠJ M", + "Ġintegr ations", + "Ġhand over", + "C ycl", + "im plant", + "Ġqu ote", + "Ġcycl one", + "ĠSte phan", + "ĠFran co", + "Ġaw ake", + "Ġfeed er", + "CH AR", + "Con dition", + "ĠChar l", + "ĠBrig ade", + "Ġremedi ation", + "c ig", + "ĠBoh r", + "ĠVac uum", + "ĠTox oplasma", + "Ġgh relin", + "ĠT RAF", + "ay e", + "Cl ient", + "ili ation", + "xy z", + "ming ham", + "ĠSU B", + "ïĢ ł", + "Ġconvers ions", + "Ġmulti path", + "miss ive", + "Ġeq n", + "bul k", + "my c", + "Ġexacerb ated", + "Ø ª", + "Ġprotein ase", + "Ġbu ilder", + "ah ara", + "Ġinver t", + "ĠRecep tion", + "ax anthin", + "Ġprim ed", + "Ġcop ula", + "Ġproceed ings", + "Ġnond egenerate", + "Ġint ox", + "Ġneed les", + "length s", + "Ġtranspos on", + "h on", + "ĠT PC", + "pl and", + "ox yn", + "IC H", + "Ġintra uterine", + "Ġlamin ated", + "ĠOBS ERV", + "M atch", + "ĠIn sur", + "ĠAm yloid", + "Ġwar ped", + "emat ical", + "ĠPrac tices", + "ãģ ®", + "ĠBrass ica", + "Ġhyperther mia", + "Ġd n", + "ĠL IF", + "ĠMet ropolitan", + "ĠBr dU", + "imp act", + "f iltered", + "ĠRe agent", + "v p", + "ĠT ip", + "ĠPro portional", + "Ġblood stream", + "Sim ple", + "Ġty ros", + "ĠHen ri", + "Ġretro trans", + "aci ens", + "Ġmist akes", + "acyl glycerol", + "ĠMir ror", + "V ERSION", + "v re", + "Ġb act", + "ock ed", + "eps is", + "Ġson ication", + "ĠPur kinje", + "Ġmism atches", + "ĠA OD", + "Ġhyper graph", + "ĠMi ami", + "am med", + "Ġcon versely", + "ĠG abor", + "ĠG DM", + "Ġco iled", + "onic a", + "Ġevol utions", + "ĠR BM", + "ĠRe ef", + "ĠAb ram", + "ĠPrec ise", + "incre ase", + "ĠPlate let", + "Gener ator", + "Ar ch", + "ĠBen ed", + "pre ceq", + "meas urable", + "C AS", + "ĠT ourn", + "Ġg iants", + "Ġed dies", + "Ġcolumn ar", + "agg regation", + "Ġzircon ia", + "duc ibility", + "Ġserv o", + "Ġbe auty", + "Ġhe ap", + "ĠâĪĴ âĪĴâĪĴ", + "Ġconduc tivities", + "Ġdark ness", + "Ġoccup ying", + "ĠCle an", + "b ash", + "ul ans", + "app y", + "ĠMark er", + "run time", + "Ġhaem oglobin", + "Ġdesk top", + "m is", + "ĠS of", + "os se", + "Ġcom oving", + "Ġcl utter", + "our ced", + "Ġsub j", + "arch ing", + "ĠSol omon", + "lock ing", + "Ġpar ap", + "Ġrot ator", + "ĠACKNOWLEDGM ENTS", + "T er", + "y ster", + "ĠWe bb", + "Ġsubs ample", + "osil icate", + "T raining", + "or pha", + "Ġtime out", + "otin amide", + "ĠFab ry", + "ĠRece iver", + "Ġconjunc tiv", + "ĠEcu ador", + "ĠI da", + "Ġcase in", + "Ġïģ ¸", + "ĠBar n", + "ĠSchool s", + "el ona", + "di p", + "ĠCh rys", + "IC I", + "Ġposterior i", + "Ġble aching", + "ĠPerson ality", + "um bers", + "ĠM odes", + "Ġno tification", + "Ġsup ine", + "alu ed", + "ke ep", + "ĠFran z", + "Ġwound ed", + "Y L", + "Ġdi lemma", + "ĠCl ara", + "ĠCar roll", + "Ġsick ness", + "Ġprox ies", + "ec ks", + "ĠÏ «", + "Ġplant ing", + "Ġcipher text", + "ĠF amilies", + "ies el", + "Ġinc ongru", + "ĠExc itation", + "Ġconfer red", + "ĠBut ter", + "Im pl", + "coll ision", + "id ol", + "Ġac quires", + "ĠO wen", + "SA M", + "ĠG UT", + "lec ts", + "Ġdele g", + "Sh ot", + "Ġanth rac", + "Russ ian", + "ĠP CE", + "Ġâ ĥĹ", + "ĠK ab", + "NA C", + "Ġarg parse", + "ĠVi ol", + "Ġantico agulation", + "Ġcredi bility", + "Ġrota virus", + "ĠIn vest", + "Ġrec ol", + "vari ety", + "Ġdeform able", + "Ġenerge tics", + "Ġconsult ations", + "le tics", + "ĠF oss", + "ĠL IGO", + "ph p", + "ĠCh al", + "ĠMal awi", + "Ġstro kes", + "h orm", + "Ġb s", + "Ġpl ural", + "str ategy", + "Ġmis alignment", + "pre vious", + "fil ters", + "ĠDem ographics", + "determ inistic", + "Ġcycl ophosphamide", + "Ġstre ak", + "ĠBios ynthesis", + "Ġsubcutaneous ly", + "j n", + "Ġam picillin", + "ĠCh ag", + "iform es", + "IF ICATION", + "Ġyour self", + "Ġtoler ability", + "Ġaut ocl", + "rh s", + "Ġpup ils", + "Ġgaug ed", + "L ay", + "ĠS anti", + "ĠD BP", + "ĠG ary", + "dri ve", + "Ġtrust worth", + "Ġconting ency", + "C ube", + "H ost", + "f u", + "Ġh sa", + "iss ner", + "IT T", + "ĠSr TiO", + "Ġcouns elling", + "inte grable", + "Ġunder way", + "Ġstandard ised", + "bi us", + "First ly", + "Ġporph yrin", + "A rea", + "i w", + "Ġ ub", + "ĠL ynch", + "ĠW BC", + "ild en", + "Ġhom eless", + "Ġmagnet osphere", + "Ġnight time", + "nc bi", + "Ġdow nt", + "le thal", + "Ġinter im", + "ĠRes il", + "Ġcontinu ally", + "ĠImmun ofluorescence", + "Des ign", + "Ġadvoc ate", + "repta vidin", + "f w", + "st ory", + "ĠP SS", + "Ġfil ed", + "Ġbed rock", + "Ġisofl urane", + "Ġrel uct", + "ew ard", + "ĠInd ependence", + "ĠBurk holder", + "Ġc inn", + "Ġcap tive", + "Ġcompos ing", + "Ġrest raint", + "Ġquestion able", + "ĠTom ato", + "Ġzer oth", + "r ins", + "ome z", + "Ġgl ia", + "ĠGl ac", + "Ind ependent", + "Ġobj ectively", + "p A", + "Ġfav oring", + "ipel ago", + "Ġincont inence", + "b ium", + "ĠL Z", + "ĠL ed", + "hex yl", + "Ġce ased", + "Ġole ic", + "ĠImpair ment", + "Ñ ĸ", + "ong o", + "Ġrun ner", + "Ġcuc umber", + "ĠPer form", + "Ġdouble ts", + "Ġeigen function", + "ĠÌ º", + "ĠHend erson", + "K lein", + "T ab", + "Ġbe er", + "oc om", + "unc iation", + "---- --", + "ĠT SC", + "og as", + "Ġr ud", + "Ġinc is", + "ĠLO G", + "FB Q", + "Ġinterconn ection", + "à ®", + "ar box", + "ĠI BS", + "ĠN CT", + "ĠG and", + "Ġy aw", + "ĠTrans verse", + "ĠSud an", + "Ġconstric tion", + "H or", + "Ġev asion", + "Ġmer omorphic", + "ĠPB MC", + "I UM", + "re ed", + "ĠB ö", + "ĠE MB", + "uk h", + "Ġwin ners", + "Ġasc ites", + "M es", + "Ġe clipse", + "ĠE ocene", + "ad iazol", + "Ġrecover ies", + "Star ting", + "re ma", + "Ġà İ", + "mon otonic", + "ĠMe OH", + "ĠFl ood", + "Ġwat ching", + "G TP", + "i el", + "m üller", + "å ħ", + "Ġpolyphen ol", + "ĠL MI", + "red it", + "ther m", + "Ġneur ite", + "Qu antum", + "rach lor", + "ĠRub in", + "Ġbf nm", + "A re", + "ar achn", + "Ġd uck", + "ĠTra jectory", + "ĠNit ric", + "l v", + "u id", + "im ag", + "ĠM ULT", + "Ġgen re", + "ari e", + "Ġtr ifluor", + "ĠCor pus", + "oli osis", + "ĠCC K", + "K it", + "f ather", + "Ġt ennis", + "its ch", + "HC V", + "l antic", + "ĠA Q", + "iz u", + "ast atin", + "oth io", + "ĠAn atomy", + "Ġá ŀ", + "glob ulin", + "Ġinterp ol", + "Ġtunn els", + "Ġn atri", + "im ed", + "ĠD ew", + "Ġsub scripts", + "tit es", + "Ġhistological ly", + "O pt", + "x n", + "Ġres ampling", + "ane y", + "Ġtr ast", + "Ġsin ensis", + "Ġsenes cent", + "F ast", + "Ġh ampered", + "Ġblock er", + "ush ima", + "Ġhospital izations", + "L im", + "o ons", + "à ¿", + "ĠA PS", + "ĠY ok", + "ĠZ am", + "Ġexperim enter", + "ĠDis ks", + "Ġà ¬", + "ĠS cop", + "ĠA ph", + "ĠP arents", + "ĠPl ots", + "ĠCON T", + "ĠÐ Ī", + "Ġhomolog ue", + "ĠCool ing", + "re th", + "Ġo vari", + "ĠT amil", + "v rule", + "ĠP CP", + "ari ous", + "Ac tive", + "oprot ection", + "ĠAlf v", + "Ġinf ra", + "ĠCo herence", + "clos ures", + "hydrox ymethyl", + "E H", + "Ġm aser", + "ĠN IST", + "lec k", + "con cat", + "Ġtra ine", + "Ġmix es", + "Ġrib osomes", + "l ia", + "p uls", + "Ġas cer", + "ĠB anks", + "ell in", + "ap plied", + "Ġcl ips", + "Ġmet ap", + "Ġcop ro", + "Ġepid id", + "ĠEpidem iological", + "ĠNich olas", + "ĠK ings", + "Ġlar va", + "Bs Ag", + "ĠS ánchez", + "ĠâĪ İ", + "AM D", + "ĠHa o", + "ĠBill board", + "ĠAbor iginal", + "Ġn ylon", + "ĠN AN", + "c ores", + "ĠC rop", + "Ġcom mittees", + "Ġdi hedral", + "ĠJ uli", + "ĠAnd y", + "hyd ration", + "correspond s", + "M ut", + "Ġt orn", + "ĠF EV", + "Ġx s", + "amp hen", + "Ġsummar ization", + "ĠEr g", + "Ë Ĩ", + "ĠJ unction", + "anc ouver", + "ĠEx amining", + "ĠMarc o", + "Po inter", + "Ġscarc ity", + "unc ing", + "Ġbi jective", + "ĠMain e", + "ĠRH IC", + "Ġtow ers", + "Ġgent amicin", + "Ġt onic", + "Ġk T", + "Ġclim bing", + "Ġrecru its", + "ĠHot el", + "ĠJew s", + "ĠRUN X", + "Ġausten ite", + "ĠOffic er", + "in ent", + "uc c", + "ĠB idirectional", + "Ġmay or", + "ĠAss ays", + "ĠER G", + "SN Ps", + "d ine", + "Ch ina", + "star ting", + "Ġirr ational", + "ĠDIF FE", + "Ġmillisecond s", + "L ik", + "in one", + "Ġâ ģĦ", + "Ġcons picuous", + "Ġsur plus", + "ĠX iong", + "Ġup grade", + "Ġtim ep", + "ĠÄ Į", + "Te V", + "orbid ities", + "in valid", + "Ġv ide", + "ter ra", + "Ġan tin", + "em ens", + "oc ese", + "ĠK I", + "Ġevolution arily", + "K er", + "ĠL ES", + "cl amp", + "Ġslow ed", + "gly coprotein", + "enti eth", + "Ġab road", + "Ġinterp olating", + "Ġcataly ze", + "ĠBelg ian", + "Ġphotograp hed", + "Ġp ector", + "ĠS IV", + "ĠE LECT", + "Ġdes al", + "one ph", + "ĠCl os", + "Ġafford able", + "b irds", + "g om", + "Ġr r", + "Ġun i", + "ĠGen us", + "ĠReg ge", + "ĠMulti dimensional", + "Ġpsych opathology", + "Ġcer tification", + "P attern", + "ĠT ower", + "Ġst ern", + "Ġsub lattice", + "Ġgr at", + "Ġly rics", + "f mt", + "o ceptive", + "Ġd P", + "ĠHol mes", + "Ġbudget s", + "Ġeut ectic", + "ĠP v", + "ĠG ott", + "Ġdis infection", + "Ġret inoic", + "ĠOb st", + "Ġrepl en", + "Ġâĸ ł", + "K utta", + "P lease", + "ĠC AG", + "ĠSti r", + "spe aking", + "Ġinsectic ides", + "ĠFung i", + "H od", + "R ON", + "co il", + "ĠVis ible", + "Ġin ception", + "Ġe GFR", + "Ġre ionization", + "Ġdom ination", + "ĠMet ro", + "Ġsw ept", + "MD S", + "Ġsubs idence", + "ĠFall s", + "ĠD rum", + "ĠCons erved", + "ĠMy ers", + "Ġadapt ability", + "Ġly ophil", + "ul ina", + "are lli", + "ocy cles", + "ĠSO A", + "Ġds DNA", + "ĠCE O", + "Ġanch oring", + "Ġde activation", + "yl er", + "Ġinteresting ly", + "Ġ iliac", + "ĠB org", + "ĠPT C", + "ocyan in", + "Ġun used", + "ĠCar rier", + "Wh ich", + "Ġinterven ing", + "Ġprivi le", + "h it", + "Ġche aper", + "ĠCycl in", + "p lying", + "ĠC leveland", + "ĠH ahn", + "Ġag glut", + "ĠAn ch", + "ĠRed ox", + "W ill", + "ĠL inn", + "ron es", + "ĠNew castle", + "ĠExp ected", + "ĠOpportun ities", + "ĠL arger", + "Ġle ach", + "Ġpep per", + "S ha", + "s ector", + "y ou", + "Ġre plications", + "ch olesterolem", + "ĠIn vasion", + "Ġb ony", + "ĠH uber", + "the nd", + "Ġreal ised", + "Ġinvest ments", + "C ataly", + "ĠW itt", + "ĠK ai", + "Ġet ched", + "ĠST EM", + "Ġexcl udes", + "Ex ec", + "ĠStrong ly", + "ĠSym posium", + "ĠTub erculosis", + "il ance", + "ĠR IS", + "ap ia", + "ens ated", + "ne b", + "ĠCh ains", + "Ġent hus", + "quad rup", + "dec l", + "Ġbin ned", + "Ġsynerg istically", + "Ġgaug es", + "whe ther", + "dise ase", + "W estern", + "Ġhyp othermia", + "ĠGard ner", + "Ġaber ration", + "R od", + "Í ĺ", + "Ġf d", + "Ġst ood", + "Ġcondition ally", + "Ġthrom bol", + "P SC", + "Ġm k", + "ĠT ER", + "od ds", + "ĠK ri", + "ĠIV F", + "Ġm ites", + "ĠC HE", + "Ġq q", + "ĠInf ants", + "ĠChar lot", + "bec co", + "et om", + "ĠCD S", + "Ġarchae al", + "ĠHN SCC", + "Ġmonod romy", + "amphen icol", + "a pers", + "re activity", + "Ġund erm", + "In ternal", + "ĠLands at", + "G erman", + "Ġcer vix", + "id azole", + "ĠS ketch", + "ĠL AM", + "ĠN erve", + "ĠTe h", + "Ġmuss el", + "Ð ·", + "ĠMicro array", + "we i", + "Ġwhe y", + "Ġmix er", + "Ġrecon figurable", + "Ġvascul itis", + "Ġkw args", + "Ġre us", + "cor relations", + "Ġwood y", + "carbon ate", + "ectom ized", + "Ġret rans", + "Ġcyt ometric", + "ĠWild life", + "ĠAnsw ering", + "Ġp encil", + "ĠD AS", + "ak rish", + "CE PT", + "ĠÄ Ŀ", + "ĠPers ian", + "conver ting", + "Ġc ater", + "Ġmean while", + "TP A", + "Ġr um", + "ĠG ros", + "up e", + "Ġreg urg", + "Ġpenal ties", + "Pos itive", + "************************************************************************ ****", + "X F", + "e enth", + "ĠC ory", + "od ulation", + "Ġqu orum", + "c odes", + "ar am", + "ĠT SA", + "ĠP n", + "âĪ ij", + "pr ison", + "Ġconfidential ity", + "E PS", + "X iv", + "i ensis", + "est ones", + "ĠZ ag", + "Ġpred ecessor", + "Ġpri ze", + "Ġâİ ¨", + "ster oidal", + "op ard", + "Ġimp ractical", + "Ġdemonstr ations", + "Ġpredis position", + "Ġk k", + "Ġmod ifiers", + "Ġprec a", + "Ġexec utes", + "Ġbin ning", + "Ġped ig", + "ĠKL F", + "ĠS keletal", + "ĠC IN", + "ature d", + "Ġdecom poses", + "Ġaph id", + "B ern", + "P ur", + "ĠE PO", + "mer ge", + "ĠCO SM", + "am yloid", + "mon ia", + "ĠSc ores", + "ĠReg istration", + "ĠAg robacterium", + "Ġenter prises", + "loc ality", + "ĠIT O", + "Ġt ess", + "Ġf cc", + "ĠN c", + "Ġco axial", + "ĠAd vant", + "AP C", + "ĠDem and", + "adj ust", + "Po ints", + "Ġhetero structure", + "iffiffiffiffiffiffiffiff iffiffiffiffiffiffiffiff", + "D Q", + "Ġt ensions", + "ab und", + "ĠH utch", + "bre w", + "Ġvit reous", + "ĠEZ H", + "Ġm erc", + "Ġdeb ated", + "Ġpal ate", + "ocol ate", + "Ġevap otranspiration", + "ĠẠ¼", + "ĠHoff man", + "ĠGALAX IES", + "C AL", + "c aps", + "le gal", + "di ed", + "ĠIs olates", + "Ġagg rav", + "q s", + "ĠI CT", + "Ġse als", + "Ġspin el", + "ĠGe or", + "Bl ue", + "Ġure ter", + "spl ine", + "ĠIntro ducing", + "thend ieck", + "op per", + "Ġafter glow", + "Ġend osomal", + "Ġreal izes", + "sol ving", + "Ġmist ake", + "ĠAthe ros", + "ĠS BS", + "ĠR ut", + "ex ist", + "Pro f", + "ĠNe isser", + "MS G", + "ĠEar lier", + "Ġd T", + "ĠSp read", + "ĠRef lection", + "second ary", + "approxim ate", + "Ġnig ra", + "S olution", + "an one", + "ĠIt ems", + "Ġwave lets", + "ĠSol uble", + "Ġcircular ly", + "ĠCU DA", + "Ġreg enerated", + "SP I", + "ĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ ĠĠĠĠĠĠĠĠĠ", + "at uring", + "RE Q", + "Ġinter oper", + "ree v", + "ON T", + "isc hen", + "ĠCho osing", + "phosphor ylated", + "á Ī", + "Ġd ress", + "ĠCon form", + "Ġrem emb", + "Ġischa emic", + "B asic", + "ĠP ang", + "Ġcr it", + "ĠOr n", + "Ġg m", + "ĠF og", + "ĠB d", + "rac heal", + "Ġphen ols", + "ĠDist ingu", + "Ġâİ ©", + "ĠGR Bs", + "ĠCe O", + "ĠBiom ass", + "Ġapt amer", + "v isc", + "he tically", + "Ġs id", + "ome g", + "Ġproportion ality", + "ÃŃ s", + "toplas mic", + "ĠConn ected", + "Ġlamin in", + "stra hlung", + "ĠL ad", + "TR AN", + "ä r", + "Ġbasal t", + "ĠCur vature", + "Ġmitig ating", + "opa edic", + "ĠMuh ammad", + "C AR", + "G i", + "Ġet ch", + "ha ir", + "Ġpur ine", + "Ġbenchmark ing", + "re ich", + "Ġmet hicillin", + "âĪ ¥", + "Ġman ages", + "sol vent", + "ĠSha o", + "h c", + "Ġstr uck", + "Ġnucle osome", + "ĠPubl ication", + "M etric", + "Ġw ines", + "ĠM BL", + "ĠH ub", + "ĠAss istant", + "Ġreli ance", + "Ġrout ers", + "ĠHer z", + "ĠTob acco", + "ro gram", + "ĠH SD", + "ĠL BP", + "Ġinf lection", + "sch ool", + "Ġspons ored", + "ĠCen ozoic", + "Ġentertain ment", + "ati an", + "archit ecture", + "brow se", + "RE C", + "ist ure", + "ĠCh olesterol", + "ĠSim plified", + "Ġpolyp eptides", + "Ġpunct ures", + "arachn oid", + "S elf", + "Ġan orexia", + "ĠO le", + "ĉĉ ĠĠĠĠ", + "GB T", + "Ġcardiomy ocyte", + "ĠFlo quet", + "anal og", + "Ġsensiti zed", + "ĠCep he", + "c atch", + "ch ial", + "Ġcere mony", + "Ġter at", + "Ġamelior ate", + "olys in", + "et ooth", + "ak in", + "ha em", + "Ġent ropies", + "Ġarg u", + "Ġcop ied", + "ling ton", + "ĠHer pes", + "ĠSchw ann", + "y k", + "ĠC EA", + "ĠI CH", + "Ġwr ink", + "Ġrun ners", + "Ġgal van", + "Ġconsol idated", + "ĠâĢ ¡", + "ĠClass ic", + "Ġepidem iologic", + "ĠDri ving", + "Ġtrast uzumab", + "C YP", + "N CT", + "t ability", + "Ġs lee", + "ĠN eck", + "Ġassess es", + "Ġsymmet rically", + "ĠPot ts", + "ĠRib osomal", + "d iction", + "g all", + "ĠAcc eleration", + "CL A", + "ACT ER", + "x ed", + "Ġg eriatric", + "th reonine", + "Ġab ort", + "Ġar tem", + "ĠDis ney", + "ĠCorrespond ence", + "Ġre nt", + "ĠN UM", + "ĠCh un", + "ĠRec ogn", + "Ġcrystall ized", + "Ġcontrad icting", + "vis ors", + "mal ignant", + "rophys iology", + "Inf rared", + "g z", + "Ġsub lim", + "omat osis", + "osyl transferase", + "Ġholog raphy", + "oren stein", + "¾ ±", + "ĠSe bas", + "acc um", + "Up per", + "ant enna", + "Ġbl ur", + "Ġsm ell", + "Ġthree fold", + "ĠPl ayers", + "Ġallevi ated", + "B in", + "Ġn inet", + "ĠD na", + "Ġgeneral izing", + "Ġbreak age", + "ĠMor rison", + "mac ro", + "Read er", + "ograv imetric", + "Ġd h", + "le w", + "xt on", + "Ġdec eleration", + "ĠCor related", + "ĠLeg ion", + "Ġgam bling", + "B inding", + "ĠIn As", + "low ering", + "Ġeuthan ized", + "ĠDall as", + "ĠD w", + "ĠDi jk", + "ĠPol ic", + "ĠT IME", + "ĠH EL", + "ĠL anguages", + "Ġpar abol", + "por ating", + "Ġfr ustration", + "μ M", + "ball s", + "ĠArm strong", + "Ġcontrac tility", + "Ġmetalloprotein ases", + "am eric", + "ĠZ ak", + "ĠCost s", + "A lex", + "d og", + "p w", + "ĠT ight", + "ĠAn terior", + "Ġpe aking", + "Ġneg ativity", + "Ġhyd ride", + "ĠL iv", + "Ġster ilized", + "Ġverb atim", + "Altern atively", + "RE QU", + "ĠTy phimurium", + "ĠWein berg", + "D SC", + "r q", + "Ġcor rug", + "Ġmic rons", + "co ord", + "i oid", + "s at", + "Ġfl occ", + "ĠAcc elerated", + "Ġsix teen", + "abs ence", + "ĠSpe aker", + "om ological", + "ĠA pr", + "Ġmat roid", + "tig ht", + "ogene tically", + "rum p", + "ĠInhib its", + "ĠOlymp us", + "Ġposs ession", + "Ġsuper visor", + "Ġconc ise", + "optim ized", + "v ivo", + "Ġstep ped", + "ocy anine", + "F ive", + "an as", + "ar ten", + "ĠC aco", + "Ġsol utes", + "IT AL", + "ĠRed dy", + "Ġwar ping", + "Ġolig omer", + "Ġc apped", + "Ġv oted", + "ĠR ico", + "ĠT rem", + "Ġl ime", + "ĠI SP", + "ĠL ayers", + "sk in", + "rang ed", + "á z", + "Ġbio activity", + "Ġd urable", + "Ġh n", + "ĠC AB", + "Ġv a", + "ĠU WB", + "ĠSt uart", + "Ġlength y", + "Ġinvas iveness", + "Ġâĩ Ķ", + "jo ining", + "ĠRB Cs", + "Ġresil ient", + "ĠManip ulation", + "G erm", + "cont ribution", + "Ġqual ify", + "ĠD ashed", + "Ġacceler ations", + "ĠCyt ochrome", + "Ġcircumst ellar", + "c avity", + "Ġan atase", + "ĠDe vi", + "Ġpur su", + "ĠMicro RNAs", + "Ġnorth ward", + "Ġsun flower", + "ĠEnter tainment", + "Pac ific", + "ĠHolog raphic", + "u j", + "ere ll", + "met hanol", + "Sur face", + "opos itive", + "Ġthreat ening", + "Ġtransc end", + "D epend", + "Ġq i", + "tis ed", + "ĠBr istol", + "umm ation", + "Ġextract or", + "Ġfav oured", + "ĠPy ro", + "ĠEngine ers", + "flat ten", + "toler ance", + "Ġ xt", + "ĠT ot", + "Ġtest bed", + "IC U", + "ĠSw arm", + "Ġintern ationally", + "Ġant ine", + "ĠInsur ance", + "b ai", + "n h", + "Ñ ĭ", + "os ac", + "ĠL ec", + "th or", + "Ġout ermost", + "Ġdo ors", + "Ġbi ometric", + "glut amate", + "ĠWood s", + "ĠMun ich", + "u ximab", + "pl aces", + "Ġam yotrophic", + "ĠPar am", + "ĠChrist ensen", + "A ge", + "en ne", + "Ġan im", + "Ġrec rystallization", + "ĠPro positions", + "Ġsn ails", + "Second ly", + "ĠPU FA", + "F rance", + "S rc", + "v itro", + "om ass", + "ur u", + "ĠL ever", + "ect onic", + "emb l", + "PC L", + "Ġcoordin ator", + "ĠFox p", + "ĠBir mingham", + "ĠLib eral", + "Ġcru ise", + "Ġi θ", + "Ġsym p", + "az aki", + "ĠPar se", + "Ġhyd rologic", + "Ġprolong ation", + "ĠHay es", + "Ġsubm uc", + "Ġaggl omeration", + "A RE", + "ĠF MR", + "ĠL omb", + "math char", + "Ġstruct uring", + "Ġelectroph oretic", + "Ġdimin ishing", + "Ġbra ke", + "chen ko", + "ĠPere ira", + "l ens", + "Ġback end", + "Ġillustr ations", + "Ġdemand ed", + "Ġnotice ably", + "ĠKa iser", + "ĠDavid son", + "Ġbrak ing", + "T p", + "For ward", + "μ ν", + "ĠCd S", + "Ġaster oids", + "Provid er", + "ĠE ut", + "Ġtr il", + "ung s", + "Ġdiv ing", + "ĠUAV s", + "ĠiP SC", + "i int", + "Ġ ×", + "th rombin", + "Ġcoordin ating", + "ext rem", + "Ġembol ization", + "ĠAdi p", + "pl ated", + "ĠH ag", + "ĠE TS", + "Ġbro od", + "An g", + "ĠPC V", + "det ail", + "R SS", + "b ens", + "Ġt ier", + "ĠC ock", + "Ġg ay", + "Ġqu int", + "Ġag enda", + "Ġaff airs", + "ĠMod erate", + "hel ical", + "ĠEqu ivalent", + "Ġproportion ally", + "Col umn", + "FW HM", + "A ir", + "E num", + "ific e", + "arc sec", + "ĠTR IM", + "ĠLab eling", + "Q AM", + "p ies", + "Ġis otropy", + "ĠG ó", + "Ġpo inters", + "tig raphy", + "ram ers", + "Ġmac aque", + "Ġmiss es", + "Ġelliptic ity", + "present ed", + "galact osidase", + "É Ľ", + "in ion", + "Ġm ite", + "ll l", + "Ob jective", + "Ġprison ers", + "ĠHerc ules", + "Ġanti s", + "Ġclos ures", + "ĠMar tian", + "Ġter pen", + "rob ust", + "Ġsequel ae", + "al arial", + "ĠC SA", + "ĠB land", + "ĠG ent", + "Ġor phan", + "Ġind ent", + "big wedge", + "Ġdefin able", + "Ġolig osaccharides", + "ĠBat talion", + "Ġis ometries", + "az olin", + "ĠSh own", + "spect ra", + "Vis ual", + "<<<< <<<<", + "Ġlenti viral", + "othel ioma", + "Ġted ious", + "ĠB CI", + "Ġge ologic", + "Ġconsum es", + "ĠAbl ation", + "le ast", + "Ġth igh", + "Ġsec recy", + "cover ing", + "e iro", + "à µ", + "ĠT BS", + "Ġis omerase", + "Ġrecomm ends", + "ĠVor tex", + "ĠB ray", + "Ġsub d", + "ĠOp tions", + "Ġmetam aterial", + "ĠSqu ares", + "t rap", + "im on", + "Ġhe sit", + "Ġab c", + "cess ing", + "ĠRE T", + "Ġpin ned", + "Ġket ones", + "Ġweld ed", + "ĠMitochond ria", + "Ġing ested", + "ĠQ FT", + "Ġcompar ator", + "Ġoxid oreductase", + "Ġtet rad", + "ĠSens itive", + "Ġcatch ments", + "Ġrefuge es", + "Ġpuber ty", + "A rab", + "Ġinter annual", + "sc attered", + "ĠMet am", + "Ġcycl ization", + "pert ures", + "ĠLIN C", + "r ules", + "ĠP ont", + "PT H", + "ĉĉĉĉ ĉĉĉĉ", + "S anta", + "ĠL NC", + "Ġsub modular", + "rec tive", + "Ġtr if", + "Ġsent inel", + "ĠTw in", + "kelet ons", + "m iral", + "am ing", + "ĠG ay", + "Ġinter specific", + "Ġrel ieve", + "Ġend omorphism", + "ĠExp anding", + "ĠRun time", + "y ang", + "re quires", + "od ine", + "omet abolic", + "St ore", + "plan et", + "Ġre nov", + "__ _", + "aden osine", + "u itive", + "Ġk el", + "ĠPro long", + "ĠAd vance", + "Ġantimicrobial s", + "ĠMunic ipal", + "ĠNeutroph il", + "F As", + "ĠF ame", + "ib us", + "ET E", + "Ġstep ping", + "ĠBl ot", + "ĠLa ura", + "Ġrock y", + "ĠLim a", + "Ġmitig ated", + "ĠLam bert", + "Ġunexpl ored", + "Ġtrigon ometric", + "p ig", + "ĠH eli", + "Ġfin ely", + "Ġoxid izing", + "Ġcolon oscopy", + "activ ities", + "ĠE asy", + "Ġunexpl ained", + "ak y", + "AS M", + "work er", + "ĠCr ist", + "ãĢ ģ", + "ul k", + "ĠS ugg", + "ĠM im", + "Ġiter ates", + "Ġsulf oxide", + "gluc an", + "Ġreact ant", + "Ġphag ocytic", + "B rain", + "uc ted", + "ĠSc and", + "ĠCa CO", + "Ġaffili ation", + "Pol icy", + "ĠInfant ry", + "F unctional", + "r times", + "Ġw ond", + "ard ment", + "ĠWe il", + "Ġdirect ors", + "uff ix", + "ĠRu iz", + "ĠPhenomen a", + "Ġmicro b", + "cos m", + "Ġutil isation", + "pers ed", + "Ġcon sole", + "tic ulate", + "Ġdes ens", + "Ġreplic as", + "Ġpluripot ency", + "ĠUk rainian", + "Ġhydroly zed", + "ĠBiod iversity", + "E fficient", + "ĠK ash", + "min or", + "Ġconcl usive", + "Ġtent ative", + "j ira", + "Ġm b", + "ĠI PA", + "ĠP is", + "Ġgover ns", + "ĠSouth west", + "oe ba", + "ĠMoh ammad", + "alb umin", + "c ircles", + "ĠH edge", + "ĠAm ph", + "B ACK", + "O ld", + "h istor", + "ac ular", + "ĠN OR", + "hen ius", + "vis ions", + "miss ibility", + "Ġthrombo embolism", + "at ized", + "Ġw il", + "aw ing", + "AS I", + "Ġheter odimer", + "Ġbuff ering", + "ĠIde ally", + "ĠE gg", + "ograph ies", + "ĠAp pl", + "ĠCI s", + "mean ing", + "ĠSM AD", + "Ġphenyl alanine", + "ĠTit anium", + "ĠZar iski", + "Ġn ymph", + "Ġh ired", + "ĠP PC", + "ĠK G", + "ĠGu ill", + "ogly cans", + "er ial", + "D ele", + "il us", + "ĠF itness", + "Ġwh ales", + "gr ant", + "most ly", + "Ġclim ates", + "ĠCamp aign", + "Mg O", + "Ġepist emic", + "L ipschitz", + "ĠL AT", + "Ġcl adding", + "vac uum", + "agglut inin", + "k ill", + "Ġs ail", + "Ġar tistic", + "ans w", + "ĠSD F", + "ĠKe ith", + "Ġsor afenib", + "Ġgall bladder", + "direct ory", + "Ġphotore ceptors", + "ĠFok ker", + "D U", + "Ġed itors", + "Ġte lecommun", + "ardi a", + "ĠPublic ations", + "Ġscrew s", + "ĠMathem atica", + "R SV", + "ĠAp ply", + "ĠST S", + "ĠMur ine", + "Ġd ump", + "Ġl ingu", + "ĠD ixon", + "Ġover comes", + "ĠPre operative", + "Ġmig rant", + "Ġbelie ves", + "B K", + "ac tively", + "ĠI SC", + "qu as", + "Ġal ga", + "ich ael", + "Ġdis asters", + "Ġprac ticed", + "hydro phobic", + "ĠNi ño", + "ĠEth anol", + "Q E", + "ĠS J", + "ĠD engue", + "Ġap pl", + "ĠY oon", + "enz o", + "IF Y", + "Ġchron ological", + "er in", + "ĠP eg", + "ĠRe levant", + "Ġqual ification", + "ev ine", + "Ġdend rite", + "DT D", + "chol inesterase", + "w atch", + "ĠS anchez", + "Ġwas hes", + "Ġper mafrost", + "ĠTer tiary", + "Ġsynthes izing", + "Ġexped ition", + "rout ine", + "ĠSear ching", + "ĠS é", + "res idual", + "ĠL CD", + "enti ties", + "Ġend ovascular", + "Ġparam ount", + "p her", + "Ġstraightforward ly", + "Ġvas odil", + "ĠSchist osoma", + "Ġper missions", + "cent red", + "Ġfr ustrated", + "struct uring", + "ĠSch l", + "ĠIniti ation", + "Ġcu ticle", + "Ġforget ting", + "ĠS as", + "ĠS ult", + "un o", + "Ġdis integration", + "ĠV G", + "Ġw ards", + "ĠI RE", + "up ro", + "Ġsub gen", + "Ġsub classes", + "ĠSt and", + "ĠHe ight", + "inter pretation", + "Ġgly can", + "ĠSol vent", + "ĠMal ignant", + "Ġuns uitable", + "ĠCox eter", + "Ġspermat ogenesis", + "Ġful lerene", + "F ox", + "S OC", + "w et", + "arm stadt", + "Ġprop ofol", + "index ed", + "Ġsn akes", + "Ed it", + "ĠmJ y", + "R IB", + "Ġe y", + "ĠAl kal", + "Ġtri axial", + "PS K", + "ne o", + "Ġend o", + "Ġglycos ides", + "Ġsyll ables", + "Ġs orghum", + "lo or", + "Ġge othermal", + "gu inal", + "ĠSerb ia", + "æ ĸ", + "ĠS entinel", + "igh ters", + "Ġkey board", + "Ġban ana", + "gran ular", + "Ġdecid uous", + "ĠH AR", + "ne uron", + "ĠCar n", + "Ġburn s", + "Bo ost", + "ĠDetermin istic", + "p ipe", + "ĠF AD", + "ĠB ovine", + "ĠR ou", + "Ġk an", + "aut onomous", + "utri ents", + "Ġhypoth yroidism", + "ĠSIN R", + "st ret", + "Ġun altered", + "ĠZ ika", + "val ley", + "Ġlong itudinally", + "Ġfluores cein", + "cat heter", + "ĠCong enital", + "Ġpie z", + "Ġabbrevi ated", + "ĠChlam ydia", + "Ġa ired", + "Ġqu een", + "Ġinstruc tive", + "Ġabrupt ly", + "Ġrecur rences", + "I MP", + "Ġex osome", + "ĠH SCs", + "Wr iter", + "el is", + "ĠAr ithmetic", + "enari os", + "Ġlig ated", + "ĠLocal ized", + "ĠFre eman", + "Ġcarn iv", + "ĠC ereb", + "Ġg rac", + "ĠG ond", + "ĠV ancouver", + "ob ox", + "Ġtyp ed", + "ĠÄ ¥", + "Up on", + "F uture", + "EN G", + "de ad", + "Ġser pent", + "ĠAss ignment", + "ĠUp dated", + "Ġhistor ian", + "Ġtroposp heric", + "C loud", + "b umin", + "ĠP ras", + "ĠB asket", + "ĠâĪĴ âĪĴ", + "benz odi", + "ĠTra uma", + "ĠBehavi ors", + "Ġp ter", + "ir radiation", + "Ġsp oke", + "ari atric", + "Ġpl ugin", + "Ġsuper sonic", + "Ġdoc etaxel", + "itig ation", + "Ġdiges tibility", + "n em", + "Ġp b", + "ĠC SR", + "Ġfo uling", + "Ġrhe ology", + "Ġflood s", + "Ġglu ing", + "agasc ar", + "j ets", + "p ti", + "est on", + "ĠK ü", + "Ġopen ings", + "Ġisol ating", + "ĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ ĠĠĠĠĠĠĠĠĠĠ", + "Ġsemicon ducting", + "r ative", + "ec ology", + "ur ization", + "Ġmulti factorial", + "sh adow", + "Ġcross linked", + "Ġphyl a", + "Ġprem ises", + "ĠLO W", + "general ized", + "ĠPolynomial s", + "Ġb ismuth", + "ĠR oz", + "ĠDec oding", + "ĠClass ifier", + "conduc ting", + "Ġlit term", + "M ann", + "Ġf ant", + "ĠC Z", + "ĠP SNR", + "Ġstar ring", + "ĠPol yg", + "ĠHol m", + "r g", + "ad ditional", + "gu an", + "prof essional", + "Ġin quiry", + "ĠP g", + "ĠSch mid", + "Ġhead ed", + "cha ft", + "ĠExp and", + "Ġcompan ions", + "V an", + "ĠS ie", + "Ġcan als", + "ored oxin", + "Ġcoll iding", + "abs olute", + "ĠPhot os", + "ĠLeg acy", + "Ġrevascular ization", + "ĠP SM", + "Ġexp enses", + "IS MA", + "inter vals", + "Ġmultic ellular", + "Ġnons m", + "Ġresemb lance", + "H ep", + "Ġw ool", + "Ġn iger", + "ess a", + "asc i", + "Ġrot ates", + "Ġcompe titions", + "Ġarri vals", + "Ġlute in", + "Ġscholar ship", + "F ran", + "Ġre used", + "ĠEqu ivalence", + "ĠGL UT", + "grad ing", + "sal t", + "Ġcommens al", + "Ġfra ud", + "ox ib", + "Ġgastro enter", + "Ġrain y", + "Ġasser ts", + "Oper ation", + "Ġflatten ing", + "P ut", + "X B", + "Ġp M", + "Ġcon ic", + "ob tain", + "ĠRo ber", + "N ovember", + "ĠJ P", + "Ġfe brile", + "ĠBar riers", + "================================ ================================", + "Ġhemic ell", + "ĠS CS", + "ĠN em", + "Ġr aster", + "cl ude", + "Ġïģ ¦", + "ĠElli ott", + "b order", + "Ġd ÏĨ", + "rib ose", + "ĠEn v", + "ĠDiff use", + "ĠSupers ymmetry", + "Pear son", + "F ETs", + "y ah", + "ul ia", + "ĠD warf", + "ĠH ull", + "ĠAtt ribution", + "Ġrepos itories", + "ĠGN SS", + "ĠV ectors", + "Ġsuccess es", + "ĠMan hattan", + "umb ent", + "dig it", + "Ġcircumf erential", + "B etween", + "D eg", + "o ue", + "Ð ¹", + "ĠD ere", + "ĠR f", + "Ġr ide", + "ĠV oc", + "Ġprot est", + "Ġpur pos", + "ĠProof s", + "names e", + "Ġbank ing", + "ĠGastro intestinal", + "ĠU nt", + "Ġwhen ce", + "ĠY ue", + "ĠRe habilitation", + "Ġexchang ing", + "ĠACT H", + "Ġc apping", + "am ido", + "ĠB ap", + "Ġpl at", + "to String", + "Ġelectro encephal", + "Ġelectrosp un", + "M pc", + "j ord", + "on v", + "Ġc raft", + "ĠC Cl", + "ĠSt rip", + "Ġmed itation", + "oxid ative", + "ĠRed uce", + "ĠCommon wealth", + "Ġrif amp", + "F lu", + "Ġre analysis", + "ot rich", + "ĠE SA", + "Ġj th", + "hel in", + "ĠGen otype", + "Ġdiagonal ization", + "ĠGab riel", + "Ġquarant ine", + "ĠC rab", + "ĠD ict", + "acc umulation", + "be k", + "ĠDiff erentially", + "Ġlac tis", + "tetrahydro furan", + "l aser", + "ĠU m", + "Ġme ga", + "rm e", + "ĠInd ians", + "ĠLeon ard", + "Ġcommod ity", + "Ġfumig atus", + "i ou", + "ĠE chin", + "ost ream", + "Ġmemb ran", + "sim ulations", + "back end", + "ĠOB JECT", + "g iving", + "Å Ļ", + "Ġinf ective", + "Al g", + "ĠHu h", + "ĠMI CR", + "Ġfollow ers", + "fer ro", + "Ġcyan ide", + "P resent", + "Ġ END", + "ĠM Cs", + "Ġtim eline", + "ĠEmbry onic", + "Identif ier", + "Ġincon clusive", + "ĠGamm aproteobacteria", + "n ets", + "ĠHe ating", + "ank ar", + "th r", + "ĠK IT", + "ĠCh ip", + "Ġbl ob", + "Ġcalc ulator", + "Ġtext ural", + "Ġalloy ing", + "Ap plication", + "ĠProte omic", + "Ġantidepress ants", + "ur k", + "Ġcrystall ography", + "Ġcred its", + "Ġmuss els", + "T om", + "ĠF ST", + "ĠF old", + "ĠH ew", + "An n", + "bro ok", + "Ġglycol ytic", + "Tor ch", + "Ġv m", + "ĠM are", + "ĠJ y", + "Ġhetero junction", + "ĠBorrel ia", + "R isk", + "ĠN aturally", + "Ġsupp lying", + "sign ature", + "l k", + "Ġa rachid", + "ol ov", + "ĠS ok", + "ĠH ö", + "ĠR az", + "ĠV ander", + "Ġdel ib", + "Ġmy th", + "Ġmid brain", + "Ġdece ased", + "ĠS CO", + "ĠTh romb", + "Ġcur r", + "Ġsum mit", + "mi RNAs", + "dimethyl amino", + "Ġphotoc atalyst", + "verb ose", + "gom ery", + "Ġw ed", + "ĠM ate", + "Ġsign i", + "rastruct ures", + "Ġrecipro city", + "b ner", + "m ast", + "n eck", + "Ġco ins", + "ĠHist ogram", + "cr it", + "Bbb k", + "A W", + "t own", + "dis placement", + "ĠNe ph", + "separ able", + "Ġdiast ere", + "ĠMODEL S", + "Dep th", + "ĠNeisser ia", + "p dev", + "u vial", + "ĠB MS", + "ĠD ennis", + "Ġr p", + "Ġnan ometer", + "roc yt", + "ĠRoman ian", + "Ġconce ivable", + "C OS", + "al veolar", + "as tig", + "ab we", + "enc ode", + "rol actone", + "Ġread mission", + "inters ection", + "Ġamplic ons", + "tim ulated", + "Ġcoll apses", + "ochrom atin", + "H aw", + "ect rum", + "ft ype", + "ric a", + "Ġam id", + "MP O", + "ĠExt ensions", + "Ġvar ic", + "Ġdimin ishes", + "Ġcathe ters", + "N odes", + "Ġb box", + "em ination", + "Ġts unami", + "diagn osis", + "c od", + "q r", + "ĠF en", + "Ġworth y", + "Ġâĩ IJ", + "inform atic", + "ograp her", + "Ġundet ected", + "ĠN CAA", + "Ġcarcin ogenic", + "R U", + "Ġan eu", + "plit udes", + "ke eper", + "ĠÄ ģ", + "Ġau tistic", + "Ġcomprom ising", + "Ġunim odal", + "Ġr umin", + "ap a", + "Ġint olerance", + "Ġdirec ting", + "Ġpe a", + "Ġcomm enced", + "Ġshadow ing", + "C enter", + "Ġcl ad", + "Ġbl ues", + "bin its", + "Ġmis classification", + "ĠFA ST", + "W at", + "Ġm Cherry", + "Ġb rig", + "est radiol", + "Ġwave functions", + "Ġblo oms", + "Ġacc ent", + "aj i", + "occ urring", + "ar rest", + "Ġspecial ty", + "Ġuncon ditional", + "Ġspong es", + "Ġdys functional", + "ĠNO X", + "Ġultrac old", + "Ġmartens ite", + "O US", + "n ier", + "is ic", + "ĠMat sum", + "Ġleuk emic", + "ĠBrad ley", + "D ensity", + "ĠS emiconductor", + "ĠC ause", + "ĠIn set", + "ĠK em", + "ĠU PR", + "par a", + "ech st", + "ym et", + "Ġag ro", + "ĠY Y", + "ĠReg eneration", + "Ġancest ors", + "ĠTiss ues", + "Ġsulfur ic", + "k d", + "Ġl asing", + "ĠP up", + "ae i", + "Ġmamm al", + "ĠBrad ford", + "Ġsegreg ated", + "is olated", + "ĠC uba", + "Ġblock age", + "Ġseam less", + "Ġperoxis ome", + "h ui", + "Ġin aug", + "Ġinf ecting", + "ĠCh ampion", + "ĠAt titudes", + "calc ulate", + "Ġt ighter", + "ĠS AC", + "ĠE pi", + "Ġat m", + "Ġphys ico", + "Ġn th", + "ĠC anyon", + "Ġser oprevalence", + "Ġhom o", + "ĠUnivers it", + "Eval uation", + "ĠAPO E", + "j ob", + "Ġm K", + "Ġre ign", + "ab o", + "ĠR ugby", + "ĠN ets", + "Ġr ituximab", + "ati veness", + "Ġph y", + "orn is", + "Ġfeedback s", + "Un ited", + "Pr inc", + "imb abwe", + "ĠGir ls", + "Ġunavoid able", + "ĠSeman tics", + "B reak", + "F ISH", + "M ix", + "Ġn x", + "ĠBa o", + "dimethyl phenyl", + "ĠT OF", + "ĠC rown", + "ĠG GA", + "ĠJ H", + "Ġsuper string", + "ĠCR Y", + "Ġkind ly", + "Y N", + "Ġund oped", + "ex cluding", + "ĠLe o", + "ĠPROP ERT", + "peritone ally", + "m ant", + "ê °", + "Ġf ranch", + "ĠPro st", + "DE s", + "Ġcot rans", + "Ġr k", + "Ġgeneral izability", + "Aut hor", + "ĠAnd rea", + "ĠConf ocal", + "ĠAdi pose", + "î Ĺ", + "er jee", + "Ġan imated", + "ĠF ad", + "ĠCor rosion", + "ĠCirc adian", + "Ġacceler ators", + "ĠArk ansas", + "Ġm ars", + "ĠC uc", + "ĠInter faces", + "Ġretrie vals", + "Ġmelan in", + "Ġss DNA", + "vast ava", + "Ġallerg ens", + "b ud", + "Ġin accessible", + "ic tions", + "ĠM ood", + "ind a", + "Ġam eric", + "Ġsym biosis", + "bers ome", + "occ ur", + "ĠMarc us", + "ĠSuperconduc tivity", + "ĠC ort", + "ĠH MS", + "Ġph ased", + "ĠJ ess", + "Ġprop ulsion", + "ext ract", + "Ġsuccin ate", + "ĠÖ Ĵ", + "ink el", + "Ġsil ence", + "ĠSU V", + "Ġconstitu ency", + "Ġbacteri ophage", + "g em", + "ĠM CL", + "ore ne", + "ĠG oss", + "IC D", + "Ġglut amic", + "Ġcoex isting", + "STE MS", + "opot ential", + "ĠE y", + "ĠL ecture", + "ell ae", + "Ġimmun oprec", + "Ġtim ber", + "ĠVul ner", + "Ġa roma", + "Ġs ands", + "ĠSp an", + "Ġher n", + "Ġincub ating", + "Ġtransmit ters", + "ĠHom ogeneous", + "ĠConstruct ing", + "d it", + "Ġt c", + "al ass", + "Ġst ents", + "ĠM ID", + "Ġan oxic", + "Ġprov isions", + "ĠCap ac", + "neut ron", + "ĠVO Cs", + "Jan uary", + "V AS", + "on ce", + "ĠC ache", + "op ulation", + "ĠV TE", + "Ġinter phase", + "Ġbl og", + "ocus ing", + "hi ro", + "ĠRE C", + "Ġanis otropies", + "ben ef", + "Ġcons tipation", + "ĠCan al", + "Ġport rait", + "sil yl", + "ĠLink ed", + "ĠBow l", + "Ġmonop oles", + "ĠPere z", + "W IN", + "ĠT AP", + "Ġr uthenium", + "ĠAd herence", + "ĠEn zymatic", + "Ġspecific ities", + "Ġsk i", + "ĠC ST", + "Ġpo etry", + "AT ES", + "ram a", + "lo res", + "AL U", + "Ġvas oconstr", + "Ġgranul ocyte", + "ib i", + "Ġop ts", + "aves drop", + "ept in", + "· ·", + "ĠJe ong", + "Ġmedull ary", + "ĠDemonstr ation", + "ĠF IB", + "ĠB RD", + "ĠV V", + "Ġall o", + "R ule", + "T f", + "Ġun realistic", + "Ġlat itudinal", + "RO P", + "ĠCorrel ates", + "I U", + "ĠP ore", + "oc rit", + "ĠK all", + "Ġchar coal", + "ĠMong olia", + "âĪ ħ", + "ĠEn tity", + "Ġgram s", + "g raphene", + "m ine", + "ent ric", + "ĠP p", + "ĠW elfare", + "ĠJ ets", + "Ġaff irm", + "ĠBel le", + "ĠStrateg ic", + "API ENTR", + "K H", + "rm ann", + "Ġassoci ating", + "ĠSur viv", + "Ġnicot inic", + "ĠWL AN", + "Ð ¿", + "Ġt ears", + "ĠRe vised", + "Ġphosph odies", + "Ġhors eradish", + "ĠL AR", + "to ok", + "ĠDes cent", + "ĠNO x", + "ĠStein er", + "ĠPerm ian", + "ĠVenez uela", + "Ġdesic cation", + "D IS", + "ĠM SP", + "Ġpo pl", + "rel s", + "Ġï£ ½", + "Ġlear nt", + "ĠBi ofilm", + "ĠPC NA", + "ĠAtt ribute", + "ĠGro thendieck", + "ĠAdoles cent", + "n v", + "st derr", + "obal t", + "ĠYam amoto", + "Ġaliqu ot", + "r ater", + "ĠO re", + "ĠK IR", + "ack er", + "Ġïĥ »", + "Ġstrat osphere", + "ĠC ust", + "resp ect", + "Ġglut amatergic", + "Ġencour ages", + "c tic", + "it ched", + "ph ins", + "Ġsub urb", + "Ġhome omorphic", + "hex ah", + "Ġmini atur", + "C AN", + "a head", + "ĠB LE", + "ĠR BF", + "Ġac utely", + "Ġï£ ¾", + "Ġanten n", + "UR N", + "ĠGir l", + "Ġbiore actor", + "ĠLeib niz", + "Ġv ial", + "ĠL ich", + "ob ac", + "ĠWhen ever", + "inhib ition", + "C ast", + "Ġstrip ped", + "ĠAst rophysics", + "pres ence", + "ĠFlo er", + "ipot ent", + "dichlor o", + "C LE", + "f inger", + "on ates", + "st ri", + "ĠS perm", + "ĠD BS", + "op eptide", + "se paration", + "ath ing", + "math p", + "ou ples", + "Ġent ropic", + "Ġsw ollen", + "Ġdon ated", + "Ġsettle ments", + "oven ous", + "P erm", + "ĠS ard", + "eg en", + "ĠAl ph", + "ĠCo operation", + "ĠPD AC", + "F inal", + "l apse", + "Ġre vol", + "ĠI x", + "ĠL ens", + "Ġk th", + "rel axation", + "Cl O", + "ichlor o", + "Ġwrap per", + "ĠSimultaneous ly", + "Comput e", + "ë Ĭ", + "im plantation", + "ĠV LA", + "hem e", + "ĠMay or", + "ĠFac ilit", + "Ġb att", + "im mer", + "Ġcur ated", + "Ġconf luent", + "gener ational", + "star ts", + "Ġgranul osa", + "arboxyl ate", + "ĠRies z", + "Ġtext book", + "Ġconstit utional", + "ĠPe ace", + "ĠComm ander", + "Ġobsc ured", + "v il", + "ad dition", + "ĠW asserstein", + "co ords", + "ĠProb es", + "Ġdeline ated", + "TZ VP", + "ĠIN F", + "Ġdos ages", + "Ġolig omerization", + "ĠNAD P", + "MK II", + "om in", + "Ġl hs", + "ug hen", + "ĠJ ong", + "anc el", + "let ter", + "ĠAN C", + "F UNCTION", + "Ġt ram", + "The ir", + "ĠGen erated", + "Ġpoly cyclic", + "Ġcul min", + "Ġrect um", + "Ġce ft", + "Ġmetam aterials", + "ĠBiot ech", + "Ġmys elf", + "Ġun ifying", + "Ġem an", + "ĠSing er", + "triang leright", + "om el", + "ĠC FA", + "oc ha", + "ĠG SM", + "Ġcent rifuge", + "ĠInd o", + "Ġtransport ing", + "LI B", + "Ġoxal ate", + "ĠDul becco", + "Ġal i", + "arg inal", + "ho o", + "isc hem", + "APIENTR YP", + "A part", + "L DA", + "ens ile", + "set tings", + "Ġep hem", + "amp a", + "Ġdu plications", + "ĠWhe eler", + "Phys ical", + "ĠCom pletion", + "ĠOr dered", + "Log ger", + "Ġinterf erences", + "ĠPoll ution", + "Optim al", + "S v", + "a icin", + "Ġp icks", + "di versity", + "tig ens", + "Ġdim orphism", + "fe res", + "ĠRob otic", + "Ġconfirm atory", + "Ġcath odic", + "Ġspir als", + "Ġspr uce", + "Lag range", + "w at", + "ĠAll an", + "den ote", + "C ID", + "al ways", + "it he", + "ĠCh im", + "con ditional", + "bar rier", + "Ġvisual izing", + "Ġïĥ ¹", + "Sch midt", + "Ġconvention ally", + "ĠQU ANT", + "GRO UND", + "Ġ ug", + "ĠC WE", + "ĠIn spired", + "Ġbu yer", + "Ġtherm ost", + "Ġkin ematical", + "an olic", + "Ġd if", + "Ġï£ ¼", + "ĠGe o", + "Ex amples", + "cons istency", + "ĠPal ace", + "ĠVacc ination", + "Ġnatri uretic", + "Y AG", + "ĠCT Cs", + "Un ivers", + "ĠAcknowledg ement", + "memb ered", + "v v", + "ĠS ession", + "Ġinst ar", + "ĠLe vin", + "AV I", + "Ġprolifer ator", + "olith s", + "ĠTemper atures", + "im ming", + "ĠTo eplitz", + "IC ATIONS", + "ĠIntegr als", + "Ġsplic ed", + "D est", + "res ulting", + "ĠH ope", + "Ġen closure", + "ie ves", + "fl av", + "ĠAbd ul", + "Ġleishman iasis", + "à ²", + "os keleton", + "Ġad duct", + "ĠInflu ences", + "E QU", + "ĠS itu", + "Ġse as", + "ĠRe ich", + "cy st", + "ĠEV OLUTION", + "Ġwith stand", + "ĠG inzburg", + "RNA i", + "ĠNon parametric", + "ĠPr incess", + "Ġintra vascular", + "UT IONS", + "Ġglut ar", + "Ġcoinc ided", + "ĠSa ito", + "pret rained", + "comb ined", + "ĠT AM", + "Ġalarm s", + "Ġcyclo oxygenase", + "Ġb n", + "Ġpl agi", + "Par ticle", + "GG G", + "e tics", + "am ber", + "AB STRACT", + "ĠExt racts", + "ĉĉĉ ĠĠĠĠ", + "ĠPhyl ogeny", + "t ow", + "ĠCon taining", + "Ġend onuclease", + "inc ubation", + "Ġoffic inal", + "Ġexplos ions", + "lay out", + "Ġtouch down", + "ĠReve aled", + "Ġinfiltr ate", + "en ith", + "tim ulation", + "ĠK ind", + "erv ices", + "PD A", + "Ġcere us", + "En v", + "Ġlap a", + "k amp", + "m ult", + "ent hal", + "ĠGold stone", + "si RNA", + "stre pt", + "Q ual", + "m other", + "di o", + "Ġinf requent", + "Ġcycl ospor", + "hep atitis", + "thromb otic", + "G ST", + "ĠL j", + "ĠU R", + "of ect", + "ĠAr row", + "eth nic", + "ĠBarc elona", + "C are", + "ti tious", + "Ġe ta", + "Ġvir ions", + "sm ash", + "ĠâIJ ¤", + "Ġa venues", + "ob arb", + "ĠCom ments", + "Ġany way", + "af il", + "ĠBe a", + "ĠBo ys", + "ĠAutom ata", + "ĠSuperconduc ting", + "P ic", + "k Hz", + "Ġn orepinephrine", + "ĠG PC", + "Ġunder lined", + "bra him", + "Ġelectrosp ray", + "Ġses qu", + "ĠTourn ament", + "A ustr", + "ĠG rowing", + "ĠWe bsite", + "LD H", + "cov ariance", + "sever al", + "st abilized", + "Ġdec arboxylase", + "Ġrem ed", + "rho e", + "ĠSR S", + "ĠTre ated", + "ĠMad agascar", + "ĠMag ic", + "Ġweap on", + "ĠYosh ida", + "Ġhypogly cemia", + "ĠBifid obacterium", + "enti tious", + ":: :", + "ĠSing les", + "Ġnic ely", + "Ġunexpected ly", + "ib les", + "ari ae", + "Ġcent roids", + "Ġbroad ened", + "ĠJoh ns", + "ĠBacter oid", + "Ġfram ing", + "Prim ary", + "ĠPict ure", + "gover nment", + "Ġre q", + "ĠT ry", + "ib o", + "Ġliqu ef", + "osens itivity", + "Ġsla ughter", + "ĠD AR", + "Ġlog it", + "Ġprom ises", + "Ġlaw yer", + "ĠFP G", + "T CP", + "Ġinter calation", + "ĠBo e", + "Ġwide band", + "Ġjudg ement", + "romagn ets", + "Last ly", + "ĠIschem ic", + "I MA", + "f ood", + "m uch", + "Ġa venue", + "Ġschist osomiasis", + "ĠExec ution", + "D QU", + "G IS", + "k ines", + "ak age", + "ech t", + "ĠSc aff", + "ĠStr ings", + "ĠMulti level", + "Ġcum bersome", + "ĠRay mond", + "Ġirregular ities", + "ĠAGN s", + "ĠMetast atic", + "ĠIber ian", + "M b", + "R NP", + "h ong", + "is inin", + "Ġth irteen", + "ĠF AS", + "Ġse aling", + "Ġap atite", + "Ġser ially", + "ĠÅ Ŀ", + "D EL", + "F o", + "ĠS oph", + "ĠB ear", + "ĠJ osh", + "rec k", + "ull er", + "Ġexc ursion", + "Ġemb odied", + "Ġhybrid ized", + "ĠLie utenant", + "Per iod", + "Ġmoll us", + "C VD", + "R en", + "RE AM", + "ĠB ACK", + "Ġacc reting", + "Ġcult uring", + "ĠBur st", + "ĠSeg ment", + "Ġaster isk", + "ĠIde al", + "Ġinter tw", + "ĠAt oms", + "ĠST E", + "Ġïģ ª", + "Ġremark ed", + "Ġhair s", + "â ľ", + "ĠMet ropolis", + "ĠPar tially", + "ĠObs erver", + "Ġhemat ologic", + "obil ization", + "ĠBerg man", + "Ġcart esian", + "Ġclath rin", + "ĠS ung", + "Ġr ation", + "Ġsc oliosis", + "oh l", + "mut ant", + "NN s", + "ĠRah man", + "ĠSpati ally", + "P IP", + "Y b", + "Ġd iaz", + "ver tebral", + "ad zu", + "als ki", + "ans wer", + "Ġge ochemistry", + "Ġstem ming", + "w es", + "ox ys", + "Ġmat s", + "ev a", + "ĠHyper bolic", + "arb age", + "Ġclip ping", + "ĠSug ar", + "ĠC ognition", + "ĠD IV", + "Ġtem pt", + "ĠPath ogen", + "ĠPed ro", + "Ġw ak", + "ent ries", + "ĠG CM", + "pro jective", + "Ġprof iciency", + "ĠKn own", + "Ġlex icon", + "ĠMend elian", + "Ġzoon otic", + "le ans", + "ĠT alk", + "Ġk urtosis", + "NA S", + "ĠNow adays", + "ĠL il", + "ĠW MAP", + "Ġdis perse", + "Ġcoll oids", + "eb ra", + "OM ET", + "ĠD CT", + "ĠR ise", + "Ġinter genic", + "GT H", + "Ġtap ered", + "Mark ovian", + "Prot ocol", + "ĠVeget ation", + "r ats", + "Ġd ivalent", + "ĠCr ust", + "zy g", + "Ġpig mentation", + "grad uate", + "ĠRic c", + "Ġcounterex ample", + "Ġs ativ", + "Ġl s", + "ĠCirc ulation", + "is otropic", + "ĠEN SO", + "Ġtrop onin", + "Ġdissol ving", + "Ġcosme tic", + "H f", + "f urther", + "Ġp anc", + "Ġh ops", + "int ra", + "ĠZ he", + "ĠRel iable", + "ivol umab", + "M X", + "R ab", + "ĠP ES", + "ĠB ü", + "Ġad hered", + "Ġflu ency", + "ĠCl aus", + "Ġdel amination", + "Ġgu anine", + "ĠMulti scale", + "ĠEqu ip", + "ĠIll ustr", + "Ġtetra hydro", + "f el", + "l ists", + "Î ŀ", + "em ulsion", + "ĠN Z", + "Ġwas n", + "ai ra", + "Ġarg uing", + "mi RNA", + "ĠExp ressed", + "Ġspectrophot ometric", + "Ġile um", + "Ġflam es", + "F it", + "G on", + "ĠC ulex", + "Ġun weighted", + "Ġnan ob", + "SH V", + "Ġalign ing", + "Ġshut tle", + "Ġchloro quine", + "Ġpyr ite", + "ĠR ica", + "Ġr ift", + "Ġcathe psin", + "ĠPROC ESS", + "P f", + "R aw", + "ray fish", + "SA L", + "coll apse", + "........ ........", + "at ases", + "Ġwork shops", + "oph ile", + "ĠâĬ ĥ", + "Ġbifurc ations", + "T race", + "Ġp ause", + "Ġorbit ing", + "oli ubov", + "ĠCur tis", + "ĠRevis iting", + "ore t", + "Ġinf used", + "lu ents", + "Ġplas tid", + "Ġïģ ¹", + "Ġexec utions", + "ĠGra ves", + "loc ally", + "ĠAtmosp here", + "diab etes", + "ĠPrad esh", + "ĠCof actor", + "is omorphic", + "Ġb od", + "ĠC BD", + "Ġinc ap", + "Ġret rovirus", + "Ġlip ophilic", + "Ġlin oleic", + "Ġtrav elled", + "c ovalent", + "p ick", + "u pl", + "ĠP ole", + "ĠTh ym", + "ĠTe ich", + "Ġcollabor ators", + "Ġinstant ons", + "ĠMEG A", + "ĠHepat ocellular", + "Ġinfest ation", + "ĠPie zo", + "ĠL ub", + "ĠN Cs", + "Ġnucle oside", + "Ġoste ogenesis", + "E igen", + "R MSE", + "Ġl ax", + "ĠK ost", + "ĠV ero", + "ĠCh ou", + "elect rochemical", + "Ġcompe ti", + "ch ia", + "Ġsub module", + "ĠAl low", + "Ġresol vent", + "Ġswe eps", + "Ġsupercon formal", + "pyrrol idine", + "l ofen", + "å Ń", + "Ġdes erves", + "ĠZ imbabwe", + "az ines", + "ĠCons ult", + "Ġcast le", + "Ġpharmaceutical s", + "Ġparac rine", + "Ġjejun i", + "Ġargu ably", + "Ġe NOS", + "Ġher ds", + "Ġvehic ular", + "Ġtriang ulated", + "Ġî µ", + "ĠGrand e", + "Ġanthocyan ins", + "ĠD uan", + "ĠV ibration", + "Ġtri ad", + "Ġhouse keeping", + "B or", + "Ġp ub", + "Ġmal formation", + "gluc osamine", + "inhib itory", + "Dir ac", + "ĠC SD", + "ĠRot ating", + "ĠHTL V", + "Ġdem ol", + "inf iltr", + "Ġhem olytic", + "Ġcarb apenem", + "Ġlum inescent", + "ĠPlan ets", + "Ġmell ifera", + "Ġcortic osterone", + "ĠAdd ress", + "Ġhub s", + "ometh acin", + "å IJ", + "ĠCh ampions", + "ĠRe vision", + "ĠHer bert", + "Ġambig uities", + "K ERN", + "Ġd é", + "Ġl p", + "Ġen vis", + "ĠCh ol", + "rop in", + "Ġdr one", + "m eyer", + "Ġis otype", + "ĠV u", + "ER C", + "Ġvers atility", + "Sp eed", + "Ġae tiology", + "Ġgonad otropin", + "Ġcogn ate", + "ĠCot ton", + "reason able", + "dis able", + "Ġdevast ating", + "P ier", + "P OL", + "ĠB é", + "inc ter", + "alu able", + "Ġpoly hedron", + "ĠRel ay", + "Ġworkflow s", + "F EM", + "in p", + "Ġm ph", + "soft max", + "m ur", + "v r", + "Ġe rent", + "ĠK N", + "Ġstat in", + "Ġflat ness", + "ĠArchitect ures", + "ĠVeter inary", + "Ġnos ocomial", + "S k", + "X ML", + "ĠF os", + "ĠL or", + "Ġradi ography", + "ĠBl um", + "ĠDiscrim ination", + "Ġp unc", + "Ġex its", + "ĠB ilateral", + "ms strahlung", + "Ġcolon ized", + "ĠFib rosis", + "Ġchaper ones", + "abor atory", + "ĠPers istence", + "Ġlum ped", + "Ġrab ies", + "ĠBurn s", + "D ense", + "on tium", + "acet ylation", + "ĠF ET", + "Ġhand ful", + "bi ology", + "Ġundes ired", + "L imit", + "ĠN BA", + "ĠSe oul", + "AP T", + "ĠTrans genic", + "oxygen ation", + "But ton", + "ĠTreat ments", + "Z V", + "is omorphism", + "oc ta", + "iff e", + "ode oxy", + "Ġorgan elle", + "Ġcoll oid", + "Ġcer amide", + "Ġtq dm", + "G PS", + "ĠI SR", + "oc linic", + "ĠL yme", + "Ġep ig", + "ĠTra il", + "I PS", + "Ġs orts", + "ĠZ ebrafish", + "Ġhydrox ylase", + "Sm irnov", + "B ax", + "ĠD ance", + "ĠH ors", + "Ġreach ability", + "Par allel", + "ĠES BL", + "Ġupl ink", + "Ġpostp randial", + "s olar", + "it abine", + "ord ism", + "Ne asy", + "Ġaband on", + "I MI", + "f ake", + "st atistical", + "ĠC ars", + "ib ia", + "Ġà ĩ", + "sp c", + "MD P", + "tiz ations", + "Intern ational", + "ular is", + "Ġvacu oles", + "K C", + "ĠA PT", + "ĠB t", + "ĠB om", + "ĠG MP", + "Ġpione er", + "ĠChair man", + "ĠT ucker", + "ĠR AF", + "ĠN ASH", + "ĠW IT", + "yn yl", + "Ġsup plier", + "ans ky", + "Ġdecom posing", + "ĠUV B", + "ophen ol", + "Ġb arium", + "ĠS MT", + "ot ocin", + "ly tic", + "ran king", + "ĠDi rections", + "Ġinn ervation", + "sw itching", + "d ac", + "Ġh T", + "Ġdoc tr", + "ĠIncre mental", + "ĠEarthqu ake", + "H as", + "L ee", + "m ates", + "pro line", + "ĠRE E", + "Ġviol ates", + "ð x", + "Ġhomogen ates", + "Bo olean", + "Ġd oxycycline", + "ĠMO F", + "iop hen", + "Ġapprec iation", + "fin als", + "character istic", + "ĠContin ental", + "B us", + "E sc", + "X P", + "Û Į", + "ĠCT A", + "Max well", + "Ġarchae a", + "N ik", + "N ONE", + "T W", + "ter ing", + "ĠP erman", + "Ġrest ores", + "opath ogenic", + "ĠMont gomery", + "Ġglucocortic oids", + "Ġ ud", + "ĠN uss", + "ĠN é", + "ĠSt urm", + "Ġatt aching", + "Ġintra peritoneally", + "las ov", + "Ġst ellate", + "Ġanti proliferative", + "Ġmicro organism", + "Ġvis u", + "Ġjud ges", + "random ized", + "allow ed", + "Ġdepri ved", + "develop ment", + "scrib ed", + "ethe rian", + "ĠFras er", + "R am", + "b ib", + "Ġl iner", + "Ġg uns", + "res net", + "ĠL TR", + "ight ing", + "In iti", + "ĠZ imm", + "ĠGe ology", + "Ġantioxid ative", + "Ġmag enta", + "ĠNiger ian", + "galax y", + "ĠMelan oma", + "F ound", + "Ġb um", + "ĠT rop", + "ĠD os", + "Ġmet ab", + "Ġinv oking", + "ĠSch izophrenia", + "CF G", + "Ġgel ation", + "Ġopi oids", + "p is", + "Ġch urches", + "Ġcan onically", + "Ġj ug", + "Ġaccept ors", + "DM EM", + "Ġobl iqu", + "ĠMedic are", + "arpo on", + "Z IP", + "ore active", + "Ġim printing", + "ĠV inc", + "Ġ ¿", + "Ġrest art", + "Ġdent ate", + "en zymatic", + "Ġin guinal", + "ĠN t", + "Ġun observed", + "uct uation", + "Ġbi asing", + "Ġintegr ins", + "Ġur l", + "FP GAM", + "ĠCL UST", + "omat ology", + "Ġmetallic ities", + "Ġintention ally", + "FPGAM GR", + "T yp", + "Ġal ly", + "Ġcom ic", + "ĠL ions", + "Ġim puted", + "Ġà Ł", + "lex ia", + "ĠJan us", + "Ġbr ass", + "ĠDown loaded", + "BU FF", + "iden tical", + "Ġpsychiat ry", + "C CT", + "if ar", + "ĠMand el", + "Ġopto electronic", + "Ġis omerization", + "ĠF ant", + "ĠL ion", + "ĠL ov", + "ĠN af", + "est a", + "Ġbi ocompatible", + "Ġsec retions", + "sc i", + "ĠRet ro", + "ois omerase", + "ĠSn ap", + "Ġsplitting s", + "Ġscav enger", + "proced ure", + "Daw ley", + "ë ĭ¤", + "un ate", + "ĠD ye", + "ĠN EC", + "Ġnan ocl", + "Ġplan etes", + "ĠTR PM", + "Ġvo ices", + "ĠHierarch y", + "m v", + "Ġl asts", + "Ġh oped", + "Ġmed ians", + "ĠAnd reev", + "Ġheight ened", + "ä »", + "Ġin definite", + "ĠK amp", + "ang el", + "gr ids", + "arch ae", + "Ġtherap ists", + "ĠMi R", + "Ġnegoti ation", + "H SP", + "ĠC ustom", + "Ġst ria", + "Ġun acceptable", + "ret in", + "pen et", + "ĠOR R", + "ĠLife time", + "ĠPhosph ate", + "Ġtrop ics", + "ĠWel ch", + "ĠP yr", + "Ġam putation", + "ĠAr tin", + "ĠCa O", + "Ġconject ures", + "Ġat rium", + "ĠCom plementary", + "ĠAl uminum", + "Ġmic row", + "ili ated", + "ĠImmun o", + "Ġbin ocular", + "ĠWeak ly", + "Ġimmun ogenic", + "Ġbath ym", + "ĠPhen otype", + "Ġsial ic", + "S ix", + "Ġa kin", + "ro tor", + "hel m", + "CC ESS", + "Ġneuro protection", + "ĠFif th", + "Ġconting ent", + "Ġsket ched", + "I mp", + "Ġc ached", + "ure ment", + "ĠB ic", + "ĠK ah", + "ber ation", + "atter son", + "Ġglyc ation", + "Ġinvest ors", + "Ass isted", + "ial es", + "sc ience", + "Ġpil ots", + "us cripts", + "MI CS", + "Ġorth opedic", + "war fs", + "gre ater", + "ĠArter y", + "V ideo", + "Ġar range", + "av ar", + "charg es", + "dial dehyde", + "ĠT PA", + "Ġsp elling", + "ĠSe iberg", + "Ġnavig ate", + "ĠPow der", + "ĠR ings", + "ĠCh ron", + "ĠAt g", + "Ġhom ocysteine", + "ĠIdentif y", + "Ġo ak", + "Ġl iability", + "Ġoper ands", + "ĠCT D", + "Ġallevi ates", + "m A", + "ĠL anger", + "Ġsub manifolds", + "ĠJ ag", + "Ġradi ance", + "const ants", + "ĠMor occo", + "Eng ine", + "á ¸", + "â Ĥ¬", + "re vers", + "PC I", + "uns queeze", + "ocon version", + "Ġintens ified", + "Ġrefin ements", + "ofect amine", + "ay as", + "Ġinc idental", + "ĠTh ur", + "Ġover d", + "Ġbit ter", + "Ġign ores", + "а н", + "ĠOT U", + "Ġs err", + "ab y", + "ĠG CN", + "ĠCons umer", + "Ġconc ordant", + "ĠMR C", + "ĠEcon omy", + "satisf ying", + "Ġbiotin ylated", + "Numer ical", + "ĠRash ba", + "st ochastic", + "ĠL al", + "Ġbur dens", + "All oc", + "ĠGraph ics", + "ĠLRR K", + "A IC", + "ĠT ed", + "ĠS ark", + "ow l", + "Ġhe most", + "ĠAn at", + "Ġhom ing", + "ĠChar lie", + "ĠBr uc", + "ih ara", + "ing en", + "ĠV ern", + "ĠY ers", + "Ġid s", + "Ġcirc RNAs", + "Ġconduc ive", + "ĠBR ST", + "Ġgall ium", + "Ġdich otomy", + "F r", + "e tition", + "Ġc esarean", + "ol an", + "Ġr n", + "ub stituted", + "ĠLe aves", + "ĠLe ader", + "col oring", + "D raw", + "Ġser ous", + "Er r", + "Ġinn ermost", + "ĠHam burg", + "S tor", + "j es", + "Ġto l", + "id ade", + "Ġr v", + "ĠIn version", + "Ġmulti phase", + "Ġpseud or", + "ĠGood man", + "ĠJS ON", + "Ġcorrid or", + "Ġp ork", + "ĠS ale", + "ĠN atal", + "Ġattack ing", + "ĠShe et", + "Ġstream wise", + "Ġatom istic", + "Ġfirm ly", + "ĠAch ie", + "Ġp ir", + "ĠI KK", + "ĠF alk", + "ile ptic", + "ĠTR PC", + "Ġadhes ions", + "HR P", + "Ġpauc ity", + "S plit", + "U DI", + "ĠS end", + "ĠP ine", + "ĠL on", + "ĠL ost", + "ef er", + "con caten", + "Ġlo yal", + "Ġgly cop", + "ĠObserv ing", + "ĠMoh amed", + "Y R", + "ĠFil ters", + "c as", + "p ages", + "Ġd A", + "Ġare al", + "ad is", + "ĠL HS", + "ĠThere by", + "Ġvisual izations", + "Ġtw istor", + "unit ary", + "Ġarch ives", + "Ġphenol ics", + "h ik", + "s son", + "ĠI K", + "ĠStud ying", + "Ġtw isting", + "ĠHydro dynamic", + "Ġsplit ter", + "Ġurothel ial", + "Ġal ken", + "ĠG PI", + "Ġcor tices", + "Ġcrop ping", + "Pati ent", + "ĠChlam yd", + "in berg", + "ĠA ircraft", + "ce le", + "ect ral", + "Ġconf erences", + "Ġcre atine", + "al ty", + "pro portional", + "Ġlept onic", + "Ġov ulation", + "uer re", + "tez omib", + "d le", + "init eness", + "ĠSpecim ens", + "Ġcom a", + "ine phrine", + "Ġep im", + "ĠPer cent", + "Co O", + "ĠLo ading", + "Ġven ue", + "ĠTN M", + "Ġpac emaker", + "ĠHoff mann", + "T ech", + "n ie", + "ĠOr leans", + "Ġmagnet ron", + "Ġhospit ality", + "ĠNord ic", + "oprol iferative", + "Ġundo ubtedly", + "ĠS rin", + "Ġhum ic", + "ĠIntegr ative", + "ĠCamp us", + "Ġplant arum", + "radi ative", + "Ġiter ator", + "ĠMes ozoic", + "AP s", + "car inic", + "Ġcheck points", + "ĠïĤ £", + "ĠmA bs", + "ĠLiver pool", + "ìĿ ´", + "ĠEcos ystem", + "Ġneovascular ization", + "Ġdem oc", + "lo ops", + "ĠSU RF", + "Ġpassiv ation", + "Ġconsec utively", + "ĠAlfv én", + "ĠS SE", + "Ġout s", + "stim ulation", + "Ġphilos ophical", + "ĠS ask", + "Ġfl akes", + "Ġfinger printing", + "Ġbuff alo", + "ĠWik imedia", + "Ġrecons titution", + "Ġepithel ia", + "on k", + "en y", + "ĠM Q", + "ĠF ork", + "end ance", + "Ġgeneral isation", + "Ġpe oples", + "Ġconn ector", + "ges ia", + "inter ference", + "Ġcolor ation", + "calc ulation", + "ĠAx ial", + "ĠDES IGN", + "Ġrecess ion", + "Ġdissol ve", + "ĠPartition ing", + "Qx MD", + "G ES", + "V o", + "k har", + "ĠE AE", + "Ġco arser", + "Ġpost traumatic", + "Ġsynthesis ed", + "sil ica", + "tetra hydropy", + "ĠPor ter", + "v ark", + "ent anyl", + "Ġcon ve", + "Ġra fts", + "bre cht", + "Ġrectif ier", + "Ġo roph", + "ĠC EP", + "Ġhist ones", + "Ġstand point", + "Ġanc illary", + "ĠHur ricane", + "c ro", + "Ġre b", + "Ġi T", + "Ġge ography", + "olar ization", + "ĠMan aging", + "Ġxyl ose", + "uther land", + "ĠTaq Man", + "K N", + "Ġt m", + "ĠT AS", + "ist le", + "âĢ «", + "Ġmy corrhizal", + "ĠTer restrial", + "haus en", + "observ able", + "Bri en", + "Ġneutrop enia", + "T aken", + "ĠS MI", + "Ġpol ishing", + "Ġphot op", + "Ġthermal ization", + "Ġpseud oscalar", + "ĠDom inic", + "romy algia", + "Ġechocardi ographic", + "Ill umina", + "ĠI PC", + "ĠH uss", + "ess ive", + "up take", + "Ġweek end", + "Ġcorrobor ate", + "ĠTas man", + "her ty", + "Ġper ine", + "Ġtrans ports", + "Ġgl ance", + "ret inal", + "Pro to", + "igen es", + "Ġprohib ited", + "behavi oral", + "ophe rol", + "ë ¡", + "ĠN ecess", + "ob iology", + "ok k", + "Ġtra versal", + "ĠAnd es", + "Res ource", + "oli tic", + "ç a", + "i rie", + "arc tan", + "Ġmorph ogenetic", + "ĠHu i", + "loss es", + "Ġfulf illing", + "Ġhur ricane", + "om bo", + "Ġg s", + "ĠL v", + "ĠN erv", + "ell osis", + "Ġconf ront", + "Ġorth ologous", + "Ġwet tability", + "Ġcyan obacterial", + "Ġcass ava", + "A UT", + "a vi", + "h len", + "ĠS LA", + "Ġcon vol", + "Ġinter metallic", + "ins ide", + "Ġpolar izability", + "Ġens uing", + "Ġchlor oplasts", + "l id", + "l ips", + "Ġre bound", + "ĠC ary", + "ĠL ambda", + "ĠV iv", + "Ġcalc ination", + "ĠÌ Ĩ", + "Ġcounter factual", + "ĠSil ica", + "Ref eree", + "Ġhomolog ues", + "ĠSpati otemporal", + "ĠArr henius", + "Ġinf lamed", + "ĠZ ambia", + "Ġanti psychotic", + "hel per", + "Bl ood", + "Ġpurch asing", + "ĠSchw inger", + "ĠWilk inson", + "Ġfain ter", + "Ġr ash", + "ĠJ ang", + "ĠCon ductivity", + "rop oda", + "ĠSe q", + "Ġprop olis", + "Ġtub ule", + "ĠLie b", + "optim ization", + "m ounted", + "em es", + "can ic", + "oradi otherapy", + "ĠJen kins", + "N c", + "T ogether", + "Ġf ove", + "Ġm v", + "ĠDef ect", + "ä t", + "ĠFin ance", + "umar in", + "mitt ance", + "ere l", + "ĠF ren", + "ĠR hyth", + "ram ified", + "Ġhyper cholesterolem", + "Ġstim ulatory", + "ĠRich mond", + "Ġadvance ments", + "b les", + "x u", + "all ation", + "Ġint ral", + "iter pene", + "Con cerning", + "Ġbul ky", + "Ġá ¾±", + "comput ation", + "ĠAgar wal", + "C entral", + "X PS", + "Ġt alks", + "ĠT ap", + "im ilar", + "ĠN CI", + "Ġacc used", + "Ġtranscript omes", + "Ġprovision ing", + "ĠEt OH", + "g m", + "Ġt id", + "ĠP OC", + "ff man", + "ĠIn er", + "ĠU B", + "inc ubated", + "ĠAt rial", + "Ġfour teen", + "ĠAstr onomical", + "ĠMig uel", + "ĠK ov", + "Ġsc ipy", + "Ġtherm oplastic", + "ĠMan uel", + "ĠProm otion", + "ĠAccess ed", + "Ġterr itorial", + "in as", + "ĠM Ps", + "mon itoring", + "ĠSim ulating", + "Ġpan or", + "Ġrhe umatic", + "select in", + "ĠLap aroscopic", + "H LA", + "ĠY ale", + "sp read", + "ET S", + "Ġglyc ans", + "Ġimmig rant", + "D onald", + "ĠC ASE", + "ĠH II", + "gl omer", + "Ġïĥ İ", + "ĠExper iences", + "ĠViet namese", + "Hod gkin", + "o ader", + "he art", + "Ġrem edy", + "Ġfacilit ators", + "open hagen", + "d odec", + "ĠF riend", + "ĠTo uch", + "arm s", + "CR s", + "Ġultra high", + "ĠDri ver", + "GEM ENTS", + "ĠO u", + "Ġend ocarditis", + "Ġauto encoder", + "Ġ ich", + "Ġf etch", + "ur ian", + "ĠOR Fs", + "Ġperme abilized", + "ĠWi Fi", + "ĠLith uan", + "Struct ure", + "L n", + "h ouses", + "Ġo ught", + "ĠConcl uding", + "Ġann iversary", + "ĠCre ation", + "Ġblind ness", + "Ġpc DNA", + "ĠSus an", + "ĠBenjamin i", + "ĠSent ence", + "Ġs nd", + "Ġf ins", + "ph is", + "ĠMod ules", + "Ġneuro psychiatric", + "ĠPot assium", + "Ġsacrific e", + "Ġdysp nea", + "Ġdeliber ately", + "omeg aly", + "M edia", + "T emporal", + "Ġsh ark", + "SC AN", + "split ting", + "Ġmis use", + "Ġbirefring ence", + "ĠÖĴ âĨĴ", + "Ġp ier", + "Ġn urs", + "ĠS clerosis", + "ad hy", + "Ġund etermined", + "Ġcomple mentation", + "ĠAff ect", + "ĠHam ps", + "Ġg ob", + "ĠF ate", + "ĠH AL", + "ĠK iss", + "Ġmicro be", + "Ġcarbon aceous", + "Ġlip osome", + "ĠUs age", + "Ġquasipar ticles", + "Ġc asp", + "ĠN arrow", + "Ġout look", + "ĠCh ord", + "Ġclaim ing", + "Ġdiver ging", + "ĠBio informatics", + "ĠPsy chiatric", + "ĠMas ters", + "Ġll vm", + "ĠI QR", + "ph ases", + "ĠTh y", + "erg er", + "ĠDi pl", + "SF R", + "Ġcred ited", + "ĠTet ra", + "âĭ ¯", + "Ġamn iotic", + "ĠCharlot te", + "C ox", + "H ard", + "ar ticle", + "ĠD EA", + "ĠE clipse", + "ĠL MP", + "Ġim prison", + "ĠV arying", + "ES Cs", + "ĠTHE O", + "Ġnerv osa", + "Ġpreced es", + "Ġgy ro", + "ĠWOR DS", + "ĠDak ota", + "ut ory", + "ĠE mer", + "ad am", + "ĠN ah", + "ĠVir go", + "Set ting", + "P Q", + "å ®", + "er us", + "Ġc ep", + "Ġb d", + "di er", + "Ġim balanced", + "Ġtimes tep", + "ä n", + "ĠRab bit", + "Ġham sters", + "Ġmedull a", + "ĠChromat ography", + "IN PUT", + "Ġloss y", + "P seud", + "ĠP BL", + "ĠD omestic", + "ia u", + "anc ell", + "Ġmulti layers", + "Ġsubs idi", + "ĠUtil izing", + "t une", + "re hend", + "ar te", + "Ġb urs", + "ĠN HE", + "Ġclos eness", + "ĠCol our", + "ĠHom o", + "Equ ations", + "Ġsut ures", + "ac us", + "Ġknock ed", + "Ġsecret ary", + "Ġascer tained", + "Ġin patients", + "ir ts", + "Ġpl ut", + "ans son", + "ram i", + "Ġoste otomy", + "ĠPrim ers", + "ĠLeg islative", + "ĠCardi ology", + "Ġadmit ting", + "Ġexcav ation", + "ĠHedge hog", + "W G", + "f rozen", + "Ġl iber", + "ĠI CE", + "ch osen", + "ĠK ohn", + "St op", + "Ph il", + "phag ia", + "ĠB CA", + "Ġem pt", + "Ġz z", + "oper s", + "ĠSi xty", + "eck man", + "Ġtransf errin", + "Ġpenal ized", + "Be ing", + "Ġextr uded", + "Ġmini ature", + "Ġeditor ial", + "Ġinterconn ect", + "g ro", + "k v", + "ol en", + "ĠSY STEMS", + "ĠColon el", + "ĠMedi ated", + "ĠE MD", + "Ġkn ife", + "Ġcyt ogenetic", + "Ġdig itized", + "abin oids", + "arter ial", + "Ġdiar rhoea", + "b ag", + "Ġb uccal", + "st ay", + "ĠL AMP", + "ok o", + "ĠPol yt", + "mask ed", + "ĠTun able", + "Ġco agul", + "par as", + "Ġterm inating", + "IC Ag", + "ĠExcell ence", + "Ġregurg itation", + "DQU FD", + "J ack", + "Ġa pertures", + "ĠI p", + "ĠH CMV", + "ĠG om", + "Ġnucle ophilic", + "Ġparen teral", + "T IM", + "o ine", + "Ġn T", + "ĠS ense", + "ĠF ocal", + "ran ges", + "Ġhe pt", + "ĠPl at", + "Ġmy x", + "Ġcode book", + "Ex pl", + "ĠRho A", + "Ġrhin itis", + "ĠErr atum", + "Orient ed", + "W ell", + "d oping", + "Ġb up", + "ĠIm pedance", + "Ġsubstit utes", + "actor ily", + "Ġcollabor ations", + "ĠWay ne", + "Ġvow els", + "ĠSh adow", + "Ġphen ology", + "Ġconcur rency", + "h aving", + "ĠC ES", + "ĠF IN", + "ĠL oh", + "ox a", + "ĠAl N", + "ĠAl varez", + "ins tit", + "Ġgerm plasm", + "ĠBol iv", + "ĠR CP", + "ass ador", + "Ġes p", + "Ġphen otyping", + "Ġskip ping", + "ĠFract al", + "ĠPED OT", + "w ake", + "ĠF IT", + "ĠE SD", + "ĠAn tif", + "ubiqu itin", + "ĠAer ial", + "ĠProgn osis", + "ĠREL ATED", + "Ġstratig raphy", + "vat ron", + "ĠPROPERT IES", + "Ġ icon", + "is ers", + "Ġw al", + "Ġst amp", + "ĠOptim um", + "Ġolig omeric", + "Ġinn erv", + "Y A", + "Ab cam", + "Ġv ials", + "ĠG rig", + "Ġun aware", + "Ġoper a", + "ĠWar ner", + "Ġproton ated", + "ĠDR G", + "Ġtro ubles", + "Ġproposition al", + "ĠAfghan istan", + "ĠHamps hire", + "G d", + "l ung", + "Ġa viation", + "Ġap artment", + "Ġinf usions", + "Ġbro ilers", + "ĠDis ability", + "ĠRob ots", + "Ġdeb ugging", + "Ġì Ŀ", + "Wil son", + "upro fen", + "obarb ital", + "J B", + "is ance", + "iti zer", + "MI S", + "ĠAR F", + "Ġprost heses", + "Ġdichlor omethane", + "m Cherry", + "ĠS SS", + "ĠL PA", + "SC F", + "att ract", + "Ġcalibr ations", + "Ġfibr il", + "Ġhapl oid", + "usal em", + "ĠN ut", + "Ġde ut", + "ch ronic", + "NA P", + "ĠCytok ines", + "rage en", + "ĠC ategories", + "rain s", + "Ġsumm ands", + "Ġprolif erate", + "ryl ov", + "Ġple asure", + "Ġdens it", + "ĠSUR VE", + "H IP", + "h all", + "ĠF US", + "Ġwas ting", + "ER Y", + "Ġstat ins", + "Ġeast ward", + "some times", + "Ġwrap ping", + "ĠTW O", + "v ine", + "Ġs acchar", + "Ġam ateur", + "Ġà Ľ", + "Ġmy ster", + "ĠMy o", + "Ġrh abd", + "ĠProte ase", + "Ġchol era", + "ĠG ov", + "ĠG CC", + "Ġcl ays", + "trans mission", + "ĠHol lywood", + "Ġxen ob", + "FLO AT", + "Ġas cent", + "Ġsh arks", + "Ġinter feres", + "ĠForm er", + "ĠHart mann", + "s ha", + "ĠS ave", + "Ġpar ks", + "ĠV enn", + "Ġun ions", + "Ġdisc our", + "Ġsuper lattices", + "Ġcou pler", + "protein s", + "ĠStation ary", + "ĠEther net", + "ĠFré chet", + "Ġk ines", + "Ġj azz", + "As n", + "Ġextension al", + "Ġtel omeres", + "Ġpermit ting", + "Ġexha usted", + "ĠSph ing", + "T urn", + "m ind", + "Ġs f", + "ĠH ak", + "ran olol", + "port ation", + "Cons istent", + "Ġventi lated", + "ĠDIST RIB", + "Ġt elling", + "Ġman nose", + "ÃŃ az", + "Ġbor ne", + "Ġintens ification", + "Ġenjoy ed", + "ĠBrun o", + "ĠSatur day", + "Ġc ocycle", + "it ate", + "Ġg olf", + "appro ved", + "ĠNik ol", + "it ri", + "ĠS entiment", + "Ġg low", + "Ġg yp", + "ĠP CT", + "ab er", + "ĠW is", + "por um", + "Ġhy phae", + "fe as", + "ĠTra its", + "ĠConfl icts", + "degrad ing", + "R aman", + "ph armac", + "Ġimmun ocyt", + "ĠBl ake", + "Ġpseud oc", + "ĠCharacter isation", + "ĠGalile o", + "E nabl", + "J y", + "Ġcl av", + "ĠÏ ³", + "Ġcommun icated", + "eu tical", + "Ġnanot echnology", + "ĠHass an", + "ĠT ec", + "Ġh anging", + "ĠB SD", + "ĠCont our", + "Ġfrag ility", + "Ġdisrup tions", + "Ġfinit eness", + "ĠPhilipp ine", + "n icity", + "Ù ĩ", + "ĠC rim", + "ĠC NF", + "ĠI SI", + "ad apter", + "ĠU CP", + "Ġtext ured", + "AA V", + "ket o", + "N p", + "c ounting", + "h ynchus", + "Ġpro sec", + "ĠAn not", + "ĠHar bor", + "deg rees", + "ak ar", + "ĠV ik", + "bf d", + "Ġdri p", + "ĠCauc as", + "Ġtren ch", + "Ġwe ed", + "Ġdist ractor", + "gen etic", + "spec ifically", + "ulf ite", + "ĠCons istently", + "Ġbreak fast", + "Ġbul let", + "Ġleg isl", + "ĠTra umatic", + "Ġcollect ors", + "ĠBul let", + "ĠMY B", + "ĠP ink", + "vers ive", + "ĠAt tem", + "Ġcult urally", + "B ell", + "und ef", + "vi i", + "Ġhist ocompatibility", + "let cher", + "ĠSte f", + "A mp", + "ĠR id", + "ĠE ucl", + "Ġdec ryption", + "ĠSp encer", + "ĠBit coin", + "w ic", + "Ġcom plicate", + "ĠPro posal", + "ĠÄ Ī", + "avirus es", + "ĠF ay", + "ĠR d", + "ĠG ale", + "ĠMetast asis", + "ĠImprove ments", + " ©", + "Ġpoly ester", + "Ġstrat ospheric", + "ĠSA H", + "Ġamph ip", + "ĠA FP", + "ĠH air", + "ĠE PI", + "ĠUl trast", + "Ġâĭ ¯", + "Ġga pless", + "H am", + "et to", + "Ġth reonine", + "ĠE CO", + "Ġi a", + "Ġund ist", + "Ġradi ology", + "Ġsuper lattice", + "ibr aries", + "Ġturb id", + "ĠPot entials", + "ĠPip eline", + "Ġwarf arin", + "W ISE", + "ĠL id", + "Ġrec urring", + "ĠMon o", + "ĠGover n", + "ĠAware ness", + "ol ab", + "if lora", + "str is", + "IN DEX", + "ĠDem entia", + "Do es", + "w right", + "Í ī", + "Ġs b", + "ĠD OM", + "ĠH BsAg", + "cl inic", + "ĠEx ped", + "Ġprote as", + "Ġster ilization", + "ĠBan erjee", + "ĠPerson nel", + "âĮ ĭ", + "oneph ritis", + "om ite", + "ĠC CF", + "os iti", + "ĠE ucalyptus", + "ĠIs otope", + "col i", + "poss ibility", + "Ġstr ontium", + "Ġra ref", + "ĠInter stellar", + "kin in", + "yleth anol", + "J T", + "n orth", + "Ġc ensored", + "is tive", + "Ġno ticing", + "Ġship ping", + "Em bed", + "Obs erv", + "Ġze olites", + "ub it", + "Ġfl aps", + "Ġdr ifts", + "Ġtherap ist", + "Ġpoll ination", + "ali platin", + "Joh nson", + "Ġimperf ections", + "N Y", + "Ġth alamic", + "oc arb", + "oz otocin", + "Ġtet ramer", + "Pl as", + "Ġmultic hannel", + "ĠIns ight", + "op ods", + "ĠN acional", + "Ġim atinib", + "act ual", + "ĠX OR", + "Ġbl ight", + "ĠLe ading", + "ames e", + "ĠAm plitude", + "ĠMon itor", + "ĠNeu rological", + "propag ating", + "Ġp addle", + "ĠHar vest", + "Ġod ont", + "BU F", + "Ġtac tics", + "ĠAnis otropy", + "ad ip", + "ĠAl pine", + "Ġfe els", + "Ġmed ieval", + "Ġel ucidation", + "Ġheter otrophic", + "Ġrelax ing", + "Ġhapp iness", + "ĠCyt otoxicity", + "ĠRAN KL", + "Walk er", + "m ig", + "ĠS SL", + "ĠS epsis", + "ĠG es", + "Ġhydro chloric", + "Ġclar ification", + "Ġdispar ate", + "t ested", + "Ġdat ap", + "Ġnovel s", + "ĠMicro c", + "á l", + "ĠAR C", + "ĠYang tze", + "etom idine", + "ĠMat rigel", + "ih ilation", + "ĠcDNA s", + "Ġprost at", + "ĠRail road", + "UB LE", + "ĠPART IC", + "ĠS ax", + "Ġins ecurity", + "Ġcr ushed", + "Ġhal ves", + "gi ant", + "ĠCro atia", + "icycl o", + "ĠUne xpected", + "Ġlon eliness", + "an u", + "Ġch ampions", + "ub erculosis", + "Ġequ i", + "Ġacc reted", + "Ġinv ading", + "Ġaff erents", + "Ġaltern ation", + "Ġkin et", + "ĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ ĠĠĠĠĠĠĠĠĠĠĠĠ", + "ĠMAG NET", + "ĠFIF A", + "z adeh", + "ip henyl", + "ĠK ro", + "ĠEval uate", + "illi ant", + "cur vature", + "ĠPier ce", + "b etter", + "n os", + "à ¥", + "ĠK CN", + "ĠSt rand", + "ca emic", + "ĠHo echst", + "ĠEX T", + "ĠLL VM", + "B Z", + "t gt", + "on dialdehyde", + "ĠE vid", + "ĠG ul", + "Ġmulti plications", + "Ġaut h", + "ĠAustr al", + "Ġstay ing", + "ĠGlut amate", + "Ġst ray", + "ĠI SA", + "Ġlow land", + "Ġparallel s", + "Ġattrac tiveness", + "Ġelectrosp inning", + "Ġportray ed", + "ospec ific", + "f olate", + "Ġcoe ff", + "ĠEst rogen", + "tum our", + "Ġhystere ctomy", + "Ġin ositol", + "ĠB az", + "ist ein", + "Ġcruc ially", + "Ġdin oflag", + "ÍĶ ÍĴ", + "ĠDrag on", + "ĠS por", + "ĠM ater", + "ĠH ero", + "plic ing", + "ĠAN T", + "ĠForm ic", + "Que ue", + "ocarcin omas", + "U PS", + "ĠP c", + "enc oders", + "Ġinv aded", + "ĠPh ases", + "Ġpost mortem", + "Ġslow s", + "ĠMc L", + "ĠVer ma", + "ĠVi ability", + "Ġcompens ating", + "Ġclamp ed", + "j m", + "ĠR iv", + "up on", + "ĠDick inson", + "initi ated", + "Ġs ider", + "ĠS elen", + "ĠA ka", + "idel berg", + "Ġqual ifying", + "Ġenfor cing", + "otroph s", + "ĠSNA P", + "Ġr ust", + "imb urs", + "Ġimmunocomp romised", + "ĠFlem ing", + "Ġl izards", + "di alysis", + "ĠUn ivariate", + "Ġgas oline", + "Ġten ure", + "Ġsustain ing", + "Ġmot one", + "b ay", + "w ani", + "ore station", + "ĠX II", + "Ġradi ofrequency", + "ĠGu ided", + "Ind ividual", + "ĠSpect rometer", + "ĠGo ing", + "ĠMart ins", + "Ap proxim", + "am ak", + "ĠâĪ ı", + "ĠO mn", + "Ġout patients", + "Ġhyper bol", + "ĠPer ceptual", + "ĠBur ke", + "Bol tzmann", + "ĠM d", + "Ġpa w", + "ĠCat hedral", + "Ġhyal uron", + "Ġbrach ial", + "Ġaflat oxin", + "im o", + "Ġen rol", + "Ġdet onation", + "Ġover ly", + "the st", + "Ġsecond ly", + "ĠSch iz", + "ĠIGF BP", + "atech in", + "Ġs aves", + "ti ers", + "ĠB ates", + "Ġall iance", + "Ġatt ri", + "Ġast ro", + "ĠPath ological", + "Ġgamb iae", + "P ark", + "id able", + "ĠN il", + "ĠJ as", + "Ġneed ing", + "me ier", + "Ġferro ptosis", + "ĠGuid ance", + "A Z", + "i ol", + "Ġac knowledg", + "ex ual", + "Ġmen opause", + "Ġadj unct", + "cap ture", + "ĠDep uty", + "Ġb ial", + "if a", + "ĠCh itosan", + "ĠTop ics", + "ĠPlas mid", + "calc ulations", + "g ive", + "respond ers", + "ull a", + "ĠMore no", + "Ġcomment ary", + "ĠMah m", + "ï£ ±", + "on acci", + "ĠC ould", + "ĠTR P", + "second s", + "Graph Pad", + "L ittle", + "he y", + "Ġal ike", + "ĠDi as", + "aro o", + "ĠÄ ±", + "Ġtax es", + "phen anth", + "ĠChe ung", + "ĠPi et", + "D f", + "G U", + "m ectin", + "z ee", + "Ġd λ", + "Ġsynt heses", + "Ġá Ī", + "Sim ulation", + "ĠEle ven", + "w orms", + "lymph ocyte", + "Ġhaemorrh age", + "ĠO wn", + "ĠK ant", + "Ġover se", + "Ġide ation", + "ĠHar per", + "Acknowledg ments", + "v ili", + "yn a", + "ĠRec urrence", + "oz a", + "Ġhence forth", + "ze es", + "Ġquas ic", + "Ġchor oidal", + "Ġantim alarial", + "Ġcoars ening", + "D eb", + "di am", + "ĠWe ights", + "Ġbu ying", + "Ġmess aging", + "Fe bruary", + "Ext ended", + "ĠRoss i", + "Ġmist aken", + "Ġut ero", + "j as", + "ic itis", + "ĠT idal", + "Ġph aryngeal", + "cl ick", + "Ġmy o", + "kn ock", + "Ġpromin ence", + "Ġamphi philic", + "c orn", + "Ġon board", + "ĠD ud", + "ĠW oman", + "ĠOut break", + "Ġprefer ably", + "Ġsket ches", + "S at", + "f ixing", + "ĠM ey", + "ĠLet ters", + "IT IES", + "ĠSD P", + "ĠLNC aP", + "D X", + "F luor", + "R v", + "S ect", + "ĠI ons", + "Ġtrac hom", + "Ġult rastructure", + "qv ist", + "rop he", + "Ġrece ipt", + "ĠQu int", + "Ġsw apping", + "amin idase", + "Ġarch ival", + "ĠCre ating", + "ĠBart on", + "diagn osed", + "at ological", + "ol ph", + "ĠP FA", + "ĠL AP", + "Ġun physical", + "eq n", + "Ġquar tiles", + "olytic a", + "ĠFre ed", + "Ġventil ator", + "Ġkary otype", + "S ta", + "s till", + "ĠT ate", + "ur ability", + "ĠG ron", + "Ġtr imer", + "IP A", + "adec a", + "ĠImplement ing", + "s ity", + "it r", + "Ġb om", + "Ġnon relativistic", + "Ġmic elle", + "ĠAd minist", + "Ġelectro lysis", + "har mon", + "OLOG ICAL", + "L iter", + "ĠG UI", + "ĠQ L", + "mon ths", + "Ġsuper flu", + "cut s", + "Ġelic its", + "Ġmultiplex ed", + "overl ap", + "Ġcada ver", + "Ġo u", + "ĠS heng", + "ere a", + "ĠN BC", + "Ġdet er", + "ty rosine", + "ĠPar ts", + "Ġess ay", + "k as", + "it ted", + "ĠP ZT", + "ess ler", + "Ġsim ulators", + "Ġradi ating", + "cut ting", + "ĠCalc ulating", + "TH ER", + "ĠROC K", + "commun ic", + "Ġbon us", + "ĠC PA", + "ĠP UR", + "ult on", + "ĠZ hi", + "Ġcal oric", + "Ġinterp olate", + "ĠSec retion", + "Ġneuro cognitive", + "Ġgad olinium", + "f requencies", + "ĠT ract", + "Ġminim ax", + "ĠBro ck", + "ryp sin", + "ĠReson ant", + "ĠACKNOWLED GEMENTS", + "D om", + "Ġhol otype", + "Spec ial", + "Ġimmunore active", + "ARN ING", + "Pan el", + "ĠJohann es", + "R FP", + "z zi", + "ĠP omer", + "Ġtrans ects", + "Ġpo ured", + "ED s", + "ĠCirc um", + "Ġabnorm ally", + "ĠPun j", + "G ol", + "H op", + "H ex", + "I LE", + "Ġsour ced", + "ocl ase", + "prot obuf", + "Ġfro gs", + "ĠOt tawa", + "Ġbioge ochemical", + "Ġlenti virus", + "Y oung", + "ĠI PS", + "ass en", + "Ġun restricted", + "Ġmat plotlib", + "Ġchlor amphenicol", + "ĠContext ual", + "ĠHawai ian", + "Leg end", + "S parse", + "b ore", + "g aussian", + "u ke", + "ĠâĢ °", + "ret est", + "SS E", + "pre ting", + "ĠPan ama", + "ĠBroad band", + "conjug ate", + "B ytes", + "G SH", + "U ns", + "r ina", + "Ġd rained", + "Ġsc ap", + "Ġinves ted", + "Ġsatisf actorily", + "Ġherbiv ores", + "Ġarachid onic", + "ymet rix", + "Ġn ect", + "Ġcon ges", + "ĠM err", + "ĠM ai", + "Ch ain", + "Ġretrie ving", + "Col lection", + "ĠMT X", + "ĠFernand o", + "h g", + "ĠR ams", + "th resh", + "aps ules", + "Ġcond uit", + "sw ap", + "Ġblow ing", + "ĠNy quist", + "Ġuncons cious", + "ĠDIFFE RENT", + "T echn", + "h iz", + "î Ĥ", + "Ġd ξ", + "ĠSt o", + "ĠFlav on", + "Dav id", + "Ġfiltr ate", + "l ith", + "ĠW ool", + "ĠK not", + "Ġhal ide", + "Ġbio assay", + "ĠGold berg", + "ĠTrich oderma", + "Ġintras pecific", + "c rystall", + "ĠR end", + "our g", + "Ġunder take", + "ĠEn um", + "inf ect", + "Ġmid gut", + "att ack", + "ĠCirc le", + "Ġplei otropic", + "es cent", + "ĠF ri", + "ph ilis", + "ast ings", + "Ġbi ogas", + "ĠÄ ľ", + "Ġaccomp any", + "Ġroll ed", + "Ġchir p", + "Ġsomat ostatin", + "vark appa", + "S cal", + "Ġd row", + "rom ed", + "ĠL up", + "ĠL uminosity", + "ĠN ig", + "fer romagnetic", + "ĠTo y", + "Ġcann abinoid", + "ĠH OX", + "ie le", + "ĠCT X", + "Ġhyd rop", + "Ġfavor ite", + "Ġstret ches", + "eval uated", + "ogroup s", + "ac al", + "ol lo", + "Ġg enders", + "ĠG raft", + "Ġinc idences", + "Ġreplac ements", + "ĠTR UNC", + "CR F", + "Ġequal ization", + "ĠRen ew", + "Ġple thora", + "ĠEnc oder", + "M it", + "Ġc aches", + "or ate", + "end ors", + "ĠCa ution", + "ĠAb el", + "comp ression", + "ĠLars en", + "ĠElim ination", + "Ġt ester", + "Ġn inth", + "ĠL ö", + "Ġsp iders", + "Ġpo em", + "Ġeduc ators", + "ĠEnh ances", + "dest ructive", + "Four ier", + "Ġseism icity", + "ĠYun nan", + "Riemann ian", + "W ID", + "v ular", + "ĠB order", + "Ġcomb in", + "sing let", + "ĠEd dington", + "ĠTem plate", + "ĠPA X", + "Ġbasal ts", + "En h", + "Ġassist ants", + "ĠCasc ade", + "Ġin breeding", + "ch ini", + "Ġup graded", + "ĠTrans it", + "sur vival", + "Ġinject or", + "ĠPas cal", + "DEV ICE", + "Ġf ost", + "ĠK and", + "Ġext ragalactic", + "epend ently", + "Ġexc ite", + "Ġfulf il", + "Ġrip arian", + "Ġuplo aded", + "a un", + "l od", + "s aving", + "ĠH ib", + "ĠE ra", + "ob ese", + "Ġu i", + "Ġspect rally", + "ke V", + "xx x", + "ĠOt to", + "Ġé tale", + "L AT", + "d ermal", + "di az", + "ĠPl i", + "Ġleg ume", + "Ġinsp ect", + "Ġthym ic", + "ĠHorm one", + "á Ģ", + "in ot", + "ĠS hib", + "ĠB CC", + "ĠV ital", + "Ġprof its", + "ĠFed erated", + "Ġflip ped", + "Ġpropri etary", + "incor porated", + "Ġbact eremia", + "Ġáŀ ĩ", + "f ins", + "ä ½", + "es ia", + "ĠH ollow", + "ge ons", + "Ġtre halose", + "ER O", + "oster ol", + "om us", + "ĠC rystall", + "Ġcur ation", + "Ġmagn on", + "ĠAm end", + "Ġhar b", + "Ġneutral ity", + "ĠDel phi", + "Ġnons ense", + "ĠHome ostasis", + "Ġexpendit ures", + "Sequ ential", + "imod ular", + "Ġz enith", + "ĠMor an", + "Ġbootstrap ping", + "i omy", + "l actic", + "it ure", + "Ġn at", + "Ġg ab", + "Ġch at", + "reg ional", + "Ġcr ashes", + "ĠAF B", + "Ġcrow ded", + "Ġtwe et", + "engine ered", + "ĠCharg ed", + "S che", + "IT IONS", + "ĠCor al", + "ĠEl i", + "Ġinver ting", + "Ġped ag", + "ĠSand ers", + "Mean while", + "ĠGriff iths", + "P SCs", + "ti ze", + "ĠM ail", + "Ġund ec", + "Ġher mitian", + "ĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ ĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ", + "ĠExpl os", + "Ġwest ward", + "ĠConf irm", + "B egin", + "Ġfactor ies", + "ĠPR L", + "she ar", + "Head er", + "ĠFLAG S", + "an omal", + "ĠQ W", + "ĠÌ ħ", + "oin ositi", + "Ġmamm ography", + "Ġdeposition al", + "EX P", + "resid ue", + "Ġunsatisf actory", + "A β", + "M UX", + "Ġst aged", + "ĠM MT", + "ĠK us", + "ll o", + "Ġtrain er", + "add en", + "Ġpin ch", + "WA RE", + "Ġcab inet", + "C SP", + "ec um", + "ot eric", + "ĠH av", + "Ġres ume", + "Ġnetwork ed", + "sh are", + "ĠCol le", + "Ġchem otactic", + "ĠGly c", + "olk it", + "Ġbot ulinum", + "ĠNeighbor hood", + "m V", + "ĠH Q", + "ef aciens", + "get t", + "Ġge ost", + "ĠCD W", + "ĠÌ §", + "Ġflo ors", + "represent ing", + "odi ode", + "ĠInst ance", + "Ġmonod is", + "d rying", + "re asing", + "ig i", + "Ġg out", + "ĠI EC", + "Ġfl ush", + "Ġtra ded", + "Re view", + "ĠïĤ ¢", + "Ġà ¤", + "Ġabbrevi ations", + "otherap ies", + "Ġindeterm inate", + "Ġglutar aldehyde", + "Ġattri tion", + "j ump", + "in de", + "ĠG ri", + "arc tion", + "TRA IN", + "Ġescap ed", + "at ement", + "ĠP am", + "ĠG AM", + "pro ductive", + "ĠAmeric as", + "agen esis", + "ĠMi xtures", + "ĠHo oft", + "ĠWind ow", + "Ġnod ular", + "Ġech in", + "D OF", + "ĠD DT", + "elect rical", + "ĠDec entralized", + "Ġcontrad ict", + "F rench", + "Ġa ustr", + "ĠA PD", + "ĠD IM", + "ĠSt en", + "ron omic", + "ĠPolym orphism", + "Ġc occ", + "it ings", + "Ġsub critical", + "ĠUn iqueness", + "OP EN", + "rot oxicity", + "Gen Bank", + "atab ases", + "N ets", + "u istic", + "y ric", + "ĠS ID", + "Ġco oked", + "ĠJ udge", + "Ġparameter izations", + "Ġenum erated", + "ĠAst hma", + "De velop", + "Ġc ake", + "ĠA ges", + "ven ile", + "Ġfl or", + "Ġcould n", + "det ach", + "Ġpip ette", + "ĠInst ant", + "Ġtent atively", + "ĠINT EGR", + "H Q", + "M apping", + "c q", + "å Ī", + "SU M", + "frac tions", + "ĠCl aud", + "Form ula", + "Ax is", + "ĠBil ly", + "ĠMeth ane", + "ĠI GM", + "c annot", + "Ø ³", + "Ġc iting", + "ĠD ynam", + "Ġle tt", + "eg ler", + "ĠPhysic ians", + "x FF", + "Ġo yster", + "ĠT OC", + "Ġsub arachnoid", + "ĠCO M", + "IT ER", + "Ġbenz odiazep", + "Ġuncom plicated", + "till o", + "Car bon", + "at em", + "Ġs el", + "ing o", + "IV ITY", + "Ġca vern", + "Ġspac elike", + "ĠColl isions", + "stra int", + "Ġmyc obacterial", + "Ġtrachom atis", + "A i", + "m f", + "ĠT ric", + "tic o", + "ĠE lection", + "ĠK DM", + "ĠEx osomes", + "af luor", + "Ġformal ized", + "ĠEL F", + "v phantom", + "ĠS ME", + "ich uan", + "ĠV Ms", + "Ġro stral", + "of er", + "ram anian", + "inter cal", + "Mer ck", + "ĠFerg uson", + "H U", + "l j", + "Ġr ack", + "Ġmicro graph", + "CT S", + "Ġpass ively", + "ĠMass es", + "rang ians", + "ĠAD M", + "ĠProvid ed", + "ĠVeter ans", + "s ound", + "ge x", + "ĠSp iral", + "Ġfoss a", + "Ġthermod ynamically", + "ĠS taining", + "Ġk ar", + "ef lon", + "ĠBr uns", + "VA E", + "olytic us", + "Ġintran asal", + "ĠProsp ects", + "at hers", + "Ġnumber ing", + "ĠRe placement", + "ĠNon commutative", + "quis itions", + "ĠSIM D", + "ĠArter ial", + "ĠH GF", + "ĠG PP", + "Ġflu vial", + "ner i", + "ĠComp ressed", + "VID IA", + "Ġshock ed", + "d ys", + "in variance", + "en stein", + "ĠS CM", + "ĠD od", + "Ġsh o", + "Ch lor", + "du ino", + "Ġurg ently", + "s oc", + "et ching", + "Ġdiff ractive", + "ĠZ F", + "Ġhyper planes", + "Ġmy ri", + "Ġfer romagnetism", + "fil ament", + "Ġjustif ies", + "f ault", + "ĠH TS", + "ĠE PC", + "to o", + "ĠTR AP", + "i ón", + "r v", + "ĠB PD", + "ĠN od", + "pos it", + "ĠCon vers", + "Ġzero es", + "ĠGl en", + "ĠDist urb", + "Ġtable au", + "Ġpseud ot", + "ĠColl ider", + "Ġadsorb ents", + "ĠGro ve", + "Ġking dom", + "E st", + "X s", + "c zyk", + "ĠW ille", + "ĠV OL", + "sc ar", + "ĠAd ler", + "ĠOr chestra", + "Ġspars ely", + "glycos ylation", + "L ac", + "o tions", + "ĠI le", + "Ġbe acon", + "ĠR n", + "ull ah", + "Ġtim elike", + "ĠFore sts", + "Ġupl oad", + "j it", + "ĠE DM", + "Ġtrans plants", + "mark er", + "ĠBre eding", + "ÎĶ ÎĶ", + "Ġfavor ably", + "ĠTransform ations", + "abel ed", + "ĠPoli tics", + "epis ode", + "Ġf ut", + "Ġd ithi", + "ĠM w", + "Ġtrans piration", + "Ġun limited", + "ĠAn tiv", + "PP V", + "Ġnom ogram", + "Ġinvent ed", + "ĠSched ule", + "all ows", + "Ġtrans vers", + "Ġwork piece", + "black square", + "call back", + "ĠAth letic", + "h ans", + "p oles", + "Ġe avesdrop", + "ĠC one", + "oc lim", + "ĠG host", + "iter ations", + "Ġweak en", + "Ġalkal oid", + "Ġveter ans", + "Ġprostat ectomy", + "Ġb og", + "ĠC ed", + "ĠF ever", + "yl an", + "arch ive", + "Ġattack ers", + "M aking", + "b ane", + "ĠP ull", + "ĠF LO", + "Ġco aches", + "ĠV SM", + "ok h", + "ĠSp o", + "amil ial", + "princ iple", + "Ġaggress iveness", + "Ġgard ens", + "S IG", + "Ġm bar", + ".. ...", + "Ġoptim izes", + "Ġmorph ologic", + "han i", + "Ġgerm anium", + "Enabl ed", + "w b", + "Ġfor amen", + "ĠS PA", + "Ġmagn ified", + "ĠSl ater", + "ĠSy rian", + "Ġt ert", + "Ġtra ditions", + "Ġoff ensive", + "Ġhyd rology", + "erge tics", + "Ph ot", + "Ġperovsk ites", + "Ġwaven umbers", + "Ġosteocl asts", + "imed ean", + "ĠBasket ball", + "benzodi ox", + "ĠTRUNC ATED", + "Ġb ishop", + "ĠS gr", + "ĠS atisfaction", + "agn ostic", + "num eric", + "Ġnorm als", + "Ġhum or", + "Ġcontin ents", + "NAT ION", + "Lag rangian", + "Ġkne es", + "ĠI DE", + "ad as", + "ad ia", + "ĠO U", + "ond s", + "ĠCh aud", + "Ġsl icing", + "ĠAc tor", + "Al t", + "Ġbroad casts", + "osa urs", + "Ġpick le", + "Ġunf amiliar", + "all us", + "Ġhas hing", + "inc idence", + "Ġmetabol ized", + "Ġhomogeneous ly", + "ĠFal con", + "Ġ Ñģ", + "ĠC ere", + "ĠC LA", + "ĠP aste", + "ĠF ry", + "ĠD re", + "ad ult", + "Ġdisc ounted", + "sens itized", + "ercul ous", + "ĠP ixel", + "ĠB ram", + "all o", + "ip ative", + "ip ality", + "ĠSt rict", + "ĠTr inity", + "ĠClass ifiers", + "ĠBas el", + "ĠCur cumin", + "ĠLU MO", + "Ġmediast inal", + "ĠF FA", + "Ġpl enty", + "pr ised", + "Ġpr inter", + "Ġcalc are", + "ins n", + "ont ology", + "Ġground ing", + "ĠAL DH", + "STR ING", + "ĠFem ales", + "ĠFocus ing", + "assess ment", + "ĠBlu etooth", + "ëĬ Ķ", + "Ġe go", + "ĠD AC", + "ond ing", + "rand a", + "ĠLud wig", + "Ġanteced ent", + "ĠErn st", + "d X", + "od eling", + "âĢ ĭ", + "In ser", + "ogn ormal", + "ĠTe vatron", + "Ġcovari ances", + "rig ing", + "ĠMg SO", + "carbon itrile", + "ĠLore n", + "Ġpolyt opes", + "ĠParent al", + "Ġun healthy", + "ither to", + "ĠMo tif", + "Data Type", + "ĠMI PS", + "ĠPhosph orus", + "Mo O", + "ĠPerturb ations", + "Ġaph ids", + "Ġanhyd ride", + "id eration", + "ĠM its", + "gra vit", + "Ġdest inations", + "Com mun", + "Ġtetra hedron", + "im plicit", + "Ġass ort", + "ĠSub t", + "ĠAcet yl", + "ec ium", + "ĠN ie", + "Ġoper and", + "ĠSc her", + "az oles", + "tle ment", + "ĠBlock ing", + "Ġbottlen ecks", + "ĠOccup ational", + "H AS", + "T eller", + "Ġv ague", + "est ing", + "SN E", + "Ġphoto ionization", + "Ġcompl aint", + "us pid", + "ow ler", + "Ġend ocytic", + "Ġfl ocks", + "eps in", + "col ors", + "otop es", + "ĠDep letion", + "ELL AR", + "ar med", + "ĠT IR", + "Ġbul lying", + "Ġâİ §", + "ospor idium", + "M r", + "ĠC ic", + "og al", + "Ġsection ed", + "Ch apter", + "ĠCont ents", + "ĠPath s", + "ĠExpl ain", + "comput ing", + "Ġshr ub", + "ĠMalays ian", + "B eta", + "M ad", + "R os", + "Ġe yel", + "ĠA CF", + "ĠM m", + "text ure", + "Ġinterpret ability", + "ĠTop ic", + "Ġbad ly", + "ĠmA h", + "E g", + "R Q", + "p ins", + "et c", + "ĠV ogel", + "Ġhyp oc", + "Ġrun away", + "Ġperson ally", + "Ġbind ers", + "sens ory", + "ĠIP v", + "rank ed", + "Ġfibr ations", + "Ġstraw berry", + "arot omy", + "F LI", + "r ator", + "od ys", + "ir an", + "ĠB ead", + "ĠD AM", + "âĪ ĥ", + "Ġill usion", + "pid ium", + "Pl ace", + "Reg ion", + "Ġalloc ations", + "Ġoh mic", + "Ġn f", + "im ino", + "ĠB ris", + "iti zing", + "pro per", + "sub group", + "Ġsal ience", + "rang ement", + "ĠMean ing", + "Ġbarc ode", + "Ġneurop eptide", + "Ġendos perm", + "im ab", + "Ġnan od", + "ĠMet z", + "Ġcoll ocation", + "ĠInf ected", + "Ġpack aged", + "ĠAD A", + "ĠBar th", + "ĠCN C", + "Ġcasc aded", + "ĠStock holm", + "it as", + "ĠM MR", + "ĠD rought", + "Ġper missible", + "Ġsc iatic", + "Ġfr inges", + "Ġexec utable", + "Ġstem ness", + "ĠEnd oscopic", + "apor in", + "T OP", + "e B", + "t ur", + "ĠSt ages", + "anc hes", + "Ġnon perturbative", + "Ġmar itime", + "Ġcovers lips", + "Ġlag oon", + "Experim ents", + "Ġcodew ords", + "Enc oder", + "d as", + "p rac", + "Ġp addy", + "Ġd raining", + "Ġk ids", + "Ġen emies", + "Ġmo tile", + "Ġsl ack", + "be es", + "ĠSup pl", + "ĠBar ber", + "ĠSP H", + "Ġcrystall ite", + "fit ted", + "cycl opent", + "ĠRMS D", + "Ġpark inson", + "Ġuncor rected", + "ĠSynt ax", + "Ġmultin omial", + "ĠIncor porating", + "akrish nan", + "J L", + "N ESS", + "m im", + "ĠT ET", + "ĠP orph", + "ĠSch we", + "Ġcatalog s", + "ĠAuthentic ation", + "B ro", + "ug ar", + "ĠAm pl", + "Ġsap iens", + "ĠGAN s", + "Ġnecessit ates", + "t g", + "ed al", + "ĠR ear", + "op eptidase", + "ĠIn formed", + "Ġtail or", + "ĠNN LO", + "Ġhemod ynamics", + "S y", + "d ating", + "ac hers", + "ĠT odd", + "ĠM ario", + "ĠU GT", + "ĠVal ent", + "Ġstream lines", + "Ġwar rants", + "Ġre w", + "ĠM ud", + "ĠG K", + "Ġco ke", + "ĠU ran", + "Ġgro oves", + "ron ate", + "ĠRad ius", + "ĠSu ite", + "atum oral", + "Work space", + "ĠSynerg istic", + "ĠAtheros clerosis", + "ma z", + "arg max", + "sh ield", + "ont in", + "Ġlist ener", + "ocyt oma", + "ĠGra v", + "ĠJer usalem", + "pyrrol idin", + "ĠSpr ings", + "Ġseaf loor", + "Ġd ips", + "ist ani", + "ob is", + "Ġphot odynamic", + "AD O", + "Ġion isation", + "Ġbar n", + "igene tics", + "Ġeconom ies", + "ĠGlac ier", + "Ġ ç", + "im es", + "ĠS asaki", + "ch io", + "Ġass isting", + "ost in", + "Ġind iff", + "ĠSh ot", + "ĠNe uron", + "CD D", + "ĠCON ST", + "ĠBS s", + "t as", + "ass ociation", + "par g", + "Ġes cal", + "ex ercise", + "ĠAd ela", + "Ġmy ogenic", + "ĠNO AA", + "ycl o", + "l inal", + "ĠH ut", + "Ġintro ductory", + "Ġheter ochromatin", + "Ġchem oresistance", + "Ġsimpl ifications", + "pyrid in", + "Ġamyloid osis", + "Ġnanof iber", + "ĠSut ton", + "ĠResil ience", + "P arent", + "ĠL MS", + "Ġle ts", + "ĠEl derly", + "Ġirre vers", + "she ets", + "Eff ects", + "Ġellips es", + "ĠPhilos ophy", + "Ġphot ographic", + "HE AD", + "Ġrevers ibility", + "Ġfed erated", + "ĠPhosph oserine", + "estim ation", + "ĠHum ph", + "J son", + "Ġf ills", + "Ġv erm", + "ĠSe if", + "with standing", + "ĠYam ada", + "er ia", + "ĠF LA", + "ĠV ick", + "to id", + "ann ier", + "Ġcancer ous", + "PR INT", + "ĠMechan istic", + "Ġdust y", + "ĠApp end", + "y cin", + "Ġa zo", + "Ġs izing", + "Ġc rayfish", + "av is", + "ĠAd vent", + "ĠCommun ist", + "ĠIM U", + "pix els", + "H all", + "p ast", + "ĠR ous", + "ression al", + "air d", + "ĠAD D", + "Ġsummar izing", + "Ġelect rol", + "St ation", + "ĠLy α", + "ĠTM EM", + "Ġpeptid ase", + "D ual", + "g it", + "ĠB OD", + "ĠR ham", + "ĠK ak", + "Ġread iness", + "ĠComp are", + "ĠRam os", + "sign ificantly", + "Ġhair y", + "Ġvas opressin", + "ĠGuid eline", + "B NP", + "Ġd irty", + "Ġinf imum", + "ĠAl ess", + "ĠVol cano", + "M agn", + "Y Y", + "ugh lin", + "bo platin", + "ĠCant or", + "Ġclot hes", + "ĠR w", + "Ġus eless", + "ĠK dV", + "oper oxidase", + "ĠCor rect", + "Ġfat ality", + "ĠRest riction", + "Comput er", + "Dep artment", + "I l", + "g ang", + "ĠElect roc", + "obar ic", + "P hen", + "Ġn ed", + "ad h", + "iss ing", + "to oth", + "Ġman uscripts", + "Ġbi otechnology", + "Sup p", + "ĠPair wise", + "ĠPars ing", + "d H", + "m elt", + "r z", + "ĠC atalyst", + "em ption", + "Ġshow ers", + "BL EM", + "ĠBro thers", + "ban on", + "Ġbrac hy", + "metall icity", + "ĠC IS", + "ĠC openhagen", + "Ġel der", + "Ġfin anc", + "odes ic", + "Ġdev ise", + "Ġsurv ives", + "Ġð tÃŀ", + "Ġfasc inating", + "Ġparall ax", + "H OR", + "y th", + "on ins", + "ĠE HR", + "ĠG ates", + "ob ase", + "ĠCon way", + "oper ations", + "man uel", + "ĠAb dominal", + "ĠAR G", + "ĠGr ö", + "Ġphotos ens", + "ĠCoul ter", + "ĠJul ian", + "ĠLev ine", + "Ġsarc oidosis", + "Ġp illars", + "Ġd R", + "ĠW G", + "Ġspec ulation", + "ans ki", + "ĠGaussian s", + "Sch w", + "ĠNam bu", + "paren ts", + "intr insic", + "ĠKend all", + "ĠR g", + "Ġprot otypical", + "bre lla", + "Ġtet rap", + "ĠPath ophys", + "nm t", + "Ġerg odicity", + "ĠYers inia", + "Q O", + "ĠI AV", + "Ġch ocolate", + "Ġconf erring", + "Cl NO", + "oti a", + "Com plete", + "ĠAMP A", + "ïĢ Ń", + "ĠḠ¡", + "ĠiP SCs", + "ĠApparent ly", + "Ġintox ication", + "ĠF ather", + "per cent", + "Ġsh aker", + "Ġfin ancing", + "Ġgenital ia", + "memb ers", + "Ġstagn ation", + "h matic", + "ro red", + "Ġcon idia", + "atal oader", + "ĠNe il", + "Ġliter atures", + "ĠGl c", + "ĠDevelop ments", + "differenti ation", + "ĠRevis ited", + "n il", + "t om", + "di ol", + "ĠAb ell", + "Ġplastic s", + "ĠLu ke", + "adj acent", + "ĠBH s", + "ĠPosition ing", + "ø r", + "overex pressing", + "E c", + "P ref", + "or ting", + "Ġin ch", + "ĠC atherine", + "ĠD MP", + "ĠO e", + "end othelial", + "IC ES", + "ĠHad ron", + "Ġrevis it", + "ĠPict ures", + "ĠKnock down", + "il ian", + "ĠA LA", + "op amine", + "ĠL ah", + "cl imate", + "Ġdist raction", + "ars ki", + "ĠAcc ount", + "ref lex", + "Ġelong ate", + "ĠAmb ient", + "C x", + "M achine", + "ĠJ PEG", + "Ġclass ifies", + "ĠReg ulate", + "oplas ia", + "inj ury", + "neigh bors", + "ĠFORM ATION", + "F IS", + "S z", + "ĠO SC", + "Ġassemb ling", + "Ġintrac erebral", + "su pers", + "Ġp F", + "Ġhe al", + "ĠV ries", + "arc he", + "ĠDec om", + "ĠDiff ic", + "agent a", + "ĠSpir it", + "ĠInters ection", + "Ġendors ed", + "ĠNob el", + "i Ïī", + "w u", + "ph aly", + "Ġqu eu", + "ĠFor um", + "land er", + "Ġspectrom etric", + "ĠHank el", + "ĠC SE", + "Ġres umed", + "ĠG RE", + "AC ES", + "CT A", + "Ġbeh aved", + "Mon itor", + "ĠNik on", + "ĠCHAR ACTER", + "ĠS AL", + "ĠI ch", + "ĠH SF", + "Ġgen otoxic", + "ific ance", + "ĠCh iang", + "ĠZ en", + "ĠAr rows", + "ĠPD T", + "ĠFL ASH", + "ocor tex", + "onstruct ing", + "T reatment", + "ĉ ĠĠĠĠĠĠ", + "ed ullary", + "il ty", + "ind entation", + "Ġam ended", + "Ġfl ed", + "roph ication", + "ĠGL M", + "ĠOper a", + "HL H", + "L ite", + "b mod", + "Ġa version", + "ĠS weet", + "Ġst reptavidin", + "ĠP airs", + "ug os", + "ep oxy", + "Ġun specified", + "Ġmicro channel", + "ĠVictor ian", + "C ould", + "in formed", + "Ġs its", + "Ġr x", + "Ġne p", + "to uch", + "RO I", + "Ġhead ers", + "fl ush", + "ĠPath ogenic", + "Ġspac ings", + "het ti", + "Ġmotiv ating", + "Ġstake holder", + "ĠSymbol ic", + "ĠC rani", + "Ġdis pute", + "Ġass ists", + "ind ler", + "ĠSp atio", + "oh m", + "Ġextrap olate", + "Ġelabor ation", + "ĠGTP ases", + "Ġcellul ase", + "ĠT uc", + "ol ide", + "ĠA IM", + "pl ast", + "ĠB ible", + "op oly", + "ub o", + "ace an", + "ĠPen rose", + "ĠMap Reduce", + "ĠKw on", + "W all", + "Ġg cd", + "ĠAr bitrary", + "Pro duct", + "ĠGit Hub", + "F n", + "Ġc k", + "ĠA us", + "Ġgra ve", + "Ġmetabol ically", + "Ġlist en", + "Ġextrac tions", + "ĠTr unc", + "ĠRad iology", + "cons erving", + "ĠComposition al", + "report ing", + "s ustain", + "î Ģ", + "ĠO bl", + "Ġk N", + "St re", + "ĠSuper gravity", + "ĠPV P", + "Ġcivil ian", + "ĠTun nel", + "Ġhelic opter", + "ĠCamb rian", + "Ġr g", + "ĠU PF", + "Ġpol yd", + "Ġobserv ability", + "con tainer", + "nit ros", + "ĠCut ting", + "Ġdeco uple", + "Ġcarbox y", + "c row", + "Ġc x", + "ĠK ell", + "Ġproject ors", + "Ġmyocardi tis", + "itone um", + "condition ing", + "ĠE TH", + "oy ama", + "Ġphosph ates", + "ĠSub jective", + "ĠSer re", + "Ġcollagen ase", + "Ġvibr ating", + "strept omycin", + "z hen", + "Ġc res", + "Ġc ull", + "Ġh aven", + "ĠG PL", + "less ness", + "Ġview points", + ",, ,", + "ochrom ic", + "uy ama", + "Ġpartnership s", + "L ICENSE", + "ĠS IFT", + "res ources", + "ĠG os", + "iv ac", + "Ġneuro genic", + "Ad j", + "Ġaqu ifers", + "ĠGly cos", + "ĠMatthe ws", + "ĠFrid ay", + "Ġp X", + "Ġan te", + "ĠF enton", + "ĠE ukary", + "ib al", + "ide ae", + "At tention", + "ĠPolymer ization", + "Ġflip ping", + "ĠMedi ates", + "Ġstation arity", + "Ġecho es", + "alid omide", + "Ġdeline ation", + "Ġn aval", + "ĠS omatic", + "Ġst ub", + "ĠB ever", + "ĠR iz", + "Ġres ummation", + "Ġass ault", + "Ġpre existing", + "Ġhyper methylation", + "Ġconserv ing", + "record ed", + "am n", + "Ġch ow", + "ĠK ill", + "ĠPro duced", + "Ġref s", + "ĠEn zymes", + "Ġdeep est", + "&& &", + "ĠFR P", + "Ġmil ieu", + "Ġirrig ated", + "ĠAn atomical", + "Ġdiss ect", + "ili ensis", + "raz olo", + "ĠProb able", + "sol ve", + "conf irmed", + "ohydro dynamic", + "l ibrary", + "ĠC iti", + "ĠP HA", + "its ky", + "Ġelect rone", + "na ive", + "Ġrib s", + "ĠPhot onic", + "Ġsubstanti a", + "ĠEST IM", + "Ġduoden um", + "ĠChag as", + "ĠSURVE Y", + "P ress", + "b ian", + " ¤", + "he i", + "ĠV AR", + "Ġcol ocalization", + "Ġpol yl", + "Ġdo ugh", + "Ġmicro controller", + "Ġinternal ized", + "Ġdischarg ing", + "ĠChlamyd omonas", + "or ad", + "it el", + "ĠW end", + "Ġlog its", + "Ġelectro cataly", + "ĠAm ar", + "Ġappreci ably", + "Ġneurotrans mitters", + "former ly", + "c ul", + "r ata", + "ĠB alk", + "ĠO sm", + "Ġsympt omatology", + "ĠFI ELD", + "ĠA Ps", + "Ġg ymn", + "ĠM MS", + "Ġref resh", + "ĠInd ices", + "Ġimplant able", + "sh uffle", + "ĠHe ath", + "Ġcr isp", + "Ġdiss ertation", + "ĠUl t", + "Des cription", + "ĠOrig inally", + "ĠF n", + "ĠF LOW", + "ub ility", + "Ġexam s", + "ĠSh or", + "ĠCd Te", + "psy cho", + "Ġdict ates", + "Ġparench ymal", + "ĠPret reatment", + "Ġrememb ered", + "Ġb ras", + "oti d", + "Ġrecomm ender", + "Ġfles h", + "p itch", + "in ist", + "Ġb title", + "Ġl c", + "ass igned", + "ĠAd visory", + "ĠGene va", + "weight ing", + "ĠAB TS", + "Ġhex agon", + "ovsk ite", + "ĠAPI s", + "Ġbol ometric", + "Ġmultif aceted", + "i ak", + "Ġt n", + "ĠB ibli", + "pro sy", + "ĠJ ama", + "Ġinf rastructures", + "ĠSh are", + "Ġintro gression", + "trans forms", + "Re port", + "ĠTR ANS", + "ĠEX P", + "ĠCB T", + "ĠUbiqu itin", + "ĠThick ness", + "p ub", + "t oxin", + "ĠF riction", + "ĠL AG", + "ma ils", + "ĠIm mediately", + "Ġweak est", + "ĠMR S", + "Ġcalcare ous", + "b ath", + "Ġc g", + "ur ational", + "ter o", + "ĠIn oue", + "Ġinstruct or", + "ac ceptor", + "ĠE volving", + "ĠL uther", + "Ġres igned", + "ĠCh ond", + "ER F", + "Ġselect or", + "Ġnewsp apers", + "G RA", + "S pe", + "V H", + "r A", + "ot ine", + "ĠF ACT", + "com position", + "rid ing", + "PC M", + "Ġmiddle ware", + "ĠGR P", + "Ph osph", + "ĠEP IC", + "spe ech", + "vor tex", + "ĠHers chel", + "am is", + "ot ube", + "ĠG omez", + "com yc", + "ĠPh yto", + "ĠCons erv", + "Ġcav a", + "arr hyth", + "ĠRestric ted", + "il icity", + "og ap", + "CT P", + "ĠLat ino", + "atten uated", + "m obility", + "an en", + "Ġn if", + "ĠV ideos", + "ĠSch ubert", + "Fe atures", + "oprop anol", + "ĠThird ly", + "at ula", + "ĠC emetery", + "enti st", + "Ġdel i", + "tri als", + "Ġgran ulation", + "TT G", + "Ġtele ost", + "mor ill", + "or se", + "otyp ically", + "ĠAb ility", + "Amin o", + "a queous", + "Ġp CO", + "ec on", + "ĠL ign", + "ess els", + "Ġform ulating", + "ĠTo o", + "ĠTrans lational", + "ours es", + "ubiqu it", + "stat istic", + "Ġburst ing", + "Ġestu aries", + "ĠNanoc om", + "Ġex ports", + "Ġà ª", + "cont aminated", + "Ġtub ing", + "Ġautom obile", + "Ġmiss ile", + "Ġhierarch ically", + "Ġrepair s", + "ĠImpro ves", + "ĠEFFECT S", + "Q Ds", + "ro z", + "ar ic", + "Ġpar sed", + "ĠBr ink", + "Ġprogress ing", + "Ġperm Neigh", + "A gg", + "Z X", + "s ink", + "Ġw ise", + "et ence", + "ĠI c", + "lo o", + "me ida", + "Ġpolar iton", + "ĠConn ections", + "Ġhall marks", + "Long rightarrow", + "Ġthe ater", + "es ar", + "Ġre imburs", + "Ġlog o", + "Ġexc reted", + "ĠNo isy", + "Ġleak s", + "ĠDa ph", + "Ġparagraph s", + "Ġlandsl ides", + "Ġprecl ude", + "Ġcoerc ivity", + "ĠBurkholder ia", + "ĠGó mez", + "p rice", + "The ory", + "sur gery", + "f name", + "f ailure", + "l iness", + "re fer", + "ri que", + "ĠD ogs", + "ĠE UV", + "ĠV apor", + "CS R", + "Big gl", + "Con straint", + "gra vitational", + "Ġcombinator ics", + "Ġartic ulated", + "ĠBax ter", + "ĠUltrason ic", + "L TE", + "l op", + "Ġinter atomic", + "int uitive", + "sim plex", + "Ġexperiment ed", + "organ izing", + "ĠOs aka", + "had ron", + "Ġdendrim ers", + "ĠElse vier", + "C IP", + "ĠB AP", + "ĠAl onso", + "art et", + "anti s", + "Ġextrac orporeal", + "Ġpow dered", + "ĠSet tings", + "et allic", + "ĠT EC", + "ĠA rena", + "Ġan od", + "ĠRe agents", + "lic enses", + "ĠRem ove", + "Ġpron unciation", + "thin space", + "ĠClin ically", + "g ative", + "Ġw age", + "ĠH ap", + "ĠG rac", + "ff t", + "Ġform ate", + "inf eld", + "ĠQu in", + "Ġglomer ul", + "W ay", + "Ġk ills", + "Ġtrans versely", + "vari ation", + "enn as", + "ĠPL L", + "Ġinstrument ed", + "ĠSpar k", + "Ġp illar", + "ĠC aval", + "ad ers", + "iss en", + "sc ene", + "other m", + "é es", + "Ġprac ticing", + "ĠBM SCs", + "ĠFernand ez", + "Ġbes ide", + "f ew", + "ĠC ru", + "Ġpro d", + "and ers", + "az oline", + "Ġleg islative", + "bal ances", + "UR L", + "Ġstere otactic", + "Ġtrib es", + "Ġá¹ ¼", + "ĠPAN I", + "adren o", + "got ten", + "c ranial", + "ĠM ick", + "ĠM MC", + "ad iazole", + "enti ation", + "ĠGl n", + "ĠHol stein", + "ĠExpl orer", + "Ġos se", + "arth y", + "ĠEV ALU", + "adrenal ine", + "J J", + "ĠC MA", + "ĠIn activation", + "AB S", + "ĠST Z", + "Con figuration", + "ĠOl factory", + "ĠSulf ur", + "symbol s", + "ĠA STM", + "di vergence", + "ĠO CR", + "med ical", + "Ġview er", + "Ġbomb ardment", + "f air", + "n ice", + "el berg", + "ĠG PT", + "ĠK ow", + "Ġphot osphere", + "Ġlab ile", + "ĠSh ang", + "Ġhom otopic", + "SV D", + "bec omes", + "Ġgon or", + "Ġdeuter on", + "Ġphylogen ies", + "ĠS AF", + "rap ment", + "ĠCH F", + "Pl an", + "ĠLeg al", + "ĠFred holm", + "Ġshar per", + "Ġnanor ib", + "ĠBuff alo", + "B MD", + "Ġl g", + "os up", + "ĠO PC", + "Ġend ophytic", + "AT R", + "ĠExpression s", + "ĠMus ical", + "Int roduction", + "ĠSL M", + "ç ois", + "ogly cos", + "agl ia", + "m ussen", + "form ans", + "Ġsub structures", + "ym pan", + "ha e", + "sh i", + "ĠInter pret", + "Ġcat abolic", + "Ġoccup ations", + "ĠBif urc", + "Hydro xy", + "ĠKau f", + "s leep", + "am as", + "ĠS f", + "ĠM BP", + "Ġnon alcoholic", + "Ġdisc ordant", + "Ġep igen", + "PR I", + "ĠRed shift", + "war n", + "Ġlap top", + "Ġabras ive", + "îĤ Ŀ", + "l h", + "ĠK nee", + "Ġsc rat", + "Ġpol oidal", + "ĠUn iv", + "omy osin", + "ĠAug mented", + "Ġtaxon om", + "Zr O", + "Ġphytochemical s", + "it en", + "ĠP atterson", + "th ym", + "di hydropy", + "Ġk y", + "ĠMeta zoa", + "ALL Y", + "Ġretin oblastoma", + "concaten ate", + "M ale", + "Ġo mission", + "ic her", + "ĠA zer", + "op p", + "ple asant", + "ning ham", + "Ġax ially", + "HD FS", + "Ġfic tional", + "Ï «", + "Ġn arc", + "Ġunder took", + "Ġmicro circ", + "ON LY", + "IV ER", + "ĠCy cles", + "Me as", + "ĠGriff in", + "ĠPli ocene", + "Ġp I", + "ĠA viation", + "ĠC ategorical", + "ĠN ils", + "Ġresid ency", + "ĠLa ur", + "Ġpref ers", + "Ġassert Equals", + "Ġliqu or", + "d M", + "os perm", + "ĠF UT", + "Al O", + "Ġcyt ometer", + "Ġstabil izers", + "Ġprem ium", + "Ser ial", + "ĠWalk ing", + "íķ ľ", + "Ġconfron ted", + "encaps ulated", + "C ard", + "ĠS eeds", + "ab ular", + "uk ov", + "List ener", + "Cho ose", + "ĠSj ö", + "M ake", + "Ġis oc", + "am ount", + "AT C", + "ij a", + "Ġsul cus", + "ĠMö bius", + "ĠPren atal", + "Ġ ß", + "Ġis ochron", + "Ġbe ans", + "ĠD ens", + "ĠW elling", + "ĠO man", + "St ats", + "ĠVal id", + "ĠRew ard", + "G K", + "Ġâ ©", + "Ġelectro poration", + "ĠSNR s", + "Ġgar lic", + "ĠParticip ant", + "ĠSplit ting", + "ĠMeteor ological", + "morill onite", + "Ġo edema", + "ĠD ots", + "ĠCl are", + "Ġstar ter", + "Ġclim atology", + "Ġcomm ence", + "Ġfall en", + "ĠAu NPs", + "attr s", + "Ġconsult ant", + "tw isted", + "Sol ving", + "Ġcoerc ive", + "ë¡ ľ", + "K ar", + "Ġs tit", + "ĠS SB", + "ĠI W", + "Ġcan vas", + "py ruvate", + "methyl sulfanyl", + "Ġast rophysics", + "ĠTra ditionally", + "Ġexcit onic", + "w ear", + "ĠT in", + "ros h", + "ĠCl ient", + "ĠCor rections", + "ĠPop ular", + "ĠLiqu ids", + "f inder", + "Ġst ran", + "pl ine", + "ore lla", + "Ġinc ur", + "Ġar che", + "Ġmed ically", + "M ur", + "p eter", + "Ġbe verage", + "ĠN Ws", + "Ġfol ic", + "Ġspec ulative", + "Ġà ±", + "Ġrib bons", + "ĠPri est", + "Qu anti", + "Ġundist urbed", + "at che", + "ass i", + "ĠPer forming", + "ĠEl ong", + "Ġmatch ings", + "Ġfranch ise", + "g io", + "ĠS arg", + "Ġab oard", + "cycl odextrin", + "ĠTH ER", + "Ġsatur ate", + "ĠKin ematics", + "Ġpeptid oglycan", + "ĠShel f", + "toc opherol", + "B ottom", + "m ith", + "r dx", + "z os", + "Ġt RNAs", + "ut f", + "EN A", + "Ġless on", + "Ġpolar on", + "br aska", + "Ġath letic", + "Ġscram bled", + "Ġpursu ing", + "Ġbod ily", + "Ġc ac", + "im en", + "ĠI κB", + "ĠR ö", + "ĠR FC", + "ĠL PC", + "Ġi Ïī", + "Ġdi ary", + "Ġqueue ing", + "ĠDiver gence", + "ĠShig ella", + "ĠUltrast ruct", + "Ġtri phosphate", + "ĠIm plant", + "Ġfer rous", + "ĠBur ton", + "ĠHert z", + "f abric", + "t uring", + "ĠS SM", + "og rad", + "Ġmet azo", + "Ch ang", + "Ġadip ogenesis", + "Ġnu isance", + "Ġanonym ity", + "Ġrefriger ator", + "ì ľ", + "oc tion", + "Ġsp aring", + "Ġch alcogen", + "Ġobserv atory", + "Ġbo oster", + "ĠAnd ré", + "ĠST O", + "yr yl", + "ĠED X", + "ĠDen ver", + "Ġhomogen ate", + "Call back", + "a C", + "h ours", + "k ova", + "ĠA UD", + "Ġsp are", + "Ġpart ons", + "ĠInt ake", + "Ġrecogn izable", + "ĠGold stein", + "Ġstriking ly", + "Ġsan itation", + "F inder", + "G eneration", + "b oy", + "t am", + "ĠR PM", + "iv ious", + "yl ak", + "oph iles", + "Ġpri est", + "Ġeas iest", + "Ġdeliver ies", + "El mer", + "Ġzircon ium", + "ĠMish ra", + "Ġâ Ķ", + "ĠW DM", + "Ġper id", + "ĠZ T", + "Ġlocal izes", + "ĠOR s", + "ĠID O", + "Ġple asant", + "ĠMW CNTs", + "ĠJim my", + "ĠYe h", + "g athered", + "k il", + "ĠK appa", + "Ġcar toon", + "ĠHe uristic", + "Ġs z", + "Ġor ifice", + "Ġman nit", + "ĠCO MM", + "IC K", + "Ġfar mer", + "ĠSil encing", + "Ġprefix es", + "q c", + "ine urin", + "Ġinf lated", + "ĠRe z", + "Ġhydro dynamical", + "Ġoscill ate", + "Ġpedest rians", + "ĠAngi otensin", + "ĠVisc osity", + "Ġoligodend rocytes", + "Ġparo tid", + "Lay out", + "rageen an", + "Ġ è", + "Ġm N", + "Ġdo zen", + "ex clusion", + "Ġpan ic", + "ĠPD I", + "Ġtw entieth", + "ĠElect roph", + "Ġmicrobi ology", + "Ser ver", + "ĠParticip ation", + "D ET", + "P oss", + "ĠN emat", + "ĠN RF", + "arg uments", + "Ġam ylase", + "Ġarg v", + "Ġresol ves", + "Ġrevis ions", + "Pack et", + "T ools", + "Y E", + "Ġt ire", + "in duction", + "as ive", + "ton ic", + "é m", + "car rying", + "ĠImmun oblot", + "ĠIP F", + "Ġdeterior ated", + "Ġjuris diction", + "rele ased", + "osm otic", + "Ġrestaur ants", + "ï ¸", + "ĠN m", + "Ġfl ips", + "Ġsepar ability", + "ĠRec ursive", + "Ġpast ure", + "ĠÄ ī", + "Ġblast ocyst", + "M CP", + "T b", + "u ene", + "es ulf", + "ess im", + "Ġhe n", + "ĠK ull", + "yl um", + "are v", + "ues ts", + "ĠZ ip", + "Ġbo ats", + "Com mand", + "Cont inu", + "ĠBog oliubov", + "Ġmannit ol", + "K now", + "Ð ³", + "ĠH ack", + "Ġmass ively", + "ĠAll oys", + "ĠCD P", + "ĠStere o", + "ĠElectro de", + "Ġisofl av", + "Ġinteroper ability", + "ĠAdela ide", + "ĠP PD", + "ĠK ou", + "Ġdi ap", + "Ġcons erve", + "Ġaggreg ating", + "Gl uc", + "Ġî ģ", + "Ġg ust", + "ĠLe b", + "ET IC", + "ĠCons ol", + "ĠMor ita", + "Rel ative", + "Ġpale o", + "Ġwitness es", + "ĠLaure n", + "azep ine", + "ĠT Y", + "ĠI di", + "ĠM ent", + "ĠR CA", + "igen in", + "ĠDef ence", + "Ġpy rimidine", + "ĠTi N", + "Ġendot helin", + "Ġpand as", + "Ġswallow ing", + "Ġconges tive", + "Ġv inc", + "ĠD IP", + "ĠH ough", + "Ġz w", + "ĠKim ura", + "represent ations", + "ĠProm ote", + "ĠTer ry", + "Ġhat ched", + "look up", + "Elect ron", + "Ġdorm ancy", + "Ġres ign", + "Ġval uations", + "Ġmake up", + "ĠAm y", + "CL UD", + "SE P", + "tub ule", + "Ġsoldi er", + "ĠT z", + "ĠT rump", + "ĠK ramer", + "con i", + "Ġeng raft", + "Ġvacu ole", + "Ġreplic ating", + "iton itis", + "ĠBacter i", + "vacc inated", + "ol t", + "ĠA hn", + "Ġan em", + "ĠB IT", + "ge o", + "Ġmicro gravity", + "ĠSh ir", + "ĠWorld wide", + "Ġign or", + "ĠË ĩ", + "Ġlubric ation", + "j ava", + "v t", + "Ġ yl", + "Ġh ills", + "ĠF OL", + "Ġbasal tic", + "Ne ill", + "ĠEthiop ian", + "ĠNOT CH", + "ĠMOS FET", + "le aving", + "ĠP ter", + "ĠW eld", + "ap le", + "Ġsand wic", + "Ġaz ide", + "ĠStim uli", + "Ġl izard", + "ĠC inc", + "ĠH ain", + "ical s", + "Ġcontact ing", + "ĠMar x", + "Ġpsych otherapy", + "ĠRet in", + "Ġcatheter ization", + "ĠNanopar ticle", + "ĠT CC", + "ver mectin", + "ĠB ASE", + "Ġnot or", + "Ġelectron ically", + "ster oid", + "Ġbil aterally", + "Ġneph ritis", + "Ġirr itation", + "ĠProlong ed", + "Y our", + "he uristic", + "ur geon", + "Ġleft most", + "ĠRE VIEW", + "Ġgast rectomy", + "ENT IAL", + "Me ans", + "ĠDys on", + "Ġbrand s", + "yield s", + "mercap to", + "r ub", + "oun cement", + "err no", + "Ġview ers", + "but an", + "ĠMal ay", + "ylind rical", + "Ġpromin ently", + "Ġescap ing", + "Ġquer ying", + "Stor age", + "F os", + "c odec", + "Ġc M", + "str ates", + "gl ove", + "ĠTra jectories", + "Ġster ol", + "Ġhemat opoiesis", + "Ġcup rates", + "O k", + "d aily", + "ĠB IO", + "ĠL ICENSE", + "ell ations", + "ass y", + "SU RE", + "Ġep inephrine", + "Ġdown wards", + "cor ner", + "assert True", + "ĠẠij", + "ĠSou za", + "M AG", + "por ph", + "Ġeff luents", + "lo em", + "oad dition", + "obut yl", + "eles tial", + "F em", + "m pi", + "ĠR s", + "ell ates", + "ĠK ag", + "Ġunc oupled", + "Ġleg umes", + "Ġomit ting", + "à »", + "ĠT ABLE", + "hal ed", + "ĠÅ ģ", + "Ġmis fit", + "Ġmol ars", + "otechn ological", + "Mark ov", + "Ġpra ised", + "ĠD ab", + "ĠV ij", + "enti lation", + "ĠCh atter", + "Ġbo iled", + "Ġcat ches", + "annot ation", + "Sign al", + "Ġlever ages", + "Ġadvis ory", + "s ong", + "on dition", + "Ġf ug", + "ra ps", + "ĠM CD", + "par ticip", + "ob ian", + "Ġcoun sel", + "ĠPR P", + "edi ol", + "ĠÅ ¨", + "Ġbr uce", + "Sh anghai", + "Data Frame", + "ĠCorrespond ingly", + "Ġacryl amide", + "f ellow", + "l ob", + "ig t", + "ĠC rystallization", + "Ġind omethacin", + "ĠPD R", + "gi ate", + "ĠPan els", + "complex es", + "ĠNic ol", + "Ġfoli ar", + "c ubic", + "Ġd E", + "ĠC CM", + "pl ating", + "Ġres istors", + "ĠG az", + "Ġover turn", + "Ġrep ress", + "Ġpot s", + "ĠPI K", + "Ġderm is", + "Rep resent", + "ĠAnders son", + "Ġretrotrans pos", + "A SA", + "C ounter", + "T et", + "im in", + "per formed", + "ĠN ept", + "Ġhe el", + "rol d", + "ai res", + "Ġread ability", + "Ġbenef ited", + "Ġpuls ation", + "ĠBal ancing", + "Ġevapor ator", + "Ġeig ens", + "ĠH ospit", + "Ġtra ils", + "ĠCo ordinate", + "acc ase", + "ĠHR MS", + "sign aling", + "ĠNP Y", + "Ġamelior ated", + "tu ples", + "Ġmetas urface", + "ĠFrances co", + "Ġspo of", + "îĹ ¸", + "F u", + "J K", + "e j", + "Ġg oss", + "ĠH im", + "Ġprior itized", + "Ġmes othelioma", + "idx s", + "Ġrecon naissance", + "Ġlam ps", + "ãĢ Ĥ", + "Ġreform ulation", + "Ġdeli rium", + "ĠN PR", + "ĠG amb", + "ill as", + "---- -", + "Ġdr illed", + "ĠGen otyping", + "ĠBl ank", + "Ġprop eller", + "Ġcere als", + "ĠAir borne", + "ĠPhot ocatalytic", + "ĠCav ity", + "Ġdol phins", + "Ġsg RNA", + "underst ood", + "e ous", + "Ġs ax", + "ol ayer", + "ĠP end", + "ĠG ET", + "cl ed", + "Ġà ¼", + "Ġcyt osine", + "Ġparallel ization", + "MM s", + "ĠOrgan isation", + "Mod els", + "Ġaccommod ated", + "Ġchol est", + "Ġin activity", + "ĠB oss", + "ĠG CS", + "Ġso aked", + "ĠSec reted", + "Ġvacu olar", + "zo an", + "ĠGre ene", + "Ġbol t", + "b j", + "ĠT all", + "Ġst or", + "ĠM ob", + "Ġbl urred", + "IN O", + "ĠMet allic", + "uff s", + "ĠNOT E", + "Ġsonic ated", + "obuty ric", + "Ġt DCS", + "ĠN es", + "osp ir", + "we igh", + "ĠReg ulator", + "Ġhem olysis", + "Ġsound ing", + "Ġcruc iate", + "Ġcaps aicin", + "ĠTy rosine", + "ĠT ell", + "ĠP EP", + "ĠR c", + "ĠE ating", + "ĠGo als", + "ure t", + "Ġear n", + "Ġcolle ges", + "Ġchemo attract", + "Ġá» ¹", + "ĠEch ocardi", + "F ort", + "s odium", + "am ined", + "ĠN PP", + "ĠK alu", + "Ġdec ipher", + "tet ramethyl", + "ĠOP N", + "stra ight", + "Ġtemp ered", + "ĠHind u", + "ĠOrd inary", + "ĠACh E", + "J NK", + "f os", + "v cpu", + "en amide", + "ĠC rack", + "ap ical", + "Ġanti serum", + "tri plet", + "dec ision", + "Ġcanc els", + "ĠPM N", + "Ġporph y", + "ĠD SA", + "Ġsub matrix", + "Ġj as", + "Ġrep tiles", + "ĠSo on", + "ĠStat istically", + "Ġlever aged", + "Willi ams", + "F LD", + "f olk", + "Ġb ang", + "ĠS CL", + "ass es", + "Ġtend ons", + "found ed", + "ĠRick etts", + "in set", + "Ġsp un", + "Ġun ramified", + "Ġra pe", + "ĠZ Z", + "ĠNe bula", + "Ġthromb otic", + "ĠBor on", + "ĠArg on", + "pool ing", + "ĠMarg inal", + "Ġfellow ship", + "Ġerythrop oietin", + "mathp zc", + "x L", + "ĠS ik", + "ĠB ayer", + "Ġover dose", + "ĠCO I", + "ĠLes ions", + "ĠCompe titive", + "ĠODE s", + "w rap", + "ach lor", + "Ġsub ordinate", + "ĠY Ba", + "head ed", + "Ġgrass es", + "Ġbir ational", + "ĠJeff rey", + "Ġmold ing", + "Ġlid ocaine", + "ĠFOX O", + "termin ated", + "ĠâĩIJ âĩĴ", + "ĠM EL", + "tic ulum", + "Ġr é", + "Ġcl aud", + "Ġj amming", + "St atic", + "Ġtribut ary", + "at et", + "ed onia", + "ĠC MP", + "ĠV N", + "rep resents", + "SO URCE", + "uck land", + "ĠInterest s", + "ĠNan oscale", + "ocon jug", + "Ġcatalog ues", + "ĠActin obacteria", + "F ixed", + "b asal", + "Ġanti parallel", + "Ġconf using", + "Ġmark ings", + "Ġdistinc tions", + "ĠHu a", + "ĠWat ts", + "Ġnanoflu id", + "Ġdiffract ometer", + "L ater", + "m igration", + "Ġco planar", + "Ġhyp omethyl", + "PD S", + "SO s", + "Cor respond", + "Ġelucid ating", + "IZ ED", + "E Vs", + "g art", + "m Tc", + "ĠT UR", + "ur acies", + "Ġfollow er", + "Ġhaz e", + "OU TPUT", + "ĠMyel oid", + "BUFF ER", + "C amp", + "an im", + "ĠT ES", + "ĠC ricket", + "ĠP aired", + "ĠP AGE", + "ĠB id", + "Ġy rs", + "Ġend ow", + "irc ase", + "ĠSup ported", + "Ġleaf let", + "ĠProm oter", + "Ġconvinc ed", + "l iers", + "he ra", + "Ġs eller", + "ag reement", + "Ġun ary", + "onstr ucted", + "Ġrest rained", + "ĠPet ersen", + "Anal og", + "Ġexacerb ations", + "Ġperfor ated", + "ti ds", + "ĠM SH", + "ou i", + "ĠCor i", + "ĠCr uc", + "Ġfract uring", + "Ġinfer tile", + "ĠPRO BLEM", + "ĠFried mann", + "Ġspectrophot ometry", + "ERG Y", + "ot us", + "pro posed", + "ĠMo isture", + "ĠNo ether", + "ĠLa unch", + "ĠLear n", + "Ġven a", + "Ġfasc i", + "Ġquies cence", + "ĠP rand", + "ĠCon vert", + "Ġtri age", + "AN E", + "Ġfeed stock", + "ĠdB m", + "Ġneo formans", + "G SE", + "ĠA PE", + "Ġcardi ometabolic", + "Ġmagnet ometer", + "En vironment", + "Ġfulf ills", + "ĠMang anese", + "B MP", + "ĠR atios", + "ist able", + "ass ume", + "Ġresp ected", + "Ġsc ars", + "Ġsup porters", + "ĠAug mentation", + "Ġglycos ylated", + "ĠUltra fast", + "Ġdemethyl ation", + "metast atic", + "c ylinder", + "Ġh ang", + "ĠM AV", + "dis joint", + "pha rose", + "ĠLe banon", + "PI s", + "lab eling", + "Ġneutral ino", + "ĠSO CS", + "xc b", + "ĠTerr itory", + "ĠPolic ies", + "K ing", + "Ġall ied", + "Ġsatur ates", + "mus cle", + "odim ers", + "Ġb t", + "ĠH ang", + "ĠE b", + "Ġch imer", + "Ġnot ational", + "Ġcol der", + "ult z", + "trans verse", + "HO UT", + "ĠKar ls", + "tors ion", + "J I", + "Ġm ari", + "em on", + "Ġlogarithm ically", + "ĠâIJ ¦", + "ìĿ Ħ", + "Ġa eration", + "Ġs oma", + "ĠS omal", + "Ġsp oil", + "di ver", + "Ġbreak points", + "ĠHar mon", + "Ġpharmac ologic", + "ĠM osquito", + "ĠMod ifications", + "Ġadj o", + "ĠPa pers", + "gener ally", + "ïĺ ¹", + "T ARGET", + "ĠP rix", + "oc aps", + "ĠE in", + "Ġmicro grid", + "ĠInter play", + "Ġcop ying", + "Al pha", + "ĠSl ope", + "ĠLip ofectamine", + "hig hest", + "D RO", + "ĠH ipp", + "Ġsh aken", + "Ġunder line", + "Ġfil med", + "mat urity", + "ict ure", + "IL S", + "Sp an", + "Ġinver ters", + "QU E", + "determ ining", + "Ġeosin ophilic", + "D Y", + "ĠL ID", + "ĠG ig", + "Ġintra epithelial", + "Nb O", + "fre edom", + "Ġass ured", + "ĠAr che", + "ĠSub stitution", + "ĠSri vastava", + "ĠMoz amb", + "Ġa ro", + "or c", + "ĠI brahim", + "ĠD ST", + "Ġab l", + "Ġx er", + "ount able", + "Ġloss less", + "Ġconcentr ating", + "Ġstain s", + "ĠSol ve", + "continu ity", + "ĠTor r", + "Ġpit falls", + "best os", + "Other wise", + "adhy ay", + "b ard", + "ĠC AA", + "ode tic", + "Ġast hmatic", + "Ġrational ity", + "ĠYork shire", + "neighbor hood", + "Ġhero in", + "Ġscatt erers", + "ĠH earing", + "ĠE FT", + "ĠN urses", + "ĠG LI", + "ĠZ eta", + "ĠNe igh", + "Ġvent ure", + "Ġtoxic ological", + "Ġroll s", + "f v", + "Ġc rick", + "Ġd Ïī", + "av ia", + "eld er", + "Ġinv ade", + "ext racted", + "ML P", + "ĠPA I", + "ĠMell itus", + "Ġbruce i", + "g pio", + "em otional", + "ĠD ale", + "ĠE z", + "Ġtrans activation", + "Ġquanti les", + "Ġnucle osynthesis", + "ĠAm el", + "Ġchrom ophore", + "Ġliter ally", + "band width", + "ato hepatitis", + "Ġultraf iltration", + "Mart in", + "Ġangio plasty", + "inser tion", + "D an", + "s queeze", + "us r", + "uc onazole", + "ĠF AR", + "Ġsh adows", + "Ġim itation", + "ĠK ann", + "hes i", + "Ġmic ellar", + "ves ter", + "ĠPer se", + "acet amol", + "GR APH", + "ĠAI PS", + "Ġprompt ly", + "anch or", + "Ġischa emia", + "p ump", + "Ġm afic", + "Ġl azy", + "ĠC EL", + "ĠG orenstein", + "ĠW GS", + "Ġsign ifies", + "Ġspl ines", + "determ ination", + "Ġrelay ing", + "piper azine", + "Ġsyncy tial", + "ĠA ub", + "ĠD X", + "Ġorth otopic", + "ĠLink age", + "Ġharmon y", + "ĠKaz akh", + "ĠVlad imir", + "Ġp ray", + "im olar", + "Ġgra yscale", + "Ġanaly st", + "ĠTrans l", + "Ġmen iscus", + "ĠMed ica", + "osa urus", + "ĠÐ ²", + "Ġinfiltr ated", + "Ġâĸ ³", + "Ġsacc ades", + "Ġdisent angle", + "H art", + "f ined", + "Ġb icycle", + "os itory", + "un likely", + "ere phthal", + "ĠL ia", + "Ġgroup ings", + "Ġcategor ize", + "Ġbioge ography", + "ĠAPPRO ACH", + "ĠN ing", + "ĠG rap", + "vers a", + "Ġradi ologists", + "ĠRec ording", + "Ġbo iler", + "add ers", + "C andid", + "M Q", + "Ġb w", + "ĠS ector", + "ĠH IT", + "ĠE SCC", + "ess ence", + "ore an", + "est yles", + "SU CCESS", + "ne in", + "ult ra", + "ram p", + "Th omas", + "ĠPre par", + "ĠInstit ut", + "Ġherb icide", + "ĠCha otic", + "Ġsph incter", + "Ġcompac tifications", + "C lear", + "Tr p", + "Dec oder", + "Ġsap phire", + "ĠIda ho", + "per sing", + "ch iral", + "ĠDis charge", + "According ly", + "ĠArth ritis", + "ĠJane iro", + "n j", + "ĠK d", + "Ġout lets", + "Ġsuscepti bilities", + "Ġdiver ged", + "Ġroll er", + "su fficient", + "clust ering", + "ĠTeh ran", + "Ġt b", + "bl ank", + "Ġdigit ally", + "Ġnecro tizing", + "F ALSE", + "Ġwh or", + "err als", + "ĠMo tivated", + "enz ae", + "ĠRef inement", + "Ġtick et", + "Ġprotr usions", + "ĠDonald son", + "ĠB eth", + "Ġsp uttered", + "Ġaut ocrine", + "cop ene", + "Ġcoll ar", + "Ġupper most", + "Ġoxygen ated", + "Int ro", + "âĨ IJ", + "ĠHip po", + "Ġd une", + "id ines", + "ĠH ä", + "Ġreg i", + "Ġno is", + "Ġphot odiode", + "ĠFe b", + "mut ated", + "ĠCF L", + "step ping", + "Se lection", + "ĠWeb ster", + "ĠHER A", + "indic ating", + "Ġtraine es", + "R ot", + "ĠF AK", + "ĠAs n", + "Ġfat s", + "fol iation", + "Ġartic ulation", + "Ġcus ps", + "ĠJenn ifer", + "Ġin timately", + "ĠP ing", + "so v", + "ox ious", + "hyd rate", + "ĠArch ives", + "Gon z", + "Ġ é", + "Ġch l", + "ĠO LS", + "cop h", + "Ġair line", + "Ġfo etal", + "ĠRoll ing", + "ĠGENER AL", + "O NAL", + "ag ons", + "ĠD orsal", + "Ġr itual", + "but yrate", + "ogl ut", + "Ġhex a", + "ĠSy ria", + "Ġont ogeny", + "ĠFB G", + "cover age", + "Ġtachy on", + "ĠPerman ent", + "l um", + "Ġs v", + "Ġo o", + "en ergetic", + "al titude", + "In c", + "ĠNe braska", + "ĠRE SP", + "Ġdys biosis", + "Ġmarket ed", + "oxic illin", + "ĠBroad cast", + "racycl o", + "ĠFif teen", + "ĠNar ayan", + "Ġlett uce", + "ore a", + "Ġinter cepts", + "Ġwork station", + "ĠPl ains", + "CC L", + "Ġorient able", + "ĠBo osting", + "ĠSO I", + "ĠCheck ing", + "ĠFIF O", + "Ġin sets", + "ĠS RT", + "Ġac rom", + "own er", + "MI X", + "ĠAr b", + "Ġfa eces", + "ĠCarl son", + "Ġperiv ascular", + "infiltr ating", + "Ì ħ", + "Ġm alle", + "oc ate", + "ĠB old", + "unc tive", + "ex cess", + "Ġlo osen", + "Ġprior itization", + "Ġannot ate", + "Ġgram mars", + "Ġb red", + "Ġex ocytosis", + "ĠD ahl", + "ath yroidism", + "vel i", + "Ġop ted", + "Ġsm oked", + "ĠPl ates", + "EM G", + "RO W", + "IF IC", + "OL S", + "oreg ulatory", + "Ġwhisk ers", + "secret ase", + "Ġexagger ated", + "ĠB ib", + "de formed", + "Ġz ur", + "rop ine", + "Ġpair ings", + "chrom osome", + "Ele ments", + "prior ity", + "Ġlyophil ized", + "ĠChaud h", + "W ilk", + "ĠC ation", + "ot ta", + "Ġnon convex", + "Ġdep olymer", + "MM ARY", + "Cont rolled", + "carbox y", + "Ġaugment ing", + "Ġappoint ments", + "Ġtravers ed", + "ĠF letcher", + "Ġexp iratory", + "Ġele phant", + "ĠBl ocks", + "ĠFlu ids", + "wall s", + "incre ased", + "propan amide", + "ĠAka ike", + "ĠC BM", + "ĠE cho", + "ad missible", + "Ġdis assembly", + "Ġar Xiv", + "ick e", + "LI ST", + "phen otype", + "ĠProv incial", + "leg end", + "P AS", + "r nn", + "s and", + "Ġb ariatric", + "ĠP ush", + "ĠAp oE", + "cap rolactone", + "model ing", + "ĠÅ µ", + "Ġsupercapac itors", + "or on", + "Ġp K", + "st rophy", + "ĠS uc", + "und a", + "te am", + "Ġit iner", + "Ġsw ell", + "ĠBio active", + "ĠIndic ators", + "ĠI FT", + "ĠD K", + "Ġcap it", + "sh apes", + "Ġtrac hea", + "delay ed", + "ĠGuang dong", + "L epid", + "T GA", + "h k", + "ol on", + "ogen in", + "ĠAc k", + "Ġlog ically", + "cont ributions", + "ĠCle avage", + "hur st", + "b dd", + "ST D", + "ĠF ut", + "te k", + "ĠIn her", + "Ġchem is", + "Ġbreak point", + "estim ates", + "ĠOtt oman", + "ĠNaf ion", + "WID TH", + "Ġs izable", + "ĠT su", + "emb olic", + "Ġright most", + "ĠCell ulose", + "iction aries", + "ĠMy coplasma", + "ĠBur gers", + "ĠKepler ian", + "U CTION", + "V B", + "Ġb cc", + "ra id", + "END IX", + "Ġsc oping", + "ĠPR I", + "ĠCd Se", + "ĠGre edy", + "ĠHam mer", + "ĠBacter oides", + "inform ative", + "Ġresemb led", + "yll ium", + "T wenty", + "Ġp ounds", + "Ġun polarized", + "Ġconfig ure", + "Ġtranscription ally", + "Ġmicros cale", + "ĠPut ting", + "Ġpyr rol", + "ĠLAS SO", + "f iltration", + "Ġt ech", + "per forming", + "Al ong", + "ĠCT LA", + "Ġauthor ization", + "UR AL", + "Ġleak y", + "Op tical", + "ĠReve al", + "ĠHUV ECs", + "W u", + "c ustom", + "di ble", + "Ġï£ ¦", + "CD Cl", + "Ġemph ys", + "Ne ut", + "coll agen", + "necess arily", + "ĠRoot s", + "P ose", + "T u", + "Ġcl ue", + "Ġperturb ing", + "ĠHel ium", + "ĠComb ustion", + "n itrogen", + "am plified", + "pro ve", + "ĠSo ils", + "normal ization", + "ĠCH OP", + "ĠMc Le", + "Ġstri kes", + "Ġcrop ped", + "ĠKu o", + "Ġvag al", + "Ġdin ucleotide", + "ĠIsa ac", + "ĠL OX", + "Ġdirection ality", + "Ġchem oradiotherapy", + "calc ulus", + "ĠMoh ammed", + "m apped", + "Ġre forms", + "Ġre ordering", + "ĠB m", + "ĠE SCs", + "ĠN UC", + "th aw", + "Ġnan oporous", + "Ġtrain able", + "ĠAT T", + "fe ats", + "OF DM", + "ĠSH P", + "ĠRich ter", + "Ġspray ed", + "ĠJeff erson", + "F OX", + "b h", + "ot te", + "Ġle iomy", + "osp ores", + "specific ity", + "ĠRef er", + "ĠHa as", + "M ove", + "M aterials", + "t ec", + "u tility", + "en tional", + "ĠM PP", + "ch ond", + "Ġse epage", + "Ġpe ach", + "ĠÎĶ t", + "embry onic", + "Y an", + "Ġlip osomal", + "ĠVal encia", + "ĠEnd o", + "ĠPA O", + "Ġdial ect", + "Ġchond rocyte", + "ĠMill imeter", + "ĠRegular ity", + "dest roy", + "ĠCond ensation", + "Bay es", + "abund ance", + "Ġd U", + "ĠS SI", + "ĠH AND", + "Ġcons ulted", + "Ġsup pliers", + "Ġdem o", + "reg istered", + "Ġmicros omal", + "Ġlam bs", + "respons iveness", + "D y", + "G AS", + "U ME", + "Ġa ero", + "Ġcal modulin", + "Ġcalc ined", + "Ġins ula", + "ĠMe i", + "ĠRE AL", + "Ġcontrac tible", + "ĠEss entially", + "Ġgam ing", + "Ġspill over", + "resid ues", + "â İ", + "ĠE MC", + "ĠSD E", + "ĠSer ine", + "eck i", + "ĠPrinc eton", + "ĠBACK GROUND", + "m asks", + "ĠL om", + "ff ield", + "ef itinib", + "Ġpat ents", + "ĠBe z", + "load s", + "Ġgon adal", + "Ġnitro cellulose", + "âĻ Ĥ", + "Ġth rown", + "Ġrec tification", + "min a", + "isc id", + "ĠBi obank", + "param agnetic", + "GS K", + "ĠDeriv ative", + "criter ion", + "ĠMonth ly", + "ë ¥", + "ĠS ichuan", + "Ġimmun ologic", + "Ġheter otic", + "ĠMc Cl", + "ĠSM ART", + "ĠBatter ies", + "Ġpremi ered", + "Ġcryopres ervation", + "N u", + "val ho", + "Ġfl otation", + "top ological", + "ĠNan jing", + "Ġju xt", + "ĠFed er", + "Ġprofound ly", + "c ad", + "i enced", + "ch uk", + "ĠIn g", + "ĠK SHV", + "amin obenz", + "ĉĉĉ ĠĠĠ", + "Ġmeta ph", + "ĠEpid emic", + "ĠAssoci ate", + "Ġsacc ade", + "Ġd awn", + "Ġre heating", + "Ġsp ell", + "frac tive", + "ĠTo olkit", + "Ġrecogn ise", + "path ogen", + "Ġophthal mic", + "Ġquer ied", + "t hens", + "ith ine", + "um ably", + "Ġstr ides", + "ha ul", + "Ġpass ion", + "Ġdys functions", + "By te", + "Ġca esarean", + "pre y", + "ĠHor se", + "ĠGAB AA", + "N atural", + "k os", + "in ators", + "od ings", + "AR RAY", + "Ġun ipotent", + "Ġelect romy", + "com part", + "Li u", + "encephal ic", + "ĠCOMP AR", + "Ġsymbion ts", + "ivac aine", + "O I", + "P VA", + "ĠN VIDIA", + "cal ibrated", + "Ġqu est", + "NA D", + "ĠX yl", + "Ġpharmac ist", + "direct ly", + "Ġquadrup le", + "ethan one", + "ĠBulg aria", + "Ġovip osition", + "r uns", + "Ġn ociceptive", + "Ġas exual", + "SU LT", + "Ġwould n", + "ĠInd ustries", + "abil izing", + "ĠComp ressive", + "CO OH", + "US H", + "ki ewicz", + "Ġign eous", + "Ġdisapp oint", + "ĠCK M", + "ĠDiagram s", + "ĠF lam", + "ĠG ould", + "Ġco enzyme", + "Ġpar an", + "Ġ ¶", + "Ġprogram mer", + "ĠTrans forming", + "Ġmus carinic", + "onucle otide", + "FI ELD", + "ĠFu ji", + "Ġnond ec", + "Ġblank et", + "Ġpredis posing", + "ĠTrig ger", + "Ġwel come", + "F amily", + "U INT", + "h fill", + "t vb", + "ĠB att", + "Ġun met", + "ĠAp o", + "oti ent", + "Ġfund us", + "ĠLear ned", + "Ġintr usions", + "Ġsolub ilization", + "fund amental", + "ĠSanti ago", + "Ġh pi", + "th row", + "ĠIn to", + "time out", + "Ġthick ened", + "ias m", + "Ġgravit ino", + "bran ched", + "V III", + "Ġo ch", + "Ġg ym", + "ĠK rylov", + "Ġcorrec tive", + "ĠInstit ution", + "Ġcrim es", + "ĠBacteroid etes", + "ĠE hr", + "Ġse ated", + "rol izumab", + "Ġfactor ized", + "rot ational", + "Ġadministr ators", + "âĭ Ĩ", + "ineral ization", + "l ining", + "â Ĺ", + "ur ai", + "ĠF AP", + "ĠF isheries", + "ĠE SO", + "tem per", + "Big gr", + "ĠAltern ating", + "t win", + "am atsu", + "Ġint rad", + "over flow", + "Ġcompar ability", + "Ġsyn optic", + "US B", + "db g", + "dem onstr", + "ĠAch ieving", + "Ġtect onics", + "ĠRand all", + "ĠPrep ared", + "Ġsublim ation", + "ĠB aj", + "Ġcl utch", + "Ġsub domain", + "Ġfl aws", + "inf lu", + "Ġwid ening", + "Ġmel ted", + "Ġadministr ator", + "Ġsubsidi ary", + "ĠP ricing", + "tic us", + "og i", + "ĠAl ign", + "ĠAD V", + "Ġvast ly", + "bench mark", + "Ġprioriti ze", + "R adi", + "ess ed", + "Ġsup rac", + "acc ard", + "Ġbiom imetic", + "ĠIr radiation", + "ĠALG OR", + "Ġpedig ree", + "ĠC MT", + "od ym", + "ĠV ig", + "ĠBi ochemistry", + "ĠAcc um", + "Ind ices", + "hard tii", + "ĠRal ph", + "Ġrumin ants", + "i T", + "on au", + "an er", + "pl anned", + "ever s", + "Ġret roviral", + "Ġquantif ier", + "ĠExt racting", + "Ġacet ylated", + "Or th", + "ĠSen ator", + "Ġnanos econd", + "Ġanticip ation", + "ĠECM O", + "Ġsemic irc", + "ĠCrypt osporidium", + "ĠT ARGET", + "Ġap ples", + "ef ield", + "Ġrem an", + "Ġser ovar", + "ĠTrans actions", + "trans itions", + "urs ions", + "ĠMel atonin", + "Ġcholecyst ectomy", + "ĠAntiv iral", + "h ous", + "Ġo tol", + "Ġm aj", + "Ġe clip", + "are l", + "AT IONAL", + "MI M", + "ĠCI mg", + "ĠEnd omet", + "ĠHay ashi", + "Ġchimpan zees", + "m bf", + "ĠI PV", + "act oring", + "out side", + "ne apolis", + "Ġdisc arding", + "num type", + "ĠRE ST", + "Ġflag ellar", + "ĠChand rase", + "hof er", + "Ġelectrocardi ogram", + "G b", + "m ock", + "o eb", + "ĠS MO", + "ĠM ord", + "ĠB oz", + "Ġmin ors", + "IN LINE", + "Ġtherm ogravimetric", + "ĠMel ting", + "ĠNS W", + "S ham", + "l otinib", + "Ġac quisitions", + "ta z", + "Ġdef aults", + "Ġoscill ates", + "ĠCap tion", + "Ġdisrup tive", + "Ġswe eping", + "ĠTool box", + "Ġureth ral", + "H BV", + "ĠR CS", + "Ġox ys", + "immun o", + "ht m", + "ofl avin", + "H IF", + "ĠS BA", + "ĠC PE", + "Ġwh ites", + "ĠRe actor", + "Ġpur p", + "Ġelectro catalytic", + "Ġnone x", + "Ġty phimurium", + "Ġeu rop", + "conc ave", + "macroph age", + "S ER", + "Ġl apse", + "Ġan atom", + "ĠR AC", + "ta x", + "Ġmin s", + "Ġsens u", + "ĠHe brew", + "Ġreal ism", + "ĠMicro glia", + "Ġserial ized", + "ĠHaz ard", + "Ġmetamorph osis", + "ĠI RA", + "Ġsm earing", + "Ġphot olysis", + "Ġchild birth", + "Ġsil enced", + "raw al", + "Ġquad rants", + "but anol", + "Ġstochastic ally", + "ĠCham bers", + "ĠNav arro", + "Ġproc urement", + "c ite", + "ĠS le", + "ĠH adoop", + "Ġdelay ing", + "At lantic", + "Sp ain", + "fal fa", + "od ialysis", + "yn ia", + "Ġplate aus", + "Ġmultim ode", + "RES ET", + "ĠRock y", + "ĠRodrig ues", + "f MRI", + "r int", + "ĠT AL", + "Ġspec ular", + "con struction", + "ĠAt hens", + "Ġcross link", + "Ġcount ably", + "Ġspread sheet", + "crib es", + "cons istently", + "Ġflood plain", + "E INVAL", + "M aca", + "Ġe i", + "Ġh itherto", + "Ġsem if", + "Ġcontin ual", + "ĠHom ology", + "Ġphotoc atalysts", + "is able", + "ĠH AT", + "Ġpoly hedra", + "ĠMay o", + "Ġlact ating", + "sam pler", + "Ġappl iances", + "T U", + "Ġc hess", + "ĠT ing", + "Ġinv itation", + "Ġdistrib uting", + "ash ima", + "Ġult ral", + "tre nd", + "Ġrelax ations", + "ĠHel en", + "Ġbed ding", + "Ġgland ular", + "Ġincrement ally", + "Ġconce al", + "claim ed", + "ĠEdd y", + "Ġm os", + "ĠT ube", + "ĠT oda", + "ra j", + "ĠM ü", + "ĠU ll", + "Ġun e", + "ber ine", + "Ġpolic ym", + "Ġscholar ly", + "Ġshore line", + "Ġald osterone", + "ĠBruc ella", + "T HE", + "RE AL", + "Ġex ome", + "ĠD AB", + "Ġext ras", + "Ġband ing", + "ĠSi emens", + "ĠBo ost", + "ĠSuper novae", + "ĠTrac ing", + "Ġascorb ate", + "Ital y", + "b und", + "Ġdecre ment", + "Ġneu rophysiological", + "Ġblack body", + "ĠMc N", + "Ġcompet encies", + "osc ape", + "ĠHon ours", + "Ġmas titis", + "criter ia", + "Ġb iaxial", + "Ġth awed", + "ĠF oll", + "ore ceptor", + "Ġinv ention", + "AD s", + "Sh ow", + "-------------------------------- ----------------", + "Ġellipso idal", + "Ġfoc ussed", + "ĠD at", + "ĠR im", + "ĠL X", + "ĠG ER", + "ins ler", + "Ġtop oisomerase", + "Ġhyper lipidemia", + "Ġmy stery", + "Ġnit rification", + "Ġonc ogenes", + "ĠFull er", + "ĠBart lett", + "Ġamphib ians", + "S ST", + "b ly", + "le ads", + "ec ycle", + "am pl", + "ĠP OM", + "ĠD CF", + "str ass", + "anti body", + "non linear", + "ĠBroad way", + "ĠCatal ogue", + "Ġμ A", + "Ġacet aminophen", + "Ġcrystall ites", + "ĠNan otubes", + "ĠAcknowledg ment", + "Ġmetam orphism", + "Ġtwin ning", + "ĠAzer bai", + "x A", + "CC C", + "ĠSol ids", + "pred s", + "ĠMont ana", + "WR ITE", + "R atio", + "Ġp unch", + "Ġr iding", + "Ġac ne", + "ĠU re", + "Ġcor r", + "ĠQ OL", + "Ġins ult", + "Ġdominant ly", + "Ġsubs amples", + "rew s", + "ĠPres ervation", + "ĠAff ine", + "methan one", + "Ġhedge hog", + "J H", + "Ġl ined", + "Ġst en", + "ĠD armstadt", + "ĠL asso", + "Ġde proton", + "Ġsh oes", + "Ġmo tives", + "Ġmic roscop", + "oph thora", + "Ġmac ron", + "Ġencour agement", + "acryl ic", + "ĠTensor Flow", + "W rapper", + "o ise", + "ay ak", + "Ġrep resses", + "Ġpr uned", + "ĠCl ar", + "ĠâĬ ²", + "ĠUnder lying", + "Im plemented", + "Ġswe at", + "Ġmeteor ites", + "Ġtwe ez", + "Ġpros ocial", + "Ġabras ion", + "h ail", + "Ġsh orth", + "ism atch", + "IN TR", + "ĠTr in", + "Ġphysic ists", + "ĠPE O", + "ĠMagn eto", + "ĠJacob son", + "ĠMMP s", + "ĠIntra venous", + "Ġneurotrans mission", + "ĠPneum onia", + "L ind", + "y re", + "Ġm aternity", + "ĠI ris", + "ri atal", + "ĠâĢ ļ", + "med etomidine", + "Ġtr iterpen", + "ĠMan uscript", + "ĠEnd oplasmic", + "ĠPot ter", + "Ġgerm inal", + "Ġphotos ystem", + "Gu ided", + "Ġguitar ist", + "ĠChile an", + "ĠSalv ador", + "É Ļ", + "Ġc elestial", + "om and", + "Ġn k", + "Ġv endors", + "ĠP INK", + "ĠIn organic", + "Ġmod erated", + "SU S", + "ĠJ oshi", + "ĠSt ata", + "ik es", + "oy e", + "ĠJohn ny", + "Le ica", + "Ġka on", + "ĠEquip ment", + "K im", + "g ado", + "t ures", + "Ġe lem", + "ĠA AC", + "ce a", + "od ality", + "Ġan iline", + "Ġex othermic", + "ĠG unn", + "ĠJ U", + "plic able", + "sc apes", + "typ ed", + "Ġinsp iratory", + "REG IST", + "ĠBry an", + "Ġanxi ous", + "ĠCarp enter", + "ĠPharmacokine tics", + "infer ior", + "F rag", + "Z Y", + "Ġo esophageal", + "ĠS uk", + "ĠP ron", + "ĠCD I", + "AG C", + "key words", + "sus ceptible", + "Germ any", + "M AS", + "i C", + "an mar", + "Ġex iting", + "ĠH ale", + "Ġr hamn", + "ind ustrial", + "Ġra ft", + "emb rolizumab", + "Ġdeploy ing", + "Ġsalic ylic", + "R n", + "Ġc ensor", + "Ġd X", + "Ġfor um", + "MS I", + "bl ad", + "Ġsqu ir", + "CP P", + "Ġgrap evine", + "ĠRA FT", + "Mon te", + "Ġmicrof lora", + "r cl", + "Ġdec ap", + "AN C", + "Ġbroad en", + "Ġfre ed", + "Ġsouth ward", + "ĠJac ques", + "Ġrequest ing", + "ĠAsp ect", + "araj an", + "F ailed", + "f printf", + "p ytest", + "Ê ¹", + "ĠC m", + "un til", + "ne iss", + "Ġmon os", + "osp inal", + "ols ky", + "cont rib", + "Con tainer", + "ĠVol unte", + "ĠAtt ributes", + "ĠTum our", + "Ġrein hardtii", + "Ġcentrom ere", + "ĠS ymph", + "ĠA o", + "ag ens", + "ple ted", + "ied er", + "Ġactiv ist", + "ĠAl meida", + "Ġdisturb ing", + "Ġreflex es", + "D SS", + "Ġfor wards", + "ron ym", + "ĠIntegr ity", + "WE EN", + "Ġôı¼ Į", + "Ġfaith fully", + "Ġperic ardial", + "Japan ese", + "ĠCEN P", + "K r", + "Ġdef ending", + "Ġz on", + "ins ensitive", + "Ġlab s", + "ĠCa M", + "ĠEu rop", + "ME A", + "B LAST", + "x N", + "al en", + "Ġcl k", + "line age", + "co ating", + "Ġtail oring", + "CON TR", + "Ġadren ergic", + "interp reted", + "N IH", + "am oeba", + "ĠC yr", + "Ġtri plicates", + "def ining", + "ĠLog an", + "tes y", + "ĠTw ist", + "ĠGram mar", + "ĠSustain ed", + "Ġan harmonic", + "Ġal ve", + "Ġr uler", + "Ġqu anta", + "Ġdirec ts", + "Ġoff loading", + "ĠGe ophysical", + "Re quire", + "Ġhepat oma", + "Ġfo o", + "ĠGeor ges", + "Ġb outs", + "ĠT AK", + "Ġanti diabetic", + "ĠRe ported", + "present ing", + "ĠLay ered", + "REN CE", + "Ġuve itis", + "b age", + "Ġf entanyl", + "ens emble", + "ĠO SCC", + "Ġmin ers", + "lo oking", + "ĠBe er", + "prec ipitation", + "ĠEnter prise", + "Ġseroton ergic", + "Ġsees aw", + "ĠAth letics", + "Ġhydroly tic", + "Ġtal ent", + "Ġdiscern ible", + "F IL", + "l ives", + "ĠS ales", + "ĠS Sc", + "ere nd", + "cl im", + "anti d", + "IN TS", + "Ġatten uating", + "Ġtw ists", + "Ġoxygen ase", + "rin i", + "Maca ulay", + "z m", + "ĠP OT", + "ĠM p", + "ĠH ey", + "ĠO VER", + "ill ion", + "Ġinv aluable", + "Ġanti platelet", + "Ġmut ans", + "Ġgrad uates", + "GR AM", + "isp heric", + "Ġincomp atibility", + "Ġcarboxyl ase", + "Ġbioc ontrol", + "ĠPhysic ochemical", + "ï Ļ", + "Ġl ae", + "ĠA ortic", + "ĠR acing", + "ĠE CD", + "iv ic", + "Ġelect romechanical", + "ĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ ĠĠĠĠĠĠĠĠĠĠĠĠĠ", + "Ġste er", + "ĠOut side", + "Ġaden ocarcinomas", + "ĠKe ep", + "Ġcoc on", + "Ġmoder ating", + "Ġreform ulated", + "Ġfundament als", + "ĠTes la", + "ĠStir ling", + "or ated", + "op id", + "Ġpa rox", + "Ġtri valent", + "Ġexchange able", + "Ġdeb uted", + "V ery", + "re ements", + "ĠT omm", + "ĠC yn", + "ĠC atalysts", + "qu at", + "ĠF ER", + "ĠR um", + "Ġsc anners", + "ĠâĨĴ âĪŀ", + "otrop ical", + "Ġven ues", + "estim ator", + "Ġempt ying", + "G PP", + "V IR", + "Ġcom plicates", + "ĠN IS", + "ĠZ hen", + "ĠBl ues", + "Ġtext books", + "Ġsi xty", + "Ġether s", + "Ġfinanc ially", + "Ġm Health", + "ĠT ut", + "Ġl aryng", + "ĠG s", + "ĠW atch", + "Ġse v", + "Ġit al", + "ass ed", + "Ġà ·", + "ĠCons ent", + "Ġnut s", + "vit real", + "Ġmeta phase", + "Ġtit ania", + "Ġfo ils", + "Ġgal ectin", + "initial ize", + "Ġadvis or", + "Ġadminister ing", + "B ool", + "Ġc em", + "Ġre forming", + "Ġg n", + "ys h", + "Ġatt or", + "SC I", + "Ex c", + "bu ilder", + "Ġcer ium", + "Ġregist ries", + "ĠMatsum oto", + "Ġtempt ing", + "is ha", + "Ġre orientation", + "ĠM old", + "ĠR AGE", + "ys on", + "Ġun equiv", + "Ġrel ocation", + "Ġà ķ", + "ĠRe form", + "ĠRE QU", + "Ġcommens urate", + "catal og", + "ĠT PS", + "Ġl amb", + "Ġpre factor", + "arch y", + "Ġdop ants", + "dr v", + "ĠPAR AMET", + "sched ule", + "ochem ically", + "Ġe Health", + "un as", + "ĠP inus", + "ĠH SA", + "Ġinter relations", + "Ġdep ot", + "ĠPl atinum", + "Ġlif elong", + "Ġpersist ently", + "ĠParad ox", + "ĠConform ational", + "es ophag", + "ĠA AT", + "pl in", + "ĠF CN", + "ĠD t", + "op oside", + "Ġch al", + "Ġhal t", + "ĠDet ect", + "Ġdiscrim inated", + "ĠLag rangians", + "Ap pro", + "ĠÈ §", + "Ġimpuls ivity", + "B AT", + "C hemical", + "g ather", + "ĠU NC", + "int ron", + "ĠSim ulator", + "ĠGl a", + "TT T", + "ĠVol atile", + "Ġsubs id", + "ĠBroad casting", + "Ġstrept ozotocin", + "Ġf umar", + "ĠM PEG", + "Ġinflu enzae", + "sub jects", + "Ġappropri ateness", + "Ġarc min", + "Ġstrand ed", + "o ylation", + "ĠD EX", + "ov iral", + "ĠQu arter", + "col ytic", + "Ġfriend ship", + "H ES", + "l oxacin", + "Ġe re", + "ĠT rad", + "ur istics", + "ĠE CT", + "ĠE GCG", + "ĠL RP", + "ĠG AG", + "ĠIn P", + "Ġcont empor", + "Ġmic ror", + "ier strass", + "ĠElect rosp", + "need ed", + "atmosp here", + "n T", + "Ġband widths", + "Ġdivers ified", + "ĠAppro priate", + "rest ore", + "roc nem", + "ĠLag uerre", + "ĠSong s", + "ĠKalu za", + "ĠS ymmetries", + "ĠSch mitt", + "Ġbiom olecular", + "scale box", + "Ġintra hepatic", + "under standing", + "ĠABC G", + "Ġunderestim ates", + "ĠStream ing", + "Ġfic titious", + "oplasm osis", + "res ident", + "ĠB ary", + "ĠCom a", + "sc rip", + "Ġdeg ran", + "ĠCa MKII", + "ĠBal mer", + "ĠPlas m", + "Ġchel ating", + "ĠParad igm", + "Ġopp onents", + "E K", + "P in", + "Ġm sec", + "ad one", + "ach t", + "CC G", + "EC O", + "normal ize", + "ĠDesign s", + "Ġyellow ish", + "glut amyl", + "Ġdomestic ation", + "Ġmonoph yletic", + "d les", + "n ested", + "ĠG race", + "ĠStud ios", + "ĠDisc ussions", + "ophen oxy", + "Ġveter in", + "Ġendos ym", + "utting er", + "b atches", + "ĠF iji", + "ĠR NF", + "ous a", + "ĠK Y", + "Ġspect rograph", + "ER IC", + "ĠMy anmar", + "ĠConst raining", + "Ġecological ly", + "Ġfro st", + "arb oux", + "ĠFib onacci", + "Ġcancel ed", + "ĠISS N", + "R ect", + "an other", + "ĠM MA", + "OL O", + "ĠTr uth", + "Ġorth opaedic", + "Ġtravers ing", + "ischem ic", + "ĠMozamb ique", + "ĠM SR", + "ap ace", + "ĠTh read", + "olog ia", + "Ġcal m", + "methyl transferase", + "ĠÍ ª", + "Ġdro ve", + "Ġcommand ed", + "Ġf eline", + "ĠR ush", + "ĠL isa", + "Ġsuper space", + "arc y", + "ĠReg ulated", + "ĠRest ing", + "caus ing", + "psycho tics", + "q t", + "Ġt ulare", + "Ġrel ocated", + "Ġrep ell", + "Ġpred atory", + "pe ople", + "tra its", + "E uclidean", + "F DA", + "X RT", + "p C", + "p and", + "Ġ Æ", + "re ve", + "Ġb ids", + "Ġco usin", + "Ġsub domains", + "til b", + "é nez", + "linear ly", + "oprotein s", + "Ġcod ec", + "Ġcontrac eption", + "ĠCd k", + "Ġrail road", + "B ench", + "r ng", + "ĠD LA", + "enti le", + "ĠCO CO", + "ĠMat th", + "ĠOver l", + "ĠRout ine", + "Ġmultif ocal", + "Ġarte fact", + "Ġsculpt ure", + "c ies", + "m ate", + "Ġ Ø", + "ure k", + "ĠB end", + "ĠN athan", + "Ġde w", + "ym ia", + "az zi", + "ĠEr k", + "Ġgrad uation", + "Bound ary", + "G ra", + "Ġb fd", + "ĠS ert", + "Ġover shoot", + "ĠSe o", + "Ġsk learn", + "Ġconserv atively", + "pir acy", + "Ġla unching", + "X D", + "ar bitrary", + "per one", + "Ġsh ops", + "comp etitive", + "ĠPak istani", + "Ġcompetit or", + "b iotics", + "ra its", + "ĠN oble", + "Ġsub regions", + "ĠJ ump", + "roll er", + "tr is", + "Ġmac rol", + "ó s", + "ĠPen ic", + "Ġmicros omes", + "Ġimprec ise", + "Ġdownt own", + "Ġe QTL", + "if est", + "ĠM FI", + "Ġr arity", + "âĢĻ âĢĻ", + "Ġbel ts", + "Ġglycos yl", + "ĠNic olas", + "synt hesis", + "O h", + "h ierarch", + "p ps", + "an ets", + "ro ads", + "AT IC", + "MI MO", + "ĠCont ract", + "Le ib", + "opy rox", + "Ġinform ational", + "Syn onyms", + "chall enge", + "ĠProxim al", + "ĠCraw ford", + "Ġis opropyl", + "Ġsub families", + "ĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ ĠĠĠĠĠĠĠĠĠĠĠĠĠĠ", + "Ġannot ators", + "Ġreconc ile", + "Ġparsim ony", + "Ġcasp ases", + "c ott", + "en vironments", + "Ġd rm", + "ĠP IL", + "ĠM ec", + "ĠIn fer", + "ĠSir t", + "S hell", + "ag ulants", + "se ismic", + "Ġsub urban", + "ĠX XX", + "iod es", + "Ġback propagation", + "tra ditional", + "Ġphotoc on", + "ĠMicrobi ology", + "Q T", + "ur idine", + "Ġch op", + "ĠTh é", + "Ġpre jud", + "Ġenc oders", + "col lected", + "rem ark", + "Ġsun spot", + "ĠPhen olic", + "Under standing", + "Ġreject ing", + "Ġrom antic", + "Ġcentim eters", + "Ġhalluc inations", + "H ome", + "c asted", + "Ġc w", + "ra i", + "ĠDis placement", + "PH Y", + "carb am", + "Ġxen on", + "Ġnarr atives", + "Ġdoll ar", + "Ġdyn asty", + "ì §", + "Ġin forming", + "ĠO CD", + "á k", + "Ġoverhead s", + "ju ana", + "ĠKra us", + "f x", + "k aya", + "Ġn id", + "ĠG rab", + "Ġinf lores", + "Ar c", + "======== ====", + "Ġcondens er", + "Ġnanoc ar", + "omm ens", + "Ġsatur ating", + "re ce", + "el if", + "ĠA LE", + "ĠB ub", + "ĠL af", + "and ran", + "Ġpo uch", + "rol ine", + "AC HE", + "CC D", + "Ġcool ant", + "Ġgrass lands", + "ĠSynchron ous", + "izz iness", + "Ġcet uximab", + "Ġdichotom ous", + "ro ch", + "ĠA uckland", + "ob esity", + "ik it", + "Ġoper ad", + "ĠOn set", + "Ġbefore hand", + "Ġunc omp", + "US ED", + "ubb ing", + "ĠSMB H", + "ĠExped ition", + "Ġh ib", + "ĠP PR", + "ĠN ED", + "ud io", + "ĠJ al", + "ĠAr p", + "ĠBe e", + "ĠVari eties", + "Com m", + "Ab out", + "ĠAtt achment", + "ODU LE", + "Calc ulate", + "T an", + "in ism", + "Ġa ra", + "Ġc abin", + "Ġcon nexin", + "Ġcom ets", + "ump tive", + "Ġdest abilization", + "ĠHol t", + "ruct ose", + "anish i", + "plastic ity", + "omyc osis", + "ovic ian", + "________ ________", + "r ar", + "Ġw ore", + "ud ine", + "ĠIn variance", + "Ġper itonitis", + "Ġmet rology", + "Ġclos es", + "Ġcolor less", + "No ise", + "DI O", + "ĠLif shitz", + "z ul", + "es tive", + "ĠM PA", + "ĠB ooth", + "ĠD oll", + "are ne", + "gen ess", + "Ġmolecular ly", + "ĠPer kin", + "Ġdos imetry", + "ĠSO FT", + "ĠPy Torch", + "Ġquar ters", + "ĠKu hn", + "Ġsplen ocytes", + "R W", + "c art", + "le b", + "Ġcon dom", + "ĠH oc", + "Ġext ents", + "Ġsl ug", + "ĠSup plementation", + "diff ic", + "ester ly", + "Y u", + "an tigens", + "Ġà Ĵ", + "Ch anges", + "Ġprop ylene", + "ĠPr ison", + "ĠAlgorithm ic", + "Ġtoler ances", + "Ad am", + "Ġester ase", + "Ġmil der", + "ĠConv ection", + "P TR", + "k pc", + "Ġex o", + "ĠF ah", + "ĠY FP", + "ĠCR M", + "Ġhepat otoxicity", + "Ġnic otinamide", + "Ġpatch y", + "depend s", + "Ġp B", + "Ġe el", + "Ġn v", + "ĠS es", + "ĠH Z", + "Ġim print", + "ep ileptic", + "fl uctuations", + "Ġformal ize", + "che v", + "Ġdip ping", + "ĠPy ramid", + "Ġhol o", + "ĠMT s", + "Ġlamin ates", + "Ġworm hole", + "L AP", + "h ape", + "Ġa k", + "Ġre als", + "Ġby stand", + "Ġinter leaved", + "Ġx z", + "ov y", + "Ġcop rime", + "ucl ides", + "Ġtrim ming", + "MIC AL", + "pyr role", + "I a", + "N LS", + "Q uality", + "t akes", + "z inc", + "ĠP ione", + "ĠE wing", + "ĠL CA", + "Ġà Ķ", + "ict us", + "Ġcoll im", + "Ġphyl ogenetically", + "ĠKe eping", + "ĠFa ith", + "bond s", + "ti ter", + "Ġsub categories", + "sh aded", + "Ġphot ospheric", + "ĠApp earance", + "ĠUnivers ities", + "Ġglomer uli", + "ĠPref rontal", + "Ġprivi lege", + "i H", + "u ya", + "ĠL CL", + "ĠIn GaAs", + "In spired", + "atal og", + "ĠPer ceptions", + "ĠNa HCO", + "Ġstream line", + "tra jectory", + "ĠMic rom", + "Ġbed side", + "ĠRom ero", + "Ġgaug ino", + "D EN", + "F a", + "O lymp", + "e al", + "u els", + "ic ylic", + "Ġg od", + "Ġat taining", + "Ġprot ests", + "Ġnow here", + "des orption", + "ĠHydro xy", + "ĠEr bB", + "ĠSP AR", + "Ġhind ers", + "heren kov", + "KERN EL", + "Ġs ect", + "ul ong", + "Ġpre processed", + "frac tional", + "oy age", + "Ġphosph atases", + "Ġcoast line", + "Ġh ref", + "ĠS utherland", + "ox one", + "Ġhom omorphic", + "D EM", + "Ġb ovis", + "ĠC BP", + "pl en", + "ĠB uc", + "ĠG ior", + "Ġcomp ost", + "ĠO racle", + "ĠSp here", + "ĠSch re", + "deriv atives", + "ly tes", + "ĠY o", + "Ġcycl ones", + "ĠMa ize", + "Ġunf air", + "Tem plate", + "Ġimpregn ation", + "Ġlapa roscopy", + "Ġh amiltonian", + "ign ore", + "Ġdis posable", + "ear ic", + "Ġelect oral", + "cc os", + "ĠSh h", + "Ġturb o", + "Ġintr usive", + "Ġpreced ence", + "annot ated", + "Ġdyst onia", + "F at", + "u ins", + "Ġs way", + "ar izing", + "ill en", + "Ġy i", + "Ġnorm ed", + "ĠÌ Ĥ", + "ĠExt r", + "ĠProte ome", + "Doc ument", + "ĠQUANT UM", + "ti ti", + "ĠC PC", + "ĠM iles", + "ĠB oc", + "ĠR TS", + "CT X", + "Ġsaf egu", + "ĠNorm ally", + "ĠÃľ ber", + "on ious", + "ĠS CE", + "Ġal falfa", + "ĠL ut", + "Ġco ut", + "Ġen large", + "ĠEn able", + "Ġvir ion", + "ĠSh allow", + "def initely", + "ĠCol in", + "ĠRet ention", + "Ġmimic ry", + "################################ ################################", + "NSC LC", + "Ġgrat itude", + "Ġt ending", + "ĠI DS", + "ere t", + "ric an", + "Ġx n", + "ĠY oo", + "Ġoptim ise", + "Ar row", + "ĠTransfer ase", + "PK C", + "ĠGuang zhou", + "r uc", + "y rid", + "is z", + "ĠF IX", + "ĠD atabases", + "ast ron", + "Ġplay back", + "Ġnarrow ly", + "Cor relation", + "ĠAff inity", + "Ġfunctor ial", + "Ġlect ins", + "Ġrup tured", + "Dis play", + "ĠSympt om", + "Ġequid istant", + "ĠRicc ati", + "ĠAchie vement", + "g rand", + "on ated", + "Ġd H", + "ĠF ID", + "ĠD ER", + "ĠCo A", + "Ġgas ification", + "ĠCON S", + "Ġaccompan ies", + "Ġimped e", + "Ġpreced e", + "Ġkit chen", + "prog ress", + "Ġw iring", + "le renes", + "ĠG ius", + "Ġtrans p", + "ret rie", + "ij er", + "aff er", + "Ġbirth day", + "ĠHal d", + "Ġmusc ulus", + "ĠTok en", + "ĠBow el", + "Ġskip ped", + "C ha", + "b v", + "ĠB low", + "Ġpre operatively", + "Ġgl ove", + "ĠLe ven", + "Ġmes op", + "ĠAux iliary", + "ensure math", + "j us", + "Å ©", + "Ġv oter", + "ĠH itch", + "pro xy", + "ĠK ut", + "Ġpo ems", + "ĠAn gl", + "cer a", + "Ġstar red", + "AG ES", + "Sc ience", + "Anal yses", + "Ġrefere es", + "Ġabrog ated", + "Ġdesal ination", + "ĠPrand tl", + "P it", + "Ġn atal", + "og ran", + "ys titis", + "Ġdes m", + "Ġcur ious", + "Ġdem on", + "uz zi", + "ochond rial", + "ĠTreat y", + "Track er", + "rhoe ae", + "L W", + "f urt", + "Ġo mp", + "is ational", + "Ġmem orial", + "ĠLat ency", + "ĠHyp ot", + "Ġglu ed", + "exact ly", + "Ġcontra ind", + "C ancer", + "Ġf fi", + "ĠN AA", + "ĠCh r", + "eg g", + "ĠMo tiv", + "Ġlay outs", + "Ġclim b", + "Ġappend icitis", + "CU DA", + "Ġphotop roduction", + "ĠS IP", + "Ġv eto", + "per in", + "ĠUn ity", + "by ear", + "Ġforward ed", + "ĠDom inant", + "hol z", + "ĠThor acic", + "DEF INE", + "Ġtyros inase", + "B ad", + "I NA", + "f uel", + "Ġg i", + "ĠV IS", + "ast olic", + "Ġox aliplatin", + "eff ector", + "ĉĉĉĉ Ġ", + "е ÑĢ", + "ĠBab y", + "Ġwash out", + "pit uitary", + "N GC", + "Ġd ns", + "ĠP oz", + "ĠU z", + "pos itron", + "ĠElect rons", + "Ġhem angi", + "ĠZn S", + "ĠTE MP", + "ĠExperiment ally", + "fluor ouracil", + "Ġlap arotomy", + "analy zer", + "ocortic oid", + "ĠIMP L", + "ĠDNN s", + "ĠFres nel", + "M ont", + "Ġt apes", + "ul omb", + "im pedance", + "ĠH ET", + "ath a", + "mod ulation", + "ĠCor tic", + "Ġâľ ĵ", + "ĠFair ness", + "ĠSti ff", + "Ġbutt ons", + "c ss", + "Ġand roid", + "el ast", + "ĠT eflon", + "ĠM BC", + "ĠJ T", + "Ġmulti layered", + "ĠRe e", + "uit ar", + "ĠPhil ips", + "ĠSk ip", + "doc toral", + "iy ama", + "ĠLead ership", + "ĠCris is", + "Ġdesens itization", + "v ous", + "ĠS PP", + "ĠP GA", + "ĠN ever", + "Ġdef eating", + "Ġfib romyalgia", + "ĠMR P", + "ĠAB CA", + "ĠLow e", + "Ġer oded", + "Ġaug ments", + "ĠBor is", + "Ġneph rectomy", + "ĠSher man", + "Ġrefrig eration", + "ĠHern ández", + "à ĺ", + "ĠT ors", + "ch us", + "ĠV arg", + "Ġro set", + "CL R", + "DE P", + "Str ong", + "Ġcin erea", + "ĠHein rich", + "R out", + "od us", + "ĠPh one", + "ĠPer l", + "Ġseason ally", + "hold ing", + "Ġencephal omyelitis", + "Ġfasc ia", + "Ġlitterm ates", + "ĠWIT HOUT", + "Ð ±", + "Ġal erts", + "ĠK oll", + "ĠU rs", + "elf and", + "ĠRNA P", + "Ġinvari ably", + "Ġscin tigraphy", + "ĠSebas tian", + "kines ia", + "C UR", + "in ants", + "Ġp ET", + "id ial", + "ĠU PLC", + "Ġsu is", + "Ġbas olateral", + "ĠMod ulates", + "orb ic", + "Im g", + "Ġparas itism", + "Ġlamin ate", + "oge ographic", + "ĠRib eiro", + "ĠGlut athione", + "ĠAber rant", + "Ġs clero", + "ĠD LS", + "ĠR uth", + "Ġrec ast", + "rec ated", + "ok ie", + "ĠPark s", + "Ġfoli ations", + "ĠDaw son", + "Ġtann ins", + "ĠAar on", + "p S", + "it ating", + "ĠI TC", + "ip ients", + "oh y", + "CC s", + "Ġeth anolic", + "cor hynchus", + "Ġorient ational", + "Ġhabit uation", + "Ġconvers ational", + "ĠVent ricular", + "Ġintercal ated", + "Ġphosphodies terase", + "ĠSeif ert", + "w k", + "al gesia", + "Ġst egan", + "ĠL us", + "oph antine", + "Ġcorrec ts", + "ĠOb ama", + "lat ency", + "Ġson ar", + "ORM AL", + "Ġseaw eed", + "ĠPow ers", + "ĠShap ley", + "L ore", + "Ġa wa", + "al ach", + "ĠF on", + "ens ate", + "Ġoptim a", + "IN F", + "Ġpoly genic", + "Ġmes oderm", + "Con ver", + "BR ID", + "ĠHel p", + "ĠRas mussen", + "Ġprokary otes", + "ĠEur asian", + "ĠPerme ability", + "Ġn au", + "ĠC lem", + "od ilation", + "ĠD iaz", + "iti ous", + "ĠCh ad", + "OR A", + "ĠSim ons", + "ĠDist ances", + "Ġast rometric", + "ĠCP Us", + "Ġthi oredoxin", + "perturb ation", + "Ġdendrim er", + "al gal", + "Ġc eliac", + "as z", + "ĠP PE", + "qu a", + "ĠB oll", + "ch r", + "Ġpre view", + "ĠPro jections", + "ĠAs ians", + "ĠInf erring", + "ĠNa ive", + "ĠHig gins", + "ĠLoc ated", + "cardi ac", + "ĠLars on", + "haz ard", + "ĠScienti sts", + "Ġp inn", + "EN CY", + "form e", + "chit ects", + "oflu orescent", + "ĠPor tal", + "Ġpup ae", + "interest ing", + "į Ģ", + "re act", + "at os", + "en in", + "ti o", + "ĠC app", + "ĠM au", + "ĠL SC", + "ĠV lasov", + "Ġsub sum", + "Ġdes erve", + "AS D", + "Rec e", + "Ġconson ant", + "Ġimpregn ated", + "Ġlignocell ulosic", + "Ġs ows", + "le ment", + "ĠT ier", + "ĠM EF", + "ĠH ugh", + "inc k", + "py razole", + "UL ATIONS", + "ĠAL I", + "ĠDr ift", + "Ġsolub ilized", + "Ġdraft ing", + "icycl ic", + "Ġredes ign", + "Ġdelib erate", + "Ġt apping", + "ĠT omas", + "ĠT unneling", + "ĠC BR", + "Ġan odes", + "ĠL SR", + "ĠN ath", + "ros ive", + "ĠHe idelberg", + "Ġcr ushing", + "ĠSh ore", + "Ġmal ondialdehyde", + "ĠMR D", + "ogl oss", + "nc ia", + "Ġgranul oma", + "Ġplain text", + "Ġarteri ovenous", + "Ġrifamp icin", + "Lepid optera", + "O ct", + "Ġl one", + "ĠAp pe", + "ĠInter mitt", + "comp ile", + "pot entials", + "ĠStandard ized", + "Ġventil atory", + "Ġhypercholesterolem ia", + "ĠEVALU ATION", + "k ed", + "x C", + "en os", + "Ġb authorbsnm", + "ĠR ost", + "math open", + "Ġcont ested", + "Ġro s", + "oth o", + "Ġem its", + "ero zo", + "Ġprop ranolol", + "Ġexacerb ate", + "Integr ating", + "ĠWars aw", + "Ñ ĩ", + "re fractory", + "ĠM ort", + "phosph onate", + "GL T", + "ĠChlor ide", + "ĠLU AD", + "ĠSQU ID", + "ĠOBSERV ATIONS", + "Ħ ĺ", + "ag les", + "ug er", + "Ġdiff using", + "yl ar", + "Ġanti p", + "ren ormal", + "Ġshe ared", + "ĠAnd r", + "ympt otics", + "ĠIdentif ied", + "Ġflex or", + "Li ouville", + "ĠCyt otoxic", + "L ock", + "d onald", + "ĠS HA", + "pro jected", + "plic ial", + "Ġbas ics", + "ĠCar valho", + "Ġheter ocyclic", + "Ġfluor ophore", + "ĠIntr igu", + "ĠAnne aling", + "G ln", + "H ispanic", + "Ġs aus", + "ĠT CS", + "ĠH AP", + "Ġy tt", + "Ġcons ulting", + "rec ts", + "Ġinf all", + "LE V", + "tri azole", + "Ġnarrow ed", + "Ġamph oteric", + "ĠSor ting", + "ĠMom ents", + "Ġarab in", + "Ġcocon ut", + "ĠIntrigu ingly", + "Ġp ushes", + "Ġm ec", + "ĠN air", + "Ġcol istin", + "ĠOb tained", + "df s", + "Ġcompet ency", + "W ORD", + "ĠA AS", + "ĠB NP", + "ĠH AS", + "ĠL un", + "ĠL nc", + "Ġhydro cephalus", + "Ġhom ological", + "Ġcarbon ic", + "ĠHi Seq", + "commun ity", + "Ġcephal ospor", + "Ġhos tile", + "prov ide", + "Ġskyrm ion", + "D AG", + "Ġc nt", + "Ġh ay", + "Ġorder ings", + "Ġfl ock", + "HE A", + "ĠNeu rom", + "Ġboost s", + "ĠCard inal", + "ĠBac helor", + "Ġdec ent", + "ĠY ak", + "Ġcalc d", + "ĠBo er", + "Ġtranscript omics", + "Ġrearrang ed", + "ĠPolym orphisms", + "ĠPras ad", + "oinositi de", + "b ars", + "Ġ ãģ", + "ĠS AA", + "Ġon ion", + "ag el", + "ĠH p", + "og rel", + "di visions", + "and an", + "ari as", + "Ġcol o", + "rag on", + "Ġsch izophren", + "âī ¡", + "Ġreplic ative", + "Ġdegener ated", + "Ġsteep est", + "Vol ume", + "I ENT", + "P ublic", + "T en", + "en berger", + "ĠC oun", + "ĠE pp", + "iz o", + "Ġcomplex ed", + "Ġfer roc", + "ken stein", + "ĠJer ry", + "Ġparadox ical", + "x g", + "ic er", + "os ol", + "Ġan nu", + "Ġan kyl", + "ch ung", + "enti ous", + "Ġpres he", + "ene tic", + "ĠHe aling", + "ĠPar abolic", + "Ġfig s", + "ĠKin ematic", + "Ġoblig ate", + "ĠLay out", + "Ġtelem edicine", + "ĠLenn ard", + "p ci", + "ar one", + "ĠZ ach", + "Ġprot otyping", + "ĠMet agen", + "IM AL", + "cons cious", + "Ġquadr ilateral", + "ĠUncertain ties", + "ĠPref ecture", + "G BM", + "r als", + "al us", + "Ġh opes", + "Ġcl icks", + "ĠJ D", + "lect ance", + "Ġpath ologists", + "uss els", + "tis one", + "CP T", + "Ġmis con", + "ĠNeuro de", + "Ġmutagen ic", + "ĠMultim edia", + "Orig inal", + "ĠDra ke", + "P WM", + "Ġp iles", + "st ant", + "AR A", + "ĠR ING", + "mod ifying", + "Ġast rocyt", + "ĠCy st", + "Ġleg ends", + "gluc uron", + "Ġincom pletely", + "ĠConf ed", + "ĠDL BCL", + "ĠPap ua", + "Ġcontras tive", + "ĠSIM ULATION", + "ĠJu venile", + "aggreg ated", + "Ġc GMP", + "ic tive", + "ĠH NF", + "ĠN PV", + "ĠK oc", + "omet allic", + "min i", + "ĠQu antit", + "ĠCor nell", + "Ġded uction", + "Ġcoinc iding", + "ĠIr r", + "Prec ision", + "Ġgins eng", + "õ es", + "j er", + "ĠRe ader", + "ĠBy r", + "cor rections", + "dev ices", + "Ġamb ul", + "Ġped icle", + "ĠDepend ency", + "ĠStri king", + "Ġware house", + "Ġrecirc ulation", + "Ġgonor rhoeae", + "ĠP RES", + "ĠB har", + "Ġfl ushing", + "tor us", + "ĠIR B", + "gly cine", + "Ġmeth amphetamine", + "Ġmir rored", + "ĠWilliam son", + "Ġcath odes", + "hydrox ylase", + "Rad io", + "Ġfurn iture", + "ĠRosen berg", + "ĠNSA IDs", + "s emiconductor", + "Ġas ynchron", + "ĠB erm", + "ĠIn ten", + "ib e", + "For ce", + "path ogenic", + "sm okers", + "Ġdip henyl", + "ĠÐ ¸", + "Ġstand alone", + "Ġlith ospheric", + "Ġtrade offs", + "Ġantic h", + "Ġthym idine", + "ĠMedic inal", + "Ġentrepreneur ial", + "Ġtrapez oidal", + "ĠAs ynchronous", + "tif ying", + "ĠColl apse", + "ĠHE V", + "ĠFro zen", + "ĠTeich müller", + "rocnem ius", + "Ġf ern", + "Ġw s", + "om ol", + "Ġen closing", + "rap id", + "Ġlog ged", + "var vec", + "Ġampl ifying", + "diff erences", + "oton in", + "ĠProm oting", + "ĠFr itz", + "Ġattain able", + "Ġal tim", + "ĠO GD", + "Ġtherm ometer", + "Sol ver", + "ĠBir k", + "LEN BQU", + "ĠGate way", + "Ġengraft ment", + "F IF", + "H SD", + "Ġre structuring", + "ĠT ensile", + "ĠC ele", + "yl us", + "Ġfe ather", + "Ġdr ifting", + "ĠPre clinical", + "yr role", + "Ġcomm em", + "Ġfix ations", + "Pet sc", + "ĠIschem ia", + "a A", + "as oro", + "ĠS ony", + "ĠU t", + "Ġext ensor", + "ĠCh au", + "ĠIs otopic", + "IL I", + "CN P", + "ĠDE F", + "Ġmountain ous", + "Ġsarcom as", + "ugos lav", + "C ALL", + "S ensitive", + "at ro", + "Ġunc oupling", + "sk ew", + "ĠEm issions", + "inn ati", + "Ġconceptual ization", + "Ġow ns", + "Ġsquad ron", + "ĠStreng ths", + "C oh", + "U AL", + "m agenta", + "us b", + "ĠS PC", + "con es", + "ĠSe lecting", + "ĠPar ish", + "Ġvalid ates", + "ĠÍ Ĺ", + "Ġposterior ly", + "omon ad", + "V OL", + "j ectivity", + "ĠC LO", + "ĠV TA", + "Ġun pleasant", + "Ġcare ers", + "Ġautom orphic", + "ĠNan ow", + "Ġaster isks", + "ĠSchul z", + "publ ication", + "Ġb iv", + "Ġr ug", + "rec ognition", + "Ġref errals", + "Ġneur ones", + "ĠCa ffe", + "Con nor", + "ĠShe ffield", + "unit inib", + "ĠAnt agon", + "Ġpneum atic", + "Ġclean er", + "ĠBA O", + "ĠScilab String", + "neigh bour", + "E uler", + "ĠT uple", + "ot y", + "di an", + "Ġy oga", + "Ġev anes", + "Ġstar ved", + "Ġfluct uate", + "ĠBiomark er", + "Ġimpuls es", + "Ġoss ification", + "Ġdemyel ination", + "ĠS AD", + "ess ing", + "Ġred dish", + "Ġsyn th", + "Ġcurv ilinear", + "ĠDen is", + "Ġphone tic", + "Ġham mer", + "Ġepiderm idis", + "Ġplagi oclase", + "Ġ ĉ", + "Ġw olf", + "os ced", + "Ġphot othermal", + "Ġche wing", + "Max imum", + "Ġmism atched", + "ĠFc γ", + "Ġum brella", + "ĠSiber ian", + "ar ra", + "ip ped", + "ym pathetic", + "acc eleration", + "Ġeigen modes", + "ĠEqu ivalently", + "ĠPR ISMA", + "cons ervative", + "ñ ez", + "Ġvolcano es", + "Ġtelem etry", + "m ile", + "ĠB och", + "op rim", + "Ġinc ipient", + "Ġunderstand able", + "atric yclo", + "ĠLog ical", + "ĠQue ue", + "Ġcry ostat", + "defin ecolor", + "ĠS ae", + "Ġar ct", + "Ġso ul", + "ĠHist opathological", + "ĠNeu rot", + "Ġmethan olic", + "P x", + "ĠT itle", + "ot omic", + "ĠE ld", + "ĠE MA", + "Ġde brid", + "tim ulatory", + "ĠZ an", + "Ġnorm ot", + "Ġfluid ity", + "Ġfluid ized", + "pre viously", + "Ġcrack ed", + "ĠExpl aining", + "ĠON E", + "ĠFlor a", + "ĠHybrid ization", + "Ġretic ul", + "F K", + "n otic", + "Ġn A", + "ĠP ab", + "tic um", + "and y", + "ug ia", + "ile t", + "MI NG", + "Ġrest s", + "omp act", + "Ġtrack ers", + "phosph atase", + "ĠTransf ection", + "ĠHospit als", + "ac rine", + "ĠD ell", + "ĠV AE", + "ĠThrough put", + "hev sky", + "ĠSom mer", + "P SA", + "ì ļ", + "Ġb ush", + "Ġl unch", + "ĠS we", + "ĠIn struction", + "ak ami", + "Ġdis infect", + "Ġcor ps", + "ĉĉ ĠĠ", + "Ġprom pts", + "MS H", + "ĠAg rawal", + "Ġlys osome", + "integr in", + "Ġá» ¸", + "Ġnondec reasing", + "ĠRe quest", + "ĠRE P", + "occ us", + "Ġlag rangian", + "oreg ulation", + "оР»", + "ĠBos on", + "I so", + "at ellites", + "res ectable", + "ri v", + "Ġde aminase", + "Ġco heren", + "Ġdec oy", + "ĠExt inction", + "acet one", + "Ġgovernment al", + "Ġcum ulants", + "Ġviscos ities", + "Reg ister", + "document ed", + "Ġimmortal ized", + "D PP", + "G el", + "b ron", + "k ow", + "ĠPro portion", + "ĠCh ase", + "ĠCl ad", + "Ġadap ts", + "ĠCA V", + "ĠÅ ¼", + "Ġpel leted", + "Ġpeng uin", + "ĠZhe jiang", + "feas ible", + "D IV", + "i ya", + "Ġth rowing", + "res ia", + "ĠN r", + "ES P", + "CD F", + "sup pressed", + "Ġtet rachlor", + "Ġaer ospace", + "Un til", + "Ġpay offs", + "Ġtown ship", + "Ġester ification", + "ĠAch illes", + "Ġrac em", + "opyran oside", + "ĠC SM", + "ass is", + "Ġsuper cell", + "ĠReg ime", + "IR A", + "Ġsubsequ ences", + "ĠPen et", + "ĠAnaly tics", + "ĠLV EF", + "Ġbip henyl", + "G radient", + "os ylation", + "ĠW RF", + "of s", + "con ductors", + "Ġback ed", + "pid al", + "ĠNF AT", + "ĠRem ember", + "Ġtel omeric", + "Ġta urine", + "incre ases", + "Ġunint ended", + "ĠNerv ous", + "R as", + "y lyl", + "Ġa estiv", + "ĠS ick", + "ĠThe ta", + "Ġcl iques", + "Ġso fter", + "ĠQ RS", + "llip tic", + "ĠImmun otherapy", + "QU F", + "onom ously", + "ĠFL U", + "ĠIncor poration", + "ĠFormic idae", + "J R", + "w hole", + "Ġc asing", + "Ġn ob", + "ĠD ou", + "Ġint ronic", + "Ġent rapment", + "orb its", + "Ġsal am", + "ĠCR S", + "ĠSw an", + "ĠEd gar", + "Ġconcomit antly", + "atet racyclo", + "ĠA HR", + "tic ks", + "ĠB ing", + "ĠR ift", + "Ġpl ugging", + "Ġsc RNA", + "Ġout reach", + "ins kii", + "Ġcustom ary", + "Ġm d", + "ĠO zone", + "uss ing", + "other s", + "Ġentire ty", + "Ar th", + "Ac et", + "ĠFle et", + "ĠBehaviour al", + "ĠQSO s", + "ar ina", + "Ġpro drug", + "ĠB ros", + "ĠW orth", + "Ġy z", + "con tig", + "ĠAm orphous", + "ĠEr lang", + "Ġhon our", + "ĠâIJ ¥", + "Ġinfiltr ates", + "ĠIvan ov", + "ĠMunic ipality", + "ĠDial ogue", + "t one", + "Ġp ytest", + "ic ulus", + "ĠG oth", + "ĠX C", + "ĠSU MMARY", + "Ġshr inks", + "Ġinvers es", + "i omas", + "ro bi", + "ĠT PR", + "ĠA NA", + "ist ries", + "Ġreg iment", + "ind o", + "ĠRe production", + "lo qu", + "inf lation", + "ET X", + "Ġïĺ »", + "ĠAPP ENDIX", + "Ġwors ened", + "Ġpsori atic", + "Ġmidw ives", + "Ġtouc hed", + "Ë ĩ", + "ĠP atric", + "ĠD ON", + "ĠL IM", + "ak os", + "ĠV ie", + "ĠAn tit", + "Ġfl ake", + "ĠSch le", + "ĠCor onal", + "Ġsal ary", + "sl ight", + "ĠCA F", + "Ġsummar ise", + "Ġflav us", + "ĠBal anced", + "ĠPH OT", + "Ġmil let", + "Ġurg ency", + "ĠGle ason", + "ĠM ie", + "ĠD p", + "ĠG arg", + "Ġle prosy", + "Ġun occupied", + "ĠSt ret", + "ile pt", + "ĠCh or", + "ibr ate", + "ĠÍ ļ", + "ĠPH B", + "Ġmonot er", + "ĠJava Script", + "bt n", + "ĠPuls ar", + "ĠKirch hoff", + "Ġoverse as", + "Ġde phosphorylation", + "ort in", + "ĠPoly akov", + "Ġinsight ful", + "ĠPur ified", + "Ġanch orage", + "ĠGly coprotein", + "stud ies", + "Ġchron ology", + "rox ine", + "ĠNept une", + "B an", + "Ġl ion", + "PS D", + "ĠBar r", + "Ġdon key", + "Ġlikelihood s", + "atche wan", + "ot et", + "os pha", + "tic ism", + "Ġr y", + "ast hen", + "rho tic", + "ĠSub group", + "ye v", + "ĠPat ri", + "provid es", + "S GD", + "b erell", + "v w", + "ĠA ACR", + "Ġsm ears", + "OD S", + "sup plemented", + "ĠEng agement", + "oglob ulins", + "Ġirregular ly", + "ĠSz eg", + "ĠWol ff", + "Ġenanti omers", + "Ġobey ing", + "Ġdestro ying", + "om ially", + "ĠA ti", + "ĠG AT", + "ĠIn variants", + "ĠSc oring", + "Ġhal ides", + "Ġtransform ants", + "Ġforest ed", + "Ġgall ic", + "ĠBet ti", + "thread ed", + "ĠBud get", + "junc tive", + "ĠInnov ative", + "Ġposit rons", + "B razil", + "e ira", + "Ġl avas", + "ĠL t", + "ph oto", + "Ġsp am", + "Ġi h", + "ust ering", + "Ġbi oluminescence", + "ĠSh apes", + "UL TI", + "tri angles", + "ĠSM N", + "enh ancing", + "ĠReduc es", + "ĠTHEO REM", + "D op", + "Ġd L", + "em ptive", + "Ġrem inder", + "Ġgon ads", + "Ġxyl an", + "cult ures", + "t les", + "Ġt d", + "Ġe rected", + "ter one", + "ĠPD C", + "Ġincongru ent", + "Ġmembran ous", + "p ac", + "yl ess", + "Ġsub algebras", + "ĠCh ir", + "ĠZ IP", + "au tious", + "Ġlight ly", + "ĠPhot ometric", + "Trans fer", + "Ġket o", + "Ġexerc ised", + "dispers ive", + "ĠBET WEEN", + "ro u", + "Ġg arbage", + "ĠM af", + "ĠD oming", + "ĠSub space", + "ĠMar ÃŃa", + "Ġtetra hedra", + "ĠBark er", + "S ide", + "b ishop", + "i D", + "re versible", + "orm an", + "ores cein", + "ĠCont rib", + "Ġderiv atization", + "rome res", + "ĠAL D", + "EE K", + "ĠTre ating", + "comb ination", + "ïĺ »", + "restric tion", + "supset eq", + "ĠRAP D", + "Ġamend ment", + "zyn ski", + "Ġc aves", + "il ot", + "Ġabund antly", + "н а", + "Ġinject able", + "ĠReinfor ced", + "ĠWid th", + "ĠHaem ophilus", + "il ane", + "pro ps", + "Ġinter vertebral", + "Ġsc roll", + "Ġam put", + "ĠUn usual", + "Ġstat ically", + "Ġsyn ergies", + "Ġdim s", + "plas mic", + "Ġneutral ized", + "Se lected", + "Ġinher its", + "ĠAutom ation", + "Ġproto planetary", + "Stat ement", + "ĠAPO BEC", + "Ġcertif icates", + "ĠCit rus", + "quadrup lex", + "N ord", + "Ġf ran", + "ĠC arcin", + "ut an", + "ĠP ump", + "ĠB av", + "ĠG ras", + "ting ales", + "Ġcaus ally", + "Ġrad on", + "Comp are", + "Ġclamp ing", + "irre ducible", + "I HC", + "Ġ Ù", + "Ġc yp", + "ĠT PP", + "ĠS uff", + "und ra", + "ĠV illa", + "Ġrel ieved", + "ĠJ CM", + "Ġtreat y", + "IG EN", + "ĠDev onian", + "Ġerythrop o", + "R AP", + "Ġa versive", + "ent ate", + "od actyl", + "ĠPar al", + "Ġmill ed", + "Ġbio informatic", + "okine tic", + "ĠSTR ING", + "ĠPed ersen", + "d atabase", + "in organic", + "Ġde put", + "Ġne b", + "ip ed", + "Ġdiff used", + "oth ione", + "Ġnon stationary", + "Ġunder taking", + "ĠEn abling", + "Ġden atured", + "Ġload er", + "ĠLy on", + "ipar ametric", + "Ġmer istem", + "ĠAngi ogenesis", + "ĠPuls ed", + "Ġex cer", + "ĠD f", + "arc hes", + "Ġcoll ide", + "ĠRel ational", + "ĠNF κB", + "Met adata", + "ĠAdd ressing", + "Ġperc ussion", + "ĠFlore nce", + "Ġnymph s", + "C n", + "st orm", + "ĠG raz", + "com posite", + "ĠAd miral", + "ĠSc otia", + "Ġbre msstrahlung", + "aps ack", + "Ġminim izers", + "Ġmanage able", + "Ġcarboxyl ate", + "Ġintermedi ary", + "ĠBran ching", + "sched uler", + "inoc ulated", + "ĠExtrem ely", + "Ġantenn ae", + "ĠT ill", + "RE SH", + "Ġop acities", + "Ġchem opre", + "Ġaden ylate", + "Ġcircumst ance", + "ĠHash imoto", + "Ä Ľ", + "ce ae", + "ĠF m", + "ĠB X", + "Ġmean time", + "acc urate", + "col linear", + "ACT IC", + "ĠSlov enia", + "F ed", + "K h", + "T m", + "f ork", + "in ology", + "le f", + "ĠD CS", + "Ġher itable", + "Ġann ouncement", + "Ġbusiness man", + "Ġbor tezomib", + "Ġtour ist", + "ĠEt ymology", + "Ġdoctr ine", + "B IN", + "s uffix", + "ar as", + "ĠS au", + "un boldmath", + "ĠM EP", + "ink er", + "Ġoptim ism", + "ĠLe uc", + "eful ness", + "cr ust", + "ĠKe ys", + "ĠâĻ ¦", + "ĠBrand t", + "âĮ ¬", + "ĠSevent y", + "Ġnurs ery", + "Ġdeput y", + "à ¬", + "on is", + "am us", + "ĠC ig", + "Ġex ergy", + "ĠF requent", + "Ġab or", + "ĠJ azz", + "Ġstat ue", + "ĠSc enarios", + "Ġcyt ological", + "fig ures", + "MC I", + "dir name", + "Ġcytokines is", + "del ivery", + "ĠBow en", + "Ġflank ed", + "Ġregener ating", + "ĠFerr ari", + "k iss", + "ĠA val", + "ĠC IT", + "ĠM um", + "ĠL SB", + "og ging", + "Ġun ited", + "Ġtri tium", + "ont amination", + "co ef", + "Ġprop ell", + "tri ple", + "Ġimm ense", + "Ġcompl ained", + "Ġdielectric s", + "ĠCardi omy", + "Ġflood ed", + "ĠCov ariance", + "Att endance", + "T MP", + "Ġs ob", + "ĠS onic", + "ĠF TS", + "ĠR SD", + "ess ors", + "ĠW on", + "iff s", + "Ġflow chart", + "ĠEle mental", + "Ġì ŀ", + "Ġfoli age", + "differenti ated", + "ĠGlob ular", + "Ġpercept ron", + "candid ate", + "S ocial", + "W itt", + "d yn", + "p aces", + "Ġm Glu", + "Ġb anned", + "ol inite", + "ĠF riends", + "ĠL ibraries", + "unc es", + "ĠRe ach", + "ĠSk ills", + "Ġrecip es", + "Ġcann ula", + "ĠOrth odox", + "ĠCarb ohydrate", + "Ġarom atase", + "Åij s", + "Ġeman ating", + "e lected", + "Ġt ense", + "ĠF LC", + "ĠL ET", + "her jee", + "Ġsub band", + "oph one", + "ĠAc tual", + "ms gs", + "EM D", + "IS ON", + "ley ball", + "ĠNi u", + "Ġber ries", + "diagn ostic", + "N ER", + "Ġd Ω", + "per centage", + "ĠH erman", + "ĠG SD", + "Ġsub problem", + "over all", + "oph or", + "Ġdel ocalized", + "acc ount", + "ĠGe ographical", + "dist ances", + "Ġà µ", + "Ġneurot oxic", + "opod ia", + "ĠDic er", + "Ġðx Ãŀ", + "Ġd unes", + "Ġwh it", + "ĠIm mediate", + "ĠÌ ¸", + "Ġadhes ives", + "ĠNS s", + "Ġguess ing", + "ĠColumb us", + "ĠUr ugu", + "behavi our", + "ĠSerb ian", + "benzodiox ol", + "im plementation", + "os ensitive", + "ĠF ill", + "ph age", + "rec overy", + "ES R", + "Ġanaly sts", + "Ġdiss atisfaction", + "band ed", + "ĠDep ressive", + "ĠRT s", + "Ref s", + "mill imeter", + "ĠOls en", + "am pton", + "ĠA CA", + "ĠA vian", + "ĠF owler", + "ub ini", + "est amps", + "ĠPro test", + "Con nection", + "Ġmer chant", + "ĠEN C", + "ĠRy u", + "ĠLymph oma", + "ĠLar ry", + "Ġjaponic um", + "ĠSymbol s", + "L ib", + "V G", + "ĠT av", + "ĠAs sim", + "ĠLe ung", + "depend ency", + "larg est", + "ĠDO E", + "Ġalign s", + "ofl urane", + "ĠAdj usted", + "Ġpeculiar ities", + "decre ase", + "ĠPlac ement", + "v ig", + "z ak", + "Ġp enta", + "Ġf res", + "Ġac ros", + "Ġsol vability", + "ans ions", + "AL A", + "Ġmal function", + "ĠGiov anni", + "A OR", + "H ad", + "Ġp orn", + "und ice", + "ĠU i", + "Ġexp elled", + "ĠAn k", + "Ġdisc ounting", + "ĠReg ulating", + "aster y", + "phen ylethyl", + "Ġcast ration", + "Ġeryth romycin", + "Ġbif unctional", + "� �", + "ĠAlger ia", + "m ess", + "Ġw is", + "ĠT ay", + "ass umed", + "Ġes calation", + "Ġhydro per", + "Ġcall osum", + "Ġatom ization", + "ĠSA W", + "Ġacetyl cholinesterase", + "Ġsucceed s", + "Ġphysi otherapy", + "t ro", + "Ġm ason", + "ĠT MB", + "Ġph ant", + "Ġadjust s", + "anth a", + "ĠEisen stein", + "Ġshorth and", + "G ABA", + "Ġpro ver", + "Ġpat rol", + "ĠMod al", + "oll aries", + "ĠInter facial", + "ĠCI A", + "att n", + "ĠCrypt ococcus", + "athe cal", + "ĠFresh water", + "Ġspectro gram", + "opid ogrel", + "m orphism", + "Ġrel apsing", + "Ġgeneral izable", + "ĠSh ale", + "ĠTrans plant", + "cont raction", + "UR I", + "ĠPet rov", + "ĠSl iding", + "Ġanterior ly", + "Ġquas ilinear", + "Ġrip ples", + "Z P", + "b acterial", + "s pr", + "an imal", + "Ġre porters", + "ĠB SS", + "ĠD ia", + "ĠR SC", + "ound ing", + "IT HM", + "log ical", + "Ġpoly carbonate", + "An imal", + "umb ai", + "Ġarch ived", + "ĠDur ham", + "âĸ Ī", + "ĠVerm ont", + "Ġp w", + "ess en", + "Ġconst expr", + "ĠPr uss", + "Ġsharp ness", + "div ide", + "prim itive", + "Ġacryl ate", + "MY C", + "ĠMond ay", + "ĠSrin ivas", + "B orn", + "at tice", + "om orpha", + "ĠM ERS", + "ĠF actory", + "ĠW N", + "rec tile", + "Ġheat s", + "UN K", + "Ġsynchron ize", + "ĠAtten uation", + "Child ren", + "P at", + "p regnant", + "Ġw ished", + "Ġth awing", + "ĠB ey", + "ĠD ÃŃaz", + "Ġle ather", + "ĠUn ic", + "Ġspecial ised", + "Ġcataly tically", + "PL GA", + "hydroxy ethyl", + "Ġmag mas", + "Ġpron oun", + "Ġeut rophication", + "ĠWeek ly", + "M HD", + "m alloc", + "ec ologic", + "il o", + "ĠF requencies", + "Ġor chestra", + "Ġmetabol omic", + "ĠBlock ade", + "Ġasser ted", + "ĠLew y", + "Ġallevi ating", + "Ġoccl usions", + "Ġchor oid", + "techn ical", + "Ġenvision ed", + "ĠHous ing", + "P n", + "ĠT ECH", + "ĠS SH", + "ĠV alle", + "yl methyl", + "Ġph loem", + "ĠPro jects", + "but ton", + "Ġacceler ometers", + "umn i", + "ĠHand ling", + "Ġvas o", + "perme able", + "Ġc ords", + "ĠC f", + "ĠD z", + "Ġed itions", + "Ġhum erus", + "do ors", + "Ġdors olateral", + "Ġapt amers", + "Ġcommod ities", + "osper ms", + "Ġprednis one", + "I Q", + "M etal", + "t us", + "Ġis otopy", + "ĠThe ater", + "iff i", + "Ġy arn", + "de letion", + "ĠQ PO", + "Ġmulti objective", + "Ġur chin", + "Ġpuls ations", + "ĠSR P", + "ð tÃŀ", + "gluc oside", + "Ġdepart ures", + "Py Object", + "ĠBand width", + "ĠAccept ance", + "re ys", + "ĠI ON", + "Ġcomp uls", + "ĠJ W", + "Ġpart hen", + "Cl ose", + "ĠBa TiO", + "ñ oz", + "aggreg ate", + "Initi ally", + "q h", + "ĠC ancers", + "op in", + "ne ver", + "ism an", + "Ġconst ancy", + "Ġtr ucks", + "Ġvisual isation", + "ĠIll ness", + "Ġsulph ide", + "ĠMetabol ites", + "Ġoxys porum", + "H PP", + "Ġnor adrenaline", + "Ġcommut ativity", + "Qu ad", + "Ni O", + "ĠGet ting", + "Ġba it", + "Ġë °", + "Ġment ally", + "Ġaur oral", + "ĠDraw ing", + "S in", + "re ceiver", + "at ov", + "is otope", + "Ġis othi", + "ĠS enes", + "ĠA CO", + "ĠG CT", + "ys mal", + "ĠV og", + "Ġdist ractors", + "Ġconnected ness", + "Ġaccum bens", + "ä ck", + "hyd rated", + "Ġpharmac odynamic", + "Ġmineral ogy", + "Ġarth ropods", + "Ġmyc otoxins", + "Ġbatt les", + "ĠS ara", + "ĠE IS", + "ĠW inn", + "Ġlimb ic", + "WOR K", + "Å ½", + "Ġe aten", + "ĠT od", + "ap illary", + "ox yp", + "ĠNew ly", + "Ġcam el", + "arr ison", + "ECT OR", + "Ġhop efully", + "ĠHur witz", + "Ġib uprofen", + "ĠFIR ST", + "Ġbist able", + "Ġdismiss ed", + "g at", + "in ogen", + "ĠP ON", + "ph as", + "ĠK orn", + "Ġpoly aniline", + "ĠMic roscope", + "Ġmuc ous", + "Ġcollision less", + "hydrogen ase", + "Bu ild", + "pair ing", + "ĠWI MP", + "built in", + "ĠSepar ate", + "ĠCun ningham", + "ĠNecess ary", + "Ġb ry", + "ec rosis", + "ĠL SS", + "Ġsy philis", + "ĠV id", + "Ġcar rot", + "ĠRes istant", + "reg istration", + "Ġmy opathy", + "Ġang ry", + "MD R", + "Ġhypothesis ed", + "ĠVol terra", + "ele vation", + "Ġmyc obacteria", + "Ġcaud ate", + "i idae", + "Ġ Ç", + "ĠD ich", + "ĠR eth", + "ell us", + "ch amber", + "sh ine", + "och ore", + "ĠCol umns", + "CO UNT", + "Ġïĥ ²", + "ĠPrim ordial", + "Ġnegoti ations", + "sted t", + "R II", + "U ES", + "ti ques", + "ĠP fe", + "Ġpl ast", + "pr on", + "ĠZ w", + "ink ler", + "Ġmetabol ome", + "EG A", + "ĠSpect rophot", + "Ġubiqu ity", + "ĠElectro des", + "Ġchond ro", + "Domain Is", + "ĠResid ues", + "Ġdns DomainIs", + "D IC", + "p th", + "Ġa est", + "Ġc ient", + "Ġp essim", + "Ġre inst", + "ĠS ans", + "end azole", + "ĠU rine", + "Ġsub acute", + "ix imab", + "Ġprof itable", + "Ġmaxim ise", + "ĠDel aware", + "Ġclinic opathologic", + "Thermo Fisher", + "F AR", + "R AS", + "w itch", + "in activated", + "en esis", + "un less", + "ĠP anc", + "ĠM TS", + "ĠB ast", + "Ġch illing", + "Ġinc umbent", + "Ġj elly", + "Ġdistrib utive", + "Ġcy to", + "sc hen", + "Ġinduc ers", + "ĠNone quilibrium", + "ĠRob otics", + "ĠArgent ine", + "Ġmerid ian", + "Ġhun ger", + "Adap tive", + "Ġg or", + "ile psy", + "Ġnon vanishing", + "Ġpe ti", + "ĠMet formin", + "Ġbiom aterial", + "Ġanten nal", + "ĠAff ective", + "ĠAqu atic", + "enedi amine", + "ĠSiber ia", + "ĠPenic illium", + "F unctions", + "Ġ lec", + "Ġf eld", + "ĠS part", + "ĠC ement", + "ad di", + "se k", + "ĠN p", + "oles ky", + "ĠMac roscopic", + "è res", + "Ġcave at", + "Ġcourts hip", + "m ice", + "Ġf ence", + "Ġm ined", + "ul ink", + "ID A", + "Ġtrunc ate", + "ĠCatal an", + "Ġtran st", + "Ġamend ments", + "uncertain ty", + "Ġoroph aryngeal", + "ĠA id", + "ould er", + "ĠInc ident", + "Ġá IJ", + "angi ogenesis", + "ĠBE H", + "Ġic osa", + "ĠFOX P", + "frag ment", + "Ġscintill ator", + "J O", + "L aw", + "Ġp L", + "Ġet oposide", + "Ġpoly aden", + "Ġhabit ual", + "Ġtax i", + "Ġcum ulant", + "Ġhind rance", + "trig ger", + "r atios", + "il io", + "ĠP IR", + "ĠThe od", + "ĠM orton", + "ĠH af", + "ĠO ch", + "ĠEx o", + "Ġur tic", + "ĠCF RP", + "Sc reen", + "Sl ice", + "Ġmush rooms", + "Ġevanes cent", + "S x", + "Ë IJ", + "ì ŀ", + "Ġs igm", + "ic l", + "Ġg uests", + "ĠG IST", + "Ġdeform ities", + "poly acrylamide", + "Sign ificant", + "Ġimpression s", + "j math", + "em oral", + "ĠB n", + "ĠH DR", + "ĠK eck", + "Ġval ine", + "sp i", + "iter ate", + "Ġsyn c", + "oti ana", + "Inter val", + "ĠBra uer", + "Ġstic ky", + "ĠNeuros cience", + "Bax ter", + "Ġc asts", + "all ocation", + "ne al", + "Ġbi op", + "Ġrest orations", + "Im ages", + "mi tic", + "ĠEle vation", + "Ġabst inence", + "ĠLess er", + "ĠRain fall", + "P AM", + "W ol", + "us ch", + "Ġprom isc", + "na ïve", + "Ġded uc", + "acchar ide", + "Ġnom inally", + "ĠExpl oratory", + "Ġreconc iliation", + "linal g", + "T CR", + "Ġs ore", + "ĠN ab", + "Ġout group", + "Ġmon ophosphate", + "ins u", + "ĠAd dis", + "SP R", + "point ing", + "HE RE", + "ĠTechn ological", + "Ġcoch lea", + "Ġspheroid al", + "ĠBald win", + "F eed", + "Ġf using", + "Ġas per", + "Ġex osomal", + "ĠL inguistic", + "SC A", + "ĠEm pty", + "Ġvac ant", + "gly col", + "immun oprecipitation", + "ĠIT ER", + "Sn O", + "pattern s", + "contin ental", + "ĠAcceler ating", + "ĠAver aging", + "Ġchemoattract ant", + "h b", + "s ulph", + "ĠB x", + "Ġcom plicating", + "ĠW are", + "Ġso aking", + "Ġup regulate", + "-------- -", + "Ġsem ester", + "ĠBro d", + "Ġcasc ading", + "ĠCast ell", + "ĠẠ½", + "ĠEQU ATIONS", + "Ġparsim onious", + "Ġs orbent", + "Ġe ug", + "od in", + "ĠW ig", + "ĠTh ir", + "Ġsol v", + "Ġcar boplatin", + "Ġz ebra", + "ven ient", + "Ġmed Rxiv", + "Ġaut obi", + "Ġrepe atable", + "Ġmig rations", + "ĠÐ ´", + "hol onomic", + "Ġmoder ator", + "Ġchim era", + "ĠGrassmann ian", + "ĠR onald", + "ĠV ega", + "ast es", + "Ġqu otes", + "Ġmon ic", + "Ġprec oding", + "ĠAss isted", + "ĠNetwork ing", + "Ġfabric ating", + "Ġbot anical", + "Ġswarm s", + "Ġmartens itic", + "ellip tic", + "pher d", + "b aryon", + "x fe", + "ro ute", + "ĠF IL", + "op ies", + "ĠPC Bs", + "Ġer asure", + "ĠRem odeling", + "Ġana er", + "Sm ad", + "inj ured", + "Ġimmunocomp etent", + "d ell", + "f ailed", + "Ġs inking", + "or acic", + "Ġd red", + "ĠV DR", + "Ġconn ectors", + "Ġintr atumoral", + "Ġcommut ators", + "ĠAle ks", + "ĠDic ty", + "A k", + "Ġre calc", + "Ġis l", + "ot rim", + "nce phal", + "ĠRe es", + "Ġste atohepatitis", + "ĠPolar ized", + "SB ATCH", + "ĠCross ing", + "Acc uracy", + "ĠGi ardia", + "ĠNov o", + "Ġvig ilance", + "Ġphosphatidyl choline", + "ĠUE FA", + "J im", + "Ġf asted", + "ĠT iny", + "Ġl ang", + "iss ociation", + "Aut o", + "ĠNor folk", + "ĠArm s", + "ĠSW I", + "ĠAmb ros", + "transf ection", + "O ryza", + "h arm", + "ĠD s", + "Ġint rag", + "Ġcall er", + "Ġwr itings", + "ĠEl ast", + "ĠMar vel", + "ĠImmun odeficiency", + "ĠMill ion", + "Text ure", + "ĠIce Cube", + "sn ap", + "Ġenj oys", + "ĠChap el", + "ĠEstabl ishing", + "Act ually", + "Ġphosphoryl ates", + "Ġchin ensis", + "Ġrhabd omy", + "Ġemphys ema", + "M iddle", + "n ant", + "Ñ ħ", + "Ġt art", + "low est", + "hem ia", + "Ġutil ising", + "cons tit", + "Ġmag matism", + "о ÑĢ", + "ĠHas an", + "dispers ed", + "H ear", + "Q t", + "z ations", + "al on", + "ĠS tau", + "ĠA mer", + "os ystems", + "Ġdem arc", + "ĠNe oproterozoic", + "ĠMe k", + "ĠDis closure", + "Ġhemat ocrit", + "ĠCyt oscape", + "Ġram ification", + "Ġcommunic ative", + "Ġbutter flies", + "Ġantis era", + "Ġaestiv um", + "B ra", + "L TP", + "s ocket", + "ĠC herenkov", + "Ġch lam", + "ang ial", + "ult ured", + "eng ed", + "ĠCl inton", + "Ġmy oblasts", + "ĠComp ensation", + "ymmet rically", + "Ġemploy er", + "oz ol", + "ĠSA XS", + "Ġretin as", + "piper idine", + "XY Z", + "ĠRough ly", + "P rep", + "Ġb inge", + "Ġe rect", + "ĠO PER", + "Ġstress or", + "Ch rist", + "ĠPD Z", + "Ġsubst an", + "ĠSn ail", + "Ġlam ellae", + "ĠCycl ing", + "shif ting", + "ĠHs ieh", + "ver ify", + "Ġpre image", + "Ġar tillery", + "Ġep il", + "ĠAp ost", + "Ġhel met", + "Ġmach ined", + "ĠMin neapolis", + "ĠCr yp", + "Ġsitu ational", + "pass ing", + "quin azolin", + "ĠCro atian", + "Ġsta ircase", + "Bon net", + "N LP", + "c ium", + "Ġs keletons", + "Ġo xim", + "or ib", + "Ġre ticular", + "ĠS LS", + "ĠA romatic", + "ĠK es", + "Ġph or", + "Ġinv ocation", + "Ġdo zens", + "ai vely", + "Ġdetect ability", + "Ġconcer ted", + "yr ins", + "ĠProcess or", + "Ġtoler able", + "att ached", + "Ġanne xin", + "ĠROS AT", + "ĠAltern ate", + "ĠWa velength", + "ĠWill is", + "Ġsemic ontinuous", + "Ġadvoc acy", + "Ġoblig ation", + "chan ter", + "ĠInser tion", + "Ġsymbion t", + "Z M", + "Ġt ars", + "ro f", + "Ġre vival", + "ĠT ST", + "ĠE MP", + "Ġme x", + "ull in", + "ĠAd op", + "ĠDNA s", + "Ġemploy ers", + "MT s", + "ĠMart ÃŃn", + "electro des", + "ĠMedica id", + "Ġt gt", + "Ġl ognormal", + "ĠF rames", + "Ġper missive", + "ĠAr duino", + "Ġsem ilinear", + "ĠAss ign", + "ĠPr EP", + "ĠSi amese", + "benz imidazol", + "conn ectivity", + "ĠPE I", + "Ġbis ulfite", + "Ġacetyl transferase", + "Ġswim mer", + "ju ven", + "Ġjejun um", + "ĠCinc innati", + "ta i", + "ĠQ I", + "ĠCom mut", + "sp acing", + "Ġaff ords", + "itis ation", + "elastic ity", + "Ġdrag on", + "Ġproteas omal", + "Ġp ant", + "ĠN itro", + "Ġsp ic", + "Ġnan opl", + "ĠAll ied", + "Ġthor ax", + "ĠFT O", + "ĠJur kat", + "chiat ry", + "y oung", + "di rections", + "Ġne ocortex", + "ĠK ik", + "ang o", + "cl ay", + "iod o", + "Ġabove mentioned", + "ĠGu ardian", + "Con jecture", + "ĠTre nd", + "Ġfertil ized", + "ĠSulf ate", + "ochron ology", + "Ġcrani ofacial", + "ĠSask atchewan", + "Q Q", + "h man", + "Ġz ym", + "log s", + "Ġïģ ®", + "Ġgrad uating", + "pin ene", + "Ġî Ģ", + "Ġeti ological", + "ĠComprehens ion", + "Ġw andering", + "Ġl an", + "Ġsy st", + "return s", + "MO F", + "cho alveolar", + "ĠArm en", + "Ġbim etallic", + "ĠPoll en", + "F iles", + "Ġs sp", + "EN SI", + "ĠY us", + "Ġfin est", + "AG EN", + "Ġmicrobi omes", + "Ġpal ind", + "Ġpet als", + "ĠRadi otherapy", + "ophen one", + "spe aker", + "Ġcopep ods", + "Ġkan amycin", + "Ġdegran ulation", + "C onstruct", + "al ter", + "ĠF gf", + "ĠN BS", + "ĠIn complete", + "Ġpar cel", + "ne au", + "Ġà IJ", + "ĠCH A", + "Ġdual s", + "Ġsilic ates", + "ĠGlob ally", + "Ġkines in", + "f id", + "ĠC PD", + "ĠY ad", + "Ġdep ress", + "OD Y", + "ĠHist ograms", + "ĠSumm arization", + "aut omatic", + "ĠDom in", + "otrans formation", + "Ġventric les", + "Wid get", + "ĠPeters burg", + "Ġcholangi ocarcinoma", + "Ġnect ar", + "P IC", + "S cope", + "T ek", + "n itz", + "ĠP HD", + "Ġsp iro", + "ĠCO G", + "ĠDi oxide", + "conduc tivity", + "ĠGran ger", + "ĠWear able", + "ĠKenn eth", + "C CR", + "L INK", + "Ġ Ü", + "re tic", + "ly a", + "Ġdem ocratic", + "Ġradi ograph", + "ĠRel ax", + "ĠInc ubation", + "ĠDen oising", + "COL OR", + "ĠClos ure", + "H MM", + "ur d", + "ra da", + "ĠR v", + "ĠL uz", + "all s", + "Ġmulti spectral", + "IN ED", + "SC N", + "Ġdys lexia", + "Ġsett lers", + "ĠVL SI", + "Ġa vid", + "Ġl arynx", + "ĠC hess", + "ĠF AA", + "Ġdef ender", + "Ġlip olysis", + "ĠEl mer", + "ĠAff ymetrix", + "Ġrhod amine", + "M orph", + "S ite", + "p urity", + "Ġ Ê", + "ĠT ank", + "ĠM iao", + "Ġrec rystall", + "We yl", + "ĠGu il", + "Ġmis folded", + "su ited", + "ĠApproxim ations", + "ĠABC B", + "don or", + "GW AS", + "------------ ---", + "Ġpu tida", + "Ġimping ement", + "yam l", + "H ill", + "Ġt l", + "ag ua", + "tim ing", + "Ġreg enerate", + "Ġmulti lingual", + "rad or", + "class ifier", + "ĠJoh ansson", + "Ġsulf ides", + "ham mer", + "Ġwalk ed", + "Ġalloc ating", + "ĠGust av", + "Ġimmunoprec ipitated", + "ĠBris bane", + "Ġsandwic hed", + "ĠChatter jee", + "omand ibular", + "Ġo sc", + "Ġass ass", + "Ġmulti stage", + "Ġmulti partite", + "Ġpig mented", + "ĠVisual izing", + "Ke ys", + "pip eline", + "Ġdub bed", + "Ġc roc", + "ĠD LC", + "ĠR AT", + "ĠN ex", + "plic a", + "ting ham", + "ĠSp ider", + "Ġunc le", + "aut s", + "ĠHow e", + "Ġarth ropod", + "ĠPap ad", + "urg y", + "Ġaccl im", + "B road", + "ac er", + "ve z", + "ĠD ivers", + "Ġmod ifiable", + "Ġanti psychotics", + "Pro g", + "osa hexa", + "amb rian", + "ĠIon ization", + "Z A", + "o ate", + "Ġp ays", + "Ġe wes", + "Ġbe aches", + "Ġev il", + "ĠCD s", + "na ud", + "Ġconform ity", + "ĠDM N", + "Ġcollabor ate", + "Ġdeterior ate", + "VAL ID", + "ĠVeg as", + "Ġultrac ent", + "B RA", + "R ub", + "Y C", + "f h", + "å ľ", + "ĠO WL", + "ose ismic", + "of errin", + "och thon", + "ĠTNF R", + "small setminus", + "ĠArg ument", + "Ġgranul ocytes", + "Ġram ified", + "Ġepi phy", + "f usc", + "ec dot", + "Ġh w", + "ĠN MS", + "erc us", + "Ġtet her", + "ĠTra it", + "Ag Cl", + "ĠNear by", + "Ġhelmin th", + "Ġlae vis", + "ĠB AR", + "ĠN ancy", + "ĠG yn", + "Ġsec reting", + "St ellar", + "Ġsil hou", + "IM T", + "Ġscaffold ing", + "ĠConver ter", + "h id", + "Ġn ud", + "est rian", + "ann o", + "Ġdep iction", + "orem ost", + "ĠSh and", + "AB CD", + "ĠPD L", + "Ġdys phagia", + "Ġintr at", + "Ġhem ip", + "Ġadapt able", + "long mapsto", + "ss bauer", + "ĠMcC arthy", + "ĠAuto immune", + "ĠCut aneous", + "Inser ting", + "M aterial", + "ĠA a", + "ĠG av", + "Ġmon ocular", + "equ il", + "ĠGe off", + "Ġtet hered", + "obil ized", + "ĠShort ly", + "Det ails", + "Ġrefuge e", + "Ġabsc isic", + "FBQ yx", + "Ġdemoc racy", + "c rafted", + "d ifluor", + "y der", + "ess ment", + "Ġhist opathologic", + "Ġast rocytic", + "Ġwithd rew", + "Ġm oles", + "ath ic", + "mon o", + "man ual", + "Ġfood borne", + "ĠRep ository", + "Ġcover t", + "OT E", + "Ġtight ness", + "Ġinstanti ated", + "Ġwatermark ing", + "Ġartem isinin", + "L anguage", + "O ES", + "c ant", + "al ready", + "un ts", + "iti a", + "ĠK aren", + "Ġall uvial", + "stratig raphy", + "ĠP IV", + "ĠF aces", + "ĠB im", + "ap plications", + "ta ils", + "Ġel d", + "IR B", + "ĠIN TE", + "ĠNot Implemented", + "Ġmis classified", + "Ġfertil izers", + "ĠElectric ity", + "Ġtribut aries", + "ĠDeut sch", + "Ġslee ve", + "f uzzy", + "ĠM TL", + "ĠB res", + "ĠW yn", + "Ġk yr", + "ne uronal", + "ox ymethyl", + "dis order", + "inc hes", + "ram idal", + "Ġpoly imide", + "Res Net", + "ĠEd mund", + "Ġdegener acies", + "uther ford", + "Drop out", + "ij Ģ", + "Ġv oiced", + "ĠG omes", + "iv ities", + "con ductance", + "com pl", + "vec s", + "Ġtun a", + "ĠKin ect", + "Ġconvey ed", + "Ġsphing osine", + "b at", + "ĠP urs", + "ound ed", + "ĠSt am", + "ĠX III", + "ĠCom ics", + "MS M", + "SS L", + "Ġperf luor", + "Ġfluor inated", + "foli os", + "Ġre position", + "ĠS err", + "ĠC ors", + "ĠL abs", + "Ġco x", + "ĠAc quired", + "Ġreason ed", + "Gen ome", + "ĠPi per", + "Ġcompac tified", + "Ġherbiv ore", + "lofen ac", + "Ġb oss", + "ĠB s", + "ĠE MR", + "Ġsh oe", + "Ġcare rs", + "Ch rom", + "SV P", + "ĠTri angle", + "Ġhemat ite", + "dor f", + "ĠMove ments", + "ĠVes icles", + "Olymp us", + "M ol", + "Ġl end", + "ur as", + "ĠA SE", + "ĠW KB", + "pro ved", + "ĠK V", + "ĠU ART", + "log arithmic", + "ĠAD I", + "ĠDo ing", + "Ġce ase", + "Ġleng thening", + "Ġpyrophosph ate", + "F re", + "ĠC LD", + "ĠM LS", + "ĠPl um", + "Ġprop ionate", + "ĠGu atem", + "CK D", + "Ġis os", + "ĠM anning", + "ne uro", + "OP ER", + "ĠWil helm", + "Ġacad emia", + "ACh R", + "ĠIner tial", + "O cc", + "u jan", + "on as", + "Ġin ulin", + "ic ia", + "and al", + "ĠK ahn", + "Ġun manned", + "ĠCo arse", + "Ġgu ilty", + "ĠPe i", + "ĠLuc a", + "ĠFib roblast", + "a vian", + "v x", + "Ġd izziness", + "ĠD ox", + "ĠH our", + "Ġdec oration", + "Ġver ifier", + "rad o", + "Ġfoot prints", + "Ġdisp ensable", + "ĠAna erobic", + "Io T", + "ĠR isks", + "ĠG LS", + "Ġch ords", + "oid y", + "Ġneu rolog", + "ru h", + "Ġvirtual ization", + "Ġproton ation", + "ĠConstant in", + "Ġkeyp oints", + "B uck", + "H opf", + "M uch", + "reg ime", + "Ġprom ised", + "ai j", + "ĠDes ulf", + "ĠForm ulas", + "Ġhum p", + "ln c", + "ĠSu icide", + "ĠHO MA", + "ogly cer", + "ĠProte omics", + "Ġdict ate", + "ĠSper mat", + "F un", + "Ġs ag", + "ĠF am", + "ep pe", + "ĠJ ah", + "Ġar isen", + "oph armaceutical", + "SA GE", + "ĠTH IS", + "enh ance", + "Ġnap us", + "ro e", + "ens ch", + "de formation", + "bon es", + "ĠEr nest", + "ira bility", + "dec om", + "Ġcrust aceans", + "Ġguarantee ing", + "OV As", + "ĠMultic enter", + "Ġct DNA", + "Ġforamin ifera", + "L inn", + "Ġc ups", + "es ch", + "Ġd F", + "ĠT ah", + "pl l", + "pro jects", + "ĠU CI", + "Ġhuman ized", + "Ġabs l", + "ĠSch o", + "Ġliter als", + "ĠSV R", + "Ġtoxic ology", + "pg f", + "ĠIPT G", + "ĠMEASU REM", + "o ing", + "ĠP asc", + "ĠB au", + "ĠW annier", + "Ġhyp re", + "att ributes", + "Ġprecondition er", + "Wr iting", + "Ġgyp sum", + "y uan", + "Ġup regulates", + "Ġte lec", + "ĠDisc re", + "gu ard", + "Ġdeb ates", + "Ġparasit oid", + "L am", + "ti ge", + "Ġis opropanol", + "ĠI was", + "pl ify", + "ind olin", + "ĠAp ollo", + "Ġland ed", + "Ġbeam line", + "Un ion", + "Ġrecipro c", + "ĠRoss by", + "princ ipal", + "Ġdescend ant", + "ĠAnalog ously", + "Ġdereg ulation", + "D SM", + "c ta", + "Ġre built", + "ĠM und", + "ĠF EC", + "ry n", + "plic e", + "ĠY ugoslav", + "ĠNorth western", + "ĠHom ogen", + "ĠLI SA", + "Ġinvest or", + "H SA", + "H PO", + "Ġd ictionaries", + "ĠC ategor", + "Ġcomp acted", + "till ed", + "ç »", + "Ġf ines", + "ur ans", + "Ġbetween ness", + "ĠZ ig", + "sc hema", + "Ġcommun e", + "ĠQu inn", + "Ġana phylaxis", + "TI ES", + "Ġsnow pack", + "ĠDO A", + "ag os", + "ĠO dd", + "ard e", + "Ġev oke", + "ĠOc ular", + "Ġfa ulting", + "Ġvolcan ism", + "ĠPale ozoic", + "Ġmycel ium", + "ĠAdjust ment", + "I CT", + "N ov", + "al ias", + "ĠT ul", + "ĠH h", + "Ġev ade", + "OR s", + "Ġstreng thens", + "ĠUS GS", + "Ġlic ensing", + "ĠCle ment", + "ĠPhyt ophthora", + "r ified", + "Ġe ighteen", + "Ġto ps", + "ĠC LP", + "Ġst abilities", + "ĠP PT", + "ĠB IN", + "ĠR ak", + "Ġgen istein", + "vol ve", + "Ġquick er", + "ĠCaus ed", + "benef it", + "Y B", + "l ift", + "Ġh ood", + "ĠS Cs", + "of a", + "ĠMic ron", + "angi otensin", + "Ġfeat hers", + "Ġantifer romagnet", + "DEC REF", + "yled ons", + "Ġmyri ad", + "Ġ iz", + "ĠT rough", + "âĪ «", + "hem oglobin", + "ĠEn velope", + "ĠCl ick", + "sol iton", + "ĠSyn chrotron", + "Ġlag ged", + "MY B", + "Ġtroph oblast", + "Ġinterrog ation", + "onv uls", + "B ac", + "Ġa periodic", + "Ġg pu", + "Ġpro pidium", + "te ps", + "ĠK arp", + "ĠV az", + "ack age", + "ons on", + "In str", + "fil er", + "rifug ation", + "KO V", + "four th", + "Ġôı¼ IJ", + "hyper bolic", + "sche tz", + "Disc ussion", + "ĠOrient ed", + "j ad", + "Ġa uctions", + "us ivity", + "ĠC ran", + "Ġk d", + "Ġint est", + "ros arcoma", + "ugg er", + "ĠIL P", + "ĠST A", + "Ġrevers als", + "Ġgrap es", + "ĠPop ulus", + "ĠKit aev", + "ĠAV P", + "Pre viously", + "Ġquadr atically", + "ĠLOC AL", + "B ert", + "P ED", + "l ive", + "à ¬", + "Ġb idding", + "Ġto ss", + "ent o", + "Ġth ylak", + "Ġcomp rehend", + "Ġdi ve", + "Ġapplic ants", + "ĠÄ ħ", + "ĠVol canic", + "adap tation", + "Ġá¹ Ģ", + "ĠJans sen", + "Ġadjo ining", + "ozol omide", + "C IS", + "d C", + "duc ted", + "ĠAn ast", + "ĠEm ployment", + "ĠEnd ocrine", + "sil oxane", + "S ession", + "ĠN arr", + "ĠâĪĴ âĪĨ", + "de ev", + "oth iaz", + "ring ing", + "po inted", + "Ġacet ylene", + "Ġglob ulin", + "pack ing", + "ĠUs es", + "A ES", + "H en", + "ĠS avage", + "ĠC anc", + "ist o", + "ĠChrom osomal", + "Ġcement ed", + "Ġpyro x", + "ĠConstit utive", + "Ġphthal ate", + "mechan ism", + "Ġcyclospor ine", + "P AP", + "ar ted", + "ĠR DT", + "Ġpl ains", + "Cl one", + "prop anol", + "regular ity", + "Ġcot angent", + "ĠLes lie", + "ĠNit rate", + "ĠKaw asaki", + "ĠPage Rank", + "Ġanhyd rase", + "ĠKrish na", + "Ġhemicell ulose", + "Ġ ery", + "ll is", + "Ġmicro gram", + "ĠDel igne", + "Ġenfor ces", + "Ġthrombol ysis", + "P arse", + "or vastatin", + "Ġm ated", + "ĠC rystalline", + "Ġaut oradi", + "Ġtherm ophilic", + "inf ectious", + "Ġult ram", + "ĠML L", + "ĠFib ers", + "Ġulcer ation", + "omed ial", + "stratig raphic", + "Ġtouc hes", + "r he", + "Ġt ame", + "ĠC ulic", + "AR DS", + "ch ter", + "Ġcounter clockwise", + "Ġcam ps", + "VD C", + "Ġmeth adone", + "depend ently", + "valid ate", + "Ġprecl udes", + "Ġparliament ary", + "ĠINTE REST", + "ĠS erg", + "ĠC BC", + "ere lla", + "ay i", + "ĠR AB", + "Ġch ym", + "Ġnan ospheres", + "Ġdiab etics", + "cons ervation", + "Ġperme ate", + "plot ted", + "Ġna phthalene", + "ĠBon n", + "ĠElectro static", + "Ġinvent ories", + "Gaussian ity", + "ĠAden osine", + "Del ay", + "ĠBegin ning", + "Ġs ided", + "ĠC ushing", + "ĠH v", + "Ġco ined", + "ĠAl m", + "sc anning", + "fer til", + "Ġα v", + "ĠRe activity", + "Ġproxim ate", + "depend encies", + "Ġdens ification", + "Ġôı¼ ij", + "Ġbacteri ocin", + "weak ly", + "Ġdenti stry", + "ĠOri ental", + "Ġdorm ant", + "Ġp C", + "Ġm um", + "RE s", + "Ġcon val", + "Ġbi ota", + "Ġmulti linear", + "ĠPT FE", + "Ġnarrow band", + "ĠRot ational", + "Ġhoney bee", + "ĠChlor ophyll", + "Bas eline", + "F ern", + "Ġl k", + "ĠM ash", + "ri ved", + "ĠB ases", + "ĠD ah", + "ĠK ui", + "Ġà ĵ", + "ĠRec ycl", + "AG N", + "PD E", + "Ġclim atological", + "ĠBas ically", + "cons erved", + "abs orbing", + "ĠKos zul", + "ouss ines", + "Ġm dx", + "ith ymia", + "ĠH inton", + "Ġk h", + "Ġad mittance", + "ĠV y", + "Ġext rema", + "Ġcre ftype", + "sub st", + "Ġble omycin", + "LINE AR", + "A Q", + "i om", + "Ġn ong", + "op ian", + "se in", + "ud al", + "Ġear ning", + "Ġstandard ize", + "ĠPar ticular", + "Ġwave vector", + "dx dy", + "ĠMac Donald", + "ĠEst uary", + "valid ated", + "ĠHur st", + "ĠMuk herjee", + "Ġbival ves", + "Ġjug ular", + "U b", + "v ill", + "en ough", + "Ġin forms", + "an atomical", + "ul ou", + "res a", + "ĠP MC", + "ĠM ira", + "ĠR PL", + "ĠSD C", + "Ġhem i", + "Mo S", + "ĠFlo at", + "Ġoccl usal", + "ĠRain bow", + "ĠProvid ing", + "Ġsupercapac itor", + "os f", + "ĠI RT", + "Ġad m", + "Ġdec oders", + "ĠX R", + "ĠRes cue", + "Ġent om", + "Ġmor tal", + "An gle", + "Ind ia", + "ĠMal i", + "Ġinsp ecting", + "ĠGALAX Y", + "ĠEri ks", + "Y F", + "r ings", + "Ġs ir", + "Ġg sl", + "ĠB ubble", + "ĠD CA", + "ĠW idespread", + "ass ignment", + "Ġge omorph", + "ĠPre ference", + "CO PD", + "process ors", + "cut off", + "ĠFlow er", + "phen omen", + "mus ic", + "ĠSlov akia", + "Support ing", + "b low", + "ed it", + "ĠT rophy", + "ĠA SF", + "ĠM oses", + "Ġind els", + "Ġnon human", + "Ġhand ic", + "Ġrepair ing", + "Ġmicrom eter", + "ĠPhilip pe", + "Ġexud ates", + "ĠâĹ ĭ", + "Ġamalg am", + "K in", + "f ors", + "f ron", + "Ġan abolic", + "ĠE ich", + "NA N", + "Ġpseud ogap", + "analy zed", + "Ġtack led", + "agin ous", + "Ġlubric ant", + "Ġradion uclides", + "arrest in", + "oussines q", + "L if", + "Î ¥", + "re ceived", + "as tive", + "ĠP BC", + "Ġam oxicillin", + "cop per", + "ubl ing", + "oph ages", + "ĠSe as", + "ĠEl ite", + "PM MA", + "Ġchol ang", + "Depend ing", + "Ġas bestos", + "ĠF ecal", + "ĠR ath", + "ĠL ey", + "Ġfact ored", + "bb les", + "Ġtoken izer", + "Ġofficinal is", + "ĠNUC LE", + "ĠS emicon", + "ĠB ous", + "ĠR is", + "Ġlo ans", + "AC P", + "âĻ Ģ", + "phos ate", + "Ġc herry", + "an an", + "ar re", + "ĠC redit", + "ise xual", + "ĠAc ta", + "ĠLet ting", + "ĠInf arction", + "ĠAcc ounting", + "Ġcounter stained", + "Ġaer ogel", + "standard ized", + "Ġly ase", + "seg ments", + "Ġbac helor", + "Ġh ue", + "ĠN ETs", + "Ġun adjusted", + "Ġmicro hardness", + "Ġsingle ts", + "ĠSP ACE", + "ĠHyd raulic", + "MET HOD", + "ĠBj ör", + "ĠK U", + "Ġrep ur", + "Ġradi ocarbon", + "Ġheter ogeneities", + "Ġgast rocnemius", + "ĠLT D", + "Ġaccident ally", + "Process ing", + "Dop pler", + "T BI", + "Ġl ingual", + "ĠA GS", + "ĠF rontal", + "ĠB rack", + "the ma", + "Ġrepresent able", + "Ġpress urized", + "AD R", + "ĠMicro fluid", + "Ġê °", + "Ġreus able", + "Ġv endor", + "all er", + "Ġdi version", + "FA ST", + "ĠKir by", + "ĠStim ulus", + "Ġattach ments", + "ĠBrid ging", + "ĠRober to", + "Ġqueu ing", + "t ling", + "ro ots", + "ĠM x", + "ĠM arrow", + "ĠL ocus", + "Ġun important", + "erg arten", + "ÃŃ k", + "ĠPot ent", + "ĠBruns wick", + "ĠS CT", + "ĠM our", + "em ias", + "ĠN CS", + "ch icine", + "ĠO ryza", + "Ġwhere ver", + "ĠX GB", + "CO X", + "Ġhydrogen ated", + "Ġhyd raz", + "ĠPers ons", + "Ġframes hift", + "Ġelectroly tic", + "ĠSen egal", + "Ġphag ocyt", + "Ġinstantaneous ly", + "ĠGround water", + "Ġimper ial", + "ĠRhod e", + "ÅĦ ska", + "ovis ual", + "onts ize", + "ĠExplan ation", + "Ġempower ment", + "N TA", + "P u", + "P or", + "S ched", + "e ats", + "Ġ ys", + "in ous", + "Ġw ilt", + "ĠM ov", + "ect on", + "ĠG ins", + "int roduction", + "ince ption", + "ĠInter preting", + "Ġstart up", + "Ġalb ino", + "Ġtet ras", + "ĠHouse hold", + "ĠEL M", + "Ġspor ulation", + "Ġosm ol", + "B is", + "er ule", + "ĠE AR", + "Ġim balances", + "Ġk t", + "Ġj l", + "ges terone", + "eral a", + "ĠPo inter", + "ĠHR QoL", + "ĠRi et", + "ĠEsc ape", + "pur ified", + "Ġinstanti ation", + "m atis", + "ion a", + "Ġn oxious", + "ĠN og", + "Ġj am", + "ĠAnt oni", + "ĠGod d", + "ĠPersonal ized", + "Ġperm uted", + "ĠS HE", + "ĠO blast", + "ĠFor bes", + "ĠRes veratrol", + "ĠFe Se", + "Ġelectro deposition", + "Ġhome obox", + "Ġpy ogenes", + "Ġviol in", + "Ġiso electric", + "ĠPP G", + "prob ably", + "AMP K", + "ĠWol fe", + "Ġultraf ine", + "B eyond", + "on at", + "ed ian", + "EN ABLE", + "ĠH AM", + "so ut", + "ĠOp inion", + "rin ted", + "typ ing", + "Un known", + "Ġbuck ets", + "Ġintuition istic", + "algorithm s", + "S SC", + "b ir", + "ĠP ond", + "ad vert", + "ip in", + "Ġup wind", + "ĠCl aire", + "ĠMat uration", + "ĠPr P", + "OP O", + "FORM ANCE", + "Ġd M", + "ĠC ities", + "Ġinter related", + "ĠAp paratus", + "Ġprec ious", + "cript ors", + "Ġprepared ness", + "ĠAR CH", + "ĠPath ogens", + "HO ST", + "ĠGib bons", + "Ġirregular ity", + "ĠLip ids", + "Ġcf u", + "Ġvas odilation", + "imet re", + "impro ved", + "m q", + "ĠH ens", + "ĠL oci", + "unc redited", + "Ġmulti grid", + "tig o", + "Ġaccount ability", + "ench yme", + "Ġdisadvant aged", + "Ġbisp henol", + "Ġt ic", + "Ġfor ks", + "ĠW ester", + "ĠV ii", + "ĠJ ere", + "sim ultaneous", + "ĠGu arant", + "ĠDo yle", + "Ġpotenti ates", + "lass ified", + "Ġile al", + "Ġvasoconstr iction", + "M ODULE", + "N ano", + "W ood", + "ĠT AT", + "ur ious", + "un ya", + "Ġins tillation", + "ĠSim mons", + "ĠDi rectional", + "Ġmal ate", + "Ġplant ation", + "Ġuns olved", + "ĠTa uri", + "Ġov ine", + "Ġkeratin ocyte", + "ĠKull back", + "ĠKazakh stan", + "Ġh irs", + "ĠA erobic", + "ĠH ai", + "ĠR iley", + "ens ible", + "Ġinter planetary", + "Ġtrans its", + "Ġgener ous", + "Ġcal pain", + "Ġapp ended", + "ĠHydro dynamics", + "Ġcolon ize", + "Ġheart beat", + "Ġmetast as", + "Ġpy reth", + "ĠPA K", + "ĠÐ ¡", + "multi plet", + "ĠBrad y", + "Ġpropri a", + "ĠFron tier", + "ĠJoy ce", + "ĠP GF", + "ĠM cl", + "rec urrent", + "ĠRe placing", + "inf erence", + "ĠWh itt", + "Ġschool ing", + "ĠHa rold", + "Ġabst ractions", + "âĬ ķ", + "mem cpy", + "Ġmicron ucle", + "Ġradion uclide", + "ot yl", + "ĠM IF", + "ĠM US", + "Ġex foli", + "ĠF amilial", + "Ġcl am", + "ON O", + "Ġvan illa", + "Ġpast oris", + "ĠAT L", + "ĠBur sts", + "Qu antitative", + "Ġelic iting", + "Ġgranul omatous", + "Ġbrow sing", + "t racks", + "Ġh ij", + "ĠB CP", + "inc omp", + "az id", + "ck pt", + "Ġlink ers", + "Ġsqu id", + "Ġhead aches", + "ĠMor al", + "Ġstabil isation", + "&& &&", + "ĠSu fficient", + "ĠArch aea", + "Ġì ł", + "ĠLuc iferase", + "Cam era", + "expand ed", + "Ġmyster ious", + "H PS", + "ĠB J", + "ĠK NN", + "Ġsuper hydrophobic", + "ĠHydro thermal", + "ĠRus so", + "ĠArsen ic", + "Ġnormot ensive", + "ul timate", + "ĠC MIP", + "ex amined", + "Ġmicro porous", + "Ġfore ver", + "ĠST ING", + "IG S", + "ĉĉĉ ĠĠ", + "Pl ant", + "Ġcoherent ly", + "charg ing", + "Ġinher it", + "altern ative", + "ĠBap tist", + "F m", + "b ipy", + "Ġo ler", + "ĠSub stit", + "Ġult rap", + "free ze", + "perg ill", + "POS E", + "Ġadvertis ements", + "ECH AN", + "Bay esian", + "Ġcob ordism", + "¸ °", + "ĠA ER", + "ĠA IP", + "ĠL NA", + "ess entially", + "rec iprocal", + "ĠAn and", + "Ġsm eared", + "ones e", + "ethyl amine", + "ĠER S", + "Ġjud icial", + "Ġwood land", + "ĠGre gor", + "Ġtab ular", + "avir in", + "mir ror", + "Ġja undice", + "astig otes", + "ĠL GBT", + "ĠN aj", + "Ġsub scheme", + "Ġmulti user", + "Ġdrain s", + "Ġevac uated", + "phosphor yl", + "ĠFeld man", + "ĠTRI zol", + "ĠBLE U", + "a romatic", + "o viÄĩ", + "p ion", + "re pr", + "ro th", + "ĠF ES", + "ĠL eeds", + "Ġun g", + "ob ranch", + "Ġpat ency", + "ĠSc r", + "ĠSim plex", + "pec ies", + "Ġbenef ici", + "Ġpolymer ases", + "ĠCy gn", + "oct adec", + "Ġpunct ured", + "Ġjaponic us", + "ĠFPG As", + "f rown", + "Ġe b", + "ut iny", + "ĠP oy", + "ĠB rent", + "ĠB AM", + "ĠH ick", + "ĠN PS", + "ĠG DF", + "ĠV IRT", + "Ġinter l", + "Ġsc Fv", + "Ġte amm", + "Ġparticip atory", + "Ġexist ential", + "Ġoste omyelitis", + "Ġpneum othorax", + "std out", + "Ġsinglet ons", + "hyp othesis", + "strat ified", + "U SD", + "on asal", + "er is", + "im its", + "ĠI Cs", + "ĠE ncephal", + "iz i", + "ĠG radients", + "Ġall op", + "Ġcor p", + "con structed", + "Ġmon ument", + "sim ulator", + "ĠFerm ions", + "ĠWy oming", + "Ġprednis olone", + "L ang", + "N otes", + "e er", + "Ġf ighter", + "ent rant", + "ĠN ij", + "ĠG PD", + "ĠPro l", + "Ġreal isation", + "Ġpack ings", + "ĠDisc overing", + "ĠAng lo", + "ĠCass ini", + "exec ute", + "Ġinhab ited", + "ac ross", + "ĠC ram", + "ĠN BR", + "ant es", + "Ġdis persing", + "ach andran", + "ĠU ND", + "Ġshould ers", + "Ġcr ises", + "ustr ine", + "Ġprop ane", + "UN E", + "br ush", + "Ġeti ologies", + "Ġshot gun", + "show ing", + "ĠPhyt ochemical", + "ĠMeh ta", + "orr hea", + "ĠImag ery", + "T re", + "w c", + "Ġe luent", + "ond in", + "ĠAt titude", + "Ġfer romagnet", + "Ġcounter measures", + "Ġalk anes", + "ĠCap illary", + "lat ent", + "Ġsolub il", + "View er", + "áz quez", + "ĠPunj ab", + "a as", + "t ang", + "Ġim ports", + "ĠY ounger", + "rough ly", + "We inberg", + "ĠAt kinson", + "bf a", + "MP a", + "ste el", + "PC P", + "chlor inated", + "ĠPsych ometric", + "Ġpyro ptosis", + "Ġwat ched", + "ĠPerc utaneous", + "R BD", + "V ARI", + "at u", + "ĠW ake", + "Ġcan yon", + "ip arous", + "Ġsc all", + "com pletely", + "inter fer", + "ophy ceae", + "Ġfatal ities", + "cz ak", + "ĠPathophys iology", + "L em", + "l ach", + "t uary", + "Ġa lex", + "Ġs isters", + "Ġp um", + "ĠC atch", + "ĠE ber", + "ine x", + "ph the", + "Ġbo ar", + "ĠSo ul", + "Ġcat fish", + "Ġcloud y", + "ĠBu ilt", + "ophyl line", + "ĠRib osome", + "ĠAnomal ies", + "Y D", + "c ategorical", + "w or", + "op enta", + "ĠL IB", + "Ġr ick", + "Ġradi ations", + "Ġhyper cube", + "Ġmal treatment", + "Ġî ĦĦ", + "dispers ity", + "contin ent", + "Dig ital", + "ĠCory neb", + "Ġre vert", + "ĠT EA", + "ĠM LR", + "ĠF CM", + "ĠL amp", + "iz abilities", + "Ġcar ved", + "ĠMon oclonal", + "Ġpen is", + "ĠMor ales", + "En ter", + "ester ification", + "Ġcab bage", + "RAN TIES", + "Ġdebrid ement", + "L ead", + "c AMP", + "Ġc esium", + "ĠC ubic", + "Ġun imodular", + "ĠEx port", + "Ġanalys er", + "den otes", + "Ġrad ically", + "ĠHist ology", + "Ġmelan omas", + "Ġwors hip", + "ĠHimal ayan", + "ĠInte grable", + "benzenesulf onamide", + "Ġharb ored", + "P utting", + "ĠT ir", + "ĠU TI", + "cent ers", + "ĠPl uripot", + "Ġhar bors", + "Ġcarb am", + "ĠApp alach", + "ĠJo an", + "ĠCommission er", + "ĠGem ini", + "N ear", + "O PS", + "Q G", + "p ytorch", + "st aining", + "Ġh CG", + "Ġg avage", + "per haps", + "ĠG rib", + "ĠZ ah", + "Ġcompar ably", + "ĠBi oscience", + "SP L", + "Con nell", + "ĠAir way", + "prim ed", + "Ġsubmuc osal", + "Enh anced", + "Ġwis dom", + "V N", + "ĠM umbai", + "ri us", + "ĠR GD", + "ĠR Neasy", + "ma i", + "ĠAD L", + "Ġadop tive", + "Out lined", + "ĠWAR RANTIES", + "ĠViol ence", + "Ġcater p", + "F und", + "d θ", + "ĠP ok", + "ĠB enson", + "ĠR IG", + "ĠV s", + "Ġinst ants", + "ĠMulti drug", + "PD MS", + "CON ST", + "Ġcart ridge", + "ĠLif estyle", + "ĠCOND ITIONS", + "odys plastic", + "CONTR OL", + "L HC", + "ti re", + "ĠS tain", + "Ġy x", + "Ġj unctional", + "ob o", + "ann ah", + "ĠCP AP", + "Ġsound ness", + "ĠUl timate", + "sil icon", + "Ġparal og", + "E vents", + "G as", + "J E", + "ĠJ orge", + "Ġover production", + "Ġmax illa", + "ĠRe asons", + "we eks", + "ĠNe arest", + "Ġhead space", + "ĠAT C", + "bal ancing", + "Ġjud ging", + "ĠUnivers ality", + "Ġsinus es", + "Ġretro peritoneal", + "Det ection", + "Ġhydrolys ate", + "H och", + "w rapper", + "Ġp Ka", + "Ġl asso", + "ĠA lu", + "ĠA PR", + "ĠD ors", + "ĠD arboux", + "ĠR FS", + "ĠK har", + "ĠTh rom", + "Ġdesign ate", + "arc o", + "Ġtherm ostat", + "ĠGl acial", + "IF F", + "ĠMan ifest", + "Ġinters persed", + "haus er", + "ĠDD X", + "Ġa le", + "ti des", + "Ġl accase", + "ĠH ered", + "ĠR acial", + "ĠK ats", + "aj o", + "ĠCo ordinated", + "ĠProb ably", + "Ġtit anate", + "SL AM", + "dri ving", + "ĠEmerg ent", + "ĠDri ves", + "Ġoblig ations", + "Ġnebul ae", + "f ried", + "ith in", + "ĠP GD", + "oc clusion", + "ĠU H", + "Ġsub routine", + "oid in", + "Ġann oy", + "ĠVir asoro", + "inst ances", + "ĠDer by", + "Ġtriang ulations", + "Ġcutoff s", + "ĠOrganization al", + "ĠVen k", + "ĠEG TA", + "ĠDeut sche", + "Ġantine ut", + "ĠVulner ability", + "i ated", + "Ġa vium", + "Ġh sp", + "em ulsions", + "ad herence", + "ĠU PS", + "ma ph", + "ĠV AP", + "rel atively", + "Ġmax ill", + "oph ase", + "Th reshold", + "ĠSup p", + "eth oprim", + "Ġpenet rated", + "Ġblast ing", + "ĠAdvant ages", + "B US", + "ol son", + "rec v", + "Ġcar nitine", + "ten ing", + "Ġprov oked", + "vari ous", + "ĠCal ab", + "len eck", + "ĠPark in", + "Ġblow up", + "ĠDW I", + "synt hesized", + "Ġdisproportion ately", + "Ġcardio respiratory", + "ĠXanth omonas", + "Ġpunc ta", + "bdd c", + "ĠP ACS", + "ase g", + "Ġinc urs", + "ost a", + "ĠJ L", + "ĠWe ierstrass", + "ole ucine", + "Ġfin als", + "Ġcaus ation", + "ĠDi rective", + "ĠPor to", + "ĠFlo res", + "arbon yl", + "---------------------------------------------------------------- ------------", + "histor ic", + "K ähler", + "on na", + "Ġc el", + "ĠT BA", + "ĠO phthal", + "Ġsub threshold", + "Ġlip s", + "ĠSub strates", + "Ġpen insula", + "Ġads or", + "Ġdry ness", + "mass es", + "è me", + "stro k", + "ĠExpand ed", + "Ġg c", + "ĠG olf", + "Ġcri tique", + "ĠÍ ©", + "Ġlens ed", + "ĠKing ma", + "ĠGold man", + "ĠFac ile", + "Car l", + "Ġchond rites", + "ĠCoh omology", + "ĠSocio economic", + "ĠDominic an", + "ĠAzerbai jan", + "ĠA ne", + "ĠM idd", + "ĠN ed", + "Ġem ulate", + "ĠSh akes", + "Ġlik ed", + "Ġbuild up", + "Ġexcess ively", + "ĠÅ ¶", + "ĠAdap ted", + "Ġauthentic ated", + "Ġlocom otive", + "Ġsubm ill", + "Ġinterpre ter", + "ĠVibr ational", + "R α", + "l aden", + "p kl", + "r w", + "y et", + "en zymes", + "Ġw av", + "ĠN MT", + "ath ion", + "Ġbi otechnological", + "arc s", + "Ġact uated", + "Ġher ring", + "EC G", + "OC I", + "Ac tivated", + "Ġpara ph", + "Obs ervation", + "ĠEk man", + "ancell or", + "veli hood", + "G auss", + "H AL", + "r dev", + "t bl", + "ic its", + "ĠR oux", + "op ram", + "Ġser opositive", + "ĠPhys ically", + "ĠEd u", + "Ġdocument ing", + "ĠÐ ¾", + "ĠSmall er", + "cher y", + "Ġlanth anide", + "T oday", + "Ñ Ĩ", + "Ġo titis", + "ĠC ores", + "if olium", + "ĠZ el", + "Ġtim ings", + "co arse", + "rep air", + "ĠLD PC", + "Ġbow l", + "ĠEpid ermal", + "Ġâľ ²", + "Ġsynonym s", + "Ġ ENT", + "Ġb illiard", + "Ġe jac", + "ĠB AA", + "Ġsc ientif", + "Ġγ γ", + "Ġcaps ular", + "Ġaz ithromycin", + "Ġcred entials", + "ĠḠł", + "ĠGli oblastoma", + "Ġunco ated", + "Ġhib ern", + "ĠT os", + "ĠB aro", + "ĠK ass", + "yl and", + "ĠX M", + "Ġagg ra", + "Ġneutral ize", + "lic ted", + "Ġsound track", + "ĠKn ud", + "ensors hip", + "emp fer", + "ĠHald ane", + "ĠR ocks", + "ĠG ou", + "ĠO pi", + "ib acterium", + "ep tives", + "ust a", + "par s", + "uk awa", + "be in", + "eli us", + "aver aging", + "ĠMW CNT", + "Ġshield ed", + "Ġquatern ionic", + "Batch Norm", + "Ġd ella", + "ĠT p", + "Ġby product", + "ĠG ow", + "ĠJ O", + "Ġparameter ize", + "gl er", + "Ġfac ult", + "ĠÍ µ", + "Ġnom ination", + "Ġbath s", + "Ġinstall ations", + "ĠJust in", + "Ġchampionship s", + "t ap", + "ĠS anc", + "ĠS usp", + "Ġsub leading", + "Ġdef ended", + "Ġbut yl", + "rem ote", + "Ġcarb ides", + "ĠPredic ts", + "ĠPrior ity", + "ĠAntib iotics", + "ĠPU FAs", + "ĠMIC s", + "ĠMaxim ization", + "b are", + "ĠP CN", + "Ġinf ested", + "Ġsol enoid", + "Ġag ronomic", + "AN GE", + "Re v", + "ĠNK G", + "Ġsap onins", + "Recomm end", + "Ġshar pen", + "othio yl", + "s uspended", + "at ron", + "us age", + "il ter", + "ĠN ER", + "CR IPT", + "inf ections", + "Ġheter osexual", + "Ġmes oc", + "ĠBob by", + "alloc ate", + "ĠPay ne", + "ĠSult an", + "è ¡", + "rac les", + "rib e", + "ĠDo ub", + "ĠPA F", + "commun ication", + "Ġninet eenth", + "Ġpopl ar", + "pgf strok", + "pgfstrok ecolor", + "S LE", + "ec ia", + "Ġdet ach", + "Ġchar ity", + "Ġmon ochrom", + "Ġpres cribe", + "Ġsuper massive", + "Ġgu ards", + "Ġcycl oaddition", + "Co hen", + "phosph atidyl", + "cre ated", + "ĠElectro dynamics", + "Ġplasm ons", + "Ñģ к", + "ĠDaph nia", + "e asy", + "Ġa q", + "Ġf imb", + "Ġw rest", + "ĠT end", + "hip p", + "Ġorgan isational", + "MA E", + "OP Y", + "Ġpotenti ated", + "Ġbr ute", + "omass ie", + "aun ay", + "l uster", + "Ġo phi", + "un ge", + "ĠP om", + "Ġpl ague", + "ber ries", + "Ġinv iscid", + "ĠQ E", + "inc ident", + "xim ide", + "Ġest rogens", + "ĠTrans parent", + "vere ign", + "ĠPre ferred", + "Ġelast ase", + "C iv", + "J F", + "K u", + "c aster", + "Ġsp ends", + "Ġabst racted", + "otechn ical", + "Ġbreed ers", + "Ġsyring ae", + "Ġclot ting", + "Af rican", + "P EC", + "us ep", + "Ġst ark", + "so lete", + "of ovir", + "Ġsens ations", + "az awa", + "Ġbiom echanics", + "Ġemerg encies", + "Ġspectrom eters", + "Ġhemisp heric", + "Ġdiscrimin atory", + "ĠInsp ection", + "nd im", + "RE P", + "ĠW ess", + "Ġhyper algesia", + "IR C", + "Ġauthors hip", + "CP A", + "Ġrotation ally", + "ĠPy th", + "ĠYam aguchi", + "Field s", + "Ġenrol ment", + "ĠReth inking", + "G ate", + "ì ĺ", + "Ġc ements", + "ĠT TS", + "ĠF ink", + "ad us", + "ĠL l", + "Ġim plicate", + "ann ihilation", + "Ġfeed ers", + "ĠPD X", + "ĠFran çois", + "Sp earman", + "ĠBenchmark ing", + "F orest", + "e vidence", + "en oyl", + "ol actone", + "ce phaly", + "ĠP EA", + "ĠN SE", + "Ġno tified", + "Ġpoly electrolyte", + "ĠMal ik", + "anth ine", + "tet rad", + "Ġflag ella", + "ãĥ ¼", + "orp ion", + "Ġbuy ers", + "Ġoligodend rocyte", + "ĠNMD AR", + "ĠHarvest ing", + "Ġkar st", + "I BD", + "ĠF olk", + "Ġsub carrier", + "Ġno tices", + "ĠY ous", + "aw ak", + "Ġadvers aries", + "Lo oking", + "Ġthym ocytes", + "Ġmening ioma", + "Ġillumin ate", + "ĠSPD X", + "Ġimping ing", + "associ ative", + "Ġt iger", + "le on", + "Ġst ature", + "ĠH ood", + "ĠR utherford", + "ĠE IT", + "Ġinf antile", + "ĠQ ubit", + "Ġpack s", + "ĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ ĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ", + "azol am", + "ภ²", + "ĠTun isia", + "dil ution", + "Ġsymp atric", + "Ġliquef action", + "porph yrin", + "G n", + "R ib", + "is othermal", + "ap o", + "Ġreg ressors", + "man i", + "ĠIL s", + "oxid ants", + "At om", + "lig o", + "ĠSR AM", + "alc one", + "cs r", + "Ġc autious", + "Ġhand lers", + "Ġgast ritis", + "ĠSuper vision", + "Ġevapor ative", + "R UN", + "ĠI CG", + "ĠP rague", + "ĠM LC", + "ĠM oney", + "ĠR m", + "ĠE CS", + "vel t", + "ĠV g", + "Ġbi ography", + "Ġmin istry", + "con volution", + "ogen omics", + "round ing", + "ĠPh ag", + "Ġaud iences", + "ĠHC Ws", + "Ġblast ocysts", + "Ġdiagon als", + "Ġpreca utions", + "Í ĵ", + "ec ewise", + "ĠT oxin", + "ĠH app", + "ĠâĢ ĭ", + "Ġpop ulate", + "mm ol", + "ĠPR S", + "Ġreinfor ces", + "IST IC", + "ozo ites", + "arri val", + "Ġpave ment", + "REGIST ER", + "ĠG ases", + "ĠEx hib", + "Ġfactor izations", + "Ġmy opia", + "Ġmov able", + "ĠLI MIT", + "Ġsole us", + "DO UBLE", + "ĠInput s", + "foot notes", + "BIT S", + "ĠCyp rus", + "re ports", + "ĠP AA", + "ist al", + "Ġgroup oids", + "orph in", + "ĠCo ordinates", + "bor o", + "ĠOs lo", + "when ever", + "Ġplaus ibility", + "ĠFox O", + "ĠIntr usion", + "Ġsimplic es", + "ĠFas o", + "Ġpic osecond", + "ĠAns atz", + "Import antly", + "ĠHutch inson", + "ov ani", + "ĠAs ymptotics", + "Ġapp ra", + "ĠEx ogenous", + "Ġcap tions", + "ĠAc anth", + "Ġill icit", + "ĠBl adder", + "Ġbo om", + "ĠSal inity", + "Ġmusc ul", + "eptid yl", + "Ġaval anches", + "Hel per", + "Ġbival ve", + "Ġreimburs ement", + "z zo", + "rom atosis", + "Ġpar acetamol", + "vi o", + "Ġval pro", + "cl amation", + "Ġu u", + "ĠSo C", + "ĠGi ac", + "Ġly copene", + "Fl ags", + "Ġstic king", + "Ġsquee ze", + "synt hetic", + "osahexa enoic", + "m obile", + "v ect", + "ĠB aryon", + "Ġne f", + "Ġfl atter", + "SS I", + "Ġsch w", + "ancre as", + "Bu f", + "Sol id", + "ĠRIP A", + "Squ are", + "Ġtranscend ental", + "Ġc yn", + "Ġm f", + "ĠS ew", + "ĠP IT", + "oc s", + "ĠB ash", + "Ġsur prised", + "Ġaut onomously", + "Ġlocal izing", + "Ġvis itor", + "Ġpers isting", + "Ġland fill", + "date time", + "Ġfire f", + "ĠEngine ered", + "ĠSn yder", + "ochrom es", + "fraction ated", + "G PI", + "Ġw oven", + "ĠT MR", + "Ġfor gotten", + "ĠM ult", + "ĠB ipolar", + "ĠH isp", + "op eptides", + "ap amil", + "Ġro uted", + "Ġag n", + "Ġday light", + "ĠÍ Ķ", + "BB B", + "ĠMajor ity", + "Ġconfound ed", + "ĠCarol ine", + "ĠShim ura", + "r uction", + "Ġt ympan", + "ac io", + "ĠT FE", + "ĠT utorial", + "Ġal lyl", + "ĠF rost", + "ĠR PS", + "ĠW ah", + "Ġi y", + "Ġsub problems", + "Ġair foil", + "Ġembed s", + "ĠMor ning", + "Ġminor ities", + "ĠMemb ership", + "Ġquadric eps", + "y ly", + "ĠB odies", + "ĠR AND", + "Ġr ationally", + "ĠMan ifold", + "phosph ine", + "cons idering", + "ez vous", + "ĠKnow ing", + "Ġtumorigen ic", + "Ġillumin ating", + "ĠFernand es", + "polynomial s", + "ĠBulg arian", + "ĠBhattach arya", + "ospi ra", + "A pi", + "hen ne", + "Ġmay s", + "ĠAr gin", + "inter pol", + "ĠAnd ean", + "ĠPD S", + "ĠCN P", + "Ġtransf usions", + "ĠNan om", + "Ġsynerg ism", + "Ġbent onite", + "Ġgravit ons", + "aqu ette", + "Ġfiss ure", + "t andem", + "w ash", + "ĠE yes", + "Ġdi lepton", + "Ġrec tified", + "ĠAr ist", + "isc ible", + "Ġir q", + "Ġlig aments", + "sec urity", + "Ġvascular ization", + "Na Cl", + "ĠStra ight", + "ĠLept in", + "ĠAbund ances", + "ĠKE Y", + "ĠMother s", + "ĠRenew able", + "Ġmason ry", + "ë ı", + "rac eutical", + "Ġar ity", + "ĠAl ves", + "osp ectral", + "Ġimmun od", + "Ġmar ble", + "Ġcover ings", + "ĠConst ants", + "ĠRever sal", + "Work s", + "ĠNur se", + "ĠAggreg ate", + "ac illin", + "pl ug", + "Ġj ury", + "one ogenesis", + "Ġam oeb", + "au kee", + "Ġphosphor ic", + "ĠRem oving", + "Ġwors en", + "ĠESR D", + "ĠHern andez", + "ĠEug ene", + "visc osity", + "F ID", + " ¦", + "Ġ Ý", + "ĠS tig", + "ĠS ING", + "ĠI MRT", + "ĠB q", + "ĠD ME", + "ĠH OM", + "ph ysis", + "ob es", + "Ġsuper fields", + "Ġarg c", + "Ġmal adaptive", + "ĠEd iting", + "Ġcond em", + "ube i", + "stim ulus", + "Ġabbrevi ation", + "H aus", + "ĠN eeds", + "Ġad hering", + "ĠV PA", + "of rontal", + "ĠÅ ª", + "ĠVer de", + "ĠSl av", + "ĠProp ag", + "Ġcongen ers", + "Ġtil apia", + "ĠRac hel", + "L ess", + "Ġm asc", + "ent angled", + "ĠD TI", + "ati k", + "rol ases", + "ĠY en", + "arm or", + "ĠDec isions", + "Ġη p", + "Int uitively", + "ĠPharmaceutical s", + "J u", + "ud din", + "ĠW ASP", + "nt on", + "Ġbi ot", + "ĠZ NF", + "Ġcr ush", + "ĠPar ity", + "SI ST", + "EV ENT", + "ĠSqu amous", + "Stud ent", + "Ġgingival is", + "f used", + "ĠM ises", + "ĠF DTD", + "ore ceptors", + "Ġdisc retion", + "OR TC", + "MS P", + "Ġexpos es", + "Ġchlor inated", + "ĠUp regulation", + "ĠLim b", + "Ġantic or", + "Reg ular", + "Adv anced", + "X e", + "ag han", + "ĠB LA", + "Ġco asts", + "ĠTh irteen", + "hes in", + "ĠMet hanol", + "rot us", + "ĠStep hens", + "Bo ok", + "ĠHistor ically", + "ĠEmploy ing", + "Ġcorrug ated", + "mercapto ethanol", + "ĠD nmt", + "ĠQu eries", + "DR B", + "ĠGR U", + "FL U", + "ĠCarbon iferous", + "OB JECT", + "ĠDiscrim inative", + "ĠBurg ess", + "Ġplanetes imals", + "ĠAmend ment", + "ĠStriking ly", + "t ric", + "ec ure", + "Ġtrans posable", + "rol ateral", + "Ġhis ti", + "ega ard", + "Ġsk im", + "ĠSP F", + "Stat istics", + "Ġintest ines", + "f eng", + "l ain", + "Ġthe at", + "Ġo rogen", + "Ġp ill", + "od opa", + "Ġcorrel ative", + "AC O", + "Ġadj unction", + "ĠCare y", + "Ġtele portation", + "ĠBound aries", + "ĠGood fellow", + "ĠLind a", + "ĠPolymer ic", + "Ġexer tion", + "Ġsteep ly", + "Ġprotr usion", + "Ġhyal uronic", + "ĠRoc hester", + "ENSI ONAL", + "D ar", + "f et", + "ĠF SS", + "hem ically", + "Ġfl ashes", + "Ġdevi ated", + "feld t", + "Ġstic ks", + "Ġoct et", + "Ġgravitation ally", + "footnotes ize", + "L ex", + "o vi", + "Ġw ired", + "ĠS MP", + "erm ans", + "Ġun broken", + "Ġem ulation", + "sim ulated", + "Ġminim ality", + "ardi ac", + "Ġship w", + "Gene tic", + "ĠHerm ann", + "ynchron ization", + "ĠNap ole", + "Ġmonodis perse", + "R ho", + "r ists", + "Ġf x", + "ĠF UV", + "ĠG elfand", + "hem ispheric", + "ron idazole", + "Ġsuper saturation", + "oud h", + "oli tical", + "ĠAir y", + "Ġmanifest ly", + "ĠHM G", + "Ġadvertis ement", + "ĠBrook lyn", + "Ġparalle led", + "answ ered", + "ĠNotImplemented Error", + "U PD", + "o ust", + "ĠT eng", + "Ġfor tified", + "ĠS ort", + "EN E", + "ĠF ris", + "ĠH IS", + "ĠR OT", + "ĠN ested", + "pro duce", + "ĠK erala", + "gen omic", + "ĠIs ab", + "Ġur acil", + "bur ger", + "ĠLog arithmic", + "Ġster ility", + "Ġunem ployed", + "Ġori ental", + "K o", + "j ima", + "ĠC TP", + "ĠL AD", + "Ġconform ers", + "CG G", + "Per kin", + "Ġbrid ged", + "ĠDiss ociation", + "ĠQi agen", + "Ġwealth y", + "Ġanaest hetic", + "ĠMinim izing", + "Ġacous tics", + "buck et", + "ĠSert oli", + "r ath", + "s aw", + "Ġg arn", + "ĠThe oretically", + "tic ul", + "ĠTh inking", + "ik er", + "ĠCh it", + "Ġtr in", + "AL ITY", + "ĠFe O", + "Ġpolymer ized", + "En coding", + "Ġanalges ics", + "ĠLex ical", + "Ġmari juana", + "âĸĪ âĸĪ", + "c rops", + "ent ropic", + "ol ocation", + "ĠP omp", + "Ġco factors", + "box times", + "ĠAr ri", + "An gel", + "ĠRequire ment", + "Ġmicrol ensing", + "ĠTRAN SF", + "å º", + "Ġd ma", + "Ġre rio", + "und ancy", + "ant el", + "Ġradi ometric", + "ĠSe an", + "rand n", + "ĠCR L", + "hal os", + "uber tal", + "Ġquin one", + "T ES", + "Ġd W", + "ĠC GM", + "Ġhe aled", + "ian e", + "Ġobtain able", + "ĠAd rian", + "Ġlik es", + "ĠMed ication", + "Ġcogn itively", + "Whe ther", + "B ob", + "d id", + "al cohol", + "Ġn ivolumab", + "ĠF Y", + "Ġat resia", + "ach s", + "ĠK ip", + "Ġun igenes", + "ĠJ accard", + "ust ri", + "Ġconf ine", + "Ġaut ofluorescence", + "Ġpy g", + "Se a", + "Set tings", + "Ġtrunc atula", + "Liter al", + "F ab", + "M ah", + "V en", + "Ġt ig", + "Ġc her", + "ĠC CI", + "ĠF unk", + "ĠB ess", + "ĠN asal", + "iff er", + "Ġobs essive", + "ĠOp ening", + "ochond ral", + "ĠTR PA", + "ĠRab in", + "Ġta per", + "Ġdeaf ness", + "D OS", + "is ites", + "an ite", + "le ost", + "ĠS TP", + "ĠB ACE", + "ĠH enn", + "ĠE SM", + "Ġsuper field", + "ĠOr land", + "ĠAMP s", + "ĠHem orrh", + "Ġresc ues", + "Ġtour ists", + "ĠVL BI", + "Ġneighbourhood s", + "communic able", + "g x", + "r atase", + "ĠN RT", + "Ġob structions", + "Ġdef orestation", + "Ġq p", + "ĠPh an", + "ĠST I", + "iment o", + "ĠIR I", + "SV s", + "Ġstrip ed", + "Po inc", + "ĠBed ford", + "ĠFrag ment", + "ĠRelig ion", + "Ġd rones", + "im ulation", + "ĠC et", + "Ġg ills", + "ĠN orton", + "ib atch", + "est ructive", + "ĠJ av", + "ĠÏ ½", + "Ġmic a", + "AG B", + "RA W", + "ĠMy D", + "ct l", + "Ġrevers ibly", + "Ġsuppress ors", + "ĠFA IL", + "ĠFuk ushima", + "E vidence", + "p ink", + "as array", + "ĠT ann", + "Ġl oved", + "Ġbi ologists", + "Ġend othermic", + "Ġbro ker", + "ĠPer kins", + "Ġcategor ised", + "ĠSO ME", + "hydroxy vitamin", + "rog ates", + "ĠAge ing", + "Ġtourn aments", + "ĠStrom al", + "Ġdefer red", + "ĠSRE BP", + "M AD", + "S ay", + "c gi", + "p he", + "ol ini", + "ĠD ü", + "Ġde hydro", + "ap eptide", + "Ġhe s", + "Ġdist ally", + "vers ions", + "Ġmed als", + "Ġfl aw", + "Ġdu o", + "Ġimpair ing", + "toplas ts", + "ĠHF ILL", + "Ġesc ulent", + "Class ification", + "ĠGriff ith", + "ĠWelling ton", + "Ġattor ney", + "A st", + "k A", + "Ġm ilit", + "Ġn ite", + "ĠC asp", + "ĠC hester", + "ĠM ok", + "ĠR AR", + "Ġch r", + "unc tor", + "Ġab duction", + "Ġun iv", + "ov ars", + "ou k", + "ER ICAL", + "é ri", + "orb ance", + "ĠIdentif ies", + "ament o", + "Ġparent hesis", + "ĠME Fs", + "Ġabsor bs", + "ĠArray List", + "Ġcareg iving", + "ĠFI LE", + "Ġfeld spar", + "ochthon ous", + "S ort", + "j al", + "Ġt antal", + "ar abine", + "ĠS aid", + "ĠB CE", + "ĠN GO", + "yn ure", + "dot eq", + "ĠLe yd", + "mod ality", + "ĠGe ometrical", + "Al most", + "Ġhard ened", + "no ea", + "new s", + "Ġclean up", + "ĠArm ed", + "ĠSn ake", + "multi ply", + "ĠMill ennium", + "ĠSmooth ing", + "posit ely", + "en ary", + "is se", + "ĠY uc", + "Ġgene al", + "Ġsuper s", + "Ġhand held", + "Ġemb ark", + "ĠBl a", + "hor st", + "ĠPD GFR", + "Ġcit r", + "Ġcalor ie", + "ĠBudd hist", + "M ember", + "Ġf ears", + "Ġf iscal", + "ĠA IF", + "LO AD", + "pe are", + "Ġbit umen", + "Par ticip", + "ĠIndian apolis", + "ĠAlb um", + "Ġscr utiny", + "acyl glycer", + "ĠSak ai", + "Ġthermod ynamical", + "Z B", + "Ġh pf", + "ĠL IP", + "Ġexp iration", + "til t", + "Ġfl ax", + "ĠSe lectivity", + "ĠSch ol", + "any a", + "orb ents", + "Ġincub ations", + "Ġmargin als", + "inv olved", + "Ġenthal pies", + "macroph ages", + "construct or", + "ĠRol and", + "ĠP m", + "ĠR Y", + "Ġbl obs", + "Ġann uli", + "Ġuns timulated", + "ĠPet roleum", + "Ġmerg es", + "Ġenvelop ing", + "ĠInitial ization", + "Ġshed s", + "Ġadvis able", + "ylethanol amine" + ] + } +} diff --git a/modules/onnx_ocr_module/src/ppocr/utils/dict/unimernet_tokenizer/tokenizer_config.json b/modules/onnx_ocr_module/src/ppocr/utils/dict/unimernet_tokenizer/tokenizer_config.json new file mode 100644 index 0000000..75dad5a --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/utils/dict/unimernet_tokenizer/tokenizer_config.json @@ -0,0 +1,205 @@ +{ + "added_tokens_decoder": { + "0": { + "content": "", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "1": { + "content": "", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "2": { + "content": "", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "3": { + "content": "", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "4": { + "content": "[START_REF]", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "5": { + "content": "[END_REF]", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "6": { + "content": "[IMAGE]", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "7": { + "content": "", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "8": { + "content": "", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "9": { + "content": "", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "10": { + "content": "", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "11": { + "content": "[START_SUP]", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "12": { + "content": "[END_SUP]", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "13": { + "content": "[START_SUB]", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "14": { + "content": "[END_SUB]", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "15": { + "content": "[START_DNA]", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "16": { + "content": "[END_DNA]", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "17": { + "content": "[START_AMINO]", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "18": { + "content": "[END_AMINO]", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "19": { + "content": "[START_SMILES]", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "20": { + "content": "[END_SMILES]", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "21": { + "content": "[START_I_SMILES]", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "22": { + "content": "[END_I_SMILES]", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + } + }, + "additional_special_tokens": [], + "bos_token": "", + "clean_up_tokenization_spaces": false, + "eos_token": "", + "max_length": 4096, + "model_max_length": 768, + "pad_to_multiple_of": null, + "pad_token": "", + "pad_token_type_id": 0, + "padding_side": "right", + "processor_class": "VariableDonutProcessor", + "stride": 0, + "tokenizer_class": "NougatTokenizer", + "truncation_side": "right", + "truncation_strategy": "longest_first", + "unk_token": "", + "vocab_file": null +} diff --git a/modules/onnx_ocr_module/src/ppocr/utils/dict/ur_dict.txt b/modules/onnx_ocr_module/src/ppocr/utils/dict/ur_dict.txt new file mode 100644 index 0000000..c06786a --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/utils/dict/ur_dict.txt @@ -0,0 +1,137 @@ +u +r +_ +i +m +g +/ +3 +I +L +S +V +R +C +2 +0 +1 +v +a +l +9 +7 +8 +. +j +p + +چ +ٹ +پ +ا +ئ +ی +ے +4 +6 +و +ل +ن +ڈ +ھ +ک +ت +ش +ف +ق +ر +د +5 +ب +ج +خ +ہ +س +ز +غ +ڑ +ں +آ +م +ؤ +ط +ص +ح +ع +گ +ث +ض +ذ +ۓ +ِ +ء +ظ +ً +ي +ُ +ۃ +أ +ٰ +ە +ژ +ۂ +ة +ّ +ك +ه +s +c +e +n +w +o +d +t +D +M +T +U +E +b +P +h +y +W +H +A +x +B +O +N +G +Y +Q +F +k +K +q +J +Z +f +z +X +' +@ +& +! +, +: +$ +- +# +? +% +é ++ +( +É diff --git a/modules/onnx_ocr_module/src/ppocr/utils/dict/vi_dict.txt b/modules/onnx_ocr_module/src/ppocr/utils/dict/vi_dict.txt new file mode 100644 index 0000000..5e8f8c6 --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/utils/dict/vi_dict.txt @@ -0,0 +1,113 @@ +A +B +C +D +E +F +G +H +I +J +K +L +M +N +P +Q +R +S +T +U +V +W +a +b +c +d +e +g +h +i +k +l +m +n +o +p +q +r +s +t +u +v +w +x +y +à +á +â +ã +è +é +ê +ì +í +ò +ó +ô +õ +ù +ú +ý +ă +Đ +đ +ĩ +ũ +ơ +ư +ạ +ả +ấ +ầ +ẩ +ẫ +ậ +ắ +ằ +ẳ +ẵ +ặ +ẹ +ẻ +ẽ +ế +ề +ể +ễ +ệ +ỉ +ị +ọ +ỏ +ố +ồ +ổ +ỗ +ộ +ớ +ờ +ở +ỡ +ợ +ụ +ủ +ứ +ừ +ử +ữ +ự +ỳ +ỵ +ỷ +ỹ diff --git a/modules/onnx_ocr_module/src/ppocr/utils/dict/xi_dict.txt b/modules/onnx_ocr_module/src/ppocr/utils/dict/xi_dict.txt new file mode 100644 index 0000000..f195f1e --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/utils/dict/xi_dict.txt @@ -0,0 +1,110 @@ +x +i +_ +m +g +/ +1 +0 +I +L +S +V +R +C +2 +v +a +l +3 +6 +4 +5 +. +j +p + +Q +u +e +r +o +8 +7 +n +c +9 +t +b +é +q +d +ó +y +F +s +, +O +í +T +f +" +U +M +h +: +P +H +A +E +D +z +N +á +ñ +ú +% +; +è ++ +Y +- +B +G +( +) +¿ +? +w +¡ +! +X +É +K +k +Á +ü +Ú +« +» +J +' +ö +W +Z +º +Ö +­ +[ +] +Ç +ç +à +ä +û +ò +Í +ê +ô +ø +ª diff --git a/modules/onnx_ocr_module/src/ppocr/utils/dict90.txt b/modules/onnx_ocr_module/src/ppocr/utils/dict90.txt new file mode 100644 index 0000000..46b8795 --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/utils/dict90.txt @@ -0,0 +1,90 @@ +0 +1 +2 +3 +4 +5 +6 +7 +8 +9 +a +b +c +d +e +f +g +h +i +j +k +l +m +n +o +p +q +r +s +t +u +v +w +x +y +z +A +B +C +D +E +F +G +H +I +J +K +L +M +N +O +P +Q +R +S +T +U +V +W +X +Y +Z +! +" +# +$ +% +& +' +( +) +* ++ +, +- +. +/ +: +; +< += +> +? +@ +[ +\ +] +_ +` +~ diff --git a/modules/onnx_ocr_module/src/ppocr/utils/e2e_metric/Deteval.py b/modules/onnx_ocr_module/src/ppocr/utils/e2e_metric/Deteval.py new file mode 100644 index 0000000..f4767e1 --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/utils/e2e_metric/Deteval.py @@ -0,0 +1,852 @@ +# Copyright (c) 2021 PaddlePaddle Authors. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import json +import numpy as np +import scipy.io as io + +from ppocr.utils.utility import check_install + +from ppocr.utils.e2e_metric.polygon_fast import iod, area_of_intersection, area + + +def get_socre_A(gt_dir, pred_dict): + allInputs = 1 + + def input_reading_mod(pred_dict): + """This helper reads input from txt files""" + det = [] + n = len(pred_dict) + for i in range(n): + points = pred_dict[i]["points"] + text = pred_dict[i]["texts"] + point = ",".join( + map( + str, + points.reshape( + -1, + ), + ) + ) + det.append([point, text]) + return det + + def gt_reading_mod(gt_dict): + """This helper reads groundtruths from mat files""" + gt = [] + n = len(gt_dict) + for i in range(n): + points = gt_dict[i]["points"].tolist() + h = len(points) + text = gt_dict[i]["text"] + xx = [ + np.array(["x:"], dtype=" 1): + gt_x = list(map(int, np.squeeze(gt[1]))) + gt_y = list(map(int, np.squeeze(gt[3]))) + for det_id, detection in enumerate(detections): + detection_orig = detection + detection = [float(x) for x in detection[0].split(",")] + detection = list(map(int, detection)) + det_x = detection[0::2] + det_y = detection[1::2] + det_gt_iou = iod(det_x, det_y, gt_x, gt_y) + if det_gt_iou > threshold: + detections[det_id] = [] + + detections[:] = [item for item in detections if item != []] + return detections + + def sigma_calculation(det_x, det_y, gt_x, gt_y): + """ + sigma = inter_area / gt_area + """ + return np.round( + (area_of_intersection(det_x, det_y, gt_x, gt_y) / area(gt_x, gt_y)), 2 + ) + + def tau_calculation(det_x, det_y, gt_x, gt_y): + if area(det_x, det_y) == 0.0: + return 0 + return np.round( + (area_of_intersection(det_x, det_y, gt_x, gt_y) / area(det_x, det_y)), 2 + ) + + ##############################Initialization################################### + # global_sigma = [] + # global_tau = [] + # global_pred_str = [] + # global_gt_str = [] + ############################################################################### + + for input_id in range(allInputs): + if ( + (input_id != ".DS_Store") + and (input_id != "Pascal_result.txt") + and (input_id != "Pascal_result_curved.txt") + and (input_id != "Pascal_result_non_curved.txt") + and (input_id != "Deteval_result.txt") + and (input_id != "Deteval_result_curved.txt") + and (input_id != "Deteval_result_non_curved.txt") + ): + detections = input_reading_mod(pred_dict) + groundtruths = gt_reading_mod(gt_dir) + detections = detection_filtering( + detections, groundtruths + ) # filters detections overlapping with DC area + dc_id = [] + for i in range(len(groundtruths)): + if groundtruths[i][5] == "#": + dc_id.append(i) + cnt = 0 + for a in dc_id: + num = a - cnt + del groundtruths[num] + cnt += 1 + + local_sigma_table = np.zeros((len(groundtruths), len(detections))) + local_tau_table = np.zeros((len(groundtruths), len(detections))) + local_pred_str = {} + local_gt_str = {} + + for gt_id, gt in enumerate(groundtruths): + if len(detections) > 0: + for det_id, detection in enumerate(detections): + detection_orig = detection + detection = [float(x) for x in detection[0].split(",")] + detection = list(map(int, detection)) + pred_seq_str = detection_orig[1].strip() + det_x = detection[0::2] + det_y = detection[1::2] + gt_x = list(map(int, np.squeeze(gt[1]))) + gt_y = list(map(int, np.squeeze(gt[3]))) + gt_seq_str = str(gt[4].tolist()[0]) + + local_sigma_table[gt_id, det_id] = sigma_calculation( + det_x, det_y, gt_x, gt_y + ) + local_tau_table[gt_id, det_id] = tau_calculation( + det_x, det_y, gt_x, gt_y + ) + local_pred_str[det_id] = pred_seq_str + local_gt_str[gt_id] = gt_seq_str + + global_sigma = local_sigma_table + global_tau = local_tau_table + global_pred_str = local_pred_str + global_gt_str = local_gt_str + + single_data = {} + single_data["sigma"] = global_sigma + single_data["global_tau"] = global_tau + single_data["global_pred_str"] = global_pred_str + single_data["global_gt_str"] = global_gt_str + return single_data + + +def get_socre_B(gt_dir, img_id, pred_dict): + allInputs = 1 + + def input_reading_mod(pred_dict): + """This helper reads input from txt files""" + det = [] + n = len(pred_dict) + for i in range(n): + points = pred_dict[i]["points"] + text = pred_dict[i]["texts"] + point = ",".join( + map( + str, + points.reshape( + -1, + ), + ) + ) + det.append([point, text]) + return det + + def gt_reading_mod(gt_dir, gt_id): + gt = io.loadmat("%s/poly_gt_img%s.mat" % (gt_dir, gt_id)) + gt = gt["polygt"] + return gt + + def detection_filtering(detections, groundtruths, threshold=0.5): + for gt_id, gt in enumerate(groundtruths): + if (gt[5] == "#") and (gt[1].shape[1] > 1): + gt_x = list(map(int, np.squeeze(gt[1]))) + gt_y = list(map(int, np.squeeze(gt[3]))) + for det_id, detection in enumerate(detections): + detection_orig = detection + detection = [float(x) for x in detection[0].split(",")] + detection = list(map(int, detection)) + det_x = detection[0::2] + det_y = detection[1::2] + det_gt_iou = iod(det_x, det_y, gt_x, gt_y) + if det_gt_iou > threshold: + detections[det_id] = [] + + detections[:] = [item for item in detections if item != []] + return detections + + def sigma_calculation(det_x, det_y, gt_x, gt_y): + """ + sigma = inter_area / gt_area + """ + return np.round( + (area_of_intersection(det_x, det_y, gt_x, gt_y) / area(gt_x, gt_y)), 2 + ) + + def tau_calculation(det_x, det_y, gt_x, gt_y): + if area(det_x, det_y) == 0.0: + return 0 + return np.round( + (area_of_intersection(det_x, det_y, gt_x, gt_y) / area(det_x, det_y)), 2 + ) + + ##############################Initialization################################### + # global_sigma = [] + # global_tau = [] + # global_pred_str = [] + # global_gt_str = [] + ############################################################################### + + for input_id in range(allInputs): + if ( + (input_id != ".DS_Store") + and (input_id != "Pascal_result.txt") + and (input_id != "Pascal_result_curved.txt") + and (input_id != "Pascal_result_non_curved.txt") + and (input_id != "Deteval_result.txt") + and (input_id != "Deteval_result_curved.txt") + and (input_id != "Deteval_result_non_curved.txt") + ): + detections = input_reading_mod(pred_dict) + groundtruths = gt_reading_mod(gt_dir, img_id).tolist() + detections = detection_filtering( + detections, groundtruths + ) # filters detections overlapping with DC area + dc_id = [] + for i in range(len(groundtruths)): + if groundtruths[i][5] == "#": + dc_id.append(i) + cnt = 0 + for a in dc_id: + num = a - cnt + del groundtruths[num] + cnt += 1 + + local_sigma_table = np.zeros((len(groundtruths), len(detections))) + local_tau_table = np.zeros((len(groundtruths), len(detections))) + local_pred_str = {} + local_gt_str = {} + + for gt_id, gt in enumerate(groundtruths): + if len(detections) > 0: + for det_id, detection in enumerate(detections): + detection_orig = detection + detection = [float(x) for x in detection[0].split(",")] + detection = list(map(int, detection)) + pred_seq_str = detection_orig[1].strip() + det_x = detection[0::2] + det_y = detection[1::2] + gt_x = list(map(int, np.squeeze(gt[1]))) + gt_y = list(map(int, np.squeeze(gt[3]))) + gt_seq_str = str(gt[4].tolist()[0]) + + local_sigma_table[gt_id, det_id] = sigma_calculation( + det_x, det_y, gt_x, gt_y + ) + local_tau_table[gt_id, det_id] = tau_calculation( + det_x, det_y, gt_x, gt_y + ) + local_pred_str[det_id] = pred_seq_str + local_gt_str[gt_id] = gt_seq_str + + global_sigma = local_sigma_table + global_tau = local_tau_table + global_pred_str = local_pred_str + global_gt_str = local_gt_str + + single_data = {} + single_data["sigma"] = global_sigma + single_data["global_tau"] = global_tau + single_data["global_pred_str"] = global_pred_str + single_data["global_gt_str"] = global_gt_str + return single_data + + +def get_score_C(gt_label, text, pred_bboxes): + """ + get score for CentripetalText (CT) prediction. + """ + check_install("Polygon", "Polygon3") + import Polygon as plg + + def gt_reading_mod(gt_label, text): + """This helper reads groundtruths from mat files""" + groundtruths = [] + nbox = len(gt_label) + for i in range(nbox): + label = {"transcription": text[i][0], "points": gt_label[i].numpy()} + groundtruths.append(label) + + return groundtruths + + def get_union(pD, pG): + areaA = pD.area() + areaB = pG.area() + return areaA + areaB - get_intersection(pD, pG) + + def get_intersection(pD, pG): + pInt = pD & pG + if len(pInt) == 0: + return 0 + return pInt.area() + + def detection_filtering(detections, groundtruths, threshold=0.5): + for gt in groundtruths: + point_num = gt["points"].shape[1] // 2 + if gt["transcription"] == "###" and (point_num > 1): + gt_p = np.array(gt["points"]).reshape(point_num, 2).astype("int32") + gt_p = plg.Polygon(gt_p) + + for det_id, detection in enumerate(detections): + det_y = detection[0::2] + det_x = detection[1::2] + + det_p = np.concatenate((np.array(det_x), np.array(det_y))) + det_p = det_p.reshape(2, -1).transpose() + det_p = plg.Polygon(det_p) + + try: + det_gt_iou = get_intersection(det_p, gt_p) / det_p.area() + except: + print(det_x, det_y, gt_p) + if det_gt_iou > threshold: + detections[det_id] = [] + + detections[:] = [item for item in detections if item != []] + return detections + + def sigma_calculation(det_p, gt_p): + """ + sigma = inter_area / gt_area + """ + if gt_p.area() == 0.0: + return 0 + return get_intersection(det_p, gt_p) / gt_p.area() + + def tau_calculation(det_p, gt_p): + """ + tau = inter_area / det_area + """ + if det_p.area() == 0.0: + return 0 + return get_intersection(det_p, gt_p) / det_p.area() + + detections = [] + + for item in pred_bboxes: + detections.append(item[:, ::-1].reshape(-1)) + + groundtruths = gt_reading_mod(gt_label, text) + + detections = detection_filtering( + detections, groundtruths + ) # filters detections overlapping with DC area + + for idx in range(len(groundtruths) - 1, -1, -1): + # NOTE: source code use 'orin' to indicate '#', here we use 'anno', + # which may cause slight drop in fscore, about 0.12 + if groundtruths[idx]["transcription"] == "###": + groundtruths.pop(idx) + + local_sigma_table = np.zeros((len(groundtruths), len(detections))) + local_tau_table = np.zeros((len(groundtruths), len(detections))) + + for gt_id, gt in enumerate(groundtruths): + if len(detections) > 0: + for det_id, detection in enumerate(detections): + point_num = gt["points"].shape[1] // 2 + + gt_p = np.array(gt["points"]).reshape(point_num, 2).astype("int32") + gt_p = plg.Polygon(gt_p) + + det_y = detection[0::2] + det_x = detection[1::2] + + det_p = np.concatenate((np.array(det_x), np.array(det_y))) + + det_p = det_p.reshape(2, -1).transpose() + det_p = plg.Polygon(det_p) + + local_sigma_table[gt_id, det_id] = sigma_calculation(det_p, gt_p) + local_tau_table[gt_id, det_id] = tau_calculation(det_p, gt_p) + + data = {} + data["sigma"] = local_sigma_table + data["global_tau"] = local_tau_table + data["global_pred_str"] = "" + data["global_gt_str"] = "" + return data + + +def combine_results(all_data, rec_flag=True): + tr = 0.7 + tp = 0.6 + fsc_k = 0.8 + k = 2 + global_sigma = [] + global_tau = [] + global_pred_str = [] + global_gt_str = [] + + for data in all_data: + global_sigma.append(data["sigma"]) + global_tau.append(data["global_tau"]) + global_pred_str.append(data["global_pred_str"]) + global_gt_str.append(data["global_gt_str"]) + + global_accumulative_recall = 0 + global_accumulative_precision = 0 + total_num_gt = 0 + total_num_det = 0 + hit_str_count = 0 + hit_count = 0 + + def one_to_one( + local_sigma_table, + local_tau_table, + local_accumulative_recall, + local_accumulative_precision, + global_accumulative_recall, + global_accumulative_precision, + gt_flag, + det_flag, + idy, + rec_flag, + ): + hit_str_num = 0 + for gt_id in range(num_gt): + gt_matching_qualified_sigma_candidates = np.where( + local_sigma_table[gt_id, :] > tr + ) + gt_matching_num_qualified_sigma_candidates = ( + gt_matching_qualified_sigma_candidates[0].shape[0] + ) + gt_matching_qualified_tau_candidates = np.where( + local_tau_table[gt_id, :] > tp + ) + gt_matching_num_qualified_tau_candidates = ( + gt_matching_qualified_tau_candidates[0].shape[0] + ) + + det_matching_qualified_sigma_candidates = np.where( + local_sigma_table[:, gt_matching_qualified_sigma_candidates[0]] > tr + ) + det_matching_num_qualified_sigma_candidates = ( + det_matching_qualified_sigma_candidates[0].shape[0] + ) + det_matching_qualified_tau_candidates = np.where( + local_tau_table[:, gt_matching_qualified_tau_candidates[0]] > tp + ) + det_matching_num_qualified_tau_candidates = ( + det_matching_qualified_tau_candidates[0].shape[0] + ) + + if ( + (gt_matching_num_qualified_sigma_candidates == 1) + and (gt_matching_num_qualified_tau_candidates == 1) + and (det_matching_num_qualified_sigma_candidates == 1) + and (det_matching_num_qualified_tau_candidates == 1) + ): + global_accumulative_recall = global_accumulative_recall + 1.0 + global_accumulative_precision = global_accumulative_precision + 1.0 + local_accumulative_recall = local_accumulative_recall + 1.0 + local_accumulative_precision = local_accumulative_precision + 1.0 + + gt_flag[0, gt_id] = 1 + matched_det_id = np.where(local_sigma_table[gt_id, :] > tr) + # recg start + if rec_flag: + gt_str_cur = global_gt_str[idy][gt_id] + pred_str_cur = global_pred_str[idy][matched_det_id[0].tolist()[0]] + if pred_str_cur == gt_str_cur: + hit_str_num += 1 + else: + if pred_str_cur.lower() == gt_str_cur.lower(): + hit_str_num += 1 + # recg end + det_flag[0, matched_det_id] = 1 + return ( + local_accumulative_recall, + local_accumulative_precision, + global_accumulative_recall, + global_accumulative_precision, + gt_flag, + det_flag, + hit_str_num, + ) + + def one_to_many( + local_sigma_table, + local_tau_table, + local_accumulative_recall, + local_accumulative_precision, + global_accumulative_recall, + global_accumulative_precision, + gt_flag, + det_flag, + idy, + rec_flag, + ): + hit_str_num = 0 + for gt_id in range(num_gt): + # skip the following if the groundtruth was matched + if gt_flag[0, gt_id] > 0: + continue + + non_zero_in_sigma = np.where(local_sigma_table[gt_id, :] > 0) + num_non_zero_in_sigma = non_zero_in_sigma[0].shape[0] + + if num_non_zero_in_sigma >= k: + ####search for all detections that overlaps with this groundtruth + qualified_tau_candidates = np.where( + (local_tau_table[gt_id, :] >= tp) & (det_flag[0, :] == 0) + ) + num_qualified_tau_candidates = qualified_tau_candidates[0].shape[0] + + if num_qualified_tau_candidates == 1: + if (local_tau_table[gt_id, qualified_tau_candidates] >= tp) and ( + local_sigma_table[gt_id, qualified_tau_candidates] >= tr + ): + # became an one-to-one case + global_accumulative_recall = global_accumulative_recall + 1.0 + global_accumulative_precision = ( + global_accumulative_precision + 1.0 + ) + local_accumulative_recall = local_accumulative_recall + 1.0 + local_accumulative_precision = ( + local_accumulative_precision + 1.0 + ) + + gt_flag[0, gt_id] = 1 + det_flag[0, qualified_tau_candidates] = 1 + # recg start + if rec_flag: + gt_str_cur = global_gt_str[idy][gt_id] + pred_str_cur = global_pred_str[idy][ + qualified_tau_candidates[0].tolist()[0] + ] + if pred_str_cur == gt_str_cur: + hit_str_num += 1 + else: + if pred_str_cur.lower() == gt_str_cur.lower(): + hit_str_num += 1 + # recg end + elif np.sum(local_sigma_table[gt_id, qualified_tau_candidates]) >= tr: + gt_flag[0, gt_id] = 1 + det_flag[0, qualified_tau_candidates] = 1 + # recg start + if rec_flag: + gt_str_cur = global_gt_str[idy][gt_id] + pred_str_cur = global_pred_str[idy][ + qualified_tau_candidates[0].tolist()[0] + ] + if pred_str_cur == gt_str_cur: + hit_str_num += 1 + else: + if pred_str_cur.lower() == gt_str_cur.lower(): + hit_str_num += 1 + # recg end + + global_accumulative_recall = global_accumulative_recall + fsc_k + global_accumulative_precision = ( + global_accumulative_precision + + num_qualified_tau_candidates * fsc_k + ) + + local_accumulative_recall = local_accumulative_recall + fsc_k + local_accumulative_precision = ( + local_accumulative_precision + + num_qualified_tau_candidates * fsc_k + ) + + return ( + local_accumulative_recall, + local_accumulative_precision, + global_accumulative_recall, + global_accumulative_precision, + gt_flag, + det_flag, + hit_str_num, + ) + + def many_to_one( + local_sigma_table, + local_tau_table, + local_accumulative_recall, + local_accumulative_precision, + global_accumulative_recall, + global_accumulative_precision, + gt_flag, + det_flag, + idy, + rec_flag, + ): + hit_str_num = 0 + for det_id in range(num_det): + # skip the following if the detection was matched + if det_flag[0, det_id] > 0: + continue + + non_zero_in_tau = np.where(local_tau_table[:, det_id] > 0) + num_non_zero_in_tau = non_zero_in_tau[0].shape[0] + + if num_non_zero_in_tau >= k: + ####search for all detections that overlaps with this groundtruth + qualified_sigma_candidates = np.where( + (local_sigma_table[:, det_id] >= tp) & (gt_flag[0, :] == 0) + ) + num_qualified_sigma_candidates = qualified_sigma_candidates[0].shape[0] + + if num_qualified_sigma_candidates == 1: + if (local_tau_table[qualified_sigma_candidates, det_id] >= tp) and ( + local_sigma_table[qualified_sigma_candidates, det_id] >= tr + ): + # became an one-to-one case + global_accumulative_recall = global_accumulative_recall + 1.0 + global_accumulative_precision = ( + global_accumulative_precision + 1.0 + ) + local_accumulative_recall = local_accumulative_recall + 1.0 + local_accumulative_precision = ( + local_accumulative_precision + 1.0 + ) + + gt_flag[0, qualified_sigma_candidates] = 1 + det_flag[0, det_id] = 1 + # recg start + if rec_flag: + pred_str_cur = global_pred_str[idy][det_id] + gt_len = len(qualified_sigma_candidates[0]) + for idx in range(gt_len): + ele_gt_id = qualified_sigma_candidates[0].tolist()[idx] + if ele_gt_id not in global_gt_str[idy]: + continue + gt_str_cur = global_gt_str[idy][ele_gt_id] + if pred_str_cur == gt_str_cur: + hit_str_num += 1 + break + else: + if pred_str_cur.lower() == gt_str_cur.lower(): + hit_str_num += 1 + break + # recg end + elif np.sum(local_tau_table[qualified_sigma_candidates, det_id]) >= tp: + det_flag[0, det_id] = 1 + gt_flag[0, qualified_sigma_candidates] = 1 + # recg start + if rec_flag: + pred_str_cur = global_pred_str[idy][det_id] + gt_len = len(qualified_sigma_candidates[0]) + for idx in range(gt_len): + ele_gt_id = qualified_sigma_candidates[0].tolist()[idx] + if ele_gt_id not in global_gt_str[idy]: + continue + gt_str_cur = global_gt_str[idy][ele_gt_id] + if pred_str_cur == gt_str_cur: + hit_str_num += 1 + break + else: + if pred_str_cur.lower() == gt_str_cur.lower(): + hit_str_num += 1 + break + # recg end + + global_accumulative_recall = ( + global_accumulative_recall + + num_qualified_sigma_candidates * fsc_k + ) + global_accumulative_precision = ( + global_accumulative_precision + fsc_k + ) + + local_accumulative_recall = ( + local_accumulative_recall + + num_qualified_sigma_candidates * fsc_k + ) + local_accumulative_precision = local_accumulative_precision + fsc_k + return ( + local_accumulative_recall, + local_accumulative_precision, + global_accumulative_recall, + global_accumulative_precision, + gt_flag, + det_flag, + hit_str_num, + ) + + for idx in range(len(global_sigma)): + local_sigma_table = np.array(global_sigma[idx]) + local_tau_table = global_tau[idx] + + num_gt = local_sigma_table.shape[0] + num_det = local_sigma_table.shape[1] + + total_num_gt = total_num_gt + num_gt + total_num_det = total_num_det + num_det + + local_accumulative_recall = 0 + local_accumulative_precision = 0 + gt_flag = np.zeros((1, num_gt)) + det_flag = np.zeros((1, num_det)) + + #######first check for one-to-one case########## + ( + local_accumulative_recall, + local_accumulative_precision, + global_accumulative_recall, + global_accumulative_precision, + gt_flag, + det_flag, + hit_str_num, + ) = one_to_one( + local_sigma_table, + local_tau_table, + local_accumulative_recall, + local_accumulative_precision, + global_accumulative_recall, + global_accumulative_precision, + gt_flag, + det_flag, + idx, + rec_flag, + ) + + hit_str_count += hit_str_num + #######then check for one-to-many case########## + ( + local_accumulative_recall, + local_accumulative_precision, + global_accumulative_recall, + global_accumulative_precision, + gt_flag, + det_flag, + hit_str_num, + ) = one_to_many( + local_sigma_table, + local_tau_table, + local_accumulative_recall, + local_accumulative_precision, + global_accumulative_recall, + global_accumulative_precision, + gt_flag, + det_flag, + idx, + rec_flag, + ) + hit_str_count += hit_str_num + #######then check for many-to-one case########## + ( + local_accumulative_recall, + local_accumulative_precision, + global_accumulative_recall, + global_accumulative_precision, + gt_flag, + det_flag, + hit_str_num, + ) = many_to_one( + local_sigma_table, + local_tau_table, + local_accumulative_recall, + local_accumulative_precision, + global_accumulative_recall, + global_accumulative_precision, + gt_flag, + det_flag, + idx, + rec_flag, + ) + hit_str_count += hit_str_num + + try: + recall = global_accumulative_recall / total_num_gt + except ZeroDivisionError: + recall = 0 + + try: + precision = global_accumulative_precision / total_num_det + except ZeroDivisionError: + precision = 0 + + try: + f_score = 2 * precision * recall / (precision + recall) + except ZeroDivisionError: + f_score = 0 + + try: + seqerr = 1 - float(hit_str_count) / global_accumulative_recall + except ZeroDivisionError: + seqerr = 1 + + try: + recall_e2e = float(hit_str_count) / total_num_gt + except ZeroDivisionError: + recall_e2e = 0 + + try: + precision_e2e = float(hit_str_count) / total_num_det + except ZeroDivisionError: + precision_e2e = 0 + + try: + f_score_e2e = 2 * precision_e2e * recall_e2e / (precision_e2e + recall_e2e) + except ZeroDivisionError: + f_score_e2e = 0 + + final = { + "total_num_gt": total_num_gt, + "total_num_det": total_num_det, + "global_accumulative_recall": global_accumulative_recall, + "hit_str_count": hit_str_count, + "recall": recall, + "precision": precision, + "f_score": f_score, + "seqerr": seqerr, + "recall_e2e": recall_e2e, + "precision_e2e": precision_e2e, + "f_score_e2e": f_score_e2e, + } + return final diff --git a/modules/onnx_ocr_module/src/ppocr/utils/e2e_metric/polygon_fast.py b/modules/onnx_ocr_module/src/ppocr/utils/e2e_metric/polygon_fast.py new file mode 100644 index 0000000..e2a08c6 --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/utils/e2e_metric/polygon_fast.py @@ -0,0 +1,84 @@ +# Copyright (c) 2021 PaddlePaddle Authors. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +import numpy as np +from shapely.geometry import Polygon + +""" +:param det_x: [1, N] Xs of detection's vertices +:param det_y: [1, N] Ys of detection's vertices +:param gt_x: [1, N] Xs of groundtruth's vertices +:param gt_y: [1, N] Ys of groundtruth's vertices + +############## +All the calculation of 'AREA' in this script is handled by: +1) First generating a binary mask with the polygon area filled up with 1's +2) Summing up all the 1's +""" + + +def area(x, y): + polygon = Polygon(np.stack([x, y], axis=1)) + return float(polygon.area) + + +def approx_area_of_intersection(det_x, det_y, gt_x, gt_y): + """ + This helper determine if both polygons are intersecting with each others with an approximation method. + Area of intersection represented by the minimum bounding rectangular [xmin, ymin, xmax, ymax] + """ + det_ymax = np.max(det_y) + det_xmax = np.max(det_x) + det_ymin = np.min(det_y) + det_xmin = np.min(det_x) + + gt_ymax = np.max(gt_y) + gt_xmax = np.max(gt_x) + gt_ymin = np.min(gt_y) + gt_xmin = np.min(gt_x) + + all_min_ymax = np.minimum(det_ymax, gt_ymax) + all_max_ymin = np.maximum(det_ymin, gt_ymin) + + intersect_heights = np.maximum(0.0, (all_min_ymax - all_max_ymin)) + + all_min_xmax = np.minimum(det_xmax, gt_xmax) + all_max_xmin = np.maximum(det_xmin, gt_xmin) + intersect_widths = np.maximum(0.0, (all_min_xmax - all_max_xmin)) + + return intersect_heights * intersect_widths + + +def area_of_intersection(det_x, det_y, gt_x, gt_y): + p1 = Polygon(np.stack([det_x, det_y], axis=1)).buffer(0) + p2 = Polygon(np.stack([gt_x, gt_y], axis=1)).buffer(0) + return float(p1.intersection(p2).area) + + +def area_of_union(det_x, det_y, gt_x, gt_y): + p1 = Polygon(np.stack([det_x, det_y], axis=1)).buffer(0) + p2 = Polygon(np.stack([gt_x, gt_y], axis=1)).buffer(0) + return float(p1.union(p2).area) + + +def iou(det_x, det_y, gt_x, gt_y): + return area_of_intersection(det_x, det_y, gt_x, gt_y) / ( + area_of_union(det_x, det_y, gt_x, gt_y) + 1.0 + ) + + +def iod(det_x, det_y, gt_x, gt_y): + """ + This helper determine the fraction of intersection area over detection area + """ + return area_of_intersection(det_x, det_y, gt_x, gt_y) / (area(det_x, det_y) + 1.0) diff --git a/modules/onnx_ocr_module/src/ppocr/utils/e2e_utils/__pycache__/extract_textpoint_fast.cpython-311.pyc b/modules/onnx_ocr_module/src/ppocr/utils/e2e_utils/__pycache__/extract_textpoint_fast.cpython-311.pyc new file mode 100644 index 0000000..fb939c8 Binary files /dev/null and b/modules/onnx_ocr_module/src/ppocr/utils/e2e_utils/__pycache__/extract_textpoint_fast.cpython-311.pyc differ diff --git a/modules/onnx_ocr_module/src/ppocr/utils/e2e_utils/__pycache__/extract_textpoint_slow.cpython-311.pyc b/modules/onnx_ocr_module/src/ppocr/utils/e2e_utils/__pycache__/extract_textpoint_slow.cpython-311.pyc new file mode 100644 index 0000000..36f9420 Binary files /dev/null and b/modules/onnx_ocr_module/src/ppocr/utils/e2e_utils/__pycache__/extract_textpoint_slow.cpython-311.pyc differ diff --git a/modules/onnx_ocr_module/src/ppocr/utils/e2e_utils/__pycache__/pgnet_pp_utils.cpython-311.pyc b/modules/onnx_ocr_module/src/ppocr/utils/e2e_utils/__pycache__/pgnet_pp_utils.cpython-311.pyc new file mode 100644 index 0000000..7f1f47d Binary files /dev/null and b/modules/onnx_ocr_module/src/ppocr/utils/e2e_utils/__pycache__/pgnet_pp_utils.cpython-311.pyc differ diff --git a/modules/onnx_ocr_module/src/ppocr/utils/e2e_utils/extract_batchsize.py b/modules/onnx_ocr_module/src/ppocr/utils/e2e_utils/extract_batchsize.py new file mode 100644 index 0000000..f1ab77b --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/utils/e2e_utils/extract_batchsize.py @@ -0,0 +1,88 @@ +import paddle +import numpy as np +import copy + + +def org_tcl_rois(batch_size, pos_lists, pos_masks, label_lists, tcl_bs): + """ """ + pos_lists_, pos_masks_, label_lists_ = [], [], [] + img_bs = batch_size + ngpu = int(batch_size / img_bs) + img_ids = np.array(pos_lists, dtype=np.int32)[:, 0, 0].copy() + pos_lists_split, pos_masks_split, label_lists_split = [], [], [] + for i in range(ngpu): + pos_lists_split.append([]) + pos_masks_split.append([]) + label_lists_split.append([]) + + for i in range(img_ids.shape[0]): + img_id = img_ids[i] + gpu_id = int(img_id / img_bs) + img_id = img_id % img_bs + pos_list = pos_lists[i].copy() + pos_list[:, 0] = img_id + pos_lists_split[gpu_id].append(pos_list) + pos_masks_split[gpu_id].append(pos_masks[i].copy()) + label_lists_split[gpu_id].append(copy.deepcopy(label_lists[i])) + # repeat or delete + for i in range(ngpu): + vp_len = len(pos_lists_split[i]) + if vp_len <= tcl_bs: + for j in range(0, tcl_bs - vp_len): + pos_list = pos_lists_split[i][j].copy() + pos_lists_split[i].append(pos_list) + pos_mask = pos_masks_split[i][j].copy() + pos_masks_split[i].append(pos_mask) + label_list = copy.deepcopy(label_lists_split[i][j]) + label_lists_split[i].append(label_list) + else: + for j in range(0, vp_len - tcl_bs): + c_len = len(pos_lists_split[i]) + pop_id = np.random.permutation(c_len)[0] + pos_lists_split[i].pop(pop_id) + pos_masks_split[i].pop(pop_id) + label_lists_split[i].pop(pop_id) + # merge + for i in range(ngpu): + pos_lists_.extend(pos_lists_split[i]) + pos_masks_.extend(pos_masks_split[i]) + label_lists_.extend(label_lists_split[i]) + return pos_lists_, pos_masks_, label_lists_ + + +def pre_process( + label_list, pos_list, pos_mask, max_text_length, max_text_nums, pad_num, tcl_bs +): + label_list = label_list.numpy() + batch, _, _, _ = label_list.shape + pos_list = pos_list.numpy() + pos_mask = pos_mask.numpy() + pos_list_t = [] + pos_mask_t = [] + label_list_t = [] + for i in range(batch): + for j in range(max_text_nums): + if pos_mask[i, j].any(): + pos_list_t.append(pos_list[i][j]) + pos_mask_t.append(pos_mask[i][j]) + label_list_t.append(label_list[i][j]) + pos_list, pos_mask, label_list = org_tcl_rois( + batch, pos_list_t, pos_mask_t, label_list_t, tcl_bs + ) + label = [] + tt = [l.tolist() for l in label_list] + for i in range(tcl_bs): + k = 0 + for j in range(max_text_length): + if tt[i][j][0] != pad_num: + k += 1 + else: + break + label.append(k) + label = paddle.to_tensor(label) + label = paddle.cast(label, dtype="int64") + pos_list = paddle.to_tensor(pos_list) + pos_mask = paddle.to_tensor(pos_mask) + label_list = paddle.squeeze(paddle.to_tensor(label_list), axis=2) + label_list = paddle.cast(label_list, dtype="int32") + return pos_list, pos_mask, label_list, label diff --git a/modules/onnx_ocr_module/src/ppocr/utils/e2e_utils/extract_textpoint_fast.py b/modules/onnx_ocr_module/src/ppocr/utils/e2e_utils/extract_textpoint_fast.py new file mode 100644 index 0000000..e399fd0 --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/utils/e2e_utils/extract_textpoint_fast.py @@ -0,0 +1,553 @@ +# Copyright (c) 2021 PaddlePaddle Authors. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +"""Contains various CTC decoders.""" +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import cv2 +import math + +import numpy as np +from itertools import groupby +from skimage.morphology._skeletonize import thin + + +def get_dict(character_dict_path): + character_str = "" + # UTF-8 인코딩 문제 해결을 위한 안전한 파일 읽기 + try: + with open(character_dict_path, "r", encoding="utf-8") as fin: + lines = fin.readlines() + for line in lines: + line = line.strip("\n").strip("\r\n") + character_str += line + except UnicodeDecodeError: + # UTF-8 실패 시 다른 인코딩 시도 + encodings = ['cp949', 'euc-kr', 'latin1', 'cp1252'] + success = False + for encoding in encodings: + try: + with open(character_dict_path, "r", encoding=encoding) as fin: + lines = fin.readlines() + for line in lines: + line = line.strip("\n").strip("\r\n") + character_str += line + success = True + break + except UnicodeDecodeError: + continue + + if not success: + # 모든 인코딩 실패 시 바이너리 모드로 시도 (기존 방식) + with open(character_dict_path, "rb") as fin: + lines = fin.readlines() + for line in lines: + try: + line = line.decode("utf-8").strip("\n").strip("\r\n") + character_str += line + except UnicodeDecodeError: + # 개별 라인 디코딩 실패 시 건너뛰기 + continue + + dict_character = list(character_str) + return dict_character + + +def softmax(logits): + """ + logits: N x d + """ + max_value = np.max(logits, axis=1, keepdims=True) + exp = np.exp(logits - max_value) + exp_sum = np.sum(exp, axis=1, keepdims=True) + dist = exp / exp_sum + return dist + + +def get_keep_pos_idxs(labels, remove_blank=None): + """ + Remove duplicate and get pos idxs of keep items. + The value of keep_blank should be [None, 95]. + """ + duplicate_len_list = [] + keep_pos_idx_list = [] + keep_char_idx_list = [] + for k, v_ in groupby(labels): + current_len = len(list(v_)) + if k != remove_blank: + current_idx = int(sum(duplicate_len_list) + current_len // 2) + keep_pos_idx_list.append(current_idx) + keep_char_idx_list.append(k) + duplicate_len_list.append(current_len) + return keep_char_idx_list, keep_pos_idx_list + + +def remove_blank(labels, blank=0): + new_labels = [x for x in labels if x != blank] + return new_labels + + +def insert_blank(labels, blank=0): + new_labels = [blank] + for l in labels: + new_labels += [l, blank] + return new_labels + + +def ctc_greedy_decoder(probs_seq, blank=95, keep_blank_in_idxs=True): + """ + CTC greedy (best path) decoder. + """ + raw_str = np.argmax(np.array(probs_seq), axis=1) + remove_blank_in_pos = None if keep_blank_in_idxs else blank + dedup_str, keep_idx_list = get_keep_pos_idxs( + raw_str, remove_blank=remove_blank_in_pos + ) + dst_str = remove_blank(dedup_str, blank=blank) + return dst_str, keep_idx_list + + +def instance_ctc_greedy_decoder( + gather_info, logits_map, pts_num=4, point_gather_mode=None +): + _, _, C = logits_map.shape + if point_gather_mode == "align": + insert_num = 0 + gather_info = np.array(gather_info) + length = len(gather_info) - 1 + for index in range(length): + stride_y = np.abs( + gather_info[index + insert_num][0] + - gather_info[index + 1 + insert_num][0] + ) + stride_x = np.abs( + gather_info[index + insert_num][1] + - gather_info[index + 1 + insert_num][1] + ) + max_points = int(max(stride_x, stride_y)) + stride = ( + gather_info[index + insert_num] - gather_info[index + 1 + insert_num] + ) / (max_points) + insert_num_temp = max_points - 1 + + for i in range(int(insert_num_temp)): + insert_value = gather_info[index + insert_num] - (i + 1) * stride + insert_index = index + i + 1 + insert_num + gather_info = np.insert(gather_info, insert_index, insert_value, axis=0) + insert_num += insert_num_temp + gather_info = gather_info.tolist() + else: + pass + ys, xs = zip(*gather_info) + logits_seq = logits_map[list(ys), list(xs)] + probs_seq = logits_seq + labels = np.argmax(probs_seq, axis=1) + dst_str = [k for k, v_ in groupby(labels) if k != C - 1] + detal = len(gather_info) // (pts_num - 1) + keep_idx_list = [0] + [detal * (i + 1) for i in range(pts_num - 2)] + [-1] + keep_gather_list = [gather_info[idx] for idx in keep_idx_list] + return dst_str, keep_gather_list + + +def ctc_decoder_for_image( + gather_info_list, logits_map, Lexicon_Table, pts_num=6, point_gather_mode=None +): + """ + CTC decoder using multiple processes. + """ + decoder_str = [] + decoder_xys = [] + for gather_info in gather_info_list: + if len(gather_info) < pts_num: + continue + dst_str, xys_list = instance_ctc_greedy_decoder( + gather_info, + logits_map, + pts_num=pts_num, + point_gather_mode=point_gather_mode, + ) + dst_str_readable = "".join([Lexicon_Table[idx] for idx in dst_str]) + if len(dst_str_readable) < 2: + continue + decoder_str.append(dst_str_readable) + decoder_xys.append(xys_list) + return decoder_str, decoder_xys + + +def sort_with_direction(pos_list, f_direction): + """ + f_direction: h x w x 2 + pos_list: [[y, x], [y, x], [y, x] ...] + """ + + def sort_part_with_direction(pos_list, point_direction): + pos_list = np.array(pos_list).reshape(-1, 2) + point_direction = np.array(point_direction).reshape(-1, 2) + average_direction = np.mean(point_direction, axis=0, keepdims=True) + pos_proj_leng = np.sum(pos_list * average_direction, axis=1) + sorted_list = pos_list[np.argsort(pos_proj_leng)].tolist() + sorted_direction = point_direction[np.argsort(pos_proj_leng)].tolist() + return sorted_list, sorted_direction + + pos_list = np.array(pos_list).reshape(-1, 2) + point_direction = f_direction[pos_list[:, 0], pos_list[:, 1]] # x, y + point_direction = point_direction[:, ::-1] # x, y -> y, x + sorted_point, sorted_direction = sort_part_with_direction(pos_list, point_direction) + + point_num = len(sorted_point) + if point_num >= 16: + middle_num = point_num // 2 + first_part_point = sorted_point[:middle_num] + first_point_direction = sorted_direction[:middle_num] + sorted_fist_part_point, sorted_fist_part_direction = sort_part_with_direction( + first_part_point, first_point_direction + ) + + last_part_point = sorted_point[middle_num:] + last_point_direction = sorted_direction[middle_num:] + sorted_last_part_point, sorted_last_part_direction = sort_part_with_direction( + last_part_point, last_point_direction + ) + sorted_point = sorted_fist_part_point + sorted_last_part_point + sorted_direction = sorted_fist_part_direction + sorted_last_part_direction + + return sorted_point, np.array(sorted_direction) + + +def add_id(pos_list, image_id=0): + """ + Add id for gather feature, for inference. + """ + new_list = [] + for item in pos_list: + new_list.append((image_id, item[0], item[1])) + return new_list + + +def sort_and_expand_with_direction(pos_list, f_direction): + """ + f_direction: h x w x 2 + pos_list: [[y, x], [y, x], [y, x] ...] + """ + h, w, _ = f_direction.shape + sorted_list, point_direction = sort_with_direction(pos_list, f_direction) + + point_num = len(sorted_list) + sub_direction_len = max(point_num // 3, 2) + left_direction = point_direction[:sub_direction_len, :] + right_dirction = point_direction[point_num - sub_direction_len :, :] + + left_average_direction = -np.mean(left_direction, axis=0, keepdims=True) + left_average_len = np.linalg.norm(left_average_direction) + left_start = np.array(sorted_list[0]) + left_step = left_average_direction / (left_average_len + 1e-6) + + right_average_direction = np.mean(right_dirction, axis=0, keepdims=True) + right_average_len = np.linalg.norm(right_average_direction) + right_step = right_average_direction / (right_average_len + 1e-6) + right_start = np.array(sorted_list[-1]) + + append_num = max(int((left_average_len + right_average_len) / 2.0 * 0.15), 1) + left_list = [] + right_list = [] + for i in range(append_num): + ly, lx = ( + np.round(left_start + left_step * (i + 1)) + .flatten() + .astype("int32") + .tolist() + ) + if ly < h and lx < w and (ly, lx) not in left_list: + left_list.append((ly, lx)) + ry, rx = ( + np.round(right_start + right_step * (i + 1)) + .flatten() + .astype("int32") + .tolist() + ) + if ry < h and rx < w and (ry, rx) not in right_list: + right_list.append((ry, rx)) + + all_list = left_list[::-1] + sorted_list + right_list + return all_list + + +def sort_and_expand_with_direction_v2(pos_list, f_direction, binary_tcl_map): + """ + f_direction: h x w x 2 + pos_list: [[y, x], [y, x], [y, x] ...] + binary_tcl_map: h x w + """ + h, w, _ = f_direction.shape + sorted_list, point_direction = sort_with_direction(pos_list, f_direction) + + point_num = len(sorted_list) + sub_direction_len = max(point_num // 3, 2) + left_direction = point_direction[:sub_direction_len, :] + right_dirction = point_direction[point_num - sub_direction_len :, :] + + left_average_direction = -np.mean(left_direction, axis=0, keepdims=True) + left_average_len = np.linalg.norm(left_average_direction) + left_start = np.array(sorted_list[0]) + left_step = left_average_direction / (left_average_len + 1e-6) + + right_average_direction = np.mean(right_dirction, axis=0, keepdims=True) + right_average_len = np.linalg.norm(right_average_direction) + right_step = right_average_direction / (right_average_len + 1e-6) + right_start = np.array(sorted_list[-1]) + + append_num = max(int((left_average_len + right_average_len) / 2.0 * 0.15), 1) + max_append_num = 2 * append_num + + left_list = [] + right_list = [] + for i in range(max_append_num): + ly, lx = ( + np.round(left_start + left_step * (i + 1)) + .flatten() + .astype("int32") + .tolist() + ) + if ly < h and lx < w and (ly, lx) not in left_list: + if binary_tcl_map[ly, lx] > 0.5: + left_list.append((ly, lx)) + else: + break + + for i in range(max_append_num): + ry, rx = ( + np.round(right_start + right_step * (i + 1)) + .flatten() + .astype("int32") + .tolist() + ) + if ry < h and rx < w and (ry, rx) not in right_list: + if binary_tcl_map[ry, rx] > 0.5: + right_list.append((ry, rx)) + else: + break + + all_list = left_list[::-1] + sorted_list + right_list + return all_list + + +def point_pair2poly(point_pair_list): + """ + Transfer vertical point_pairs into poly point in clockwise. + """ + point_num = len(point_pair_list) * 2 + point_list = [0] * point_num + for idx, point_pair in enumerate(point_pair_list): + point_list[idx] = point_pair[0] + point_list[point_num - 1 - idx] = point_pair[1] + return np.array(point_list).reshape(-1, 2) + + +def shrink_quad_along_width(quad, begin_width_ratio=0.0, end_width_ratio=1.0): + ratio_pair = np.array([[begin_width_ratio], [end_width_ratio]], dtype=np.float32) + p0_1 = quad[0] + (quad[1] - quad[0]) * ratio_pair + p3_2 = quad[3] + (quad[2] - quad[3]) * ratio_pair + return np.array([p0_1[0], p0_1[1], p3_2[1], p3_2[0]]) + + +def expand_poly_along_width(poly, shrink_ratio_of_width=0.3): + """ + expand poly along width. + """ + point_num = poly.shape[0] + left_quad = np.array([poly[0], poly[1], poly[-2], poly[-1]], dtype=np.float32) + left_ratio = ( + -shrink_ratio_of_width + * np.linalg.norm(left_quad[0] - left_quad[3]) + / (np.linalg.norm(left_quad[0] - left_quad[1]) + 1e-6) + ) + left_quad_expand = shrink_quad_along_width(left_quad, left_ratio, 1.0) + right_quad = np.array( + [ + poly[point_num // 2 - 2], + poly[point_num // 2 - 1], + poly[point_num // 2], + poly[point_num // 2 + 1], + ], + dtype=np.float32, + ) + right_ratio = 1.0 + shrink_ratio_of_width * np.linalg.norm( + right_quad[0] - right_quad[3] + ) / (np.linalg.norm(right_quad[0] - right_quad[1]) + 1e-6) + right_quad_expand = shrink_quad_along_width(right_quad, 0.0, right_ratio) + poly[0] = left_quad_expand[0] + poly[-1] = left_quad_expand[-1] + poly[point_num // 2 - 1] = right_quad_expand[1] + poly[point_num // 2] = right_quad_expand[2] + return poly + + +def restore_poly( + instance_yxs_list, seq_strs, p_border, ratio_w, ratio_h, src_w, src_h, valid_set +): + poly_list = [] + keep_str_list = [] + for yx_center_line, keep_str in zip(instance_yxs_list, seq_strs): + if len(keep_str) < 2: + print("--> too short, {}".format(keep_str)) + continue + + offset_expand = 1.0 + if valid_set == "totaltext": + offset_expand = 1.2 + + point_pair_list = [] + for y, x in yx_center_line: + offset = p_border[:, y, x].reshape(2, 2) * offset_expand + ori_yx = np.array([y, x], dtype=np.float32) + point_pair = ( + (ori_yx + offset)[:, ::-1] + * 4.0 + / np.array([ratio_w, ratio_h]).reshape(-1, 2) + ) + point_pair_list.append(point_pair) + + detected_poly = point_pair2poly(point_pair_list) + detected_poly = expand_poly_along_width( + detected_poly, shrink_ratio_of_width=0.2 + ) + detected_poly[:, 0] = np.clip(detected_poly[:, 0], a_min=0, a_max=src_w) + detected_poly[:, 1] = np.clip(detected_poly[:, 1], a_min=0, a_max=src_h) + + keep_str_list.append(keep_str) + if valid_set == "partvgg": + middle_point = len(detected_poly) // 2 + detected_poly = detected_poly[[0, middle_point - 1, middle_point, -1], :] + poly_list.append(detected_poly) + elif valid_set == "totaltext": + poly_list.append(detected_poly) + else: + print("--> Not supported format.") + exit(-1) + return poly_list, keep_str_list + + +def generate_pivot_list_fast( + p_score, + p_char_maps, + f_direction, + Lexicon_Table, + score_thresh=0.5, + point_gather_mode=None, +): + """ + return center point and end point of TCL instance; filter with the char maps; + """ + p_score = p_score[0] + f_direction = f_direction.transpose(1, 2, 0) + p_tcl_map = (p_score > score_thresh) * 1.0 + skeleton_map = thin(p_tcl_map.astype(np.uint8)) + instance_count, instance_label_map = cv2.connectedComponents( + skeleton_map.astype(np.uint8), connectivity=8 + ) + + # get TCL Instance + all_pos_yxs = [] + if instance_count > 0: + for instance_id in range(1, instance_count): + pos_list = [] + ys, xs = np.where(instance_label_map == instance_id) + pos_list = list(zip(ys, xs)) + + if len(pos_list) < 3: + continue + + pos_list_sorted = sort_and_expand_with_direction_v2( + pos_list, f_direction, p_tcl_map + ) + all_pos_yxs.append(pos_list_sorted) + + p_char_maps = p_char_maps.transpose([1, 2, 0]) + decoded_str, keep_yxs_list = ctc_decoder_for_image( + all_pos_yxs, + logits_map=p_char_maps, + Lexicon_Table=Lexicon_Table, + point_gather_mode=point_gather_mode, + ) + return keep_yxs_list, decoded_str + + +def extract_main_direction(pos_list, f_direction): + """ + f_direction: h x w x 2 + pos_list: [[y, x], [y, x], [y, x] ...] + """ + pos_list = np.array(pos_list) + point_direction = f_direction[pos_list[:, 0], pos_list[:, 1]] + point_direction = point_direction[:, ::-1] # x, y -> y, x + average_direction = np.mean(point_direction, axis=0, keepdims=True) + average_direction = average_direction / (np.linalg.norm(average_direction) + 1e-6) + return average_direction + + +def sort_by_direction_with_image_id_deprecated(pos_list, f_direction): + """ + f_direction: h x w x 2 + pos_list: [[id, y, x], [id, y, x], [id, y, x] ...] + """ + pos_list_full = np.array(pos_list).reshape(-1, 3) + pos_list = pos_list_full[:, 1:] + point_direction = f_direction[pos_list[:, 0], pos_list[:, 1]] # x, y + point_direction = point_direction[:, ::-1] # x, y -> y, x + average_direction = np.mean(point_direction, axis=0, keepdims=True) + pos_proj_leng = np.sum(pos_list * average_direction, axis=1) + sorted_list = pos_list_full[np.argsort(pos_proj_leng)].tolist() + return sorted_list + + +def sort_by_direction_with_image_id(pos_list, f_direction): + """ + f_direction: h x w x 2 + pos_list: [[y, x], [y, x], [y, x] ...] + """ + + def sort_part_with_direction(pos_list_full, point_direction): + pos_list_full = np.array(pos_list_full).reshape(-1, 3) + pos_list = pos_list_full[:, 1:] + point_direction = np.array(point_direction).reshape(-1, 2) + average_direction = np.mean(point_direction, axis=0, keepdims=True) + pos_proj_leng = np.sum(pos_list * average_direction, axis=1) + sorted_list = pos_list_full[np.argsort(pos_proj_leng)].tolist() + sorted_direction = point_direction[np.argsort(pos_proj_leng)].tolist() + return sorted_list, sorted_direction + + pos_list = np.array(pos_list).reshape(-1, 3) + point_direction = f_direction[pos_list[:, 1], pos_list[:, 2]] # x, y + point_direction = point_direction[:, ::-1] # x, y -> y, x + sorted_point, sorted_direction = sort_part_with_direction(pos_list, point_direction) + + point_num = len(sorted_point) + if point_num >= 16: + middle_num = point_num // 2 + first_part_point = sorted_point[:middle_num] + first_point_direction = sorted_direction[:middle_num] + sorted_fist_part_point, sorted_fist_part_direction = sort_part_with_direction( + first_part_point, first_point_direction + ) + + last_part_point = sorted_point[middle_num:] + last_point_direction = sorted_direction[middle_num:] + sorted_last_part_point, sorted_last_part_direction = sort_part_with_direction( + last_part_point, last_point_direction + ) + sorted_point = sorted_fist_part_point + sorted_last_part_point + sorted_direction = sorted_fist_part_direction + sorted_last_part_direction + + return sorted_point diff --git a/modules/onnx_ocr_module/src/ppocr/utils/e2e_utils/extract_textpoint_slow.py b/modules/onnx_ocr_module/src/ppocr/utils/e2e_utils/extract_textpoint_slow.py new file mode 100644 index 0000000..2beff89 --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/utils/e2e_utils/extract_textpoint_slow.py @@ -0,0 +1,624 @@ +# Copyright (c) 2021 PaddlePaddle Authors. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +"""Contains various CTC decoders.""" +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import cv2 +import math + +import numpy as np +from itertools import groupby +from skimage.morphology._skeletonize import thin + + +def get_dict(character_dict_path): + character_str = "" + with open(character_dict_path, "rb") as fin: + lines = fin.readlines() + for line in lines: + line = line.decode("utf-8").strip("\n").strip("\r\n") + character_str += line + dict_character = list(character_str) + return dict_character + + +def point_pair2poly(point_pair_list): + """ + Transfer vertical point_pairs into poly point in clockwise. + """ + pair_length_list = [] + for point_pair in point_pair_list: + pair_length = np.linalg.norm(point_pair[0] - point_pair[1]) + pair_length_list.append(pair_length) + pair_length_list = np.array(pair_length_list) + pair_info = ( + pair_length_list.max(), + pair_length_list.min(), + pair_length_list.mean(), + ) + + point_num = len(point_pair_list) * 2 + point_list = [0] * point_num + for idx, point_pair in enumerate(point_pair_list): + point_list[idx] = point_pair[0] + point_list[point_num - 1 - idx] = point_pair[1] + return np.array(point_list).reshape(-1, 2), pair_info + + +def shrink_quad_along_width(quad, begin_width_ratio=0.0, end_width_ratio=1.0): + """ + Generate shrink_quad_along_width. + """ + ratio_pair = np.array([[begin_width_ratio], [end_width_ratio]], dtype=np.float32) + p0_1 = quad[0] + (quad[1] - quad[0]) * ratio_pair + p3_2 = quad[3] + (quad[2] - quad[3]) * ratio_pair + return np.array([p0_1[0], p0_1[1], p3_2[1], p3_2[0]]) + + +def expand_poly_along_width(poly, shrink_ratio_of_width=0.3): + """ + expand poly along width. + """ + point_num = poly.shape[0] + left_quad = np.array([poly[0], poly[1], poly[-2], poly[-1]], dtype=np.float32) + left_ratio = ( + -shrink_ratio_of_width + * np.linalg.norm(left_quad[0] - left_quad[3]) + / (np.linalg.norm(left_quad[0] - left_quad[1]) + 1e-6) + ) + left_quad_expand = shrink_quad_along_width(left_quad, left_ratio, 1.0) + right_quad = np.array( + [ + poly[point_num // 2 - 2], + poly[point_num // 2 - 1], + poly[point_num // 2], + poly[point_num // 2 + 1], + ], + dtype=np.float32, + ) + right_ratio = 1.0 + shrink_ratio_of_width * np.linalg.norm( + right_quad[0] - right_quad[3] + ) / (np.linalg.norm(right_quad[0] - right_quad[1]) + 1e-6) + right_quad_expand = shrink_quad_along_width(right_quad, 0.0, right_ratio) + poly[0] = left_quad_expand[0] + poly[-1] = left_quad_expand[-1] + poly[point_num // 2 - 1] = right_quad_expand[1] + poly[point_num // 2] = right_quad_expand[2] + return poly + + +def softmax(logits): + """ + logits: N x d + """ + max_value = np.max(logits, axis=1, keepdims=True) + exp = np.exp(logits - max_value) + exp_sum = np.sum(exp, axis=1, keepdims=True) + dist = exp / exp_sum + return dist + + +def get_keep_pos_idxs(labels, remove_blank=None): + """ + Remove duplicate and get pos idxs of keep items. + The value of keep_blank should be [None, 95]. + """ + duplicate_len_list = [] + keep_pos_idx_list = [] + keep_char_idx_list = [] + for k, v_ in groupby(labels): + current_len = len(list(v_)) + if k != remove_blank: + current_idx = int(sum(duplicate_len_list) + current_len // 2) + keep_pos_idx_list.append(current_idx) + keep_char_idx_list.append(k) + duplicate_len_list.append(current_len) + return keep_char_idx_list, keep_pos_idx_list + + +def remove_blank(labels, blank=0): + new_labels = [x for x in labels if x != blank] + return new_labels + + +def insert_blank(labels, blank=0): + new_labels = [blank] + for l in labels: + new_labels += [l, blank] + return new_labels + + +def ctc_greedy_decoder(probs_seq, blank=95, keep_blank_in_idxs=True): + """ + CTC greedy (best path) decoder. + """ + raw_str = np.argmax(np.array(probs_seq), axis=1) + remove_blank_in_pos = None if keep_blank_in_idxs else blank + dedup_str, keep_idx_list = get_keep_pos_idxs( + raw_str, remove_blank=remove_blank_in_pos + ) + dst_str = remove_blank(dedup_str, blank=blank) + return dst_str, keep_idx_list + + +def instance_ctc_greedy_decoder(gather_info, logits_map, keep_blank_in_idxs=True): + """ + gather_info: [[x, y], [x, y] ...] + logits_map: H x W X (n_chars + 1) + """ + _, _, C = logits_map.shape + ys, xs = zip(*gather_info) + logits_seq = logits_map[list(ys), list(xs)] # n x 96 + probs_seq = softmax(logits_seq) + dst_str, keep_idx_list = ctc_greedy_decoder( + probs_seq, blank=C - 1, keep_blank_in_idxs=keep_blank_in_idxs + ) + keep_gather_list = [gather_info[idx] for idx in keep_idx_list] + return dst_str, keep_gather_list + + +def ctc_decoder_for_image(gather_info_list, logits_map, keep_blank_in_idxs=True): + """ + CTC decoder using multiple processes. + """ + decoder_results = [] + for gather_info in gather_info_list: + res = instance_ctc_greedy_decoder( + gather_info, logits_map, keep_blank_in_idxs=keep_blank_in_idxs + ) + decoder_results.append(res) + return decoder_results + + +def sort_with_direction(pos_list, f_direction): + """ + f_direction: h x w x 2 + pos_list: [[y, x], [y, x], [y, x] ...] + """ + + def sort_part_with_direction(pos_list, point_direction): + pos_list = np.array(pos_list).reshape(-1, 2) + point_direction = np.array(point_direction).reshape(-1, 2) + average_direction = np.mean(point_direction, axis=0, keepdims=True) + pos_proj_leng = np.sum(pos_list * average_direction, axis=1) + sorted_list = pos_list[np.argsort(pos_proj_leng)].tolist() + sorted_direction = point_direction[np.argsort(pos_proj_leng)].tolist() + return sorted_list, sorted_direction + + pos_list = np.array(pos_list).reshape(-1, 2) + point_direction = f_direction[pos_list[:, 0], pos_list[:, 1]] # x, y + point_direction = point_direction[:, ::-1] # x, y -> y, x + sorted_point, sorted_direction = sort_part_with_direction(pos_list, point_direction) + + point_num = len(sorted_point) + if point_num >= 16: + middle_num = point_num // 2 + first_part_point = sorted_point[:middle_num] + first_point_direction = sorted_direction[:middle_num] + sorted_fist_part_point, sorted_fist_part_direction = sort_part_with_direction( + first_part_point, first_point_direction + ) + + last_part_point = sorted_point[middle_num:] + last_point_direction = sorted_direction[middle_num:] + sorted_last_part_point, sorted_last_part_direction = sort_part_with_direction( + last_part_point, last_point_direction + ) + sorted_point = sorted_fist_part_point + sorted_last_part_point + sorted_direction = sorted_fist_part_direction + sorted_last_part_direction + + return sorted_point, np.array(sorted_direction) + + +def add_id(pos_list, image_id=0): + """ + Add id for gather feature, for inference. + """ + new_list = [] + for item in pos_list: + new_list.append((image_id, item[0], item[1])) + return new_list + + +def sort_and_expand_with_direction(pos_list, f_direction): + """ + f_direction: h x w x 2 + pos_list: [[y, x], [y, x], [y, x] ...] + """ + h, w, _ = f_direction.shape + sorted_list, point_direction = sort_with_direction(pos_list, f_direction) + + # expand along + point_num = len(sorted_list) + sub_direction_len = max(point_num // 3, 2) + left_direction = point_direction[:sub_direction_len, :] + right_dirction = point_direction[point_num - sub_direction_len :, :] + + left_average_direction = -np.mean(left_direction, axis=0, keepdims=True) + left_average_len = np.linalg.norm(left_average_direction) + left_start = np.array(sorted_list[0]) + left_step = left_average_direction / (left_average_len + 1e-6) + + right_average_direction = np.mean(right_dirction, axis=0, keepdims=True) + right_average_len = np.linalg.norm(right_average_direction) + right_step = right_average_direction / (right_average_len + 1e-6) + right_start = np.array(sorted_list[-1]) + + append_num = max(int((left_average_len + right_average_len) / 2.0 * 0.15), 1) + left_list = [] + right_list = [] + for i in range(append_num): + ly, lx = ( + np.round(left_start + left_step * (i + 1)) + .flatten() + .astype("int32") + .tolist() + ) + if ly < h and lx < w and (ly, lx) not in left_list: + left_list.append((ly, lx)) + ry, rx = ( + np.round(right_start + right_step * (i + 1)) + .flatten() + .astype("int32") + .tolist() + ) + if ry < h and rx < w and (ry, rx) not in right_list: + right_list.append((ry, rx)) + + all_list = left_list[::-1] + sorted_list + right_list + return all_list + + +def sort_and_expand_with_direction_v2(pos_list, f_direction, binary_tcl_map): + """ + f_direction: h x w x 2 + pos_list: [[y, x], [y, x], [y, x] ...] + binary_tcl_map: h x w + """ + h, w, _ = f_direction.shape + sorted_list, point_direction = sort_with_direction(pos_list, f_direction) + + # expand along + point_num = len(sorted_list) + sub_direction_len = max(point_num // 3, 2) + left_direction = point_direction[:sub_direction_len, :] + right_dirction = point_direction[point_num - sub_direction_len :, :] + + left_average_direction = -np.mean(left_direction, axis=0, keepdims=True) + left_average_len = np.linalg.norm(left_average_direction) + left_start = np.array(sorted_list[0]) + left_step = left_average_direction / (left_average_len + 1e-6) + + right_average_direction = np.mean(right_dirction, axis=0, keepdims=True) + right_average_len = np.linalg.norm(right_average_direction) + right_step = right_average_direction / (right_average_len + 1e-6) + right_start = np.array(sorted_list[-1]) + + append_num = max(int((left_average_len + right_average_len) / 2.0 * 0.15), 1) + max_append_num = 2 * append_num + + left_list = [] + right_list = [] + for i in range(max_append_num): + ly, lx = ( + np.round(left_start + left_step * (i + 1)) + .flatten() + .astype("int32") + .tolist() + ) + if ly < h and lx < w and (ly, lx) not in left_list: + if binary_tcl_map[ly, lx] > 0.5: + left_list.append((ly, lx)) + else: + break + + for i in range(max_append_num): + ry, rx = ( + np.round(right_start + right_step * (i + 1)) + .flatten() + .astype("int32") + .tolist() + ) + if ry < h and rx < w and (ry, rx) not in right_list: + if binary_tcl_map[ry, rx] > 0.5: + right_list.append((ry, rx)) + else: + break + + all_list = left_list[::-1] + sorted_list + right_list + return all_list + + +def generate_pivot_list_curved( + p_score, + p_char_maps, + f_direction, + score_thresh=0.5, + is_expand=True, + is_backbone=False, + image_id=0, +): + """ + return center point and end point of TCL instance; filter with the char maps; + """ + p_score = p_score[0] + f_direction = f_direction.transpose(1, 2, 0) + p_tcl_map = (p_score > score_thresh) * 1.0 + skeleton_map = thin(p_tcl_map) + instance_count, instance_label_map = cv2.connectedComponents( + skeleton_map.astype(np.uint8), connectivity=8 + ) + + # get TCL Instance + all_pos_yxs = [] + center_pos_yxs = [] + end_points_yxs = [] + instance_center_pos_yxs = [] + pred_strs = [] + if instance_count > 0: + for instance_id in range(1, instance_count): + pos_list = [] + ys, xs = np.where(instance_label_map == instance_id) + pos_list = list(zip(ys, xs)) + + ### FIX-ME, eliminate outlier + if len(pos_list) < 3: + continue + + if is_expand: + pos_list_sorted = sort_and_expand_with_direction_v2( + pos_list, f_direction, p_tcl_map + ) + else: + pos_list_sorted, _ = sort_with_direction(pos_list, f_direction) + all_pos_yxs.append(pos_list_sorted) + + # use decoder to filter background points. + p_char_maps = p_char_maps.transpose([1, 2, 0]) + decode_res = ctc_decoder_for_image( + all_pos_yxs, logits_map=p_char_maps, keep_blank_in_idxs=True + ) + for decoded_str, keep_yxs_list in decode_res: + if is_backbone: + keep_yxs_list_with_id = add_id(keep_yxs_list, image_id=image_id) + instance_center_pos_yxs.append(keep_yxs_list_with_id) + pred_strs.append(decoded_str) + else: + end_points_yxs.extend((keep_yxs_list[0], keep_yxs_list[-1])) + center_pos_yxs.extend(keep_yxs_list) + + if is_backbone: + return pred_strs, instance_center_pos_yxs + else: + return center_pos_yxs, end_points_yxs + + +def generate_pivot_list_horizontal( + p_score, p_char_maps, f_direction, score_thresh=0.5, is_backbone=False, image_id=0 +): + """ + return center point and end point of TCL instance; filter with the char maps; + """ + p_score = p_score[0] + f_direction = f_direction.transpose(1, 2, 0) + p_tcl_map_bi = (p_score > score_thresh) * 1.0 + instance_count, instance_label_map = cv2.connectedComponents( + p_tcl_map_bi.astype(np.uint8), connectivity=8 + ) + + # get TCL Instance + all_pos_yxs = [] + center_pos_yxs = [] + end_points_yxs = [] + instance_center_pos_yxs = [] + + if instance_count > 0: + for instance_id in range(1, instance_count): + pos_list = [] + ys, xs = np.where(instance_label_map == instance_id) + pos_list = list(zip(ys, xs)) + + ### FIX-ME, eliminate outlier + if len(pos_list) < 5: + continue + + # add rule here + main_direction = extract_main_direction(pos_list, f_direction) # y x + reference_directin = np.array([0, 1]).reshape([-1, 2]) # y x + is_h_angle = abs(np.sum(main_direction * reference_directin)) < math.cos( + math.pi / 180 * 70 + ) + + point_yxs = np.array(pos_list) + max_y, max_x = np.max(point_yxs, axis=0) + min_y, min_x = np.min(point_yxs, axis=0) + is_h_len = (max_y - min_y) < 1.5 * (max_x - min_x) + + pos_list_final = [] + if is_h_len: + xs = np.unique(xs) + for x in xs: + ys = instance_label_map[:, x].copy().reshape((-1,)) + y = int(np.where(ys == instance_id)[0].mean()) + pos_list_final.append((y, x)) + else: + ys = np.unique(ys) + for y in ys: + xs = instance_label_map[y, :].copy().reshape((-1,)) + x = int(np.where(xs == instance_id)[0].mean()) + pos_list_final.append((y, x)) + + pos_list_sorted, _ = sort_with_direction(pos_list_final, f_direction) + all_pos_yxs.append(pos_list_sorted) + + # use decoder to filter background points. + p_char_maps = p_char_maps.transpose([1, 2, 0]) + decode_res = ctc_decoder_for_image( + all_pos_yxs, logits_map=p_char_maps, keep_blank_in_idxs=True + ) + for decoded_str, keep_yxs_list in decode_res: + if is_backbone: + keep_yxs_list_with_id = add_id(keep_yxs_list, image_id=image_id) + instance_center_pos_yxs.append(keep_yxs_list_with_id) + else: + end_points_yxs.extend((keep_yxs_list[0], keep_yxs_list[-1])) + center_pos_yxs.extend(keep_yxs_list) + + if is_backbone: + return instance_center_pos_yxs + else: + return center_pos_yxs, end_points_yxs + + +def generate_pivot_list_slow( + p_score, + p_char_maps, + f_direction, + score_thresh=0.5, + is_backbone=False, + is_curved=True, + image_id=0, +): + """ + Warp all the function together. + """ + if is_curved: + return generate_pivot_list_curved( + p_score, + p_char_maps, + f_direction, + score_thresh=score_thresh, + is_expand=True, + is_backbone=is_backbone, + image_id=image_id, + ) + else: + return generate_pivot_list_horizontal( + p_score, + p_char_maps, + f_direction, + score_thresh=score_thresh, + is_backbone=is_backbone, + image_id=image_id, + ) + + +# for refine module +def extract_main_direction(pos_list, f_direction): + """ + f_direction: h x w x 2 + pos_list: [[y, x], [y, x], [y, x] ...] + """ + pos_list = np.array(pos_list) + point_direction = f_direction[pos_list[:, 0], pos_list[:, 1]] + point_direction = point_direction[:, ::-1] # x, y -> y, x + average_direction = np.mean(point_direction, axis=0, keepdims=True) + average_direction = average_direction / (np.linalg.norm(average_direction) + 1e-6) + return average_direction + + +def sort_by_direction_with_image_id_deprecated(pos_list, f_direction): + """ + f_direction: h x w x 2 + pos_list: [[id, y, x], [id, y, x], [id, y, x] ...] + """ + pos_list_full = np.array(pos_list).reshape(-1, 3) + pos_list = pos_list_full[:, 1:] + point_direction = f_direction[pos_list[:, 0], pos_list[:, 1]] # x, y + point_direction = point_direction[:, ::-1] # x, y -> y, x + average_direction = np.mean(point_direction, axis=0, keepdims=True) + pos_proj_leng = np.sum(pos_list * average_direction, axis=1) + sorted_list = pos_list_full[np.argsort(pos_proj_leng)].tolist() + return sorted_list + + +def sort_by_direction_with_image_id(pos_list, f_direction): + """ + f_direction: h x w x 2 + pos_list: [[y, x], [y, x], [y, x] ...] + """ + + def sort_part_with_direction(pos_list_full, point_direction): + pos_list_full = np.array(pos_list_full).reshape(-1, 3) + pos_list = pos_list_full[:, 1:] + point_direction = np.array(point_direction).reshape(-1, 2) + average_direction = np.mean(point_direction, axis=0, keepdims=True) + pos_proj_leng = np.sum(pos_list * average_direction, axis=1) + sorted_list = pos_list_full[np.argsort(pos_proj_leng)].tolist() + sorted_direction = point_direction[np.argsort(pos_proj_leng)].tolist() + return sorted_list, sorted_direction + + pos_list = np.array(pos_list).reshape(-1, 3) + point_direction = f_direction[pos_list[:, 1], pos_list[:, 2]] # x, y + point_direction = point_direction[:, ::-1] # x, y -> y, x + sorted_point, sorted_direction = sort_part_with_direction(pos_list, point_direction) + + point_num = len(sorted_point) + if point_num >= 16: + middle_num = point_num // 2 + first_part_point = sorted_point[:middle_num] + first_point_direction = sorted_direction[:middle_num] + sorted_fist_part_point, sorted_fist_part_direction = sort_part_with_direction( + first_part_point, first_point_direction + ) + + last_part_point = sorted_point[middle_num:] + last_point_direction = sorted_direction[middle_num:] + sorted_last_part_point, sorted_last_part_direction = sort_part_with_direction( + last_part_point, last_point_direction + ) + sorted_point = sorted_fist_part_point + sorted_last_part_point + sorted_direction = sorted_fist_part_direction + sorted_last_part_direction + + return sorted_point + + +def generate_pivot_list_tt_inference( + p_score, + p_char_maps, + f_direction, + score_thresh=0.5, + is_backbone=False, + is_curved=True, + image_id=0, +): + """ + return center point and end point of TCL instance; filter with the char maps; + """ + p_score = p_score[0] + f_direction = f_direction.transpose(1, 2, 0) + p_tcl_map = (p_score > score_thresh) * 1.0 + skeleton_map = thin(p_tcl_map) + instance_count, instance_label_map = cv2.connectedComponents( + skeleton_map.astype(np.uint8), connectivity=8 + ) + + # get TCL Instance + all_pos_yxs = [] + if instance_count > 0: + for instance_id in range(1, instance_count): + pos_list = [] + ys, xs = np.where(instance_label_map == instance_id) + pos_list = list(zip(ys, xs)) + ### FIX-ME, eliminate outlier + if len(pos_list) < 3: + continue + pos_list_sorted = sort_and_expand_with_direction_v2( + pos_list, f_direction, p_tcl_map + ) + pos_list_sorted_with_id = add_id(pos_list_sorted, image_id=image_id) + all_pos_yxs.append(pos_list_sorted_with_id) + return all_pos_yxs diff --git a/modules/onnx_ocr_module/src/ppocr/utils/e2e_utils/pgnet_pp_utils.py b/modules/onnx_ocr_module/src/ppocr/utils/e2e_utils/pgnet_pp_utils.py new file mode 100644 index 0000000..71379e5 --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/utils/e2e_utils/pgnet_pp_utils.py @@ -0,0 +1,179 @@ +# Copyright (c) 2021 PaddlePaddle Authors. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function +import paddle +import os +import sys + +__dir__ = os.path.dirname(__file__) +sys.path.append(__dir__) +sys.path.append(os.path.join(__dir__, "..")) +from extract_textpoint_slow import * +from extract_textpoint_fast import generate_pivot_list_fast, restore_poly + + +class PGNet_PostProcess(object): + # two different post-process + def __init__( + self, + character_dict_path, + valid_set, + score_thresh, + outs_dict, + shape_list, + point_gather_mode=None, + ): + self.Lexicon_Table = get_dict(character_dict_path) + self.valid_set = valid_set + self.score_thresh = score_thresh + self.outs_dict = outs_dict + self.shape_list = shape_list + self.point_gather_mode = point_gather_mode + + def pg_postprocess_fast(self): + p_score = self.outs_dict["f_score"] + p_border = self.outs_dict["f_border"] + p_char = self.outs_dict["f_char"] + p_direction = self.outs_dict["f_direction"] + if isinstance(p_score, paddle.Tensor): + p_score = p_score[0].numpy() + p_border = p_border[0].numpy() + p_direction = p_direction[0].numpy() + p_char = p_char[0].numpy() + else: + p_score = p_score[0] + p_border = p_border[0] + p_direction = p_direction[0] + p_char = p_char[0] + + src_h, src_w, ratio_h, ratio_w = self.shape_list[0] + instance_yxs_list, seq_strs = generate_pivot_list_fast( + p_score, + p_char, + p_direction, + self.Lexicon_Table, + score_thresh=self.score_thresh, + point_gather_mode=self.point_gather_mode, + ) + poly_list, keep_str_list = restore_poly( + instance_yxs_list, + seq_strs, + p_border, + ratio_w, + ratio_h, + src_w, + src_h, + self.valid_set, + ) + data = { + "points": poly_list, + "texts": keep_str_list, + } + return data + + def pg_postprocess_slow(self): + p_score = self.outs_dict["f_score"] + p_border = self.outs_dict["f_border"] + p_char = self.outs_dict["f_char"] + p_direction = self.outs_dict["f_direction"] + if isinstance(p_score, paddle.Tensor): + p_score = p_score[0].numpy() + p_border = p_border[0].numpy() + p_direction = p_direction[0].numpy() + p_char = p_char[0].numpy() + else: + p_score = p_score[0] + p_border = p_border[0] + p_direction = p_direction[0] + p_char = p_char[0] + src_h, src_w, ratio_h, ratio_w = self.shape_list[0] + is_curved = self.valid_set == "totaltext" + char_seq_idx_set, instance_yxs_list = generate_pivot_list_slow( + p_score, + p_char, + p_direction, + score_thresh=self.score_thresh, + is_backbone=True, + is_curved=is_curved, + ) + seq_strs = [] + for char_idx_set in char_seq_idx_set: + pr_str = "".join([self.Lexicon_Table[pos] for pos in char_idx_set]) + seq_strs.append(pr_str) + poly_list = [] + keep_str_list = [] + all_point_list = [] + all_point_pair_list = [] + for yx_center_line, keep_str in zip(instance_yxs_list, seq_strs): + if len(yx_center_line) == 1: + yx_center_line.append(yx_center_line[-1]) + + offset_expand = 1.0 + if self.valid_set == "totaltext": + offset_expand = 1.2 + + point_pair_list = [] + for batch_id, y, x in yx_center_line: + offset = p_border[:, y, x].reshape(2, 2) + if offset_expand != 1.0: + offset_length = np.linalg.norm(offset, axis=1, keepdims=True) + expand_length = np.clip( + offset_length * (offset_expand - 1), a_min=0.5, a_max=3.0 + ) + offset_detal = offset / offset_length * expand_length + offset = offset + offset_detal + ori_yx = np.array([y, x], dtype=np.float32) + point_pair = ( + (ori_yx + offset)[:, ::-1] + * 4.0 + / np.array([ratio_w, ratio_h]).reshape(-1, 2) + ) + point_pair_list.append(point_pair) + + all_point_list.append( + [int(round(x * 4.0 / ratio_w)), int(round(y * 4.0 / ratio_h))] + ) + all_point_pair_list.append(point_pair.round().astype(np.int32).tolist()) + + detected_poly, pair_length_info = point_pair2poly(point_pair_list) + detected_poly = expand_poly_along_width( + detected_poly, shrink_ratio_of_width=0.2 + ) + detected_poly[:, 0] = np.clip(detected_poly[:, 0], a_min=0, a_max=src_w) + detected_poly[:, 1] = np.clip(detected_poly[:, 1], a_min=0, a_max=src_h) + + if len(keep_str) < 2: + continue + + keep_str_list.append(keep_str) + detected_poly = np.round(detected_poly).astype("int32") + if self.valid_set == "partvgg": + middle_point = len(detected_poly) // 2 + detected_poly = detected_poly[ + [0, middle_point - 1, middle_point, -1], : + ] + poly_list.append(detected_poly) + elif self.valid_set == "totaltext": + poly_list.append(detected_poly) + else: + print("--> Not supported format.") + exit(-1) + data = { + "points": poly_list, + "texts": keep_str_list, + } + return data diff --git a/modules/onnx_ocr_module/src/ppocr/utils/e2e_utils/visual.py b/modules/onnx_ocr_module/src/ppocr/utils/e2e_utils/visual.py new file mode 100644 index 0000000..e321827 --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/utils/e2e_utils/visual.py @@ -0,0 +1,167 @@ +# Copyright (c) 2021 PaddlePaddle Authors. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +import numpy as np +import cv2 +import time + + +def resize_image(im, max_side_len=512): + """ + resize image to a size multiple of max_stride which is required by the network + :param im: the resized image + :param max_side_len: limit of max image size to avoid out of memory in gpu + :return: the resized image and the resize ratio + """ + h, w, _ = im.shape + + resize_w = w + resize_h = h + + if resize_h > resize_w: + ratio = float(max_side_len) / resize_h + else: + ratio = float(max_side_len) / resize_w + + resize_h = int(resize_h * ratio) + resize_w = int(resize_w * ratio) + + max_stride = 128 + resize_h = (resize_h + max_stride - 1) // max_stride * max_stride + resize_w = (resize_w + max_stride - 1) // max_stride * max_stride + im = cv2.resize(im, (int(resize_w), int(resize_h))) + ratio_h = resize_h / float(h) + ratio_w = resize_w / float(w) + + return im, (ratio_h, ratio_w) + + +def resize_image_min(im, max_side_len=512): + """ """ + h, w, _ = im.shape + + resize_w = w + resize_h = h + + if resize_h < resize_w: + ratio = float(max_side_len) / resize_h + else: + ratio = float(max_side_len) / resize_w + + resize_h = int(resize_h * ratio) + resize_w = int(resize_w * ratio) + + max_stride = 128 + resize_h = (resize_h + max_stride - 1) // max_stride * max_stride + resize_w = (resize_w + max_stride - 1) // max_stride * max_stride + im = cv2.resize(im, (int(resize_w), int(resize_h))) + ratio_h = resize_h / float(h) + ratio_w = resize_w / float(w) + return im, (ratio_h, ratio_w) + + +def resize_image_for_totaltext(im, max_side_len=512): + """ """ + h, w, _ = im.shape + + resize_w = w + resize_h = h + ratio = 1.25 + if h * ratio > max_side_len: + ratio = float(max_side_len) / resize_h + + resize_h = int(resize_h * ratio) + resize_w = int(resize_w * ratio) + + max_stride = 128 + resize_h = (resize_h + max_stride - 1) // max_stride * max_stride + resize_w = (resize_w + max_stride - 1) // max_stride * max_stride + im = cv2.resize(im, (int(resize_w), int(resize_h))) + ratio_h = resize_h / float(h) + ratio_w = resize_w / float(w) + return im, (ratio_h, ratio_w) + + +def point_pair2poly(point_pair_list): + """ + Transfer vertical point_pairs into poly point in clockwise. + """ + pair_length_list = [] + for point_pair in point_pair_list: + pair_length = np.linalg.norm(point_pair[0] - point_pair[1]) + pair_length_list.append(pair_length) + pair_length_list = np.array(pair_length_list) + pair_info = ( + pair_length_list.max(), + pair_length_list.min(), + pair_length_list.mean(), + ) + + point_num = len(point_pair_list) * 2 + point_list = [0] * point_num + for idx, point_pair in enumerate(point_pair_list): + point_list[idx] = point_pair[0] + point_list[point_num - 1 - idx] = point_pair[1] + return np.array(point_list).reshape(-1, 2), pair_info + + +def shrink_quad_along_width(quad, begin_width_ratio=0.0, end_width_ratio=1.0): + """ + Generate shrink_quad_along_width. + """ + ratio_pair = np.array([[begin_width_ratio], [end_width_ratio]], dtype=np.float32) + p0_1 = quad[0] + (quad[1] - quad[0]) * ratio_pair + p3_2 = quad[3] + (quad[2] - quad[3]) * ratio_pair + return np.array([p0_1[0], p0_1[1], p3_2[1], p3_2[0]]) + + +def expand_poly_along_width(poly, shrink_ratio_of_width=0.3): + """ + expand poly along width. + """ + point_num = poly.shape[0] + left_quad = np.array([poly[0], poly[1], poly[-2], poly[-1]], dtype=np.float32) + left_ratio = ( + -shrink_ratio_of_width + * np.linalg.norm(left_quad[0] - left_quad[3]) + / (np.linalg.norm(left_quad[0] - left_quad[1]) + 1e-6) + ) + left_quad_expand = shrink_quad_along_width(left_quad, left_ratio, 1.0) + right_quad = np.array( + [ + poly[point_num // 2 - 2], + poly[point_num // 2 - 1], + poly[point_num // 2], + poly[point_num // 2 + 1], + ], + dtype=np.float32, + ) + right_ratio = 1.0 + shrink_ratio_of_width * np.linalg.norm( + right_quad[0] - right_quad[3] + ) / (np.linalg.norm(right_quad[0] - right_quad[1]) + 1e-6) + right_quad_expand = shrink_quad_along_width(right_quad, 0.0, right_ratio) + poly[0] = left_quad_expand[0] + poly[-1] = left_quad_expand[-1] + poly[point_num // 2 - 1] = right_quad_expand[1] + poly[point_num // 2] = right_quad_expand[2] + return poly + + +def norm2(x, axis=None): + if axis: + return np.sqrt(np.sum(x**2, axis=axis)) + return np.sqrt(np.sum(x**2)) + + +def cos(p1, p2): + return (p1 * p2).sum() / (norm2(p1) * norm2(p2)) diff --git a/modules/onnx_ocr_module/src/ppocr/utils/en_dict.txt b/modules/onnx_ocr_module/src/ppocr/utils/en_dict.txt new file mode 100644 index 0000000..7677d31 --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/utils/en_dict.txt @@ -0,0 +1,95 @@ +0 +1 +2 +3 +4 +5 +6 +7 +8 +9 +: +; +< += +> +? +@ +A +B +C +D +E +F +G +H +I +J +K +L +M +N +O +P +Q +R +S +T +U +V +W +X +Y +Z +[ +\ +] +^ +_ +` +a +b +c +d +e +f +g +h +i +j +k +l +m +n +o +p +q +r +s +t +u +v +w +x +y +z +{ +| +} +~ +! +" +# +$ +% +& +' +( +) +* ++ +, +- +. +/ + diff --git a/modules/onnx_ocr_module/src/ppocr/utils/export_model.py b/modules/onnx_ocr_module/src/ppocr/utils/export_model.py new file mode 100644 index 0000000..9d2f766 --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/utils/export_model.py @@ -0,0 +1,532 @@ +# Copyright (c) 2024 PaddlePaddle Authors. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import os +import yaml +import json +import copy +import shutil +import paddle +import paddle.nn as nn +from paddle.jit import to_static + +from collections import OrderedDict +from packaging import version +from argparse import ArgumentParser, RawDescriptionHelpFormatter +from ppocr.modeling.architectures import build_model +from ppocr.postprocess import build_post_process +from ppocr.utils.save_load import load_model +from ppocr.utils.logging import get_logger + + +def represent_dictionary_order(self, dict_data): + return self.represent_mapping("tag:yaml.org,2002:map", dict_data.items()) + + +def setup_orderdict(): + yaml.add_representer(OrderedDict, represent_dictionary_order) + + +def dump_infer_config(config, path, logger): + setup_orderdict() + infer_cfg = OrderedDict() + if not os.path.exists(os.path.dirname(path)): + os.makedirs(os.path.dirname(path)) + if config["Global"].get("pdx_model_name", None): + infer_cfg["Global"] = {"model_name": config["Global"]["pdx_model_name"]} + if config["Global"].get("uniform_output_enabled", None): + arch_config = config["Architecture"] + if arch_config["algorithm"] in ["SVTR_LCNet", "SVTR_HGNet"]: + common_dynamic_shapes = { + "x": [[1, 3, 48, 160], [1, 3, 48, 320], [8, 3, 48, 640]] + } + elif arch_config["model_type"] == "det": + common_dynamic_shapes = { + "x": [[1, 3, 160, 160], [1, 3, 640, 640], [1, 3, 1280, 1280]] + } + elif arch_config["algorithm"] == "SLANet": + if config["Global"].get("pdx_model_name", None) == "SLANet_plus": + common_dynamic_shapes = { + "x": [[1, 3, 32, 32], [1, 3, 64, 448], [1, 3, 488, 488]] + } + else: + common_dynamic_shapes = { + "x": [[1, 3, 32, 32], [1, 3, 64, 448], [8, 3, 488, 488]] + } + elif arch_config["algorithm"] == "SLANeXt": + common_dynamic_shapes = { + "x": [[1, 3, 512, 512], [1, 3, 512, 512], [1, 3, 512, 512]] + } + elif arch_config["algorithm"] == "LaTeXOCR": + common_dynamic_shapes = { + "x": [[1, 1, 32, 32], [1, 1, 64, 448], [1, 1, 192, 672]] + } + elif arch_config["algorithm"] == "UniMERNet": + common_dynamic_shapes = { + "x": [[1, 1, 192, 672], [1, 1, 192, 672], [8, 1, 192, 672]] + } + elif arch_config["algorithm"] == "PP-FormulaNet-L": + common_dynamic_shapes = { + "x": [[1, 1, 768, 768], [1, 1, 768, 768], [8, 1, 768, 768]] + } + elif arch_config["algorithm"] == "PP-FormulaNet-S": + common_dynamic_shapes = { + "x": [[1, 1, 384, 384], [1, 1, 384, 384], [8, 1, 384, 384]] + } + else: + common_dynamic_shapes = None + + backend_keys = ["paddle_infer", "tensorrt"] + hpi_config = { + "backend_configs": { + key: { + ( + "dynamic_shapes" if key == "tensorrt" else "trt_dynamic_shapes" + ): common_dynamic_shapes + } + for key in backend_keys + } + } + if common_dynamic_shapes: + infer_cfg["Hpi"] = hpi_config + + infer_cfg["PreProcess"] = {"transform_ops": config["Eval"]["dataset"]["transforms"]} + postprocess = OrderedDict() + for k, v in config["PostProcess"].items(): + if config["Architecture"].get("algorithm") in [ + "LaTeXOCR", + "UniMERNet", + "PP-FormulaNet-L", + "PP-FormulaNet-S", + ]: + if k != "rec_char_dict_path": + postprocess[k] = v + else: + postprocess[k] = v + + if config["Architecture"].get("algorithm") in ["LaTeXOCR"]: + tokenizer_file = config["Global"].get("rec_char_dict_path") + if tokenizer_file is not None: + with open(tokenizer_file, encoding="utf-8") as tokenizer_config_handle: + character_dict = json.load(tokenizer_config_handle) + postprocess["character_dict"] = character_dict + elif config["Architecture"].get("algorithm") in [ + "UniMERNet", + "PP-FormulaNet-L", + "PP-FormulaNet-S", + ]: + tokenizer_file = config["Global"].get("rec_char_dict_path") + fast_tokenizer_file = os.path.join(tokenizer_file, "tokenizer.json") + tokenizer_config_file = os.path.join(tokenizer_file, "tokenizer_config.json") + postprocess["character_dict"] = {} + if fast_tokenizer_file is not None: + with open(fast_tokenizer_file, encoding="utf-8") as tokenizer_config_handle: + character_dict = json.load(tokenizer_config_handle) + postprocess["character_dict"]["fast_tokenizer_file"] = character_dict + if tokenizer_config_file is not None: + with open( + tokenizer_config_file, encoding="utf-8" + ) as tokenizer_config_handle: + character_dict = json.load(tokenizer_config_handle) + postprocess["character_dict"]["tokenizer_config_file"] = character_dict + else: + if config["Global"].get("character_dict_path") is not None: + with open(config["Global"]["character_dict_path"], encoding="utf-8") as f: + lines = f.readlines() + character_dict = [line.strip("\n") for line in lines] + postprocess["character_dict"] = character_dict + + infer_cfg["PostProcess"] = postprocess + + with open(path, "w", encoding="utf-8") as f: + yaml.dump(infer_cfg, f, default_flow_style=False, allow_unicode=True) + logger.info("Export inference config file to {}".format(os.path.join(path))) + + +def dynamic_to_static(model, arch_config, logger, input_shape=None): + if arch_config["algorithm"] == "SRN": + max_text_length = arch_config["Head"]["max_text_length"] + other_shape = [ + paddle.static.InputSpec(shape=[None, 1, 64, 256], dtype="float32"), + [ + paddle.static.InputSpec(shape=[None, 256, 1], dtype="int64"), + paddle.static.InputSpec( + shape=[None, max_text_length, 1], dtype="int64" + ), + paddle.static.InputSpec( + shape=[None, 8, max_text_length, max_text_length], dtype="int64" + ), + paddle.static.InputSpec( + shape=[None, 8, max_text_length, max_text_length], dtype="int64" + ), + ], + ] + model = to_static(model, input_spec=other_shape) + elif arch_config["algorithm"] == "SAR": + other_shape = [ + paddle.static.InputSpec(shape=[None, 3, 48, 160], dtype="float32"), + [paddle.static.InputSpec(shape=[None], dtype="float32")], + ] + model = to_static(model, input_spec=other_shape) + elif arch_config["algorithm"] in ["SVTR_LCNet", "SVTR_HGNet"]: + other_shape = [ + paddle.static.InputSpec(shape=[None, 3, 48, -1], dtype="float32"), + ] + model = to_static(model, input_spec=other_shape) + elif arch_config["algorithm"] in ["SVTR", "CPPD"]: + other_shape = [ + paddle.static.InputSpec(shape=[None] + input_shape, dtype="float32"), + ] + model = to_static(model, input_spec=other_shape) + elif arch_config["algorithm"] == "PREN": + other_shape = [ + paddle.static.InputSpec(shape=[None, 3, 64, 256], dtype="float32"), + ] + model = to_static(model, input_spec=other_shape) + elif arch_config["model_type"] == "sr": + other_shape = [ + paddle.static.InputSpec(shape=[None, 3, 16, 64], dtype="float32") + ] + model = to_static(model, input_spec=other_shape) + elif arch_config["algorithm"] == "ViTSTR": + other_shape = [ + paddle.static.InputSpec(shape=[None, 1, 224, 224], dtype="float32"), + ] + model = to_static(model, input_spec=other_shape) + elif arch_config["algorithm"] == "ABINet": + if not input_shape: + input_shape = [3, 32, 128] + other_shape = [ + paddle.static.InputSpec(shape=[None] + input_shape, dtype="float32"), + ] + model = to_static(model, input_spec=other_shape) + elif arch_config["algorithm"] in ["NRTR", "SPIN", "RFL"]: + other_shape = [ + paddle.static.InputSpec(shape=[None, 1, 32, 100], dtype="float32"), + ] + model = to_static(model, input_spec=other_shape) + elif arch_config["algorithm"] in ["SATRN"]: + other_shape = [ + paddle.static.InputSpec(shape=[None, 3, 32, 100], dtype="float32"), + ] + model = to_static(model, input_spec=other_shape) + elif arch_config["algorithm"] == "VisionLAN": + other_shape = [ + paddle.static.InputSpec(shape=[None, 3, 64, 256], dtype="float32"), + ] + model = to_static(model, input_spec=other_shape) + elif arch_config["algorithm"] == "RobustScanner": + max_text_length = arch_config["Head"]["max_text_length"] + other_shape = [ + paddle.static.InputSpec(shape=[None, 3, 48, 160], dtype="float32"), + [ + paddle.static.InputSpec( + shape=[ + None, + ], + dtype="float32", + ), + paddle.static.InputSpec(shape=[None, max_text_length], dtype="int64"), + ], + ] + model = to_static(model, input_spec=other_shape) + elif arch_config["algorithm"] == "CAN": + other_shape = [ + [ + paddle.static.InputSpec(shape=[None, 1, None, None], dtype="float32"), + paddle.static.InputSpec(shape=[None, 1, None, None], dtype="float32"), + paddle.static.InputSpec( + shape=[None, arch_config["Head"]["max_text_length"]], dtype="int64" + ), + ] + ] + model = to_static(model, input_spec=other_shape) + elif arch_config["algorithm"] == "LaTeXOCR": + other_shape = [ + paddle.static.InputSpec(shape=[None, 1, None, None], dtype="float32"), + ] + model = to_static(model, input_spec=other_shape) + elif arch_config["algorithm"] == "UniMERNet": + model = paddle.jit.to_static( + model, + input_spec=[ + paddle.static.InputSpec(shape=[-1, 1, 192, 672], dtype="float32") + ], + full_graph=True, + ) + elif arch_config["algorithm"] == "SLANeXt": + model = paddle.jit.to_static( + model, + input_spec=[ + paddle.static.InputSpec(shape=[-1, 3, 512, 512], dtype="float32") + ], + full_graph=True, + ) + elif arch_config["algorithm"] == "PP-FormulaNet-L": + model = paddle.jit.to_static( + model, + input_spec=[ + paddle.static.InputSpec(shape=[-1, 1, 768, 768], dtype="float32") + ], + full_graph=True, + ) + elif arch_config["algorithm"] == "PP-FormulaNet-S": + model = paddle.jit.to_static( + model, + input_spec=[ + paddle.static.InputSpec(shape=[-1, 1, 384, 384], dtype="float32") + ], + full_graph=True, + ) + + elif arch_config["algorithm"] in ["LayoutLM", "LayoutLMv2", "LayoutXLM"]: + input_spec = [ + paddle.static.InputSpec(shape=[None, 512], dtype="int64"), # input_ids + paddle.static.InputSpec(shape=[None, 512, 4], dtype="int64"), # bbox + paddle.static.InputSpec(shape=[None, 512], dtype="int64"), # attention_mask + paddle.static.InputSpec(shape=[None, 512], dtype="int64"), # token_type_ids + paddle.static.InputSpec(shape=[None, 3, 224, 224], dtype="int64"), # image + ] + if "Re" in arch_config["Backbone"]["name"]: + input_spec.extend( + [ + paddle.static.InputSpec( + shape=[None, 512, 3], dtype="int64" + ), # entities + paddle.static.InputSpec( + shape=[None, None, 2], dtype="int64" + ), # relations + ] + ) + if model.backbone.use_visual_backbone is False: + input_spec.pop(4) + model = to_static(model, input_spec=[input_spec]) + else: + infer_shape = [3, -1, -1] + if arch_config["model_type"] == "rec": + infer_shape = [3, 32, -1] # for rec model, H must be 32 + if ( + "Transform" in arch_config + and arch_config["Transform"] is not None + and arch_config["Transform"]["name"] == "TPS" + ): + logger.info( + "When there is tps in the network, variable length input is not supported, and the input size needs to be the same as during training" + ) + infer_shape[-1] = 100 + elif arch_config["model_type"] == "table": + infer_shape = [3, 488, 488] + if arch_config["algorithm"] == "TableMaster": + infer_shape = [3, 480, 480] + if arch_config["algorithm"] == "SLANet": + infer_shape = [3, -1, -1] + model = to_static( + model, + input_spec=[ + paddle.static.InputSpec(shape=[None] + infer_shape, dtype="float32") + ], + ) + + if ( + arch_config["model_type"] != "sr" + and arch_config["Backbone"]["name"] == "PPLCNetV3" + ): + # for rep lcnetv3 + for layer in model.sublayers(): + if hasattr(layer, "rep") and not getattr(layer, "is_repped"): + layer.rep() + return model + + +def export_single_model( + model, + arch_config, + save_path, + logger, + yaml_path, + config, + input_shape=None, + quanter=None, +): + + model = dynamic_to_static(model, arch_config, logger, input_shape) + + if quanter is None: + try: + import encryption # Attempt to import the encryption module for AIStudio's encryption model + except ( + ModuleNotFoundError + ): # Encryption is not needed if the module cannot be imported + print("Skipping import of the encryption module") + paddle_version = version.parse(paddle.__version__) + if config["Global"].get("export_with_pir", False): + assert ( + paddle_version >= version.parse("3.0.0b2") + or paddle_version == version.parse("0.0.0") + ) and os.environ.get("FLAGS_enable_pir_api", None) not in ["0", "False"] + paddle.jit.save(model, save_path) + else: + if paddle_version >= version.parse( + "3.0.0b2" + ) or paddle_version == version.parse("0.0.0"): + model.forward.rollback() + with paddle.pir_utils.OldIrGuard(): + model = dynamic_to_static(model, arch_config, logger, input_shape) + paddle.jit.save(model, save_path) + else: + paddle.jit.save(model, save_path) + else: + quanter.save_quantized_model(model, save_path) + logger.info("inference model is saved to {}".format(save_path)) + return + + +def convert_bn(model): + for n, m in model.named_children(): + if isinstance(m, nn.SyncBatchNorm): + bn = nn.BatchNorm2D( + m._num_features, m._momentum, m._epsilon, m._weight_attr, m._bias_attr + ) + bn.set_dict(m.state_dict()) + setattr(model, n, bn) + else: + convert_bn(m) + + +def export(config, base_model=None, save_path=None): + if paddle.distributed.get_rank() != 0: + return + logger = get_logger() + # build post process + post_process_class = build_post_process(config["PostProcess"], config["Global"]) + + # build model + # for rec algorithm + if hasattr(post_process_class, "character"): + char_num = len(getattr(post_process_class, "character")) + if config["Architecture"]["algorithm"] in [ + "Distillation", + ]: # distillation model + for key in config["Architecture"]["Models"]: + if ( + config["Architecture"]["Models"][key]["Head"]["name"] == "MultiHead" + ): # multi head + out_channels_list = {} + if config["PostProcess"]["name"] == "DistillationSARLabelDecode": + char_num = char_num - 2 + if config["PostProcess"]["name"] == "DistillationNRTRLabelDecode": + char_num = char_num - 3 + out_channels_list["CTCLabelDecode"] = char_num + out_channels_list["SARLabelDecode"] = char_num + 2 + out_channels_list["NRTRLabelDecode"] = char_num + 3 + config["Architecture"]["Models"][key]["Head"][ + "out_channels_list" + ] = out_channels_list + else: + config["Architecture"]["Models"][key]["Head"][ + "out_channels" + ] = char_num + # just one final tensor needs to exported for inference + config["Architecture"]["Models"][key]["return_all_feats"] = False + elif config["Architecture"]["Head"]["name"] == "MultiHead": # multi head + out_channels_list = {} + char_num = len(getattr(post_process_class, "character")) + if config["PostProcess"]["name"] == "SARLabelDecode": + char_num = char_num - 2 + if config["PostProcess"]["name"] == "NRTRLabelDecode": + char_num = char_num - 3 + out_channels_list["CTCLabelDecode"] = char_num + out_channels_list["SARLabelDecode"] = char_num + 2 + out_channels_list["NRTRLabelDecode"] = char_num + 3 + config["Architecture"]["Head"]["out_channels_list"] = out_channels_list + else: # base rec model + config["Architecture"]["Head"]["out_channels"] = char_num + + # for sr algorithm + if config["Architecture"]["model_type"] == "sr": + config["Architecture"]["Transform"]["infer_mode"] = True + + # for latexocr algorithm + if config["Architecture"].get("algorithm") in ["LaTeXOCR"]: + config["Architecture"]["Backbone"]["is_predict"] = True + config["Architecture"]["Backbone"]["is_export"] = True + config["Architecture"]["Head"]["is_export"] = True + if config["Architecture"].get("algorithm") in ["UniMERNet"]: + config["Architecture"]["Backbone"]["is_export"] = True + config["Architecture"]["Head"]["is_export"] = True + if config["Architecture"].get("algorithm") in [ + "PP-FormulaNet-S", + "PP-FormulaNet-L", + ]: + config["Architecture"]["Head"]["is_export"] = True + if base_model is not None: + model = base_model + if isinstance(model, paddle.DataParallel): + model = copy.deepcopy(model._layers) + else: + model = copy.deepcopy(model) + else: + model = build_model(config["Architecture"]) + load_model(config, model, model_type=config["Architecture"]["model_type"]) + convert_bn(model) + model.eval() + + if not save_path: + save_path = config["Global"]["save_inference_dir"] + yaml_path = os.path.join(save_path, "inference.yml") + + arch_config = config["Architecture"] + + if ( + arch_config["algorithm"] in ["SVTR", "CPPD"] + and arch_config["Head"]["name"] != "MultiHead" + ): + input_shape = config["Eval"]["dataset"]["transforms"][-2]["SVTRRecResizeImg"][ + "image_shape" + ] + elif arch_config["algorithm"].lower() == "ABINet".lower(): + rec_rs = [ + c + for c in config["Eval"]["dataset"]["transforms"] + if "ABINetRecResizeImg" in c + ] + input_shape = rec_rs[0]["ABINetRecResizeImg"]["image_shape"] if rec_rs else None + else: + input_shape = None + dump_infer_config(config, yaml_path, logger) + if arch_config["algorithm"] in [ + "Distillation", + ]: # distillation model + archs = list(arch_config["Models"].values()) + for idx, name in enumerate(model.model_name_list): + sub_model_save_path = os.path.join(save_path, name, "inference") + export_single_model( + model.model_list[idx], + archs[idx], + sub_model_save_path, + logger, + yaml_path, + config, + ) + else: + save_path = os.path.join(save_path, "inference") + export_single_model( + model, + arch_config, + save_path, + logger, + yaml_path, + config, + input_shape=input_shape, + ) diff --git a/modules/onnx_ocr_module/src/ppocr/utils/formula_utils/math_txt2pkl.py b/modules/onnx_ocr_module/src/ppocr/utils/formula_utils/math_txt2pkl.py new file mode 100644 index 0000000..beafc13 --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/utils/formula_utils/math_txt2pkl.py @@ -0,0 +1,74 @@ +# copyright (c) 2024 PaddlePaddle Authors. All Rights Reserve. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import pickle +from tqdm import tqdm +import os +import math +from paddle.utils import try_import +from collections import defaultdict +import glob +from os.path import join +import argparse + + +def txt2pickle(images, equations, save_dir): + imagesize = try_import("imagesize") + save_p = os.path.join(save_dir, "latexocr_{}.pkl".format(images.split("/")[-1])) + min_dimensions = (32, 32) + max_dimensions = (672, 192) + max_length = 512 + data = defaultdict(lambda: []) + if images is not None and equations is not None: + images_list = [ + path.replace("\\", "/") for path in glob.glob(join(images, "*.png")) + ] + indices = [int(os.path.basename(img).split(".")[0]) for img in images_list] + eqs = open(equations, "r").read().split("\n") + for i, im in tqdm(enumerate(images_list), total=len(images_list)): + width, height = imagesize.get(im) + if ( + min_dimensions[0] <= width <= max_dimensions[0] + and min_dimensions[1] <= height <= max_dimensions[1] + ): + divide_h = math.ceil(height / 16) * 16 + divide_w = math.ceil(width / 16) * 16 + im = os.path.basename(im) + data[(divide_w, divide_h)].append((eqs[indices[i]], im)) + data = dict(data) + with open(save_p, "wb") as file: + pickle.dump(data, file) + + +if __name__ == "__main__": + parser = argparse.ArgumentParser() + + parser.add_argument( + "--image_dir", + type=str, + default=".", + help="Input_label or input path to be converted", + ) + parser.add_argument( + "--mathtxt_path", + type=str, + default=".", + help="Input_label or input path to be converted", + ) + parser.add_argument( + "--output_dir", type=str, default="out_label.txt", help="Output file name" + ) + + args = parser.parse_args() + txt2pickle(args.image_dir, args.mathtxt_path, args.output_dir) diff --git a/modules/onnx_ocr_module/src/ppocr/utils/formula_utils/unimernet_data_convert.py b/modules/onnx_ocr_module/src/ppocr/utils/formula_utils/unimernet_data_convert.py new file mode 100644 index 0000000..fed8a7f --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/utils/formula_utils/unimernet_data_convert.py @@ -0,0 +1,107 @@ +# copyright (c) 2024 PaddlePaddle Authors. All Rights Reserve. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import os +import cv2 +import glob +import argparse +from os.path import join +from tqdm import tqdm + + +def latexocr2paddleocr_train(image_path, math_unimernet_file, math_hwe_file, save_path): + convert_f = open(save_path, "w") + sub_dir = "UniMER-1M/images" + img_sub_dir = os.path.join(image_path, sub_dir) + with open(math_unimernet_file, "r") as f: + lines = f.readlines() + formula_num = len(lines) + for i, line in tqdm(enumerate(lines), total=formula_num): + image_name = "{0:07d}.png".format(i) + math_gt = line.strip() + image_p = os.path.join(img_sub_dir, image_name) + img_name_subdir = os.path.join(sub_dir, image_name) + if os.path.exists(image_p): + convert_f.writelines("{}\t{}\n".format(img_name_subdir, math_gt)) + + sub_dir = "HME100K/train_images" + img_sub_dir = os.path.join(image_path, sub_dir) + with open(math_hwe_file, "r") as f: + lines = f.readlines() + formula_num = len(lines) + for i, line in tqdm(enumerate(lines), total=formula_num): + img_name, math_gt = line.strip().split("\t") + image_path = os.path.join(img_sub_dir, img_name) + img_name_subdir = os.path.join(sub_dir, img_name) + convert_f.writelines("{}\t{}\n".format(img_name_subdir, math_gt)) + + convert_f.close() + + +def unimernet2paddleocr_test(image_path, math_file, save_path): + convert_f = open(save_path, "w") + with open(math_file, "r") as f: + # load maths which + lines = f.readlines() + formula_num = len(lines) + for i, line in tqdm(enumerate(lines), total=formula_num): + image_name = "{0:07d}.png".format(i) + math_gt = line.strip() + image_p = os.path.join(image_path, image_name) + if os.path.exists(image_p): + convert_f.writelines("{}\t{}\n".format(image_name, math_gt)) + convert_f.close() + + +if __name__ == "__main__": + parser = argparse.ArgumentParser() + + parser.add_argument( + "--image_dir", + type=str, + default=".", + help="Input_label or input path to be converted", + ) + parser.add_argument( + "--unimernet_txt_path", + type=str, + default="", + help="Input_label or input path to be converted", + ) + parser.add_argument( + "--hme100k_txt_path", + type=str, + default="", + help="Input_label or input path to be converted", + ) + parser.add_argument( + "--output_path", type=str, default="out_label.txt", help="Output file name" + ) + parser.add_argument( + "--datatype", type=str, default="out_label.txt", help="datatype" + ) + args = parser.parse_args() + if args.datatype == "unimernet_train": + latexocr2paddleocr_train( + args.image_dir, + args.unimernet_txt_path, + args.hme100k_txt_path, + args.output_path, + ) + elif args.datatype == "unimernet_test": + unimernet2paddleocr_test( + args.image_dir, args.unimernet_txt_path, args.output_path + ) + else: + raise NotImplementedError("the datatype is not supported") diff --git a/modules/onnx_ocr_module/src/ppocr/utils/gen_label.py b/modules/onnx_ocr_module/src/ppocr/utils/gen_label.py new file mode 100644 index 0000000..1634b38 --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/utils/gen_label.py @@ -0,0 +1,82 @@ +# copyright (c) 2020 PaddlePaddle Authors. All Rights Reserve. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +import os +import argparse +import json + + +def gen_rec_label(input_path, out_label): + with open(out_label, "w") as out_file: + with open(input_path, "r") as f: + for line in f.readlines(): + tmp = line.strip("\n").replace(" ", "").split(",") + img_path, label = tmp[0], tmp[1] + label = label.replace('"', "") + out_file.write(img_path + "\t" + label + "\n") + + +def gen_det_label(root_path, input_dir, out_label): + with open(out_label, "w") as out_file: + for label_file in os.listdir(input_dir): + img_path = os.path.join(root_path, label_file[3:-4] + ".jpg") + label = [] + with open( + os.path.join(input_dir, label_file), "r", encoding="utf-8-sig" + ) as f: + for line in f.readlines(): + tmp = line.strip("\n\r").replace("\xef\xbb\xbf", "").split(",") + points = tmp[:8] + s = [] + for i in range(0, len(points), 2): + b = points[i : i + 2] + b = [int(t) for t in b] + s.append(b) + result = {"transcription": tmp[8], "points": s} + label.append(result) + + out_file.write( + img_path + "\t" + json.dumps(label, ensure_ascii=False) + "\n" + ) + + +if __name__ == "__main__": + parser = argparse.ArgumentParser() + parser.add_argument( + "--mode", + type=str, + default="rec", + help="Generate rec_label or det_label, can be set rec or det", + ) + parser.add_argument( + "--root_path", + type=str, + default=".", + help="The root directory of images.Only takes effect when mode=det ", + ) + parser.add_argument( + "--input_path", + type=str, + default=".", + help="Input_label or input path to be converted", + ) + parser.add_argument( + "--output_label", type=str, default="out_label.txt", help="Output file name" + ) + + args = parser.parse_args() + if args.mode == "rec": + print("Generate rec label") + gen_rec_label(args.input_path, args.output_label) + elif args.mode == "det": + gen_det_label(args.root_path, args.input_path, args.output_label) diff --git a/modules/onnx_ocr_module/src/ppocr/utils/ic15_dict.txt b/modules/onnx_ocr_module/src/ppocr/utils/ic15_dict.txt new file mode 100644 index 0000000..7104368 --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/utils/ic15_dict.txt @@ -0,0 +1,36 @@ +0 +1 +2 +3 +4 +5 +6 +7 +8 +9 +a +b +c +d +e +f +g +h +i +j +k +l +m +n +o +p +q +r +s +t +u +v +w +x +y +z diff --git a/modules/onnx_ocr_module/src/ppocr/utils/iou.py b/modules/onnx_ocr_module/src/ppocr/utils/iou.py new file mode 100644 index 0000000..cb77c34 --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/utils/iou.py @@ -0,0 +1,54 @@ +# copyright (c) 2021 PaddlePaddle Authors. All Rights Reserve. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +""" +This code is refer from: +https://github.com/whai362/PSENet/blob/python3/models/loss/iou.py +""" + +import paddle + +EPS = 1e-6 + + +def iou_single(a, b, mask, n_class): + valid = mask == 1 + a = a.masked_select(valid) + b = b.masked_select(valid) + miou = [] + for i in range(n_class): + if a.shape == [0] and a.shape == b.shape: + inter = paddle.to_tensor(0.0) + union = paddle.to_tensor(0.0) + else: + inter = ((a == i).logical_and(b == i)).astype("float32") + union = ((a == i).logical_or(b == i)).astype("float32") + miou.append(paddle.sum(inter) / (paddle.sum(union) + EPS)) + miou = sum(miou) / len(miou) + return miou + + +def iou(a, b, mask, n_class=2, reduce=True): + batch_size = a.shape[0] + + a = a.reshape([batch_size, -1]) + b = b.reshape([batch_size, -1]) + mask = mask.reshape([batch_size, -1]) + + iou = paddle.zeros((batch_size,), dtype="float32") + for i in range(batch_size): + iou[i] = iou_single(a[i], b[i], mask[i], n_class) + + if reduce: + iou = paddle.mean(iou) + return iou diff --git a/modules/onnx_ocr_module/src/ppocr/utils/loggers/__init__.py b/modules/onnx_ocr_module/src/ppocr/utils/loggers/__init__.py new file mode 100644 index 0000000..a73351f --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/utils/loggers/__init__.py @@ -0,0 +1,2 @@ +from .wandb_logger import WandbLogger +from .loggers import Loggers diff --git a/modules/onnx_ocr_module/src/ppocr/utils/loggers/base_logger.py b/modules/onnx_ocr_module/src/ppocr/utils/loggers/base_logger.py new file mode 100644 index 0000000..0aa132d --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/utils/loggers/base_logger.py @@ -0,0 +1,16 @@ +import os +from abc import ABC, abstractmethod + + +class BaseLogger(ABC): + def __init__(self, save_dir): + self.save_dir = save_dir + os.makedirs(self.save_dir, exist_ok=True) + + @abstractmethod + def log_metrics(self, metrics, prefix=None): + pass + + @abstractmethod + def close(self): + pass diff --git a/modules/onnx_ocr_module/src/ppocr/utils/loggers/loggers.py b/modules/onnx_ocr_module/src/ppocr/utils/loggers/loggers.py new file mode 100644 index 0000000..a14dbcb --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/utils/loggers/loggers.py @@ -0,0 +1,19 @@ +from .wandb_logger import WandbLogger + + +class Loggers(object): + def __init__(self, loggers): + super().__init__() + self.loggers = loggers + + def log_metrics(self, metrics, prefix=None, step=None): + for logger in self.loggers: + logger.log_metrics(metrics, prefix=prefix, step=step) + + def log_model(self, is_best, prefix, metadata=None): + for logger in self.loggers: + logger.log_model(is_best=is_best, prefix=prefix, metadata=metadata) + + def close(self): + for logger in self.loggers: + logger.close() diff --git a/modules/onnx_ocr_module/src/ppocr/utils/loggers/wandb_logger.py b/modules/onnx_ocr_module/src/ppocr/utils/loggers/wandb_logger.py new file mode 100644 index 0000000..44cbfc1 --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/utils/loggers/wandb_logger.py @@ -0,0 +1,84 @@ +import os +from .base_logger import BaseLogger +from ppocr.utils.logging import get_logger + + +class WandbLogger(BaseLogger): + def __init__( + self, + project=None, + name=None, + id=None, + entity=None, + save_dir=None, + config=None, + **kwargs, + ): + try: + import wandb + + self.wandb = wandb + except ModuleNotFoundError: + raise ModuleNotFoundError("Please install wandb using `pip install wandb`") + + self.project = project + self.name = name + self.id = id + self.save_dir = save_dir + self.config = config + self.kwargs = kwargs + self.entity = entity + self._run = None + self._wandb_init = dict( + project=self.project, + name=self.name, + id=self.id, + entity=self.entity, + dir=self.save_dir, + resume="allow", + ) + self._wandb_init.update(**kwargs) + self.logger = get_logger() + + _ = self.run + + if self.config: + self.run.config.update(self.config) + + @property + def run(self): + if self._run is None: + if self.wandb.run is not None: + self.logger.info( + "There is a wandb run already in progress " + "and newly created instances of `WandbLogger` will reuse" + " this run. If this is not desired, call `wandb.finish()`" + "before instantiating `WandbLogger`." + ) + self._run = self.wandb.run + else: + self._run = self.wandb.init(**self._wandb_init) + return self._run + + def log_metrics(self, metrics, prefix=None, step=None): + if not prefix: + prefix = "" + updated_metrics = {prefix.lower() + "/" + k: v for k, v in metrics.items()} + + self.run.log(updated_metrics, step=step) + + def log_model(self, is_best, prefix, metadata=None): + model_path = os.path.join(self.save_dir, prefix + ".pdparams") + artifact = self.wandb.Artifact( + "model-{}".format(self.run.id), type="model", metadata=metadata + ) + artifact.add_file(model_path, name="model_ckpt.pdparams") + + aliases = [prefix] + if is_best: + aliases.append("best") + + self.run.log_artifact(artifact, aliases=aliases) + + def close(self): + self.run.finish() diff --git a/modules/onnx_ocr_module/src/ppocr/utils/logging.py b/modules/onnx_ocr_module/src/ppocr/utils/logging.py new file mode 100644 index 0000000..cc34232 --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/utils/logging.py @@ -0,0 +1,84 @@ +# copyright (c) 2020 PaddlePaddle Authors. All Rights Reserve. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +""" +This code is refer from: +https://github.com/WenmuZhou/PytorchOCR/blob/master/torchocr/utils/logging.py +""" + +import os +import sys +import logging +import functools +try: + import paddle.distributed as dist +except Exception: + class _DummyDist: + def get_rank(self): + return 0 + dist = _DummyDist() + +logger_initialized = {} + + +@functools.lru_cache() +def get_logger(name="ppocr", log_file=None, log_level=logging.DEBUG, log_ranks="0"): + """Initialize and get a logger by name. + If the logger has not been initialized, this method will initialize the + logger by adding one or two handlers, otherwise the initialized logger will + be directly returned. During initialization, a StreamHandler will always be + added. If `log_file` is specified a FileHandler will also be added. + Args: + name (str): Logger name. + log_file (str | None): The log filename. If specified, a FileHandler + will be added to the logger. + log_level (int): The logger level. Note that only the process of + rank 0 is affected, and other processes will set the level to + "Error" thus be silent most of the time. + log_ranks (str): The ids of gpu to log which are separated by "," when more than 1, "0" by default. + Returns: + logging.Logger: The expected logger. + """ + logger = logging.getLogger(name) + if name in logger_initialized: + return logger + for logger_name in logger_initialized: + if name.startswith(logger_name): + return logger + + formatter = logging.Formatter( + "[%(asctime)s] %(name)s %(levelname)s: %(message)s", datefmt="%Y/%m/%d %H:%M:%S" + ) + + stream_handler = logging.StreamHandler(stream=sys.stdout) + stream_handler.setFormatter(formatter) + logger.addHandler(stream_handler) + if log_file is not None and dist.get_rank() == 0: + log_file_folder = os.path.split(log_file)[0] + os.makedirs(log_file_folder, exist_ok=True) + file_handler = logging.FileHandler(log_file, "a") + file_handler.setFormatter(formatter) + logger.addHandler(file_handler) + + if isinstance(log_ranks, str): + log_ranks = [int(i) for i in log_ranks.split(",")] + elif isinstance(log_ranks, int): + log_ranks = [log_ranks] + + if dist.get_rank() in log_ranks: + logger.setLevel(log_level) + else: + logger.setLevel(logging.ERROR) + logger_initialized[name] = True + logger.propagate = False + return logger diff --git a/modules/onnx_ocr_module/src/ppocr/utils/network.py b/modules/onnx_ocr_module/src/ppocr/utils/network.py new file mode 100644 index 0000000..761338a --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/utils/network.py @@ -0,0 +1,155 @@ +# copyright (c) 2020 PaddlePaddle Authors. All Rights Reserve. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import os +import sys +import time +import shutil +import tarfile +import requests +import os.path as osp +import paddle.distributed as dist +from tqdm import tqdm + +from ppocr.utils.logging import get_logger + +MODELS_DIR = os.path.join( + os.environ.get("PADDLE_OCR_BASE_DIR", os.path.expanduser("~/.paddleocr/")), "models" +) +DOWNLOAD_RETRY_LIMIT = 3 + + +def download_with_progressbar(url, save_path): + logger = get_logger() + if save_path and os.path.exists(save_path): + logger.info(f"Path {save_path} already exists. Skipping...") + return + else: + # Mainly used to solve the problem of downloading data from different + # machines in the case of multiple machines. Different nodes will download + # data, and the same node will only download data once. + if dist.get_rank() == 0: + _download(url, save_path) + else: + while not os.path.exists(save_path): + time.sleep(1) + + +def _download(url, save_path): + """ + Download from url, save to path. + + url (str): download url + save_path (str): download to given path + """ + logger = get_logger() + + fname = osp.split(url)[-1] + retry_cnt = 0 + + while not osp.exists(save_path): + if retry_cnt < DOWNLOAD_RETRY_LIMIT: + retry_cnt += 1 + else: + raise RuntimeError( + "Download from {} failed. " "Retry limit reached".format(url) + ) + + try: + req = requests.get(url, stream=True) + except Exception as e: # requests.exceptions.ConnectionError + logger.info( + "Downloading {} from {} failed {} times with exception {}".format( + fname, url, retry_cnt + 1, str(e) + ) + ) + time.sleep(1) + continue + + if req.status_code != 200: + raise RuntimeError( + "Downloading from {} failed with code " + "{}!".format(url, req.status_code) + ) + + # For protecting download interrupted, download to + # tmp_file firstly, move tmp_file to save_path + # after download finished + tmp_file = save_path + ".tmp" + total_size = req.headers.get("content-length") + with open(tmp_file, "wb") as f: + if total_size: + with tqdm(total=(int(total_size) + 1023) // 1024) as pbar: + for chunk in req.iter_content(chunk_size=1024): + f.write(chunk) + pbar.update(1) + else: + for chunk in req.iter_content(chunk_size=1024): + if chunk: + f.write(chunk) + shutil.move(tmp_file, save_path) + + return save_path + + +def maybe_download(model_storage_directory, url): + # using custom model + tar_file_name_list = [".pdiparams", ".pdiparams.info", ".pdmodel"] + if not os.path.exists( + os.path.join(model_storage_directory, "inference.pdiparams") + ) or not os.path.exists(os.path.join(model_storage_directory, "inference.pdmodel")): + assert url.endswith(".tar"), "Only supports tar compressed package" + tmp_path = os.path.join(model_storage_directory, url.split("/")[-1]) + print("download {} to {}".format(url, tmp_path)) + os.makedirs(model_storage_directory, exist_ok=True) + download_with_progressbar(url, tmp_path) + with tarfile.open(tmp_path, "r") as tarObj: + for member in tarObj.getmembers(): + filename = None + for tar_file_name in tar_file_name_list: + if member.name.endswith(tar_file_name): + filename = "inference" + tar_file_name + if filename is None: + continue + file = tarObj.extractfile(member) + with open(os.path.join(model_storage_directory, filename), "wb") as f: + f.write(file.read()) + os.remove(tmp_path) + + +def maybe_download_params(model_path): + if os.path.exists(model_path) or not is_link(model_path): + return model_path + else: + url = model_path + tmp_path = os.path.join(MODELS_DIR, url.split("/")[-1]) + print("download {} to {}".format(url, tmp_path)) + os.makedirs(MODELS_DIR, exist_ok=True) + download_with_progressbar(url, tmp_path) + return tmp_path + + +def is_link(s): + return s is not None and s.startswith("http") + + +def confirm_model_dir_url(model_dir, default_model_dir, default_url): + url = default_url + if model_dir is None or is_link(model_dir): + if is_link(model_dir): + url = model_dir + file_name = url.split("/")[-1][:-4] + model_dir = default_model_dir + model_dir = os.path.join(model_dir, file_name) + return model_dir, url diff --git a/modules/onnx_ocr_module/src/ppocr/utils/poly_nms.py b/modules/onnx_ocr_module/src/ppocr/utils/poly_nms.py new file mode 100644 index 0000000..c3a1338 --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/utils/poly_nms.py @@ -0,0 +1,146 @@ +# copyright (c) 2022 PaddlePaddle Authors. All Rights Reserve. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import numpy as np +from shapely.geometry import Polygon + + +def points2polygon(points): + """Convert k points to 1 polygon. + + Args: + points (ndarray or list): A ndarray or a list of shape (2k) + that indicates k points. + + Returns: + polygon (Polygon): A polygon object. + """ + if isinstance(points, list): + points = np.array(points) + + assert isinstance(points, np.ndarray) + assert (points.size % 2 == 0) and (points.size >= 8) + + point_mat = points.reshape([-1, 2]) + return Polygon(point_mat) + + +def poly_intersection(poly_det, poly_gt, buffer=0.0001): + """Calculate the intersection area between two polygon. + + Args: + poly_det (Polygon): A polygon predicted by detector. + poly_gt (Polygon): A gt polygon. + + Returns: + intersection_area (float): The intersection area between two polygons. + """ + assert isinstance(poly_det, Polygon) + assert isinstance(poly_gt, Polygon) + + if buffer == 0: + poly_inter = poly_det & poly_gt + else: + poly_inter = poly_det.buffer(buffer) & poly_gt.buffer(buffer) + return poly_inter.area, poly_inter + + +def poly_union(poly_det, poly_gt): + """Calculate the union area between two polygon. + + Args: + poly_det (Polygon): A polygon predicted by detector. + poly_gt (Polygon): A gt polygon. + + Returns: + union_area (float): The union area between two polygons. + """ + assert isinstance(poly_det, Polygon) + assert isinstance(poly_gt, Polygon) + + area_det = poly_det.area + area_gt = poly_gt.area + area_inters, _ = poly_intersection(poly_det, poly_gt) + return area_det + area_gt - area_inters + + +def valid_boundary(x, with_score=True): + num = len(x) + if num < 8: + return False + if num % 2 == 0 and (not with_score): + return True + if num % 2 == 1 and with_score: + return True + + return False + + +def boundary_iou(src, target): + """Calculate the IOU between two boundaries. + + Args: + src (list): Source boundary. + target (list): Target boundary. + + Returns: + iou (float): The iou between two boundaries. + """ + assert valid_boundary(src, False) + assert valid_boundary(target, False) + src_poly = points2polygon(src) + target_poly = points2polygon(target) + + return poly_iou(src_poly, target_poly) + + +def poly_iou(poly_det, poly_gt): + """Calculate the IOU between two polygons. + + Args: + poly_det (Polygon): A polygon predicted by detector. + poly_gt (Polygon): A gt polygon. + + Returns: + iou (float): The IOU between two polygons. + """ + assert isinstance(poly_det, Polygon) + assert isinstance(poly_gt, Polygon) + area_inters, _ = poly_intersection(poly_det, poly_gt) + area_union = poly_union(poly_det, poly_gt) + if area_union == 0: + return 0.0 + return area_inters / area_union + + +def poly_nms(polygons, threshold): + assert isinstance(polygons, list) + + polygons = np.array(sorted(polygons, key=lambda x: x[-1])) + + keep_poly = [] + index = [i for i in range(polygons.shape[0])] + + while len(index) > 0: + keep_poly.append(polygons[index[-1]].tolist()) + A = polygons[index[-1]][:-1] + index = np.delete(index, -1) + iou_list = np.zeros((len(index),)) + for i in range(len(index)): + B = polygons[index[i]][:-1] + iou_list[i] = boundary_iou(A, B) + remove_index = np.where(iou_list > threshold) + index = np.delete(index, remove_index) + + return keep_poly diff --git a/modules/onnx_ocr_module/src/ppocr/utils/ppocr_keys_v1.txt b/modules/onnx_ocr_module/src/ppocr/utils/ppocr_keys_v1.txt new file mode 100644 index 0000000..b75af21 --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/utils/ppocr_keys_v1.txt @@ -0,0 +1,6623 @@ +' +疗 +绚 +诚 +娇 +溜 +题 +贿 +者 +廖 +更 +纳 +加 +奉 +公 +一 +就 +汴 +计 +与 +路 +房 +原 +妇 +2 +0 +8 +- +7 +其 +> +: +] +, +, +骑 +刈 +全 +消 +昏 +傈 +安 +久 +钟 +嗅 +不 +影 +处 +驽 +蜿 +资 +关 +椤 +地 +瘸 +专 +问 +忖 +票 +嫉 +炎 +韵 +要 +月 +田 +节 +陂 +鄙 +捌 +备 +拳 +伺 +眼 +网 +盎 +大 +傍 +心 +东 +愉 +汇 +蹿 +科 +每 +业 +里 +航 +晏 +字 +平 +录 +先 +1 +3 +彤 +鲶 +产 +稍 +督 +腴 +有 +象 +岳 +注 +绍 +在 +泺 +文 +定 +核 +名 +水 +过 +理 +让 +偷 +率 +等 +这 +发 +” +为 +含 +肥 +酉 +相 +鄱 +七 +编 +猥 +锛 +日 +镀 +蒂 +掰 +倒 +辆 +栾 +栗 +综 +涩 +州 +雌 +滑 +馀 +了 +机 +块 +司 +宰 +甙 +兴 +矽 +抚 +保 +用 +沧 +秩 +如 +收 +息 +滥 +页 +疑 +埠 +! +! +姥 +异 +橹 +钇 +向 +下 +跄 +的 +椴 +沫 +国 +绥 +獠 +报 +开 +民 +蜇 +何 +分 +凇 +长 +讥 +藏 +掏 +施 +羽 +中 +讲 +派 +嘟 +人 +提 +浼 +间 +世 +而 +古 +多 +倪 +唇 +饯 +控 +庚 +首 +赛 +蜓 +味 +断 +制 +觉 +技 +替 +艰 +溢 +潮 +夕 +钺 +外 +摘 +枋 +动 +双 +单 +啮 +户 +枇 +确 +锦 +曜 +杜 +或 +能 +效 +霜 +盒 +然 +侗 +电 +晁 +放 +步 +鹃 +新 +杖 +蜂 +吒 +濂 +瞬 +评 +总 +隍 +对 +独 +合 +也 +是 +府 +青 +天 +诲 +墙 +组 +滴 +级 +邀 +帘 +示 +已 +时 +骸 +仄 +泅 +和 +遨 +店 +雇 +疫 +持 +巍 +踮 +境 +只 +亨 +目 +鉴 +崤 +闲 +体 +泄 +杂 +作 +般 +轰 +化 +解 +迂 +诿 +蛭 +璀 +腾 +告 +版 +服 +省 +师 +小 +规 +程 +线 +海 +办 +引 +二 +桧 +牌 +砺 +洄 +裴 +修 +图 +痫 +胡 +许 +犊 +事 +郛 +基 +柴 +呼 +食 +研 +奶 +律 +蛋 +因 +葆 +察 +戏 +褒 +戒 +再 +李 +骁 +工 +貂 +油 +鹅 +章 +啄 +休 +场 +给 +睡 +纷 +豆 +器 +捎 +说 +敏 +学 +会 +浒 +设 +诊 +格 +廓 +查 +来 +霓 +室 +溆 +¢ +诡 +寥 +焕 +舜 +柒 +狐 +回 +戟 +砾 +厄 +实 +翩 +尿 +五 +入 +径 +惭 +喹 +股 +宇 +篝 +| +; +美 +期 +云 +九 +祺 +扮 +靠 +锝 +槌 +系 +企 +酰 +阊 +暂 +蚕 +忻 +豁 +本 +羹 +执 +条 +钦 +H +獒 +限 +进 +季 +楦 +于 +芘 +玖 +铋 +茯 +未 +答 +粘 +括 +样 +精 +欠 +矢 +甥 +帷 +嵩 +扣 +令 +仔 +风 +皈 +行 +支 +部 +蓉 +刮 +站 +蜡 +救 +钊 +汗 +松 +嫌 +成 +可 +. +鹤 +院 +从 +交 +政 +怕 +活 +调 +球 +局 +验 +髌 +第 +韫 +谗 +串 +到 +圆 +年 +米 +/ +* +友 +忿 +检 +区 +看 +自 +敢 +刃 +个 +兹 +弄 +流 +留 +同 +没 +齿 +星 +聆 +轼 +湖 +什 +三 +建 +蛔 +儿 +椋 +汕 +震 +颧 +鲤 +跟 +力 +情 +璺 +铨 +陪 +务 +指 +族 +训 +滦 +鄣 +濮 +扒 +商 +箱 +十 +召 +慷 +辗 +所 +莞 +管 +护 +臭 +横 +硒 +嗓 +接 +侦 +六 +露 +党 +馋 +驾 +剖 +高 +侬 +妪 +幂 +猗 +绺 +骐 +央 +酐 +孝 +筝 +课 +徇 +缰 +门 +男 +西 +项 +句 +谙 +瞒 +秃 +篇 +教 +碲 +罚 +声 +呐 +景 +前 +富 +嘴 +鳌 +稀 +免 +朋 +啬 +睐 +去 +赈 +鱼 +住 +肩 +愕 +速 +旁 +波 +厅 +健 +茼 +厥 +鲟 +谅 +投 +攸 +炔 +数 +方 +击 +呋 +谈 +绩 +别 +愫 +僚 +躬 +鹧 +胪 +炳 +招 +喇 +膨 +泵 +蹦 +毛 +结 +5 +4 +谱 +识 +陕 +粽 +婚 +拟 +构 +且 +搜 +任 +潘 +比 +郢 +妨 +醪 +陀 +桔 +碘 +扎 +选 +哈 +骷 +楷 +亿 +明 +缆 +脯 +监 +睫 +逻 +婵 +共 +赴 +淝 +凡 +惦 +及 +达 +揖 +谩 +澹 +减 +焰 +蛹 +番 +祁 +柏 +员 +禄 +怡 +峤 +龙 +白 +叽 +生 +闯 +起 +细 +装 +谕 +竟 +聚 +钙 +上 +导 +渊 +按 +艾 +辘 +挡 +耒 +盹 +饪 +臀 +记 +邮 +蕙 +受 +各 +医 +搂 +普 +滇 +朗 +茸 +带 +翻 +酚 +( +光 +堤 +墟 +蔷 +万 +幻 +〓 +瑙 +辈 +昧 +盏 +亘 +蛀 +吉 +铰 +请 +子 +假 +闻 +税 +井 +诩 +哨 +嫂 +好 +面 +琐 +校 +馊 +鬣 +缂 +营 +访 +炖 +占 +农 +缀 +否 +经 +钚 +棵 +趟 +张 +亟 +吏 +茶 +谨 +捻 +论 +迸 +堂 +玉 +信 +吧 +瞠 +乡 +姬 +寺 +咬 +溏 +苄 +皿 +意 +赉 +宝 +尔 +钰 +艺 +特 +唳 +踉 +都 +荣 +倚 +登 +荐 +丧 +奇 +涵 +批 +炭 +近 +符 +傩 +感 +道 +着 +菊 +虹 +仲 +众 +懈 +濯 +颞 +眺 +南 +释 +北 +缝 +标 +既 +茗 +整 +撼 +迤 +贲 +挎 +耱 +拒 +某 +妍 +卫 +哇 +英 +矶 +藩 +治 +他 +元 +领 +膜 +遮 +穗 +蛾 +飞 +荒 +棺 +劫 +么 +市 +火 +温 +拈 +棚 +洼 +转 +果 +奕 +卸 +迪 +伸 +泳 +斗 +邡 +侄 +涨 +屯 +萋 +胭 +氡 +崮 +枞 +惧 +冒 +彩 +斜 +手 +豚 +随 +旭 +淑 +妞 +形 +菌 +吲 +沱 +争 +驯 +歹 +挟 +兆 +柱 +传 +至 +包 +内 +响 +临 +红 +功 +弩 +衡 +寂 +禁 +老 +棍 +耆 +渍 +织 +害 +氵 +渑 +布 +载 +靥 +嗬 +虽 +苹 +咨 +娄 +库 +雉 +榜 +帜 +嘲 +套 +瑚 +亲 +簸 +欧 +边 +6 +腿 +旮 +抛 +吹 +瞳 +得 +镓 +梗 +厨 +继 +漾 +愣 +憨 +士 +策 +窑 +抑 +躯 +襟 +脏 +参 +贸 +言 +干 +绸 +鳄 +穷 +藜 +音 +折 +详 +) +举 +悍 +甸 +癌 +黎 +谴 +死 +罩 +迁 +寒 +驷 +袖 +媒 +蒋 +掘 +模 +纠 +恣 +观 +祖 +蛆 +碍 +位 +稿 +主 +澧 +跌 +筏 +京 +锏 +帝 +贴 +证 +糠 +才 +黄 +鲸 +略 +炯 +饱 +四 +出 +园 +犀 +牧 +容 +汉 +杆 +浈 +汰 +瑷 +造 +虫 +瘩 +怪 +驴 +济 +应 +花 +沣 +谔 +夙 +旅 +价 +矿 +以 +考 +s +u +呦 +晒 +巡 +茅 +准 +肟 +瓴 +詹 +仟 +褂 +译 +桌 +混 +宁 +怦 +郑 +抿 +些 +余 +鄂 +饴 +攒 +珑 +群 +阖 +岔 +琨 +藓 +预 +环 +洮 +岌 +宀 +杲 +瀵 +最 +常 +囡 +周 +踊 +女 +鼓 +袭 +喉 +简 +范 +薯 +遐 +疏 +粱 +黜 +禧 +法 +箔 +斤 +遥 +汝 +奥 +直 +贞 +撑 +置 +绱 +集 +她 +馅 +逗 +钧 +橱 +魉 +[ +恙 +躁 +唤 +9 +旺 +膘 +待 +脾 +惫 +购 +吗 +依 +盲 +度 +瘿 +蠖 +俾 +之 +镗 +拇 +鲵 +厝 +簧 +续 +款 +展 +啃 +表 +剔 +品 +钻 +腭 +损 +清 +锶 +统 +涌 +寸 +滨 +贪 +链 +吠 +冈 +伎 +迥 +咏 +吁 +览 +防 +迅 +失 +汾 +阔 +逵 +绀 +蔑 +列 +川 +凭 +努 +熨 +揪 +利 +俱 +绉 +抢 +鸨 +我 +即 +责 +膦 +易 +毓 +鹊 +刹 +玷 +岿 +空 +嘞 +绊 +排 +术 +估 +锷 +违 +们 +苟 +铜 +播 +肘 +件 +烫 +审 +鲂 +广 +像 +铌 +惰 +铟 +巳 +胍 +鲍 +康 +憧 +色 +恢 +想 +拷 +尤 +疳 +知 +S +Y +F +D +A +峄 +裕 +帮 +握 +搔 +氐 +氘 +难 +墒 +沮 +雨 +叁 +缥 +悴 +藐 +湫 +娟 +苑 +稠 +颛 +簇 +后 +阕 +闭 +蕤 +缚 +怎 +佞 +码 +嘤 +蔡 +痊 +舱 +螯 +帕 +赫 +昵 +升 +烬 +岫 +、 +疵 +蜻 +髁 +蕨 +隶 +烛 +械 +丑 +盂 +梁 +强 +鲛 +由 +拘 +揉 +劭 +龟 +撤 +钩 +呕 +孛 +费 +妻 +漂 +求 +阑 +崖 +秤 +甘 +通 +深 +补 +赃 +坎 +床 +啪 +承 +吼 +量 +暇 +钼 +烨 +阂 +擎 +脱 +逮 +称 +P +神 +属 +矗 +华 +届 +狍 +葑 +汹 +育 +患 +窒 +蛰 +佼 +静 +槎 +运 +鳗 +庆 +逝 +曼 +疱 +克 +代 +官 +此 +麸 +耧 +蚌 +晟 +例 +础 +榛 +副 +测 +唰 +缢 +迹 +灬 +霁 +身 +岁 +赭 +扛 +又 +菡 +乜 +雾 +板 +读 +陷 +徉 +贯 +郁 +虑 +变 +钓 +菜 +圾 +现 +琢 +式 +乐 +维 +渔 +浜 +左 +吾 +脑 +钡 +警 +T +啵 +拴 +偌 +漱 +湿 +硕 +止 +骼 +魄 +积 +燥 +联 +踢 +玛 +则 +窿 +见 +振 +畿 +送 +班 +钽 +您 +赵 +刨 +印 +讨 +踝 +籍 +谡 +舌 +崧 +汽 +蔽 +沪 +酥 +绒 +怖 +财 +帖 +肱 +私 +莎 +勋 +羔 +霸 +励 +哼 +帐 +将 +帅 +渠 +纪 +婴 +娩 +岭 +厘 +滕 +吻 +伤 +坝 +冠 +戊 +隆 +瘁 +介 +涧 +物 +黍 +并 +姗 +奢 +蹑 +掣 +垸 +锴 +命 +箍 +捉 +病 +辖 +琰 +眭 +迩 +艘 +绌 +繁 +寅 +若 +毋 +思 +诉 +类 +诈 +燮 +轲 +酮 +狂 +重 +反 +职 +筱 +县 +委 +磕 +绣 +奖 +晋 +濉 +志 +徽 +肠 +呈 +獐 +坻 +口 +片 +碰 +几 +村 +柿 +劳 +料 +获 +亩 +惕 +晕 +厌 +号 +罢 +池 +正 +鏖 +煨 +家 +棕 +复 +尝 +懋 +蜥 +锅 +岛 +扰 +队 +坠 +瘾 +钬 +@ +卧 +疣 +镇 +譬 +冰 +彷 +频 +黯 +据 +垄 +采 +八 +缪 +瘫 +型 +熹 +砰 +楠 +襁 +箐 +但 +嘶 +绳 +啤 +拍 +盥 +穆 +傲 +洗 +盯 +塘 +怔 +筛 +丿 +台 +恒 +喂 +葛 +永 +¥ +烟 +酒 +桦 +书 +砂 +蚝 +缉 +态 +瀚 +袄 +圳 +轻 +蛛 +超 +榧 +遛 +姒 +奘 +铮 +右 +荽 +望 +偻 +卡 +丶 +氰 +附 +做 +革 +索 +戚 +坨 +桷 +唁 +垅 +榻 +岐 +偎 +坛 +莨 +山 +殊 +微 +骇 +陈 +爨 +推 +嗝 +驹 +澡 +藁 +呤 +卤 +嘻 +糅 +逛 +侵 +郓 +酌 +德 +摇 +※ +鬃 +被 +慨 +殡 +羸 +昌 +泡 +戛 +鞋 +河 +宪 +沿 +玲 +鲨 +翅 +哽 +源 +铅 +语 +照 +邯 +址 +荃 +佬 +顺 +鸳 +町 +霭 +睾 +瓢 +夸 +椁 +晓 +酿 +痈 +咔 +侏 +券 +噎 +湍 +签 +嚷 +离 +午 +尚 +社 +锤 +背 +孟 +使 +浪 +缦 +潍 +鞅 +军 +姹 +驶 +笑 +鳟 +鲁 +》 +孽 +钜 +绿 +洱 +礴 +焯 +椰 +颖 +囔 +乌 +孔 +巴 +互 +性 +椽 +哞 +聘 +昨 +早 +暮 +胶 +炀 +隧 +低 +彗 +昝 +铁 +呓 +氽 +藉 +喔 +癖 +瑗 +姨 +权 +胱 +韦 +堑 +蜜 +酋 +楝 +砝 +毁 +靓 +歙 +锲 +究 +屋 +喳 +骨 +辨 +碑 +武 +鸠 +宫 +辜 +烊 +适 +坡 +殃 +培 +佩 +供 +走 +蜈 +迟 +翼 +况 +姣 +凛 +浔 +吃 +飘 +债 +犟 +金 +促 +苛 +崇 +坂 +莳 +畔 +绂 +兵 +蠕 +斋 +根 +砍 +亢 +欢 +恬 +崔 +剁 +餐 +榫 +快 +扶 +‖ +濒 +缠 +鳜 +当 +彭 +驭 +浦 +篮 +昀 +锆 +秸 +钳 +弋 +娣 +瞑 +夷 +龛 +苫 +拱 +致 +% +嵊 +障 +隐 +弑 +初 +娓 +抉 +汩 +累 +蓖 +" +唬 +助 +苓 +昙 +押 +毙 +破 +城 +郧 +逢 +嚏 +獭 +瞻 +溱 +婿 +赊 +跨 +恼 +璧 +萃 +姻 +貉 +灵 +炉 +密 +氛 +陶 +砸 +谬 +衔 +点 +琛 +沛 +枳 +层 +岱 +诺 +脍 +榈 +埂 +征 +冷 +裁 +打 +蹴 +素 +瘘 +逞 +蛐 +聊 +激 +腱 +萘 +踵 +飒 +蓟 +吆 +取 +咙 +簋 +涓 +矩 +曝 +挺 +揣 +座 +你 +史 +舵 +焱 +尘 +苏 +笈 +脚 +溉 +榨 +诵 +樊 +邓 +焊 +义 +庶 +儋 +蟋 +蒲 +赦 +呷 +杞 +诠 +豪 +还 +试 +颓 +茉 +太 +除 +紫 +逃 +痴 +草 +充 +鳕 +珉 +祗 +墨 +渭 +烩 +蘸 +慕 +璇 +镶 +穴 +嵘 +恶 +骂 +险 +绋 +幕 +碉 +肺 +戳 +刘 +潞 +秣 +纾 +潜 +銮 +洛 +须 +罘 +销 +瘪 +汞 +兮 +屉 +r +林 +厕 +质 +探 +划 +狸 +殚 +善 +煊 +烹 +〒 +锈 +逯 +宸 +辍 +泱 +柚 +袍 +远 +蹋 +嶙 +绝 +峥 +娥 +缍 +雀 +徵 +认 +镱 +谷 += +贩 +勉 +撩 +鄯 +斐 +洋 +非 +祚 +泾 +诒 +饿 +撬 +威 +晷 +搭 +芍 +锥 +笺 +蓦 +候 +琊 +档 +礁 +沼 +卵 +荠 +忑 +朝 +凹 +瑞 +头 +仪 +弧 +孵 +畏 +铆 +突 +衲 +车 +浩 +气 +茂 +悖 +厢 +枕 +酝 +戴 +湾 +邹 +飚 +攘 +锂 +写 +宵 +翁 +岷 +无 +喜 +丈 +挑 +嗟 +绛 +殉 +议 +槽 +具 +醇 +淞 +笃 +郴 +阅 +饼 +底 +壕 +砚 +弈 +询 +缕 +庹 +翟 +零 +筷 +暨 +舟 +闺 +甯 +撞 +麂 +茌 +蔼 +很 +珲 +捕 +棠 +角 +阉 +媛 +娲 +诽 +剿 +尉 +爵 +睬 +韩 +诰 +匣 +危 +糍 +镯 +立 +浏 +阳 +少 +盆 +舔 +擘 +匪 +申 +尬 +铣 +旯 +抖 +赘 +瓯 +居 +ˇ +哮 +游 +锭 +茏 +歌 +坏 +甚 +秒 +舞 +沙 +仗 +劲 +潺 +阿 +燧 +郭 +嗖 +霏 +忠 +材 +奂 +耐 +跺 +砀 +输 +岖 +媳 +氟 +极 +摆 +灿 +今 +扔 +腻 +枝 +奎 +药 +熄 +吨 +话 +q +额 +慑 +嘌 +协 +喀 +壳 +埭 +视 +著 +於 +愧 +陲 +翌 +峁 +颅 +佛 +腹 +聋 +侯 +咎 +叟 +秀 +颇 +存 +较 +罪 +哄 +岗 +扫 +栏 +钾 +羌 +己 +璨 +枭 +霉 +煌 +涸 +衿 +键 +镝 +益 +岢 +奏 +连 +夯 +睿 +冥 +均 +糖 +狞 +蹊 +稻 +爸 +刿 +胥 +煜 +丽 +肿 +璃 +掸 +跚 +灾 +垂 +樾 +濑 +乎 +莲 +窄 +犹 +撮 +战 +馄 +软 +络 +显 +鸢 +胸 +宾 +妲 +恕 +埔 +蝌 +份 +遇 +巧 +瞟 +粒 +恰 +剥 +桡 +博 +讯 +凯 +堇 +阶 +滤 +卖 +斌 +骚 +彬 +兑 +磺 +樱 +舷 +两 +娱 +福 +仃 +差 +找 +桁 +÷ +净 +把 +阴 +污 +戬 +雷 +碓 +蕲 +楚 +罡 +焖 +抽 +妫 +咒 +仑 +闱 +尽 +邑 +菁 +爱 +贷 +沥 +鞑 +牡 +嗉 +崴 +骤 +塌 +嗦 +订 +拮 +滓 +捡 +锻 +次 +坪 +杩 +臃 +箬 +融 +珂 +鹗 +宗 +枚 +降 +鸬 +妯 +阄 +堰 +盐 +毅 +必 +杨 +崃 +俺 +甬 +状 +莘 +货 +耸 +菱 +腼 +铸 +唏 +痤 +孚 +澳 +懒 +溅 +翘 +疙 +杷 +淼 +缙 +骰 +喊 +悉 +砻 +坷 +艇 +赁 +界 +谤 +纣 +宴 +晃 +茹 +归 +饭 +梢 +铡 +街 +抄 +肼 +鬟 +苯 +颂 +撷 +戈 +炒 +咆 +茭 +瘙 +负 +仰 +客 +琉 +铢 +封 +卑 +珥 +椿 +镧 +窨 +鬲 +寿 +御 +袤 +铃 +萎 +砖 +餮 +脒 +裳 +肪 +孕 +嫣 +馗 +嵇 +恳 +氯 +江 +石 +褶 +冢 +祸 +阻 +狈 +羞 +银 +靳 +透 +咳 +叼 +敷 +芷 +啥 +它 +瓤 +兰 +痘 +懊 +逑 +肌 +往 +捺 +坊 +甩 +呻 +〃 +沦 +忘 +膻 +祟 +菅 +剧 +崆 +智 +坯 +臧 +霍 +墅 +攻 +眯 +倘 +拢 +骠 +铐 +庭 +岙 +瓠 +′ +缺 +泥 +迢 +捶 +? +? +郏 +喙 +掷 +沌 +纯 +秘 +种 +听 +绘 +固 +螨 +团 +香 +盗 +妒 +埚 +蓝 +拖 +旱 +荞 +铀 +血 +遏 +汲 +辰 +叩 +拽 +幅 +硬 +惶 +桀 +漠 +措 +泼 +唑 +齐 +肾 +念 +酱 +虚 +屁 +耶 +旗 +砦 +闵 +婉 +馆 +拭 +绅 +韧 +忏 +窝 +醋 +葺 +顾 +辞 +倜 +堆 +辋 +逆 +玟 +贱 +疾 +董 +惘 +倌 +锕 +淘 +嘀 +莽 +俭 +笏 +绑 +鲷 +杈 +择 +蟀 +粥 +嗯 +驰 +逾 +案 +谪 +褓 +胫 +哩 +昕 +颚 +鲢 +绠 +躺 +鹄 +崂 +儒 +俨 +丝 +尕 +泌 +啊 +萸 +彰 +幺 +吟 +骄 +苣 +弦 +脊 +瑰 +〈 +诛 +镁 +析 +闪 +剪 +侧 +哟 +框 +螃 +守 +嬗 +燕 +狭 +铈 +缮 +概 +迳 +痧 +鲲 +俯 +售 +笼 +痣 +扉 +挖 +满 +咋 +援 +邱 +扇 +歪 +便 +玑 +绦 +峡 +蛇 +叨 +〖 +泽 +胃 +斓 +喋 +怂 +坟 +猪 +该 +蚬 +炕 +弥 +赞 +棣 +晔 +娠 +挲 +狡 +创 +疖 +铕 +镭 +稷 +挫 +弭 +啾 +翔 +粉 +履 +苘 +哦 +楼 +秕 +铂 +土 +锣 +瘟 +挣 +栉 +习 +享 +桢 +袅 +磨 +桂 +谦 +延 +坚 +蔚 +噗 +署 +谟 +猬 +钎 +恐 +嬉 +雒 +倦 +衅 +亏 +璩 +睹 +刻 +殿 +王 +算 +雕 +麻 +丘 +柯 +骆 +丸 +塍 +谚 +添 +鲈 +垓 +桎 +蚯 +芥 +予 +飕 +镦 +谌 +窗 +醚 +菀 +亮 +搪 +莺 +蒿 +羁 +足 +J +真 +轶 +悬 +衷 +靛 +翊 +掩 +哒 +炅 +掐 +冼 +妮 +l +谐 +稚 +荆 +擒 +犯 +陵 +虏 +浓 +崽 +刍 +陌 +傻 +孜 +千 +靖 +演 +矜 +钕 +煽 +杰 +酗 +渗 +伞 +栋 +俗 +泫 +戍 +罕 +沾 +疽 +灏 +煦 +芬 +磴 +叱 +阱 +榉 +湃 +蜀 +叉 +醒 +彪 +租 +郡 +篷 +屎 +良 +垢 +隗 +弱 +陨 +峪 +砷 +掴 +颁 +胎 +雯 +绵 +贬 +沐 +撵 +隘 +篙 +暖 +曹 +陡 +栓 +填 +臼 +彦 +瓶 +琪 +潼 +哪 +鸡 +摩 +啦 +俟 +锋 +域 +耻 +蔫 +疯 +纹 +撇 +毒 +绶 +痛 +酯 +忍 +爪 +赳 +歆 +嘹 +辕 +烈 +册 +朴 +钱 +吮 +毯 +癜 +娃 +谀 +邵 +厮 +炽 +璞 +邃 +丐 +追 +词 +瓒 +忆 +轧 +芫 +谯 +喷 +弟 +半 +冕 +裙 +掖 +墉 +绮 +寝 +苔 +势 +顷 +褥 +切 +衮 +君 +佳 +嫒 +蚩 +霞 +佚 +洙 +逊 +镖 +暹 +唛 +& +殒 +顶 +碗 +獗 +轭 +铺 +蛊 +废 +恹 +汨 +崩 +珍 +那 +杵 +曲 +纺 +夏 +薰 +傀 +闳 +淬 +姘 +舀 +拧 +卷 +楂 +恍 +讪 +厩 +寮 +篪 +赓 +乘 +灭 +盅 +鞣 +沟 +慎 +挂 +饺 +鼾 +杳 +树 +缨 +丛 +絮 +娌 +臻 +嗳 +篡 +侩 +述 +衰 +矛 +圈 +蚜 +匕 +筹 +匿 +濞 +晨 +叶 +骋 +郝 +挚 +蚴 +滞 +增 +侍 +描 +瓣 +吖 +嫦 +蟒 +匾 +圣 +赌 +毡 +癞 +恺 +百 +曳 +需 +篓 +肮 +庖 +帏 +卿 +驿 +遗 +蹬 +鬓 +骡 +歉 +芎 +胳 +屐 +禽 +烦 +晌 +寄 +媾 +狄 +翡 +苒 +船 +廉 +终 +痞 +殇 +々 +畦 +饶 +改 +拆 +悻 +萄 +£ +瓿 +乃 +訾 +桅 +匮 +溧 +拥 +纱 +铍 +骗 +蕃 +龋 +缬 +父 +佐 +疚 +栎 +醍 +掳 +蓄 +x +惆 +颜 +鲆 +榆 +〔 +猎 +敌 +暴 +谥 +鲫 +贾 +罗 +玻 +缄 +扦 +芪 +癣 +落 +徒 +臾 +恿 +猩 +托 +邴 +肄 +牵 +春 +陛 +耀 +刊 +拓 +蓓 +邳 +堕 +寇 +枉 +淌 +啡 +湄 +兽 +酷 +萼 +碚 +濠 +萤 +夹 +旬 +戮 +梭 +琥 +椭 +昔 +勺 +蜊 +绐 +晚 +孺 +僵 +宣 +摄 +冽 +旨 +萌 +忙 +蚤 +眉 +噼 +蟑 +付 +契 +瓜 +悼 +颡 +壁 +曾 +窕 +颢 +澎 +仿 +俑 +浑 +嵌 +浣 +乍 +碌 +褪 +乱 +蔟 +隙 +玩 +剐 +葫 +箫 +纲 +围 +伐 +决 +伙 +漩 +瑟 +刑 +肓 +镳 +缓 +蹭 +氨 +皓 +典 +畲 +坍 +铑 +檐 +塑 +洞 +倬 +储 +胴 +淳 +戾 +吐 +灼 +惺 +妙 +毕 +珐 +缈 +虱 +盖 +羰 +鸿 +磅 +谓 +髅 +娴 +苴 +唷 +蚣 +霹 +抨 +贤 +唠 +犬 +誓 +逍 +庠 +逼 +麓 +籼 +釉 +呜 +碧 +秧 +氩 +摔 +霄 +穸 +纨 +辟 +妈 +映 +完 +牛 +缴 +嗷 +炊 +恩 +荔 +茆 +掉 +紊 +慌 +莓 +羟 +阙 +萁 +磐 +另 +蕹 +辱 +鳐 +湮 +吡 +吩 +唐 +睦 +垠 +舒 +圜 +冗 +瞿 +溺 +芾 +囱 +匠 +僳 +汐 +菩 +饬 +漓 +黑 +霰 +浸 +濡 +窥 +毂 +蒡 +兢 +驻 +鹉 +芮 +诙 +迫 +雳 +厂 +忐 +臆 +猴 +鸣 +蚪 +栈 +箕 +羡 +渐 +莆 +捍 +眈 +哓 +趴 +蹼 +埕 +嚣 +骛 +宏 +淄 +斑 +噜 +严 +瑛 +垃 +椎 +诱 +压 +庾 +绞 +焘 +廿 +抡 +迄 +棘 +夫 +纬 +锹 +眨 +瞌 +侠 +脐 +竞 +瀑 +孳 +骧 +遁 +姜 +颦 +荪 +滚 +萦 +伪 +逸 +粳 +爬 +锁 +矣 +役 +趣 +洒 +颔 +诏 +逐 +奸 +甭 +惠 +攀 +蹄 +泛 +尼 +拼 +阮 +鹰 +亚 +颈 +惑 +勒 +〉 +际 +肛 +爷 +刚 +钨 +丰 +养 +冶 +鲽 +辉 +蔻 +画 +覆 +皴 +妊 +麦 +返 +醉 +皂 +擀 +〗 +酶 +凑 +粹 +悟 +诀 +硖 +港 +卜 +z +杀 +涕 +± +舍 +铠 +抵 +弛 +段 +敝 +镐 +奠 +拂 +轴 +跛 +袱 +e +t +沉 +菇 +俎 +薪 +峦 +秭 +蟹 +历 +盟 +菠 +寡 +液 +肢 +喻 +染 +裱 +悱 +抱 +氙 +赤 +捅 +猛 +跑 +氮 +谣 +仁 +尺 +辊 +窍 +烙 +衍 +架 +擦 +倏 +璐 +瑁 +币 +楞 +胖 +夔 +趸 +邛 +惴 +饕 +虔 +蝎 +§ +哉 +贝 +宽 +辫 +炮 +扩 +饲 +籽 +魏 +菟 +锰 +伍 +猝 +末 +琳 +哚 +蛎 +邂 +呀 +姿 +鄞 +却 +歧 +仙 +恸 +椐 +森 +牒 +寤 +袒 +婆 +虢 +雅 +钉 +朵 +贼 +欲 +苞 +寰 +故 +龚 +坭 +嘘 +咫 +礼 +硷 +兀 +睢 +汶 +’ +铲 +烧 +绕 +诃 +浃 +钿 +哺 +柜 +讼 +颊 +璁 +腔 +洽 +咐 +脲 +簌 +筠 +镣 +玮 +鞠 +谁 +兼 +姆 +挥 +梯 +蝴 +谘 +漕 +刷 +躏 +宦 +弼 +b +垌 +劈 +麟 +莉 +揭 +笙 +渎 +仕 +嗤 +仓 +配 +怏 +抬 +错 +泯 +镊 +孰 +猿 +邪 +仍 +秋 +鼬 +壹 +歇 +吵 +炼 +< +尧 +射 +柬 +廷 +胧 +霾 +凳 +隋 +肚 +浮 +梦 +祥 +株 +堵 +退 +L +鹫 +跎 +凶 +毽 +荟 +炫 +栩 +玳 +甜 +沂 +鹿 +顽 +伯 +爹 +赔 +蛴 +徐 +匡 +欣 +狰 +缸 +雹 +蟆 +疤 +默 +沤 +啜 +痂 +衣 +禅 +w +i +h +辽 +葳 +黝 +钗 +停 +沽 +棒 +馨 +颌 +肉 +吴 +硫 +悯 +劾 +娈 +马 +啧 +吊 +悌 +镑 +峭 +帆 +瀣 +涉 +咸 +疸 +滋 +泣 +翦 +拙 +癸 +钥 +蜒 ++ +尾 +庄 +凝 +泉 +婢 +渴 +谊 +乞 +陆 +锉 +糊 +鸦 +淮 +I +B +N +晦 +弗 +乔 +庥 +葡 +尻 +席 +橡 +傣 +渣 +拿 +惩 +麋 +斛 +缃 +矮 +蛏 +岘 +鸽 +姐 +膏 +催 +奔 +镒 +喱 +蠡 +摧 +钯 +胤 +柠 +拐 +璋 +鸥 +卢 +荡 +倾 +^ +_ +珀 +逄 +萧 +塾 +掇 +贮 +笆 +聂 +圃 +冲 +嵬 +M +滔 +笕 +值 +炙 +偶 +蜱 +搐 +梆 +汪 +蔬 +腑 +鸯 +蹇 +敞 +绯 +仨 +祯 +谆 +梧 +糗 +鑫 +啸 +豺 +囹 +猾 +巢 +柄 +瀛 +筑 +踌 +沭 +暗 +苁 +鱿 +蹉 +脂 +蘖 +牢 +热 +木 +吸 +溃 +宠 +序 +泞 +偿 +拜 +檩 +厚 +朐 +毗 +螳 +吞 +媚 +朽 +担 +蝗 +橘 +畴 +祈 +糟 +盱 +隼 +郜 +惜 +珠 +裨 +铵 +焙 +琚 +唯 +咚 +噪 +骊 +丫 +滢 +勤 +棉 +呸 +咣 +淀 +隔 +蕾 +窈 +饨 +挨 +煅 +短 +匙 +粕 +镜 +赣 +撕 +墩 +酬 +馁 +豌 +颐 +抗 +酣 +氓 +佑 +搁 +哭 +递 +耷 +涡 +桃 +贻 +碣 +截 +瘦 +昭 +镌 +蔓 +氚 +甲 +猕 +蕴 +蓬 +散 +拾 +纛 +狼 +猷 +铎 +埋 +旖 +矾 +讳 +囊 +糜 +迈 +粟 +蚂 +紧 +鲳 +瘢 +栽 +稼 +羊 +锄 +斟 +睁 +桥 +瓮 +蹙 +祉 +醺 +鼻 +昱 +剃 +跳 +篱 +跷 +蒜 +翎 +宅 +晖 +嗑 +壑 +峻 +癫 +屏 +狠 +陋 +袜 +途 +憎 +祀 +莹 +滟 +佶 +溥 +臣 +约 +盛 +峰 +磁 +慵 +婪 +拦 +莅 +朕 +鹦 +粲 +裤 +哎 +疡 +嫖 +琵 +窟 +堪 +谛 +嘉 +儡 +鳝 +斩 +郾 +驸 +酊 +妄 +胜 +贺 +徙 +傅 +噌 +钢 +栅 +庇 +恋 +匝 +巯 +邈 +尸 +锚 +粗 +佟 +蛟 +薹 +纵 +蚊 +郅 +绢 +锐 +苗 +俞 +篆 +淆 +膀 +鲜 +煎 +诶 +秽 +寻 +涮 +刺 +怀 +噶 +巨 +褰 +魅 +灶 +灌 +桉 +藕 +谜 +舸 +薄 +搀 +恽 +借 +牯 +痉 +渥 +愿 +亓 +耘 +杠 +柩 +锔 +蚶 +钣 +珈 +喘 +蹒 +幽 +赐 +稗 +晤 +莱 +泔 +扯 +肯 +菪 +裆 +腩 +豉 +疆 +骜 +腐 +倭 +珏 +唔 +粮 +亡 +润 +慰 +伽 +橄 +玄 +誉 +醐 +胆 +龊 +粼 +塬 +陇 +彼 +削 +嗣 +绾 +芽 +妗 +垭 +瘴 +爽 +薏 +寨 +龈 +泠 +弹 +赢 +漪 +猫 +嘧 +涂 +恤 +圭 +茧 +烽 +屑 +痕 +巾 +赖 +荸 +凰 +腮 +畈 +亵 +蹲 +偃 +苇 +澜 +艮 +换 +骺 +烘 +苕 +梓 +颉 +肇 +哗 +悄 +氤 +涠 +葬 +屠 +鹭 +植 +竺 +佯 +诣 +鲇 +瘀 +鲅 +邦 +移 +滁 +冯 +耕 +癔 +戌 +茬 +沁 +巩 +悠 +湘 +洪 +痹 +锟 +循 +谋 +腕 +鳃 +钠 +捞 +焉 +迎 +碱 +伫 +急 +榷 +奈 +邝 +卯 +辄 +皲 +卟 +醛 +畹 +忧 +稳 +雄 +昼 +缩 +阈 +睑 +扌 +耗 +曦 +涅 +捏 +瞧 +邕 +淖 +漉 +铝 +耦 +禹 +湛 +喽 +莼 +琅 +诸 +苎 +纂 +硅 +始 +嗨 +傥 +燃 +臂 +赅 +嘈 +呆 +贵 +屹 +壮 +肋 +亍 +蚀 +卅 +豹 +腆 +邬 +迭 +浊 +} +童 +螂 +捐 +圩 +勐 +触 +寞 +汊 +壤 +荫 +膺 +渌 +芳 +懿 +遴 +螈 +泰 +蓼 +蛤 +茜 +舅 +枫 +朔 +膝 +眙 +避 +梅 +判 +鹜 +璜 +牍 +缅 +垫 +藻 +黔 +侥 +惚 +懂 +踩 +腰 +腈 +札 +丞 +唾 +慈 +顿 +摹 +荻 +琬 +~ +斧 +沈 +滂 +胁 +胀 +幄 +莜 +Z +匀 +鄄 +掌 +绰 +茎 +焚 +赋 +萱 +谑 +汁 +铒 +瞎 +夺 +蜗 +野 +娆 +冀 +弯 +篁 +懵 +灞 +隽 +芡 +脘 +俐 +辩 +芯 +掺 +喏 +膈 +蝈 +觐 +悚 +踹 +蔗 +熠 +鼠 +呵 +抓 +橼 +峨 +畜 +缔 +禾 +崭 +弃 +熊 +摒 +凸 +拗 +穹 +蒙 +抒 +祛 +劝 +闫 +扳 +阵 +醌 +踪 +喵 +侣 +搬 +仅 +荧 +赎 +蝾 +琦 +买 +婧 +瞄 +寓 +皎 +冻 +赝 +箩 +莫 +瞰 +郊 +笫 +姝 +筒 +枪 +遣 +煸 +袋 +舆 +痱 +涛 +母 +〇 +启 +践 +耙 +绲 +盘 +遂 +昊 +搞 +槿 +诬 +纰 +泓 +惨 +檬 +亻 +越 +C +o +憩 +熵 +祷 +钒 +暧 +塔 +阗 +胰 +咄 +娶 +魔 +琶 +钞 +邻 +扬 +杉 +殴 +咽 +弓 +〆 +髻 +】 +吭 +揽 +霆 +拄 +殖 +脆 +彻 +岩 +芝 +勃 +辣 +剌 +钝 +嘎 +甄 +佘 +皖 +伦 +授 +徕 +憔 +挪 +皇 +庞 +稔 +芜 +踏 +溴 +兖 +卒 +擢 +饥 +鳞 +煲 +‰ +账 +颗 +叻 +斯 +捧 +鳍 +琮 +讹 +蛙 +纽 +谭 +酸 +兔 +莒 +睇 +伟 +觑 +羲 +嗜 +宜 +褐 +旎 +辛 +卦 +诘 +筋 +鎏 +溪 +挛 +熔 +阜 +晰 +鳅 +丢 +奚 +灸 +呱 +献 +陉 +黛 +鸪 +甾 +萨 +疮 +拯 +洲 +疹 +辑 +叙 +恻 +谒 +允 +柔 +烂 +氏 +逅 +漆 +拎 +惋 +扈 +湟 +纭 +啕 +掬 +擞 +哥 +忽 +涤 +鸵 +靡 +郗 +瓷 +扁 +廊 +怨 +雏 +钮 +敦 +E +懦 +憋 +汀 +拚 +啉 +腌 +岸 +f +痼 +瞅 +尊 +咀 +眩 +飙 +忌 +仝 +迦 +熬 +毫 +胯 +篑 +茄 +腺 +凄 +舛 +碴 +锵 +诧 +羯 +後 +漏 +汤 +宓 +仞 +蚁 +壶 +谰 +皑 +铄 +棰 +罔 +辅 +晶 +苦 +牟 +闽 +\ +烃 +饮 +聿 +丙 +蛳 +朱 +煤 +涔 +鳖 +犁 +罐 +荼 +砒 +淦 +妤 +黏 +戎 +孑 +婕 +瑾 +戢 +钵 +枣 +捋 +砥 +衩 +狙 +桠 +稣 +阎 +肃 +梏 +诫 +孪 +昶 +婊 +衫 +嗔 +侃 +塞 +蜃 +樵 +峒 +貌 +屿 +欺 +缫 +阐 +栖 +诟 +珞 +荭 +吝 +萍 +嗽 +恂 +啻 +蜴 +磬 +峋 +俸 +豫 +谎 +徊 +镍 +韬 +魇 +晴 +U +囟 +猜 +蛮 +坐 +囿 +伴 +亭 +肝 +佗 +蝠 +妃 +胞 +滩 +榴 +氖 +垩 +苋 +砣 +扪 +馏 +姓 +轩 +厉 +夥 +侈 +禀 +垒 +岑 +赏 +钛 +辐 +痔 +披 +纸 +碳 +“ +坞 +蠓 +挤 +荥 +沅 +悔 +铧 +帼 +蒌 +蝇 +a +p +y +n +g +哀 +浆 +瑶 +凿 +桶 +馈 +皮 +奴 +苜 +佤 +伶 +晗 +铱 +炬 +优 +弊 +氢 +恃 +甫 +攥 +端 +锌 +灰 +稹 +炝 +曙 +邋 +亥 +眶 +碾 +拉 +萝 +绔 +捷 +浍 +腋 +姑 +菖 +凌 +涞 +麽 +锢 +桨 +潢 +绎 +镰 +殆 +锑 +渝 +铬 +困 +绽 +觎 +匈 +糙 +暑 +裹 +鸟 +盔 +肽 +迷 +綦 +『 +亳 +佝 +俘 +钴 +觇 +骥 +仆 +疝 +跪 +婶 +郯 +瀹 +唉 +脖 +踞 +针 +晾 +忒 +扼 +瞩 +叛 +椒 +疟 +嗡 +邗 +肆 +跆 +玫 +忡 +捣 +咧 +唆 +艄 +蘑 +潦 +笛 +阚 +沸 +泻 +掊 +菽 +贫 +斥 +髂 +孢 +镂 +赂 +麝 +鸾 +屡 +衬 +苷 +恪 +叠 +希 +粤 +爻 +喝 +茫 +惬 +郸 +绻 +庸 +撅 +碟 +宄 +妹 +膛 +叮 +饵 +崛 +嗲 +椅 +冤 +搅 +咕 +敛 +尹 +垦 +闷 +蝉 +霎 +勰 +败 +蓑 +泸 +肤 +鹌 +幌 +焦 +浠 +鞍 +刁 +舰 +乙 +竿 +裔 +。 +茵 +函 +伊 +兄 +丨 +娜 +匍 +謇 +莪 +宥 +似 +蝽 +翳 +酪 +翠 +粑 +薇 +祢 +骏 +赠 +叫 +Q +噤 +噻 +竖 +芗 +莠 +潭 +俊 +羿 +耜 +O +郫 +趁 +嗪 +囚 +蹶 +芒 +洁 +笋 +鹑 +敲 +硝 +啶 +堡 +渲 +揩 +』 +携 +宿 +遒 +颍 +扭 +棱 +割 +萜 +蔸 +葵 +琴 +捂 +饰 +衙 +耿 +掠 +募 +岂 +窖 +涟 +蔺 +瘤 +柞 +瞪 +怜 +匹 +距 +楔 +炜 +哆 +秦 +缎 +幼 +茁 +绪 +痨 +恨 +楸 +娅 +瓦 +桩 +雪 +嬴 +伏 +榔 +妥 +铿 +拌 +眠 +雍 +缇 +‘ +卓 +搓 +哌 +觞 +噩 +屈 +哧 +髓 +咦 +巅 +娑 +侑 +淫 +膳 +祝 +勾 +姊 +莴 +胄 +疃 +薛 +蜷 +胛 +巷 +芙 +芋 +熙 +闰 +勿 +窃 +狱 +剩 +钏 +幢 +陟 +铛 +慧 +靴 +耍 +k +浙 +浇 +飨 +惟 +绗 +祜 +澈 +啼 +咪 +磷 +摞 +诅 +郦 +抹 +跃 +壬 +吕 +肖 +琏 +颤 +尴 +剡 +抠 +凋 +赚 +泊 +津 +宕 +殷 +倔 +氲 +漫 +邺 +涎 +怠 +$ +垮 +荬 +遵 +俏 +叹 +噢 +饽 +蜘 +孙 +筵 +疼 +鞭 +羧 +牦 +箭 +潴 +c +眸 +祭 +髯 +啖 +坳 +愁 +芩 +驮 +倡 +巽 +穰 +沃 +胚 +怒 +凤 +槛 +剂 +趵 +嫁 +v +邢 +灯 +鄢 +桐 +睽 +檗 +锯 +槟 +婷 +嵋 +圻 +诗 +蕈 +颠 +遭 +痢 +芸 +怯 +馥 +竭 +锗 +徜 +恭 +遍 +籁 +剑 +嘱 +苡 +龄 +僧 +桑 +潸 +弘 +澶 +楹 +悲 +讫 +愤 +腥 +悸 +谍 +椹 +呢 +桓 +葭 +攫 +阀 +翰 +躲 +敖 +柑 +郎 +笨 +橇 +呃 +魁 +燎 +脓 +葩 +磋 +垛 +玺 +狮 +沓 +砜 +蕊 +锺 +罹 +蕉 +翱 +虐 +闾 +巫 +旦 +茱 +嬷 +枯 +鹏 +贡 +芹 +汛 +矫 +绁 +拣 +禺 +佃 +讣 +舫 +惯 +乳 +趋 +疲 +挽 +岚 +虾 +衾 +蠹 +蹂 +飓 +氦 +铖 +孩 +稞 +瑜 +壅 +掀 +勘 +妓 +畅 +髋 +W +庐 +牲 +蓿 +榕 +练 +垣 +唱 +邸 +菲 +昆 +婺 +穿 +绡 +麒 +蚱 +掂 +愚 +泷 +涪 +漳 +妩 +娉 +榄 +讷 +觅 +旧 +藤 +煮 +呛 +柳 +腓 +叭 +庵 +烷 +阡 +罂 +蜕 +擂 +猖 +咿 +媲 +脉 +【 +沏 +貅 +黠 +熏 +哲 +烁 +坦 +酵 +兜 +× +潇 +撒 +剽 +珩 +圹 +乾 +摸 +樟 +帽 +嗒 +襄 +魂 +轿 +憬 +锡 +〕 +喃 +皆 +咖 +隅 +脸 +残 +泮 +袂 +鹂 +珊 +囤 +捆 +咤 +误 +徨 +闹 +淙 +芊 +淋 +怆 +囗 +拨 +梳 +渤 +R +G +绨 +蚓 +婀 +幡 +狩 +麾 +谢 +唢 +裸 +旌 +伉 +纶 +裂 +驳 +砼 +咛 +澄 +樨 +蹈 +宙 +澍 +倍 +貔 +操 +勇 +蟠 +摈 +砧 +虬 +够 +缁 +悦 +藿 +撸 +艹 +摁 +淹 +豇 +虎 +榭 +ˉ +吱 +d +° +喧 +荀 +踱 +侮 +奋 +偕 +饷 +犍 +惮 +坑 +璎 +徘 +宛 +妆 +袈 +倩 +窦 +昂 +荏 +乖 +K +怅 +撰 +鳙 +牙 +袁 +酞 +X +痿 +琼 +闸 +雁 +趾 +荚 +虻 +涝 +《 +杏 +韭 +偈 +烤 +绫 +鞘 +卉 +症 +遢 +蓥 +诋 +杭 +荨 +匆 +竣 +簪 +辙 +敕 +虞 +丹 +缭 +咩 +黟 +m +淤 +瑕 +咂 +铉 +硼 +茨 +嶂 +痒 +畸 +敬 +涿 +粪 +窘 +熟 +叔 +嫔 +盾 +忱 +裘 +憾 +梵 +赡 +珙 +咯 +娘 +庙 +溯 +胺 +葱 +痪 +摊 +荷 +卞 +乒 +髦 +寐 +铭 +坩 +胗 +枷 +爆 +溟 +嚼 +羚 +砬 +轨 +惊 +挠 +罄 +竽 +菏 +氧 +浅 +楣 +盼 +枢 +炸 +阆 +杯 +谏 +噬 +淇 +渺 +俪 +秆 +墓 +泪 +跻 +砌 +痰 +垡 +渡 +耽 +釜 +讶 +鳎 +煞 +呗 +韶 +舶 +绷 +鹳 +缜 +旷 +铊 +皱 +龌 +檀 +霖 +奄 +槐 +艳 +蝶 +旋 +哝 +赶 +骞 +蚧 +腊 +盈 +丁 +` +蜚 +矸 +蝙 +睨 +嚓 +僻 +鬼 +醴 +夜 +彝 +磊 +笔 +拔 +栀 +糕 +厦 +邰 +纫 +逭 +纤 +眦 +膊 +馍 +躇 +烯 +蘼 +冬 +诤 +暄 +骶 +哑 +瘠 +」 +臊 +丕 +愈 +咱 +螺 +擅 +跋 +搏 +硪 +谄 +笠 +淡 +嘿 +骅 +谧 +鼎 +皋 +姚 +歼 +蠢 +驼 +耳 +胬 +挝 +涯 +狗 +蒽 +孓 +犷 +凉 +芦 +箴 +铤 +孤 +嘛 +坤 +V +茴 +朦 +挞 +尖 +橙 +诞 +搴 +碇 +洵 +浚 +帚 +蜍 +漯 +柘 +嚎 +讽 +芭 +荤 +咻 +祠 +秉 +跖 +埃 +吓 +糯 +眷 +馒 +惹 +娼 +鲑 +嫩 +讴 +轮 +瞥 +靶 +褚 +乏 +缤 +宋 +帧 +删 +驱 +碎 +扑 +俩 +俄 +偏 +涣 +竹 +噱 +皙 +佰 +渚 +唧 +斡 +# +镉 +刀 +崎 +筐 +佣 +夭 +贰 +肴 +峙 +哔 +艿 +匐 +牺 +镛 +缘 +仡 +嫡 +劣 +枸 +堀 +梨 +簿 +鸭 +蒸 +亦 +稽 +浴 +{ +衢 +束 +槲 +j +阁 +揍 +疥 +棋 +潋 +聪 +窜 +乓 +睛 +插 +冉 +阪 +苍 +搽 +「 +蟾 +螟 +幸 +仇 +樽 +撂 +慢 +跤 +幔 +俚 +淅 +覃 +觊 +溶 +妖 +帛 +侨 +曰 +妾 +泗 +· +: +瀘 +風 +Ë +( +) +∶ +紅 +紗 +瑭 +雲 +頭 +鶏 +財 +許 +• +¥ +樂 +焗 +麗 +— +; +滙 +東 +榮 +繪 +興 +… +門 +業 +π +楊 +國 +顧 +é +盤 +寳 +Λ +龍 +鳳 +島 +誌 +緣 +結 +銭 +萬 +勝 +祎 +璟 +優 +歡 +臨 +時 +購 += +★ +藍 +昇 +鐵 +觀 +勅 +農 +聲 +畫 +兿 +術 +發 +劉 +記 +專 +耑 +園 +書 +壴 +種 +Ο +● +褀 +號 +銀 +匯 +敟 +锘 +葉 +橪 +廣 +進 +蒄 +鑽 +阝 +祙 +貢 +鍋 +豊 +夬 +喆 +團 +閣 +開 +燁 +賓 +館 +酡 +沔 +順 ++ +硚 +劵 +饸 +陽 +車 +湓 +復 +萊 +氣 +軒 +華 +堃 +迮 +纟 +戶 +馬 +學 +裡 +電 +嶽 +獨 +マ +シ +サ +ジ +燘 +袪 +環 +❤ +臺 +灣 +専 +賣 +孖 +聖 +攝 +線 +▪ +α +傢 +俬 +夢 +達 +莊 +喬 +貝 +薩 +劍 +羅 +壓 +棛 +饦 +尃 +璈 +囍 +醫 +G +I +A +# +N +鷄 +髙 +嬰 +啓 +約 +隹 +潔 +賴 +藝 +~ +寶 +籣 +麺 +  +嶺 +√ +義 +網 +峩 +長 +∧ +魚 +機 +構 +② +鳯 +偉 +L +B +㙟 +畵 +鴿 +' +詩 +溝 +嚞 +屌 +藔 +佧 +玥 +蘭 +織 +1 +3 +9 +0 +7 +點 +砭 +鴨 +鋪 +銘 +廳 +弍 +‧ +創 +湯 +坶 +℃ +卩 +骝 +& +烜 +荘 +當 +潤 +扞 +係 +懷 +碶 +钅 +蚨 +讠 +☆ +叢 +爲 +埗 +涫 +塗 +→ +楽 +現 +鯨 +愛 +瑪 +鈺 +忄 +悶 +藥 +飾 +樓 +視 +孬 +ㆍ +燚 +苪 +師 +① +丼 +锽 +│ +韓 +標 +è +兒 +閏 +匋 +張 +漢 +Ü +髪 +會 +閑 +檔 +習 +裝 +の +峯 +菘 +輝 +И +雞 +釣 +億 +浐 +K +O +R +8 +H +E +P +T +W +D +S +C +M +F +姌 +饹 +» +晞 +廰 +ä +嵯 +鷹 +負 +飲 +絲 +冚 +楗 +澤 +綫 +區 +❋ +← +質 +靑 +揚 +③ +滬 +統 +産 +協 +﹑ +乸 +畐 +經 +運 +際 +洺 +岽 +為 +粵 +諾 +崋 +豐 +碁 +ɔ +V +2 +6 +齋 +誠 +訂 +´ +勑 +雙 +陳 +無 +í +泩 +媄 +夌 +刂 +i +c +t +o +r +a +嘢 +耄 +燴 +暃 +壽 +媽 +靈 +抻 +體 +唻 +É +冮 +甹 +鎮 +錦 +ʌ +蜛 +蠄 +尓 +駕 +戀 +飬 +逹 +倫 +貴 +極 +Я +Й +寬 +磚 +嶪 +郎 +職 +| +間 +n +d +剎 +伈 +課 +飛 +橋 +瘊 +№ +譜 +骓 +圗 +滘 +縣 +粿 +咅 +養 +濤 +彳 +® +% +Ⅱ +啰 +㴪 +見 +矞 +薬 +糁 +邨 +鲮 +顔 +罱 +З +選 +話 +贏 +氪 +俵 +競 +瑩 +繡 +枱 +β +綉 +á +獅 +爾 +™ +麵 +戋 +淩 +徳 +個 +劇 +場 +務 +簡 +寵 +h +實 +膠 +轱 +圖 +築 +嘣 +樹 +㸃 +營 +耵 +孫 +饃 +鄺 +飯 +麯 +遠 +輸 +坫 +孃 +乚 +閃 +鏢 +㎡ +題 +廠 +關 +↑ +爺 +將 +軍 +連 +篦 +覌 +參 +箸 +- +窠 +棽 +寕 +夀 +爰 +歐 +呙 +閥 +頡 +熱 +雎 +垟 +裟 +凬 +勁 +帑 +馕 +夆 +疌 +枼 +馮 +貨 +蒤 +樸 +彧 +旸 +靜 +龢 +暢 +㐱 +鳥 +珺 +鏡 +灡 +爭 +堷 +廚 +Ó +騰 +診 +┅ +蘇 +褔 +凱 +頂 +豕 +亞 +帥 +嘬 +⊥ +仺 +桖 +複 +饣 +絡 +穂 +顏 +棟 +納 +▏ +濟 +親 +設 +計 +攵 +埌 +烺 +ò +頤 +燦 +蓮 +撻 +節 +講 +濱 +濃 +娽 +洳 +朿 +燈 +鈴 +護 +膚 +铔 +過 +補 +Z +U +5 +4 +坋 +闿 +䖝 +餘 +缐 +铞 +貿 +铪 +桼 +趙 +鍊 +[ +㐂 +垚 +菓 +揸 +捲 +鐘 +滏 +𣇉 +爍 +輪 +燜 +鴻 +鮮 +動 +鹞 +鷗 +丄 +慶 +鉌 +翥 +飮 +腸 +⇋ +漁 +覺 +來 +熘 +昴 +翏 +鲱 +圧 +鄉 +萭 +頔 +爐 +嫚 +г +貭 +類 +聯 +幛 +輕 +訓 +鑒 +夋 +锨 +芃 +珣 +䝉 +扙 +嵐 +銷 +處 +ㄱ +語 +誘 +苝 +歸 +儀 +燒 +楿 +內 +粢 +葒 +奧 +麥 +礻 +滿 +蠔 +穵 +瞭 +態 +鱬 +榞 +硂 +鄭 +黃 +煙 +祐 +奓 +逺 +* +瑄 +獲 +聞 +薦 +讀 +這 +樣 +決 +問 +啟 +們 +執 +説 +轉 +單 +隨 +唘 +帶 +倉 +庫 +還 +贈 +尙 +皺 +■ +餅 +產 +○ +∈ +報 +狀 +楓 +賠 +琯 +嗮 +禮 +` +傳 +> +≤ +嗞 +Φ +≥ +換 +咭 +∣ +↓ +曬 +ε +応 +寫 +″ +終 +様 +純 +費 +療 +聨 +凍 +壐 +郵 +ü +黒 +∫ +製 +塊 +調 +軽 +確 +撃 +級 +馴 +Ⅲ +涇 +繹 +數 +碼 +證 +狒 +処 +劑 +< +晧 +賀 +衆 +] +櫥 +兩 +陰 +絶 +對 +鯉 +憶 +◎ +p +e +Y +蕒 +煖 +頓 +測 +試 +鼽 +僑 +碩 +妝 +帯 +≈ +鐡 +舖 +權 +喫 +倆 +ˋ +該 +悅 +ā +俫 +. +f +s +b +m +k +g +u +j +貼 +淨 +濕 +針 +適 +備 +l +/ +給 +謢 +強 +觸 +衛 +與 +⊙ +$ +緯 +變 +⑴ +⑵ +⑶ +㎏ +殺 +∩ +幚 +─ +價 +▲ +離 +ú +ó +飄 +烏 +関 +閟 +﹝ +﹞ +邏 +輯 +鍵 +驗 +訣 +導 +歷 +屆 +層 +▼ +儱 +錄 +熳 +ē +艦 +吋 +錶 +辧 +飼 +顯 +④ +禦 +販 +気 +対 +枰 +閩 +紀 +幹 +瞓 +貊 +淚 +△ +眞 +墊 +Ω +獻 +褲 +縫 +緑 +亜 +鉅 +餠 +{ +} +◆ +蘆 +薈 +█ +◇ +溫 +彈 +晳 +粧 +犸 +穩 +訊 +崬 +凖 +熥 +П +舊 +條 +紋 +圍 +Ⅳ +筆 +尷 +難 +雜 +錯 +綁 +識 +頰 +鎖 +艶 +□ +殁 +殼 +⑧ +├ +▕ +鵬 +ǐ +ō +ǒ +糝 +綱 +▎ +μ +盜 +饅 +醬 +籤 +蓋 +釀 +鹽 +據 +à +ɡ +辦 +◥ +彐 +┌ +婦 +獸 +鲩 +伱 +ī +蒟 +蒻 +齊 +袆 +腦 +寧 +凈 +妳 +煥 +詢 +偽 +謹 +啫 +鯽 +騷 +鱸 +損 +傷 +鎻 +髮 +買 +冏 +儥 +両 +﹢ +∞ +載 +喰 +z +羙 +悵 +燙 +曉 +員 +組 +徹 +艷 +痠 +鋼 +鼙 +縮 +細 +嚒 +爯 +≠ +維 +" +鱻 +壇 +厍 +帰 +浥 +犇 +薡 +軎 +² +應 +醜 +刪 +緻 +鶴 +賜 +噁 +軌 +尨 +镔 +鷺 +槗 +彌 +葚 +濛 +請 +溇 +緹 +賢 +訪 +獴 +瑅 +資 +縤 +陣 +蕟 +栢 +韻 +祼 +恁 +伢 +謝 +劃 +涑 +總 +衖 +踺 +砋 +凉 +籃 +駿 +苼 +瘋 +昽 +紡 +驊 +腎 +﹗ +響 +杋 +剛 +嚴 +禪 +歓 +槍 +傘 +檸 +檫 +炣 +勢 +鏜 +鎢 +銑 +尐 +減 +奪 +惡 +θ +僮 +婭 +臘 +ū +ì +殻 +鉄 +∑ +蛲 +焼 +緖 +續 +紹 +懮 diff --git a/modules/onnx_ocr_module/src/ppocr/utils/profiler.py b/modules/onnx_ocr_module/src/ppocr/utils/profiler.py new file mode 100644 index 0000000..e4e3e05 --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/utils/profiler.py @@ -0,0 +1,130 @@ +# copyright (c) 2021 PaddlePaddle Authors. All Rights Reserve. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import sys +import paddle.profiler as profiler + +# A global variable to record the number of calling times for profiler +# functions. It is used to specify the tracing range of training steps. +_profiler_step_id = 0 + +# A global variable to avoid parsing from string every time. +_profiler_options = None +_prof = None + + +class ProfilerOptions(object): + """ + Use a string to initialize a ProfilerOptions. + The string should be in the format: "key1=value1;key2=value;key3=value3". + For example: + "profile_path=model.profile" + "batch_range=[50, 60]; profile_path=model.profile" + "batch_range=[50, 60]; tracer_option=OpDetail; profile_path=model.profile" + + ProfilerOptions supports following key-value pair: + batch_range - a integer list, e.g. [100, 110]. + state - a string, the optional values are 'CPU', 'GPU' or 'All'. + sorted_key - a string, the optional values are 'calls', 'total', + 'max', 'min' or 'ave. + tracer_option - a string, the optional values are 'Default', 'OpDetail', + 'AllOpDetail'. + profile_path - a string, the path to save the serialized profile data, + which can be used to generate a timeline. + exit_on_finished - a boolean. + """ + + def __init__(self, options_str): + assert isinstance(options_str, str) + + self._options = { + "batch_range": [10, 20], + "state": "All", + "sorted_key": "total", + "tracer_option": "Default", + "profile_path": "/tmp/profile", + "exit_on_finished": True, + "timer_only": True, + } + self._parse_from_string(options_str) + + def _parse_from_string(self, options_str): + for kv in options_str.replace(" ", "").split(";"): + key, value = kv.split("=") + if key == "batch_range": + value_list = value.replace("[", "").replace("]", "").split(",") + value_list = list(map(int, value_list)) + if ( + len(value_list) >= 2 + and value_list[0] >= 0 + and value_list[1] > value_list[0] + ): + self._options[key] = value_list + elif key == "exit_on_finished": + self._options[key] = value.lower() in ("yes", "true", "t", "1") + elif key in ["state", "sorted_key", "tracer_option", "profile_path"]: + self._options[key] = value + elif key == "timer_only": + self._options[key] = value + + def __getitem__(self, name): + if self._options.get(name, None) is None: + raise ValueError("ProfilerOptions does not have an option named %s." % name) + return self._options[name] + + +def add_profiler_step(options_str=None): + """ + Enable the operator-level timing using PaddlePaddle's profiler. + The profiler uses a independent variable to count the profiler steps. + One call of this function is treated as a profiler step. + Args: + profiler_options - a string to initialize the ProfilerOptions. + Default is None, and the profiler is disabled. + """ + if options_str is None: + return + + global _prof + global _profiler_step_id + global _profiler_options + + if _profiler_options is None: + _profiler_options = ProfilerOptions(options_str) + # profile : https://www.paddlepaddle.org.cn/documentation/docs/zh/guides/performance_improving/profiling_model.html#chakanxingnengshujudetongjibiaodan + # timer_only = True only the model's throughput and time overhead are displayed + # timer_only = False calling summary can print a statistical form that presents performance data from different perspectives. + # timer_only = False the output Timeline information can be found in the profiler_log directory + if _prof is None: + _timer_only = str(_profiler_options["timer_only"]) == str(True) + _prof = profiler.Profiler( + scheduler=( + _profiler_options["batch_range"][0], + _profiler_options["batch_range"][1], + ), + on_trace_ready=profiler.export_chrome_tracing("./profiler_log"), + timer_only=_timer_only, + ) + _prof.start() + else: + _prof.step() + + if _profiler_step_id == _profiler_options["batch_range"][1]: + _prof.stop() + _prof.summary(op_detail=True, thread_sep=False, time_unit="ms") + _prof = None + if _profiler_options["exit_on_finished"]: + sys.exit(0) + + _profiler_step_id += 1 diff --git a/modules/onnx_ocr_module/src/ppocr/utils/save_load.py b/modules/onnx_ocr_module/src/ppocr/utils/save_load.py new file mode 100644 index 0000000..4d4b7ba --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/utils/save_load.py @@ -0,0 +1,371 @@ +# Copyright (c) 2020 PaddlePaddle Authors. All Rights Reserve. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import errno +import os +import pickle +import json + +import paddle + +from ppocr.utils.logging import get_logger +from ppocr.utils.network import maybe_download_params + +try: + import encryption # Attempt to import the encryption module for AIStudio's encryption model + + encrypted = encryption.is_encryption_needed() +except ImportError: + get_logger().warning("Skipping import of the encryption module.") + encrypted = False # Encryption is not needed if the module cannot be imported + +__all__ = ["load_model"] + + +def _mkdir_if_not_exist(path, logger): + """ + mkdir if not exists, ignore the exception when multiprocess mkdir together + """ + if not os.path.exists(path): + try: + os.makedirs(path) + except OSError as e: + if e.errno == errno.EEXIST and os.path.isdir(path): + logger.warning( + "be happy if some process has already created {}".format(path) + ) + else: + raise OSError("Failed to mkdir {}".format(path)) + + +def load_model(config, model, optimizer=None, model_type="det"): + """ + load model from checkpoint or pretrained_model + """ + logger = get_logger() + global_config = config["Global"] + checkpoints = global_config.get("checkpoints") + pretrained_model = global_config.get("pretrained_model") + best_model_dict = {} + is_float16 = False + is_nlp_model = model_type == "kie" and config["Architecture"]["algorithm"] not in [ + "SDMGR" + ] + + if is_nlp_model is True: + # NOTE: for kie model dsitillation, resume training is not supported now + if config["Architecture"]["algorithm"] in ["Distillation"]: + return best_model_dict + checkpoints = config["Architecture"]["Backbone"]["checkpoints"] + # load kie method metric + if checkpoints: + if os.path.exists(os.path.join(checkpoints, "metric.states")): + with open(os.path.join(checkpoints, "metric.states"), "rb") as f: + states_dict = pickle.load(f, encoding="latin1") + best_model_dict = states_dict.get("best_model_dict", {}) + if "epoch" in states_dict: + best_model_dict["start_epoch"] = states_dict["epoch"] + 1 + logger.info("resume from {}".format(checkpoints)) + + if optimizer is not None: + if checkpoints[-1] in ["/", "\\"]: + checkpoints = checkpoints[:-1] + if os.path.exists(checkpoints + ".pdopt"): + optim_dict = paddle.load(checkpoints + ".pdopt") + optimizer.set_state_dict(optim_dict) + else: + logger.warning( + "{}.pdopt is not exists, params of optimizer is not loaded".format( + checkpoints + ) + ) + + return best_model_dict + + if checkpoints: + if checkpoints.endswith(".pdparams"): + checkpoints = checkpoints.replace(".pdparams", "") + assert os.path.exists( + checkpoints + ".pdparams" + ), "The {}.pdparams does not exists!".format(checkpoints) + + # load params from trained model + params = paddle.load(checkpoints + ".pdparams") + state_dict = model.state_dict() + new_state_dict = {} + for key, value in state_dict.items(): + if key not in params: + logger.warning( + "{} not in loaded params {} !".format(key, params.keys()) + ) + continue + pre_value = params[key] + if pre_value.dtype == paddle.float16: + is_float16 = True + if pre_value.dtype != value.dtype: + pre_value = pre_value.astype(value.dtype) + if list(value.shape) == list(pre_value.shape): + new_state_dict[key] = pre_value + else: + logger.warning( + "The shape of model params {} {} not matched with loaded params shape {} !".format( + key, value.shape, pre_value.shape + ) + ) + model.set_state_dict(new_state_dict) + if is_float16: + logger.info( + "The parameter type is float16, which is converted to float32 when loading" + ) + if optimizer is not None: + if os.path.exists(checkpoints + ".pdopt"): + optim_dict = paddle.load(checkpoints + ".pdopt") + optimizer.set_state_dict(optim_dict) + else: + logger.warning( + "{}.pdopt is not exists, params of optimizer is not loaded".format( + checkpoints + ) + ) + + if os.path.exists(checkpoints + ".states"): + with open(checkpoints + ".states", "rb") as f: + states_dict = pickle.load(f, encoding="latin1") + best_model_dict = states_dict.get("best_model_dict", {}) + best_model_dict["acc"] = 0.0 + if "epoch" in states_dict: + best_model_dict["start_epoch"] = states_dict["epoch"] + 1 + logger.info("resume from {}".format(checkpoints)) + elif pretrained_model: + is_float16 = load_pretrained_params(model, pretrained_model) + else: + logger.info("train from scratch") + best_model_dict["is_float16"] = is_float16 + return best_model_dict + + +def load_pretrained_params(model, path): + logger = get_logger() + path = maybe_download_params(path) + if path.endswith(".pdparams"): + path = path.replace(".pdparams", "") + assert os.path.exists( + path + ".pdparams" + ), "The {}.pdparams does not exists!".format(path) + + params = paddle.load(path + ".pdparams") + + state_dict = model.state_dict() + + new_state_dict = {} + is_float16 = False + + for k1 in params.keys(): + if k1 not in state_dict.keys(): + logger.warning("The pretrained params {} not in model".format(k1)) + else: + if params[k1].dtype == paddle.float16: + is_float16 = True + if params[k1].dtype != state_dict[k1].dtype: + params[k1] = params[k1].astype(state_dict[k1].dtype) + if list(state_dict[k1].shape) == list(params[k1].shape): + new_state_dict[k1] = params[k1] + else: + logger.warning( + "The shape of model params {} {} not matched with loaded params {} {} !".format( + k1, state_dict[k1].shape, k1, params[k1].shape + ) + ) + + model.set_state_dict(new_state_dict) + if is_float16: + logger.info( + "The parameter type is float16, which is converted to float32 when loading" + ) + logger.info("load pretrain successful from {}".format(path)) + return is_float16 + + +def save_model( + model, + optimizer, + model_path, + logger, + config, + is_best=False, + prefix="ppocr", + **kwargs, +): + """ + save model to the target path + """ + _mkdir_if_not_exist(model_path, logger) + model_prefix = os.path.join(model_path, prefix) + + if prefix == "best_accuracy": + best_model_path = os.path.join(model_path, "best_model") + _mkdir_if_not_exist(best_model_path, logger) + + paddle.save(optimizer.state_dict(), model_prefix + ".pdopt") + if prefix == "best_accuracy": + paddle.save( + optimizer.state_dict(), os.path.join(best_model_path, "model.pdopt") + ) + + is_nlp_model = config["Architecture"]["model_type"] == "kie" and config[ + "Architecture" + ]["algorithm"] not in ["SDMGR"] + if is_nlp_model is not True: + paddle.save(model.state_dict(), model_prefix + ".pdparams") + metric_prefix = model_prefix + + if prefix == "best_accuracy": + paddle.save( + model.state_dict(), os.path.join(best_model_path, "model.pdparams") + ) + + else: # for kie system, we follow the save/load rules in NLP + if config["Global"]["distributed"]: + arch = model._layers + else: + arch = model + if config["Architecture"]["algorithm"] in ["Distillation"]: + arch = arch.Student + arch.backbone.model.save_pretrained(model_prefix) + metric_prefix = os.path.join(model_prefix, "metric") + + if prefix == "best_accuracy": + arch.backbone.model.save_pretrained(best_model_path) + + save_model_info = kwargs.pop("save_model_info", False) + if save_model_info: + with open(os.path.join(model_path, f"{prefix}.info.json"), "w") as f: + json.dump(kwargs, f) + logger.info("Already save model info in {}".format(model_path)) + if prefix != "latest": + done_flag = kwargs.pop("done_flag", False) + update_train_results(config, prefix, save_model_info, done_flag=done_flag) + + # save metric and config + with open(metric_prefix + ".states", "wb") as f: + pickle.dump(kwargs, f, protocol=2) + if is_best: + logger.info("save best model is to {}".format(model_prefix)) + else: + logger.info("save model in {}".format(model_prefix)) + + +def update_train_results(config, prefix, metric_info, done_flag=False, last_num=5): + if paddle.distributed.get_rank() != 0: + return + + assert last_num >= 1 + train_results_path = os.path.join( + config["Global"]["save_model_dir"], "train_result.json" + ) + save_model_tag = ["pdparams", "pdopt", "pdstates"] + save_inference_tag = ["inference_config", "pdmodel", "pdiparams", "pdiparams.info"] + if os.path.exists(train_results_path): + with open(train_results_path, "r") as fp: + train_results = json.load(fp) + else: + train_results = {} + train_results["model_name"] = config["Global"]["pdx_model_name"] + label_dict_path = config["Global"].get("character_dict_path", "") + if label_dict_path != "": + label_dict_path = os.path.abspath(label_dict_path) + if not os.path.exists(label_dict_path): + label_dict_path = "" + train_results["label_dict"] = label_dict_path + train_results["train_log"] = "train.log" + train_results["visualdl_log"] = "" + train_results["config"] = "config.yaml" + train_results["models"] = {} + for i in range(1, last_num + 1): + train_results["models"][f"last_{i}"] = {} + train_results["models"]["best"] = {} + train_results["done_flag"] = done_flag + if "best" in prefix: + if "acc" in metric_info["metric"]: + metric_score = metric_info["metric"]["acc"] + elif "precision" in metric_info["metric"]: + metric_score = metric_info["metric"]["precision"] + elif "exp_rate" in metric_info["metric"]: + metric_score = metric_info["metric"]["exp_rate"] + else: + raise ValueError("No metric score found.") + train_results["models"]["best"]["score"] = metric_score + for tag in save_model_tag: + if tag == "pdparams" and encrypted: + train_results["models"]["best"][tag] = os.path.join( + prefix, + ( + f"{prefix}.encrypted.{tag}" + if tag != "pdstates" + else f"{prefix}.states" + ), + ) + else: + train_results["models"]["best"][tag] = os.path.join( + prefix, + f"{prefix}.{tag}" if tag != "pdstates" else f"{prefix}.states", + ) + for tag in save_inference_tag: + train_results["models"]["best"][tag] = os.path.join( + prefix, + "inference", + f"inference.{tag}" if tag != "inference_config" else "inference.yml", + ) + else: + for i in range(last_num - 1, 0, -1): + train_results["models"][f"last_{i + 1}"] = train_results["models"][ + f"last_{i}" + ].copy() + if "acc" in metric_info["metric"]: + metric_score = metric_info["metric"]["acc"] + elif "precision" in metric_info["metric"]: + metric_score = metric_info["metric"]["precision"] + elif "exp_rate" in metric_info["metric"]: + metric_score = metric_info["metric"]["exp_rate"] + else: + metric_score = 0 + train_results["models"][f"last_{1}"]["score"] = metric_score + for tag in save_model_tag: + if tag == "pdparams" and encrypted: + train_results["models"][f"last_{1}"][tag] = os.path.join( + prefix, + ( + f"{prefix}.encrypted.{tag}" + if tag != "pdstates" + else f"{prefix}.states" + ), + ) + else: + train_results["models"][f"last_{1}"][tag] = os.path.join( + prefix, + f"{prefix}.{tag}" if tag != "pdstates" else f"{prefix}.states", + ) + for tag in save_inference_tag: + train_results["models"][f"last_{1}"][tag] = os.path.join( + prefix, + "inference", + f"inference.{tag}" if tag != "inference_config" else "inference.yml", + ) + + with open(train_results_path, "w") as fp: + json.dump(train_results, fp) diff --git a/modules/onnx_ocr_module/src/ppocr/utils/stats.py b/modules/onnx_ocr_module/src/ppocr/utils/stats.py new file mode 100644 index 0000000..6dd8c58 --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/utils/stats.py @@ -0,0 +1,70 @@ +# Copyright (c) 2020 PaddlePaddle Authors. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import collections +import numpy as np +import datetime + +__all__ = ["TrainingStats", "Time"] + + +class SmoothedValue(object): + """Track a series of values and provide access to smoothed values over a + window or the global series average. + """ + + def __init__(self, window_size): + self.deque = collections.deque(maxlen=window_size) + + def add_value(self, value): + self.deque.append(value) + + def get_median_value(self): + return np.median(self.deque) + + +def Time(): + return datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S.%f") + + +class TrainingStats(object): + def __init__(self, window_size, stats_keys): + self.window_size = window_size + self.smoothed_losses_and_metrics = { + key: SmoothedValue(window_size) for key in stats_keys + } + + def update(self, stats): + for k, v in stats.items(): + if k not in self.smoothed_losses_and_metrics: + self.smoothed_losses_and_metrics[k] = SmoothedValue(self.window_size) + self.smoothed_losses_and_metrics[k].add_value(v) + + def get(self, extras=None): + stats = collections.OrderedDict() + if extras: + for k, v in extras.items(): + stats[k] = v + for k, v in self.smoothed_losses_and_metrics.items(): + stats[k] = round(v.get_median_value(), 6) + + return stats + + def log(self, extras=None): + d = self.get(extras) + strs = [] + for k, v in d.items(): + strs.append("{}: {:x<6f}".format(k, v)) + strs = ", ".join(strs) + return strs diff --git a/modules/onnx_ocr_module/src/ppocr/utils/utility.py b/modules/onnx_ocr_module/src/ppocr/utils/utility.py new file mode 100644 index 0000000..386f010 --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/utils/utility.py @@ -0,0 +1,220 @@ +# Copyright (c) 2020 PaddlePaddle Authors. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import logging +import os +import cv2 +import random +import numpy as np +# ONNX-only runtime: remove paddle dependency +import importlib.util +import sys +import subprocess + + +def print_dict(d, logger, delimiter=0): + """ + Recursively visualize a dict and + indenting acrrording by the relationship of keys. + """ + for k, v in sorted(d.items()): + if isinstance(v, dict): + logger.info("{}{} : ".format(delimiter * " ", str(k))) + print_dict(v, logger, delimiter + 4) + elif isinstance(v, list) and len(v) >= 1 and isinstance(v[0], dict): + logger.info("{}{} : ".format(delimiter * " ", str(k))) + for value in v: + print_dict(value, logger, delimiter + 4) + else: + logger.info("{}{} : {}".format(delimiter * " ", k, v)) + + +def get_check_global_params(mode): + check_params = [ + "use_gpu", + "max_text_length", + "image_shape", + "image_shape", + "character_type", + "loss_type", + ] + if mode == "train_eval": + check_params = check_params + [ + "train_batch_size_per_card", + "test_batch_size_per_card", + ] + elif mode == "test": + check_params = check_params + ["test_batch_size_per_card"] + return check_params + + +def _check_image_file(path): + img_end = {"jpg", "bmp", "png", "jpeg", "rgb", "tif", "tiff", "gif", "pdf"} + return any([path.lower().endswith(e) for e in img_end]) + + +def get_image_file_list(img_file, infer_list=None): + imgs_lists = [] + if infer_list and not os.path.exists(infer_list): + raise Exception("not found infer list {}".format(infer_list)) + if infer_list: + with open(infer_list, "r") as f: + lines = f.readlines() + for line in lines: + image_path = line.strip().split("\t")[0] + image_path = os.path.join(img_file, image_path) + imgs_lists.append(image_path) + else: + if img_file is None or not os.path.exists(img_file): + raise Exception("not found any img file in {}".format(img_file)) + + img_end = {"jpg", "bmp", "png", "jpeg", "rgb", "tif", "tiff", "gif", "pdf"} + if os.path.isfile(img_file) and _check_image_file(img_file): + imgs_lists.append(img_file) + elif os.path.isdir(img_file): + for single_file in os.listdir(img_file): + file_path = os.path.join(img_file, single_file) + if os.path.isfile(file_path) and _check_image_file(file_path): + imgs_lists.append(file_path) + + if len(imgs_lists) == 0: + raise Exception("not found any img file in {}".format(img_file)) + imgs_lists = sorted(imgs_lists) + return imgs_lists + + +def binarize_img(img): + if len(img.shape) == 3 and img.shape[2] == 3: + gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # conversion to grayscale image + # use cv2 threshold binarization + _, gray = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU) + img = cv2.cvtColor(gray, cv2.COLOR_GRAY2BGR) + return img + + +def alpha_to_color(img, alpha_color=(255, 255, 255)): + if len(img.shape) == 3 and img.shape[2] == 4: + B, G, R, A = cv2.split(img) + alpha = A / 255 + + R = (alpha_color[0] * (1 - alpha) + R * alpha).astype(np.uint8) + G = (alpha_color[1] * (1 - alpha) + G * alpha).astype(np.uint8) + B = (alpha_color[2] * (1 - alpha) + B * alpha).astype(np.uint8) + + img = cv2.merge((B, G, R)) + return img + + +def check_and_read(img_path): + if os.path.basename(img_path)[-3:].lower() == "gif": + gif = cv2.VideoCapture(img_path) + ret, frame = gif.read() + if not ret: + logger = logging.getLogger("ppocr") + logger.info("Cannot read {}. This gif image maybe corrupted.") + return None, False + if len(frame.shape) == 2 or frame.shape[-1] == 1: + frame = cv2.cvtColor(frame, cv2.COLOR_GRAY2RGB) + imgvalue = frame[:, :, ::-1] + return imgvalue, True, False + elif os.path.basename(img_path)[-3:].lower() == "pdf": + try: + import importlib + fitz = importlib.import_module("fitz") + from PIL import Image + except Exception: + logger = logging.getLogger("ppocr") + logger.info("PyMuPDF(fitz) 미설치로 PDF 처리를 건너뜁니다.") + return None, False, False + + imgs = [] + with fitz.open(img_path) as pdf: + for pg in range(0, pdf.page_count): + page = pdf[pg] + mat = fitz.Matrix(2, 2) + pm = page.get_pixmap(matrix=mat, alpha=False) + + # if width or height > 2000 pixels, don't enlarge the image + if pm.width > 2000 or pm.height > 2000: + pm = page.get_pixmap(matrix=fitz.Matrix(1, 1), alpha=False) + + img = Image.frombytes("RGB", [pm.width, pm.height], pm.samples) + img = cv2.cvtColor(np.array(img), cv2.COLOR_RGB2BGR) + imgs.append(img) + return imgs, False, True + return None, False, False + + +def load_vqa_bio_label_maps(label_map_path): + with open(label_map_path, "r", encoding="utf-8") as fin: + lines = fin.readlines() + old_lines = [line.strip() for line in lines] + lines = ["O"] + for line in old_lines: + # "O" has already been in lines + if line.upper() in ["OTHER", "OTHERS", "IGNORE"]: + continue + lines.append(line) + labels = ["O"] + for line in lines[1:]: + labels.append("B-" + line) + labels.append("I-" + line) + label2id_map = {label.upper(): idx for idx, label in enumerate(labels)} + id2label_map = {idx: label.upper() for idx, label in enumerate(labels)} + return label2id_map, id2label_map + + +def set_seed(seed=1024): + random.seed(seed) + np.random.seed(seed) + # paddle-free + + +def check_install(module_name, install_name): + spec = importlib.util.find_spec(module_name) + if spec is None: + print(f"Warning! The {module_name} module is NOT installed") + print( + f"Try install {module_name} module automatically. You can also try to install manually by pip install {install_name}." + ) + python = sys.executable + try: + subprocess.check_call( + [python, "-m", "pip", "install", install_name], + stdout=subprocess.DEVNULL, + ) + print(f"The {module_name} module is now installed") + except subprocess.CalledProcessError as exc: + raise Exception(f"Install {module_name} failed, please install manually") + else: + print(f"{module_name} has been installed.") + + +class AverageMeter: + def __init__(self): + self.reset() + + def reset(self): + """reset""" + self.val = 0 + self.avg = 0 + self.sum = 0 + self.count = 0 + + def update(self, val, n=1): + """update""" + self.val = val + self.sum += val * n + self.count += n + self.avg = self.sum / self.count diff --git a/modules/onnx_ocr_module/src/ppocr/utils/visual.py b/modules/onnx_ocr_module/src/ppocr/utils/visual.py new file mode 100644 index 0000000..1a49c31 --- /dev/null +++ b/modules/onnx_ocr_module/src/ppocr/utils/visual.py @@ -0,0 +1,142 @@ +# copyright (c) 2021 PaddlePaddle Authors. All Rights Reserve. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +import cv2 +import os +import numpy as np +import PIL +from PIL import Image, ImageDraw, ImageFont + + +def draw_ser_results( + image, ocr_results, font_path="doc/fonts/simfang.ttf", font_size=14 +): + np.random.seed(2021) + color = ( + np.random.permutation(range(255)), + np.random.permutation(range(255)), + np.random.permutation(range(255)), + ) + color_map = { + idx: (color[0][idx], color[1][idx], color[2][idx]) for idx in range(1, 255) + } + if isinstance(image, np.ndarray): + image = Image.fromarray(image) + elif isinstance(image, str) and os.path.isfile(image): + image = Image.open(image).convert("RGB") + img_new = image.copy() + draw = ImageDraw.Draw(img_new) + + font = ImageFont.truetype(font_path, font_size, encoding="utf-8") + for ocr_info in ocr_results: + if ocr_info["pred_id"] not in color_map: + continue + color = color_map[ocr_info["pred_id"]] + text = "{}: {}".format(ocr_info["pred"], ocr_info["transcription"]) + + if "bbox" in ocr_info: + # draw with ocr engine + bbox = ocr_info["bbox"] + else: + # draw with ocr groundtruth + bbox = trans_poly_to_bbox(ocr_info["points"]) + draw_box_txt(bbox, text, draw, font, font_size, color) + + img_new = Image.blend(image, img_new, 0.7) + return np.array(img_new) + + +def draw_box_txt(bbox, text, draw, font, font_size, color): + # draw ocr results outline + bbox = ((bbox[0], bbox[1]), (bbox[2], bbox[3])) + draw.rectangle(bbox, fill=color) + + # draw ocr results + if int(PIL.__version__.split(".")[0]) < 10: + tw = font.getsize(text)[0] + th = font.getsize(text)[1] + else: + left, top, right, bottom = font.getbbox(text) + tw, th = right - left, bottom - top + + start_y = max(0, bbox[0][1] - th) + draw.rectangle( + [(bbox[0][0] + 1, start_y), (bbox[0][0] + tw + 1, start_y + th)], + fill=(0, 0, 255), + ) + draw.text((bbox[0][0] + 1, start_y), text, fill=(255, 255, 255), font=font) + + +def trans_poly_to_bbox(poly): + x1 = np.min([p[0] for p in poly]) + x2 = np.max([p[0] for p in poly]) + y1 = np.min([p[1] for p in poly]) + y2 = np.max([p[1] for p in poly]) + return [x1, y1, x2, y2] + + +def draw_re_results(image, result, font_path="doc/fonts/simfang.ttf", font_size=18): + np.random.seed(0) + if isinstance(image, np.ndarray): + image = Image.fromarray(image) + elif isinstance(image, str) and os.path.isfile(image): + image = Image.open(image).convert("RGB") + img_new = image.copy() + draw = ImageDraw.Draw(img_new) + + font = ImageFont.truetype(font_path, font_size, encoding="utf-8") + color_head = (0, 0, 255) + color_tail = (255, 0, 0) + color_line = (0, 255, 0) + + for ocr_info_head, ocr_info_tail in result: + draw_box_txt( + ocr_info_head["bbox"], + ocr_info_head["transcription"], + draw, + font, + font_size, + color_head, + ) + draw_box_txt( + ocr_info_tail["bbox"], + ocr_info_tail["transcription"], + draw, + font, + font_size, + color_tail, + ) + + center_head = ( + (ocr_info_head["bbox"][0] + ocr_info_head["bbox"][2]) // 2, + (ocr_info_head["bbox"][1] + ocr_info_head["bbox"][3]) // 2, + ) + center_tail = ( + (ocr_info_tail["bbox"][0] + ocr_info_tail["bbox"][2]) // 2, + (ocr_info_tail["bbox"][1] + ocr_info_tail["bbox"][3]) // 2, + ) + + draw.line([center_head, center_tail], fill=color_line, width=5) + + img_new = Image.blend(image, img_new, 0.5) + return np.array(img_new) + + +def draw_rectangle(img_path, boxes): + boxes = np.array(boxes) + img = cv2.imread(img_path) + img_show = img.copy() + for box in boxes.astype(int): + x1, y1, x2, y2 = box + cv2.rectangle(img_show, (x1, y1), (x2, y2), (255, 0, 0), 2) + return img_show diff --git a/modules/onnx_ocr_module/src/tools/__init__.py b/modules/onnx_ocr_module/src/tools/__init__.py new file mode 100644 index 0000000..d56c9db --- /dev/null +++ b/modules/onnx_ocr_module/src/tools/__init__.py @@ -0,0 +1,14 @@ +# Copyright (c) 2021 PaddlePaddle Authors. All Rights Reserved. +# Copyright 2018 The Google AI Language Team Authors and The HuggingFace Inc. team. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. diff --git a/modules/onnx_ocr_module/src/tools/__pycache__/__init__.cpython-311.pyc b/modules/onnx_ocr_module/src/tools/__pycache__/__init__.cpython-311.pyc new file mode 100644 index 0000000..9559f5c Binary files /dev/null and b/modules/onnx_ocr_module/src/tools/__pycache__/__init__.cpython-311.pyc differ diff --git a/modules/onnx_ocr_module/src/tools/end2end/convert_ppocr_label.py b/modules/onnx_ocr_module/src/tools/end2end/convert_ppocr_label.py new file mode 100644 index 0000000..08cea77 --- /dev/null +++ b/modules/onnx_ocr_module/src/tools/end2end/convert_ppocr_label.py @@ -0,0 +1,103 @@ +# Copyright (c) 2022 PaddlePaddle Authors. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +import numpy as np +import json +import os + + +def poly_to_string(poly): + if len(poly.shape) > 1: + poly = np.array(poly).flatten() + + string = "\t".join(str(i) for i in poly) + return string + + +def convert_label(label_dir, mode="gt", save_dir="./save_results/"): + if not os.path.exists(label_dir): + raise ValueError(f"The file {label_dir} does not exist!") + + assert label_dir != save_dir, "hahahhaha" + + label_file = open(label_dir, "r") + data = label_file.readlines() + + gt_dict = {} + + for line in data: + try: + tmp = line.split("\t") + assert len(tmp) == 2, "" + except: + tmp = line.strip().split(" ") + + gt_lists = [] + + if tmp[0].split("/")[0] is not None: + img_path = tmp[0] + anno = json.loads(tmp[1]) + gt_collect = [] + for dic in anno: + # txt = dic['transcription'].replace(' ', '') # ignore blank + txt = dic["transcription"] + if "score" in dic and float(dic["score"]) < 0.5: + continue + if "\u3000" in txt: + txt = txt.replace("\u3000", " ") + # while ' ' in txt: + # txt = txt.replace(' ', '') + poly = np.array(dic["points"]).flatten() + if txt == "###": + txt_tag = 1 ## ignore 1 + else: + txt_tag = 0 + if mode == "gt": + gt_label = ( + poly_to_string(poly) + "\t" + str(txt_tag) + "\t" + txt + "\n" + ) + else: + gt_label = poly_to_string(poly) + "\t" + txt + "\n" + + gt_lists.append(gt_label) + + gt_dict[img_path] = gt_lists + else: + continue + + if not os.path.exists(save_dir): + os.makedirs(save_dir) + + for img_name in gt_dict.keys(): + save_name = img_name.split("/")[-1] + save_file = os.path.join(save_dir, save_name + ".txt") + with open(save_file, "w") as f: + f.writelines(gt_dict[img_name]) + + print("The convert label saved in {}".format(save_dir)) + + +def parse_args(): + import argparse + + parser = argparse.ArgumentParser(description="args") + parser.add_argument("--label_path", type=str, required=True) + parser.add_argument("--save_folder", type=str, required=True) + parser.add_argument("--mode", type=str, default=False) + args = parser.parse_args() + return args + + +if __name__ == "__main__": + args = parse_args() + convert_label(args.label_path, args.mode, args.save_folder) diff --git a/modules/onnx_ocr_module/src/tools/end2end/draw_html.py b/modules/onnx_ocr_module/src/tools/end2end/draw_html.py new file mode 100644 index 0000000..c894f44 --- /dev/null +++ b/modules/onnx_ocr_module/src/tools/end2end/draw_html.py @@ -0,0 +1,72 @@ +# Copyright (c) 2022 PaddlePaddle Authors. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import os +import argparse + + +def str2bool(v): + return v.lower() in ("true", "t", "1") + + +def init_args(): + parser = argparse.ArgumentParser() + parser.add_argument("--image_dir", type=str, default="") + parser.add_argument("--save_html_path", type=str, default="./default.html") + parser.add_argument("--width", type=int, default=640) + return parser + + +def parse_args(): + parser = init_args() + return parser.parse_args() + + +def draw_debug_img(args): + html_path = args.save_html_path + + err_cnt = 0 + with open(html_path, "w") as html: + html.write("\n\n") + html.write('\n') + html.write( + '' + ) + image_list = [] + path = args.image_dir + for i, filename in enumerate(sorted(os.listdir(path))): + if filename.endswith("txt"): + continue + # The image path + base = "{}/{}".format(path, filename) + html.write("\n") + html.write(f"') + + html.write("\n") + html.write("\n") + html.write("
{filename}\n GT") + html.write(f'GT\n
\n") + html.write("\n\n") + print(f"The html file saved in {html_path}") + return + + +if __name__ == "__main__": + args = parse_args() + + draw_debug_img(args) diff --git a/modules/onnx_ocr_module/src/tools/end2end/eval_end2end.py b/modules/onnx_ocr_module/src/tools/end2end/eval_end2end.py new file mode 100644 index 0000000..3795c3c --- /dev/null +++ b/modules/onnx_ocr_module/src/tools/end2end/eval_end2end.py @@ -0,0 +1,191 @@ +# Copyright (c) 2022 PaddlePaddle Authors. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import os +import re +import sys +import shapely +from shapely.geometry import Polygon +import numpy as np +from collections import defaultdict +import operator +import editdistance + + +def strQ2B(ustring): + rstring = "" + for uchar in ustring: + inside_code = ord(uchar) + if inside_code == 12288: + inside_code = 32 + elif inside_code >= 65281 and inside_code <= 65374: + inside_code -= 65248 + rstring += chr(inside_code) + return rstring + + +def polygon_from_str(polygon_points): + """ + Create a shapely polygon object from gt or dt line. + """ + polygon_points = np.array(polygon_points).reshape(4, 2) + polygon = Polygon(polygon_points).convex_hull + return polygon + + +def polygon_iou(poly1, poly2): + """ + Intersection over union between two shapely polygons. + """ + if not poly1.intersects(poly2): # this test is fast and can accelerate calculation + iou = 0 + else: + try: + inter_area = poly1.intersection(poly2).area + union_area = poly1.area + poly2.area - inter_area + iou = float(inter_area) / union_area + except shapely.geos.TopologicalError: + # except Exception as e: + # print(e) + print("shapely.geos.TopologicalError occurred, iou set to 0") + iou = 0 + return iou + + +def ed(str1, str2): + return editdistance.eval(str1, str2) + + +def e2e_eval(gt_dir, res_dir, ignore_blank=False): + print("start testing...") + iou_thresh = 0.5 + val_names = os.listdir(gt_dir) + num_gt_chars = 0 + gt_count = 0 + dt_count = 0 + hit = 0 + ed_sum = 0 + + for i, val_name in enumerate(val_names): + with open(os.path.join(gt_dir, val_name), encoding="utf-8") as f: + gt_lines = [o.strip() for o in f.readlines()] + gts = [] + ignore_masks = [] + for line in gt_lines: + parts = line.strip().split("\t") + # ignore illegal data + if len(parts) < 9: + continue + assert len(parts) < 11 + if len(parts) == 9: + gts.append(parts[:8] + [""]) + else: + gts.append(parts[:8] + [parts[-1]]) + + ignore_masks.append(parts[8]) + + val_path = os.path.join(res_dir, val_name) + if not os.path.exists(val_path): + dt_lines = [] + else: + with open(val_path, encoding="utf-8") as f: + dt_lines = [o.strip() for o in f.readlines()] + dts = [] + for line in dt_lines: + # print(line) + parts = line.strip().split("\t") + assert len(parts) < 10, "line error: {}".format(line) + if len(parts) == 8: + dts.append(parts + [""]) + else: + dts.append(parts) + + dt_match = [False] * len(dts) + gt_match = [False] * len(gts) + all_ious = defaultdict(tuple) + for index_gt, gt in enumerate(gts): + gt_coors = [float(gt_coor) for gt_coor in gt[0:8]] + gt_poly = polygon_from_str(gt_coors) + for index_dt, dt in enumerate(dts): + dt_coors = [float(dt_coor) for dt_coor in dt[0:8]] + dt_poly = polygon_from_str(dt_coors) + iou = polygon_iou(dt_poly, gt_poly) + if iou >= iou_thresh: + all_ious[(index_gt, index_dt)] = iou + sorted_ious = sorted(all_ious.items(), key=operator.itemgetter(1), reverse=True) + sorted_gt_dt_pairs = [item[0] for item in sorted_ious] + + # matched gt and dt + for gt_dt_pair in sorted_gt_dt_pairs: + index_gt, index_dt = gt_dt_pair + if gt_match[index_gt] == False and dt_match[index_dt] == False: + gt_match[index_gt] = True + dt_match[index_dt] = True + if ignore_blank: + gt_str = strQ2B(gts[index_gt][8]).replace(" ", "") + dt_str = strQ2B(dts[index_dt][8]).replace(" ", "") + else: + gt_str = strQ2B(gts[index_gt][8]) + dt_str = strQ2B(dts[index_dt][8]) + if ignore_masks[index_gt] == "0": + ed_sum += ed(gt_str, dt_str) + num_gt_chars += len(gt_str) + if gt_str == dt_str: + hit += 1 + gt_count += 1 + dt_count += 1 + + # unmatched dt + for tindex, dt_match_flag in enumerate(dt_match): + if dt_match_flag == False: + dt_str = dts[tindex][8] + gt_str = "" + ed_sum += ed(dt_str, gt_str) + dt_count += 1 + + # unmatched gt + for tindex, gt_match_flag in enumerate(gt_match): + if gt_match_flag == False and ignore_masks[tindex] == "0": + dt_str = "" + gt_str = gts[tindex][8] + ed_sum += ed(gt_str, dt_str) + num_gt_chars += len(gt_str) + gt_count += 1 + + eps = 1e-9 + print("hit, dt_count, gt_count", hit, dt_count, gt_count) + precision = hit / (dt_count + eps) + recall = hit / (gt_count + eps) + fmeasure = 2.0 * precision * recall / (precision + recall + eps) + avg_edit_dist_img = ed_sum / len(val_names) + avg_edit_dist_field = ed_sum / (gt_count + eps) + character_acc = 1 - ed_sum / (num_gt_chars + eps) + + print("character_acc: %.2f" % (character_acc * 100) + "%") + print("avg_edit_dist_field: %.2f" % (avg_edit_dist_field)) + print("avg_edit_dist_img: %.2f" % (avg_edit_dist_img)) + print("precision: %.2f" % (precision * 100) + "%") + print("recall: %.2f" % (recall * 100) + "%") + print("fmeasure: %.2f" % (fmeasure * 100) + "%") + + +if __name__ == "__main__": + # if len(sys.argv) != 3: + # print("python3 ocr_e2e_eval.py gt_dir res_dir") + # exit(-1) + # gt_folder = sys.argv[1] + # pred_folder = sys.argv[2] + gt_folder = sys.argv[1] + pred_folder = sys.argv[2] + e2e_eval(gt_folder, pred_folder) diff --git a/modules/onnx_ocr_module/src/tools/end2end/readme.md b/modules/onnx_ocr_module/src/tools/end2end/readme.md new file mode 100644 index 0000000..636ee76 --- /dev/null +++ b/modules/onnx_ocr_module/src/tools/end2end/readme.md @@ -0,0 +1,63 @@ + +# 简介 + +`tools/end2end`目录下存放了文本检测+文本识别pipeline串联预测的指标评测代码以及可视化工具。本节介绍文本检测+文本识别的端对端指标评估方式。 + + +## 端对端评测步骤 + +**步骤一:** + +运行`tools/infer/predict_system.py`,得到保存的结果: + +``` +python3 tools/infer/predict_system.py --det_model_dir=./ch_PP-OCRv2_det_infer/ --rec_model_dir=./ch_PP-OCRv2_rec_infer/ --image_dir=./datasets/img_dir/ --draw_img_save_dir=./ch_PP-OCRv2_results/ --is_visualize=True +``` + +文本检测识别可视化图默认保存在`./ch_PP-OCRv2_results/`目录下,预测结果默认保存在`./ch_PP-OCRv2_results/system_results.txt`中,格式如下: +``` +all-sum-510/00224225.jpg [{"transcription": "超赞", "points": [[8.0, 48.0], [157.0, 44.0], [159.0, 115.0], [10.0, 119.0]], "score": "0.99396634"}, {"transcription": "中", "points": [[202.0, 152.0], [230.0, 152.0], [230.0, 163.0], [202.0, 163.0]], "score": "0.09310734"}, {"transcription": "58.0m", "points": [[196.0, 192.0], [444.0, 192.0], [444.0, 240.0], [196.0, 240.0]], "score": "0.44041982"}, {"transcription": "汽配", "points": [[55.0, 263.0], [95.0, 263.0], [95.0, 281.0], [55.0, 281.0]], "score": "0.9986651"}, {"transcription": "成总店", "points": [[120.0, 262.0], [176.0, 262.0], [176.0, 283.0], [120.0, 283.0]], "score": "0.9929402"}, {"transcription": "K", "points": [[237.0, 286.0], [311.0, 286.0], [311.0, 345.0], [237.0, 345.0]], "score": "0.6074794"}, {"transcription": "88:-8", "points": [[203.0, 405.0], [477.0, 414.0], [475.0, 459.0], [201.0, 450.0]], "score": "0.7106863"}] +``` + + +**步骤二:** + +将步骤一保存的数据转换为端对端评测需要的数据格式: + +修改 `tools/end2end/convert_ppocr_label.py`中的代码,convert_label函数中设置输入标签路径,Mode,保存标签路径等,对预测数据的GTlabel和预测结果的label格式进行转换。 + +``` +python3 tools/end2end/convert_ppocr_label.py --mode=gt --label_path=path/to/label_txt --save_folder=save_gt_label + +python3 tools/end2end/convert_ppocr_label.py --mode=pred --label_path=path/to/pred_txt --save_folder=save_PPOCRV2_infer +``` + +得到如下结果: +``` +├── ./save_gt_label/ +├── ./save_PPOCRV2_infer/ +``` + +**步骤三:** + +执行端对端评测,运行`tools/eval_end2end.py`计算端对端指标,运行方式如下: + +``` +python3 tools/eval_end2end.py "gt_label_dir" "predict_label_dir" +``` + +比如: + +``` +python3 tools/eval_end2end.py ./save_gt_label/ ./save_PPOCRV2_infer/ +``` +将得到如下结果,fmeasure为主要关注的指标: +``` +hit, dt_count, gt_count 1557 2693 3283 +character_acc: 61.77% +avg_edit_dist_field: 3.08 +avg_edit_dist_img: 51.82 +precision: 57.82% +recall: 47.43% +fmeasure: 52.11% +``` diff --git a/modules/onnx_ocr_module/src/tools/eval.py b/modules/onnx_ocr_module/src/tools/eval.py new file mode 100644 index 0000000..f5328ff --- /dev/null +++ b/modules/onnx_ocr_module/src/tools/eval.py @@ -0,0 +1,178 @@ +# Copyright (c) 2020 PaddlePaddle Authors. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import os +import sys + +__dir__ = os.path.dirname(os.path.abspath(__file__)) +sys.path.insert(0, __dir__) +sys.path.insert(0, os.path.abspath(os.path.join(__dir__, ".."))) + +import paddle +from ppocr.data import build_dataloader, set_signal_handlers +from ppocr.modeling.architectures import build_model +from ppocr.postprocess import build_post_process +from ppocr.metrics import build_metric +from ppocr.utils.save_load import load_model +import tools.program as program + + +def main(): + global_config = config["Global"] + # build dataloader + set_signal_handlers() + valid_dataloader = build_dataloader(config, "Eval", device, logger) + + # build post process + post_process_class = build_post_process(config["PostProcess"], global_config) + + # build model + # for rec algorithm + if hasattr(post_process_class, "character"): + char_num = len(getattr(post_process_class, "character")) + if config["Architecture"]["algorithm"] in [ + "Distillation", + ]: # distillation model + for key in config["Architecture"]["Models"]: + if ( + config["Architecture"]["Models"][key]["Head"]["name"] == "MultiHead" + ): # for multi head + out_channels_list = {} + if config["PostProcess"]["name"] == "DistillationSARLabelDecode": + char_num = char_num - 2 + if config["PostProcess"]["name"] == "DistillationNRTRLabelDecode": + char_num = char_num - 3 + out_channels_list["CTCLabelDecode"] = char_num + out_channels_list["SARLabelDecode"] = char_num + 2 + out_channels_list["NRTRLabelDecode"] = char_num + 3 + config["Architecture"]["Models"][key]["Head"][ + "out_channels_list" + ] = out_channels_list + else: + config["Architecture"]["Models"][key]["Head"][ + "out_channels" + ] = char_num + elif config["Architecture"]["Head"]["name"] == "MultiHead": # for multi head + out_channels_list = {} + if config["PostProcess"]["name"] == "SARLabelDecode": + char_num = char_num - 2 + if config["PostProcess"]["name"] == "NRTRLabelDecode": + char_num = char_num - 3 + out_channels_list["CTCLabelDecode"] = char_num + out_channels_list["SARLabelDecode"] = char_num + 2 + out_channels_list["NRTRLabelDecode"] = char_num + 3 + config["Architecture"]["Head"]["out_channels_list"] = out_channels_list + else: # base rec model + config["Architecture"]["Head"]["out_channels"] = char_num + + model = build_model(config["Architecture"]) + extra_input_models = [ + "SRN", + "NRTR", + "SAR", + "SEED", + "SVTR", + "SVTR_LCNet", + "VisionLAN", + "RobustScanner", + "SVTR_HGNet", + ] + extra_input = False + if config["Architecture"]["algorithm"] == "Distillation": + for key in config["Architecture"]["Models"]: + extra_input = ( + extra_input + or config["Architecture"]["Models"][key]["algorithm"] + in extra_input_models + ) + else: + extra_input = config["Architecture"]["algorithm"] in extra_input_models + if "model_type" in config["Architecture"].keys(): + if config["Architecture"]["algorithm"] == "CAN": + model_type = "can" + elif config["Architecture"]["algorithm"] == "LaTeXOCR": + model_type = "latexocr" + config["Metric"]["cal_bleu_score"] = True + elif config["Architecture"]["algorithm"] == "UniMERNet": + model_type = "unimernet" + config["Metric"]["cal_bleu_score"] = True + elif config["Architecture"]["algorithm"] in [ + "PP-FormulaNet-S", + "PP-FormulaNet-L", + ]: + model_type = "pp_formulanet" + config["Metric"]["cal_bleu_score"] = True + else: + model_type = config["Architecture"]["model_type"] + else: + model_type = None + + # build metric + eval_class = build_metric(config["Metric"]) + # amp + use_amp = config["Global"].get("use_amp", False) + amp_level = config["Global"].get("amp_level", "O2") + amp_custom_black_list = config["Global"].get("amp_custom_black_list", []) + if use_amp: + AMP_RELATED_FLAGS_SETTING = { + "FLAGS_cudnn_batchnorm_spatial_persistent": 1, + } + paddle.set_flags(AMP_RELATED_FLAGS_SETTING) + scale_loss = config["Global"].get("scale_loss", 1.0) + use_dynamic_loss_scaling = config["Global"].get( + "use_dynamic_loss_scaling", False + ) + scaler = paddle.amp.GradScaler( + init_loss_scaling=scale_loss, + use_dynamic_loss_scaling=use_dynamic_loss_scaling, + ) + if amp_level == "O2": + model = paddle.amp.decorate( + models=model, level=amp_level, master_weight=True + ) + else: + scaler = None + + best_model_dict = load_model( + config, model, model_type=config["Architecture"]["model_type"] + ) + if len(best_model_dict): + logger.info("metric in ckpt ***************") + for k, v in best_model_dict.items(): + logger.info("{}:{}".format(k, v)) + + # start eval + metric = program.eval( + model, + valid_dataloader, + post_process_class, + eval_class, + model_type, + extra_input, + scaler, + amp_level, + amp_custom_black_list, + ) + logger.info("metric eval ***************") + for k, v in metric.items(): + logger.info("{}:{}".format(k, v)) + + +if __name__ == "__main__": + config, device, logger, vdl_writer = program.preprocess() + main() diff --git a/modules/onnx_ocr_module/src/tools/export_center.py b/modules/onnx_ocr_module/src/tools/export_center.py new file mode 100644 index 0000000..e79c2b8 --- /dev/null +++ b/modules/onnx_ocr_module/src/tools/export_center.py @@ -0,0 +1,77 @@ +# Copyright (c) 2020 PaddlePaddle Authors. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import os +import sys +import pickle + +__dir__ = os.path.dirname(os.path.abspath(__file__)) +sys.path.append(__dir__) +sys.path.append(os.path.abspath(os.path.join(__dir__, ".."))) + +from ppocr.data import build_dataloader, set_signal_handlers +from ppocr.modeling.architectures import build_model +from ppocr.postprocess import build_post_process +from ppocr.utils.save_load import load_model +from ppocr.utils.utility import print_dict +import tools.program as program + + +def main(): + global_config = config["Global"] + # build dataloader + config["Eval"]["dataset"]["name"] = config["Train"]["dataset"]["name"] + config["Eval"]["dataset"]["data_dir"] = config["Train"]["dataset"]["data_dir"] + config["Eval"]["dataset"]["label_file_list"] = config["Train"]["dataset"][ + "label_file_list" + ] + set_signal_handlers() + eval_dataloader = build_dataloader(config, "Eval", device, logger) + + # build post process + post_process_class = build_post_process(config["PostProcess"], global_config) + + # build model + # for rec algorithm + if hasattr(post_process_class, "character"): + char_num = len(getattr(post_process_class, "character")) + config["Architecture"]["Head"]["out_channels"] = char_num + + # set return_features = True + config["Architecture"]["Head"]["return_feats"] = True + + model = build_model(config["Architecture"]) + + best_model_dict = load_model(config, model) + if len(best_model_dict): + logger.info("metric in ckpt ***************") + for k, v in best_model_dict.items(): + logger.info("{}:{}".format(k, v)) + + # get features from train data + char_center = program.get_center(model, eval_dataloader, post_process_class) + + # serialize to disk + with open("train_center.pkl", "wb") as f: + pickle.dump(char_center, f) + return + + +if __name__ == "__main__": + config, device, logger, vdl_writer = program.preprocess() + main() diff --git a/modules/onnx_ocr_module/src/tools/export_model.py b/modules/onnx_ocr_module/src/tools/export_model.py new file mode 100644 index 0000000..da1bcbb --- /dev/null +++ b/modules/onnx_ocr_module/src/tools/export_model.py @@ -0,0 +1,37 @@ +# Copyright (c) 2020 PaddlePaddle Authors. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import os +import sys + +__dir__ = os.path.dirname(os.path.abspath(__file__)) +sys.path.append(__dir__) +sys.path.insert(0, os.path.abspath(os.path.join(__dir__, ".."))) + +import argparse + +from tools.program import load_config, merge_config, ArgsParser +from ppocr.utils.export_model import export + + +def main(): + FLAGS = ArgsParser().parse_args() + config = load_config(FLAGS.config) + config = merge_config(config, FLAGS.opt) + # export model + export(config) + + +if __name__ == "__main__": + main() diff --git a/modules/onnx_ocr_module/src/tools/infer/__init__.py b/modules/onnx_ocr_module/src/tools/infer/__init__.py new file mode 100644 index 0000000..3df8252 --- /dev/null +++ b/modules/onnx_ocr_module/src/tools/infer/__init__.py @@ -0,0 +1 @@ +# tools infer 패키지 diff --git a/modules/onnx_ocr_module/src/tools/infer/__pycache__/__init__.cpython-311.pyc b/modules/onnx_ocr_module/src/tools/infer/__pycache__/__init__.cpython-311.pyc new file mode 100644 index 0000000..b8aba62 Binary files /dev/null and b/modules/onnx_ocr_module/src/tools/infer/__pycache__/__init__.cpython-311.pyc differ diff --git a/modules/onnx_ocr_module/src/tools/infer/__pycache__/predict_cls.cpython-311.pyc b/modules/onnx_ocr_module/src/tools/infer/__pycache__/predict_cls.cpython-311.pyc new file mode 100644 index 0000000..f119ecd Binary files /dev/null and b/modules/onnx_ocr_module/src/tools/infer/__pycache__/predict_cls.cpython-311.pyc differ diff --git a/modules/onnx_ocr_module/src/tools/infer/__pycache__/predict_det.cpython-311.pyc b/modules/onnx_ocr_module/src/tools/infer/__pycache__/predict_det.cpython-311.pyc new file mode 100644 index 0000000..f4edbfa Binary files /dev/null and b/modules/onnx_ocr_module/src/tools/infer/__pycache__/predict_det.cpython-311.pyc differ diff --git a/modules/onnx_ocr_module/src/tools/infer/__pycache__/predict_rec.cpython-311.pyc b/modules/onnx_ocr_module/src/tools/infer/__pycache__/predict_rec.cpython-311.pyc new file mode 100644 index 0000000..bac8462 Binary files /dev/null and b/modules/onnx_ocr_module/src/tools/infer/__pycache__/predict_rec.cpython-311.pyc differ diff --git a/modules/onnx_ocr_module/src/tools/infer/__pycache__/predict_system.cpython-311.pyc b/modules/onnx_ocr_module/src/tools/infer/__pycache__/predict_system.cpython-311.pyc new file mode 100644 index 0000000..4f72dca Binary files /dev/null and b/modules/onnx_ocr_module/src/tools/infer/__pycache__/predict_system.cpython-311.pyc differ diff --git a/modules/onnx_ocr_module/src/tools/infer/__pycache__/utility.cpython-311.pyc b/modules/onnx_ocr_module/src/tools/infer/__pycache__/utility.cpython-311.pyc new file mode 100644 index 0000000..e3a0097 Binary files /dev/null and b/modules/onnx_ocr_module/src/tools/infer/__pycache__/utility.cpython-311.pyc differ diff --git a/modules/onnx_ocr_module/src/tools/infer/predict_cls.py b/modules/onnx_ocr_module/src/tools/infer/predict_cls.py new file mode 100644 index 0000000..e4d5a88 --- /dev/null +++ b/modules/onnx_ocr_module/src/tools/infer/predict_cls.py @@ -0,0 +1,156 @@ +# Copyright (c) 2020 PaddlePaddle Authors. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +import os +import sys + +__dir__ = os.path.dirname(os.path.abspath(__file__)) +sys.path.append(__dir__) +sys.path.insert(0, os.path.abspath(os.path.join(__dir__, "../.."))) + +os.environ["FLAGS_allocator_strategy"] = "auto_growth" + +import cv2 +import copy +import numpy as np +import math +import time +import traceback + +import tools.infer.utility as utility +from ppocr.postprocess import build_post_process +from ppocr.utils.logging import get_logger +from ppocr.utils.utility import get_image_file_list, check_and_read + +logger = get_logger() + + +class TextClassifier(object): + def __init__(self, args): + self.cls_image_shape = [int(v) for v in args.cls_image_shape.split(",")] + self.cls_batch_num = args.cls_batch_num + self.cls_thresh = args.cls_thresh + postprocess_params = { + "name": "ClsPostProcess", + "label_list": args.label_list, + } + self.postprocess_op = build_post_process(postprocess_params) + ( + self.predictor, + self.input_tensor, + self.output_tensors, + _, + ) = utility.create_predictor(args, "cls", logger) + self.use_onnx = args.use_onnx + + def resize_norm_img(self, img): + imgC, imgH, imgW = self.cls_image_shape + h = img.shape[0] + w = img.shape[1] + ratio = w / float(h) + if math.ceil(imgH * ratio) > imgW: + resized_w = imgW + else: + resized_w = int(math.ceil(imgH * ratio)) + resized_image = cv2.resize(img, (resized_w, imgH)) + resized_image = resized_image.astype("float32") + if self.cls_image_shape[0] == 1: + resized_image = resized_image / 255 + resized_image = resized_image[np.newaxis, :] + else: + resized_image = resized_image.transpose((2, 0, 1)) / 255 + resized_image -= 0.5 + resized_image /= 0.5 + padding_im = np.zeros((imgC, imgH, imgW), dtype=np.float32) + padding_im[:, :, 0:resized_w] = resized_image + return padding_im + + def __call__(self, img_list): + img_list = copy.deepcopy(img_list) + img_num = len(img_list) + # Calculate the aspect ratio of all text bars + width_list = [] + for img in img_list: + width_list.append(img.shape[1] / float(img.shape[0])) + # Sorting can speed up the cls process + indices = np.argsort(np.array(width_list)) + + cls_res = [["", 0.0]] * img_num + batch_num = self.cls_batch_num + elapse = 0 + for beg_img_no in range(0, img_num, batch_num): + end_img_no = min(img_num, beg_img_no + batch_num) + norm_img_batch = [] + max_wh_ratio = 0 + starttime = time.time() + for ino in range(beg_img_no, end_img_no): + h, w = img_list[indices[ino]].shape[0:2] + wh_ratio = w * 1.0 / h + max_wh_ratio = max(max_wh_ratio, wh_ratio) + for ino in range(beg_img_no, end_img_no): + norm_img = self.resize_norm_img(img_list[indices[ino]]) + norm_img = norm_img[np.newaxis, :] + norm_img_batch.append(norm_img) + norm_img_batch = np.concatenate(norm_img_batch) + norm_img_batch = norm_img_batch.copy() + + if self.use_onnx: + input_dict = {} + input_dict[self.input_tensor.name] = norm_img_batch + outputs = self.predictor.run(self.output_tensors, input_dict) + prob_out = outputs[0] + else: + self.input_tensor.copy_from_cpu(norm_img_batch) + self.predictor.run() + prob_out = self.output_tensors[0].copy_to_cpu() + self.predictor.try_shrink_memory() + cls_result = self.postprocess_op(prob_out) + elapse += time.time() - starttime + for rno in range(len(cls_result)): + label, score = cls_result[rno] + cls_res[indices[beg_img_no + rno]] = [label, score] + if "180" in label and score > self.cls_thresh: + img_list[indices[beg_img_no + rno]] = cv2.rotate( + img_list[indices[beg_img_no + rno]], 1 + ) + return img_list, cls_res, elapse + + +def main(args): + image_file_list = get_image_file_list(args.image_dir) + text_classifier = TextClassifier(args) + valid_image_file_list = [] + img_list = [] + for image_file in image_file_list: + img, flag, _ = check_and_read(image_file) + if not flag: + img = cv2.imread(image_file) + if img is None: + logger.info("error in loading image:{}".format(image_file)) + continue + valid_image_file_list.append(image_file) + img_list.append(img) + try: + img_list, cls_res, predict_time = text_classifier(img_list) + except Exception as E: + logger.info(traceback.format_exc()) + logger.info(E) + exit() + for ino in range(len(img_list)): + logger.info( + "Predicts of {}:{}".format(valid_image_file_list[ino], cls_res[ino]) + ) + + +if __name__ == "__main__": + main(utility.parse_args()) diff --git a/modules/onnx_ocr_module/src/tools/infer/predict_det.py b/modules/onnx_ocr_module/src/tools/infer/predict_det.py new file mode 100644 index 0000000..9f5c953 --- /dev/null +++ b/modules/onnx_ocr_module/src/tools/infer/predict_det.py @@ -0,0 +1,490 @@ +# Copyright (c) 2020 PaddlePaddle Authors. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +import os +import sys + +__dir__ = os.path.dirname(os.path.abspath(__file__)) +sys.path.append(__dir__) +sys.path.insert(0, os.path.abspath(os.path.join(__dir__, "../.."))) + +os.environ["FLAGS_allocator_strategy"] = "auto_growth" + +import cv2 +import numpy as np +import time +import sys + +import tools.infer.utility as utility +from ppocr.utils.logging import get_logger +from ppocr.utils.utility import get_image_file_list, check_and_read +from ppocr.data.imaug import transform, create_operators +from ppocr.postprocess import build_post_process +import json + + +class TextDetector(object): + def __init__(self, args, logger=None): + if logger is None: + logger = get_logger() + self.args = args + self.det_algorithm = args.det_algorithm + self.use_onnx = args.use_onnx + pre_process_list = [ + { + "DetResizeForTest": { + "limit_side_len": args.det_limit_side_len, + "limit_type": args.det_limit_type, + } + }, + { + "NormalizeImage": { + "std": [0.229, 0.224, 0.225], + "mean": [0.485, 0.456, 0.406], + "scale": "1./255.", + "order": "hwc", + } + }, + {"ToCHWImage": None}, + {"KeepKeys": {"keep_keys": ["image", "shape"]}}, + ] + postprocess_params = {} + if self.det_algorithm == "DB": + postprocess_params["name"] = "DBPostProcess" + postprocess_params["thresh"] = args.det_db_thresh + postprocess_params["box_thresh"] = args.det_db_box_thresh + postprocess_params["max_candidates"] = 1000 + postprocess_params["unclip_ratio"] = args.det_db_unclip_ratio + postprocess_params["use_dilation"] = args.use_dilation + postprocess_params["score_mode"] = args.det_db_score_mode + postprocess_params["box_type"] = args.det_box_type + elif self.det_algorithm == "DB++": + postprocess_params["name"] = "DBPostProcess" + postprocess_params["thresh"] = args.det_db_thresh + postprocess_params["box_thresh"] = args.det_db_box_thresh + postprocess_params["max_candidates"] = 1000 + postprocess_params["unclip_ratio"] = args.det_db_unclip_ratio + postprocess_params["use_dilation"] = args.use_dilation + postprocess_params["score_mode"] = args.det_db_score_mode + postprocess_params["box_type"] = args.det_box_type + pre_process_list[1] = { + "NormalizeImage": { + "std": [1.0, 1.0, 1.0], + "mean": [0.48109378172549, 0.45752457890196, 0.40787054090196], + "scale": "1./255.", + "order": "hwc", + } + } + elif self.det_algorithm == "EAST": + postprocess_params["name"] = "EASTPostProcess" + postprocess_params["score_thresh"] = args.det_east_score_thresh + postprocess_params["cover_thresh"] = args.det_east_cover_thresh + postprocess_params["nms_thresh"] = args.det_east_nms_thresh + elif self.det_algorithm == "SAST": + pre_process_list[0] = { + "DetResizeForTest": {"resize_long": args.det_limit_side_len} + } + postprocess_params["name"] = "SASTPostProcess" + postprocess_params["score_thresh"] = args.det_sast_score_thresh + postprocess_params["nms_thresh"] = args.det_sast_nms_thresh + + if args.det_box_type == "poly": + postprocess_params["sample_pts_num"] = 6 + postprocess_params["expand_scale"] = 1.2 + postprocess_params["shrink_ratio_of_width"] = 0.2 + else: + postprocess_params["sample_pts_num"] = 2 + postprocess_params["expand_scale"] = 1.0 + postprocess_params["shrink_ratio_of_width"] = 0.3 + + elif self.det_algorithm == "PSE": + postprocess_params["name"] = "PSEPostProcess" + postprocess_params["thresh"] = args.det_pse_thresh + postprocess_params["box_thresh"] = args.det_pse_box_thresh + postprocess_params["min_area"] = args.det_pse_min_area + postprocess_params["box_type"] = args.det_box_type + postprocess_params["scale"] = args.det_pse_scale + elif self.det_algorithm == "FCE": + pre_process_list[0] = {"DetResizeForTest": {"rescale_img": [1080, 736]}} + postprocess_params["name"] = "FCEPostProcess" + postprocess_params["scales"] = args.scales + postprocess_params["alpha"] = args.alpha + postprocess_params["beta"] = args.beta + postprocess_params["fourier_degree"] = args.fourier_degree + postprocess_params["box_type"] = args.det_box_type + elif self.det_algorithm == "CT": + pre_process_list[0] = {"ScaleAlignedShort": {"short_size": 640}} + postprocess_params["name"] = "CTPostProcess" + else: + logger.info("unknown det_algorithm:{}".format(self.det_algorithm)) + sys.exit(0) + + self.preprocess_op = create_operators(pre_process_list) + self.postprocess_op = build_post_process(postprocess_params) + ( + self.predictor, + self.input_tensor, + self.output_tensors, + self.config, + ) = utility.create_predictor(args, "det", logger) + + if self.use_onnx: + img_h, img_w = self.input_tensor.shape[2:] + if isinstance(img_h, str) or isinstance(img_w, str): + pass + elif img_h is not None and img_w is not None and img_h > 0 and img_w > 0: + pre_process_list[0] = { + "DetResizeForTest": {"image_shape": [img_h, img_w]} + } + self.preprocess_op = create_operators(pre_process_list) + + if args.benchmark: + import auto_log + + pid = os.getpid() + gpu_id = utility.get_infer_gpuid() + self.autolog = auto_log.AutoLogger( + model_name="det", + model_precision=args.precision, + batch_size=1, + data_shape="dynamic", + save_path=None, # not used if logger is not None + inference_config=self.config, + pids=pid, + process_name=None, + gpu_ids=gpu_id if args.use_gpu else None, + time_keys=["preprocess_time", "inference_time", "postprocess_time"], + warmup=2, + logger=logger, + ) + + def order_points_clockwise(self, pts): + rect = np.zeros((4, 2), dtype="float32") + s = pts.sum(axis=1) + rect[0] = pts[np.argmin(s)] + rect[2] = pts[np.argmax(s)] + tmp = np.delete(pts, (np.argmin(s), np.argmax(s)), axis=0) + diff = np.diff(np.array(tmp), axis=1) + rect[1] = tmp[np.argmin(diff)] + rect[3] = tmp[np.argmax(diff)] + return rect + + def pad_polygons(self, polygon, max_points): + padding_size = max_points - len(polygon) + if padding_size == 0: + return polygon + last_point = polygon[-1] + padding = np.repeat([last_point], padding_size, axis=0) + return np.vstack([polygon, padding]) + + def clip_det_res(self, points, img_height, img_width): + for pno in range(points.shape[0]): + points[pno, 0] = int(min(max(points[pno, 0], 0), img_width - 1)) + points[pno, 1] = int(min(max(points[pno, 1], 0), img_height - 1)) + return points + + def filter_tag_det_res(self, dt_boxes, image_shape): + img_height, img_width = image_shape[0:2] + dt_boxes_new = [] + for box in dt_boxes: + if type(box) is list: + box = np.array(box) + box = self.order_points_clockwise(box) + box = self.clip_det_res(box, img_height, img_width) + rect_width = int(np.linalg.norm(box[0] - box[1])) + rect_height = int(np.linalg.norm(box[0] - box[3])) + if rect_width <= 3 or rect_height <= 3: + continue + dt_boxes_new.append(box) + dt_boxes = np.array(dt_boxes_new) + return dt_boxes + + def filter_tag_det_res_only_clip(self, dt_boxes, image_shape): + img_height, img_width = image_shape[0:2] + dt_boxes_new = [] + for box in dt_boxes: + if type(box) is list: + box = np.array(box) + box = self.clip_det_res(box, img_height, img_width) + dt_boxes_new.append(box) + + if len(dt_boxes_new) > 0: + max_points = max(len(polygon) for polygon in dt_boxes_new) + dt_boxes_new = [ + self.pad_polygons(polygon, max_points) for polygon in dt_boxes_new + ] + + dt_boxes = np.array(dt_boxes_new) + return dt_boxes + + def predict(self, img): + ori_im = img.copy() + data = {"image": img} + + st = time.time() + + if self.args.benchmark: + self.autolog.times.start() + + data = transform(data, self.preprocess_op) + img, shape_list = data + if img is None: + return None, 0 + img = np.expand_dims(img, axis=0) + shape_list = np.expand_dims(shape_list, axis=0) + img = img.copy() + + if self.args.benchmark: + self.autolog.times.stamp() + if self.use_onnx: + input_dict = {} + input_dict[self.input_tensor.name] = img + outputs = self.predictor.run(self.output_tensors, input_dict) + else: + self.input_tensor.copy_from_cpu(img) + self.predictor.run() + outputs = [] + for output_tensor in self.output_tensors: + output = output_tensor.copy_to_cpu() + outputs.append(output) + if self.args.benchmark: + self.autolog.times.stamp() + + preds = {} + if self.det_algorithm == "EAST": + preds["f_geo"] = outputs[0] + preds["f_score"] = outputs[1] + elif self.det_algorithm == "SAST": + preds["f_border"] = outputs[0] + preds["f_score"] = outputs[1] + preds["f_tco"] = outputs[2] + preds["f_tvo"] = outputs[3] + elif self.det_algorithm in ["DB", "PSE", "DB++"]: + preds["maps"] = outputs[0] + elif self.det_algorithm == "FCE": + for i, output in enumerate(outputs): + preds["level_{}".format(i)] = output + elif self.det_algorithm == "CT": + preds["maps"] = outputs[0] + preds["score"] = outputs[1] + else: + raise NotImplementedError + + post_result = self.postprocess_op(preds, shape_list) + dt_boxes = post_result[0]["points"] + + if self.args.det_box_type == "poly": + dt_boxes = self.filter_tag_det_res_only_clip(dt_boxes, ori_im.shape) + else: + dt_boxes = self.filter_tag_det_res(dt_boxes, ori_im.shape) + + if self.args.benchmark: + self.autolog.times.end(stamp=True) + et = time.time() + return dt_boxes, et - st + + def __call__(self, img, use_slice=False): + # For image like poster with one side much greater than the other side, + # splitting recursively and processing with overlap to enhance performance. + MIN_BOUND_DISTANCE = 50 + dt_boxes = np.zeros((0, 4, 2), dtype=np.float32) + elapse = 0 + if ( + img.shape[0] / img.shape[1] > 2 + and img.shape[0] > self.args.det_limit_side_len + and use_slice + ): + start_h = 0 + end_h = 0 + while end_h <= img.shape[0]: + end_h = start_h + img.shape[1] * 3 // 4 + subimg = img[start_h:end_h, :] + if len(subimg) == 0: + break + sub_dt_boxes, sub_elapse = self.predict(subimg) + offset = start_h + # To prevent text blocks from being cut off, roll back a certain buffer area. + if ( + len(sub_dt_boxes) == 0 + or img.shape[1] - max([x[-1][1] for x in sub_dt_boxes]) + > MIN_BOUND_DISTANCE + ): + start_h = end_h + else: + sorted_indices = np.argsort(sub_dt_boxes[:, 2, 1]) + sub_dt_boxes = sub_dt_boxes[sorted_indices] + bottom_line = ( + 0 + if len(sub_dt_boxes) <= 1 + else int(np.max(sub_dt_boxes[:-1, 2, 1])) + ) + if bottom_line > 0: + start_h += bottom_line + sub_dt_boxes = sub_dt_boxes[ + sub_dt_boxes[:, 2, 1] <= bottom_line + ] + else: + start_h = end_h + if len(sub_dt_boxes) > 0: + if dt_boxes.shape[0] == 0: + dt_boxes = sub_dt_boxes + np.array( + [0, offset], dtype=np.float32 + ) + else: + dt_boxes = np.append( + dt_boxes, + sub_dt_boxes + np.array([0, offset], dtype=np.float32), + axis=0, + ) + elapse += sub_elapse + elif ( + img.shape[1] / img.shape[0] > 3 + and img.shape[1] > self.args.det_limit_side_len * 3 + and use_slice + ): + start_w = 0 + end_w = 0 + while end_w <= img.shape[1]: + end_w = start_w + img.shape[0] * 3 // 4 + subimg = img[:, start_w:end_w] + if len(subimg) == 0: + break + sub_dt_boxes, sub_elapse = self.predict(subimg) + offset = start_w + if ( + len(sub_dt_boxes) == 0 + or img.shape[0] - max([x[-1][0] for x in sub_dt_boxes]) + > MIN_BOUND_DISTANCE + ): + start_w = end_w + else: + sorted_indices = np.argsort(sub_dt_boxes[:, 2, 0]) + sub_dt_boxes = sub_dt_boxes[sorted_indices] + right_line = ( + 0 + if len(sub_dt_boxes) <= 1 + else int(np.max(sub_dt_boxes[:-1, 1, 0])) + ) + if right_line > 0: + start_w += right_line + sub_dt_boxes = sub_dt_boxes[sub_dt_boxes[:, 1, 0] <= right_line] + else: + start_w = end_w + if len(sub_dt_boxes) > 0: + if dt_boxes.shape[0] == 0: + dt_boxes = sub_dt_boxes + np.array( + [offset, 0], dtype=np.float32 + ) + else: + dt_boxes = np.append( + dt_boxes, + sub_dt_boxes + np.array([offset, 0], dtype=np.float32), + axis=0, + ) + elapse += sub_elapse + else: + dt_boxes, elapse = self.predict(img) + return dt_boxes, elapse + + +if __name__ == "__main__": + args = utility.parse_args() + image_file_list = get_image_file_list(args.image_dir) + total_time = 0 + draw_img_save_dir = args.draw_img_save_dir + os.makedirs(draw_img_save_dir, exist_ok=True) + + # logger + log_file = args.save_log_path + if os.path.isdir(args.save_log_path) or ( + not os.path.exists(args.save_log_path) and args.save_log_path.endswith("/") + ): + log_file = os.path.join(log_file, "benchmark_detection.log") + logger = get_logger(log_file=log_file) + + # create text detector + text_detector = TextDetector(args, logger) + + if args.warmup: + img = np.random.uniform(0, 255, [640, 640, 3]).astype(np.uint8) + for i in range(2): + res = text_detector(img) + + save_results = [] + for idx, image_file in enumerate(image_file_list): + img, flag_gif, flag_pdf = check_and_read(image_file) + if not flag_gif and not flag_pdf: + img = cv2.imread(image_file) + if not flag_pdf: + if img is None: + logger.debug("error in loading image:{}".format(image_file)) + continue + imgs = [img] + else: + page_num = args.page_num + if page_num > len(img) or page_num == 0: + page_num = len(img) + imgs = img[:page_num] + for index, img in enumerate(imgs): + st = time.time() + dt_boxes, _ = text_detector(img) + elapse = time.time() - st + total_time += elapse + if len(imgs) > 1: + save_pred = ( + os.path.basename(image_file) + + "_" + + str(index) + + "\t" + + str(json.dumps([x.tolist() for x in dt_boxes])) + + "\n" + ) + else: + save_pred = ( + os.path.basename(image_file) + + "\t" + + str(json.dumps([x.tolist() for x in dt_boxes])) + + "\n" + ) + save_results.append(save_pred) + logger.info(save_pred) + if len(imgs) > 1: + logger.info( + "{}_{} The predict time of {}: {}".format( + idx, index, image_file, elapse + ) + ) + else: + logger.info( + "{} The predict time of {}: {}".format(idx, image_file, elapse) + ) + + src_im = utility.draw_text_det_res(dt_boxes, img) + + if flag_gif: + save_file = image_file[:-3] + "png" + elif flag_pdf: + save_file = image_file.replace(".pdf", "_" + str(index) + ".png") + else: + save_file = image_file + img_path = os.path.join( + draw_img_save_dir, "det_res_{}".format(os.path.basename(save_file)) + ) + cv2.imwrite(img_path, src_im) + logger.info("The visualized image saved in {}".format(img_path)) + + with open(os.path.join(draw_img_save_dir, "det_results.txt"), "w") as f: + f.writelines(save_results) + f.close() + if args.benchmark: + text_detector.autolog.report() diff --git a/modules/onnx_ocr_module/src/tools/infer/predict_e2e.py b/modules/onnx_ocr_module/src/tools/infer/predict_e2e.py new file mode 100644 index 0000000..895eedc --- /dev/null +++ b/modules/onnx_ocr_module/src/tools/infer/predict_e2e.py @@ -0,0 +1,170 @@ +# Copyright (c) 2020 PaddlePaddle Authors. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +import os +import sys + +__dir__ = os.path.dirname(os.path.abspath(__file__)) +sys.path.append(__dir__) +sys.path.insert(0, os.path.abspath(os.path.join(__dir__, "../.."))) + +os.environ["FLAGS_allocator_strategy"] = "auto_growth" + +import cv2 +import numpy as np +import time +import sys + +import tools.infer.utility as utility +from ppocr.utils.logging import get_logger +from ppocr.utils.utility import get_image_file_list, check_and_read +from ppocr.data.imaug import transform, create_operators +from ppocr.postprocess import build_post_process + +logger = get_logger() + + +class TextE2E(object): + def __init__(self, args): + self.args = args + self.e2e_algorithm = args.e2e_algorithm + self.use_onnx = args.use_onnx + pre_process_list = [ + {"E2EResizeForTest": {}}, + { + "NormalizeImage": { + "std": [0.229, 0.224, 0.225], + "mean": [0.485, 0.456, 0.406], + "scale": "1./255.", + "order": "hwc", + } + }, + {"ToCHWImage": None}, + {"KeepKeys": {"keep_keys": ["image", "shape"]}}, + ] + postprocess_params = {} + if self.e2e_algorithm == "PGNet": + pre_process_list[0] = { + "E2EResizeForTest": { + "max_side_len": args.e2e_limit_side_len, + "valid_set": "totaltext", + } + } + postprocess_params["name"] = "PGPostProcess" + postprocess_params["score_thresh"] = args.e2e_pgnet_score_thresh + postprocess_params["character_dict_path"] = args.e2e_char_dict_path + postprocess_params["valid_set"] = args.e2e_pgnet_valid_set + postprocess_params["mode"] = args.e2e_pgnet_mode + else: + logger.info("unknown e2e_algorithm:{}".format(self.e2e_algorithm)) + sys.exit(0) + + self.preprocess_op = create_operators(pre_process_list) + self.postprocess_op = build_post_process(postprocess_params) + ( + self.predictor, + self.input_tensor, + self.output_tensors, + _, + ) = utility.create_predictor( + args, "e2e", logger + ) # paddle.jit.load(args.det_model_dir) + # self.predictor.eval() + + def clip_det_res(self, points, img_height, img_width): + for pno in range(points.shape[0]): + points[pno, 0] = int(min(max(points[pno, 0], 0), img_width - 1)) + points[pno, 1] = int(min(max(points[pno, 1], 0), img_height - 1)) + return points + + def filter_tag_det_res_only_clip(self, dt_boxes, image_shape): + img_height, img_width = image_shape[0:2] + dt_boxes_new = [] + for box in dt_boxes: + box = self.clip_det_res(box, img_height, img_width) + dt_boxes_new.append(box) + dt_boxes = np.array(dt_boxes_new) + return dt_boxes + + def __call__(self, img): + ori_im = img.copy() + data = {"image": img} + data = transform(data, self.preprocess_op) + img, shape_list = data + if img is None: + return None, 0 + img = np.expand_dims(img, axis=0) + shape_list = np.expand_dims(shape_list, axis=0) + img = img.copy() + starttime = time.time() + + if self.use_onnx: + input_dict = {} + input_dict[self.input_tensor.name] = img + outputs = self.predictor.run(self.output_tensors, input_dict) + preds = {} + preds["f_border"] = outputs[0] + preds["f_char"] = outputs[1] + preds["f_direction"] = outputs[2] + preds["f_score"] = outputs[3] + else: + self.input_tensor.copy_from_cpu(img) + self.predictor.run() + outputs = [] + for output_tensor in self.output_tensors: + output = output_tensor.copy_to_cpu() + outputs.append(output) + + preds = {} + if self.e2e_algorithm == "PGNet": + preds["f_border"] = outputs[0] + preds["f_char"] = outputs[1] + preds["f_direction"] = outputs[2] + preds["f_score"] = outputs[3] + else: + raise NotImplementedError + post_result = self.postprocess_op(preds, shape_list) + points, strs = post_result["points"], post_result["texts"] + dt_boxes = self.filter_tag_det_res_only_clip(points, ori_im.shape) + elapse = time.time() - starttime + return dt_boxes, strs, elapse + + +if __name__ == "__main__": + args = utility.parse_args() + image_file_list = get_image_file_list(args.image_dir) + text_detector = TextE2E(args) + count = 0 + total_time = 0 + draw_img_save = "./inference_results" + if not os.path.exists(draw_img_save): + os.makedirs(draw_img_save) + for image_file in image_file_list: + img, flag, _ = check_and_read(image_file) + if not flag: + img = cv2.imread(image_file) + if img is None: + logger.info("error in loading image:{}".format(image_file)) + continue + points, strs, elapse = text_detector(img) + if count > 0: + total_time += elapse + count += 1 + logger.info("Predict time of {}: {}".format(image_file, elapse)) + src_im = utility.draw_e2e_res(points, strs, image_file) + img_name_pure = os.path.split(image_file)[-1] + img_path = os.path.join(draw_img_save, "e2e_res_{}".format(img_name_pure)) + cv2.imwrite(img_path, src_im) + logger.info("The visualized image saved in {}".format(img_path)) + if count > 1: + logger.info("Avg Time: {}".format(total_time / (count - 1))) diff --git a/modules/onnx_ocr_module/src/tools/infer/predict_rec.py b/modules/onnx_ocr_module/src/tools/infer/predict_rec.py new file mode 100644 index 0000000..c2a6cbc --- /dev/null +++ b/modules/onnx_ocr_module/src/tools/infer/predict_rec.py @@ -0,0 +1,875 @@ +# Copyright (c) 2020 PaddlePaddle Authors. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +import os +import sys +from PIL import Image + +__dir__ = os.path.dirname(os.path.abspath(__file__)) +sys.path.append(__dir__) +sys.path.insert(0, os.path.abspath(os.path.join(__dir__, "../.."))) + +os.environ["FLAGS_allocator_strategy"] = "auto_growth" + +import cv2 +import numpy as np +import math +import time +import traceback +# ONNX 전용 실행: paddle 미사용 + +import tools.infer.utility as utility +from ppocr.postprocess import build_post_process +from ppocr.utils.logging import get_logger +from ppocr.utils.utility import get_image_file_list, check_and_read + +logger = get_logger() + + +class TextRecognizer(object): + def __init__(self, args, logger=None): + if logger is None: + logger = get_logger() + self.rec_image_shape = [int(v) for v in args.rec_image_shape.split(",")] + self.rec_batch_num = args.rec_batch_num + self.rec_algorithm = args.rec_algorithm + postprocess_params = { + "name": "CTCLabelDecode", + "character_dict_path": args.rec_char_dict_path, + "use_space_char": args.use_space_char, + } + if self.rec_algorithm == "SRN": + postprocess_params = { + "name": "SRNLabelDecode", + "character_dict_path": args.rec_char_dict_path, + "use_space_char": args.use_space_char, + } + elif self.rec_algorithm == "RARE": + postprocess_params = { + "name": "AttnLabelDecode", + "character_dict_path": args.rec_char_dict_path, + "use_space_char": args.use_space_char, + } + elif self.rec_algorithm == "NRTR": + postprocess_params = { + "name": "NRTRLabelDecode", + "character_dict_path": args.rec_char_dict_path, + "use_space_char": args.use_space_char, + } + elif self.rec_algorithm == "SAR": + postprocess_params = { + "name": "SARLabelDecode", + "character_dict_path": args.rec_char_dict_path, + "use_space_char": args.use_space_char, + } + elif self.rec_algorithm == "VisionLAN": + postprocess_params = { + "name": "VLLabelDecode", + "character_dict_path": args.rec_char_dict_path, + "use_space_char": args.use_space_char, + "max_text_length": args.max_text_length, + } + elif self.rec_algorithm == "ViTSTR": + postprocess_params = { + "name": "ViTSTRLabelDecode", + "character_dict_path": args.rec_char_dict_path, + "use_space_char": args.use_space_char, + } + elif self.rec_algorithm == "ABINet": + postprocess_params = { + "name": "ABINetLabelDecode", + "character_dict_path": args.rec_char_dict_path, + "use_space_char": args.use_space_char, + } + elif self.rec_algorithm == "SPIN": + postprocess_params = { + "name": "SPINLabelDecode", + "character_dict_path": args.rec_char_dict_path, + "use_space_char": args.use_space_char, + } + elif self.rec_algorithm == "RobustScanner": + postprocess_params = { + "name": "SARLabelDecode", + "character_dict_path": args.rec_char_dict_path, + "use_space_char": args.use_space_char, + "rm_symbol": True, + } + elif self.rec_algorithm == "RFL": + postprocess_params = { + "name": "RFLLabelDecode", + "character_dict_path": None, + "use_space_char": args.use_space_char, + } + elif self.rec_algorithm == "SATRN": + postprocess_params = { + "name": "SATRNLabelDecode", + "character_dict_path": args.rec_char_dict_path, + "use_space_char": args.use_space_char, + "rm_symbol": True, + } + elif self.rec_algorithm in ["CPPD", "CPPDPadding"]: + postprocess_params = { + "name": "CPPDLabelDecode", + "character_dict_path": args.rec_char_dict_path, + "use_space_char": args.use_space_char, + "rm_symbol": True, + } + elif self.rec_algorithm == "PREN": + postprocess_params = {"name": "PRENLabelDecode"} + elif self.rec_algorithm == "CAN": + self.inverse = args.rec_image_inverse + postprocess_params = { + "name": "CANLabelDecode", + "character_dict_path": args.rec_char_dict_path, + "use_space_char": args.use_space_char, + } + elif self.rec_algorithm == "LaTeXOCR": + postprocess_params = { + "name": "LaTeXOCRDecode", + "rec_char_dict_path": args.rec_char_dict_path, + } + elif self.rec_algorithm == "ParseQ": + postprocess_params = { + "name": "ParseQLabelDecode", + "character_dict_path": args.rec_char_dict_path, + "use_space_char": args.use_space_char, + } + self.postprocess_op = build_post_process(postprocess_params) + self.postprocess_params = postprocess_params + ( + self.predictor, + self.input_tensor, + self.output_tensors, + self.config, + ) = utility.create_predictor(args, "rec", logger) + self.benchmark = args.benchmark + self.use_onnx = args.use_onnx + if args.benchmark: + import auto_log + + pid = os.getpid() + gpu_id = utility.get_infer_gpuid() + self.autolog = auto_log.AutoLogger( + model_name="rec", + model_precision=args.precision, + batch_size=args.rec_batch_num, + data_shape="dynamic", + save_path=None, # not used if logger is not None + inference_config=self.config, + pids=pid, + process_name=None, + gpu_ids=gpu_id if args.use_gpu else None, + time_keys=["preprocess_time", "inference_time", "postprocess_time"], + warmup=0, + logger=logger, + ) + self.return_word_box = args.return_word_box + + def resize_norm_img(self, img, max_wh_ratio): + imgC, imgH, imgW = self.rec_image_shape + if self.rec_algorithm == "NRTR" or self.rec_algorithm == "ViTSTR": + img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) + # return padding_im + image_pil = Image.fromarray(np.uint8(img)) + if self.rec_algorithm == "ViTSTR": + img = image_pil.resize([imgW, imgH], Image.BICUBIC) + else: + img = image_pil.resize([imgW, imgH], Image.Resampling.LANCZOS) + img = np.array(img) + norm_img = np.expand_dims(img, -1) + norm_img = norm_img.transpose((2, 0, 1)) + if self.rec_algorithm == "ViTSTR": + norm_img = norm_img.astype(np.float32) / 255.0 + else: + norm_img = norm_img.astype(np.float32) / 128.0 - 1.0 + return norm_img + elif self.rec_algorithm == "RFL": + img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) + resized_image = cv2.resize(img, (imgW, imgH), interpolation=cv2.INTER_CUBIC) + resized_image = resized_image.astype("float32") + resized_image = resized_image / 255 + resized_image = resized_image[np.newaxis, :] + resized_image -= 0.5 + resized_image /= 0.5 + return resized_image + + assert imgC == img.shape[2] + imgW = int((imgH * max_wh_ratio)) + if self.use_onnx: + w = self.input_tensor.shape[3:][0] + if isinstance(w, str): + pass + elif w is not None and w > 0: + imgW = w + h, w = img.shape[:2] + ratio = w / float(h) + if math.ceil(imgH * ratio) > imgW: + resized_w = imgW + else: + resized_w = int(math.ceil(imgH * ratio)) + if self.rec_algorithm == "RARE": + if resized_w > self.rec_image_shape[2]: + resized_w = self.rec_image_shape[2] + imgW = self.rec_image_shape[2] + resized_image = cv2.resize(img, (resized_w, imgH)) + resized_image = resized_image.astype("float32") + resized_image = resized_image.transpose((2, 0, 1)) / 255 + resized_image -= 0.5 + resized_image /= 0.5 + padding_im = np.zeros((imgC, imgH, imgW), dtype=np.float32) + padding_im[:, :, 0:resized_w] = resized_image + return padding_im + + def resize_norm_img_vl(self, img, image_shape): + imgC, imgH, imgW = image_shape + img = img[:, :, ::-1] # bgr2rgb + resized_image = cv2.resize(img, (imgW, imgH), interpolation=cv2.INTER_LINEAR) + resized_image = resized_image.astype("float32") + resized_image = resized_image.transpose((2, 0, 1)) / 255 + return resized_image + + def resize_norm_img_srn(self, img, image_shape): + imgC, imgH, imgW = image_shape + + img_black = np.zeros((imgH, imgW)) + im_hei = img.shape[0] + im_wid = img.shape[1] + + if im_wid <= im_hei * 1: + img_new = cv2.resize(img, (imgH * 1, imgH)) + elif im_wid <= im_hei * 2: + img_new = cv2.resize(img, (imgH * 2, imgH)) + elif im_wid <= im_hei * 3: + img_new = cv2.resize(img, (imgH * 3, imgH)) + else: + img_new = cv2.resize(img, (imgW, imgH)) + + img_np = np.asarray(img_new) + img_np = cv2.cvtColor(img_np, cv2.COLOR_BGR2GRAY) + img_black[:, 0 : img_np.shape[1]] = img_np + img_black = img_black[:, :, np.newaxis] + + row, col, c = img_black.shape + c = 1 + + return np.reshape(img_black, (c, row, col)).astype(np.float32) + + def srn_other_inputs(self, image_shape, num_heads, max_text_length): + imgC, imgH, imgW = image_shape + feature_dim = int((imgH / 8) * (imgW / 8)) + + encoder_word_pos = ( + np.array(range(0, feature_dim)).reshape((feature_dim, 1)).astype("int64") + ) + gsrm_word_pos = ( + np.array(range(0, max_text_length)) + .reshape((max_text_length, 1)) + .astype("int64") + ) + + gsrm_attn_bias_data = np.ones((1, max_text_length, max_text_length)) + gsrm_slf_attn_bias1 = np.triu(gsrm_attn_bias_data, 1).reshape( + [-1, 1, max_text_length, max_text_length] + ) + gsrm_slf_attn_bias1 = np.tile(gsrm_slf_attn_bias1, [1, num_heads, 1, 1]).astype( + "float32" + ) * [-1e9] + + gsrm_slf_attn_bias2 = np.tril(gsrm_attn_bias_data, -1).reshape( + [-1, 1, max_text_length, max_text_length] + ) + gsrm_slf_attn_bias2 = np.tile(gsrm_slf_attn_bias2, [1, num_heads, 1, 1]).astype( + "float32" + ) * [-1e9] + + encoder_word_pos = encoder_word_pos[np.newaxis, :] + gsrm_word_pos = gsrm_word_pos[np.newaxis, :] + + return [ + encoder_word_pos, + gsrm_word_pos, + gsrm_slf_attn_bias1, + gsrm_slf_attn_bias2, + ] + + def process_image_srn(self, img, image_shape, num_heads, max_text_length): + norm_img = self.resize_norm_img_srn(img, image_shape) + norm_img = norm_img[np.newaxis, :] + + [ + encoder_word_pos, + gsrm_word_pos, + gsrm_slf_attn_bias1, + gsrm_slf_attn_bias2, + ] = self.srn_other_inputs(image_shape, num_heads, max_text_length) + + gsrm_slf_attn_bias1 = gsrm_slf_attn_bias1.astype(np.float32) + gsrm_slf_attn_bias2 = gsrm_slf_attn_bias2.astype(np.float32) + encoder_word_pos = encoder_word_pos.astype(np.int64) + gsrm_word_pos = gsrm_word_pos.astype(np.int64) + + return ( + norm_img, + encoder_word_pos, + gsrm_word_pos, + gsrm_slf_attn_bias1, + gsrm_slf_attn_bias2, + ) + + def resize_norm_img_sar(self, img, image_shape, width_downsample_ratio=0.25): + imgC, imgH, imgW_min, imgW_max = image_shape + h = img.shape[0] + w = img.shape[1] + valid_ratio = 1.0 + # make sure new_width is an integral multiple of width_divisor. + width_divisor = int(1 / width_downsample_ratio) + # resize + ratio = w / float(h) + resize_w = math.ceil(imgH * ratio) + if resize_w % width_divisor != 0: + resize_w = round(resize_w / width_divisor) * width_divisor + if imgW_min is not None: + resize_w = max(imgW_min, resize_w) + if imgW_max is not None: + valid_ratio = min(1.0, 1.0 * resize_w / imgW_max) + resize_w = min(imgW_max, resize_w) + resized_image = cv2.resize(img, (resize_w, imgH)) + resized_image = resized_image.astype("float32") + # norm + if image_shape[0] == 1: + resized_image = resized_image / 255 + resized_image = resized_image[np.newaxis, :] + else: + resized_image = resized_image.transpose((2, 0, 1)) / 255 + resized_image -= 0.5 + resized_image /= 0.5 + resize_shape = resized_image.shape + padding_im = -1.0 * np.ones((imgC, imgH, imgW_max), dtype=np.float32) + padding_im[:, :, 0:resize_w] = resized_image + pad_shape = padding_im.shape + + return padding_im, resize_shape, pad_shape, valid_ratio + + def resize_norm_img_spin(self, img): + img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) + # return padding_im + img = cv2.resize(img, tuple([100, 32]), cv2.INTER_CUBIC) + img = np.array(img, np.float32) + img = np.expand_dims(img, -1) + img = img.transpose((2, 0, 1)) + mean = [127.5] + std = [127.5] + mean = np.array(mean, dtype=np.float32) + std = np.array(std, dtype=np.float32) + mean = np.float32(mean.reshape(1, -1)) + stdinv = 1 / np.float32(std.reshape(1, -1)) + img -= mean + img *= stdinv + return img + + def resize_norm_img_svtr(self, img, image_shape): + imgC, imgH, imgW = image_shape + resized_image = cv2.resize(img, (imgW, imgH), interpolation=cv2.INTER_LINEAR) + resized_image = resized_image.astype("float32") + resized_image = resized_image.transpose((2, 0, 1)) / 255 + resized_image -= 0.5 + resized_image /= 0.5 + return resized_image + + def resize_norm_img_cppd_padding( + self, img, image_shape, padding=True, interpolation=cv2.INTER_LINEAR + ): + imgC, imgH, imgW = image_shape + h = img.shape[0] + w = img.shape[1] + if not padding: + resized_image = cv2.resize(img, (imgW, imgH), interpolation=interpolation) + resized_w = imgW + else: + ratio = w / float(h) + if math.ceil(imgH * ratio) > imgW: + resized_w = imgW + else: + resized_w = int(math.ceil(imgH * ratio)) + resized_image = cv2.resize(img, (resized_w, imgH)) + resized_image = resized_image.astype("float32") + if image_shape[0] == 1: + resized_image = resized_image / 255 + resized_image = resized_image[np.newaxis, :] + else: + resized_image = resized_image.transpose((2, 0, 1)) / 255 + resized_image -= 0.5 + resized_image /= 0.5 + padding_im = np.zeros((imgC, imgH, imgW), dtype=np.float32) + padding_im[:, :, 0:resized_w] = resized_image + + return padding_im + + def resize_norm_img_abinet(self, img, image_shape): + imgC, imgH, imgW = image_shape + + resized_image = cv2.resize(img, (imgW, imgH), interpolation=cv2.INTER_LINEAR) + resized_image = resized_image.astype("float32") + resized_image = resized_image / 255.0 + + mean = np.array([0.485, 0.456, 0.406]) + std = np.array([0.229, 0.224, 0.225]) + resized_image = (resized_image - mean[None, None, ...]) / std[None, None, ...] + resized_image = resized_image.transpose((2, 0, 1)) + resized_image = resized_image.astype("float32") + + return resized_image + + def norm_img_can(self, img, image_shape): + img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # CAN only predict gray scale image + + if self.inverse: + img = 255 - img + + if self.rec_image_shape[0] == 1: + h, w = img.shape + _, imgH, imgW = self.rec_image_shape + if h < imgH or w < imgW: + padding_h = max(imgH - h, 0) + padding_w = max(imgW - w, 0) + img_padded = np.pad( + img, + ((0, padding_h), (0, padding_w)), + "constant", + constant_values=(255), + ) + img = img_padded + + img = np.expand_dims(img, 0) / 255.0 # h,w,c -> c,h,w + img = img.astype("float32") + + return img + + def pad_(self, img, divable=32): + threshold = 128 + data = np.array(img.convert("LA")) + if data[..., -1].var() == 0: + data = (data[..., 0]).astype(np.uint8) + else: + data = (255 - data[..., -1]).astype(np.uint8) + data = (data - data.min()) / (data.max() - data.min()) * 255 + if data.mean() > threshold: + # To invert the text to white + gray = 255 * (data < threshold).astype(np.uint8) + else: + gray = 255 * (data > threshold).astype(np.uint8) + data = 255 - data + + coords = cv2.findNonZero(gray) # Find all non-zero points (text) + a, b, w, h = cv2.boundingRect(coords) # Find minimum spanning bounding box + rect = data[b : b + h, a : a + w] + im = Image.fromarray(rect).convert("L") + dims = [] + for x in [w, h]: + div, mod = divmod(x, divable) + dims.append(divable * (div + (1 if mod > 0 else 0))) + padded = Image.new("L", dims, 255) + padded.paste(im, (0, 0, im.size[0], im.size[1])) + return padded + + def minmax_size_( + self, + img, + max_dimensions, + min_dimensions, + ): + if max_dimensions is not None: + ratios = [a / b for a, b in zip(img.size, max_dimensions)] + if any([r > 1 for r in ratios]): + size = np.array(img.size) // max(ratios) + img = img.resize(tuple(size.astype(int)), Image.BILINEAR) + if min_dimensions is not None: + # hypothesis: there is a dim in img smaller than min_dimensions, and return a proper dim >= min_dimensions + padded_size = [ + max(img_dim, min_dim) + for img_dim, min_dim in zip(img.size, min_dimensions) + ] + if padded_size != list(img.size): # assert hypothesis + padded_im = Image.new("L", padded_size, 255) + padded_im.paste(img, img.getbbox()) + img = padded_im + return img + + def norm_img_latexocr(self, img): + # CAN only predict gray scale image + shape = (1, 1, 3) + mean = [0.7931, 0.7931, 0.7931] + std = [0.1738, 0.1738, 0.1738] + scale = np.float32(1.0 / 255.0) + min_dimensions = [32, 32] + max_dimensions = [672, 192] + mean = np.array(mean).reshape(shape).astype("float32") + std = np.array(std).reshape(shape).astype("float32") + + im_h, im_w = img.shape[:2] + if ( + min_dimensions[0] <= im_w <= max_dimensions[0] + and min_dimensions[1] <= im_h <= max_dimensions[1] + ): + pass + else: + img = Image.fromarray(np.uint8(img)) + img = self.minmax_size_(self.pad_(img), max_dimensions, min_dimensions) + img = np.array(img) + im_h, im_w = img.shape[:2] + img = np.dstack([img, img, img]) + img = (img.astype("float32") * scale - mean) / std + img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) + divide_h = math.ceil(im_h / 16) * 16 + divide_w = math.ceil(im_w / 16) * 16 + img = np.pad( + img, ((0, divide_h - im_h), (0, divide_w - im_w)), constant_values=(1, 1) + ) + img = img[:, :, np.newaxis].transpose(2, 0, 1) + img = img.astype("float32") + return img + + def __call__(self, img_list): + img_num = len(img_list) + # Calculate the aspect ratio of all text bars + width_list = [] + for img in img_list: + width_list.append(img.shape[1] / float(img.shape[0])) + # Sorting can speed up the recognition process + indices = np.argsort(np.array(width_list)) + rec_res = [["", 0.0]] * img_num + batch_num = self.rec_batch_num + st = time.time() + if self.benchmark: + self.autolog.times.start() + for beg_img_no in range(0, img_num, batch_num): + end_img_no = min(img_num, beg_img_no + batch_num) + norm_img_batch = [] + if self.rec_algorithm == "SRN": + encoder_word_pos_list = [] + gsrm_word_pos_list = [] + gsrm_slf_attn_bias1_list = [] + gsrm_slf_attn_bias2_list = [] + if self.rec_algorithm == "SAR": + valid_ratios = [] + imgC, imgH, imgW = self.rec_image_shape[:3] + max_wh_ratio = imgW / imgH + wh_ratio_list = [] + for ino in range(beg_img_no, end_img_no): + h, w = img_list[indices[ino]].shape[0:2] + wh_ratio = w * 1.0 / h + max_wh_ratio = max(max_wh_ratio, wh_ratio) + wh_ratio_list.append(wh_ratio) + for ino in range(beg_img_no, end_img_no): + if self.rec_algorithm == "SAR": + norm_img, _, _, valid_ratio = self.resize_norm_img_sar( + img_list[indices[ino]], self.rec_image_shape + ) + norm_img = norm_img[np.newaxis, :] + valid_ratio = np.expand_dims(valid_ratio, axis=0) + valid_ratios.append(valid_ratio) + norm_img_batch.append(norm_img) + elif self.rec_algorithm == "SRN": + norm_img = self.process_image_srn( + img_list[indices[ino]], self.rec_image_shape, 8, 25 + ) + encoder_word_pos_list.append(norm_img[1]) + gsrm_word_pos_list.append(norm_img[2]) + gsrm_slf_attn_bias1_list.append(norm_img[3]) + gsrm_slf_attn_bias2_list.append(norm_img[4]) + norm_img_batch.append(norm_img[0]) + elif self.rec_algorithm in ["SVTR", "SATRN", "ParseQ", "CPPD"]: + norm_img = self.resize_norm_img_svtr( + img_list[indices[ino]], self.rec_image_shape + ) + norm_img = norm_img[np.newaxis, :] + norm_img_batch.append(norm_img) + elif self.rec_algorithm in ["CPPDPadding"]: + norm_img = self.resize_norm_img_cppd_padding( + img_list[indices[ino]], self.rec_image_shape + ) + norm_img = norm_img[np.newaxis, :] + norm_img_batch.append(norm_img) + elif self.rec_algorithm in ["VisionLAN", "PREN"]: + norm_img = self.resize_norm_img_vl( + img_list[indices[ino]], self.rec_image_shape + ) + norm_img = norm_img[np.newaxis, :] + norm_img_batch.append(norm_img) + elif self.rec_algorithm == "SPIN": + norm_img = self.resize_norm_img_spin(img_list[indices[ino]]) + norm_img = norm_img[np.newaxis, :] + norm_img_batch.append(norm_img) + elif self.rec_algorithm == "ABINet": + norm_img = self.resize_norm_img_abinet( + img_list[indices[ino]], self.rec_image_shape + ) + norm_img = norm_img[np.newaxis, :] + norm_img_batch.append(norm_img) + elif self.rec_algorithm == "RobustScanner": + norm_img, _, _, valid_ratio = self.resize_norm_img_sar( + img_list[indices[ino]], + self.rec_image_shape, + width_downsample_ratio=0.25, + ) + norm_img = norm_img[np.newaxis, :] + valid_ratio = np.expand_dims(valid_ratio, axis=0) + valid_ratios = [] + valid_ratios.append(valid_ratio) + norm_img_batch.append(norm_img) + word_positions_list = [] + word_positions = np.array(range(0, 40)).astype("int64") + word_positions = np.expand_dims(word_positions, axis=0) + word_positions_list.append(word_positions) + elif self.rec_algorithm == "CAN": + norm_img = self.norm_img_can(img_list[indices[ino]], max_wh_ratio) + norm_img = norm_img[np.newaxis, :] + norm_img_batch.append(norm_img) + norm_image_mask = np.ones(norm_img.shape, dtype="float32") + word_label = np.ones([1, 36], dtype="int64") + norm_img_mask_batch = [] + word_label_list = [] + norm_img_mask_batch.append(norm_image_mask) + word_label_list.append(word_label) + elif self.rec_algorithm == "LaTeXOCR": + norm_img = self.norm_img_latexocr(img_list[indices[ino]]) + norm_img = norm_img[np.newaxis, :] + norm_img_batch.append(norm_img) + else: + norm_img = self.resize_norm_img( + img_list[indices[ino]], max_wh_ratio + ) + norm_img = norm_img[np.newaxis, :] + norm_img_batch.append(norm_img) + norm_img_batch = np.concatenate(norm_img_batch) + norm_img_batch = norm_img_batch.copy() + if self.benchmark: + self.autolog.times.stamp() + + if self.rec_algorithm == "SRN": + encoder_word_pos_list = np.concatenate(encoder_word_pos_list) + gsrm_word_pos_list = np.concatenate(gsrm_word_pos_list) + gsrm_slf_attn_bias1_list = np.concatenate(gsrm_slf_attn_bias1_list) + gsrm_slf_attn_bias2_list = np.concatenate(gsrm_slf_attn_bias2_list) + + inputs = [ + norm_img_batch, + encoder_word_pos_list, + gsrm_word_pos_list, + gsrm_slf_attn_bias1_list, + gsrm_slf_attn_bias2_list, + ] + if self.use_onnx: + input_dict = {} + input_dict[self.input_tensor.name] = norm_img_batch + outputs = self.predictor.run(self.output_tensors, input_dict) + preds = {"predict": outputs[2]} + else: + input_names = self.predictor.get_input_names() + for i in range(len(input_names)): + input_tensor = self.predictor.get_input_handle(input_names[i]) + input_tensor.copy_from_cpu(inputs[i]) + self.predictor.run() + outputs = [] + for output_tensor in self.output_tensors: + output = output_tensor.copy_to_cpu() + outputs.append(output) + if self.benchmark: + self.autolog.times.stamp() + preds = {"predict": outputs[2]} + elif self.rec_algorithm == "SAR": + valid_ratios = np.concatenate(valid_ratios) + inputs = [ + norm_img_batch, + np.array([valid_ratios], dtype=np.float32).T, + ] + if self.use_onnx: + input_dict = {} + input_dict[self.input_tensor.name] = norm_img_batch + outputs = self.predictor.run(self.output_tensors, input_dict) + preds = outputs[0] + else: + input_names = self.predictor.get_input_names() + for i in range(len(input_names)): + input_tensor = self.predictor.get_input_handle(input_names[i]) + input_tensor.copy_from_cpu(inputs[i]) + self.predictor.run() + outputs = [] + for output_tensor in self.output_tensors: + output = output_tensor.copy_to_cpu() + outputs.append(output) + if self.benchmark: + self.autolog.times.stamp() + preds = outputs[0] + elif self.rec_algorithm == "RobustScanner": + valid_ratios = np.concatenate(valid_ratios) + word_positions_list = np.concatenate(word_positions_list) + inputs = [norm_img_batch, valid_ratios, word_positions_list] + + if self.use_onnx: + input_dict = {} + input_dict[self.input_tensor.name] = norm_img_batch + outputs = self.predictor.run(self.output_tensors, input_dict) + preds = outputs[0] + else: + input_names = self.predictor.get_input_names() + for i in range(len(input_names)): + input_tensor = self.predictor.get_input_handle(input_names[i]) + input_tensor.copy_from_cpu(inputs[i]) + self.predictor.run() + outputs = [] + for output_tensor in self.output_tensors: + output = output_tensor.copy_to_cpu() + outputs.append(output) + if self.benchmark: + self.autolog.times.stamp() + preds = outputs[0] + elif self.rec_algorithm == "CAN": + norm_img_mask_batch = np.concatenate(norm_img_mask_batch) + word_label_list = np.concatenate(word_label_list) + inputs = [norm_img_batch, norm_img_mask_batch, word_label_list] + if self.use_onnx: + input_dict = {} + input_dict[self.input_tensor.name] = norm_img_batch + outputs = self.predictor.run(self.output_tensors, input_dict) + preds = outputs + else: + input_names = self.predictor.get_input_names() + input_tensor = [] + for i in range(len(input_names)): + input_tensor_i = self.predictor.get_input_handle(input_names[i]) + input_tensor_i.copy_from_cpu(inputs[i]) + input_tensor.append(input_tensor_i) + self.input_tensor = input_tensor + self.predictor.run() + outputs = [] + for output_tensor in self.output_tensors: + output = output_tensor.copy_to_cpu() + outputs.append(output) + if self.benchmark: + self.autolog.times.stamp() + preds = outputs + elif self.rec_algorithm == "LaTeXOCR": + inputs = [norm_img_batch] + if self.use_onnx: + input_dict = {} + input_dict[self.input_tensor.name] = norm_img_batch + outputs = self.predictor.run(self.output_tensors, input_dict) + preds = outputs + else: + input_names = self.predictor.get_input_names() + input_tensor = [] + for i in range(len(input_names)): + input_tensor_i = self.predictor.get_input_handle(input_names[i]) + input_tensor_i.copy_from_cpu(inputs[i]) + input_tensor.append(input_tensor_i) + self.input_tensor = input_tensor + self.predictor.run() + outputs = [] + for output_tensor in self.output_tensors: + output = output_tensor.copy_to_cpu() + outputs.append(output) + if self.benchmark: + self.autolog.times.stamp() + preds = outputs + else: + if self.use_onnx: + input_dict = {} + input_dict[self.input_tensor.name] = norm_img_batch + outputs = self.predictor.run(self.output_tensors, input_dict) + preds = outputs[0] + else: + self.input_tensor.copy_from_cpu(norm_img_batch) + self.predictor.run() + outputs = [] + for output_tensor in self.output_tensors: + output = output_tensor.copy_to_cpu() + outputs.append(output) + if self.benchmark: + self.autolog.times.stamp() + if len(outputs) != 1: + preds = outputs + else: + preds = outputs[0] + if self.postprocess_params["name"] == "CTCLabelDecode": + rec_result = self.postprocess_op( + preds, + return_word_box=self.return_word_box, + wh_ratio_list=wh_ratio_list, + max_wh_ratio=max_wh_ratio, + ) + elif self.postprocess_params["name"] == "LaTeXOCRDecode": + preds = [p.reshape([-1]) for p in preds] + rec_result = self.postprocess_op(preds) + else: + rec_result = self.postprocess_op(preds) + for rno in range(len(rec_result)): + rec_res[indices[beg_img_no + rno]] = rec_result[rno] + if self.benchmark: + self.autolog.times.end(stamp=True) + return rec_res, time.time() - st + + +def main(args): + image_file_list = get_image_file_list(args.image_dir) + valid_image_file_list = [] + img_list = [] + + # logger + log_file = args.save_log_path + if os.path.isdir(args.save_log_path) or ( + not os.path.exists(args.save_log_path) and args.save_log_path.endswith("/") + ): + log_file = os.path.join(log_file, "benchmark_recognition.log") + logger = get_logger(log_file=log_file) + + # create text recognizer + text_recognizer = TextRecognizer(args) + + logger.info( + "In PP-OCRv3, rec_image_shape parameter defaults to '3, 48, 320', " + "if you are using recognition model with PP-OCRv2 or an older version, please set --rec_image_shape='3,32,320" + ) + + # warmup 2 times + if args.warmup: + img = np.random.uniform(0, 255, [48, 320, 3]).astype(np.uint8) + for i in range(2): + res = text_recognizer([img] * int(args.rec_batch_num)) + + for image_file in image_file_list: + img, flag, _ = check_and_read(image_file) + if not flag: + img = cv2.imread(image_file) + if img is None: + logger.info("error in loading image:{}".format(image_file)) + continue + valid_image_file_list.append(image_file) + img_list.append(img) + try: + rec_res, _ = text_recognizer(img_list) + + except Exception as E: + logger.info(traceback.format_exc()) + logger.info(E) + exit() + for ino in range(len(img_list)): + logger.info( + "Predicts of {}:{}".format(valid_image_file_list[ino], rec_res[ino]) + ) + if args.benchmark: + text_recognizer.autolog.report() + + +if __name__ == "__main__": + main(utility.parse_args()) diff --git a/modules/onnx_ocr_module/src/tools/infer/predict_sr.py b/modules/onnx_ocr_module/src/tools/infer/predict_sr.py new file mode 100644 index 0000000..101c775 --- /dev/null +++ b/modules/onnx_ocr_module/src/tools/infer/predict_sr.py @@ -0,0 +1,165 @@ +# Copyright (c) 2020 PaddlePaddle Authors. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +import os +import sys +from PIL import Image + +__dir__ = os.path.dirname(os.path.abspath(__file__)) +sys.path.insert(0, __dir__) +sys.path.insert(0, os.path.abspath(os.path.join(__dir__, "../.."))) + +os.environ["FLAGS_allocator_strategy"] = "auto_growth" + +import cv2 +import numpy as np +import math +import time +import traceback +import paddle + +import tools.infer.utility as utility +from ppocr.postprocess import build_post_process +from ppocr.utils.logging import get_logger +from ppocr.utils.utility import get_image_file_list, check_and_read + +logger = get_logger() + + +class TextSR(object): + def __init__(self, args): + self.sr_image_shape = [int(v) for v in args.sr_image_shape.split(",")] + self.sr_batch_num = args.sr_batch_num + + ( + self.predictor, + self.input_tensor, + self.output_tensors, + self.config, + ) = utility.create_predictor(args, "sr", logger) + self.benchmark = args.benchmark + if args.benchmark: + import auto_log + + pid = os.getpid() + gpu_id = utility.get_infer_gpuid() + self.autolog = auto_log.AutoLogger( + model_name="sr", + model_precision=args.precision, + batch_size=args.sr_batch_num, + data_shape="dynamic", + save_path=None, # args.save_log_path, + inference_config=self.config, + pids=pid, + process_name=None, + gpu_ids=gpu_id if args.use_gpu else None, + time_keys=["preprocess_time", "inference_time", "postprocess_time"], + warmup=0, + logger=logger, + ) + + def resize_norm_img(self, img): + imgC, imgH, imgW = self.sr_image_shape + img = img.resize((imgW // 2, imgH // 2), Image.BICUBIC) + img_numpy = np.array(img).astype("float32") + img_numpy = img_numpy.transpose((2, 0, 1)) / 255 + return img_numpy + + def __call__(self, img_list): + img_num = len(img_list) + batch_num = self.sr_batch_num + st = time.time() + st = time.time() + all_result = [] * img_num + if self.benchmark: + self.autolog.times.start() + for beg_img_no in range(0, img_num, batch_num): + end_img_no = min(img_num, beg_img_no + batch_num) + norm_img_batch = [] + imgC, imgH, imgW = self.sr_image_shape + for ino in range(beg_img_no, end_img_no): + norm_img = self.resize_norm_img(img_list[ino]) + norm_img = norm_img[np.newaxis, :] + norm_img_batch.append(norm_img) + + norm_img_batch = np.concatenate(norm_img_batch) + norm_img_batch = norm_img_batch.copy() + if self.benchmark: + self.autolog.times.stamp() + self.input_tensor.copy_from_cpu(norm_img_batch) + self.predictor.run() + outputs = [] + for output_tensor in self.output_tensors: + output = output_tensor.copy_to_cpu() + outputs.append(output) + if len(outputs) != 1: + preds = outputs + else: + preds = outputs[0] + all_result.append(outputs) + if self.benchmark: + self.autolog.times.end(stamp=True) + return all_result, time.time() - st + + +def main(args): + image_file_list = get_image_file_list(args.image_dir) + text_recognizer = TextSR(args) + valid_image_file_list = [] + img_list = [] + + # warmup 2 times + if args.warmup: + img = np.random.uniform(0, 255, [16, 64, 3]).astype(np.uint8) + for i in range(2): + res = text_recognizer([img] * int(args.sr_batch_num)) + + for image_file in image_file_list: + img, flag, _ = check_and_read(image_file) + if not flag: + img = Image.open(image_file).convert("RGB") + if img is None: + logger.info("error in loading image:{}".format(image_file)) + continue + valid_image_file_list.append(image_file) + img_list.append(img) + try: + preds, _ = text_recognizer(img_list) + for beg_no in range(len(preds)): + sr_img = preds[beg_no][1] + lr_img = preds[beg_no][0] + for i in range(sr_img.shape[0]): + fm_sr = (sr_img[i] * 255).transpose(1, 2, 0).astype(np.uint8) + fm_lr = (lr_img[i] * 255).transpose(1, 2, 0).astype(np.uint8) + img_name_pure = os.path.split( + valid_image_file_list[beg_no * args.sr_batch_num + i] + )[-1] + cv2.imwrite( + "infer_result/sr_{}".format(img_name_pure), fm_sr[:, :, ::-1] + ) + logger.info( + "The visualized image saved in infer_result/sr_{}".format( + img_name_pure + ) + ) + + except Exception as E: + logger.info(traceback.format_exc()) + logger.info(E) + exit() + if args.benchmark: + text_recognizer.autolog.report() + + +if __name__ == "__main__": + main(utility.parse_args()) diff --git a/modules/onnx_ocr_module/src/tools/infer/predict_system.py b/modules/onnx_ocr_module/src/tools/infer/predict_system.py new file mode 100644 index 0000000..bcb0758 --- /dev/null +++ b/modules/onnx_ocr_module/src/tools/infer/predict_system.py @@ -0,0 +1,326 @@ +# Copyright (c) 2020 PaddlePaddle Authors. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +import os +import sys +import subprocess + +__dir__ = os.path.dirname(os.path.abspath(__file__)) +sys.path.append(__dir__) +sys.path.insert(0, os.path.abspath(os.path.join(__dir__, "../.."))) + +os.environ["FLAGS_allocator_strategy"] = "auto_growth" + +import cv2 +import copy +import numpy as np +import json +import time +import logging +from PIL import Image +import tools.infer.utility as utility +import tools.infer.predict_rec as predict_rec +import tools.infer.predict_det as predict_det +import tools.infer.predict_cls as predict_cls +from ppocr.utils.utility import get_image_file_list, check_and_read +from ppocr.utils.logging import get_logger +from tools.infer.utility import ( + draw_ocr_box_txt, + get_rotate_crop_image, + get_minarea_rect_crop, + slice_generator, + merge_fragmented, +) + +logger = get_logger() + + +class TextSystem(object): + def __init__(self, args): + if not args.show_log: + logger.setLevel(logging.INFO) + + self.text_detector = predict_det.TextDetector(args) + self.text_recognizer = predict_rec.TextRecognizer(args) + self.use_angle_cls = args.use_angle_cls + self.drop_score = args.drop_score + if self.use_angle_cls: + self.text_classifier = predict_cls.TextClassifier(args) + + self.args = args + self.crop_image_res_index = 0 + + def draw_crop_rec_res(self, output_dir, img_crop_list, rec_res): + os.makedirs(output_dir, exist_ok=True) + bbox_num = len(img_crop_list) + for bno in range(bbox_num): + cv2.imwrite( + os.path.join( + output_dir, f"mg_crop_{bno+self.crop_image_res_index}.jpg" + ), + img_crop_list[bno], + ) + logger.debug(f"{bno}, {rec_res[bno]}") + self.crop_image_res_index += bbox_num + + def __call__(self, img, cls=True, slice={}): + time_dict = {"det": 0, "rec": 0, "cls": 0, "all": 0} + + if img is None: + logger.debug("no valid image provided") + return None, None, time_dict + + start = time.time() + ori_im = img.copy() + if slice: + slice_gen = slice_generator( + img, + horizontal_stride=slice["horizontal_stride"], + vertical_stride=slice["vertical_stride"], + ) + elapsed = [] + dt_slice_boxes = [] + for slice_crop, v_start, h_start in slice_gen: + dt_boxes, elapse = self.text_detector(slice_crop, use_slice=True) + if dt_boxes.size: + dt_boxes[:, :, 0] += h_start + dt_boxes[:, :, 1] += v_start + dt_slice_boxes.append(dt_boxes) + elapsed.append(elapse) + dt_boxes = np.concatenate(dt_slice_boxes) + + dt_boxes = merge_fragmented( + boxes=dt_boxes, + x_threshold=slice["merge_x_thres"], + y_threshold=slice["merge_y_thres"], + ) + elapse = sum(elapsed) + else: + dt_boxes, elapse = self.text_detector(img) + + time_dict["det"] = elapse + + if dt_boxes is None: + logger.debug("no dt_boxes found, elapsed : {}".format(elapse)) + end = time.time() + time_dict["all"] = end - start + return None, None, time_dict + else: + logger.debug( + "dt_boxes num : {}, elapsed : {}".format(len(dt_boxes), elapse) + ) + img_crop_list = [] + + dt_boxes = sorted_boxes(dt_boxes) + + for bno in range(len(dt_boxes)): + tmp_box = copy.deepcopy(dt_boxes[bno]) + if self.args.det_box_type == "quad": + img_crop = get_rotate_crop_image(ori_im, tmp_box) + else: + img_crop = get_minarea_rect_crop(ori_im, tmp_box) + img_crop_list.append(img_crop) + if self.use_angle_cls and cls: + img_crop_list, angle_list, elapse = self.text_classifier(img_crop_list) + time_dict["cls"] = elapse + logger.debug( + "cls num : {}, elapsed : {}".format(len(img_crop_list), elapse) + ) + if len(img_crop_list) > 1000: + logger.debug( + f"rec crops num: {len(img_crop_list)}, time and memory cost may be large." + ) + + rec_res, elapse = self.text_recognizer(img_crop_list) + time_dict["rec"] = elapse + logger.debug("rec_res num : {}, elapsed : {}".format(len(rec_res), elapse)) + if self.args.save_crop_res: + self.draw_crop_rec_res(self.args.crop_res_save_dir, img_crop_list, rec_res) + filter_boxes, filter_rec_res = [], [] + for box, rec_result in zip(dt_boxes, rec_res): + text, score = rec_result[0], rec_result[1] + if score >= self.drop_score: + filter_boxes.append(box) + filter_rec_res.append(rec_result) + end = time.time() + time_dict["all"] = end - start + return filter_boxes, filter_rec_res, time_dict + + +def sorted_boxes(dt_boxes): + """ + Sort text boxes in order from top to bottom, left to right + args: + dt_boxes(array):detected text boxes with shape [4, 2] + return: + sorted boxes(array) with shape [4, 2] + """ + num_boxes = dt_boxes.shape[0] + sorted_boxes = sorted(dt_boxes, key=lambda x: (x[0][1], x[0][0])) + _boxes = list(sorted_boxes) + + for i in range(num_boxes - 1): + for j in range(i, -1, -1): + if abs(_boxes[j + 1][0][1] - _boxes[j][0][1]) < 10 and ( + _boxes[j + 1][0][0] < _boxes[j][0][0] + ): + tmp = _boxes[j] + _boxes[j] = _boxes[j + 1] + _boxes[j + 1] = tmp + else: + break + return _boxes + + +def main(args): + image_file_list = get_image_file_list(args.image_dir) + image_file_list = image_file_list[args.process_id :: args.total_process_num] + text_sys = TextSystem(args) + is_visualize = True + font_path = args.vis_font_path + drop_score = args.drop_score + draw_img_save_dir = args.draw_img_save_dir + os.makedirs(draw_img_save_dir, exist_ok=True) + save_results = [] + + logger.info( + "In PP-OCRv3, rec_image_shape parameter defaults to '3, 48, 320', " + "if you are using recognition model with PP-OCRv2 or an older version, please set --rec_image_shape='3,32,320" + ) + + # warm up 10 times + if args.warmup: + img = np.random.uniform(0, 255, [640, 640, 3]).astype(np.uint8) + for i in range(10): + res = text_sys(img) + + total_time = 0 + cpu_mem, gpu_mem, gpu_util = 0, 0, 0 + _st = time.time() + count = 0 + for idx, image_file in enumerate(image_file_list): + img, flag_gif, flag_pdf = check_and_read(image_file) + if not flag_gif and not flag_pdf: + img = cv2.imread(image_file) + if not flag_pdf: + if img is None: + logger.debug("error in loading image:{}".format(image_file)) + continue + imgs = [img] + else: + page_num = args.page_num + if page_num > len(img) or page_num == 0: + page_num = len(img) + imgs = img[:page_num] + for index, img in enumerate(imgs): + starttime = time.time() + dt_boxes, rec_res, time_dict = text_sys(img) + elapse = time.time() - starttime + total_time += elapse + if len(imgs) > 1: + logger.debug( + str(idx) + + "_" + + str(index) + + " Predict time of %s: %.3fs" % (image_file, elapse) + ) + else: + logger.debug( + str(idx) + " Predict time of %s: %.3fs" % (image_file, elapse) + ) + for text, score in rec_res: + logger.debug("{}, {:.3f}".format(text, score)) + + res = [ + { + "transcription": rec_res[i][0], + "points": np.array(dt_boxes[i]).astype(np.int32).tolist(), + } + for i in range(len(dt_boxes)) + ] + if len(imgs) > 1: + save_pred = ( + os.path.basename(image_file) + + "_" + + str(index) + + "\t" + + json.dumps(res, ensure_ascii=False) + + "\n" + ) + else: + save_pred = ( + os.path.basename(image_file) + + "\t" + + json.dumps(res, ensure_ascii=False) + + "\n" + ) + save_results.append(save_pred) + + if is_visualize: + image = Image.fromarray(cv2.cvtColor(img, cv2.COLOR_BGR2RGB)) + boxes = dt_boxes + txts = [rec_res[i][0] for i in range(len(rec_res))] + scores = [rec_res[i][1] for i in range(len(rec_res))] + + draw_img = draw_ocr_box_txt( + image, + boxes, + txts, + scores, + drop_score=drop_score, + font_path=font_path, + ) + if flag_gif: + save_file = image_file[:-3] + "png" + elif flag_pdf: + save_file = image_file.replace(".pdf", "_" + str(index) + ".png") + else: + save_file = image_file + cv2.imwrite( + os.path.join(draw_img_save_dir, os.path.basename(save_file)), + draw_img[:, :, ::-1], + ) + logger.debug( + "The visualized image saved in {}".format( + os.path.join(draw_img_save_dir, os.path.basename(save_file)) + ) + ) + + logger.info("The predict total time is {}".format(time.time() - _st)) + if args.benchmark: + text_sys.text_detector.autolog.report() + text_sys.text_recognizer.autolog.report() + + with open( + os.path.join(draw_img_save_dir, "system_results.txt"), "w", encoding="utf-8" + ) as f: + f.writelines(save_results) + + +if __name__ == "__main__": + args = utility.parse_args() + if args.use_mp: + p_list = [] + total_process_num = args.total_process_num + for process_id in range(total_process_num): + cmd = ( + [sys.executable, "-u"] + + sys.argv + + ["--process_id={}".format(process_id), "--use_mp={}".format(False)] + ) + p = subprocess.Popen(cmd, stdout=sys.stdout, stderr=sys.stdout) + p_list.append(p) + for p in p_list: + p.wait() + else: + main(args) diff --git a/modules/onnx_ocr_module/src/tools/infer/utility.py b/modules/onnx_ocr_module/src/tools/infer/utility.py new file mode 100644 index 0000000..21d1c86 --- /dev/null +++ b/modules/onnx_ocr_module/src/tools/infer/utility.py @@ -0,0 +1,875 @@ +# Copyright (c) 2020 PaddlePaddle Authors. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import argparse +import os +import sys +import cv2 +import numpy as np +# ONNX-only runtime +import PIL +from PIL import Image, ImageDraw, ImageFont +import math +# Remove paddle inference in ONNX mode; keep names guarded in non-ONNX branches +import random +from ppocr.utils.logging import get_logger + + +def str2bool(v): + return v.lower() in ("true", "yes", "t", "y", "1") + + +def str2int_tuple(v): + return tuple([int(i.strip()) for i in v.split(",")]) + + +def init_args(): + parser = argparse.ArgumentParser() + # params for prediction engine + parser.add_argument("--use_gpu", type=str2bool, default=True) + parser.add_argument("--use_xpu", type=str2bool, default=False) + parser.add_argument("--use_npu", type=str2bool, default=False) + parser.add_argument("--use_mlu", type=str2bool, default=False) + parser.add_argument( + "--use_gcu", + type=str2bool, + default=False, + help="Use Enflame GCU(General Compute Unit)", + ) + parser.add_argument("--ir_optim", type=str2bool, default=True) + parser.add_argument("--use_tensorrt", type=str2bool, default=False) + parser.add_argument("--min_subgraph_size", type=int, default=15) + parser.add_argument("--precision", type=str, default="fp32") + parser.add_argument("--gpu_mem", type=int, default=500) + parser.add_argument("--gpu_id", type=int, default=0) + + # params for text detector + parser.add_argument("--image_dir", type=str) + parser.add_argument("--page_num", type=int, default=0) + parser.add_argument("--det_algorithm", type=str, default="DB") + parser.add_argument("--det_model_dir", type=str) + parser.add_argument("--det_limit_side_len", type=float, default=960) + parser.add_argument("--det_limit_type", type=str, default="max") + parser.add_argument("--det_box_type", type=str, default="quad") + + # DB params + parser.add_argument("--det_db_thresh", type=float, default=0.3) + parser.add_argument("--det_db_box_thresh", type=float, default=0.6) + parser.add_argument("--det_db_unclip_ratio", type=float, default=1.5) + parser.add_argument("--max_batch_size", type=int, default=10) + parser.add_argument("--use_dilation", type=str2bool, default=False) + parser.add_argument("--det_db_score_mode", type=str, default="fast") + + # EAST params + parser.add_argument("--det_east_score_thresh", type=float, default=0.8) + parser.add_argument("--det_east_cover_thresh", type=float, default=0.1) + parser.add_argument("--det_east_nms_thresh", type=float, default=0.2) + + # SAST params + parser.add_argument("--det_sast_score_thresh", type=float, default=0.5) + parser.add_argument("--det_sast_nms_thresh", type=float, default=0.2) + + # PSE params + parser.add_argument("--det_pse_thresh", type=float, default=0) + parser.add_argument("--det_pse_box_thresh", type=float, default=0.85) + parser.add_argument("--det_pse_min_area", type=float, default=16) + parser.add_argument("--det_pse_scale", type=int, default=1) + + # FCE params + parser.add_argument("--scales", type=list, default=[8, 16, 32]) + parser.add_argument("--alpha", type=float, default=1.0) + parser.add_argument("--beta", type=float, default=1.0) + parser.add_argument("--fourier_degree", type=int, default=5) + + # params for text recognizer + parser.add_argument("--rec_algorithm", type=str, default="SVTR_LCNet") + parser.add_argument("--rec_model_dir", type=str) + parser.add_argument("--rec_image_inverse", type=str2bool, default=True) + parser.add_argument("--rec_image_shape", type=str, default="3, 48, 320") + parser.add_argument("--rec_batch_num", type=int, default=6) + parser.add_argument("--max_text_length", type=int, default=25) + parser.add_argument( + "--rec_char_dict_path", type=str, default="./ppocr/utils/ppocr_keys_v1.txt" + ) + parser.add_argument("--use_space_char", type=str2bool, default=True) + parser.add_argument("--vis_font_path", type=str, default="./doc/fonts/simfang.ttf") + parser.add_argument("--drop_score", type=float, default=0.5) + + # params for e2e + parser.add_argument("--e2e_algorithm", type=str, default="PGNet") + parser.add_argument("--e2e_model_dir", type=str) + parser.add_argument("--e2e_limit_side_len", type=float, default=768) + parser.add_argument("--e2e_limit_type", type=str, default="max") + + # PGNet params + parser.add_argument("--e2e_pgnet_score_thresh", type=float, default=0.5) + parser.add_argument( + "--e2e_char_dict_path", type=str, default="./ppocr/utils/ic15_dict.txt" + ) + parser.add_argument("--e2e_pgnet_valid_set", type=str, default="totaltext") + parser.add_argument("--e2e_pgnet_mode", type=str, default="fast") + + # params for text classifier + parser.add_argument("--use_angle_cls", type=str2bool, default=False) + parser.add_argument("--cls_model_dir", type=str) + parser.add_argument("--cls_image_shape", type=str, default="3, 48, 192") + parser.add_argument("--label_list", type=list, default=["0", "180"]) + parser.add_argument("--cls_batch_num", type=int, default=6) + parser.add_argument("--cls_thresh", type=float, default=0.9) + + parser.add_argument("--enable_mkldnn", type=str2bool, default=False) + parser.add_argument("--cpu_threads", type=int, default=10) + parser.add_argument("--use_pdserving", type=str2bool, default=False) + parser.add_argument("--warmup", type=str2bool, default=False) + + # SR params + parser.add_argument("--sr_model_dir", type=str) + parser.add_argument("--sr_image_shape", type=str, default="3, 32, 128") + parser.add_argument("--sr_batch_num", type=int, default=1) + + # + parser.add_argument("--draw_img_save_dir", type=str, default="./inference_results") + parser.add_argument("--save_crop_res", type=str2bool, default=False) + parser.add_argument("--crop_res_save_dir", type=str, default="./output") + + # multi-process + parser.add_argument("--use_mp", type=str2bool, default=False) + parser.add_argument("--total_process_num", type=int, default=1) + parser.add_argument("--process_id", type=int, default=0) + + parser.add_argument("--benchmark", type=str2bool, default=False) + parser.add_argument("--save_log_path", type=str, default="./log_output/") + + parser.add_argument("--show_log", type=str2bool, default=True) + parser.add_argument("--use_onnx", type=str2bool, default=False) + parser.add_argument("--onnx_providers", nargs="+", type=str, default=False) + parser.add_argument("--onnx_sess_options", type=list, default=False) + + # extended function + parser.add_argument( + "--return_word_box", + type=str2bool, + default=False, + help="Whether return the bbox of each word (split by space) or chinese character. Only used in ppstructure for layout recovery", + ) + + return parser + + +def parse_args(): + parser = init_args() + return parser.parse_args() + + +def create_predictor(args, mode, logger): + if mode == "det": + model_dir = args.det_model_dir + elif mode == "cls": + model_dir = args.cls_model_dir + elif mode == "rec": + model_dir = args.rec_model_dir + elif mode == "table": + model_dir = args.table_model_dir + elif mode == "ser": + model_dir = args.ser_model_dir + elif mode == "re": + model_dir = args.re_model_dir + elif mode == "sr": + model_dir = args.sr_model_dir + elif mode == "layout": + model_dir = args.layout_model_dir + else: + model_dir = args.e2e_model_dir + + if model_dir is None: + logger.info("not find {} model file path {}".format(mode, model_dir)) + sys.exit(0) + if args.use_onnx: + import onnxruntime as ort + + model_file_path = model_dir + if not os.path.exists(model_file_path): + raise ValueError("not find model file path {}".format(model_file_path)) + + sess_options = args.onnx_sess_options or None + + if args.onnx_providers and len(args.onnx_providers) > 0: + sess = ort.InferenceSession( + model_file_path, + providers=args.onnx_providers, + sess_options=sess_options, + ) + elif args.use_gpu: + sess = ort.InferenceSession( + model_file_path, + providers=[ + ( + "CUDAExecutionProvider", + {"device_id": args.gpu_id, "cudnn_conv_algo_search": "DEFAULT"}, + ) + ], + sess_options=sess_options, + ) + else: + sess = ort.InferenceSession( + model_file_path, + providers=["CPUExecutionProvider"], + sess_options=sess_options, + ) + inputs = sess.get_inputs() + return ( + sess, + inputs[0] if len(inputs) == 1 else [vo.name for vo in inputs], + None, + None, + ) + + else: + from paddle import inference # lazy import to avoid top-level paddle dependency + file_names = ["model", "inference"] + for file_name in file_names: + params_file_path = f"{model_dir}/{file_name}.pdiparams" + if os.path.exists(params_file_path): + break + + if not os.path.exists(params_file_path): + raise ValueError(f"not find {file_name}.pdiparams in {model_dir}") + + if not ( + os.path.exists(f"{model_dir}/{file_name}.pdmodel") + or os.path.exists(f"{model_dir}/{file_name}.json") + ): + raise ValueError( + f"neither {file_name}.json nor {file_name}.pdmodel was found in {model_dir}." + ) + + if os.path.exists(f"{model_dir}/{file_name}.json"): + model_file_path = f"{model_dir}/{file_name}.json" + else: + model_file_path = f"{model_dir}/{file_name}.pdmodel" + + config = inference.Config(model_file_path, params_file_path) + + if hasattr(args, "precision"): + if args.precision == "fp16" and args.use_tensorrt: + precision = inference.PrecisionType.Half + elif args.precision == "int8": + precision = inference.PrecisionType.Int8 + else: + precision = inference.PrecisionType.Float32 + else: + precision = inference.PrecisionType.Float32 + + if args.use_gpu: + gpu_id = get_infer_gpuid() + if gpu_id is None: + logger.warning( + "GPU is not found in current device by nvidia-smi. Please check your device or ignore it if run on jetson." + ) + config.enable_use_gpu(args.gpu_mem, args.gpu_id) + if args.use_tensorrt: + config.enable_tensorrt_engine( + workspace_size=1 << 30, + precision_mode=precision, + max_batch_size=args.max_batch_size, + min_subgraph_size=args.min_subgraph_size, # skip the minimum trt subgraph + use_calib_mode=False, + ) + + # collect shape + trt_shape_f = os.path.join(model_dir, f"{mode}_trt_dynamic_shape.txt") + + if not os.path.exists(trt_shape_f): + config.collect_shape_range_info(trt_shape_f) + logger.info(f"collect dynamic shape info into : {trt_shape_f}") + try: + config.enable_tuned_tensorrt_dynamic_shape(trt_shape_f, True) + except Exception as E: + logger.info(E) + logger.info("Please keep your paddlepaddle-gpu >= 2.3.0!") + + elif args.use_npu: + config.enable_custom_device("npu") + elif args.use_mlu: + config.enable_custom_device("mlu") + elif args.use_xpu: + config.enable_xpu(10 * 1024 * 1024) + elif args.use_gcu: # for Enflame GCU(General Compute Unit) + assert paddle.device.is_compiled_with_custom_device("gcu"), ( + "Args use_gcu cannot be set as True while your paddle " + "is not compiled with gcu! \nPlease try: \n" + "\t1. Install paddle-custom-gcu to run model on GCU. \n" + "\t2. Set use_gcu as False in args to run model on CPU." + ) + import paddle_custom_device.gcu.passes as gcu_passes + + gcu_passes.setUp() + if args.precision == "fp16": + config.enable_custom_device( + "gcu", 0, paddle.inference.PrecisionType.Half + ) + gcu_passes.set_exp_enable_mixed_precision_ops(config) + else: + config.enable_custom_device("gcu") + + if paddle.framework.use_pir_api(): + config.enable_new_ir(True) + config.enable_new_executor(True) + else: + pass_builder = config.pass_builder() + gcu_passes.append_passes_for_legacy_ir(pass_builder, "PaddleOCR") + else: + config.disable_gpu() + if args.enable_mkldnn: + # cache 10 different shapes for mkldnn to avoid memory leak + config.set_mkldnn_cache_capacity(10) + config.enable_mkldnn() + if args.precision == "fp16": + config.enable_mkldnn_bfloat16() + if hasattr(args, "cpu_threads"): + config.set_cpu_math_library_num_threads(args.cpu_threads) + else: + # default cpu threads as 10 + config.set_cpu_math_library_num_threads(10) + # enable memory optim + config.enable_memory_optim() + config.disable_glog_info() + if not args.use_gcu: # for Enflame GCU(General Compute Unit) + config.delete_pass("conv_transpose_eltwiseadd_bn_fuse_pass") + config.delete_pass("matmul_transpose_reshape_fuse_pass") + if mode == "rec" and args.rec_algorithm == "SRN": + config.delete_pass("gpu_cpu_map_matmul_v2_to_matmul_pass") + if mode == "re": + config.delete_pass("simplify_with_basic_ops_pass") + if mode == "table": + config.delete_pass("fc_fuse_pass") # not supported for table + config.switch_use_feed_fetch_ops(False) + config.switch_ir_optim(True) + + # create predictor + predictor = inference.create_predictor(config) + input_names = predictor.get_input_names() + if mode in ["ser", "re"]: + input_tensor = [] + for name in input_names: + input_tensor.append(predictor.get_input_handle(name)) + else: + for name in input_names: + input_tensor = predictor.get_input_handle(name) + output_tensors = get_output_tensors(args, mode, predictor) + return predictor, input_tensor, output_tensors, config + + +def get_output_tensors(args, mode, predictor): + output_names = predictor.get_output_names() + output_tensors = [] + if mode == "rec" and args.rec_algorithm in ["CRNN", "SVTR_LCNet", "SVTR_HGNet"]: + output_name = "softmax_0.tmp_0" + if output_name in output_names: + return [predictor.get_output_handle(output_name)] + else: + for output_name in output_names: + output_tensor = predictor.get_output_handle(output_name) + output_tensors.append(output_tensor) + else: + for output_name in output_names: + output_tensor = predictor.get_output_handle(output_name) + output_tensors.append(output_tensor) + return output_tensors + + +def get_infer_gpuid(): + """ + Get the GPU ID to be used for inference. + + Returns: + int: The GPU ID to be used for inference. + """ + logger = get_logger() + try: + import paddle + is_rocm = not paddle.device.is_compiled_with_rocm + except Exception: + is_rocm = True + if is_rocm: + gpu_id_str = os.environ.get("CUDA_VISIBLE_DEVICES", "0") + else: + gpu_id_str = os.environ.get("HIP_VISIBLE_DEVICES", "0") + + gpu_ids = gpu_id_str.split(",") + logger.warning( + "The first GPU is used for inference by default, GPU ID: {}".format(gpu_ids[0]) + ) + return int(gpu_ids[0]) + + +def draw_e2e_res(dt_boxes, strs, img_path): + src_im = cv2.imread(img_path) + for box, str in zip(dt_boxes, strs): + box = box.astype(np.int32).reshape((-1, 1, 2)) + cv2.polylines(src_im, [box], True, color=(255, 255, 0), thickness=2) + cv2.putText( + src_im, + str, + org=(int(box[0, 0, 0]), int(box[0, 0, 1])), + fontFace=cv2.FONT_HERSHEY_COMPLEX, + fontScale=0.7, + color=(0, 255, 0), + thickness=1, + ) + return src_im + + +def draw_text_det_res(dt_boxes, img): + for box in dt_boxes: + box = np.array(box).astype(np.int32).reshape(-1, 2) + cv2.polylines(img, [box], True, color=(255, 255, 0), thickness=2) + return img + + +def resize_img(img, input_size=600): + """ + resize img and limit the longest side of the image to input_size + """ + img = np.array(img) + im_shape = img.shape + im_size_max = np.max(im_shape[0:2]) + im_scale = float(input_size) / float(im_size_max) + img = cv2.resize(img, None, None, fx=im_scale, fy=im_scale) + return img + + +def draw_ocr( + image, + boxes, + txts=None, + scores=None, + drop_score=0.5, + font_path="./doc/fonts/simfang.ttf", +): + """ + Visualize the results of OCR detection and recognition + args: + image(Image|array): RGB image + boxes(list): boxes with shape(N, 4, 2) + txts(list): the texts + scores(list): txxs corresponding scores + drop_score(float): only scores greater than drop_threshold will be visualized + font_path: the path of font which is used to draw text + return(array): + the visualized img + """ + if scores is None: + scores = [1] * len(boxes) + box_num = len(boxes) + for i in range(box_num): + if scores is not None and (scores[i] < drop_score or math.isnan(scores[i])): + continue + box = np.reshape(np.array(boxes[i]), [-1, 1, 2]).astype(np.int64) + image = cv2.polylines(np.array(image), [box], True, (255, 0, 0), 2) + if txts is not None: + img = np.array(resize_img(image, input_size=600)) + txt_img = text_visual( + txts, + scores, + img_h=img.shape[0], + img_w=600, + threshold=drop_score, + font_path=font_path, + ) + img = np.concatenate([np.array(img), np.array(txt_img)], axis=1) + return img + return image + + +def draw_ocr_box_txt( + image, + boxes, + txts=None, + scores=None, + drop_score=0.5, + font_path="./doc/fonts/simfang.ttf", +): + h, w = image.height, image.width + img_left = image.copy() + img_right = np.ones((h, w, 3), dtype=np.uint8) * 255 + random.seed(0) + + draw_left = ImageDraw.Draw(img_left) + if txts is None or len(txts) != len(boxes): + txts = [None] * len(boxes) + for idx, (box, txt) in enumerate(zip(boxes, txts)): + if scores is not None and scores[idx] < drop_score: + continue + color = (random.randint(0, 255), random.randint(0, 255), random.randint(0, 255)) + draw_left.polygon(box, fill=color) + img_right_text = draw_box_txt_fine((w, h), box, txt, font_path) + pts = np.array(box, np.int32).reshape((-1, 1, 2)) + cv2.polylines(img_right_text, [pts], True, color, 1) + img_right = cv2.bitwise_and(img_right, img_right_text) + img_left = Image.blend(image, img_left, 0.5) + img_show = Image.new("RGB", (w * 2, h), (255, 255, 255)) + img_show.paste(img_left, (0, 0, w, h)) + img_show.paste(Image.fromarray(img_right), (w, 0, w * 2, h)) + return np.array(img_show) + + +def draw_box_txt_fine(img_size, box, txt, font_path="./doc/fonts/simfang.ttf"): + box_height = int( + math.sqrt((box[0][0] - box[3][0]) ** 2 + (box[0][1] - box[3][1]) ** 2) + ) + box_width = int( + math.sqrt((box[0][0] - box[1][0]) ** 2 + (box[0][1] - box[1][1]) ** 2) + ) + + if box_height > 2 * box_width and box_height > 30: + img_text = Image.new("RGB", (box_height, box_width), (255, 255, 255)) + draw_text = ImageDraw.Draw(img_text) + if txt: + font = create_font(txt, (box_height, box_width), font_path) + draw_text.text([0, 0], txt, fill=(0, 0, 0), font=font) + img_text = img_text.transpose(Image.ROTATE_270) + else: + img_text = Image.new("RGB", (box_width, box_height), (255, 255, 255)) + draw_text = ImageDraw.Draw(img_text) + if txt: + font = create_font(txt, (box_width, box_height), font_path) + draw_text.text([0, 0], txt, fill=(0, 0, 0), font=font) + + pts1 = np.float32( + [[0, 0], [box_width, 0], [box_width, box_height], [0, box_height]] + ) + pts2 = np.array(box, dtype=np.float32) + M = cv2.getPerspectiveTransform(pts1, pts2) + + img_text = np.array(img_text, dtype=np.uint8) + img_right_text = cv2.warpPerspective( + img_text, + M, + img_size, + flags=cv2.INTER_NEAREST, + borderMode=cv2.BORDER_CONSTANT, + borderValue=(255, 255, 255), + ) + return img_right_text + + +def create_font(txt, sz, font_path="./doc/fonts/simfang.ttf"): + font_size = int(sz[1] * 0.99) + font = ImageFont.truetype(font_path, font_size, encoding="utf-8") + if int(PIL.__version__.split(".")[0]) < 10: + length = font.getsize(txt)[0] + else: + length = font.getlength(txt) + + if length > sz[0]: + font_size = int(font_size * sz[0] / length) + font = ImageFont.truetype(font_path, font_size, encoding="utf-8") + return font + + +def str_count(s): + """ + Count the number of Chinese characters, + a single English character and a single number + equal to half the length of Chinese characters. + args: + s(string): the input of string + return(int): + the number of Chinese characters + """ + import string + + count_zh = count_pu = 0 + s_len = len(s) + en_dg_count = 0 + for c in s: + if c in string.ascii_letters or c.isdigit() or c.isspace(): + en_dg_count += 1 + elif c.isalpha(): + count_zh += 1 + else: + count_pu += 1 + return s_len - math.ceil(en_dg_count / 2) + + +def text_visual( + texts, scores, img_h=400, img_w=600, threshold=0.0, font_path="./doc/simfang.ttf" +): + """ + create new blank img and draw txt on it + args: + texts(list): the text will be draw + scores(list|None): corresponding score of each txt + img_h(int): the height of blank img + img_w(int): the width of blank img + font_path: the path of font which is used to draw text + return(array): + """ + if scores is not None: + assert len(texts) == len( + scores + ), "The number of txts and corresponding scores must match" + + def create_blank_img(): + blank_img = np.ones(shape=[img_h, img_w], dtype=np.uint8) * 255 + blank_img[:, img_w - 1 :] = 0 + blank_img = Image.fromarray(blank_img).convert("RGB") + draw_txt = ImageDraw.Draw(blank_img) + return blank_img, draw_txt + + blank_img, draw_txt = create_blank_img() + + font_size = 20 + txt_color = (0, 0, 0) + font = ImageFont.truetype(font_path, font_size, encoding="utf-8") + + gap = font_size + 5 + txt_img_list = [] + count, index = 1, 0 + for idx, txt in enumerate(texts): + index += 1 + if scores[idx] < threshold or math.isnan(scores[idx]): + index -= 1 + continue + first_line = True + while str_count(txt) >= img_w // font_size - 4: + tmp = txt + txt = tmp[: img_w // font_size - 4] + if first_line: + new_txt = str(index) + ": " + txt + first_line = False + else: + new_txt = " " + txt + draw_txt.text((0, gap * count), new_txt, txt_color, font=font) + txt = tmp[img_w // font_size - 4 :] + if count >= img_h // gap - 1: + txt_img_list.append(np.array(blank_img)) + blank_img, draw_txt = create_blank_img() + count = 0 + count += 1 + if first_line: + new_txt = str(index) + ": " + txt + " " + "%.3f" % (scores[idx]) + else: + new_txt = " " + txt + " " + "%.3f" % (scores[idx]) + draw_txt.text((0, gap * count), new_txt, txt_color, font=font) + # whether add new blank img or not + if count >= img_h // gap - 1 and idx + 1 < len(texts): + txt_img_list.append(np.array(blank_img)) + blank_img, draw_txt = create_blank_img() + count = 0 + count += 1 + txt_img_list.append(np.array(blank_img)) + if len(txt_img_list) == 1: + blank_img = np.array(txt_img_list[0]) + else: + blank_img = np.concatenate(txt_img_list, axis=1) + return np.array(blank_img) + + +def base64_to_cv2(b64str): + import base64 + + data = base64.b64decode(b64str.encode("utf8")) + data = np.frombuffer(data, np.uint8) + data = cv2.imdecode(data, cv2.IMREAD_COLOR) + return data + + +def draw_boxes(image, boxes, scores=None, drop_score=0.5): + if scores is None: + scores = [1] * len(boxes) + for box, score in zip(boxes, scores): + if score < drop_score: + continue + box = np.reshape(np.array(box), [-1, 1, 2]).astype(np.int64) + image = cv2.polylines(np.array(image), [box], True, (255, 0, 0), 2) + return image + + +def get_rotate_crop_image(img, points): + """ + img_height, img_width = img.shape[0:2] + left = int(np.min(points[:, 0])) + right = int(np.max(points[:, 0])) + top = int(np.min(points[:, 1])) + bottom = int(np.max(points[:, 1])) + img_crop = img[top:bottom, left:right, :].copy() + points[:, 0] = points[:, 0] - left + points[:, 1] = points[:, 1] - top + """ + assert len(points) == 4, "shape of points must be 4*2" + img_crop_width = int( + max( + np.linalg.norm(points[0] - points[1]), np.linalg.norm(points[2] - points[3]) + ) + ) + img_crop_height = int( + max( + np.linalg.norm(points[0] - points[3]), np.linalg.norm(points[1] - points[2]) + ) + ) + pts_std = np.float32( + [ + [0, 0], + [img_crop_width, 0], + [img_crop_width, img_crop_height], + [0, img_crop_height], + ] + ) + M = cv2.getPerspectiveTransform(points, pts_std) + dst_img = cv2.warpPerspective( + img, + M, + (img_crop_width, img_crop_height), + borderMode=cv2.BORDER_REPLICATE, + flags=cv2.INTER_CUBIC, + ) + dst_img_height, dst_img_width = dst_img.shape[0:2] + if dst_img_height * 1.0 / dst_img_width >= 1.5: + dst_img = np.rot90(dst_img) + return dst_img + + +def get_minarea_rect_crop(img, points): + bounding_box = cv2.minAreaRect(np.array(points).astype(np.int32)) + points = sorted(list(cv2.boxPoints(bounding_box)), key=lambda x: x[0]) + + index_a, index_b, index_c, index_d = 0, 1, 2, 3 + if points[1][1] > points[0][1]: + index_a = 0 + index_d = 1 + else: + index_a = 1 + index_d = 0 + if points[3][1] > points[2][1]: + index_b = 2 + index_c = 3 + else: + index_b = 3 + index_c = 2 + + box = [points[index_a], points[index_b], points[index_c], points[index_d]] + crop_img = get_rotate_crop_image(img, np.array(box)) + return crop_img + + +def slice_generator(image, horizontal_stride, vertical_stride, maximum_slices=500): + if not isinstance(image, np.ndarray): + image = np.array(image) + + image_h, image_w = image.shape[:2] + vertical_num_slices = (image_h + vertical_stride - 1) // vertical_stride + horizontal_num_slices = (image_w + horizontal_stride - 1) // horizontal_stride + + assert ( + vertical_num_slices > 0 + ), f"Invalid number ({vertical_num_slices}) of vertical slices" + + assert ( + horizontal_num_slices > 0 + ), f"Invalid number ({horizontal_num_slices}) of horizontal slices" + + if vertical_num_slices >= maximum_slices: + recommended_vertical_stride = max(1, image_h // maximum_slices) + 1 + assert ( + False + ), f"Too computationally expensive with {vertical_num_slices} slices, try a higher vertical stride (recommended minimum: {recommended_vertical_stride})" + + if horizontal_num_slices >= maximum_slices: + recommended_horizontal_stride = max(1, image_w // maximum_slices) + 1 + assert ( + False + ), f"Too computationally expensive with {horizontal_num_slices} slices, try a higher horizontal stride (recommended minimum: {recommended_horizontal_stride})" + + for v_slice_idx in range(vertical_num_slices): + v_start = max(0, (v_slice_idx * vertical_stride)) + v_end = min(((v_slice_idx + 1) * vertical_stride), image_h) + vertical_slice = image[v_start:v_end, :] + for h_slice_idx in range(horizontal_num_slices): + h_start = max(0, (h_slice_idx * horizontal_stride)) + h_end = min(((h_slice_idx + 1) * horizontal_stride), image_w) + horizontal_slice = vertical_slice[:, h_start:h_end] + + yield (horizontal_slice, v_start, h_start) + + +def calculate_box_extents(box): + min_x = box[0][0] + max_x = box[1][0] + min_y = box[0][1] + max_y = box[2][1] + return min_x, max_x, min_y, max_y + + +def merge_boxes(box1, box2, x_threshold, y_threshold): + min_x1, max_x1, min_y1, max_y1 = calculate_box_extents(box1) + min_x2, max_x2, min_y2, max_y2 = calculate_box_extents(box2) + + if ( + abs(min_y1 - min_y2) <= y_threshold + and abs(max_y1 - max_y2) <= y_threshold + and abs(max_x1 - min_x2) <= x_threshold + ): + new_xmin = min(min_x1, min_x2) + new_xmax = max(max_x1, max_x2) + new_ymin = min(min_y1, min_y2) + new_ymax = max(max_y1, max_y2) + return [ + [new_xmin, new_ymin], + [new_xmax, new_ymin], + [new_xmax, new_ymax], + [new_xmin, new_ymax], + ] + else: + return None + + +def merge_fragmented(boxes, x_threshold=10, y_threshold=10): + merged_boxes = [] + visited = set() + + for i, box1 in enumerate(boxes): + if i in visited: + continue + + merged_box = [point[:] for point in box1] + + for j, box2 in enumerate(boxes[i + 1 :], start=i + 1): + if j not in visited: + merged_result = merge_boxes( + merged_box, box2, x_threshold=x_threshold, y_threshold=y_threshold + ) + if merged_result: + merged_box = merged_result + visited.add(j) + + merged_boxes.append(merged_box) + + if len(merged_boxes) == len(boxes): + return np.array(merged_boxes) + else: + return merge_fragmented(merged_boxes, x_threshold, y_threshold) + + +def check_gpu(use_gpu): + if use_gpu and ( + not paddle.is_compiled_with_cuda() or paddle.device.get_device() == "cpu" + ): + use_gpu = False + return use_gpu + + +if __name__ == "__main__": + pass diff --git a/modules/onnx_ocr_module/src/tools/infer_cls.py b/modules/onnx_ocr_module/src/tools/infer_cls.py new file mode 100644 index 0000000..f4c1776 --- /dev/null +++ b/modules/onnx_ocr_module/src/tools/infer_cls.py @@ -0,0 +1,84 @@ +# Copyright (c) 2020 PaddlePaddle Authors. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import numpy as np + +import os +import sys + +__dir__ = os.path.dirname(os.path.abspath(__file__)) +sys.path.append(__dir__) +sys.path.insert(0, os.path.abspath(os.path.join(__dir__, ".."))) + +os.environ["FLAGS_allocator_strategy"] = "auto_growth" + +import paddle + +from ppocr.data.imaug import transform, create_operators +from ppocr.modeling.architectures import build_model +from ppocr.postprocess import build_post_process +from ppocr.utils.save_load import load_model +from ppocr.utils.utility import get_image_file_list +import tools.program as program + + +def main(): + global_config = config["Global"] + + # build post process + post_process_class = build_post_process(config["PostProcess"], global_config) + + # build model + model = build_model(config["Architecture"]) + + load_model(config, model) + + # create data ops + transforms = [] + for op in config["Eval"]["dataset"]["transforms"]: + op_name = list(op)[0] + if "Label" in op_name: + continue + elif op_name == "KeepKeys": + op[op_name]["keep_keys"] = ["image"] + elif op_name == "SSLRotateResize": + op[op_name]["mode"] = "test" + transforms.append(op) + global_config["infer_mode"] = True + ops = create_operators(transforms, global_config) + + model.eval() + for file in get_image_file_list(config["Global"]["infer_img"]): + logger.info("infer_img: {}".format(file)) + with open(file, "rb") as f: + img = f.read() + data = {"image": img} + batch = transform(data, ops) + + images = np.expand_dims(batch[0], axis=0) + images = paddle.to_tensor(images) + preds = model(images) + post_result = post_process_class(preds) + for rec_result in post_result: + logger.info("\t result: {}".format(rec_result)) + logger.info("success!") + + +if __name__ == "__main__": + config, device, logger, vdl_writer = program.preprocess() + main() diff --git a/modules/onnx_ocr_module/src/tools/infer_det.py b/modules/onnx_ocr_module/src/tools/infer_det.py new file mode 100644 index 0000000..f961267 --- /dev/null +++ b/modules/onnx_ocr_module/src/tools/infer_det.py @@ -0,0 +1,136 @@ +# Copyright (c) 2020 PaddlePaddle Authors. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import numpy as np + +import os +import sys + +__dir__ = os.path.dirname(os.path.abspath(__file__)) +sys.path.append(__dir__) +sys.path.insert(0, os.path.abspath(os.path.join(__dir__, ".."))) + +os.environ["FLAGS_allocator_strategy"] = "auto_growth" + +import cv2 +import json +import paddle + +from ppocr.data.imaug import transform, create_operators +from ppocr.modeling.architectures import build_model +from ppocr.postprocess import build_post_process +from ppocr.utils.save_load import load_model +from ppocr.utils.utility import get_image_file_list +import tools.program as program + + +def draw_det_res(dt_boxes, config, img, img_name, save_path): + import cv2 + + src_im = img + for box in dt_boxes: + box = np.array(box).astype(np.int32).reshape((-1, 1, 2)) + cv2.polylines(src_im, [box], True, color=(255, 255, 0), thickness=2) + if not os.path.exists(save_path): + os.makedirs(save_path) + save_path = os.path.join(save_path, os.path.basename(img_name)) + cv2.imwrite(save_path, src_im) + logger.info("The detected Image saved in {}".format(save_path)) + + +@paddle.no_grad() +def main(): + global_config = config["Global"] + + # build model + model = build_model(config["Architecture"]) + + load_model(config, model) + # build post process + post_process_class = build_post_process(config["PostProcess"]) + + # create data ops + transforms = [] + for op in config["Eval"]["dataset"]["transforms"]: + op_name = list(op)[0] + if "Label" in op_name: + continue + elif op_name == "KeepKeys": + op[op_name]["keep_keys"] = ["image", "shape"] + transforms.append(op) + + ops = create_operators(transforms, global_config) + + save_res_path = config["Global"]["save_res_path"] + if not os.path.exists(os.path.dirname(save_res_path)): + os.makedirs(os.path.dirname(save_res_path)) + + model.eval() + with open(save_res_path, "wb") as fout: + for file in get_image_file_list(config["Global"]["infer_img"]): + logger.info("infer_img: {}".format(file)) + with open(file, "rb") as f: + img = f.read() + data = {"image": img} + batch = transform(data, ops) + + images = np.expand_dims(batch[0], axis=0) + shape_list = np.expand_dims(batch[1], axis=0) + images = paddle.to_tensor(images) + preds = model(images) + post_result = post_process_class(preds, shape_list) + + src_img = cv2.imread(file) + + dt_boxes_json = [] + # parser boxes if post_result is dict + if isinstance(post_result, dict): + det_box_json = {} + for k in post_result.keys(): + boxes = post_result[k][0]["points"] + dt_boxes_list = [] + for box in boxes: + tmp_json = {"transcription": ""} + tmp_json["points"] = np.array(box).tolist() + dt_boxes_list.append(tmp_json) + det_box_json[k] = dt_boxes_list + save_det_path = os.path.dirname( + config["Global"]["save_res_path"] + ) + "/det_results_{}/".format(k) + draw_det_res(boxes, config, src_img, file, save_det_path) + else: + boxes = post_result[0]["points"] + dt_boxes_json = [] + # write result + for box in boxes: + tmp_json = {"transcription": ""} + tmp_json["points"] = np.array(box).tolist() + dt_boxes_json.append(tmp_json) + save_det_path = ( + os.path.dirname(config["Global"]["save_res_path"]) + "/det_results/" + ) + draw_det_res(boxes, config, src_img, file, save_det_path) + otstr = file + "\t" + json.dumps(dt_boxes_json) + "\n" + fout.write(otstr.encode()) + + logger.info("success!") + + +if __name__ == "__main__": + config, device, logger, vdl_writer = program.preprocess() + main() diff --git a/modules/onnx_ocr_module/src/tools/infer_e2e.py b/modules/onnx_ocr_module/src/tools/infer_e2e.py new file mode 100644 index 0000000..796f4ae --- /dev/null +++ b/modules/onnx_ocr_module/src/tools/infer_e2e.py @@ -0,0 +1,170 @@ +# Copyright (c) 2020 PaddlePaddle Authors. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import numpy as np + +import os +import sys + +__dir__ = os.path.dirname(os.path.abspath(__file__)) +sys.path.append(__dir__) +sys.path.insert(0, os.path.abspath(os.path.join(__dir__, ".."))) + +os.environ["FLAGS_allocator_strategy"] = "auto_growth" + +import cv2 +import json +import paddle + +from ppocr.data.imaug import transform, create_operators +from ppocr.modeling.architectures import build_model +from ppocr.postprocess import build_post_process +from ppocr.utils.save_load import load_model +from ppocr.utils.utility import get_image_file_list +import tools.program as program +from PIL import Image, ImageDraw, ImageFont +import math + + +def draw_e2e_res_for_chinese( + image, boxes, txts, config, img_name, font_path="./doc/simfang.ttf" +): + h, w = image.height, image.width + img_left = image.copy() + img_right = Image.new("RGB", (w, h), (255, 255, 255)) + + import random + + random.seed(0) + draw_left = ImageDraw.Draw(img_left) + draw_right = ImageDraw.Draw(img_right) + for idx, (box, txt) in enumerate(zip(boxes, txts)): + box = np.array(box) + box = [tuple(x) for x in box] + color = (random.randint(0, 255), random.randint(0, 255), random.randint(0, 255)) + draw_left.polygon(box, fill=color) + draw_right.polygon(box, outline=color) + font = ImageFont.truetype(font_path, 15, encoding="utf-8") + draw_right.text([box[0][0], box[0][1]], txt, fill=(0, 0, 0), font=font) + img_left = Image.blend(image, img_left, 0.5) + img_show = Image.new("RGB", (w * 2, h), (255, 255, 255)) + img_show.paste(img_left, (0, 0, w, h)) + img_show.paste(img_right, (w, 0, w * 2, h)) + + save_e2e_path = os.path.dirname(config["Global"]["save_res_path"]) + "/e2e_results/" + if not os.path.exists(save_e2e_path): + os.makedirs(save_e2e_path) + save_path = os.path.join(save_e2e_path, os.path.basename(img_name)) + cv2.imwrite(save_path, np.array(img_show)[:, :, ::-1]) + logger.info("The e2e Image saved in {}".format(save_path)) + + +def draw_e2e_res(dt_boxes, strs, config, img, img_name): + if len(dt_boxes) > 0: + src_im = img + for box, str in zip(dt_boxes, strs): + box = box.astype(np.int32).reshape((-1, 1, 2)) + cv2.polylines(src_im, [box], True, color=(255, 255, 0), thickness=2) + cv2.putText( + src_im, + str, + org=(int(box[0, 0, 0]), int(box[0, 0, 1])), + fontFace=cv2.FONT_HERSHEY_COMPLEX, + fontScale=0.7, + color=(0, 255, 0), + thickness=1, + ) + save_det_path = ( + os.path.dirname(config["Global"]["save_res_path"]) + "/e2e_results/" + ) + if not os.path.exists(save_det_path): + os.makedirs(save_det_path) + save_path = os.path.join(save_det_path, os.path.basename(img_name)) + cv2.imwrite(save_path, src_im) + logger.info("The e2e Image saved in {}".format(save_path)) + + +def main(): + global_config = config["Global"] + + # build model + model = build_model(config["Architecture"]) + + load_model(config, model) + + # build post process + post_process_class = build_post_process(config["PostProcess"], global_config) + + # create data ops + transforms = [] + for op in config["Eval"]["dataset"]["transforms"]: + op_name = list(op)[0] + if "Label" in op_name: + continue + elif op_name == "KeepKeys": + op[op_name]["keep_keys"] = ["image", "shape"] + transforms.append(op) + + ops = create_operators(transforms, global_config) + + save_res_path = config["Global"]["save_res_path"] + if not os.path.exists(os.path.dirname(save_res_path)): + os.makedirs(os.path.dirname(save_res_path)) + + model.eval() + with open(save_res_path, "wb") as fout: + for file in get_image_file_list(config["Global"]["infer_img"]): + logger.info("infer_img: {}".format(file)) + with open(file, "rb") as f: + img = f.read() + data = {"image": img} + batch = transform(data, ops) + images = np.expand_dims(batch[0], axis=0) + shape_list = np.expand_dims(batch[1], axis=0) + images = paddle.to_tensor(images) + preds = model(images) + post_result = post_process_class(preds, shape_list) + points, strs = post_result["points"], post_result["texts"] + # write result + dt_boxes_json = [] + for poly, str in zip(points, strs): + tmp_json = {"transcription": str} + tmp_json["points"] = poly.tolist() + dt_boxes_json.append(tmp_json) + otstr = file + "\t" + json.dumps(dt_boxes_json) + "\n" + fout.write(otstr.encode()) + src_img = cv2.imread(file) + if global_config["infer_visual_type"] == "EN": + draw_e2e_res(points, strs, config, src_img, file) + elif global_config["infer_visual_type"] == "CN": + src_img = Image.fromarray(cv2.cvtColor(src_img, cv2.COLOR_BGR2RGB)) + draw_e2e_res_for_chinese( + src_img, + points, + strs, + config, + file, + font_path="./doc/fonts/simfang.ttf", + ) + + logger.info("success!") + + +if __name__ == "__main__": + config, device, logger, vdl_writer = program.preprocess() + main() diff --git a/modules/onnx_ocr_module/src/tools/infer_kie.py b/modules/onnx_ocr_module/src/tools/infer_kie.py new file mode 100644 index 0000000..36b6d55 --- /dev/null +++ b/modules/onnx_ocr_module/src/tools/infer_kie.py @@ -0,0 +1,186 @@ +# Copyright (c) 2020 PaddlePaddle Authors. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import numpy as np +import paddle.nn.functional as F + +import os +import sys + +__dir__ = os.path.dirname(os.path.abspath(__file__)) +sys.path.append(__dir__) +sys.path.insert(0, os.path.abspath(os.path.join(__dir__, ".."))) + +os.environ["FLAGS_allocator_strategy"] = "auto_growth" + +import cv2 +import paddle + +from ppocr.data.imaug import transform, create_operators +from ppocr.modeling.architectures import build_model +from ppocr.utils.save_load import load_model +import tools.program as program +import time + + +def read_class_list(filepath): + ret = {} + with open(filepath, "r") as f: + lines = f.readlines() + for idx, line in enumerate(lines): + ret[idx] = line.strip("\n") + return ret + + +def draw_kie_result(batch, node, idx_to_cls, count): + img = batch[6].copy() + boxes = batch[7] + h, w = img.shape[:2] + pred_img = np.ones((h, w * 2, 3), dtype=np.uint8) * 255 + max_value, max_idx = paddle.max(node, -1), paddle.argmax(node, -1) + node_pred_label = max_idx.numpy().tolist() + node_pred_score = max_value.numpy().tolist() + + for i, box in enumerate(boxes): + if i >= len(node_pred_label): + break + new_box = [ + [box[0], box[1]], + [box[2], box[1]], + [box[2], box[3]], + [box[0], box[3]], + ] + Pts = np.array([new_box], np.int32) + cv2.polylines( + img, [Pts.reshape((-1, 1, 2))], True, color=(255, 255, 0), thickness=1 + ) + x_min = int(min([point[0] for point in new_box])) + y_min = int(min([point[1] for point in new_box])) + + pred_label = node_pred_label[i] + if pred_label in idx_to_cls: + pred_label = idx_to_cls[pred_label] + pred_score = "{:.2f}".format(node_pred_score[i]) + text = pred_label + "(" + pred_score + ")" + cv2.putText( + pred_img, + text, + (x_min * 2, y_min), + cv2.FONT_HERSHEY_SIMPLEX, + 0.5, + (255, 0, 0), + 1, + ) + vis_img = np.ones((h, w * 3, 3), dtype=np.uint8) * 255 + vis_img[:, :w] = img + vis_img[:, w:] = pred_img + save_kie_path = os.path.dirname(config["Global"]["save_res_path"]) + "/kie_results/" + if not os.path.exists(save_kie_path): + os.makedirs(save_kie_path) + save_path = os.path.join(save_kie_path, str(count) + ".png") + cv2.imwrite(save_path, vis_img) + logger.info("The Kie Image saved in {}".format(save_path)) + + +def write_kie_result(fout, node, data): + """ + Write infer result to output file, sorted by the predict label of each line. + The format keeps the same as the input with additional score attribute. + """ + import json + + label = data["label"] + annotations = json.loads(label) + max_value, max_idx = paddle.max(node, -1), paddle.argmax(node, -1) + node_pred_label = max_idx.numpy().tolist() + node_pred_score = max_value.numpy().tolist() + res = [] + for i, label in enumerate(node_pred_label): + pred_score = "{:.2f}".format(node_pred_score[i]) + pred_res = { + "label": label, + "transcription": annotations[i]["transcription"], + "score": pred_score, + "points": annotations[i]["points"], + } + res.append(pred_res) + res.sort(key=lambda x: x["label"]) + fout.writelines([json.dumps(res, ensure_ascii=False) + "\n"]) + + +def main(): + global_config = config["Global"] + + # build model + model = build_model(config["Architecture"]) + load_model(config, model) + + # create data ops + transforms = [] + for op in config["Eval"]["dataset"]["transforms"]: + transforms.append(op) + + data_dir = config["Eval"]["dataset"]["data_dir"] + + ops = create_operators(transforms, global_config) + + save_res_path = config["Global"]["save_res_path"] + class_path = config["Global"]["class_path"] + idx_to_cls = read_class_list(class_path) + os.makedirs(os.path.dirname(save_res_path), exist_ok=True) + + model.eval() + + warmup_times = 0 + count_t = [] + with open(save_res_path, "w") as fout: + with open(config["Global"]["infer_img"], "rb") as f: + lines = f.readlines() + for index, data_line in enumerate(lines): + if index == 10: + warmup_t = time.time() + data_line = data_line.decode("utf-8") + substr = data_line.strip("\n").split("\t") + img_path, label = data_dir + "/" + substr[0], substr[1] + data = {"img_path": img_path, "label": label} + with open(data["img_path"], "rb") as f: + img = f.read() + data["image"] = img + st = time.time() + batch = transform(data, ops) + batch_pred = [0] * len(batch) + for i in range(len(batch)): + batch_pred[i] = paddle.to_tensor(np.expand_dims(batch[i], axis=0)) + st = time.time() + node, edge = model(batch_pred) + node = F.softmax(node, -1) + count_t.append(time.time() - st) + draw_kie_result(batch, node, idx_to_cls, index) + write_kie_result(fout, node, data) + fout.close() + logger.info("success!") + logger.info( + "It took {} s for predict {} images.".format(np.sum(count_t), len(count_t)) + ) + ips = len(count_t[warmup_times:]) / np.sum(count_t[warmup_times:]) + logger.info("The ips is {} images/s".format(ips)) + + +if __name__ == "__main__": + config, device, logger, vdl_writer = program.preprocess() + main() diff --git a/modules/onnx_ocr_module/src/tools/infer_kie_token_ser.py b/modules/onnx_ocr_module/src/tools/infer_kie_token_ser.py new file mode 100644 index 0000000..28bdd40 --- /dev/null +++ b/modules/onnx_ocr_module/src/tools/infer_kie_token_ser.py @@ -0,0 +1,178 @@ +# Copyright (c) 2020 PaddlePaddle Authors. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import numpy as np + +import os +import sys + +__dir__ = os.path.dirname(os.path.abspath(__file__)) +sys.path.append(__dir__) +sys.path.insert(0, os.path.abspath(os.path.join(__dir__, ".."))) + +os.environ["FLAGS_allocator_strategy"] = "auto_growth" +import cv2 +import json +import paddle + +from ppocr.data.imaug import transform, create_operators +from ppocr.modeling.architectures import build_model +from ppocr.postprocess import build_post_process +from ppocr.utils.save_load import load_model +from ppocr.utils.visual import draw_ser_results +from ppocr.utils.utility import get_image_file_list, load_vqa_bio_label_maps +import tools.program as program + + +def to_tensor(data): + import numbers + from collections import defaultdict + + data_dict = defaultdict(list) + to_tensor_idxs = [] + + for idx, v in enumerate(data): + if isinstance(v, (np.ndarray, paddle.Tensor, numbers.Number)): + if idx not in to_tensor_idxs: + to_tensor_idxs.append(idx) + data_dict[idx].append(v) + for idx in to_tensor_idxs: + data_dict[idx] = paddle.to_tensor(data_dict[idx]) + return list(data_dict.values()) + + +class SerPredictor(object): + def __init__(self, config): + global_config = config["Global"] + self.algorithm = config["Architecture"]["algorithm"] + + # build post process + self.post_process_class = build_post_process( + config["PostProcess"], global_config + ) + + # build model + self.model = build_model(config["Architecture"]) + + load_model(config, self.model, model_type=config["Architecture"]["model_type"]) + + from paddleocr import PaddleOCR + + self.ocr_engine = PaddleOCR( + use_angle_cls=False, + show_log=False, + rec_model_dir=global_config.get("kie_rec_model_dir", None), + det_model_dir=global_config.get("kie_det_model_dir", None), + use_gpu=global_config["use_gpu"], + ) + + # create data ops + transforms = [] + for op in config["Eval"]["dataset"]["transforms"]: + op_name = list(op)[0] + if "Label" in op_name: + op[op_name]["ocr_engine"] = self.ocr_engine + elif op_name == "KeepKeys": + op[op_name]["keep_keys"] = [ + "input_ids", + "bbox", + "attention_mask", + "token_type_ids", + "image", + "labels", + "segment_offset_id", + "ocr_info", + "entities", + ] + + transforms.append(op) + if config["Global"].get("infer_mode", None) is None: + global_config["infer_mode"] = True + self.ops = create_operators( + config["Eval"]["dataset"]["transforms"], global_config + ) + self.model.eval() + + def __call__(self, data): + with open(data["img_path"], "rb") as f: + img = f.read() + data["image"] = img + batch = transform(data, self.ops) + batch = to_tensor(batch) + preds = self.model(batch) + + post_result = self.post_process_class( + preds, segment_offset_ids=batch[6], ocr_infos=batch[7] + ) + return post_result, batch + + +if __name__ == "__main__": + config, device, logger, vdl_writer = program.preprocess() + os.makedirs(config["Global"]["save_res_path"], exist_ok=True) + + ser_engine = SerPredictor(config) + + if config["Global"].get("infer_mode", None) is False: + data_dir = config["Eval"]["dataset"]["data_dir"] + with open(config["Global"]["infer_img"], "rb") as f: + infer_imgs = f.readlines() + else: + infer_imgs = get_image_file_list(config["Global"]["infer_img"]) + + with open( + os.path.join(config["Global"]["save_res_path"], "infer_results.txt"), + "w", + encoding="utf-8", + ) as fout: + for idx, info in enumerate(infer_imgs): + if config["Global"].get("infer_mode", None) is False: + data_line = info.decode("utf-8") + substr = data_line.strip("\n").split("\t") + img_path = os.path.join(data_dir, substr[0]) + data = {"img_path": img_path, "label": substr[1]} + else: + img_path = info + data = {"img_path": img_path} + + save_img_path = os.path.join( + config["Global"]["save_res_path"], + os.path.splitext(os.path.basename(img_path))[0] + "_ser.jpg", + ) + + result, _ = ser_engine(data) + result = result[0] + fout.write( + img_path + + "\t" + + json.dumps( + { + "ocr_info": result, + }, + ensure_ascii=False, + ) + + "\n" + ) + img_res = draw_ser_results(img_path, result) + cv2.imwrite(save_img_path, img_res) + + logger.info( + "process: [{}/{}], save result to {}".format( + idx, len(infer_imgs), save_img_path + ) + ) diff --git a/modules/onnx_ocr_module/src/tools/infer_kie_token_ser_re.py b/modules/onnx_ocr_module/src/tools/infer_kie_token_ser_re.py new file mode 100644 index 0000000..666749b --- /dev/null +++ b/modules/onnx_ocr_module/src/tools/infer_kie_token_ser_re.py @@ -0,0 +1,226 @@ +# Copyright (c) 2020 PaddlePaddle Authors. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import numpy as np + +import os +import sys + +__dir__ = os.path.dirname(os.path.abspath(__file__)) +sys.path.append(__dir__) +sys.path.insert(0, os.path.abspath(os.path.join(__dir__, ".."))) + +os.environ["FLAGS_allocator_strategy"] = "auto_growth" +import cv2 +import json +import paddle +import paddle.distributed as dist + +from ppocr.data.imaug import transform, create_operators +from ppocr.modeling.architectures import build_model +from ppocr.postprocess import build_post_process +from ppocr.utils.save_load import load_model +from ppocr.utils.visual import draw_re_results +from ppocr.utils.logging import get_logger +from ppocr.utils.utility import get_image_file_list, load_vqa_bio_label_maps, print_dict +from tools.program import ArgsParser, load_config, merge_config +from tools.infer_kie_token_ser import SerPredictor + + +class ReArgsParser(ArgsParser): + def __init__(self): + super(ReArgsParser, self).__init__() + self.add_argument( + "-c_ser", "--config_ser", help="ser configuration file to use" + ) + self.add_argument( + "-o_ser", "--opt_ser", nargs="+", help="set ser configuration options " + ) + + def parse_args(self, argv=None): + args = super(ReArgsParser, self).parse_args(argv) + assert ( + args.config_ser is not None + ), "Please specify --config_ser=ser_configure_file_path." + args.opt_ser = self._parse_opt(args.opt_ser) + return args + + +def make_input(ser_inputs, ser_results): + entities_labels = {"HEADER": 0, "QUESTION": 1, "ANSWER": 2} + batch_size, max_seq_len = ser_inputs[0].shape[:2] + entities = ser_inputs[8][0] + ser_results = ser_results[0] + assert len(entities) == len(ser_results) + + # entities + start = [] + end = [] + label = [] + entity_idx_dict = {} + for i, (res, entity) in enumerate(zip(ser_results, entities)): + if res["pred"] == "O": + continue + entity_idx_dict[len(start)] = i + start.append(entity["start"]) + end.append(entity["end"]) + label.append(entities_labels[res["pred"]]) + + entities = np.full([max_seq_len + 1, 3], fill_value=-1, dtype=np.int64) + entities[0, 0] = len(start) + entities[1 : len(start) + 1, 0] = start + entities[0, 1] = len(end) + entities[1 : len(end) + 1, 1] = end + entities[0, 2] = len(label) + entities[1 : len(label) + 1, 2] = label + + # relations + head = [] + tail = [] + for i in range(len(label)): + for j in range(len(label)): + if label[i] == 1 and label[j] == 2: + head.append(i) + tail.append(j) + + relations = np.full([len(head) + 1, 2], fill_value=-1, dtype=np.int64) + relations[0, 0] = len(head) + relations[1 : len(head) + 1, 0] = head + relations[0, 1] = len(tail) + relations[1 : len(tail) + 1, 1] = tail + + entities = np.expand_dims(entities, axis=0) + entities = np.repeat(entities, batch_size, axis=0) + relations = np.expand_dims(relations, axis=0) + relations = np.repeat(relations, batch_size, axis=0) + + # remove ocr_info segment_offset_id and label in ser input + if isinstance(ser_inputs[0], paddle.Tensor): + entities = paddle.to_tensor(entities) + relations = paddle.to_tensor(relations) + ser_inputs = ser_inputs[:5] + [entities, relations] + + entity_idx_dict_batch = [] + for b in range(batch_size): + entity_idx_dict_batch.append(entity_idx_dict) + return ser_inputs, entity_idx_dict_batch + + +class SerRePredictor(object): + def __init__(self, config, ser_config): + global_config = config["Global"] + if "infer_mode" in global_config: + ser_config["Global"]["infer_mode"] = global_config["infer_mode"] + + self.ser_engine = SerPredictor(ser_config) + + # init re model + + # build post process + self.post_process_class = build_post_process( + config["PostProcess"], global_config + ) + + # build model + self.model = build_model(config["Architecture"]) + + load_model(config, self.model, model_type=config["Architecture"]["model_type"]) + + self.model.eval() + + def __call__(self, data): + ser_results, ser_inputs = self.ser_engine(data) + re_input, entity_idx_dict_batch = make_input(ser_inputs, ser_results) + if self.model.backbone.use_visual_backbone is False: + re_input.pop(4) + preds = self.model(re_input) + post_result = self.post_process_class( + preds, ser_results=ser_results, entity_idx_dict_batch=entity_idx_dict_batch + ) + return post_result + + +def preprocess(): + FLAGS = ReArgsParser().parse_args() + config = load_config(FLAGS.config) + config = merge_config(config, FLAGS.opt) + + ser_config = load_config(FLAGS.config_ser) + ser_config = merge_config(ser_config, FLAGS.opt_ser) + + logger = get_logger() + + # check if set use_gpu=True in paddlepaddle cpu version + use_gpu = config["Global"]["use_gpu"] + + device = "gpu:{}".format(dist.ParallelEnv().dev_id) if use_gpu else "cpu" + device = paddle.set_device(device) + + logger.info("{} re config {}".format("*" * 10, "*" * 10)) + print_dict(config, logger) + logger.info("\n") + logger.info("{} ser config {}".format("*" * 10, "*" * 10)) + print_dict(ser_config, logger) + logger.info("train with paddle {} and device {}".format(paddle.__version__, device)) + return config, ser_config, device, logger + + +if __name__ == "__main__": + config, ser_config, device, logger = preprocess() + os.makedirs(config["Global"]["save_res_path"], exist_ok=True) + + ser_re_engine = SerRePredictor(config, ser_config) + + if config["Global"].get("infer_mode", None) is False: + data_dir = config["Eval"]["dataset"]["data_dir"] + with open(config["Global"]["infer_img"], "rb") as f: + infer_imgs = f.readlines() + else: + infer_imgs = get_image_file_list(config["Global"]["infer_img"]) + + with open( + os.path.join(config["Global"]["save_res_path"], "infer_results.txt"), + "w", + encoding="utf-8", + ) as fout: + for idx, info in enumerate(infer_imgs): + if config["Global"].get("infer_mode", None) is False: + data_line = info.decode("utf-8") + substr = data_line.strip("\n").split("\t") + img_path = os.path.join(data_dir, substr[0]) + data = {"img_path": img_path, "label": substr[1]} + else: + img_path = info + data = {"img_path": img_path} + + save_img_path = os.path.join( + config["Global"]["save_res_path"], + os.path.splitext(os.path.basename(img_path))[0] + "_ser_re.jpg", + ) + + result = ser_re_engine(data) + result = result[0] + fout.write(img_path + "\t" + json.dumps(result, ensure_ascii=False) + "\n") + img_res = draw_re_results(img_path, result) + cv2.imwrite(save_img_path, img_res) + + logger.info( + "process: [{}/{}], save result to {}".format( + idx, len(infer_imgs), save_img_path + ) + ) diff --git a/modules/onnx_ocr_module/src/tools/infer_rec.py b/modules/onnx_ocr_module/src/tools/infer_rec.py new file mode 100644 index 0000000..6b97a3b --- /dev/null +++ b/modules/onnx_ocr_module/src/tools/infer_rec.py @@ -0,0 +1,218 @@ +# Copyright (c) 2020 PaddlePaddle Authors. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import numpy as np + +import os +import sys +import json + +__dir__ = os.path.dirname(os.path.abspath(__file__)) +sys.path.append(__dir__) +sys.path.insert(0, os.path.abspath(os.path.join(__dir__, ".."))) + +os.environ["FLAGS_allocator_strategy"] = "auto_growth" + +import paddle + +from ppocr.data.imaug import transform, create_operators +from ppocr.modeling.architectures import build_model +from ppocr.postprocess import build_post_process +from ppocr.utils.save_load import load_model +from ppocr.utils.utility import get_image_file_list +import tools.program as program + + +def main(): + global_config = config["Global"] + + # build post process + post_process_class = build_post_process(config["PostProcess"], global_config) + + # build model + if hasattr(post_process_class, "character"): + char_num = len(getattr(post_process_class, "character")) + if config["Architecture"]["algorithm"] in [ + "Distillation", + ]: # distillation model + for key in config["Architecture"]["Models"]: + if ( + config["Architecture"]["Models"][key]["Head"]["name"] == "MultiHead" + ): # multi head + out_channels_list = {} + if config["PostProcess"]["name"] == "DistillationSARLabelDecode": + char_num = char_num - 2 + if config["PostProcess"]["name"] == "DistillationNRTRLabelDecode": + char_num = char_num - 3 + out_channels_list["CTCLabelDecode"] = char_num + out_channels_list["SARLabelDecode"] = char_num + 2 + out_channels_list["NRTRLabelDecode"] = char_num + 3 + config["Architecture"]["Models"][key]["Head"][ + "out_channels_list" + ] = out_channels_list + else: + config["Architecture"]["Models"][key]["Head"][ + "out_channels" + ] = char_num + elif config["Architecture"]["Head"]["name"] == "MultiHead": # multi head + out_channels_list = {} + char_num = len(getattr(post_process_class, "character")) + if config["PostProcess"]["name"] == "SARLabelDecode": + char_num = char_num - 2 + if config["PostProcess"]["name"] == "NRTRLabelDecode": + char_num = char_num - 3 + out_channels_list["CTCLabelDecode"] = char_num + out_channels_list["SARLabelDecode"] = char_num + 2 + out_channels_list["NRTRLabelDecode"] = char_num + 3 + config["Architecture"]["Head"]["out_channels_list"] = out_channels_list + else: # base rec model + config["Architecture"]["Head"]["out_channels"] = char_num + + if config["Architecture"].get("algorithm") in ["LaTeXOCR"]: + config["Architecture"]["Backbone"]["is_predict"] = True + config["Architecture"]["Backbone"]["is_export"] = True + config["Architecture"]["Head"]["is_export"] = True + + model = build_model(config["Architecture"]) + + load_model(config, model) + + # create data ops + transforms = [] + for op in config["Eval"]["dataset"]["transforms"]: + op_name = list(op)[0] + if "Label" in op_name: + continue + elif op_name in ["RecResizeImg"]: + op[op_name]["infer_mode"] = True + elif op_name == "KeepKeys": + if config["Architecture"]["algorithm"] == "SRN": + op[op_name]["keep_keys"] = [ + "image", + "encoder_word_pos", + "gsrm_word_pos", + "gsrm_slf_attn_bias1", + "gsrm_slf_attn_bias2", + ] + elif config["Architecture"]["algorithm"] == "SAR": + op[op_name]["keep_keys"] = ["image", "valid_ratio"] + elif config["Architecture"]["algorithm"] == "RobustScanner": + op[op_name]["keep_keys"] = ["image", "valid_ratio", "word_positons"] + else: + op[op_name]["keep_keys"] = ["image"] + transforms.append(op) + global_config["infer_mode"] = True + ops = create_operators(transforms, global_config) + + save_res_path = config["Global"].get( + "save_res_path", "./output/rec/predicts_rec.txt" + ) + if not os.path.exists(os.path.dirname(save_res_path)): + os.makedirs(os.path.dirname(save_res_path)) + + model.eval() + + infer_imgs = config["Global"]["infer_img"] + infer_list = config["Global"].get("infer_list", None) + with open(save_res_path, "w") as fout: + for file in get_image_file_list(infer_imgs, infer_list=infer_list): + logger.info("infer_img: {}".format(file)) + with open(file, "rb") as f: + img = f.read() + if config["Architecture"]["algorithm"] in [ + "UniMERNet", + "PP-FormulaNet-S", + "PP-FormulaNet-L", + ]: + data = {"image": img, "filename": file} + else: + data = {"image": img} + batch = transform(data, ops) + if config["Architecture"]["algorithm"] == "SRN": + encoder_word_pos_list = np.expand_dims(batch[1], axis=0) + gsrm_word_pos_list = np.expand_dims(batch[2], axis=0) + gsrm_slf_attn_bias1_list = np.expand_dims(batch[3], axis=0) + gsrm_slf_attn_bias2_list = np.expand_dims(batch[4], axis=0) + + others = [ + paddle.to_tensor(encoder_word_pos_list), + paddle.to_tensor(gsrm_word_pos_list), + paddle.to_tensor(gsrm_slf_attn_bias1_list), + paddle.to_tensor(gsrm_slf_attn_bias2_list), + ] + if config["Architecture"]["algorithm"] == "SAR": + valid_ratio = np.expand_dims(batch[-1], axis=0) + img_metas = [paddle.to_tensor(valid_ratio)] + if config["Architecture"]["algorithm"] == "RobustScanner": + valid_ratio = np.expand_dims(batch[1], axis=0) + word_positons = np.expand_dims(batch[2], axis=0) + img_metas = [ + paddle.to_tensor(valid_ratio), + paddle.to_tensor(word_positons), + ] + if config["Architecture"]["algorithm"] == "CAN": + image_mask = paddle.ones( + (np.expand_dims(batch[0], axis=0).shape), dtype="float32" + ) + label = paddle.ones((1, 36), dtype="int64") + images = np.expand_dims(batch[0], axis=0) + images = paddle.to_tensor(images) + if config["Architecture"]["algorithm"] == "SRN": + preds = model(images, others) + elif config["Architecture"]["algorithm"] == "SAR": + preds = model(images, img_metas) + elif config["Architecture"]["algorithm"] == "RobustScanner": + preds = model(images, img_metas) + elif config["Architecture"]["algorithm"] == "CAN": + preds = model([images, image_mask, label]) + else: + preds = model(images) + post_result = post_process_class(preds) + info = None + if isinstance(post_result, dict): + rec_info = dict() + for key in post_result: + if len(post_result[key][0]) >= 2: + rec_info[key] = { + "label": post_result[key][0][0], + "score": float(post_result[key][0][1]), + } + info = json.dumps(rec_info, ensure_ascii=False) + elif isinstance(post_result, list) and isinstance(post_result[0], int): + # for RFLearning CNT branch + info = str(post_result[0]) + elif config["Architecture"]["algorithm"] in [ + "LaTeXOCR", + "UniMERNet", + "PP-FormulaNet-S", + "PP-FormulaNet-L", + ]: + info = str(post_result[0]) + else: + if len(post_result[0]) >= 2: + info = post_result[0][0] + "\t" + str(post_result[0][1]) + + if info is not None: + logger.info("\t result: {}".format(info)) + fout.write(file + "\t" + info + "\n") + logger.info("success!") + + +if __name__ == "__main__": + config, device, logger, vdl_writer = program.preprocess() + main() diff --git a/modules/onnx_ocr_module/src/tools/infer_sr.py b/modules/onnx_ocr_module/src/tools/infer_sr.py new file mode 100644 index 0000000..2aa13c8 --- /dev/null +++ b/modules/onnx_ocr_module/src/tools/infer_sr.py @@ -0,0 +1,101 @@ +# Copyright (c) 2020 PaddlePaddle Authors. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import numpy as np + +import os +import sys +import json +from PIL import Image +import cv2 + +__dir__ = os.path.dirname(os.path.abspath(__file__)) +sys.path.insert(0, __dir__) +sys.path.insert(0, os.path.abspath(os.path.join(__dir__, ".."))) + +os.environ["FLAGS_allocator_strategy"] = "auto_growth" + +import paddle + +from ppocr.data.imaug import transform, create_operators +from ppocr.modeling.architectures import build_model +from ppocr.postprocess import build_post_process +from ppocr.utils.save_load import load_model +from ppocr.utils.utility import get_image_file_list +import tools.program as program + + +def main(): + global_config = config["Global"] + + # build post process + post_process_class = build_post_process(config["PostProcess"], global_config) + + # sr transform + config["Architecture"]["Transform"]["infer_mode"] = True + + model = build_model(config["Architecture"]) + + load_model(config, model) + + # create data ops + transforms = [] + for op in config["Eval"]["dataset"]["transforms"]: + op_name = list(op)[0] + if "Label" in op_name: + continue + elif op_name in ["SRResize"]: + op[op_name]["infer_mode"] = True + elif op_name == "KeepKeys": + op[op_name]["keep_keys"] = ["img_lr"] + transforms.append(op) + global_config["infer_mode"] = True + ops = create_operators(transforms, global_config) + + save_visual_path = config["Global"].get("save_visual", "infer_result/") + if not os.path.exists(os.path.dirname(save_visual_path)): + os.makedirs(os.path.dirname(save_visual_path)) + + model.eval() + for file in get_image_file_list(config["Global"]["infer_img"]): + logger.info("infer_img: {}".format(file)) + img = Image.open(file).convert("RGB") + data = {"image_lr": img} + batch = transform(data, ops) + images = np.expand_dims(batch[0], axis=0) + images = paddle.to_tensor(images) + + preds = model(images) + sr_img = preds["sr_img"][0] + lr_img = preds["lr_img"][0] + fm_sr = (sr_img.numpy() * 255).transpose(1, 2, 0).astype(np.uint8) + fm_lr = (lr_img.numpy() * 255).transpose(1, 2, 0).astype(np.uint8) + img_name_pure = os.path.split(file)[-1] + cv2.imwrite( + "{}/sr_{}".format(save_visual_path, img_name_pure), fm_sr[:, :, ::-1] + ) + logger.info( + "The visualized image saved in infer_result/sr_{}".format(img_name_pure) + ) + + logger.info("success!") + + +if __name__ == "__main__": + config, device, logger, vdl_writer = program.preprocess() + main() diff --git a/modules/onnx_ocr_module/src/tools/infer_table.py b/modules/onnx_ocr_module/src/tools/infer_table.py new file mode 100644 index 0000000..4056aa8 --- /dev/null +++ b/modules/onnx_ocr_module/src/tools/infer_table.py @@ -0,0 +1,120 @@ +# Copyright (c) 2020 PaddlePaddle Authors. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import numpy as np + +import os +import sys +import json + +__dir__ = os.path.dirname(os.path.abspath(__file__)) +sys.path.append(__dir__) +sys.path.insert(0, os.path.abspath(os.path.join(__dir__, ".."))) + +os.environ["FLAGS_allocator_strategy"] = "auto_growth" + +import paddle +from paddle.jit import to_static + +from ppocr.data.imaug import transform, create_operators +from ppocr.modeling.architectures import build_model +from ppocr.postprocess import build_post_process +from ppocr.utils.save_load import load_model +from ppocr.utils.utility import get_image_file_list +from ppocr.utils.visual import draw_rectangle +from tools.infer.utility import draw_boxes +import tools.program as program +import cv2 + + +@paddle.no_grad() +def main(config, device, logger, vdl_writer): + global_config = config["Global"] + + # build post process + post_process_class = build_post_process(config["PostProcess"], global_config) + + # build model + if hasattr(post_process_class, "character"): + config["Architecture"]["Head"]["out_channels"] = len( + getattr(post_process_class, "character") + ) + + model = build_model(config["Architecture"]) + algorithm = config["Architecture"]["algorithm"] + + load_model(config, model) + + # create data ops + transforms = [] + for op in config["Eval"]["dataset"]["transforms"]: + op_name = list(op)[0] + if "Encode" in op_name: + continue + if op_name == "KeepKeys": + op[op_name]["keep_keys"] = ["image", "shape"] + transforms.append(op) + + global_config["infer_mode"] = True + ops = create_operators(transforms, global_config) + + save_res_path = config["Global"]["save_res_path"] + os.makedirs(save_res_path, exist_ok=True) + + model.eval() + with open( + os.path.join(save_res_path, "infer.txt"), mode="w", encoding="utf-8" + ) as f_w: + for file in get_image_file_list(config["Global"]["infer_img"]): + logger.info("infer_img: {}".format(file)) + with open(file, "rb") as f: + img = f.read() + data = {"image": img} + batch = transform(data, ops) + images = np.expand_dims(batch[0], axis=0) + shape_list = np.expand_dims(batch[1], axis=0) + + images = paddle.to_tensor(images) + preds = model(images) + post_result = post_process_class(preds, [shape_list]) + + structure_str_list = post_result["structure_batch_list"][0] + bbox_list = post_result["bbox_batch_list"][0] + structure_str_list = structure_str_list[0] + structure_str_list = ( + ["", "", ""] + + structure_str_list + + ["
", "", ""] + ) + bbox_list_str = json.dumps(bbox_list.tolist()) + + logger.info("result: {}, {}".format(structure_str_list, bbox_list_str)) + f_w.write("result: {}, {}\n".format(structure_str_list, bbox_list_str)) + + if len(bbox_list) > 0 and len(bbox_list[0]) == 4: + img = draw_rectangle(file, bbox_list) + else: + img = draw_boxes(cv2.imread(file), bbox_list) + cv2.imwrite(os.path.join(save_res_path, os.path.basename(file)), img) + logger.info("save result to {}".format(save_res_path)) + logger.info("success!") + + +if __name__ == "__main__": + config, device, logger, vdl_writer = program.preprocess() + main(config, device, logger, vdl_writer) diff --git a/modules/onnx_ocr_module/src/tools/naive_sync_bn.py b/modules/onnx_ocr_module/src/tools/naive_sync_bn.py new file mode 100644 index 0000000..966fd9d --- /dev/null +++ b/modules/onnx_ocr_module/src/tools/naive_sync_bn.py @@ -0,0 +1,121 @@ +# Copyright (c) 2024 PaddlePaddle Authors. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import paddle.distributed as dist +import math +import paddle +import paddle.nn as nn + + +class _AllReduce(paddle.autograd.PyLayer): + @staticmethod + def forward(ctx, input): + input_list = [paddle.zeros_like(input) for k in range(dist.get_world_size())] + # Use allgather instead of allreduce since I don't trust in-place operations .. + dist.all_gather(input_list, input, sync_op=True) + inputs = paddle.stack(input_list, axis=0) + return paddle.sum(inputs, axis=0) + + @staticmethod + def backward(ctx, grad_output): + dist.all_reduce(grad_output, sync_op=True) + return grad_output + + +def differentiable_all_reduce(input): + """ + Differentiable counterpart of `dist.all_reduce`. + """ + if ( + not dist.is_available() + or not dist.is_initialized() + or dist.get_world_size() == 1 + ): + return input + return _AllReduce.apply(input) + + +class NaiveSyncBatchNorm(nn.BatchNorm2D): + + def __init__(self, *args, stats_mode="", **kwargs): + super().__init__(*args, **kwargs) + assert stats_mode in ["", "N"] + self._stats_mode = stats_mode + + def forward(self, input): + if dist.get_world_size() == 1 or not self.training: + return super().forward(input) + + B, C = input.shape[0], input.shape[1] + + mean = paddle.mean(input, axis=[0, 2, 3]) + meansqr = paddle.mean(input * input, axis=[0, 2, 3]) + + if self._stats_mode == "": + assert ( + B > 0 + ), 'SyncBatchNorm(stats_mode="") does not support zero batch size.' + vec = paddle.concat([mean, meansqr], axis=0) + vec = differentiable_all_reduce(vec) * (1.0 / dist.get_world_size()) + mean, meansqr = paddle.split(vec, [C, C]) + momentum = ( + 1 - self._momentum + ) # NOTE: paddle has reverse momentum definition + else: + if B == 0: + vec = paddle.zeros([2 * C + 1], dtype=mean.dtype) + vec = vec + input.sum() # make sure there is gradient w.r.t input + else: + vec = paddle.concat( + [ + mean, + meansqr, + paddle.ones([1], dtype=mean.dtype), + ], + axis=0, + ) + vec = differentiable_all_reduce(vec * B) + + total_batch = vec[-1].detach() + momentum = total_batch.clip(max=1) * ( + 1 - self._momentum + ) # no update if total_batch is 0 + mean, meansqr, _ = paddle.split( + vec / total_batch.clip(min=1), [C, C, int(vec.shape[0] - 2 * C)] + ) # avoid div-by-zero + + var = meansqr - mean * mean + invstd = paddle.rsqrt(var + self._epsilon) + scale = self.weight * invstd + bias = self.bias - mean * scale + scale = scale.reshape([1, -1, 1, 1]) + bias = bias.reshape([1, -1, 1, 1]) + + tmp_mean = self._mean + momentum * (mean.detach() - self._mean) + self._mean.set_value(tmp_mean) + tmp_variance = self._variance + (momentum * (var.detach() - self._variance)) + self._variance.set_value(tmp_variance) + ret = input * scale + bias + return ret + + +def convert_syncbn(model): + for n, m in model.named_children(): + if isinstance(m, nn.layer.norm._BatchNormBase): + syncbn = NaiveSyncBatchNorm( + m._num_features, m._momentum, m._epsilon, m._weight_attr, m._bias_attr + ) + setattr(model, n, syncbn) + else: + convert_syncbn(m) diff --git a/modules/onnx_ocr_module/src/tools/program.py b/modules/onnx_ocr_module/src/tools/program.py new file mode 100644 index 0000000..3eb43b7 --- /dev/null +++ b/modules/onnx_ocr_module/src/tools/program.py @@ -0,0 +1,922 @@ +# Copyright (c) 2021 PaddlePaddle Authors. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import os +import gc +import sys +import platform +import yaml +import time +import datetime +import paddle +import paddle.distributed as dist +from tqdm import tqdm +import cv2 +import numpy as np +import copy +from argparse import ArgumentParser, RawDescriptionHelpFormatter + +from ppocr.utils.stats import TrainingStats +from ppocr.utils.save_load import save_model +from ppocr.utils.utility import print_dict, AverageMeter +from ppocr.utils.logging import get_logger +from ppocr.utils.loggers import WandbLogger, Loggers +from ppocr.utils import profiler +from ppocr.data import build_dataloader +from ppocr.utils.export_model import export + + +class ArgsParser(ArgumentParser): + def __init__(self): + super(ArgsParser, self).__init__(formatter_class=RawDescriptionHelpFormatter) + self.add_argument("-c", "--config", help="configuration file to use") + self.add_argument("-o", "--opt", nargs="+", help="set configuration options") + self.add_argument( + "-p", + "--profiler_options", + type=str, + default=None, + help="The option of profiler, which should be in format " + '"key1=value1;key2=value2;key3=value3".', + ) + + def parse_args(self, argv=None): + args = super(ArgsParser, self).parse_args(argv) + assert args.config is not None, "Please specify --config=configure_file_path." + args.opt = self._parse_opt(args.opt) + return args + + def _parse_opt(self, opts): + config = {} + if not opts: + return config + for s in opts: + s = s.strip() + k, v = s.split("=") + config[k] = yaml.load(v, Loader=yaml.Loader) + return config + + +def load_config(file_path): + """ + Load config from yml/yaml file. + Args: + file_path (str): Path of the config file to be loaded. + Returns: global config + """ + _, ext = os.path.splitext(file_path) + assert ext in [".yml", ".yaml"], "only support yaml files for now" + config = yaml.load(open(file_path, "rb"), Loader=yaml.Loader) + return config + + +def merge_config(config, opts): + """ + Merge config into global config. + Args: + config (dict): Config to be merged. + Returns: global config + """ + for key, value in opts.items(): + if "." not in key: + if isinstance(value, dict) and key in config: + config[key].update(value) + else: + config[key] = value + else: + sub_keys = key.split(".") + assert sub_keys[0] in config, ( + "the sub_keys can only be one of global_config: {}, but get: " + "{}, please check your running command".format( + config.keys(), sub_keys[0] + ) + ) + cur = config[sub_keys[0]] + for idx, sub_key in enumerate(sub_keys[1:]): + if idx == len(sub_keys) - 2: + cur[sub_key] = value + else: + cur = cur[sub_key] + return config + + +def check_device(use_gpu, use_xpu=False, use_npu=False, use_mlu=False, use_gcu=False): + """ + Log error and exit when set use_gpu=true in paddlepaddle + cpu version. + """ + err = ( + "Config {} cannot be set as true while your paddle " + "is not compiled with {} ! \nPlease try: \n" + "\t1. Install paddlepaddle to run model on {} \n" + "\t2. Set {} as false in config file to run " + "model on CPU" + ) + + try: + if use_gpu and use_xpu: + print("use_xpu and use_gpu can not both be true.") + if use_gpu and not paddle.is_compiled_with_cuda(): + print(err.format("use_gpu", "cuda", "gpu", "use_gpu")) + sys.exit(1) + if use_xpu and not paddle.device.is_compiled_with_xpu(): + print(err.format("use_xpu", "xpu", "xpu", "use_xpu")) + sys.exit(1) + if use_npu: + if ( + int(paddle.version.major) != 0 + and int(paddle.version.major) <= 2 + and int(paddle.version.minor) <= 4 + ): + if not paddle.device.is_compiled_with_npu(): + print(err.format("use_npu", "npu", "npu", "use_npu")) + sys.exit(1) + # is_compiled_with_npu() has been updated after paddle-2.4 + else: + if not paddle.device.is_compiled_with_custom_device("npu"): + print(err.format("use_npu", "npu", "npu", "use_npu")) + sys.exit(1) + if use_mlu and not paddle.device.is_compiled_with_mlu(): + print(err.format("use_mlu", "mlu", "mlu", "use_mlu")) + sys.exit(1) + if use_gcu and not paddle.device.is_compiled_with_custom_device("gcu"): + print(err.format("use_gcu", "gcu", "gcu", "use_gcu")) + sys.exit(1) + except Exception as e: + pass + + +def to_float32(preds): + if isinstance(preds, dict): + for k in preds: + if isinstance(preds[k], dict) or isinstance(preds[k], list): + preds[k] = to_float32(preds[k]) + elif isinstance(preds[k], paddle.Tensor): + preds[k] = preds[k].astype(paddle.float32) + elif isinstance(preds, list): + for k in range(len(preds)): + if isinstance(preds[k], dict): + preds[k] = to_float32(preds[k]) + elif isinstance(preds[k], list): + preds[k] = to_float32(preds[k]) + elif isinstance(preds[k], paddle.Tensor): + preds[k] = preds[k].astype(paddle.float32) + elif isinstance(preds, paddle.Tensor): + preds = preds.astype(paddle.float32) + return preds + + +def train( + config, + train_dataloader, + valid_dataloader, + device, + model, + loss_class, + optimizer, + lr_scheduler, + post_process_class, + eval_class, + pre_best_model_dict, + logger, + step_pre_epoch, + log_writer=None, + scaler=None, + amp_level="O2", + amp_custom_black_list=[], + amp_custom_white_list=[], + amp_dtype="float16", +): + cal_metric_during_train = config["Global"].get("cal_metric_during_train", False) + calc_epoch_interval = config["Global"].get("calc_epoch_interval", 1) + log_smooth_window = config["Global"]["log_smooth_window"] + epoch_num = config["Global"]["epoch_num"] + print_batch_step = config["Global"]["print_batch_step"] + eval_batch_step = config["Global"]["eval_batch_step"] + eval_batch_epoch = config["Global"].get("eval_batch_epoch", None) + profiler_options = config["profiler_options"] + print_mem_info = config["Global"].get("print_mem_info", True) + uniform_output_enabled = config["Global"].get("uniform_output_enabled", False) + + global_step = 0 + if "global_step" in pre_best_model_dict: + global_step = pre_best_model_dict["global_step"] + start_eval_step = 0 + if isinstance(eval_batch_step, list) and len(eval_batch_step) >= 2: + start_eval_step = eval_batch_step[0] if not eval_batch_epoch else 0 + eval_batch_step = ( + eval_batch_step[1] + if not eval_batch_epoch + else step_pre_epoch * eval_batch_epoch + ) + if len(valid_dataloader) == 0: + logger.info( + "No Images in eval dataset, evaluation during training " + "will be disabled" + ) + start_eval_step = 1e111 + logger.info( + "During the training process, after the {}th iteration, " + "an evaluation is run every {} iterations".format( + start_eval_step, eval_batch_step + ) + ) + save_epoch_step = config["Global"]["save_epoch_step"] + save_model_dir = config["Global"]["save_model_dir"] + if not os.path.exists(save_model_dir): + os.makedirs(save_model_dir) + main_indicator = eval_class.main_indicator + best_model_dict = {main_indicator: 0} + best_model_dict.update(pre_best_model_dict) + train_stats = TrainingStats(log_smooth_window, ["lr"]) + model_average = False + model.train() + + use_srn = config["Architecture"]["algorithm"] == "SRN" + extra_input_models = [ + "SRN", + "NRTR", + "SAR", + "SEED", + "SVTR", + "SVTR_LCNet", + "SPIN", + "VisionLAN", + "RobustScanner", + "RFL", + "DRRG", + "SATRN", + "SVTR_HGNet", + "ParseQ", + "CPPD", + ] + extra_input = False + if config["Architecture"]["algorithm"] == "Distillation": + for key in config["Architecture"]["Models"]: + extra_input = ( + extra_input + or config["Architecture"]["Models"][key]["algorithm"] + in extra_input_models + ) + else: + extra_input = config["Architecture"]["algorithm"] in extra_input_models + try: + model_type = config["Architecture"]["model_type"] + except: + model_type = None + + algorithm = config["Architecture"]["algorithm"] + + start_epoch = ( + best_model_dict["start_epoch"] if "start_epoch" in best_model_dict else 1 + ) + + total_samples = 0 + train_reader_cost = 0.0 + train_batch_cost = 0.0 + reader_start = time.time() + eta_meter = AverageMeter() + + max_iter = ( + len(train_dataloader) - 1 + if platform.system() == "Windows" + else len(train_dataloader) + ) + + for epoch in range(start_epoch, epoch_num + 1): + if train_dataloader.dataset.need_reset: + train_dataloader = build_dataloader( + config, "Train", device, logger, seed=epoch + ) + max_iter = ( + len(train_dataloader) - 1 + if platform.system() == "Windows" + else len(train_dataloader) + ) + + for idx, batch in enumerate(train_dataloader): + model.train() + profiler.add_profiler_step(profiler_options) + train_reader_cost += time.time() - reader_start + if idx >= max_iter: + break + lr = optimizer.get_lr() + images = batch[0] + if use_srn: + model_average = True + # use amp + if scaler: + with paddle.amp.auto_cast( + level=amp_level, + custom_black_list=amp_custom_black_list, + custom_white_list=amp_custom_white_list, + dtype=amp_dtype, + ): + if model_type == "table" or extra_input: + preds = model(images, data=batch[1:]) + elif model_type in ["kie"]: + preds = model(batch) + elif algorithm in ["CAN"]: + preds = model(batch[:3]) + elif algorithm in [ + "LaTeXOCR", + "UniMERNet", + "PP-FormulaNet-S", + "PP-FormulaNet-L", + ]: + preds = model(batch) + else: + preds = model(images) + preds = to_float32(preds) + loss = loss_class(preds, batch) + avg_loss = loss["loss"] + scaled_avg_loss = scaler.scale(avg_loss) + scaled_avg_loss.backward() + scaler.minimize(optimizer, scaled_avg_loss) + else: + if model_type == "table" or extra_input: + preds = model(images, data=batch[1:]) + elif model_type in ["kie", "sr"]: + preds = model(batch) + elif algorithm in ["CAN"]: + preds = model(batch[:3]) + elif algorithm in [ + "LaTeXOCR", + "UniMERNet", + "PP-FormulaNet-S", + "PP-FormulaNet-L", + ]: + preds = model(batch) + else: + preds = model(images) + loss = loss_class(preds, batch) + avg_loss = loss["loss"] + avg_loss.backward() + optimizer.step() + + optimizer.clear_grad() + + if ( + cal_metric_during_train and epoch % calc_epoch_interval == 0 + ): # only rec and cls need + batch = [item.numpy() for item in batch] + if model_type in ["kie", "sr"]: + eval_class(preds, batch) + elif model_type in ["table"]: + post_result = post_process_class(preds, batch) + eval_class(post_result, batch) + elif algorithm in ["CAN"]: + model_type = "can" + eval_class(preds[0], batch[2:], epoch_reset=(idx == 0)) + elif algorithm in ["LaTeXOCR"]: + model_type = "latexocr" + post_result = post_process_class(preds, batch[1], mode="train") + eval_class(post_result[0], post_result[1], epoch_reset=(idx == 0)) + elif algorithm in ["UniMERNet"]: + model_type = "unimernet" + post_result = post_process_class(preds[0], batch[1], mode="train") + eval_class(post_result[0], post_result[1], epoch_reset=(idx == 0)) + elif algorithm in ["PP-FormulaNet-S", "PP-FormulaNet-L"]: + model_type = "pp_formulanet" + post_result = post_process_class(preds[0], batch[1], mode="train") + eval_class(post_result[0], post_result[1], epoch_reset=(idx == 0)) + else: + if config["Loss"]["name"] in [ + "MultiLoss", + "MultiLoss_v2", + ]: # for multi head loss + post_result = post_process_class( + preds["ctc"], batch[1] + ) # for CTC head out + elif config["Loss"]["name"] in ["VLLoss"]: + post_result = post_process_class(preds, batch[1], batch[-1]) + else: + post_result = post_process_class(preds, batch[1]) + eval_class(post_result, batch) + metric = eval_class.get_metric() + train_stats.update(metric) + + train_batch_time = time.time() - reader_start + train_batch_cost += train_batch_time + eta_meter.update(train_batch_time) + global_step += 1 + total_samples += len(images) + + if not isinstance(lr_scheduler, float): + lr_scheduler.step() + + # logger and visualdl + stats = { + k: float(v) if v.shape == [] else v.numpy().mean() + for k, v in loss.items() + } + stats["lr"] = lr + train_stats.update(stats) + + if log_writer is not None and dist.get_rank() == 0: + log_writer.log_metrics( + metrics=train_stats.get(), prefix="TRAIN", step=global_step + ) + + if (global_step > 0 and global_step % print_batch_step == 0) or ( + idx >= len(train_dataloader) - 1 + ): + logs = train_stats.log() + + eta_sec = ( + (epoch_num + 1 - epoch) * len(train_dataloader) - idx - 1 + ) * eta_meter.avg + eta_sec_format = str(datetime.timedelta(seconds=int(eta_sec))) + max_mem_reserved_str = "" + max_mem_allocated_str = "" + if paddle.device.is_compiled_with_cuda() and print_mem_info: + max_mem_reserved_str = f", max_mem_reserved: {paddle.device.cuda.max_memory_reserved() // (1024 ** 2)} MB," + max_mem_allocated_str = f" max_mem_allocated: {paddle.device.cuda.max_memory_allocated() // (1024 ** 2)} MB" + strs = ( + "epoch: [{}/{}], global_step: {}, {}, avg_reader_cost: " + "{:.5f} s, avg_batch_cost: {:.5f} s, avg_samples: {}, " + "ips: {:.5f} samples/s, eta: {}{}{}".format( + epoch, + epoch_num, + global_step, + logs, + train_reader_cost / print_batch_step, + train_batch_cost / print_batch_step, + total_samples / print_batch_step, + total_samples / train_batch_cost, + eta_sec_format, + max_mem_reserved_str, + max_mem_allocated_str, + ) + ) + logger.info(strs) + + total_samples = 0 + train_reader_cost = 0.0 + train_batch_cost = 0.0 + # eval + if ( + global_step > start_eval_step + and (global_step - start_eval_step) % eval_batch_step == 0 + and dist.get_rank() == 0 + ): + if model_average: + Model_Average = paddle.incubate.ModelAverage( + 0.15, + parameters=model.parameters(), + min_average_window=10000, + max_average_window=15625, + ) + Model_Average.apply() + cur_metric = eval( + model, + valid_dataloader, + post_process_class, + eval_class, + model_type, + extra_input=extra_input, + scaler=scaler, + amp_level=amp_level, + amp_custom_black_list=amp_custom_black_list, + amp_custom_white_list=amp_custom_white_list, + amp_dtype=amp_dtype, + ) + cur_metric_str = "cur metric, {}".format( + ", ".join(["{}: {}".format(k, v) for k, v in cur_metric.items()]) + ) + logger.info(cur_metric_str) + + # logger metric + if log_writer is not None: + log_writer.log_metrics( + metrics=cur_metric, prefix="EVAL", step=global_step + ) + + if cur_metric[main_indicator] >= best_model_dict[main_indicator]: + best_model_dict.update(cur_metric) + best_model_dict["best_epoch"] = epoch + prefix = "best_accuracy" + if uniform_output_enabled: + export( + config, + model, + os.path.join(save_model_dir, prefix, "inference"), + ) + gc.collect() + model_info = {"epoch": epoch, "metric": best_model_dict} + else: + model_info = None + save_model( + model, + optimizer, + ( + os.path.join(save_model_dir, prefix) + if uniform_output_enabled + else save_model_dir + ), + logger, + config, + is_best=True, + prefix=prefix, + save_model_info=model_info, + best_model_dict=best_model_dict, + epoch=epoch, + global_step=global_step, + ) + best_str = "best metric, {}".format( + ", ".join( + ["{}: {}".format(k, v) for k, v in best_model_dict.items()] + ) + ) + logger.info(best_str) + # logger best metric + if log_writer is not None: + log_writer.log_metrics( + metrics={ + "best_{}".format(main_indicator): best_model_dict[ + main_indicator + ] + }, + prefix="EVAL", + step=global_step, + ) + + log_writer.log_model( + is_best=True, prefix="best_accuracy", metadata=best_model_dict + ) + + reader_start = time.time() + if dist.get_rank() == 0: + prefix = "latest" + if uniform_output_enabled: + export(config, model, os.path.join(save_model_dir, prefix, "inference")) + gc.collect() + model_info = {"epoch": epoch, "metric": best_model_dict} + else: + model_info = None + save_model( + model, + optimizer, + ( + os.path.join(save_model_dir, prefix) + if uniform_output_enabled + else save_model_dir + ), + logger, + config, + is_best=False, + prefix=prefix, + save_model_info=model_info, + best_model_dict=best_model_dict, + epoch=epoch, + global_step=global_step, + ) + + if log_writer is not None: + log_writer.log_model(is_best=False, prefix="latest") + + if dist.get_rank() == 0 and epoch > 0 and epoch % save_epoch_step == 0: + prefix = "iter_epoch_{}".format(epoch) + if uniform_output_enabled: + export(config, model, os.path.join(save_model_dir, prefix, "inference")) + gc.collect() + model_info = {"epoch": epoch, "metric": best_model_dict} + else: + model_info = None + save_model( + model, + optimizer, + ( + os.path.join(save_model_dir, prefix) + if uniform_output_enabled + else save_model_dir + ), + logger, + config, + is_best=False, + prefix=prefix, + save_model_info=model_info, + best_model_dict=best_model_dict, + epoch=epoch, + global_step=global_step, + done_flag=epoch == config["Global"]["epoch_num"], + ) + if log_writer is not None: + log_writer.log_model( + is_best=False, prefix="iter_epoch_{}".format(epoch) + ) + + best_str = "best metric, {}".format( + ", ".join(["{}: {}".format(k, v) for k, v in best_model_dict.items()]) + ) + logger.info(best_str) + if dist.get_rank() == 0 and log_writer is not None: + log_writer.close() + return + + +def eval( + model, + valid_dataloader, + post_process_class, + eval_class, + model_type=None, + extra_input=False, + scaler=None, + amp_level="O2", + amp_custom_black_list=[], + amp_custom_white_list=[], + amp_dtype="float16", +): + model.eval() + with paddle.no_grad(): + total_frame = 0.0 + total_time = 0.0 + pbar = tqdm( + total=len(valid_dataloader), desc="eval model:", position=0, leave=True + ) + max_iter = ( + len(valid_dataloader) - 1 + if platform.system() == "Windows" + else len(valid_dataloader) + ) + sum_images = 0 + for idx, batch in enumerate(valid_dataloader): + if idx >= max_iter: + break + images = batch[0] + start = time.time() + + # use amp + if scaler: + with paddle.amp.auto_cast( + level=amp_level, + custom_black_list=amp_custom_black_list, + dtype=amp_dtype, + ): + if model_type == "table" or extra_input: + preds = model(images, data=batch[1:]) + elif model_type in ["kie"]: + preds = model(batch) + elif model_type in ["can"]: + preds = model(batch[:3]) + elif model_type in ["latexocr"]: + preds = model(batch) + elif model_type in ["sr"]: + preds = model(batch) + sr_img = preds["sr_img"] + lr_img = preds["lr_img"] + else: + preds = model(images) + preds = to_float32(preds) + else: + if model_type == "table" or extra_input: + preds = model(images, data=batch[1:]) + elif model_type in ["kie"]: + preds = model(batch) + elif model_type in ["can"]: + preds = model(batch[:3]) + elif model_type in ["latexocr", "unimernet", "pp_formulanet"]: + preds = model(batch) + elif model_type in ["sr"]: + preds = model(batch) + sr_img = preds["sr_img"] + lr_img = preds["lr_img"] + else: + preds = model(images) + + batch_numpy = [] + for item in batch: + if isinstance(item, paddle.Tensor): + batch_numpy.append(item.numpy()) + else: + batch_numpy.append(item) + # Obtain usable results from post-processing methods + total_time += time.time() - start + # Evaluate the results of the current batch + if model_type in ["table", "kie"]: + if post_process_class is None: + eval_class(preds, batch_numpy) + else: + post_result = post_process_class(preds, batch_numpy) + eval_class(post_result, batch_numpy) + elif model_type in ["sr"]: + eval_class(preds, batch_numpy) + elif model_type in ["can"]: + eval_class(preds[0], batch_numpy[2:], epoch_reset=(idx == 0)) + elif model_type in ["latexocr", "unimernet", "pp_formulanet"]: + post_result = post_process_class(preds, batch[1], "eval") + eval_class(post_result[0], post_result[1], epoch_reset=(idx == 0)) + else: + post_result = post_process_class(preds, batch_numpy[1]) + eval_class(post_result, batch_numpy) + + pbar.update(1) + total_frame += len(images) + sum_images += 1 + # Get final metric,eg. acc or hmean + metric = eval_class.get_metric() + + pbar.close() + model.train() + # Avoid ZeroDivisionError + if total_time > 0: + metric["fps"] = total_frame / total_time + else: + metric["fps"] = 0 # or set to a fallback value + return metric + + +def update_center(char_center, post_result, preds): + result, label = post_result + feats, logits = preds + logits = paddle.argmax(logits, axis=-1) + feats = feats.numpy() + logits = logits.numpy() + + for idx_sample in range(len(label)): + if result[idx_sample][0] == label[idx_sample][0]: + feat = feats[idx_sample] + logit = logits[idx_sample] + for idx_time in range(len(logit)): + index = logit[idx_time] + if index in char_center.keys(): + char_center[index][0] = ( + char_center[index][0] * char_center[index][1] + feat[idx_time] + ) / (char_center[index][1] + 1) + char_center[index][1] += 1 + else: + char_center[index] = [feat[idx_time], 1] + return char_center + + +def get_center(model, eval_dataloader, post_process_class): + pbar = tqdm(total=len(eval_dataloader), desc="get center:") + max_iter = ( + len(eval_dataloader) - 1 + if platform.system() == "Windows" + else len(eval_dataloader) + ) + char_center = dict() + for idx, batch in enumerate(eval_dataloader): + if idx >= max_iter: + break + images = batch[0] + start = time.time() + preds = model(images) + + batch = [item.numpy() for item in batch] + # Obtain usable results from post-processing methods + post_result = post_process_class(preds, batch[1]) + + # update char_center + char_center = update_center(char_center, post_result, preds) + pbar.update(1) + + pbar.close() + for key in char_center.keys(): + char_center[key] = char_center[key][0] + return char_center + + +def preprocess(is_train=False): + FLAGS = ArgsParser().parse_args() + profiler_options = FLAGS.profiler_options + config = load_config(FLAGS.config) + config = merge_config(config, FLAGS.opt) + profile_dic = {"profiler_options": FLAGS.profiler_options} + config = merge_config(config, profile_dic) + + if is_train: + # save_config + save_model_dir = config["Global"]["save_model_dir"] + os.makedirs(save_model_dir, exist_ok=True) + with open(os.path.join(save_model_dir, "config.yml"), "w") as f: + yaml.dump(dict(config), f, default_flow_style=False, sort_keys=False) + log_file = "{}/train.log".format(save_model_dir) + else: + log_file = None + + log_ranks = config["Global"].get("log_ranks", "0") + logger = get_logger(log_file=log_file, log_ranks=log_ranks) + + # check if set use_gpu=True in paddlepaddle cpu version + use_gpu = config["Global"].get("use_gpu", False) + use_xpu = config["Global"].get("use_xpu", False) + use_npu = config["Global"].get("use_npu", False) + use_mlu = config["Global"].get("use_mlu", False) + use_gcu = config["Global"].get("use_gcu", False) + + alg = config["Architecture"]["algorithm"] + assert alg in [ + "EAST", + "DB", + "SAST", + "Rosetta", + "CRNN", + "STARNet", + "RARE", + "SRN", + "CLS", + "PGNet", + "Distillation", + "NRTR", + "TableAttn", + "SAR", + "PSE", + "SEED", + "SDMGR", + "LayoutXLM", + "LayoutLM", + "LayoutLMv2", + "PREN", + "FCE", + "SVTR", + "SVTR_LCNet", + "ViTSTR", + "ABINet", + "DB++", + "TableMaster", + "SPIN", + "VisionLAN", + "Gestalt", + "SLANet", + "RobustScanner", + "CT", + "RFL", + "DRRG", + "CAN", + "Telescope", + "SATRN", + "SVTR_HGNet", + "ParseQ", + "CPPD", + "LaTeXOCR", + "UniMERNet", + "SLANeXt", + "PP-FormulaNet-S", + "PP-FormulaNet-L", + ] + + if use_xpu: + device = "xpu:{0}".format(os.getenv("FLAGS_selected_xpus", 0)) + elif use_npu: + device = "npu:{0}".format(os.getenv("FLAGS_selected_npus", 0)) + elif use_mlu: + device = "mlu:{0}".format(os.getenv("FLAGS_selected_mlus", 0)) + elif use_gcu: # Use Enflame GCU(General Compute Unit) + device = "gcu:{0}".format(os.getenv("FLAGS_selected_gcus", 0)) + else: + device = "gpu:{}".format(dist.ParallelEnv().dev_id) if use_gpu else "cpu" + check_device(use_gpu, use_xpu, use_npu, use_mlu, use_gcu) + + device = paddle.set_device(device) + + config["Global"]["distributed"] = dist.get_world_size() != 1 + + loggers = [] + + if "use_visualdl" in config["Global"] and config["Global"]["use_visualdl"]: + logger.warning( + "You are using VisualDL, the VisualDL is deprecated and " + "removed in ppocr!" + ) + log_writer = None + if ( + "use_wandb" in config["Global"] and config["Global"]["use_wandb"] + ) or "wandb" in config: + save_dir = config["Global"]["save_model_dir"] + wandb_writer_path = "{}/wandb".format(save_dir) + if "wandb" in config: + wandb_params = config["wandb"] + else: + wandb_params = dict() + wandb_params.update({"save_dir": save_dir}) + log_writer = WandbLogger(**wandb_params, config=config) + loggers.append(log_writer) + else: + log_writer = None + print_dict(config, logger) + + if loggers: + log_writer = Loggers(loggers) + else: + log_writer = None + + logger.info("train with paddle {} and device {}".format(paddle.__version__, device)) + return config, device, logger, log_writer diff --git a/modules/onnx_ocr_module/src/tools/test_hubserving.py b/modules/onnx_ocr_module/src/tools/test_hubserving.py new file mode 100644 index 0000000..5480107 --- /dev/null +++ b/modules/onnx_ocr_module/src/tools/test_hubserving.py @@ -0,0 +1,162 @@ +# Copyright (c) 2020 PaddlePaddle Authors. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +import os +import sys + +__dir__ = os.path.dirname(os.path.abspath(__file__)) +sys.path.append(__dir__) +sys.path.append(os.path.abspath(os.path.join(__dir__, ".."))) + +from ppocr.utils.logging import get_logger + +logger = get_logger() + +import cv2 +import numpy as np +import time +from PIL import Image +from ppocr.utils.utility import get_image_file_list +from tools.infer.utility import draw_ocr, draw_boxes, str2bool +from ppstructure.utility import draw_structure_result +from ppstructure.predict_system import to_excel + +import requests +import json +import base64 + + +def cv2_to_base64(image): + return base64.b64encode(image).decode("utf8") + + +def draw_server_result(image_file, res): + img = cv2.imread(image_file) + image = Image.fromarray(cv2.cvtColor(img, cv2.COLOR_BGR2RGB)) + if len(res) == 0: + return np.array(image) + keys = res[0].keys() + if "text_region" not in keys: # for ocr_rec, draw function is invalid + logger.info("draw function is invalid for ocr_rec!") + return None + elif "text" not in keys: # for ocr_det + logger.info("draw text boxes only!") + boxes = [] + for dno in range(len(res)): + boxes.append(res[dno]["text_region"]) + boxes = np.array(boxes) + draw_img = draw_boxes(image, boxes) + return draw_img + else: # for ocr_system + logger.info("draw boxes and texts!") + boxes = [] + texts = [] + scores = [] + for dno in range(len(res)): + boxes.append(res[dno]["text_region"]) + texts.append(res[dno]["text"]) + scores.append(res[dno]["confidence"]) + boxes = np.array(boxes) + scores = np.array(scores) + draw_img = draw_ocr(image, boxes, texts, scores, draw_txt=True, drop_score=0.5) + return draw_img + + +def save_structure_res(res, save_folder, image_file): + img = cv2.imread(image_file) + excel_save_folder = os.path.join(save_folder, os.path.basename(image_file)) + os.makedirs(excel_save_folder, exist_ok=True) + # save res + with open(os.path.join(excel_save_folder, "res.txt"), "w", encoding="utf8") as f: + for region in res: + if region["type"] == "Table": + excel_path = os.path.join( + excel_save_folder, "{}.xlsx".format(region["bbox"]) + ) + to_excel(region["res"], excel_path) + elif region["type"] == "Figure": + x1, y1, x2, y2 = region["bbox"] + print(region["bbox"]) + roi_img = img[y1:y2, x1:x2, :] + img_path = os.path.join( + excel_save_folder, "{}.jpg".format(region["bbox"]) + ) + cv2.imwrite(img_path, roi_img) + else: + for text_result in region["res"]: + f.write("{}\n".format(json.dumps(text_result))) + + +def main(args): + image_file_list = get_image_file_list(args.image_dir) + is_visualize = False + headers = {"Content-type": "application/json"} + cnt = 0 + total_time = 0 + for image_file in image_file_list: + img = open(image_file, "rb").read() + if img is None: + logger.info("error in loading image:{}".format(image_file)) + continue + img_name = os.path.basename(image_file) + # seed http request + starttime = time.time() + data = {"images": [cv2_to_base64(img)]} + r = requests.post(url=args.server_url, headers=headers, data=json.dumps(data)) + elapse = time.time() - starttime + total_time += elapse + logger.info("Predict time of %s: %.3fs" % (image_file, elapse)) + res = r.json()["results"][0] + logger.info(res) + + if args.visualize: + draw_img = None + if "structure_table" in args.server_url: + to_excel(res["html"], "./{}.xlsx".format(img_name)) + elif "structure_system" in args.server_url: + save_structure_res(res["regions"], args.output, image_file) + else: + draw_img = draw_server_result(image_file, res) + if draw_img is not None: + if not os.path.exists(args.output): + os.makedirs(args.output) + cv2.imwrite( + os.path.join(args.output, os.path.basename(image_file)), + draw_img[:, :, ::-1], + ) + logger.info( + "The visualized image saved in {}".format( + os.path.join(args.output, os.path.basename(image_file)) + ) + ) + cnt += 1 + if cnt % 100 == 0: + logger.info("{} processed".format(cnt)) + logger.info("avg time cost: {}".format(float(total_time) / cnt)) + + +def parse_args(): + import argparse + + parser = argparse.ArgumentParser(description="args for hub serving") + parser.add_argument("--server_url", type=str, required=True) + parser.add_argument("--image_dir", type=str, required=True) + parser.add_argument("--visualize", type=str2bool, default=False) + parser.add_argument("--output", type=str, default="./hubserving_result") + args = parser.parse_args() + return args + + +if __name__ == "__main__": + args = parse_args() + main(args) diff --git a/modules/onnx_ocr_module/src/tools/train.py b/modules/onnx_ocr_module/src/tools/train.py new file mode 100644 index 0000000..0bddc49 --- /dev/null +++ b/modules/onnx_ocr_module/src/tools/train.py @@ -0,0 +1,271 @@ +# Copyright (c) 2020 PaddlePaddle Authors. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import os +import sys + +__dir__ = os.path.dirname(os.path.abspath(__file__)) +sys.path.append(__dir__) +sys.path.insert(0, os.path.abspath(os.path.join(__dir__, ".."))) + +import yaml +import paddle +import paddle.distributed as dist + +from ppocr.data import build_dataloader, set_signal_handlers +from ppocr.modeling.architectures import build_model +from ppocr.losses import build_loss +from ppocr.optimizer import build_optimizer +from ppocr.postprocess import build_post_process +from ppocr.metrics import build_metric +from ppocr.utils.save_load import load_model +from ppocr.utils.utility import set_seed +from ppocr.modeling.architectures import apply_to_static +import tools.program as program +import tools.naive_sync_bn as naive_sync_bn + +dist.get_world_size() + + +def main(config, device, logger, vdl_writer, seed): + # init dist environment + if config["Global"]["distributed"]: + dist.init_parallel_env() + + global_config = config["Global"] + + # build dataloader + set_signal_handlers() + train_dataloader = build_dataloader(config, "Train", device, logger, seed) + if len(train_dataloader) == 0: + logger.error( + "No Images in train dataset, please ensure\n" + + "\t1. The images num in the train label_file_list should be larger than or equal with batch size.\n" + + "\t2. The annotation file and path in the configuration file are provided normally." + ) + return + + if config["Eval"]: + valid_dataloader = build_dataloader(config, "Eval", device, logger, seed) + else: + valid_dataloader = None + step_pre_epoch = len(train_dataloader) + + # build post process + post_process_class = build_post_process(config["PostProcess"], global_config) + + # build model + # for rec algorithm + if hasattr(post_process_class, "character"): + char_num = len(getattr(post_process_class, "character")) + if config["Architecture"]["algorithm"] in [ + "Distillation", + ]: # distillation model + for key in config["Architecture"]["Models"]: + if ( + config["Architecture"]["Models"][key]["Head"]["name"] == "MultiHead" + ): # for multi head + if config["PostProcess"]["name"] == "DistillationSARLabelDecode": + char_num = char_num - 2 + if config["PostProcess"]["name"] == "DistillationNRTRLabelDecode": + char_num = char_num - 3 + out_channels_list = {} + out_channels_list["CTCLabelDecode"] = char_num + # update SARLoss params + if ( + list(config["Loss"]["loss_config_list"][-1].keys())[0] + == "DistillationSARLoss" + ): + config["Loss"]["loss_config_list"][-1]["DistillationSARLoss"][ + "ignore_index" + ] = (char_num + 1) + out_channels_list["SARLabelDecode"] = char_num + 2 + elif any( + "DistillationNRTRLoss" in d + for d in config["Loss"]["loss_config_list"] + ): + out_channels_list["NRTRLabelDecode"] = char_num + 3 + + config["Architecture"]["Models"][key]["Head"][ + "out_channels_list" + ] = out_channels_list + else: + config["Architecture"]["Models"][key]["Head"][ + "out_channels" + ] = char_num + elif config["Architecture"]["Head"]["name"] == "MultiHead": # for multi head + if config["PostProcess"]["name"] == "SARLabelDecode": + char_num = char_num - 2 + if config["PostProcess"]["name"] == "NRTRLabelDecode": + char_num = char_num - 3 + out_channels_list = {} + out_channels_list["CTCLabelDecode"] = char_num + # update SARLoss params + if list(config["Loss"]["loss_config_list"][1].keys())[0] == "SARLoss": + if config["Loss"]["loss_config_list"][1]["SARLoss"] is None: + config["Loss"]["loss_config_list"][1]["SARLoss"] = { + "ignore_index": char_num + 1 + } + else: + config["Loss"]["loss_config_list"][1]["SARLoss"]["ignore_index"] = ( + char_num + 1 + ) + out_channels_list["SARLabelDecode"] = char_num + 2 + elif list(config["Loss"]["loss_config_list"][1].keys())[0] == "NRTRLoss": + out_channels_list["NRTRLabelDecode"] = char_num + 3 + config["Architecture"]["Head"]["out_channels_list"] = out_channels_list + else: # base rec model + config["Architecture"]["Head"]["out_channels"] = char_num + + if config["PostProcess"]["name"] == "SARLabelDecode": # for SAR model + config["Loss"]["ignore_index"] = char_num - 1 + + model = build_model(config["Architecture"]) + + use_sync_bn = config["Global"].get("use_sync_bn", False) + if use_sync_bn: + if config["Global"].get("use_npu", False): + naive_sync_bn.convert_syncbn(model) + else: + model = paddle.nn.SyncBatchNorm.convert_sync_batchnorm(model) + logger.info("convert_sync_batchnorm") + + model = apply_to_static(model, config, logger) + + # build loss + loss_class = build_loss(config["Loss"]) + + # build optim + optimizer, lr_scheduler = build_optimizer( + config["Optimizer"], + epochs=config["Global"]["epoch_num"], + step_each_epoch=len(train_dataloader), + model=model, + ) + + # build metric + eval_class = build_metric(config["Metric"]) + + logger.info("train dataloader has {} iters".format(len(train_dataloader))) + if valid_dataloader is not None: + logger.info("valid dataloader has {} iters".format(len(valid_dataloader))) + + use_amp = config["Global"].get("use_amp", False) + amp_level = config["Global"].get("amp_level", "O2") + amp_dtype = config["Global"].get("amp_dtype", "float16") + amp_custom_black_list = config["Global"].get("amp_custom_black_list", []) + amp_custom_white_list = config["Global"].get("amp_custom_white_list", []) + if os.path.exists( + os.path.join(config["Global"]["save_model_dir"], "train_result.json") + ): + try: + os.remove( + os.path.join(config["Global"]["save_model_dir"], "train_result.json") + ) + except: + pass + if use_amp: + AMP_RELATED_FLAGS_SETTING = {} + if paddle.is_compiled_with_cuda(): + AMP_RELATED_FLAGS_SETTING.update( + { + "FLAGS_cudnn_batchnorm_spatial_persistent": 1, + "FLAGS_gemm_use_half_precision_compute_type": 0, + } + ) + paddle.set_flags(AMP_RELATED_FLAGS_SETTING) + scale_loss = config["Global"].get("scale_loss", 1.0) + use_dynamic_loss_scaling = config["Global"].get( + "use_dynamic_loss_scaling", False + ) + scaler = paddle.amp.GradScaler( + init_loss_scaling=scale_loss, + use_dynamic_loss_scaling=use_dynamic_loss_scaling, + ) + if amp_level == "O2": + model, optimizer = paddle.amp.decorate( + models=model, + optimizers=optimizer, + level=amp_level, + master_weight=True, + dtype=amp_dtype, + ) + else: + scaler = None + + # load pretrain model + pre_best_model_dict = load_model( + config, model, optimizer, config["Architecture"]["model_type"] + ) + + if config["Global"]["distributed"]: + find_unused_parameters = config["Global"].get("find_unused_parameters", False) + model = paddle.DataParallel( + model, find_unused_parameters=find_unused_parameters + ) + # start train + program.train( + config, + train_dataloader, + valid_dataloader, + device, + model, + loss_class, + optimizer, + lr_scheduler, + post_process_class, + eval_class, + pre_best_model_dict, + logger, + step_pre_epoch, + vdl_writer, + scaler, + amp_level, + amp_custom_black_list, + amp_custom_white_list, + amp_dtype, + ) + + +def test_reader(config, device, logger): + loader = build_dataloader(config, "Train", device, logger) + import time + + starttime = time.time() + count = 0 + try: + for data in loader(): + count += 1 + if count % 1 == 0: + batch_time = time.time() - starttime + starttime = time.time() + logger.info( + "reader: {}, {}, {}".format(count, len(data[0]), batch_time) + ) + except Exception as e: + logger.info(e) + logger.info("finish reader: {}, Success!".format(count)) + + +if __name__ == "__main__": + config, device, logger, vdl_writer = program.preprocess(is_train=True) + seed = config["Global"]["seed"] if "seed" in config["Global"] else 1024 + set_seed(seed) + main(config, device, logger, vdl_writer, seed) + # test_reader(config, device, logger) diff --git a/modules/onnx_ocr_module/test/1.jpg b/modules/onnx_ocr_module/test/1.jpg new file mode 100644 index 0000000..d2aaea8 Binary files /dev/null and b/modules/onnx_ocr_module/test/1.jpg differ diff --git a/modules/onnx_ocr_module/test/compare_ocr_modules.py b/modules/onnx_ocr_module/test/compare_ocr_modules.py new file mode 100644 index 0000000..ac527c1 --- /dev/null +++ b/modules/onnx_ocr_module/test/compare_ocr_modules.py @@ -0,0 +1,406 @@ +# -*- coding: utf-8 -*- +""" +기존 OCRModule vs 새로운 ONNX OCRModule 비교 테스트 +완전히 동일한 인터페이스로 성능과 정확도 비교 +""" + +import os +import sys +import time +import logging +import traceback +from typing import Dict, List, Any + +# 경로 설정 +current_dir = os.path.dirname(__file__) +project_root = os.path.dirname(os.path.dirname(current_dir)) +onnx_module_dir = os.path.dirname(current_dir) + +sys.path.insert(0, project_root) # 기존 프로젝트 root +sys.path.insert(0, os.path.join(onnx_module_dir, 'src')) # ONNX 모듈 + +# Mock 클래스들 (기존 프로젝트 의존성 대체) +class MockGPUManager: + def __init__(self, can_use_cuda=True): + self.can_use_cuda = can_use_cuda + +class MockLogger: + def __init__(self, verbose=True): + self.verbose = verbose + + def log(self, message: str, level=logging.INFO, exc_info=False): + if not self.verbose: + return + + level_name = { + logging.DEBUG: "DEBUG", + logging.INFO: "INFO", + logging.WARNING: "WARNING", + logging.ERROR: "ERROR" + }.get(level, "INFO") + + print(f"[{level_name}] {message}") + + if exc_info and level >= logging.ERROR: + traceback.print_exc() + +class OCRModuleComparator: + """OCRModule들을 비교하는 클래스""" + + def __init__(self): + self.test_image = os.path.join(current_dir, "1.jpg") + self.results = {} + + def create_mock_dependencies(self, use_cuda=True, verbose=False): + """Mock 의존성 객체들 생성""" + gpu_manager = MockGPUManager(can_use_cuda=use_cuda) + logger = MockLogger(verbose=verbose) + return gpu_manager, logger + + def test_original_ocr_module(self, use_cuda=True, verbose=False): + """기존 PaddleOCR 기반 OCRModule 테스트""" + print(f"\n🐼 기존 OCRModule 테스트 (CUDA: {use_cuda})") + print("-" * 50) + + try: + # 기존 OCRModule import 시도 + try: + from src.modules.ocr_module import OCRModule + except ImportError: + print("❌ 기존 OCRModule을 찾을 수 없습니다. 경로를 확인해주세요.") + return None + + gpu_manager, logger = self.create_mock_dependencies(use_cuda, verbose) + + # 모듈 초기화 + start_time = time.time() + ocr_module = OCRModule( + logger=logger, + base_dir=project_root, + gpu_manager=gpu_manager + ) + init_time = (time.time() - start_time) * 1000 + + # OCR 테스트 + start_time = time.time() + results = ocr_module.detect_text(self.test_image, method='polygon') + inference_time = (time.time() - start_time) * 1000 + + # 결과 저장 + test_results = { + 'module_type': 'PaddleOCR', + 'use_cuda': use_cuda, + 'init_time_ms': init_time, + 'inference_time_ms': inference_time, + 'text_count': len(results), + 'results': results, + 'success': True + } + + print(f"✅ 초기화: {init_time:.1f}ms") + print(f"✅ 추론: {inference_time:.1f}ms") + print(f"✅ 텍스트 수: {len(results)}개") + + if results: + print(f"📝 샘플 결과:") + for i, result in enumerate(results[:3]): + print(f" {i+1}. '{result['text']}' (신뢰도: {result['confidence']:.3f})") + + return test_results + + except Exception as e: + print(f"❌ 기존 OCRModule 테스트 실패: {e}") + if verbose: + traceback.print_exc() + return { + 'module_type': 'PaddleOCR', + 'use_cuda': use_cuda, + 'success': False, + 'error': str(e) + } + + def test_onnx_ocr_module(self, use_cuda=True, verbose=False): + """새로운 ONNX 기반 OCRModule 테스트""" + print(f"\n🚀 ONNX OCRModule 테스트 (CUDA: {use_cuda})") + print("-" * 50) + + try: + from onnx_ocr_wrapper import ONNXOCRModule + + gpu_manager, logger = self.create_mock_dependencies(use_cuda, verbose) + + # 모듈 초기화 + start_time = time.time() + ocr_module = ONNXOCRModule( + logger=logger, + base_dir=onnx_module_dir, + gpu_manager=gpu_manager + ) + init_time = (time.time() - start_time) * 1000 + + # OCR 테스트 + start_time = time.time() + results = ocr_module.detect_text(self.test_image, method='polygon') + inference_time = (time.time() - start_time) * 1000 + + # 결과 저장 + test_results = { + 'module_type': 'ONNX', + 'use_cuda': use_cuda, + 'init_time_ms': init_time, + 'inference_time_ms': inference_time, + 'text_count': len(results), + 'results': results, + 'success': True + } + + print(f"✅ 초기화: {init_time:.1f}ms") + print(f"✅ 추론: {inference_time:.1f}ms") + print(f"✅ 텍스트 수: {len(results)}개") + + if results: + print(f"📝 샘플 결과:") + for i, result in enumerate(results[:3]): + print(f" {i+1}. '{result['text']}' (신뢰도: {result['confidence']:.3f})") + + return test_results + + except Exception as e: + print(f"❌ ONNX OCRModule 테스트 실패: {e}") + if verbose: + traceback.print_exc() + return { + 'module_type': 'ONNX', + 'use_cuda': use_cuda, + 'success': False, + 'error': str(e) + } + + def test_detection_methods(self, ocr_module, module_name): + """다양한 감지 방식 테스트""" + print(f"\n🔍 {module_name} 감지 방식 테스트") + print("-" * 50) + + methods = ['polygon', 'bbox', 'expanded_bbox', 'rotated_bbox', 'contour'] + method_results = {} + + for method in methods: + try: + start_time = time.time() + results = ocr_module.detect_text(self.test_image, method=method) + inference_time = (time.time() - start_time) * 1000 + + method_results[method] = { + 'text_count': len(results), + 'inference_time_ms': inference_time, + 'success': True + } + + print(f"✅ {method}: {len(results)}개 텍스트, {inference_time:.1f}ms") + + except Exception as e: + method_results[method] = { + 'success': False, + 'error': str(e) + } + print(f"❌ {method}: 실패 - {e}") + + return method_results + + def compare_text_results(self, paddle_results, onnx_results): + """텍스트 감지 결과 상세 비교""" + print(f"\n📊 텍스트 감지 결과 상세 비교") + print("=" * 70) + + if not paddle_results or not onnx_results: + print("❌ 비교할 결과가 없습니다.") + return + + paddle_texts = set(r['text'] for r in paddle_results if r['text'].strip()) + onnx_texts = set(r['text'] for r in onnx_results if r['text'].strip()) + + # 공통 텍스트 + common_texts = paddle_texts & onnx_texts + # PaddleOCR에만 있는 텍스트 + paddle_only = paddle_texts - onnx_texts + # ONNX에만 있는 텍스트 + onnx_only = onnx_texts - paddle_texts + + print(f"📈 감지 수량 비교:") + print(f" PaddleOCR: {len(paddle_texts)}개") + print(f" ONNX: {len(onnx_texts)}개") + print(f" 공통: {len(common_texts)}개") + + if common_texts: + print(f"\n🤝 공통으로 감지된 텍스트 ({len(common_texts)}개):") + for text in sorted(common_texts): + # 신뢰도 비교 + paddle_conf = next((r['confidence'] for r in paddle_results if r['text'] == text), 0) + onnx_conf = next((r['confidence'] for r in onnx_results if r['text'] == text), 0) + + conf_diff = onnx_conf - paddle_conf + conf_symbol = "🟢" if conf_diff > 0.05 else "🔴" if conf_diff < -0.05 else "🟡" + + print(f" {conf_symbol} '{text}' | Paddle: {paddle_conf:.3f} vs ONNX: {onnx_conf:.3f} (차이: {conf_diff:+.3f})") + + if paddle_only: + print(f"\n🐼 PaddleOCR에서만 감지 ({len(paddle_only)}개):") + for text in sorted(paddle_only): + conf = next((r['confidence'] for r in paddle_results if r['text'] == text), 0) + print(f" - '{text}' (신뢰도: {conf:.3f})") + + if onnx_only: + print(f"\n🚀 ONNX에서만 감지 ({len(onnx_only)}개):") + for text in sorted(onnx_only): + conf = next((r['confidence'] for r in onnx_results if r['text'] == text), 0) + print(f" - '{text}' (신뢰도: {conf:.3f})") + + def run_comprehensive_test(self, verbose=False): + """전체 비교 테스트 실행""" + print("🔬 OCRModule 종합 비교 테스트") + print("=" * 70) + + if not os.path.exists(self.test_image): + print(f"❌ 테스트 이미지를 찾을 수 없습니다: {self.test_image}") + return + + results = {} + + # 1. CPU 테스트 + print(f"\n💻 CPU 성능 테스트") + print("=" * 30) + + paddle_cpu = self.test_original_ocr_module(use_cuda=False, verbose=verbose) + onnx_cpu = self.test_onnx_ocr_module(use_cuda=False, verbose=verbose) + + results['cpu'] = { + 'paddle': paddle_cpu, + 'onnx': onnx_cpu + } + + # 2. CUDA 테스트 + print(f"\n🚀 CUDA 성능 테스트") + print("=" * 30) + + paddle_cuda = self.test_original_ocr_module(use_cuda=True, verbose=verbose) + onnx_cuda = self.test_onnx_ocr_module(use_cuda=True, verbose=verbose) + + results['cuda'] = { + 'paddle': paddle_cuda, + 'onnx': onnx_cuda + } + + # 3. 감지 방식 테스트 (CUDA 버전으로) + if onnx_cuda and onnx_cuda.get('success'): + try: + from onnx_ocr_wrapper import ONNXOCRModule + gpu_manager, logger = self.create_mock_dependencies(True, False) + onnx_module = ONNXOCRModule(logger=logger, base_dir=onnx_module_dir, gpu_manager=gpu_manager) + onnx_methods = self.test_detection_methods(onnx_module, "ONNX") + results['onnx_methods'] = onnx_methods + except Exception as e: + print(f"❌ ONNX 감지 방식 테스트 실패: {e}") + + # 4. 텍스트 결과 비교 (CUDA 버전으로) + if (paddle_cuda and paddle_cuda.get('success') and + onnx_cuda and onnx_cuda.get('success')): + self.compare_text_results( + paddle_cuda.get('results', []), + onnx_cuda.get('results', []) + ) + + # 5. 성능 요약 + self.print_performance_summary(results) + + return results + + def print_performance_summary(self, results): + """성능 비교 요약 출력""" + print(f"\n📊 성능 비교 요약") + print("=" * 70) + + summary_data = [] + + for mode in ['cpu', 'cuda']: + paddle = results.get(mode, {}).get('paddle', {}) + onnx = results.get(mode, {}).get('onnx', {}) + + if paddle.get('success') and onnx.get('success'): + paddle_time = paddle.get('inference_time_ms', 0) + onnx_time = onnx.get('inference_time_ms', 0) + speedup = paddle_time / onnx_time if onnx_time > 0 else 0 + + summary_data.append({ + 'mode': mode.upper(), + 'paddle_time': paddle_time, + 'onnx_time': onnx_time, + 'speedup': speedup, + 'paddle_count': paddle.get('text_count', 0), + 'onnx_count': onnx.get('text_count', 0) + }) + + if summary_data: + print(f"{'모드':<8} {'Paddle(ms)':<12} {'ONNX(ms)':<12} {'배속':<8} {'P텍스트':<8} {'O텍스트':<8}") + print("-" * 70) + + for data in summary_data: + speedup_str = f"{data['speedup']:.2f}x" if data['speedup'] > 0 else "N/A" + print(f"{data['mode']:<8} {data['paddle_time']:<12.1f} {data['onnx_time']:<12.1f} {speedup_str:<8} {data['paddle_count']:<8} {data['onnx_count']:<8}") + + # 최고 성능 찾기 + best_performance = max(summary_data, key=lambda x: x['speedup'] if x['speedup'] > 0 else 0) + if best_performance['speedup'] > 1: + print(f"\n🏆 최고 성능: ONNX {best_performance['mode']} ({best_performance['speedup']:.2f}배 빠름)") + elif best_performance['speedup'] > 0: + print(f"\n📊 성능 비교: ONNX가 {best_performance['speedup']:.2f}배 성능") + +def main(): + """메인 테스트 함수""" + comparator = OCRModuleComparator() + + print("🎯 OCRModule 비교 테스트 시작") + print("기존 PaddleOCR 기반 vs 새로운 ONNX 기반") + print("=" * 70) + + # 자동으로 상세 로그 비활성화 + verbose = False + print("자동 테스트 모드: 상세 로그 비활성화") + + try: + results = comparator.run_comprehensive_test(verbose=verbose) + + print(f"\n🎉 비교 테스트 완료!") + print("=" * 70) + + # 간단한 결론 + cpu_results = results.get('cpu', {}) + cuda_results = results.get('cuda', {}) + + print(f"\n💡 결론:") + + # CPU 성능 비교 + if (cpu_results.get('paddle', {}).get('success') and + cpu_results.get('onnx', {}).get('success')): + paddle_cpu_time = cpu_results['paddle']['inference_time_ms'] + onnx_cpu_time = cpu_results['onnx']['inference_time_ms'] + cpu_speedup = paddle_cpu_time / onnx_cpu_time + print(f" CPU: ONNX가 {cpu_speedup:.2f}배 {'빠름' if cpu_speedup > 1 else '느림'}") + + # CUDA 성능 비교 + if (cuda_results.get('paddle', {}).get('success') and + cuda_results.get('onnx', {}).get('success')): + paddle_cuda_time = cuda_results['paddle']['inference_time_ms'] + onnx_cuda_time = cuda_results['onnx']['inference_time_ms'] + cuda_speedup = paddle_cuda_time / onnx_cuda_time + print(f" CUDA: ONNX가 {cuda_speedup:.2f}배 {'빠름' if cuda_speedup > 1 else '느림'}") + + print(f"\n✅ 새로운 ONNX OCRModule은 기존 인터페이스와 100% 호환됩니다!") + + except KeyboardInterrupt: + print(f"\n\n⚠️ 테스트가 중단되었습니다.") + except Exception as e: + print(f"\n❌ 테스트 실행 중 오류: {e}") + traceback.print_exc() + +if __name__ == "__main__": + main() diff --git a/modules/onnx_ocr_module/test/compare_results.py b/modules/onnx_ocr_module/test/compare_results.py new file mode 100644 index 0000000..5abf561 --- /dev/null +++ b/modules/onnx_ocr_module/test/compare_results.py @@ -0,0 +1,243 @@ +# -*- coding: utf-8 -*- +""" +ONNX vs PaddlePaddle 상세 텍스트 결과 비교 +""" + +import os +import sys +import time +import logging +from typing import Dict, List + +# 모듈 경로 추가 +sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', 'src')) + +# 가짜 GPU 매니저 및 로거 클래스 +class MockGPUManager: + def __init__(self, can_use_cuda=True): + self.can_use_cuda = can_use_cuda + +class MockLogger: + def log(self, message: str, level=logging.INFO, exc_info=False): + pass # 조용한 로거 + +def compare_ocr_results(): + """ONNX와 PaddlePaddle OCR 결과 상세 비교""" + print("🔍 ONNX vs PaddlePaddle 텍스트 감지 결과 상세 비교") + print("=" * 70) + + test_image = os.path.join(os.path.dirname(__file__), "1.jpg") + if not os.path.exists(test_image): + print(f"❌ 테스트 이미지를 찾을 수 없습니다: {test_image}") + return + + logger = MockLogger() + gpu_manager = MockGPUManager(can_use_cuda=True) + + # 1. ONNX CPU 결과 수집 + print("\n🔧 ONNX CPU 결과 수집 중...") + try: + from onnx_ocr_module import ONNXOCRModule + + ocr_onnx_cpu = ONNXOCRModule( + logger=logger, + gpu_manager=gpu_manager, + execution_provider='cpu' + ) + + start_time = time.time() + onnx_cpu_results = ocr_onnx_cpu.detect_text(test_image) + onnx_cpu_time = (time.time() - start_time) * 1000 + + print(f"✅ ONNX CPU: {len(onnx_cpu_results)}개 텍스트, {onnx_cpu_time:.1f}ms") + + except Exception as e: + print(f"❌ ONNX CPU 실패: {e}") + onnx_cpu_results = [] + onnx_cpu_time = 0 + + # 2. ONNX CUDA 결과 수집 + print("\n🚀 ONNX CUDA 결과 수집 중...") + try: + ocr_onnx_cuda = ONNXOCRModule( + logger=logger, + gpu_manager=gpu_manager, + execution_provider='cuda' + ) + + start_time = time.time() + onnx_cuda_results = ocr_onnx_cuda.detect_text(test_image) + onnx_cuda_time = (time.time() - start_time) * 1000 + + print(f"✅ ONNX CUDA: {len(onnx_cuda_results)}개 텍스트, {onnx_cuda_time:.1f}ms") + + except Exception as e: + print(f"❌ ONNX CUDA 실패: {e}") + onnx_cuda_results = [] + onnx_cuda_time = 0 + + # 3. PaddlePaddle CPU 결과 수집 + print("\n🐼 PaddlePaddle CPU 결과 수집 중...") + try: + from paddleocr import PaddleOCR + + ocr_paddle_cpu = PaddleOCR(use_gpu=False, use_angle_cls=True, lang='ch', show_log=False) + + start_time = time.time() + paddle_cpu_raw = ocr_paddle_cpu.ocr(test_image) + paddle_cpu_time = (time.time() - start_time) * 1000 + + # PaddleOCR 결과 파싱 + paddle_cpu_results = [] + if paddle_cpu_raw and paddle_cpu_raw[0]: + for i, line in enumerate(paddle_cpu_raw[0]): + if len(line) >= 2: + polygon = line[0] + text = line[1][0] + confidence = line[1][1] + + # 바운딩 박스 계산 + import numpy as np + import cv2 + polygon_np = np.array(polygon, dtype=np.int32) + x, y, w, h = cv2.boundingRect(polygon_np) + + paddle_cpu_results.append({ + 'text': text, + 'confidence': confidence, + 'polygon': polygon, + 'bbox': (x, y, w, h), + 'method': 'paddle' + }) + + print(f"✅ Paddle CPU: {len(paddle_cpu_results)}개 텍스트, {paddle_cpu_time:.1f}ms") + + except Exception as e: + print(f"❌ PaddlePaddle CPU 실패: {e}") + paddle_cpu_results = [] + paddle_cpu_time = 0 + + # 4. PaddlePaddle CUDA 결과 수집 + print("\n🐼🚀 PaddlePaddle CUDA 결과 수집 중...") + try: + ocr_paddle_cuda = PaddleOCR(use_gpu=True, use_angle_cls=True, lang='ch', show_log=False) + + start_time = time.time() + paddle_cuda_raw = ocr_paddle_cuda.ocr(test_image) + paddle_cuda_time = (time.time() - start_time) * 1000 + + # PaddleOCR 결과 파싱 + paddle_cuda_results = [] + if paddle_cuda_raw and paddle_cuda_raw[0]: + for i, line in enumerate(paddle_cuda_raw[0]): + if len(line) >= 2: + polygon = line[0] + text = line[1][0] + confidence = line[1][1] + + # 바운딩 박스 계산 + import numpy as np + import cv2 + polygon_np = np.array(polygon, dtype=np.int32) + x, y, w, h = cv2.boundingRect(polygon_np) + + paddle_cuda_results.append({ + 'text': text, + 'confidence': confidence, + 'polygon': polygon, + 'bbox': (x, y, w, h), + 'method': 'paddle' + }) + + print(f"✅ Paddle CUDA: {len(paddle_cuda_results)}개 텍스트, {paddle_cuda_time:.1f}ms") + + except Exception as e: + print(f"❌ PaddlePaddle CUDA 실패: {e}") + paddle_cuda_results = [] + paddle_cuda_time = 0 + + # 5. 결과 상세 비교 + print("\n" + "="*70) + print("📊 텍스트 감지 결과 상세 비교") + print("="*70) + + # 성능 비교 + print(f"\n⚡ 성능 비교:") + print(f"{'모델':<20} {'시간(ms)':<12} {'텍스트 수':<10}") + print("-" * 42) + print(f"{'ONNX CPU':<20} {onnx_cpu_time:<12.1f} {len(onnx_cpu_results):<10}") + print(f"{'ONNX CUDA':<20} {onnx_cuda_time:<12.1f} {len(onnx_cuda_results):<10}") + print(f"{'Paddle CPU':<20} {paddle_cpu_time:<12.1f} {len(paddle_cpu_results):<10}") + print(f"{'Paddle CUDA':<20} {paddle_cuda_time:<12.1f} {len(paddle_cuda_results):<10}") + + # 텍스트 내용 비교 (ONNX CUDA vs Paddle CUDA) + print(f"\n📝 텍스트 내용 상세 비교 (ONNX CUDA vs Paddle CUDA):") + print("-" * 70) + + if onnx_cuda_results and paddle_cuda_results: + print(f"\n🔧 ONNX CUDA 감지 텍스트 ({len(onnx_cuda_results)}개):") + for i, result in enumerate(onnx_cuda_results, 1): + text = result['text'] + conf = result['confidence'] + bbox = result['bbox'] + print(f" {i:2d}. '{text}' (신뢰도: {conf:.3f}, 위치: {bbox})") + + print(f"\n🐼 Paddle CUDA 감지 텍스트 ({len(paddle_cuda_results)}개):") + for i, result in enumerate(paddle_cuda_results, 1): + text = result['text'] + conf = result['confidence'] + bbox = result['bbox'] + print(f" {i:2d}. '{text}' (신뢰도: {conf:.3f}, 위치: {bbox})") + + # 텍스트 세트 비교 + onnx_texts = set(r['text'] for r in onnx_cuda_results if r['text'].strip()) + paddle_texts = set(r['text'] for r in paddle_cuda_results if r['text'].strip()) + + # ONNX에만 있는 텍스트 + onnx_only = onnx_texts - paddle_texts + if onnx_only: + print(f"\n🆕 ONNX에서만 발견된 텍스트 ({len(onnx_only)}개):") + for text in sorted(onnx_only): + # 해당 텍스트의 신뢰도와 위치 찾기 + for result in onnx_cuda_results: + if result['text'] == text: + print(f" - '{text}' (신뢰도: {result['confidence']:.3f}, 위치: {result['bbox']})") + break + + # Paddle에만 있는 텍스트 + paddle_only = paddle_texts - onnx_texts + if paddle_only: + print(f"\n🆕 Paddle에서만 발견된 텍스트 ({len(paddle_only)}개):") + for text in sorted(paddle_only): + # 해당 텍스트의 신뢰도와 위치 찾기 + for result in paddle_cuda_results: + if result['text'] == text: + print(f" - '{text}' (신뢰도: {result['confidence']:.3f}, 위치: {result['bbox']})") + break + + # 공통 텍스트 + common_texts = onnx_texts & paddle_texts + if common_texts: + print(f"\n🤝 공통으로 발견된 텍스트 ({len(common_texts)}개):") + for text in sorted(common_texts): + # 신뢰도 비교 + onnx_conf = next((r['confidence'] for r in onnx_cuda_results if r['text'] == text), 0) + paddle_conf = next((r['confidence'] for r in paddle_cuda_results if r['text'] == text), 0) + + conf_diff = onnx_conf - paddle_conf + conf_symbol = "🟢" if conf_diff > 0.1 else "🔴" if conf_diff < -0.1 else "🟡" + + print(f" {conf_symbol} '{text}' | ONNX: {onnx_conf:.3f} vs Paddle: {paddle_conf:.3f} (차이: {conf_diff:+.3f})") + + print("\n🎯 분석 결론:") + if len(onnx_cuda_results) > len(paddle_cuda_results): + diff = len(onnx_cuda_results) - len(paddle_cuda_results) + print(f"✅ ONNX가 {diff}개 더 많은 텍스트를 감지했습니다!") + elif len(paddle_cuda_results) > len(onnx_cuda_results): + diff = len(paddle_cuda_results) - len(onnx_cuda_results) + print(f"⚠️ Paddle이 {diff}개 더 많은 텍스트를 감지했습니다.") + else: + print("🟰 두 모델이 동일한 수의 텍스트를 감지했습니다.") + +if __name__ == "__main__": + compare_ocr_results() diff --git a/modules/onnx_ocr_module/test/test_onnx_ocr.py b/modules/onnx_ocr_module/test/test_onnx_ocr.py new file mode 100644 index 0000000..b5b766f --- /dev/null +++ b/modules/onnx_ocr_module/test/test_onnx_ocr.py @@ -0,0 +1,328 @@ +# -*- coding: utf-8 -*- +""" +ONNX OCR 모듈 테스트 및 벤치마킹 코드 +""" + +import os +import sys +import time +import logging +from typing import Dict, List + +# 모듈 경로 추가 +sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', 'src')) + +# 가짜 GPU 매니저 클래스 (기존 프로젝트의 GPU 매니저 대체) +class MockGPUManager: + def __init__(self, can_use_cuda=True): + self.can_use_cuda = can_use_cuda + +# 가짜 로거 클래스 +class MockLogger: + def log(self, message: str, level=logging.INFO, exc_info=False): + level_name = { + logging.DEBUG: "DEBUG", + logging.INFO: "INFO", + logging.WARNING: "WARNING", + logging.ERROR: "ERROR" + }.get(level, "INFO") + print(f"[{level_name}] {message}") + +def test_onnx_ocr_basic(): + """기본 ONNX OCR 테스트""" + print("🧪 기본 ONNX OCR 테스트 시작...") + + try: + from onnx_ocr_module import ONNXOCRModule + + # 모듈 초기화 + logger = MockLogger() + gpu_manager = MockGPUManager(can_use_cuda=True) + + ocr_module = ONNXOCRModule( + logger=logger, + base_dir=os.path.dirname(os.path.dirname(__file__)), + gpu_manager=gpu_manager, + execution_provider='cpu' # 기본 테스트는 CPU로 + ) + + # 테스트 이미지 경로 + test_image = os.path.join(os.path.dirname(__file__), "1.jpg") + if not os.path.exists(test_image): + print(f"❌ 테스트 이미지를 찾을 수 없습니다: {test_image}") + return False + + # OCR 실행 + results = ocr_module.detect_text(test_image, method='polygon') + + if results: + print(f"✅ OCR 성공: {len(results)}개 텍스트 감지") + for i, result in enumerate(results[:3]): # 상위 3개만 출력 + print(f" {i+1}. '{result['text']}' (신뢰도: {result['confidence']:.3f})") + return True + else: + print("❌ OCR 결과가 비어있습니다") + return False + + except Exception as e: + print(f"❌ 테스트 실패: {e}") + import traceback + traceback.print_exc() + return False + +def benchmark_ocr_performance(): + """OCR 성능 벤치마킹""" + print("\n⚡ OCR 성능 벤치마킹 시작...") + + test_image = os.path.join(os.path.dirname(__file__), "1.jpg") + if not os.path.exists(test_image): + print(f"❌ 테스트 이미지를 찾을 수 없습니다: {test_image}") + return + + logger = MockLogger() + gpu_manager = MockGPUManager(can_use_cuda=True) + + # 벤치마크 결과 저장 + benchmark_results = {} + + # 1. ONNX CPU 테스트 + print("\n🔄 ONNX CPU 테스트...") + try: + from onnx_ocr_module import ONNXOCRModule + + ocr_cpu = ONNXOCRModule( + logger=logger, + gpu_manager=gpu_manager, + execution_provider='cpu' + ) + + # 워밍업 + ocr_cpu.detect_text(test_image) + + # 실제 측정 + times = [] + for i in range(3): + start_time = time.time() + results = ocr_cpu.detect_text(test_image) + end_time = time.time() + times.append((end_time - start_time) * 1000) + + avg_time = sum(times) / len(times) + benchmark_results['ONNX_CPU'] = { + 'time_ms': avg_time, + 'text_count': len(results), + 'results': results[:3] # 상위 3개 결과만 저장 + } + + print(f"✅ ONNX CPU: {avg_time:.1f}ms (평균), {len(results)}개 텍스트") + + except Exception as e: + print(f"❌ ONNX CPU 테스트 실패: {e}") + benchmark_results['ONNX_CPU'] = {'error': str(e)} + + # 2. ONNX CUDA 테스트 + print("\n🚀 ONNX CUDA 테스트...") + try: + ocr_cuda = ONNXOCRModule( + logger=logger, + gpu_manager=gpu_manager, + execution_provider='cuda' + ) + + # 워밍업 + ocr_cuda.detect_text(test_image) + + # 실제 측정 + times = [] + for i in range(3): + start_time = time.time() + results = ocr_cuda.detect_text(test_image) + end_time = time.time() + times.append((end_time - start_time) * 1000) + + avg_time = sum(times) / len(times) + benchmark_results['ONNX_CUDA'] = { + 'time_ms': avg_time, + 'text_count': len(results), + 'results': results[:3] + } + + print(f"✅ ONNX CUDA: {avg_time:.1f}ms (평균), {len(results)}개 텍스트") + + except Exception as e: + print(f"❌ ONNX CUDA 테스트 실패: {e}") + benchmark_results['ONNX_CUDA'] = {'error': str(e)} + + # 3. PaddlePaddle CPU 테스트 (비교용) + print("\n🐼 PaddlePaddle CPU 테스트...") + try: + from paddleocr import PaddleOCR + + ocr_paddle_cpu = PaddleOCR(use_gpu=False, use_angle_cls=True, lang='ch') + + # 워밍업 + ocr_paddle_cpu.ocr(test_image) + + # 실제 측정 + times = [] + for i in range(3): + start_time = time.time() + paddle_results = ocr_paddle_cpu.ocr(test_image) + end_time = time.time() + times.append((end_time - start_time) * 1000) + + avg_time = sum(times) / len(times) + + # PaddleOCR 결과 파싱 + text_count = 0 + parsed_results = [] + if paddle_results and paddle_results[0]: + for line in paddle_results[0]: + if len(line) >= 2: + text = line[1][0] + confidence = line[1][1] + parsed_results.append({'text': text, 'confidence': confidence}) + text_count += 1 + + benchmark_results['Paddle_CPU'] = { + 'time_ms': avg_time, + 'text_count': text_count, + 'results': parsed_results[:3] + } + + print(f"✅ Paddle CPU: {avg_time:.1f}ms (평균), {text_count}개 텍스트") + + except Exception as e: + print(f"❌ PaddlePaddle CPU 테스트 실패: {e}") + benchmark_results['Paddle_CPU'] = {'error': str(e)} + + # 4. PaddlePaddle CUDA 테스트 (비교용) + print("\n🐼🚀 PaddlePaddle CUDA 테스트...") + try: + ocr_paddle_gpu = PaddleOCR(use_gpu=True, use_angle_cls=True, lang='ch') + + # 워밍업 + ocr_paddle_gpu.ocr(test_image) + + # 실제 측정 + times = [] + for i in range(3): + start_time = time.time() + paddle_results = ocr_paddle_gpu.ocr(test_image) + end_time = time.time() + times.append((end_time - start_time) * 1000) + + avg_time = sum(times) / len(times) + + # PaddleOCR 결과 파싱 + text_count = 0 + parsed_results = [] + if paddle_results and paddle_results[0]: + for line in paddle_results[0]: + if len(line) >= 2: + text = line[1][0] + confidence = line[1][1] + parsed_results.append({'text': text, 'confidence': confidence}) + text_count += 1 + + benchmark_results['Paddle_CUDA'] = { + 'time_ms': avg_time, + 'text_count': text_count, + 'results': parsed_results[:3] + } + + print(f"✅ Paddle CUDA: {avg_time:.1f}ms (평균), {text_count}개 텍스트") + + except Exception as e: + print(f"❌ PaddlePaddle CUDA 테스트 실패: {e}") + benchmark_results['Paddle_CUDA'] = {'error': str(e)} + + # 결과 요약 + print("\n📊 벤치마크 결과 요약:") + print("=" * 60) + print(f"{'모델':<15} {'시간(ms)':<12} {'텍스트 수':<10} {'상태':<10}") + print("-" * 60) + + for model_name, result in benchmark_results.items(): + if 'error' in result: + print(f"{model_name:<15} {'N/A':<12} {'N/A':<10} {'실패':<10}") + print(f" 오류: {result['error']}") + else: + time_ms = result['time_ms'] + text_count = result['text_count'] + print(f"{model_name:<15} {time_ms:<12.1f} {text_count:<10} {'성공':<10}") + + # 속도 비교 + print("\n🏁 속도 비교:") + successful_results = {k: v for k, v in benchmark_results.items() if 'error' not in v} + + if len(successful_results) > 1: + fastest = min(successful_results.items(), key=lambda x: x[1]['time_ms']) + print(f"🥇 가장 빠른 모델: {fastest[0]} ({fastest[1]['time_ms']:.1f}ms)") + + for model_name, result in successful_results.items(): + if model_name != fastest[0]: + speedup = result['time_ms'] / fastest[1]['time_ms'] + print(f" {model_name}: {speedup:.2f}배 느림") + + return benchmark_results + +def test_all_detection_methods(): + """모든 감지 방식 테스트""" + print("\n🔍 모든 감지 방식 테스트...") + + test_image = os.path.join(os.path.dirname(__file__), "1.jpg") + if not os.path.exists(test_image): + print(f"❌ 테스트 이미지를 찾을 수 없습니다: {test_image}") + return + + try: + from onnx_ocr_module import ONNXOCRModule + + logger = MockLogger() + gpu_manager = MockGPUManager(can_use_cuda=True) + + ocr_module = ONNXOCRModule( + logger=logger, + gpu_manager=gpu_manager, + execution_provider='cpu' + ) + + methods = ['polygon', 'bbox', 'expanded_bbox', 'rotated_bbox', 'contour'] + + for method in methods: + print(f"\n🔧 {method} 방식 테스트...") + start_time = time.time() + results = ocr_module.detect_text(test_image, method=method) + end_time = time.time() + + print(f" ✅ {len(results)}개 텍스트 감지, {(end_time-start_time)*1000:.1f}ms") + + if results: + sample = results[0] + print(f" 📝 샘플: '{sample['text']}' (method: {sample['method']})") + + except Exception as e: + print(f"❌ 감지 방식 테스트 실패: {e}") + +def main(): + """메인 테스트 함수""" + print("🚀 ONNX OCR 모듈 테스트 시작") + print("=" * 50) + + # 기본 테스트 + success = test_onnx_ocr_basic() + + if success: + # 모든 감지 방식 테스트 + test_all_detection_methods() + + # 성능 벤치마킹 + benchmark_results = benchmark_ocr_performance() + + print("\n🎉 모든 테스트 완료!") + else: + print("\n❌ 기본 테스트 실패로 인해 추가 테스트를 건너뜁니다.") + +if __name__ == "__main__": + main() diff --git a/modules/output/1_removed.png b/modules/output/1_removed.png new file mode 100644 index 0000000..333fd79 Binary files /dev/null and b/modules/output/1_removed.png differ diff --git a/modules/output/2_removed.png b/modules/output/2_removed.png new file mode 100644 index 0000000..ef13e0a Binary files /dev/null and b/modules/output/2_removed.png differ diff --git a/modules/output/3_removed.png b/modules/output/3_removed.png new file mode 100644 index 0000000..83d7a08 Binary files /dev/null and b/modules/output/3_removed.png differ diff --git a/modules/output/4_removed.png b/modules/output/4_removed.png new file mode 100644 index 0000000..f3b7ef2 Binary files /dev/null and b/modules/output/4_removed.png differ diff --git a/modules/output/5_removed.png b/modules/output/5_removed.png new file mode 100644 index 0000000..85d9448 Binary files /dev/null and b/modules/output/5_removed.png differ diff --git a/modules/output/5_removed_white.png b/modules/output/5_removed_white.png new file mode 100644 index 0000000..6766069 Binary files /dev/null and b/modules/output/5_removed_white.png differ diff --git a/modules/output/6_removed.png b/modules/output/6_removed.png new file mode 100644 index 0000000..eb372f6 Binary files /dev/null and b/modules/output/6_removed.png differ diff --git a/modules/output/6_removed_white.png b/modules/output/6_removed_white.png new file mode 100644 index 0000000..e008999 Binary files /dev/null and b/modules/output/6_removed_white.png differ diff --git a/modules/output/7_removed.png b/modules/output/7_removed.png new file mode 100644 index 0000000..04f2db0 Binary files /dev/null and b/modules/output/7_removed.png differ diff --git a/modules/outputs/modnet_hrnet18.png b/modules/outputs/modnet_hrnet18.png new file mode 100644 index 0000000..2c29f3a Binary files /dev/null and b/modules/outputs/modnet_hrnet18.png differ diff --git a/modules/outputs/modnet_mobilenetv2.png b/modules/outputs/modnet_mobilenetv2.png new file mode 100644 index 0000000..7166fb9 Binary files /dev/null and b/modules/outputs/modnet_mobilenetv2.png differ diff --git a/modules/outputs/modnet_resnet50vd.png b/modules/outputs/modnet_resnet50vd.png new file mode 100644 index 0000000..660f96e Binary files /dev/null and b/modules/outputs/modnet_resnet50vd.png differ diff --git a/modules/postImageManager.py b/modules/postImageManager.py new file mode 100644 index 0000000..3517df5 --- /dev/null +++ b/modules/postImageManager.py @@ -0,0 +1,265 @@ +# import base64 +# import pyperclip +# import win32clipboard +from io import BytesIO +from PIL import Image, ImageGrab, ImageFont, ImageDraw +import requests +import numpy as np +import cv2 +import time +import os, sys +from datetime import datetime +import random +import io +import logging + +class PostImageManager: + def __init__(self, logger, toggle_states): + self.logger = logger + self.toggle_states = toggle_states + self.font = None + + # 프로그램이 위치한 경로 기준으로 폰트 경로 설정 + self.base_path = self.toggle_states.get('base_dir', "") + self.font_path = self.toggle_states.get('image_font_path', os.path.join(self.base_path, "HakgyoansimDunggeunmisoTTFB.ttf")) + self.watermark_font_size = 36 + + # 폰트 로드 + self.font_load() + + def update_toggle_states(self, toggle_states1): + self.toggle_states = toggle_states1 + self.base_path = self.toggle_states.get('base_dir', "") + self.font_path = self.toggle_states.get('image_font_path', os.path.join(self.base_path, "HakgyoansimDunggeunmisoTTFB.ttf")) + self.watermark_font_size = 36 + + def font_load(self): + # 폰트 로드 + try: + self.font = ImageFont.truetype(self.font_path, self.watermark_font_size) + self.logger.log(f"폰트 로드 성공: {self.font_path}", level=logging.DEBUG) + except Exception as e: + self.logger.log(f"커스텀 폰트 로드 실패 ({self.font_path}): {e}", level=logging.WARNING) + try: + # 기본 폰트 사용 + self.font = ImageFont.load_default() + self.logger.log("기본 폰트를 사용합니다.", level=logging.INFO) + except Exception as e2: + self.logger.log(f"기본 폰트 로드도 실패: {e2}", level=logging.ERROR) + # 최후의 수단으로 None 설정 + self.font = None + + + def save_image_to_path(self, image, path): + try: + # None 체크 추가 + if image is None: + self.logger.log(f"이미지가 None입니다. 저장을 건너뜁니다: {path}", level=logging.WARNING) + return None + + # 타입별 처리 + if isinstance(image, np.ndarray): # 이미 np.ndarray(BGR)라면 + if image.size == 0: # 빈 배열 체크 + self.logger.log(f"빈 배열입니다. 저장을 건너뜁니다: {path}", level=logging.WARNING) + return None + cv2.imwrite(path, image) # 바로 저장 + elif isinstance(image, Image.Image): # PIL 객체 + import os + file_ext = os.path.splitext(path)[1].lower() + + # RGBA 이미지를 JPEG로 저장할 때 에러 방지 + if image.mode == 'RGBA' and file_ext in ['.jpg', '.jpeg']: + # RGBA를 RGB로 변환 (흰색 배경 사용) + rgb_image = Image.new('RGB', image.size, (255, 255, 255)) + rgb_image.paste(image, mask=image.split()[-1]) # 알파 채널을 마스크로 사용 + image = rgb_image + self.logger.log(f"RGBA 이미지를 RGB로 변환하여 JPEG 저장", level=logging.INFO) + + # 확장자에 따른 형식 결정 (품질 우선 순위: WebP > PNG > JPEG) + if file_ext == '.webp': + # WebP: 최고의 압축률과 품질 + image.save(path, format='WebP', quality=95, method=6) + self.logger.log(f"WebP 형식으로 저장 (품질: 95, 최적 압축)", level=logging.DEBUG) + elif file_ext == '.png': + # PNG: 무손실, 투명도 지원 + image.save(path, format='PNG', optimize=True) + self.logger.log(f"PNG 형식으로 저장 (무손실, 최적화됨)", level=logging.DEBUG) + elif file_ext in ['.jpg', '.jpeg']: + # JPEG: 호환성 위주 (품질 높게 설정) + image.save(path, format='JPEG', quality=95, optimize=True) + self.logger.log(f"JPEG 형식으로 저장 (품질: 95)", level=logging.DEBUG) + else: + # 기본값: PNG 형식 (가장 안전하고 품질 좋음) + image.save(path, format='PNG', optimize=True) + self.logger.log(f"기본 PNG 형식으로 저장", level=logging.DEBUG) + else: + # 예상하지 못한 타입의 경우 로그에 타입 정보 추가 + actual_type = type(image).__name__ + self.logger.log(f"지원하지 않는 이미지 타입: {actual_type}, 값: {image}", level=logging.ERROR) + raise TypeError(f"지원하지 않는 형식: {actual_type}") + + self.logger.log(f"이미지 저장 완료 : {path}", level=logging.INFO) + return path + except Exception as e: + raise RuntimeError(f"이미지 저장 중 오류 발생: {e}") + + def add_watermark(self, image_data, watermark_text="Watermark", opacity_percent=30, angle=30, font_size=36): + """ + 이미지에 텍스트 워터마크를 이미지 전체에 걸쳐서 추가하는 함수 + :param image_data: PIL 이미지 객체 + :param watermark_text: 워터마크로 추가할 텍스트 + :param opacity_percent: 워터마크의 투명도 (0~100) + :param angle: 워터마크 텍스트 회전 각도 (기본 30도) + :param font_size: 워터마크 텍스트의 폰트 크기 (기본 36) + :return: 워터마크가 추가된 이미지 + """ + try: + # --- (1) 입력 형식 통일: RGB np.ndarray 로 변환 -------------------- + if isinstance(image_data, np.ndarray): + img_rgb = cv2.cvtColor(image_data, cv2.COLOR_BGR2RGB) + elif isinstance(image_data, Image.Image): + image_data.load() # ⬅️ JPEG 완전 디코딩 + img_rgb = np.array(image_data.convert("RGB")) + else: + raise TypeError("지원하지 않는 이미지 타입") + + # --- (2) 텍스트 작업은 Pillow(RGBA) 로 수행 ------------------------ + watermark_image = Image.fromarray(img_rgb).convert("RGBA") + + # 폰트가 로드되지 않은 경우 원본 이미지 반환 + if self.font is None: + self.logger.log("폰트가 로드되지 않아 워터마크를 추가할 수 없습니다. 원본 이미지를 반환합니다.", level=logging.WARNING) + return image_data + + # 텍스트 투명도를 0~255로 변환 + opacity = int(255 * (opacity_percent / 100)) + + # 텍스트 크기 측정 (textbbox 사용) + draw = ImageDraw.Draw(watermark_image) + bbox = draw.textbbox((0, 0), watermark_text, font=self.font) + text_width, text_height = bbox[2] - bbox[0], bbox[3] - bbox[1] + + # 텍스트 크기가 0인 경우 예외 처리 (빈 문자열 등) + if text_width == 0 or text_height == 0: + self.logger.log("워터마크 텍스트 크기를 계산할 수 없어 워터마크를 건너뜁니다.", level=logging.WARNING) + return image_data + + # 이미지 크기 + width, height = watermark_image.size + + # 지그재그 간격 설정 (0 방지를 위해 최소 1 보장) + zigzag_step = max(1, int(text_height * 2)) # Y축의 지그재그 간격 + + step_x = max(1, int(text_width * 3)) # X축 반복 간격도 0 방지 + + # 이미지 전체에 반복적으로 워터마크 텍스트 그리기 (지그재그 형태) + for y in range(0, height, zigzag_step): + for x in range(0, width, step_x): # 3배 너비 간격으로 반복 + # 텍스트가 한 줄씩 지그재그 형태로 X축을 교차하여 이동 + x_offset = (y // zigzag_step) % 2 * int(text_width * 1.5) # 짝수 행에서는 X축을 약간 이동 + + # 텍스트 레이어 생성 + text_layer = Image.new("RGBA", (text_width, text_height), (255, 255, 255, 0)) + text_draw = ImageDraw.Draw(text_layer) + + # 텍스트 그리기 + text_draw.text((0, 0), watermark_text, fill=(255, 255, 255, opacity), font=self.font) + + # 텍스트 회전 + rotated_text_layer = text_layer.rotate(angle, expand=1) + + # 회전된 텍스트를 원본 이미지에 직접 추가 + watermark_image.paste(rotated_text_layer, (x + x_offset, y), rotated_text_layer) + + # --- (3) 최종 결과를 BGR ndarray 로 변환해 반환 ---------------------- + result_rgb = watermark_image.convert("RGB") + result_bgr = cv2.cvtColor(np.array(result_rgb), cv2.COLOR_RGB2BGR) + return result_bgr + + except Exception as e: + self.logger.log(f"워터마크 추가 중 오류 발생: {e}", level=logging.ERROR, exc_info=True) + return image_data + + + def download_image_from_url(self, url, max_retries=3): + """URL에서 이미지를 다운로드하고 PIL 이미지 객체로 반환""" + headers = { + "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.150 Safari/537.36", + "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9", + "Accept-Language": "en-US,en;q=0.9", + "Accept-Encoding": "gzip, deflate, br", + "DNT": "1", # Do Not Track 요청 헤더 + "Connection": "keep-alive", + "Upgrade-Insecure-Requests": "1", + "Cache-Control": "max-age=0" + } + + retries = 0 + while retries < max_retries: + try: + self.logger.log(f"이미지 URL 다운로드 중: {url}", level=logging.DEBUG) + response = requests.get(url, headers=headers, stream=True) + + # 상태 코드가 200이 아니면 재시도 + if response.status_code == 200: + # OpenCV로 이미지를 로드하여 변환 + image = np.asarray(bytearray(response.content), dtype="uint8") + image = cv2.imdecode(image, cv2.IMREAD_COLOR) + + # OpenCV에서 이미지를 PIL로 변환 + if image is not None: + pil_image = Image.fromarray(cv2.cvtColor(image, cv2.COLOR_BGR2RGB)) + return pil_image + else: + self.logger.log(f"이미지 파일 형식이 올바르지 않습니다. 대상 URL: {url}", level=logging.DEBUG) + return None + else: + self.logger.log(f"이미지 로딩 실패, HTTP 상태 코드: {response.status_code}. 재시도 {retries + 1}/{max_retries}", level=logging.DEBUG) + retries += 1 + # await asyncio.sleep(random.randint(2, 5)) # 2~5초 대기 후 재시도 + time.sleep(random.randint(2, 5)) + + except Exception as e: + self.logger.log(f"이미지 로딩 중 오류 발생: {e}. 재시도 {retries + 1}/{max_retries}", level=logging.DEBUG) + retries += 1 + # await asyncio.sleep(random.randint(2, 5)) # 예외 발생 시 대기 후 재시도 + time.sleep(random.randint(2, 5)) + + self.logger.log("이미지 다운로드 최대 재시도 횟수를 초과했습니다.", level=logging.DEBUG) + return None + + def crop_image(self, image, is_thumb=False, crop_percentage=0.01): + """이미지를 주어진 퍼센트만큼 크롭하는 함수""" + if is_thumb: + crop_percentage = 0.03 + self.logger.log(f"썸네일 이미지 이므로 크롭 3%로 조정", level=logging.DEBUG) + + width, height = image.size + left = width * crop_percentage + top = height * crop_percentage + right = width * (1 - crop_percentage) + bottom = height * (1 - crop_percentage) + + cropped_image = image.crop((left, top, right, bottom)) + + if self.debug: + # 디버그 모드일 경우 크롭 전후 다양한 비율로 이미지 저장 + timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") + original_image_path = os.path.join(os.getcwd(), f"original_image_{timestamp}.png") + image.save(original_image_path) # 크롭 전 이미지 저장 + self.logger.log(f"크롭 전 이미지 저장됨: {original_image_path}", level=logging.DEBUG) + + # 1%, 2%, 3% 크롭 이미지 저장 + crop_alternatives = [0.01, 0.02, 0.03] + for crop in crop_alternatives: + left_alt = width * crop + top_alt = height * crop + right_alt = width * (1 - crop) + bottom_alt = height * (1 - crop) + + cropped_alt_image = image.crop((left_alt, top_alt, right_alt, bottom_alt)) + cropped_image_path = os.path.join(os.getcwd(), f"cropped_image_{int(crop*100)}_{timestamp}.png") + cropped_alt_image.save(cropped_image_path) + self.logger.log(f"{int(crop*100)}% 크롭된 이미지 저장됨: {cropped_image_path}", level=logging.DEBUG) + + return cropped_image \ No newline at end of file diff --git a/modules/rembg_models/BriaRMBG1.4_model_fp16.onnx b/modules/rembg_models/BriaRMBG1.4_model_fp16.onnx new file mode 100644 index 0000000..ff8ce05 Binary files /dev/null and b/modules/rembg_models/BriaRMBG1.4_model_fp16.onnx differ diff --git a/modules/request_inpaint.py b/modules/request_inpaint.py new file mode 100644 index 0000000..de8dfa0 --- /dev/null +++ b/modules/request_inpaint.py @@ -0,0 +1,746 @@ +import requests +import cv2 +import base64 +import numpy as np +import os +import logging +from PIL import Image +from .bria_background_removal_module import BriaBackgroundRemovalModule + + +class Request_AI_Server: + """로컬 인페인팅/배경제거 어댑터 + + - 인페인팅: 로컬 MIGAN ONNX 파이프라인 사용 + - 배경제거: 로컬 BriaAI RMBG 사용 + + 원격 서버 연동 로직은 제거되었습니다. + """ + def __init__(self, logger, + gpu_manager=None, + local_rembg_model_path: str | None = None): + """로컬 인페인팅/배경제거 초기화""" + self.logger = logger + self.gpu_manager = gpu_manager + + inpaint_server_url = None + rembg_server_url = None + self.inpaint_base_url = "" + self.rembg_base_url = "" + self.local_rembg_model_path = local_rembg_model_path + + self.inpaint_api_url = None + # RemoveBG 플러그인 엔드포인트(현재 미사용) + self.rembg_api_url = None + + # 백업용 내장 rembg 모듈 (필요시에만 초기화) + self._backup_rembg = None + self._backup_rembg_error = None # 백업 모듈 초기화 오류 저장 + + # GPU 사용 가능 여부 로그 + if self.gpu_manager: + gpu_status = self.gpu_manager.get_cuda_status() + self.logger.log(f"Request_AI_Server GPU 상태: CUDA 사용 가능={gpu_status['can_use_cuda']}", level=logging.DEBUG) + # MIGAN 파이프라인 (lazy) + self._migan = None + self._migan_error = None + + def _get_backup_rembg(self): + """백업용 내장 rembg 모듈을 lazy loading으로 초기화 (안전한 처리)""" + if self._backup_rembg is not None: + return self._backup_rembg + + # 이미 초기화 실패한 경우 + if self._backup_rembg_error is not None: + return None + + try: + # BriaAI 배경제거 모듈 초기화 + self._backup_rembg = BriaBackgroundRemovalModule( + logger=self.logger, + default_model="bria-rmbg-1.4", + gpu_manager=self.gpu_manager, + local_rembg_model_path=self.local_rembg_model_path + ) + + # 백업 모듈의 rembg 사용 가능성 확인 + if not self._backup_rembg.is_available(): + init_error = self._backup_rembg.get_init_error() + self._backup_rembg_error = f"백업 rembg 모듈 의존성 오류: {init_error}" + self.logger.log(self._backup_rembg_error, level=logging.ERROR) + self._backup_rembg = None + return None + + self.logger.log("백업용 내장 rembg 모듈 초기화됨", level=logging.DEBUG) + return self._backup_rembg + + except ImportError as e: + self._backup_rembg_error = f"백업용 rembg 모듈 import 실패: {e}" + self.logger.log(self._backup_rembg_error, level=logging.ERROR) + except Exception as e: + self._backup_rembg_error = f"백업용 rembg 모듈 초기화 오류: {e}" + self.logger.log(self._backup_rembg_error, level=logging.ERROR, exc_info=True) + + return None + + def request_inpaint(self, image: np.ndarray, mask: np.ndarray, invert_mask: bool = False, inpaint_model: str = "migan") -> np.ndarray: + """로컬 MIGAN으로 인페인팅 수행. + + Args: + image: 경로(str) 또는 BGR np.ndarray + mask: 0/255 uint8 2D, 텍스트영역=255 + invert_mask: 무시(서버 호환 옵션) + inpaint_model: 미사용(서버 호환) + Returns: + 인페인트된 BGR np.ndarray or None + """ + try: + # 이미지 경로 확보 + img_path = None + cleanup = False + if isinstance(image, str) and os.path.isfile(image): + img_path = image + image_data = cv2.imread(image) + else: + image_data = image if isinstance(image, np.ndarray) else None + if image_data is None: + self.logger.log(f"이미지 입력이 유효하지 않습니다: {type(image)}", level=logging.ERROR) + return None + import tempfile + tmp = tempfile.NamedTemporaryFile(delete=False, suffix=".png") + cv2.imwrite(tmp.name, image_data) + img_path = tmp.name + cleanup = True + + # 마스크 정규화 + if mask is None: + self.logger.log("마스크가 None입니다", level=logging.ERROR) + return None + mask_norm = mask + if mask_norm.ndim == 3: + mask_norm = cv2.cvtColor(mask_norm, cv2.COLOR_BGR2GRAY) + if mask_norm.dtype != np.uint8: + mask_norm = mask_norm.astype(np.uint8) + _, mask_norm = cv2.threshold(mask_norm, 0, 255, cv2.THRESH_BINARY) + if mask_norm.shape[:2] != image_data.shape[:2]: + self.logger.log( + f"마스크 크기 보정: mask={mask_norm.shape} → image={image_data.shape[:2]}", + level=logging.WARNING, + ) + mask_norm = cv2.resize(mask_norm, (image_data.shape[1], image_data.shape[0]), interpolation=cv2.INTER_NEAREST) + + # MIGAN 파이프라인 준비 + migan = self._get_migan() + if migan is None: + self.logger.log("MIGAN 파이프라인 초기화 실패", level=logging.ERROR) + return None + + result = migan.inpaint(img_path, mask_norm) + return result + except Exception as e: + self.logger.log(f"로컬 MIGAN 인페인팅 실패: {e}", level=logging.ERROR, exc_info=True) + return None + finally: + try: + if 'cleanup' in locals() and cleanup and img_path and os.path.exists(img_path): + os.unlink(img_path) + except Exception: + pass + + def request_rembg(self, image: np.ndarray, use_local_rembg: bool = False, local_model_name: str = "bria-rmbg-1.4", object_ratio: float = 0.95) -> np.ndarray: + """RemoveBG 플러그인 호출 후 결과 이미지를 흰 배경 중앙 배치로 후처리. + 서버가 다운될 경우 백업으로 내장 rembg 모듈 사용. + + Args: + image: 입력 이미지 (np.ndarray 또는 파일 경로) + use_local_rembg: True면 서버 실패 시 백업으로 내장 rembg 모듈 사용 허용 + local_model_name: 내장 BriaAI에서 사용할 모델명 (기본값: "bria-rmbg-1.4") + object_ratio: 객체가 캔버스에서 차지할 비율 (기본값: 0.75 = 75%) + """ + try: + self.logger.log(f"request_rembg 호출: use_local_rembg={use_local_rembg}, local_model_name={local_model_name}", level=logging.DEBUG) + # 입력 이미지 로드/확정 + if isinstance(image, str) and os.path.isfile(image): + image_data = cv2.imread(image) + elif isinstance(image, np.ndarray): + image_data = image + else: + self.logger.log(f"이미지 파일을 읽을 수 없습니다: {image}", level=logging.ERROR) + return None + + return self._use_backup_rembg(image_data, local_model_name, object_ratio, debug_save=True, debug_prefix="wrong_shape", force_cpu=False) + + except Exception as e: + self.logger.log(f"request_rembg 실행 중 오류: {e}", level=logging.WARNING, exc_info=True) + return None + + def _use_backup_rembg(self, image_data: np.ndarray, model_name: str = "bria-rmbg-1.4", object_ratio: float = 0.75, debug_save: bool = False, debug_prefix: str = "debug", force_cpu: bool = False) -> np.ndarray: + """내장 rembg 모듈을 사용하여 배경 제거 (안전한 처리) + + Args: + image_data: 입력 이미지 데이터 + model_name: 사용할 rembg 모델명 + object_ratio: 객체가 캔버스에서 차지할 비율 + debug_save: True면 중간 단계 이미지들을 저장 + debug_prefix: 디버그 이미지 파일명 접두사 + force_cpu: True면 CPU 모드로 강제 실행 (DirectML 알파 채널 이슈 회피) + """ + try: + import tempfile + import uuid + import shutil + + self.logger.log(f"🔧 백업 rembg 시작: model_name={model_name}, object_ratio={object_ratio}, debug_save={debug_save}, force_cpu={force_cpu}", level=logging.INFO) + + # 디버그 저장을 위한 설정 + debug_dir = None + if debug_save: + debug_dir = tempfile.mkdtemp(prefix=f"{debug_prefix}_rembg_debug_") + self.logger.log(f"📁 디버그 이미지 저장 디렉토리: {debug_dir}", level=logging.INFO) + + # 1단계: 입력 이미지 저장 + input_path = os.path.join(debug_dir, "01_input_image.png") + cv2.imwrite(input_path, image_data) + self.logger.log(f"💾 1단계 저장 완료: 입력 이미지 {image_data.shape} → {input_path}", level=logging.DEBUG) + + backup_rembg = self._get_backup_rembg() + if backup_rembg is None: + error_msg = self._backup_rembg_error or "백업 rembg 모듈을 사용할 수 없습니다" + self.logger.log(f"백업 rembg 모듈 사용 불가: {error_msg}", level=logging.ERROR) + return None + + # 모델명 유효성 확인 및 대체 + supported_models = backup_rembg.get_supported_models() + if model_name not in supported_models: + self.logger.log(f"지원하지 않는 모델명 ({model_name}). bria-rmbg-1.4로 대체 사용", level=logging.WARNING) + model_name = "bria-rmbg-1.4" # 기본 모델로 대체 + + # 안전한 임시 파일 저장 (UTF-8 경로 문제 해결) + + try: + # ASCII 경로로 안전한 임시 파일 생성 + temp_dir = tempfile.gettempdir() + safe_filename = f"rembg_{uuid.uuid4().hex[:8]}.png" + temp_path = os.path.join(temp_dir, safe_filename) + + # 이미지 저장 시 인코딩 안전성 확보 + encode_param = [cv2.IMWRITE_PNG_COMPRESSION, 1] + success = cv2.imwrite(temp_path, image_data, encode_param) + + if not success or not os.path.exists(temp_path) or os.path.getsize(temp_path) == 0: + self.logger.log("임시 이미지 파일 생성 실패", level=logging.ERROR) + return None + + self.logger.log(f"✅ 안전한 임시 파일 생성: {temp_path} (크기: {os.path.getsize(temp_path)} bytes)", level=logging.DEBUG) + + # 2단계: 임시 파일 저장 확인 (디버그) + if debug_save and debug_dir: + temp_copy_path = os.path.join(debug_dir, "02_temp_file_copy.png") + shutil.copy2(temp_path, temp_copy_path) + self.logger.log(f"💾 2단계 저장 완료: 임시 파일 복사 → {temp_copy_path}", level=logging.DEBUG) + + # 내장 rembg로 배경 제거 (CPU/GPU 모드 선택 가능) + try: + self.logger.log(f"백업 rembg 모듈로 배경 제거 시작: {model_name}", level=logging.DEBUG) + + if force_cpu: + # CPU 모드로 강제 실행 + self.logger.log("⚠️ CPU 모드로 강제 실행", level=logging.WARNING) + original_gpu_state = backup_rembg.gpu_manager.can_use_cuda if backup_rembg.gpu_manager else False + + if backup_rembg.gpu_manager: + backup_rembg.gpu_manager.can_use_cuda = False + + try: + result_pil = backup_rembg.remove_background(temp_path, model_name=model_name, force_cpu=True) + if result_pil is not None: + self.logger.log(f"CPU 강제 모드로 배경 제거 성공", level=logging.INFO) + else: + self.logger.log(f"내장 rembg 모듈({model_name}) 처리 실패", level=logging.ERROR) + return None + finally: + # GPU 상태 복원 + if backup_rembg.gpu_manager: + backup_rembg.gpu_manager.can_use_cuda = original_gpu_state + else: + # DirectML GPU 모드로 실행 + self.logger.log("🔥 DirectML GPU 모드로 배경 제거 실행", level=logging.INFO) + result_pil = backup_rembg.remove_background(temp_path, model_name=model_name, force_cpu=force_cpu) + if result_pil is not None: + self.logger.log(f"DirectML GPU 모드로 배경 제거 성공", level=logging.INFO) + else: + self.logger.log(f"내장 rembg 모듈({model_name}) 처리 실패", level=logging.ERROR) + return None + + self.logger.log(f"✅ 백업 rembg 처리 성공: {result_pil.mode}, 크기: {result_pil.size}", level=logging.DEBUG) + + # 3단계: PIL 결과 저장 (디버그) + if debug_save and debug_dir: + pil_result_path = os.path.join(debug_dir, "03_pil_result.png") + result_pil.save(pil_result_path) + self.logger.log(f"💾 3단계 저장 완료: PIL 결과 {result_pil.mode} {result_pil.size} → {pil_result_path}", level=logging.DEBUG) + + # PIL Image 상세 분석 (디버그) + self.logger.log(f"🔍 PIL 결과 분석: mode={result_pil.mode}, size={result_pil.size}, format={result_pil.format}", level=logging.DEBUG) + if hasattr(result_pil, 'getbands'): + bands = result_pil.getbands() + self.logger.log(f"🔍 PIL 밴드: {bands}", level=logging.DEBUG) + + # PIL 알파 채널 통계 확인 + if result_pil.mode == 'RGBA': + pil_array = np.array(result_pil) + alpha_channel = pil_array[:, :, 3] + alpha_stats = { + 'min': alpha_channel.min(), + 'max': alpha_channel.max(), + 'mean': alpha_channel.mean(), + 'nonzero_count': np.count_nonzero(alpha_channel), + 'above_128_count': np.sum(alpha_channel > 128) + } + self.logger.log(f"🔍 PIL 알파 채널 통계: {alpha_stats}", level=logging.DEBUG) + + # PIL Image를 numpy array로 변환 (DirectML 알파 채널 이슈 회피) + if result_pil.mode == 'RGBA': + # BriaAI에서 RGBA가 나오는 경우 (레거시) + result_rgba = np.array(result_pil) + # DirectML 이슈 회피: BGRA 변환 대신 흰 배경 합성으로 RGB 처리 + rgb_part = result_rgba[:, :, :3] + alpha_part = result_rgba[:, :, 3:4].astype(np.float32) / 255.0 + white_bg = np.full_like(rgb_part, 255, dtype=np.uint8) + result_rgb = ( + rgb_part.astype(np.float32) * alpha_part + + white_bg.astype(np.float32) * (1.0 - alpha_part) + ).astype(np.uint8) + result_img = cv2.cvtColor(result_rgb, cv2.COLOR_RGB2BGR) + self.logger.log(f"🔄 RGBA → 흰배경 합성 → BGR 변환: {result_img.shape}", level=logging.DEBUG) + else: + # RGB 모드 (BriaAI가 이미 흰 배경 합성을 완료한 경우) + result_rgb = np.array(result_pil) + result_img = cv2.cvtColor(result_rgb, cv2.COLOR_RGB2BGR) + self.logger.log(f"🔄 RGB → BGR 변환: {result_img.shape}", level=logging.DEBUG) + + # 4단계: numpy 변환 결과 저장 (디버그) - 이제 BGR 형태 + if debug_save and debug_dir: + numpy_result_path = os.path.join(debug_dir, "04_numpy_result.png") + cv2.imwrite(numpy_result_path, result_img) + self.logger.log(f"💾 4단계 저장 완료: numpy 변환 결과 {result_img.shape} (흰배경 합성 완료) → {numpy_result_path}", level=logging.DEBUG) + + # 동일한 후처리 적용 + self.logger.log(f"🔧 후처리 시작: object_ratio={object_ratio}", level=logging.DEBUG) + processed_result = self._postprocess_rembg_result(result_img, object_ratio, debug_save=debug_save, debug_dir=debug_dir, step_offset=4) + + if processed_result is not None: + self.logger.log(f"✅ 백업 rembg 후처리 완료: {processed_result.shape}", level=logging.DEBUG) + + # 최종 결과 저장 (디버그) + if debug_save and debug_dir: + final_result_path = os.path.join(debug_dir, "99_final_result.png") + cv2.imwrite(final_result_path, processed_result) + self.logger.log(f"💾 최종 결과 저장: {processed_result.shape} → {final_result_path}", level=logging.INFO) + else: + self.logger.log("❌ 백업 rembg 후처리 실패", level=logging.ERROR) + + return processed_result + + except Exception as rembg_error: + self.logger.log(f"백업 rembg 내부 처리 중 오류: {rembg_error}", level=logging.ERROR, exc_info=True) + return None + + except Exception as e: + self.logger.log(f"백업 rembg 처리 중 오류: {e}", level=logging.ERROR, exc_info=True) + return None + finally: + # 임시 파일 안전하게 삭제 + if 'temp_path' in locals() and os.path.exists(temp_path): + try: + os.unlink(temp_path) + self.logger.log(f"임시 파일 삭제 완료: {temp_path}", level=logging.DEBUG) + except Exception as delete_error: + self.logger.log(f"임시 파일 삭제 실패: {delete_error}", level=logging.WARNING) + + except Exception as e: + self.logger.log(f"백업 rembg 모듈 실행 중 치명적 오류: {e}", level=logging.ERROR, exc_info=True) + return None + + def _postprocess_rembg_result(self, result_img: np.ndarray, object_ratio: float = 0.75, debug_save: bool = False, debug_dir: str = None, step_offset: int = 0) -> np.ndarray: + """RemoveBG 결과 이미지 후처리: 마스크 정제 및 중앙 배치""" + try: + # 입력 검증 + if result_img is None: + self.logger.log("❌ 입력 이미지가 None입니다", level=logging.ERROR) + return None + + if not isinstance(result_img, np.ndarray): + self.logger.log(f"❌ 입력이 numpy array가 아닙니다: {type(result_img)}", level=logging.ERROR) + return None + + if result_img.size == 0: + self.logger.log("❌ 입력 이미지가 비어있습니다", level=logging.ERROR) + return None + + if len(result_img.shape) != 3 or result_img.shape[2] not in [3, 4]: + self.logger.log(f"❌ 잘못된 이미지 형태: {result_img.shape}", level=logging.ERROR) + return None + + self.logger.log(f"🎨 후처리 시작: 입력 이미지 크기 {result_img.shape}, object_ratio={object_ratio}", level=logging.DEBUG) + + # 1) BGR 이미지에서 객체 마스크 생성 (흰 배경이 아닌 부분 감지) + if result_img.shape[2] == 4: + # 레거시 RGBA 처리 (이제는 거의 없을 것) + mask_init = (result_img[:, :, 3] > 128).astype(np.uint8) + rgba_img = result_img + self.logger.log(f"✅ RGBA 이미지 처리: 알파 채널 > 128인 픽셀 수: {np.sum(mask_init)}", level=logging.DEBUG) + else: + # BGR 이미지에서 흰 배경(255,255,255)이 아닌 부분을 객체로 인식 + gray = cv2.cvtColor(result_img, cv2.COLOR_BGR2GRAY) + mask_init = (gray < 250).astype(np.uint8) # 거의 흰색(250 미만)이 아닌 부분을 객체로 인식 + # BGR 이미지를 그대로 사용 (이미 흰 배경 합성 완료) + rgba_img = result_img + self.logger.log(f"✅ BGR 이미지 처리: 그레이 < 250인 픽셀 수: {np.sum(mask_init)}", level=logging.DEBUG) + + # 초기 마스크 저장 (디버그) + if debug_save and debug_dir: + step_num = step_offset + 1 + mask_init_path = os.path.join(debug_dir, f"{step_num:02d}_initial_mask.png") + input_img_path = os.path.join(debug_dir, f"{step_num:02d}_input_image.png") + cv2.imwrite(mask_init_path, mask_init * 255) # 마스크를 흰/검은색으로 표시 + cv2.imwrite(input_img_path, rgba_img) + self.logger.log(f"💾 {step_num}단계 저장: 초기 마스크 ({np.sum(mask_init)}개 픽셀) → {mask_init_path}", level=logging.DEBUG) + + # 2) 모폴로지 정제 (너무 공격적이지 않게 수정) + kernel = np.ones((3, 3), np.uint8) + mask_before_erode = mask_init.copy() + # 침식 연산을 건너뛰고 팽창만 적용 (객체 보존) + mask = cv2.dilate(mask_init, kernel, iterations=1) + self.logger.log(f"🔧 마스크 정제: 정제 전 {np.sum(mask_before_erode)}개 → 정제 후 {np.sum(mask)}개 픽셀", level=logging.DEBUG) + + # 정제된 마스크 저장 (디버그) + if debug_save and debug_dir: + step_num = step_offset + 2 + refined_mask_path = os.path.join(debug_dir, f"{step_num:02d}_refined_mask.png") + cv2.imwrite(refined_mask_path, mask * 255) + self.logger.log(f"💾 {step_num}단계 저장: 정제된 마스크 ({np.sum(mask)}개 픽셀) → {refined_mask_path}", level=logging.DEBUG) + + # 3) 최대 연결 요소만 유지 + num, labels, stats, _ = cv2.connectedComponentsWithStats(mask, connectivity=8) + self.logger.log(f"🔍 연결 요소 분석: {num-1}개 객체 발견", level=logging.DEBUG) + + if num > 1: + largest = 1 + np.argmax(stats[1:, cv2.CC_STAT_AREA]) + mask = (labels == largest).astype(np.uint8) + self.logger.log(f"🎯 최대 연결 요소 선택: {np.sum(mask)}개 픽셀", level=logging.DEBUG) + + # 연결 요소 분석 결과 저장 (디버그) + if debug_save and debug_dir: + step_num = step_offset + 3 + component_mask_path = os.path.join(debug_dir, f"{step_num:02d}_component_mask.png") + cv2.imwrite(component_mask_path, mask * 255) + self.logger.log(f"💾 {step_num}단계 저장: 연결 요소 마스크 ({np.sum(mask)}개 픽셀) → {component_mask_path}", level=logging.DEBUG) + + ys, xs = np.where(mask > 0) + if len(xs) == 0 or len(ys) == 0: + # 객체 감지 실패 → 원본 이미지를 그대로 1000x1000으로 리사이즈 + self.logger.log("⚠️ 객체 감지 실패: 원본 이미지를 그대로 리사이즈", level=logging.WARNING) + + # 원본 이미지는 이미 BGR (흰 배경 합성 완료) + original_bgr = result_img[:, :, :3] if result_img.shape[2] == 4 else result_img + + # 객체 감지 실패 이미지 저장 (디버그) + if debug_save and debug_dir: + step_num = step_offset + 4 + fallback_path = os.path.join(debug_dir, f"{step_num:02d}_fallback_original.png") + cv2.imwrite(fallback_path, original_bgr) + self.logger.log(f"💾 {step_num}단계 저장: 폴백 원본 이미지 → {fallback_path}", level=logging.DEBUG) + + # 1000x1000으로 리사이즈 + result_1000x1000 = self._create_square_canvas_with_dpi( + original_bgr, + target_size=1000, + target_dpi=72, + object_ratio=object_ratio + ) + + # 폴백 최종 결과 저장 (디버그) + if debug_save and debug_dir and result_1000x1000 is not None: + fallback_final_path = os.path.join(debug_dir, f"98_fallback_final_result.png") + cv2.imwrite(fallback_final_path, result_1000x1000) + self.logger.log(f"💾 폴백 최종 결과 저장: {result_1000x1000.shape} → {fallback_final_path}", level=logging.DEBUG) + + return result_1000x1000 + + # 4) 바운딩 박스 + 마진 + self.logger.log(f"📐 바운딩 박스 계산: 유효 픽셀 {len(xs)}개", level=logging.DEBUG) + top, left = ys.min(), xs.min() + bottom, right = ys.max(), xs.max() + crop_img = rgba_img[top:bottom + 1, left:right + 1] + self.logger.log(f"✂️ 크롭 영역: ({left},{top}) ~ ({right},{bottom}), 크기: {crop_img.shape}", level=logging.DEBUG) + + # 바운딩 박스 결과 저장 (디버그) + if debug_save and debug_dir: + step_num = step_offset + 5 + bbox_path = os.path.join(debug_dir, f"{step_num:02d}_bbox_crop.png") + cv2.imwrite(bbox_path, crop_img) + self.logger.log(f"💾 {step_num}단계 저장: 바운딩 박스 크롭 {crop_img.shape} → {bbox_path}", level=logging.DEBUG) + + ch, cw = crop_img.shape[:2] + margin = int(max(ch, cw) * 0.01) + # BGR 이미지에 흰색 마진 추가 + crop_img = cv2.copyMakeBorder( + crop_img, margin, margin, margin, margin, + borderType=cv2.BORDER_CONSTANT, + value=[255, 255, 255] # BGR 흰색 + ) + self.logger.log(f"📏 마진 추가: {margin}px, 최종 크기: {crop_img.shape}", level=logging.DEBUG) + + # 마진 추가 결과 저장 (디버그) + if debug_save and debug_dir: + step_num = step_offset + 6 + margin_path = os.path.join(debug_dir, f"{step_num:02d}_with_margin.png") + cv2.imwrite(margin_path, crop_img) + self.logger.log(f"💾 {step_num}단계 저장: 마진 추가 {crop_img.shape} → {margin_path}", level=logging.DEBUG) + + # 5) 이미 흰 배경 합성이 완료된 BGR 이미지이므로 그대로 사용 + final_img = crop_img + + if final_img is None: + self.logger.log("❌ 이미지 처리 실패", level=logging.ERROR) + return None + + self.logger.log(f"✅ 이미지 처리 완료 (이미 흰배경 합성됨): {final_img.shape}", level=logging.DEBUG) + + # 흰 배경 합성 결과 저장 (디버그) + if debug_save and debug_dir: + step_num = step_offset + 7 + white_bg_path = os.path.join(debug_dir, f"{step_num:02d}_white_background.png") + cv2.imwrite(white_bg_path, final_img) + self.logger.log(f"💾 {step_num}단계 저장: 흰 배경 합성 {final_img.shape} → {white_bg_path}", level=logging.DEBUG) + + # 6) 1000x1000 크기로 리사이즈하고 DPI 72 설정 + if final_img is None: + self.logger.log("❌ final_img가 None이어서 리사이즈 건너뜀", level=logging.ERROR) + return None + + self.logger.log(f"🎯 1000x1000 리사이즈 시작: object_ratio={object_ratio}, final_img.shape={final_img.shape}", level=logging.DEBUG) + result_1000x1000 = self._create_square_canvas_with_dpi( + final_img, + target_size=1000, + target_dpi=72, + object_ratio=object_ratio + ) + + if result_1000x1000 is not None: + self.logger.log(f"✅ 1000x1000 리사이즈 완료: {result_1000x1000.shape}", level=logging.DEBUG) + else: + self.logger.log("❌ 1000x1000 리사이즈 실패", level=logging.ERROR) + + return result_1000x1000 + + except Exception as e: + self.logger.log(f"❌ rembg 결과 후처리 에러: {e}", level=logging.ERROR, exc_info=True) + return None + + def _postprocess_rembg_result_old(self, result_img: np.ndarray) -> np.ndarray: + """RemoveBG 결과 이미지 후처리: 마스크 정제 및 중앙 배치""" + try: + # 1) 초기 마스크 + if result_img.shape[2] == 4: + mask_init = (result_img[:, :, 3] > 200).astype(np.uint8) + rgba_img = result_img + else: + gray = cv2.cvtColor(result_img[:, :, :3], cv2.COLOR_BGR2GRAY) + mask_init = (gray < 230).astype(np.uint8) + alpha_channel = (mask_init * 255).astype(np.uint8) + rgba_img = np.dstack([result_img, alpha_channel]) + + # 2) 모폴로지 정제 + kernel = np.ones((3, 3), np.uint8) + mask = cv2.erode(mask_init, kernel, iterations=1) + mask = cv2.dilate(mask, kernel, iterations=2) + + # 3) 최대 연결 요소만 유지 + num, labels, stats, _ = cv2.connectedComponentsWithStats(mask, connectivity=8) + if num > 1: + largest = 1 + np.argmax(stats[1:, cv2.CC_STAT_AREA]) + mask = (labels == largest).astype(np.uint8) + + ys, xs = np.where(mask > 0) + if len(xs) == 0 or len(ys) == 0: + # 객체 감지 실패 → 흰 배경 합성만 + white_bg = np.full_like(rgba_img[:, :, :3], 255) + return white_bg + + # 4) 바운딩 박스 + 마진 + top, left = ys.min(), xs.min() + bottom, right = ys.max(), xs.max() + crop_rgba = rgba_img[top:bottom + 1, left:right + 1] + + ch, cw = crop_rgba.shape[:2] + margin = int(max(ch, cw) * 0.01) + crop_rgba = cv2.copyMakeBorder( + crop_rgba, margin, margin, margin, margin, + borderType=cv2.BORDER_CONSTANT, + value=[255, 255, 255, 0] + ) + + # 5) 흰 배경으로 합성 + bgr_crop = crop_rgba[:, :, :3].astype(np.float32) + alpha_crop = crop_rgba[:, :, 3:4].astype(np.float32) / 255.0 + white_bg = np.full_like(bgr_crop, 255.0) + final_img = (bgr_crop * alpha_crop + white_bg * (1 - alpha_crop)).astype(np.uint8) + if final_img: + self.logger.log("흰배경 처리완료.", level=logging.DEBUG) + return None + return final_img + + except Exception as e: + self.logger.log(f"rembg 결과 후처리 에러: {e}", level=logging.ERROR, exc_info=True) + return None + + def _create_square_canvas_with_dpi(self, img: np.ndarray, target_size: int = 1000, target_dpi: int = 72, object_ratio: float = 0.75) -> np.ndarray: + """이미지를 1:1 비율로 만들고 지정된 크기와 DPI로 설정하며, 객체를 일정 비율로 스케일링""" + try: + # 입력 검증 + if img is None: + self.logger.log("❌ _create_square_canvas_with_dpi: 입력 이미지가 None입니다", level=logging.ERROR) + return None + + if not isinstance(img, np.ndarray): + self.logger.log(f"❌ _create_square_canvas_with_dpi: 입력이 numpy array가 아닙니다: {type(img)}", level=logging.ERROR) + return None + + if img.size == 0: + self.logger.log("❌ _create_square_canvas_with_dpi: 입력 이미지가 비어있습니다", level=logging.ERROR) + return None + + if len(img.shape) != 3 or img.shape[2] != 3: + self.logger.log(f"❌ _create_square_canvas_with_dpi: 잘못된 이미지 형태: {img.shape} (3채널 BGR 필요)", level=logging.ERROR) + return None + + h, w = img.shape[:2] + self.logger.log(f"🎯 정사각형 캔버스 생성 시작: 입력={h}x{w}, 목표={target_size}x{target_size}, 객체비율={object_ratio}", level=logging.DEBUG) + + # 1) 목표 객체 크기 계산 (캔버스의 75% 차지하도록) + target_object_size = int(target_size * object_ratio) + + # 2) 현재 이미지의 최대 치수 + max_dim = max(h, w) + + # 3) 스케일 팩터 계산 (객체가 목표 크기를 차지하도록) + scale_factor = target_object_size / max_dim + + # 4) 이미지 리사이즈 (객체 크기 정규화) + new_h = int(h * scale_factor) + new_w = int(w * scale_factor) + + # 크기 유효성 검사 + if new_h <= 0 or new_w <= 0: + self.logger.log(f"⚠️ 스케일링 결과 크기 이상: new_h={new_h}, new_w={new_w}, scale_factor={scale_factor}", level=logging.WARNING) + scaled_img = img + new_h, new_w = h, w + elif new_h > target_size * 2 or new_w > target_size * 2: + self.logger.log(f"⚠️ 스케일링 결과가 너무 큼: new_h={new_h}, new_w={new_w}, 원본 사용", level=logging.WARNING) + scaled_img = img + new_h, new_w = h, w + else: + try: + scaled_img = cv2.resize(img, (new_w, new_h), interpolation=cv2.INTER_LANCZOS4) + self.logger.log(f"✅ 이미지 리사이즈 완료: {h}x{w} → {new_h}x{new_w} (scale_factor={scale_factor:.3f})", level=logging.DEBUG) + except Exception as resize_error: + self.logger.log(f"⚠️ 리사이즈 실패: {resize_error}, 원본 사용", level=logging.WARNING) + scaled_img = img + new_h, new_w = h, w + + # 5) 1000x1000 정사각형 캔버스 생성 (흰색 배경) + square_canvas = np.full((target_size, target_size, 3), 255, dtype=np.uint8) + + # 6) 중앙에 스케일된 이미지 배치 + y_offset = (target_size - new_h) // 2 + x_offset = (target_size - new_w) // 2 + + # 경계 확인 및 안전성 검사 + if y_offset < 0 or x_offset < 0: + self.logger.log(f"⚠️ 오프셋 음수: y_offset={y_offset}, x_offset={x_offset}", level=logging.WARNING) + y_offset = max(0, y_offset) + x_offset = max(0, x_offset) + + y_end = min(y_offset + new_h, target_size) + x_end = min(x_offset + new_w, target_size) + scaled_h = y_end - y_offset + scaled_w = x_end - x_offset + + if scaled_h <= 0 or scaled_w <= 0: + self.logger.log(f"❌ 배치 영역 크기 이상: scaled_h={scaled_h}, scaled_w={scaled_w}", level=logging.ERROR) + return None + + # 배치 영역과 소스 영역 유효성 확인 + if scaled_h > scaled_img.shape[0] or scaled_w > scaled_img.shape[1]: + self.logger.log(f"⚠️ 배치 영역이 소스보다 큼: scaled={scaled_h}x{scaled_w}, source={scaled_img.shape[:2]}", level=logging.WARNING) + scaled_h = min(scaled_h, scaled_img.shape[0]) + scaled_w = min(scaled_w, scaled_img.shape[1]) + y_end = y_offset + scaled_h + x_end = x_offset + scaled_w + + try: + # 5) 1000x1000 정사각형 캔버스 생성 (흰색 배경) + square_canvas = np.full((target_size, target_size, 3), 255, dtype=np.uint8) + self.logger.log(f"✅ 정사각형 캔버스 생성: {square_canvas.shape}", level=logging.DEBUG) + + square_canvas[y_offset:y_end, x_offset:x_end] = scaled_img[:scaled_h, :scaled_w] + self.logger.log(f"✅ 이미지 배치 완료: offset=({x_offset},{y_offset}), size=({scaled_w},{scaled_h})", level=logging.DEBUG) + + # 7) PIL로 변환하여 DPI 설정 + pil_img = Image.fromarray(cv2.cvtColor(square_canvas, cv2.COLOR_BGR2RGB)) + + # DPI 설정 (인치당 픽셀 수) + pil_img = pil_img.copy() + pil_img.info['dpi'] = (target_dpi, target_dpi) + + # numpy로 다시 변환 + final_img = cv2.cvtColor(np.array(pil_img), cv2.COLOR_RGB2BGR) + + self.logger.log(f"✅ 정사각형 캔버스 완성: {object_ratio*100:.0f}% 스케일링, {target_size}x{target_size}, DPI {target_dpi}", level=logging.DEBUG) + return final_img + + except Exception as canvas_error: + self.logger.log(f"❌ 캔버스 생성/배치 실패: {canvas_error}", level=logging.ERROR, exc_info=True) + return None + + except Exception as e: + self.logger.log(f"정사각형 캔버스 생성 에러: {e}", level=logging.ERROR, exc_info=True) + return None + + def is_server_alive(self, base_url: str, timeout: int = 3) -> bool: + """서버 헬스체크(현재는 사용 안 함). base_url이 비어있으면 False.""" + import requests as _rq + try: + if not base_url: + return False + model_url = base_url.rstrip('/') + '/api/v1/model' + response = _rq.get(model_url, timeout=timeout) + return response.status_code == 200 + except Exception as e: + self.logger.log(f"서버 상태 확인 실패 ({base_url}): {e}", level=logging.WARNING) + return False + + # ------------------------- MIGAN helper ------------------------- + def _get_migan(self): + if getattr(self, "_migan", None) is not None: + return self._migan + if getattr(self, "_migan_error", None) is not None: + return None + try: + from modules.migan_module import build_migan_from_toggle + modules_dir = os.path.dirname(os.path.abspath(__file__)) + base_dir = modules_dir + onnx_path = os.path.join(base_dir, "migan_onnx", "migan_pipeline_v2.onnx") + toggles = { + "migan_onnx_path": onnx_path, + "migan_use_accel": True, + "migan_provider_override": os.environ.get("IMGWK_MIGAN_PROVIDER", "auto"), + } + self._migan = build_migan_from_toggle(toggles, logger=self.logger, gpu_manager=self.gpu_manager) + return self._migan + except Exception as e: + self._migan_error = str(e) + self.logger.log(f"MIGAN 초기화 실패: {e}", level=logging.ERROR, exc_info=True) + return None \ No newline at end of file diff --git a/modules/test/1.jpg b/modules/test/1.jpg new file mode 100644 index 0000000..d810fab Binary files /dev/null and b/modules/test/1.jpg differ diff --git a/modules/test/1_resized.jpg b/modules/test/1_resized.jpg new file mode 100644 index 0000000..7b26941 Binary files /dev/null and b/modules/test/1_resized.jpg differ diff --git a/modules/test/2.jpg b/modules/test/2.jpg new file mode 100644 index 0000000..b6ecdcf Binary files /dev/null and b/modules/test/2.jpg differ diff --git a/modules/test/2_resized.jpg b/modules/test/2_resized.jpg new file mode 100644 index 0000000..9394a37 Binary files /dev/null and b/modules/test/2_resized.jpg differ diff --git a/modules/test/5.jpg b/modules/test/5.jpg new file mode 100644 index 0000000..ac74d00 Binary files /dev/null and b/modules/test/5.jpg differ diff --git a/modules/test/5_resized.jpg b/modules/test/5_resized.jpg new file mode 100644 index 0000000..8eb1c42 Binary files /dev/null and b/modules/test/5_resized.jpg differ diff --git a/modules/test/concurrency_test.py b/modules/test/concurrency_test.py new file mode 100644 index 0000000..bb780e9 --- /dev/null +++ b/modules/test/concurrency_test.py @@ -0,0 +1,174 @@ +import os +import time +import argparse +import asyncio +import json +import requests + + +def submit_job_sync(api, img_path, prefix="detail", group="g1", seq=1, toggles=None): + payload = { + "file_path": img_path, + "index": seq - 1, + "file_prefix": prefix, + "group_id": group, + "seq": seq, + } + if toggles: + payload["toggle_overrides"] = toggles + r = requests.post(f"{api}/v1/process-image", json=payload, timeout=30) + r.raise_for_status() + return r.json()["job_id"] + + +def submit_rembg_sync(api, img_path, prefix="thumb", toggles=None): + payload = { + "file_path": img_path, + "file_prefix": prefix, + } + if toggles: + payload["toggle_overrides"] = toggles + r = requests.post(f"{api}/v1/remove-background", json=payload, timeout=30) + r.raise_for_status() + return r.json()["job_id"] + + +async def wait_job_async(session, api, job_id, timeout=900): + import aiohttp + end = time.time() + timeout + async with session.get(f"{api}/v1/jobs/{job_id}") as resp: + _ = await resp.text() # priming + while time.time() < end: + try: + async with session.get(f"{api}/v1/jobs/{job_id}", timeout=15) as r: + if r.status == 200: + data = await r.json() + if data.get("status") in ("done", "error", "cancelled"): + return data + except Exception: + await asyncio.sleep(0.2) + await asyncio.sleep(0.2) + raise TimeoutError("job wait timeout") + + +async def run_concurrent(api, images, mode="translate", prefix="detail", rembg_prefix="thumb", outdir=None, toggles=None, concurrency=4): + import aiohttp + sem = asyncio.Semaphore(concurrency) + + async def submit_and_wait(idx, img): + nonlocal api, mode, prefix, rembg_prefix, outdir, toggles + async with sem: + # 제출(동기 HTTP는 스레드풀로) + loop = asyncio.get_running_loop() + if mode == "translate": + jid = await loop.run_in_executor(None, submit_job_sync, api, img, prefix, "grp-t", idx + 1, toggles) + elif mode == "rembg": + jid = await loop.run_in_executor(None, submit_rembg_sync, api, img, rembg_prefix, toggles) + else: # both: 번역 먼저, rembg도 추가 + jid_t = await loop.run_in_executor(None, submit_job_sync, api, img, prefix, "grp-t", idx + 1, toggles) + jid_r = await loop.run_in_executor(None, submit_rembg_sync, api, img, rembg_prefix, toggles) + return (img, (jid_t, jid_r)) + + # 대기(aiohttp) + async with aiohttp.ClientSession() as session: + res = await wait_job_async(session, api, jid) + if outdir: + rr = res.get("result") or {} + path = (rr.get("path") or rr.get("result", {}).get("path")) + if path and os.path.isfile(path): + import shutil + base = os.path.basename(path) + tag = "t_" if mode == "translate" else "r_" + try: + shutil.copy2(path, os.path.join(outdir, f"{tag}{base}")) + except Exception: + pass + return (img, res) + + tasks = [submit_and_wait(i, img) for i, img in enumerate(images)] + return await asyncio.gather(*tasks) + + +def main(): + parser = argparse.ArgumentParser() + parser.add_argument("--api", default="http://127.0.0.1:8009") + parser.add_argument("--samples", nargs="*", default=None) + parser.add_argument("--mode", default="translate", choices=["translate", "rembg", "both"]) + parser.add_argument("--prefix", default="detail") + parser.add_argument("--rembg-prefix", default="thumb") + parser.add_argument("--outdir", default=None) + parser.add_argument("--concurrency", type=int, default=4) + parser.add_argument("--font", default=None, help="폰트 타입(예: 폰트3). toggle_overrides.font_type") + args = parser.parse_args() + + api = args.api.rstrip("/") + + # 헬스체크 + try: + hr = requests.get(f"{api}/health", timeout=10) + hr.raise_for_status() + h = hr.json() + if not h.get("ready"): + print("Worker not ready. Start API server: python main.py", flush=True) + return + except Exception as e: + print(f"Cannot reach API at {api}. Start server: python main.py | error={e}", flush=True) + return + + # 샘플 이미지 + root = os.path.abspath(os.path.join(os.path.dirname(__file__), "..")) + default_samples = [ + os.path.join(root, "test", "1.jpg"), + os.path.join(root, "test", "2.jpg"), + os.path.join(root, "test", "5.jpg"), + ] + samples = args.samples or default_samples + samples = [s for s in samples if os.path.isfile(s)] + if not samples: + print("No sample images found. Use --samples with absolute paths.") + return + + if args.outdir: + os.makedirs(args.outdir, exist_ok=True) + + # 토글 오버라이드 + toggles = {} + if args.font: + toggles["font_type"] = args.font + + # 실행 + total_start = time.time() + results = asyncio.run(run_concurrent(api, samples, mode=args.mode, prefix=args.prefix, rembg_prefix=args.rembg_prefix, outdir=args.outdir, toggles=toggles, concurrency=args.concurrency)) + total_s = time.time() - total_start + + # 출력 및 간단 통계 + done = 0 + timings = {"download": [], "ocr": [], "translate": [], "mask": [], "inpaint": [], "render": [], "save": [], "total_ms": []} + for item in results: + img, res = item + if isinstance(res, tuple): + print(f"submitted both for: {img} -> translate_jid={res[0]} rembg_jid={res[1]}") + continue + rr = (res or {}).get("result") or {} + if rr.get("status") == "translated" or rr.get("status") == "removed": + done += 1 + t = rr.get("timings") or {} + for k in timings.keys(): + v = t.get(k) + if v is not None: + try: + timings[k].append(float(v)) + except Exception: + pass + print(f"done: {img} -> status={rr.get('status')} path={rr.get('path')} timings_keys={list((rr.get('timings') or {}).keys())}") + + print(f"\nConcurrent batch finished: {done}/{len(results)} succeeded in {total_s:.2f}s") + if timings["total_ms"]: + avg = sum(timings["total_ms"]) / len(timings["total_ms"]) + print(f"avg total_ms: {avg:.1f} (n={len(timings['total_ms'])})") + + +if __name__ == "__main__": + main() + + diff --git a/modules/test/old_test/test_backup_rembg.py b/modules/test/old_test/test_backup_rembg.py new file mode 100644 index 0000000..bc3122f --- /dev/null +++ b/modules/test/old_test/test_backup_rembg.py @@ -0,0 +1,194 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +""" +Request_AI_Server의 _use_backup_rembg 함수 테스트 코드 +""" + +import os +import sys +import cv2 +import logging +import numpy as np +from datetime import datetime + +# 현재 파일의 디렉토리를 기준으로 src 폴더를 Python 경로에 추가 +current_dir = os.path.dirname(os.path.abspath(__file__)) +src_dir = os.path.dirname(current_dir) # src 폴더 +project_dir = os.path.dirname(src_dir) # 프로젝트 루트 +sys.path.insert(0, src_dir) + +try: + from modules.request_inpaint import Request_AI_Server + from modules.gpu_utils import GPUManager +except ImportError as e: + print(f"모듈 import 실패: {e}") + print(f"현재 디렉토리: {current_dir}") + print(f"src 디렉토리: {src_dir}") + print(f"sys.path: {sys.path[:3]}...") + sys.exit(1) + +class SimpleLogger: + """간단한 로거 클래스""" + + def __init__(self, name="TestLogger"): + self.name = name + + def log(self, message, level=logging.INFO, exc_info=False): + timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S") + level_name = logging.getLevelName(level) + print(f"[{timestamp}] [{level_name}] {message}") + if exc_info: + import traceback + traceback.print_exc() + +def test_backup_rembg(): + """_use_backup_rembg 함수 테스트""" + + print("=== Request_AI_Server _use_backup_rembg 함수 테스트 ===") + + # 1. 테스트 이미지 경로 확인 + test_image_path = os.path.join(current_dir, "1.jpg") + if not os.path.exists(test_image_path): + print(f"❌ 테스트 이미지가 없습니다: {test_image_path}") + print("1.jpg 파일을 다음 경로에 복사해주세요:") + print(f" {current_dir}") + return False + + print(f"✅ 테스트 이미지 발견: {test_image_path}") + + # 2. 이미지 로드 + try: + image_data = cv2.imread(test_image_path) + if image_data is None: + print(f"❌ 이미지 로드 실패: {test_image_path}") + return False + + height, width = image_data.shape[:2] + print(f"✅ 이미지 로드 성공: {width}x{height}") + + except Exception as e: + print(f"❌ 이미지 로드 중 오류: {e}") + return False + + # 3. 로거 및 GPU 관리자 초기화 + logger = SimpleLogger("BackupRemBGTest") + + try: + gpu_manager = GPUManager(logger=logger) + # DirectML GPU 모드로 강제 설정 (테스트용) + gpu_manager.can_use_cuda = True + print(f"✅ GPU 관리자 초기화 성공: CUDA 사용 가능 = {gpu_manager.can_use_cuda} (DirectML 강제 활성화)") + except Exception as e: + print(f"⚠️ GPU 관리자 초기화 실패, None으로 설정: {e}") + gpu_manager = None + + # 4. Request_AI_Server 초기화 + try: + # 로컬 rembg 모델 경로 설정 + local_rembg_model_path = os.path.join(src_dir, "modules", "rembg_models", "birefnet-general-lite.onnx") + if not os.path.exists(local_rembg_model_path): + print(f"⚠️ 로컬 rembg 모델이 없습니다: {local_rembg_model_path}") + local_rembg_model_path = None + else: + print(f"✅ 로컬 rembg 모델 발견: {local_rembg_model_path}") + + request_ai_server = Request_AI_Server( + logger=logger, + inpaint_server_url="http://localhost:8008", # 테스트용 (실제로는 사용하지 않음) + rembg_server_url=None, + gpu_manager=gpu_manager, + local_rembg_model_path=local_rembg_model_path + ) + print("✅ Request_AI_Server 초기화 성공") + + except Exception as e: + print(f"❌ Request_AI_Server 초기화 실패: {e}") + return False + + # 5. _use_backup_rembg 함수 테스트 + print("\n--- 백업 rembg 모듈 테스트 시작 ---") + + test_cases = [ + {"model_name": "birefnet-general-lite", "object_ratio": 0.75, "force_cpu": False, "desc": "DirectML GPU 모드 (문제)"}, + {"model_name": "birefnet-general-lite", "object_ratio": 0.75, "force_cpu": True, "desc": "CPU 모드 (정상)"}, + {"model_name": "birefnet-general-lite", "object_ratio": 1, "force_cpu": True, "desc": "CPU 모드 + 다른 비율"}, + ] + + results = [] + for i, test_case in enumerate(test_cases): + print(f"\n테스트 케이스 {i+1}: {test_case}") + + try: + start_time = datetime.now() + + result_img = request_ai_server._use_backup_rembg( + image_data=image_data, + model_name=test_case["model_name"], + object_ratio=test_case["object_ratio"], + debug_save=True, + debug_prefix=f"test_{i+1}_{test_case['desc'].replace(' ', '_')}", + force_cpu=test_case["force_cpu"] + ) + + end_time = datetime.now() + processing_time = (end_time - start_time).total_seconds() + + if result_img is not None: + # 결과 이미지 저장 + # 안전한 파일명 생성 (한글 및 특수문자 제거) + safe_desc = test_case['desc'].replace('DirectML GPU 모드 (문제)', 'DirectML_GPU_problem').replace('CPU 모드 (정상)', 'CPU_normal').replace('CPU 모드 + 다른 비율', 'CPU_different_ratio') + output_filename = f"backup_rembg_result_{i+1}_{safe_desc}_ratio{int(test_case['object_ratio']*100)}.png" + output_path = os.path.join(current_dir, output_filename) + + success = cv2.imwrite(output_path, result_img) + if success: + result_height, result_width = result_img.shape[:2] + print(f" ✅ 성공! 결과 이미지 크기: {result_width}x{result_height}") + print(f" 📁 저장 경로: {output_path}") + print(f" ⏱️ 처리 시간: {processing_time:.2f}초") + + results.append({ + "test_case": i+1, + "success": True, + "output_path": output_path, + "size": (result_width, result_height), + "processing_time": processing_time + }) + else: + print(f" ❌ 결과 이미지 저장 실패: {output_path}") + results.append({"test_case": i+1, "success": False, "error": "이미지 저장 실패"}) + else: + print(f" ❌ 함수 실행 실패: 결과 이미지가 None") + results.append({"test_case": i+1, "success": False, "error": "결과 이미지 None"}) + + except Exception as e: + print(f" ❌ 테스트 케이스 {i+1} 실행 중 오류: {e}") + results.append({"test_case": i+1, "success": False, "error": str(e)}) + + # 6. 테스트 결과 요약 + print("\n=== 테스트 결과 요약 ===") + success_count = sum(1 for r in results if r.get("success", False)) + total_count = len(results) + + print(f"총 테스트: {total_count}개") + print(f"성공: {success_count}개") + print(f"실패: {total_count - success_count}개") + + for result in results: + if result.get("success", False): + print(f" ✅ 케이스 {result['test_case']}: {result['size'][0]}x{result['size'][1]}, {result['processing_time']:.2f}초") + else: + print(f" ❌ 케이스 {result['test_case']}: {result.get('error', '알 수 없는 오류')}") + + return success_count > 0 + +if __name__ == "__main__": + success = test_backup_rembg() + + if success: + print("\n🎉 테스트 완료! 결과 이미지를 확인해보세요.") + print(f"📁 결과 파일 위치: {current_dir}") + else: + print("\n💥 테스트 실패!") + + sys.exit(0 if success else 1) diff --git a/modules/test/old_test/test_bg.py b/modules/test/old_test/test_bg.py new file mode 100644 index 0000000..2b0072f --- /dev/null +++ b/modules/test/old_test/test_bg.py @@ -0,0 +1,43 @@ +import fastdeploy as fd +import cv2 +import numpy as np +from PIL import Image + +class FastDeployMattingModule: + def __init__(self, encoder_onnx, decoder_onnx, use_gpu=False, device_id=0): + rt_opt = fd.RuntimeOption() + if use_gpu: + rt_opt.use_gpu(device_id) + else: + rt_opt.use_cpu() + self.model = fd.vision.matting.SAM( + model_file=encoder_onnx, + params_file=decoder_onnx, + runtime_option=rt_opt, + model_format=fd.ModelFormat.ONNX, + ) + + def remove_background(self, img_path): + img = cv2.imread(img_path) + if img is None: + raise FileNotFoundError("Failed to load:", img_path) + + res = self.model.predict(img) + alpha = (res.alpha * 255).astype(np.uint8) + bgr = res.foreground + + bgra = cv2.cvtColor(bgr, cv2.COLOR_BGR2BGRA) + bgra[..., 3] = alpha + return Image.fromarray(cv2.cvtColor(bgra, cv2.COLOR_BGRA2RGBA)) + + +# 사용 예시 +if __name__ == "__main__": + fm = FastDeployMattingModule( + encoder_onnx="sam_vit_b_01ec64.encoder.onnx", + decoder_onnx="sam_vit_b_01ec64.decoder.onnx", + use_gpu=False + ) + img_rgba = fm.remove_background("8.jpg") + img_rgba.save("8_sam_matting.png") + print("✅ 배경제거 완료 및 저장됨") diff --git a/modules/test/old_test/test_bria_rembg.py b/modules/test/old_test/test_bria_rembg.py new file mode 100644 index 0000000..738b33a --- /dev/null +++ b/modules/test/old_test/test_bria_rembg.py @@ -0,0 +1,216 @@ +#!/usr/bin/env python3 +""" +BriaAI 배경제거 모듈 테스트 스크립트 +DirectML vs CPU 모드 비교 테스트 +""" +import os +import sys +import cv2 +import numpy as np +from datetime import datetime + +# 현재 스크립트 디렉토리 및 프로젝트 루트 설정 +current_dir = os.path.dirname(os.path.abspath(__file__)) +src_dir = os.path.dirname(current_dir) +project_root = os.path.dirname(src_dir) + +# Python 경로에 추가 +if project_root not in sys.path: + sys.path.insert(0, project_root) + +# 모듈 임포트 +try: + # 직접 모듈 경로에서 임포트 + sys.path.insert(0, src_dir) + from src.modules.gpu_utils import GPUManager + from src.modules.request_inpaint import Request_AI_Server + print("✅ 모듈 임포트 성공") +except ImportError as e: + print(f"❌ 모듈 임포트 실패: {e}") + print(f"디버그 정보: src_dir={src_dir}, project_root={project_root}") + print(f"Python path: {sys.path[:3]}...") + sys.exit(1) + + +class SimpleLogger: + """간단한 로거 클래스""" + def __init__(self, name): + self.name = name + + def log(self, msg, level=None, exc_info=False): + timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S") + level_str = f"[{level.name if hasattr(level, 'name') else str(level)}]" if level else "" + print(f"[{timestamp}] {level_str} {msg}") + + +def main(): + print("=== BriaAI 배경제거 모듈 테스트 ===") + + # 1. 테스트 이미지 확인 + test_image_path = os.path.join(current_dir, "1.jpg") + if not os.path.exists(test_image_path): + print(f"❌ 테스트 이미지를 찾을 수 없습니다: {test_image_path}") + print("📝 같은 폴더에 '1.jpg' 파일을 복사해주세요.") + return False + + print(f"✅ 테스트 이미지 발견: {test_image_path}") + + # 2. 이미지 로드 및 확인 + image_data = cv2.imread(test_image_path) + if image_data is None: + print(f"❌ 이미지 로드 실패: {test_image_path}") + return False + + height, width = image_data.shape[:2] + print(f"✅ 이미지 로드 성공: {width}x{height}") + + # 3. 로거 및 GPU 관리자 초기화 + logger = SimpleLogger("BriaAITest") + + try: + gpu_manager = GPUManager(logger=logger) + # DirectML GPU 모드로 강제 설정 (테스트용) + gpu_manager.can_use_cuda = True + print(f"✅ GPU 관리자 초기화 성공: CUDA 사용 가능 = {gpu_manager.can_use_cuda} (DirectML 강제 활성화)") + except Exception as e: + print(f"⚠️ GPU 관리자 초기화 실패, None으로 설정: {e}") + gpu_manager = None + + # 4. BriaAI 모델 경로 설정 (사용자가 설정해야 함) + # TODO: 실제 BriaAI ONNX 모델 경로로 변경하세요 + bria_model_path = os.path.join(src_dir, "modules", "bria_models", "BriaRMBG1.4_model_fp16.onnx") + if not os.path.exists(bria_model_path): + print(f"⚠️ BriaAI 모델을 찾을 수 없습니다: {bria_model_path}") + print("📝 BriaAI RMBG 1.4 ONNX 모델을 다운로드하고 경로를 설정해주세요.") + # 대체 경로들 시도 + alternative_paths = [ + os.path.join(src_dir, "modules", "rembg_models", "BriaRMBG1.4_model_fp16.onnx"), + os.path.join(current_dir, "BriaRMBG1.4_model_fp16.onnx"), + os.path.join(project_root, "models", "BriaRMBG1.4_model_fp16.onnx") + ] + + bria_model_found = False + for alt_path in alternative_paths: + if os.path.exists(alt_path): + bria_model_path = alt_path + bria_model_found = True + print(f"✅ 대체 BriaAI 모델 발견: {bria_model_path}") + break + + if not bria_model_found: + print("❌ BriaAI 모델을 찾을 수 없어 테스트를 중단합니다.") + print("💡 다음 중 한 위치에 BriaAI RMBG 1.4 ONNX 모델을 배치해주세요:") + for path in alternative_paths: + print(f" - {path}") + return False + else: + print(f"✅ BriaAI 모델 발견: {bria_model_path}") + + # 5. Request_AI_Server 초기화 + try: + request_ai_server = Request_AI_Server( + logger=logger, + inpaint_server_url="http://192.168.0.150:8008", # 더미 URL + rembg_server_url=None, # 로컬 모드 사용 + gpu_manager=gpu_manager, + local_rembg_model_path=bria_model_path # BriaAI 모델 경로 + ) + print("✅ Request_AI_Server 초기화 성공") + except Exception as e: + print(f"❌ Request_AI_Server 초기화 실패: {e}") + return False + + # 6. BriaAI 배경제거 모듈 테스트 + print("\n--- BriaAI 배경제거 모듈 테스트 시작 ---") + + test_cases = [ + {"model_name": "bria-rmbg-1.4", "object_ratio": 0.75, "force_cpu": False, "desc": "DirectML_GPU_BriaAI"}, + {"model_name": "bria-rmbg-1.4", "object_ratio": 0.75, "force_cpu": True, "desc": "CPU_BriaAI"}, + {"model_name": "bria-rmbg-aggressive", "object_ratio": 0.8, "force_cpu": True, "desc": "Aggressive_BriaAI"}, + {"model_name": "bria-rmbg-gentle", "object_ratio": 0.6, "force_cpu": True, "desc": "Gentle_BriaAI"}, + ] + + results = [] + for i, test_case in enumerate(test_cases): + print(f"\n테스트 케이스 {i+1}: {test_case}") + + try: + start_time = datetime.now() + + result_img = request_ai_server._use_backup_rembg( + image_data=image_data, + model_name=test_case["model_name"], + object_ratio=test_case["object_ratio"], + debug_save=True, + debug_prefix=f"bria_test_{i+1}_{test_case['desc'].replace(' ', '_')}", + force_cpu=test_case["force_cpu"] + ) + + end_time = datetime.now() + processing_time = (end_time - start_time).total_seconds() + + if result_img is not None: + # 결과 이미지 저장 (영어 파일명) + safe_desc = test_case['desc'] # 이미 영어로 설정됨 + output_filename = f"bria_rembg_result_{i+1}_{safe_desc}_ratio{int(test_case['object_ratio']*100)}.png" + output_path = os.path.join(current_dir, output_filename) + + success = cv2.imwrite(output_path, result_img) + if success: + result_height, result_width = result_img.shape[:2] + print(f" ✅ 성공! 결과 이미지 크기: {result_width}x{result_height}") + print(f" 📁 저장 경로: {output_path}") + print(f" ⏱️ 처리 시간: {processing_time:.2f}초") + + results.append({ + 'case': i+1, + 'success': True, + 'size': f"{result_width}x{result_height}", + 'time': processing_time, + 'path': output_path + }) + else: + print(f" ❌ 결과 이미지 저장 실패: {output_path}") + results.append({'case': i+1, 'success': False, 'error': '이미지 저장 실패'}) + else: + print(f" ❌ 함수 실행 실패: 결과 이미지가 None") + results.append({'case': i+1, 'success': False, 'error': '결과 이미지 None'}) + + except Exception as e: + print(f" ❌ 예외 발생: {e}") + results.append({'case': i+1, 'success': False, 'error': str(e)}) + + # 7. 테스트 결과 요약 + print(f"\n=== 테스트 결과 요약 ===") + total_tests = len(test_cases) + successful_tests = sum(1 for r in results if r['success']) + failed_tests = total_tests - successful_tests + + print(f"총 테스트: {total_tests}개") + print(f"성공: {successful_tests}개") + print(f"실패: {failed_tests}개") + + for i, result in enumerate(results): + if result['success']: + print(f" ✅ 케이스 {result['case']}: {result['size']}, {result['time']:.2f}초") + else: + print(f" ❌ 케이스 {result['case']}: {result['error']}") + + print(f"\n🎉 테스트 완료! 결과 이미지를 확인해보세요.") + print(f"📁 결과 파일 위치: {current_dir}") + + return True + + +if __name__ == "__main__": + try: + success = main() + sys.exit(0 if success else 1) + except KeyboardInterrupt: + print("\n⚠️ 사용자에 의해 테스트가 중단되었습니다.") + sys.exit(1) + except Exception as e: + print(f"❌ 예상치 못한 오류 발생: {e}") + import traceback + traceback.print_exc() + sys.exit(1) diff --git a/modules/test/old_test/test_easyocr_translate.py b/modules/test/old_test/test_easyocr_translate.py new file mode 100644 index 0000000..043427c --- /dev/null +++ b/modules/test/old_test/test_easyocr_translate.py @@ -0,0 +1,44 @@ +import os +import asyncio +from image_processor3 import ImageProcessor3 + +class SimpleLogger: + def log(self, msg, level=None, exc_info=None): + print(msg) + +async def main(): + logger = SimpleLogger() + base_dir = os.path.dirname(os.path.abspath(__file__)) + file_num = 7 + img_path = os.path.join(base_dir, 'img', f"{file_num}.jpg") + + # ImageProcessor3 인스턴스 생성 + toggle_states = { + 'TEMP_IMAGE_DIR': os.path.join(base_dir, 'img'), + 'ocr': True + } + image_processor = ImageProcessor3( + logger=logger, + browser_page=None, + toggle_states=toggle_states, + base_dir=base_dir + ) + + unwanted_texts = { + "고품질": "저품질", + "세탁기": "세세탁기" + } + + image_processor.update_unwanted_texts(unwanted_texts) + + # # process_single_image 호출 + # result = await image_processor.process_single_image( + # page=None, original_image_url=img_path, delay=0, index=file_num, file_prefix="" + # ) + + result = await image_processor.remove_background(img_path, index=file_num, file_prefix="") + + print(f"처리 결과: {result}") + +if __name__ == '__main__': + asyncio.run(main()) \ No newline at end of file diff --git a/modules/test/old_test/test_gpt_client.py b/modules/test/old_test/test_gpt_client.py new file mode 100644 index 0000000..9228961 --- /dev/null +++ b/modules/test/old_test/test_gpt_client.py @@ -0,0 +1,41 @@ +import logging +from openai import OpenAI +import json +import re +import logging + + +class GPTClient: + def __init__(self, model="gpt-4o-mini", temperature=0.2): + self.client = None + self.model = model + self.temperature = temperature + + self.set_client(api_key='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') + + def set_client(self, api_key): + self.client = OpenAI(api_key=api_key) + + def ask(self, prompt: str) -> dict: + """프롬프트를 이용하여 GPT 모델로부터 응답을 받습니다. 항상 JSON 형식으로 반환.""" + try: + response = self.client.chat.completions.create( + model=self.model, + temperature=self.temperature, + messages=[{"role": "user", "content": prompt}] + ) + # GPT 응답 내용 가져오기 + content = response.choices[0].message.content.strip() + print(f'GPT 응답: {content}') + # 불필요한 포맷팅 제거 (```json```) + cleaned_content = re.sub(r"^```json|```$", "", content).strip() + + # JSON 변환 시도 + return json.loads(cleaned_content) + except json.JSONDecodeError as e: + print(f'JSON 디코딩 실패: {e}. 원본 응답: {content}') + + return {} + except Exception as e: + print(f'GPT 통신 오류: {e}') + return {} diff --git a/modules/test/old_test/test_image_processor.py b/modules/test/old_test/test_image_processor.py new file mode 100644 index 0000000..33453d2 --- /dev/null +++ b/modules/test/old_test/test_image_processor.py @@ -0,0 +1,306 @@ +import time +import numpy as np +from typing import Dict, List, Optional, Any +from .ocr_module import OCRModule +from .inpainting_module import InpaintingModule +from .text_renderer import TextRenderer +from .image_utils import ImageUtils +from .translation_module import translate_texts, translate_texts_deepl +import os + +class ImageProcessor: + """이미지 번역을 위한 메인 프로세서""" + + def __init__(self, use_gpu: bool = True): + """ + 이미지 프로세서 초기화 + + Args: + use_gpu: GPU 사용 여부 + """ + self.use_gpu = use_gpu + + # 모듈 초기화 + print("모듈들을 초기화하고 있습니다...") + self.ocr_module = OCRModule(use_gpu=use_gpu) + self.inpainting_module = InpaintingModule() + self.text_renderer = TextRenderer() + + print("이미지 프로세서 초기화 완료!") + + def process_image(self, base64_image: str, options: Optional[Dict] = None) -> Dict: + """ + 이미지 번역 전체 프로세스 수행 + + Args: + base64_image: Base64 인코딩된 입력 이미지 + options: 처리 옵션 + - inpainting_model: 사용할 인페인팅 모델 + - translator: 사용할 번역기 + - save_result: 결과 저장 여부 + - return_intermediate: 중간 결과 반환 여부 + + Returns: + 처리 결과 딕셔너리 + """ + start_time = time.time() + + if options is None: + options = {} + + try: + # 1. Base64 이미지를 OpenCV 이미지로 변환 + print("1. 이미지 변환 중...") + image = ImageUtils.base64_to_image(base64_image) + if image is None: + print("이미지 디코딩 실패: base64 데이터가 올바르지 않습니다.") + return { + 'success': False, + 'error': '이미지 디코딩 실패: base64 데이터가 올바르지 않습니다.' + } + original_image = image.copy() + + # 2. 중국어 텍스트 존재 여부 확인 + print("2. 중국어 텍스트 확인 중...") + has_chinese = self.ocr_module.has_chinese_text(image) + + if not has_chinese: + return { + 'success': True, + 'message': '중국어 텍스트가 발견되지 않았습니다.', + 'has_chinese_text': False, + 'processing_time': time.time() - start_time, + 'result_url': None + } + + # 3. OCR 수행 - 중국어 텍스트 추출 + print("3. OCR 수행 중...") + ocr_result = self.ocr_module.extract_chinese_text(image) + + if not ocr_result['texts']: + return { + 'success': True, + 'message': '추출할 중국어 텍스트가 없습니다.', + 'has_chinese_text': True, + 'detected_texts': [], + 'processing_time': time.time() - start_time, + 'result_url': None + } + + detected_texts = ocr_result['texts'] + polygons = ocr_result['polygons'] + masks = ocr_result['masks'] + + print(f" - 감지된 텍스트: {len(detected_texts)}개") + for i, text in enumerate(detected_texts): + print(f" {i+1}. {text}") + + # 4. 인페인팅 수행 + print("4. 인페인팅 수행 중...") + inpainting_model = options.get('inpainting_model', 'opencv_telea') + + if len(masks) == 1: + inpainted_image = self.inpainting_module.inpaint_single( + image, masks[0] + ) + else: + # 여러 마스크를 결합하여 한 번에 처리 (더 자연스러운 결과) + inpainted_image = self.inpainting_module.inpaint_combined( + image, masks + ) + + # 인페인팅 결과 저장 (별도 폴더) + import uuid, os + result_dir = "results/inpainted" + os.makedirs(result_dir, exist_ok=True) + inpainted_filename = os.path.join(result_dir, f"inpainted_{uuid.uuid4().hex}.jpg") + ImageUtils.save_image(inpainted_image, inpainted_filename) + + # 5. 번역 수행 + print("5. 번역 수행 중...") + translated_texts = translate_texts_deepl(detected_texts) + # translated_texts = translate_texts(detected_texts) + + print(f" - 번역 결과:") + for i, (original, translated) in enumerate(zip(detected_texts, translated_texts)): + print(f" {i+1}. {original} -> {translated}") + + # OpenCV 인페인팅(telea, ns) 결과도 함께 저장 + import cv2 + if len(masks) == 1: + mask_for_cv = masks[0] + else: + mask_for_cv = np.maximum.reduce(masks) + inpainted_telea = cv2.inpaint(image, mask_for_cv, 3, cv2.INPAINT_TELEA) + inpainted_ns = cv2.inpaint(image, mask_for_cv, 3, cv2.INPAINT_NS) + ImageUtils.save_image(inpainted_telea, f"results/inpainted_opencv_telea_{uuid.uuid4().hex}.jpg") + ImageUtils.save_image(inpainted_ns, f"results/inpainted_opencv_ns_{uuid.uuid4().hex}.jpg") + + # # OpenCV 인페인팅 + 텍스트 렌더링 결과도 저장 + # telea_with_text = self.text_renderer.render_translated_texts( + # inpainted_telea, translated_texts, polygons, detected_texts + # ) + # ns_with_text = self.text_renderer.render_translated_texts( + # inpainted_ns, translated_texts, polygons, detected_texts + # ) + # ImageUtils.save_image(telea_with_text, f"results/inpainted_opencv_telea_text_{uuid.uuid4().hex}.jpg") + # ImageUtils.save_image(ns_with_text, f"results/inpainted_opencv_ns_text_{uuid.uuid4().hex}.jpg") + + # 6. 텍스트 렌더링 + print("6. 텍스트 렌더링 중...") + final_image = self.text_renderer.render_translated_texts( + inpainted_image, translated_texts, polygons, detected_texts + ) + + # 7. 결과 저장 및 URL 생성 + result_url = None + if options.get('save_result', True): + print("7. 결과 저장 중...") + # 웹서버가 설정되어 있다면 URL 생성 + if hasattr(self, 'web_server') and self.web_server: + result_url = self.web_server.save_result_image(final_image) + else: + # 로컬 저장 + filename = f"result_{uuid.uuid4().hex}.jpg" + ImageUtils.save_image(final_image, f"results/{filename}") + result_url = f"results/{filename}" + + processing_time = time.time() - start_time + print(f"처리 완료! (소요 시간: {processing_time:.2f}초)") + + result = { + 'success': True, + 'message': '이미지 번역이 성공적으로 완료되었습니다.', + 'has_chinese_text': True, + 'detected_texts': detected_texts, + 'translated_texts': translated_texts, + 'processing_time': processing_time, + 'result_url': result_url + } + + # 중간 결과 포함 (옵션) + if options.get('return_intermediate', False): + result['intermediate'] = { + 'original_image': ImageUtils.image_to_base64(original_image), + 'inpainted_image': ImageUtils.image_to_base64(inpainted_image), + 'final_image': ImageUtils.image_to_base64(final_image), + 'ocr_result': ocr_result + } + + return result + + except Exception as e: + print(f"이미지 처리 중 오류 발생: {e}") + return { + 'success': False, + 'error': str(e), + 'processing_time': time.time() - start_time + } + + + def set_web_server(self, web_server): + """웹서버 설정""" + self.web_server = web_server + + def get_available_models(self) -> Dict: + """사용 가능한 모델 정보 반환""" + return { + 'inpainting_models': self.inpainting_module.get_available_models(), + 'current_settings': { + 'inpainting_model': self.inpainting_module.current_model, + } + } + + def set_models(self, config: Dict) -> Dict: + """모델 설정""" + try: + result = {'success': True, 'updated': []} + + if 'inpainting_model' in config: + self.inpainting_module.set_model(config['inpainting_model']) + result['updated'].append('inpainting_model') + + if 'translator' in config: + self.translation_module.set_translator(config['translator']) + result['updated'].append('translator') + + result['message'] = f"모델 설정이 업데이트되었습니다: {', '.join(result['updated'])}" + return result + + except Exception as e: + return { + 'success': False, + 'error': str(e) + } + def test_with_sample_image(self, image_path_or_url: str) -> Dict: + """ + 샘플 이미지로 테스트 (로컬 파일 또는 URL 지원) + + Args: + image_path_or_url: 테스트 이미지 경로 또는 URL + + Returns: + 테스트 결과 + """ + try: + # 이미지 로드 (자동으로 URL 또는 로컬 파일 판단) + image = ImageUtils.load_image_auto(image_path_or_url) + + # Base64로 변환 + base64_image = ImageUtils.image_to_base64(image) + + # 처리 수행 + result = self.process_image(base64_image, { + 'return_intermediate': True, + 'save_result': True + }) + + return result + + except Exception as e: + return { + 'success': False, + 'error': f"테스트 중 오류: {e}" + } + + def compare_models(self, base64_image: str) -> Dict: + """ + 여러 모델로 결과 비교 + + Args: + base64_image: 입력 이미지 + + Returns: + 모델별 비교 결과 + """ + try: + image = ImageUtils.base64_to_image(base64_image) + + # OCR 수행 + ocr_result = self.ocr_module.extract_chinese_text(image) + + if not ocr_result['texts']: + return { + 'success': False, + 'error': '중국어 텍스트가 발견되지 않았습니다.' + } + + # 인페인팅 모델 비교 + inpainting_results = self.inpainting_module.compare_models( + image, ocr_result['masks'][0] if ocr_result['masks'] else None + ) + + return { + 'success': True, + 'detected_texts': ocr_result['texts'], + 'inpainting_comparison': { + model: ImageUtils.image_to_base64(result) + for model, result in inpainting_results.items() + } + } + + except Exception as e: + return { + 'success': False, + 'error': str(e) + } \ No newline at end of file diff --git a/modules/test/old_test/test_img.py b/modules/test/old_test/test_img.py new file mode 100644 index 0000000..4f0836c --- /dev/null +++ b/modules/test/old_test/test_img.py @@ -0,0 +1,133 @@ +import os +import sys +import asyncio +import shutil +from src.modules.image_processor2 import ImageProcessor +from src.modules.test_loggerModule import Logger1 +from src.modules.test_gpt_client import GPTClient +import logging +import cv2 + +# 더미 Logger +class DummyLogger: + def log(self, msg, level=logging.INFO, exc_info=None): + print(f"[{logging.getLevelName(level)}] {msg}") + + +# 테스트용 치환단어 +unwanted_texts = { + '크리스탈': '이미지삭제', + '세탁기': '세탁기는개뿔', +} + +def get_image_list(img_dir): + files = [f for f in os.listdir(img_dir) if f.lower().endswith(('.jpg', '.jpeg', '.png', '.bmp', '.webp'))] + files.sort() + return [os.path.join(img_dir, f) for f in files] + +def ensure_dir(path): + if not os.path.exists(path): + os.makedirs(path) + +def save_image(image, path): + cv2.imwrite(path, image) + +async def sequential_process(image_paths, processor, output_dir): + print("[순차처리] 시작") + results = [] + for idx, img_path in enumerate(image_paths): + print(f"[{idx+1}] {img_path} 처리 중...") + # OCR, 번역, 치환, 인페인팅 등 전체 파이프라인 실행 + # process_single_image는 내부적으로 모든 로직을 처리함 + result = await processor.process_single_image( + page=None, # 실제 Playwright 객체 대신 None + original_image_url=img_path, + index=idx, + is_localServer=True, + delay=0.1, + file_prefix="seq", + use_inpainting=True + ) + # 결과 파일명 결정 + if isinstance(result, dict): + status = result.get('status', 'unknown') + path = result.get('path', img_path) + if status == 'failed': + out_name = f"{idx+1}_failed_{os.path.basename(img_path)}" + shutil.copy(img_path, os.path.join(output_dir, out_name)) + elif status == 'exclude': + # 이미지삭제: 파일을 output에 저장하지 않음 + print(f"[{idx+1}] 이미지삭제로 제외됨: {img_path}") + continue + else: + out_name = f"{idx+1}_{status}_{os.path.basename(img_path)}" + shutil.copy(path, os.path.join(output_dir, out_name)) + else: + # result가 경로(str)라면 원본/번역된 이미지로 간주 + out_name = f"{idx+1}_original_{os.path.basename(img_path)}" + shutil.copy(result, os.path.join(output_dir, out_name)) + results.append(out_name) + print("[순차처리] 완료") + return results + +async def parallel_process(image_paths, processor, output_dir): + print("[동시처리] 시작") + tasks = [] + for idx, img_path in enumerate(image_paths): + tasks.append(processor.process_single_image( + page=None, + original_image_url=img_path, + index=idx, + is_localServer=True, + delay=0.1, + file_prefix="par", + use_inpainting=True + )) + results = await asyncio.gather(*tasks) + for idx, (img_path, result) in enumerate(zip(image_paths, results)): + if isinstance(result, dict): + status = result.get('status', 'unknown') + path = result.get('path', img_path) + if status == 'failed': + out_name = f"{idx+1}_failed_{os.path.basename(img_path)}" + shutil.copy(img_path, os.path.join(output_dir, out_name)) + elif status == 'exclude': + print(f"[{idx+1}] 이미지삭제로 제외됨: {img_path}") + continue + else: + out_name = f"{idx+1}_{status}_{os.path.basename(img_path)}" + shutil.copy(path, os.path.join(output_dir, out_name)) + else: + out_name = f"{idx+1}_original_{os.path.basename(img_path)}" + shutil.copy(result, os.path.join(output_dir, out_name)) + print("[동시처리] 완료") + return results + +async def main(): + base_dir = os.path.dirname(os.path.abspath(__file__)) + img_dir = os.path.join(base_dir, 'img') + output_dir = os.path.join(base_dir, 'output') + ensure_dir(output_dir) + image_paths = get_image_list(img_dir) + print(f"테스트 이미지: {image_paths}") + + # 더미 logger, gpt_client, toggle_states + set_log = Logger1() + gpt_client = GPTClient() + toggle_states = { + 'image_font_path': os.path.join(base_dir, "HakgyoansimDunggeunmisoTTFB.ttf"), + 'TEMP_IMAGE_DIR': output_dir, + 'ocr': True, + 'watermark_text': '테스트워터마크', + } + processor = ImageProcessor(set_log, None, toggle_states, gpt_client, base_dir) + processor.update_unwanted_texts(unwanted_texts) + + print("1. 순차처리 테스트") + await sequential_process(image_paths, processor, output_dir) + + print("2. 동시처리 테스트") + await parallel_process(image_paths, processor, output_dir) + +if __name__ == '__main__': + asyncio.run(main()) diff --git a/modules/test/old_test/test_loggerModule.py b/modules/test/old_test/test_loggerModule.py new file mode 100644 index 0000000..03be67c --- /dev/null +++ b/modules/test/old_test/test_loggerModule.py @@ -0,0 +1,199 @@ +import logging +from logging.handlers import RotatingFileHandler, BaseRotatingHandler +import os +import glob +from PySide6.QtCore import QObject, Signal +import traceback +import inspect + + +class Logger1(QObject): + log_signal = Signal(str) # GUI로 로그 메시지를 전달할 시그널 + + def __init__(self, gui_logger=None, log_file="app.log", logger_name="MainLogger", + file_log_level=logging.DEBUG, gui_log_level=logging.INFO): + """ + Logger 초기화 + :param gui_logger: GUI에 로그를 출력할 콜백 함수 + :param log_file: 로그 파일 이름 + :param logger_name: 로거 이름 + :param file_log_level: 파일 로거의 로그 레벨 + :param gui_log_level: GUI 로거의 로그 레벨 + """ + super().__init__() + self.gui_logger = gui_logger + self.file_log_level = file_log_level + self.gui_log_level = gui_log_level + + # 로그 설정 + self.logger = logging.getLogger(logger_name) + self.logger.setLevel(file_log_level) # 파일 로거 레벨 설정 + + # 포맷 설정 + self.simple_format = "[%(asctime)s] [%(levelname)s] %(message)s" + self.detailed_format = ( + "[%(asctime)s] [%(threadName)s] [%(levelname)s] " + "[%(filename)s:%(funcName)s:%(lineno)d] %(message)s" + ) + + # 핸들러 추가 + self._add_console_handler(file_log_level) + self._add_file_handler(log_file, file_log_level) + + # GUI Logger 연결 + if self.gui_logger: + self.log_signal.connect(self.gui_logger) + + def _add_console_handler(self, level): + """콘솔 핸들러 추가""" + console_handler = logging.StreamHandler() + console_handler.setLevel(level) + formatter = logging.Formatter( + self.detailed_format if level <= logging.DEBUG else self.simple_format + ) + console_handler.setFormatter(formatter) + self.logger.addHandler(console_handler) + + def _add_file_handler(self, log_file, level): + """파일 핸들러 추가""" + # 확장자가 .log가 아니면 .log로 변경 + if not log_file.endswith('.log'): + base_name, _ = os.path.splitext(log_file) + log_file = base_name + '.log' + + # 커스텀 로테이팅 핸들러 사용 + file_handler = CustomRotatingFileHandler( + log_file, maxBytes=10 * 1024 * 1024, backupCount=5, encoding="utf-8" + ) + file_handler.setLevel(level) + formatter = logging.Formatter( + self.detailed_format if level <= logging.DEBUG else self.simple_format + ) + file_handler.setFormatter(formatter) + self.logger.addHandler(file_handler) + + def log(self, message, level=logging.INFO, exc_info=False): + """로그 메시지 기록""" + if exc_info: + message = f"{message}\n{traceback.format_exc()}" + + # 호출 위치 정보를 동적으로 추출 + caller_frame = logging.currentframe().f_back + record = self.logger.makeRecord( + self.logger.name, level, caller_frame.f_code.co_filename, + caller_frame.f_lineno, message, None, None, caller_frame.f_code.co_name + ) + + # 파일 로거에 메시지 전달 + if level >= self.file_log_level: + self.logger.handle(record) + + # GUI 로그로 전달 (포맷 적용) + if self.gui_logger and level >= self.gui_log_level: + formatter = logging.Formatter( + self.detailed_format if self.logger.level <= logging.DEBUG else self.simple_format + ) + formatted_message = formatter.format(record) + colored_message = self.format_gui_message(formatted_message, level) + self.log_signal.emit(colored_message) + + def format_gui_message(self, message, level): + """GUI 로그 메시지의 HTML 색상 지정""" + color_map = { + logging.DEBUG: "gray", + logging.INFO: "black", + logging.WARNING: "orange", + logging.ERROR: "red", + logging.CRITICAL: "purple", + } + color = color_map.get(level, "black") + return f'{message}' + + def set_gui_logger(self, gui_logger, gui_log_level=None): + """ + GUI 로그 콜백 함수를 설정하고, log_signal에 연결합니다. + 선택적으로 GUI 로그 레벨도 변경할 수 있습니다. + """ + self.gui_logger = gui_logger + self.log_signal.connect(gui_logger) + + if gui_log_level is not None: + self.gui_log_level = gui_log_level + + +class CustomRotatingFileHandler(BaseRotatingHandler): + """로그 파일을 모두 .log 확장자로 생성하는 커스텀 핸들러""" + + def __init__(self, filename, mode='a', maxBytes=0, backupCount=0, encoding=None): + super().__init__(filename, mode, encoding) + self.maxBytes = maxBytes + self.backupCount = backupCount + # 기존 로그 파일 확인 및 인덱스 설정 + self._base_filename = filename + self._extension = '.log' + + def doRollover(self): + """롤오버 수행 - 파일이 최대 크기에 도달하면 새 파일 생성""" + if self.stream: + self.stream.close() + self.stream = None + + # 기존 로그 파일 이름을 기반으로 백업 파일 생성 + base_name, ext = os.path.splitext(self._base_filename) + + # 현재 디렉토리의 모든 로그 파일 확인 + log_dir = os.path.dirname(self._base_filename) or '.' + existing_logs = glob.glob(f"{base_name}*.log") + existing_logs.sort() + + # 최대 백업 수 초과하는 파일 제거 + while len(existing_logs) >= self.backupCount: + try: + oldest_file = existing_logs.pop(0) + os.remove(oldest_file) + except Exception: + pass + + # 새 로그 파일 이름 생성 (주 로그 파일 이름은 그대로 유지) + # 예: log.log, log_1.log, log_2.log, ... + if os.path.exists(self._base_filename): + # 인덱스가 있는 로그 파일들 찾기 + indexed_logs = [f for f in existing_logs if f != self._base_filename] + max_index = 0 + + for log_file in indexed_logs: + try: + # 파일 이름에서 인덱스 부분 추출 + name_part = os.path.basename(log_file) + name_without_ext = os.path.splitext(name_part)[0] + if '_' in name_without_ext: + idx_str = name_without_ext.split('_')[-1] + if idx_str.isdigit(): + max_index = max(max_index, int(idx_str)) + except Exception: + pass + + # 새 인덱스로 파일 이름 설정 + new_index = max_index + 1 + new_log_file = f"{base_name}_{new_index}.log" + + # 기존 파일 이름 변경 + try: + os.rename(self._base_filename, new_log_file) + except Exception: + pass + + # 스트림 다시 열기 + self.mode = 'w' + self.stream = self._open() + + def shouldRollover(self, record): + """롤오버가 필요한지 확인""" + if self.stream is None: # 첫 번째 로그 쓰기 시도 + self.stream = self._open() + + if self.maxBytes > 0: # 최대 크기가 지정된 경우만 검사 + self.stream.seek(0, 2) # 파일 끝으로 이동 + if self.stream.tell() >= self.maxBytes: + return True + return False diff --git a/modules/test/old_test/test_migan_module.py b/modules/test/old_test/test_migan_module.py new file mode 100644 index 0000000..60cd264 --- /dev/null +++ b/modules/test/old_test/test_migan_module.py @@ -0,0 +1,63 @@ +# -*- coding: utf-8 -*- +""" +tests/test_migan_module.py + +- 로컬 이미지와 마스크로 MIGAN 파이프라인을 단독 테스트 +- 너의 MaskModule 없이도 간단 폴리곤으로 마스크 생성 가능 +""" + +import os +import cv2 +import numpy as np +import logging +from src.modules.migan_module import MIGANPipelineONNXCompat + +def get_logger(): + lg = logging.getLogger("MIGAN_TEST") + if not lg.handlers: + lg.setLevel(logging.DEBUG) + h = logging.StreamHandler() + h.setFormatter(logging.Formatter("[%(asctime)s][%(levelname)s] %(message)s")) + lg.addHandler(h) + # 네 프로젝트와 호환(.log 인터페이스) + class _Adapter: + def __init__(self, _lg): self._lg = _lg + def log(self, msg, level=logging.INFO, **kwargs): self._lg.log(level, msg) + return _Adapter(lg) + +def make_demo_mask_like_yours(img_path: str): + """네 MaskModule 스타일(텍스트영역=255) 흉내내기: 사각형 2개를 255로 채움 + 블러""" + img = cv2.imread(img_path, cv2.IMREAD_COLOR) + h,w = img.shape[:2] + mask = np.zeros((h,w), np.uint8) + cv2.rectangle(mask, (int(0.1*w), int(0.2*h)), (int(0.4*w), int(0.3*h)), 255, -1) + cv2.rectangle(mask, (int(0.6*w), int(0.55*h)), (int(0.9*w), int(0.65*h)), 255, -1) + mask = cv2.GaussianBlur(mask, (15,15), 0) + return mask + +def main(): + logger = get_logger() + + # 경로 설정 + onnx_path = os.environ.get("MIGAN_ONNX", "migan_pipeline_v2.onnx") + img_path = os.environ.get("MIGAN_IMG", "examples/input.png") + out_path = os.environ.get("MIGAN_OUT", "examples/output_migan.png") + + if not os.path.exists(onnx_path): + logger.log(f"ONNX 파일 없음: {onnx_path}", level=logging.ERROR); return + if not os.path.exists(img_path): + logger.log(f"입력 이미지 없음: {img_path}", level=logging.ERROR); return + + migan = MIGANPipelineONNXCompat(onnx_path, logger=logger, use_cuda=True) + mask = make_demo_mask_like_yours(img_path) + + out = migan.inpaint(img_path, mask) + if out is None: + logger.log("인페인팅 실패", level=logging.ERROR); return + + os.makedirs(os.path.dirname(out_path) or ".", exist_ok=True) + cv2.imwrite(out_path, out) + logger.log(f"저장 완료: {out_path}") + +if __name__ == "__main__": + main() diff --git a/modules/test/old_test/test_paddleocr_translate.py b/modules/test/old_test/test_paddleocr_translate.py new file mode 100644 index 0000000..670910b --- /dev/null +++ b/modules/test/old_test/test_paddleocr_translate.py @@ -0,0 +1,59 @@ +import os +import asyncio +from image_processor3 import ImageProcessor3 +from test_papago_translator import PapagoTranslator +from test_loggerModule import Logger1 + +class SimpleLogger: + def log(self, msg, level=None, exc_info=None): + print(msg) + +async def main(): + logger = SimpleLogger() + base_dir = os.path.dirname(os.path.abspath(__file__)) + file_num = 6 + img_path = os.path.join(base_dir, 'img', f"{file_num}.jpg") + + # ImageProcessor3 인스턴스 생성 + toggle_states = { + 'TEMP_IMAGE_DIR': os.path.join(base_dir, 'img'), + #'image_font_path': os.path.join(base_dir, 'fonts', 'HakgyoansimDunggeunmisoTTFB.ttf'), + #'image_font_path': os.path.join(base_dir, 'fonts', 'NanumBarunGothic.ttf'), + #'image_font_path': os.path.join(base_dir, 'fonts', 'NanumSquareRoundR.ttf'), + #'image_font_path': os.path.join(base_dir, 'fonts', 'gamtanload.ttf'), + 'image_font_path': os.path.join(base_dir, 'fonts', 'Cafe24Ohsquare-v2.0.ttf'), + + 'ocr': True, + 'membership_level': 'vip', + 'request_inpainting_server_url': 'http://192.168.0.150:35756' + } + unwanted_words = { + "고품질": "저품질", + "세탁기": "세세탁기" + } + + papago_translator = PapagoTranslator(logger) + logger = Logger1() + image_processor = ImageProcessor3( + logger=logger, + page=None, + toggle_states=toggle_states, + unwanted_words=unwanted_words, + base_dir=base_dir, + papago_translator=papago_translator, + authenticated_by_admin=True, + ) + + image_processor.update_unwanted_texts(unwanted_words) + + # process_single_image 호출 + result = await image_processor.process_single_image( + page=None, original_image_url=img_path, delay=0, index=file_num-1, file_prefix="" + ) + + #result = await image_processor.remove_background(page=None, original_image_url=img_path, index=file_num, file_prefix="") + + print(f"처리 결과: {result}") + +if __name__ == '__main__': + asyncio.run(main()) \ No newline at end of file diff --git a/modules/test/old_test/test_papago_translator.py b/modules/test/old_test/test_papago_translator.py new file mode 100644 index 0000000..513169c --- /dev/null +++ b/modules/test/old_test/test_papago_translator.py @@ -0,0 +1,293 @@ +import asyncio +from typing import Optional, Dict, Any, List +import logging +from translatepy.translators.google import GoogleTranslate + +class PapagoTranslator: + """파파고/구글 번역 통합 클래스""" + + def __init__(self, logger, browser_control = None, page= None, timeout: int = 60000): + """ + 초기화 + + Args: + page: Playwright page 객체 + timeout: 타임아웃 시간 (밀리초, 기본값: 60초) + """ + self.page = page + self.browser_control = browser_control + self.timeout = timeout + self.logger = logger + self.initialized = False + self.gtranslate = GoogleTranslate() + + self.logger.log(f"파파고 번역기 초기화 완료", level=logging.INFO) + + def update_page(self, page): + self.page = page + self.logger.log(f"파파고 페이지 갱신: {self.page}", level=logging.INFO) + + async def initialize(self): + """파파고 사이트 초기화""" + try: + # # 페이지 타임아웃 설정 + # self.page.set_default_timeout(self.timeout) + + # # User-Agent 설정 + # await self.page.set_extra_http_headers({ + # 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36' + # }) + + # 파파고 사이트 접속 (재시도 로직) + max_retries = 3 + for attempt in range(max_retries): + try: + self.logger.log(f"파파고 사이트 접속 시도 {attempt + 1}/{max_retries}", level=logging.INFO) + await self.page.goto("https://papago.naver.com/", wait_until="domcontentloaded") + + # 페이지 로딩 대기 + await self.page.wait_for_selector("#txtSource", timeout=30000) + + self.logger.log("파파고 번역기 초기화 완료", level=logging.INFO) + self.initialized = True + return + + except Exception as e: + self.logger.log(f"접속 시도 {attempt + 1} 실패: {e}", level=logging.WARNING) + if attempt == max_retries - 1: + raise + await asyncio.sleep(2) + + except Exception as e: + self.logger.log(f"초기화 실패: {e}", level=logging.ERROR, exc_info=True) + self.browser_control.save_error_screenshot("papago_translator_init") + raise + + async def papago_translate(self, text: str, source_lang: str = "zh", target_lang: str = "ko") -> Optional[List[str]]: + """파파고로 번역, 여러 줄 반환. 실패시 None.""" + if not self.page: + raise RuntimeError("Page 객체가 없습니다.") + + try: + if not self.initialized: + self.logger.log("파파고 번역기 초기화", level=logging.INFO) + await self.initialize() + + await self.page.fill("#txtSource", "") + await asyncio.sleep(0.3) + await self.page.fill("#txtSource", text) + await self.page.wait_for_selector("#txtTarget span", state="visible", timeout=15000) + + elements = await self.page.query_selector_all("#txtTarget span") + translated_lines = [] + for elem in elements: + txt = await elem.text_content() + if txt: + translated_lines.append(txt.strip()) + + if translated_lines: + self.logger.log(f"Papago 번역 성공 ({len(translated_lines)}개)", level=logging.INFO) + return translated_lines + else: + self.logger.log("Papago 번역 결과 없음", level=logging.WARNING) + return None + except Exception as e: + self.logger.log(f"Papago 번역 실패: {e}", level=logging.ERROR, exc_info=True) + self.browser_control.save_error_screenshot("papago_translate_error") + return None + + def google_translate_lines(self, lines: List[str], source_lang: str = "zh", target_lang: str = "ko") -> List[str]: + """여러 줄을 한 번에 번역 후, 줄바꿈 기준으로 분리""" + try: + # 1. 줄바꿈으로 합치기 + src_text = "\n".join(lines) + result = self.gtranslate.translate(src_text, target_lang) + translated_text = str(result.result) + # 2. 다시 줄바꿈 기준으로 split + translated_lines = translated_text.split('\n') + # 3. 개수 안 맞을 경우 예외 처리(원본 반환 등) + if len(translated_lines) != len(lines): + self.logger.log( + f"구글 번역 줄 개수 불일치: {len(translated_lines)} != {len(lines)}. 원본 반환.", + level=logging.WARNING + ) + return lines # 개수 안 맞으면 원본 반환(혹은 더 안전한 조치) + return translated_lines + except Exception as e: + self.logger.log(f"Google 번역 실패: {e}", level=logging.ERROR, exc_info=True) + self.browser_control.save_error_screenshot("google_translate_error") + return lines # 전체 실패 시 원본 반환 + + async def translate( + self, + text: str, + source_lang: str = "zh", + target_lang: str = "ko", + engine: str = "papago", # <--- 번역 엔진 선택 옵션 추가! + fallback: bool = True # <--- 실패시 자동 폴백 옵션(기본 True) + ) -> List[str]: + """ + 번역 엔진 선택 지원: engine="papago"|"google" + """ + lines = text.split("\n") if "\n" in text else [text] + + if not self.page or engine == "google": + self.logger.log("구글 번역 시작", level=logging.INFO) + result = self.google_translate_lines(lines, source_lang, target_lang) + self.logger.log(f"구글 번역 완료: {result}", level=logging.INFO) + return result + + # engine == "papago" (기본) + self.logger.log("파파고 번역 시작", level=logging.INFO) + papago_result = await self.papago_translate(text, source_lang, target_lang) + if papago_result and len(papago_result) == len(lines): + self.logger.log(f"파파고 번역 완료: {papago_result}", level=logging.INFO) + return papago_result + + if fallback: + self.logger.log("Papago 번역 실패 → Google 번역으로 폴백", level=logging.WARNING) + google_result = self.google_translate_lines(lines, source_lang, target_lang) + self.logger.log(f"구글 번역 완료: {google_result}", level=logging.INFO) + return google_result + else: + self.logger.log(f"번역 실패 → 원본 그대로 반환 : {lines}", level=logging.WARNING) + return lines # 번역 실패 시 원본 그대로 + + # 아래는 기존 단일 제품명 구글 번역 메서드, 내부에서 사용해도 됨 + def translate_product_name_from_google(self, original_name: str) -> str: + try: + translated_name = self.gtranslate.translate(original_name, 'ko') + return str(translated_name.result) + except Exception as e: + self.logger.log(f"구글 번역 중 오류 발생: {e}", level=logging.ERROR, exc_info=True) + return original_name + + +# # Playwright 통합 클래스 (기존 호환성 유지) +# class PapagoTranslatorWithPlaywright: +# """Playwright를 내장한 파파고 번역기 클래스 (기존 호환성용)""" + +# def __init__(self, logger, headless: bool = True, timeout: int = 60000): +# """ +# 초기화 + +# Args: +# headless: 헤드리스 모드 여부 (기본값: True) +# timeout: 타임아웃 시간 (밀리초, 기본값: 60초) +# """ +# self.headless = headless +# self.timeout = timeout +# self.browser = None +# self.page = None +# self.translator = None +# self.logger = logger + +# async def __aenter__(self): +# """비동기 컨텍스트 매니저 진입""" +# await self.initialize() +# return self + +# async def __aexit__(self, exc_type, exc_val, exc_tb): +# """비동기 컨텍스트 매니저 종료""" +# await self.close() + +# async def initialize(self): +# """브라우저 초기화""" +# try: +# from playwright.async_api import async_playwright + +# self.playwright = await async_playwright().start() +# self.browser = await self.playwright.chromium.launch( +# headless=self.headless, +# args=['--no-sandbox', '--disable-setuid-sandbox'] +# ) +# self.page = await self.browser.new_page() + +# # PapagoTranslator 인스턴스 생성 +# self.translator = PapagoTranslator(self.logger, self.page, self.timeout) +# await self.translator.initialize() + +# except Exception as e: +# self.logger.error(f"초기화 실패: {e}") +# await self.close() +# raise + +# async def close(self): +# """브라우저 종료""" +# try: +# if self.browser: +# await self.browser.close() +# if hasattr(self, 'playwright'): +# await self.playwright.stop() +# self.logger.info("브라우저 종료 완료") +# except Exception as e: +# self.logger.error(f"브라우저 종료 중 오류: {e}") + +# async def translate(self, text: str, engine: str = "papago", source_lang: str = "zh", target_lang: str = "ko") -> Optional[str]: +# if not self.translator: +# raise RuntimeError("번역기가 초기화되지 않았습니다.") +# return await self.translator.translate( +# text, +# source_lang=source_lang, +# target_lang=target_lang, +# engine=engine +# ) + +# import asyncio +# import logging +# import time +# def custom_log(self, msg, level=logging.INFO, exc_info=False): +# if level == logging.DEBUG: +# self.debug(msg, exc_info=exc_info) +# elif level == logging.INFO: +# self.info(msg, exc_info=exc_info) +# elif level == logging.WARNING: +# self.warning(msg, exc_info=exc_info) +# elif level == logging.ERROR: +# self.error(msg, exc_info=exc_info) +# elif level == logging.CRITICAL: +# self.critical(msg, exc_info=exc_info) +# else: +# self.log(level, msg, exc_info=exc_info) + +# logging.Logger.log = custom_log + +# logger = logging.getLogger(__name__) + +# text = { +# '全透明': '全透明', '全马卡龙色(浅色)': '全马卡龙色(浅色)', '半透明半马卡龙色': '半透明半马卡龙色', +# '全彩色(深色)': '全彩色(深色)', '半透明半彩色(深色)': '半透明半彩色(深色)', +# '金银铜橙': '金银铜橙', '白色': '白色', '黑색': '黑色', +# '28mm(可选颜色)': '28mm(可选颜色)', '32mm(可选颜色)': '32mm(可选颜色)', +# '45mm(可选颜色)': '45mm(可选颜色)', '50mm(可选颜色)': '50mm(可选颜色)', +# '60mm(可选颜色)': '60mm(可选颜色)', '65mm(可选颜色)': '65mm(可选颜色)', +# '70mm(可选颜色)': '70mm(可选颜色)', '75mm(可选颜色)': '75mm(可选颜色)', +# '80mm(可选颜色)': '80mm(可选颜色)', '92mm(可选颜色)': '92mm(可选颜色)', +# '100mm(可选颜色)': '100mm(可选颜色)', '110mm(可选颜色)': '110mm(可选颜色)', +# '120mm(可选颜色)': '120mm(可选颜色)', '160mm(可选颜色)': '160mm(可选颜色)', +# '200mm(可选颜色)': '200mm(可选颜色)', '50个': '50个', '100个': '100个', +# '200个': '200个', '500个': '500个', '1000个': '1000个' +# } + +# async def translate_values_one_time(text: dict, papago_translator) -> dict: +# values = list(text.values()) +# src_text = "\n".join(values) + +# # 번역 1회만 호출, 이제 결과가 리스트로 나옴 +# translated_lines = await papago_translator.translate(src_text, engine='papago', source_lang="zh", target_lang="ko") + +# print("=== 번역 결과 ===") +# print(translated_lines) +# print(f"=== 개수: {len(translated_lines)} / 원본 개수: {len(values)} ===") + +# # key와 다시 매핑 +# translated_dict = dict(zip(text.keys(), translated_lines)) +# return translated_dict + +# async def main(): +# async with PapagoTranslatorWithPlaywright(logger, headless=True) as translator: +# translated_dict = await translate_values_one_time(text, translator) +# print(translated_dict) + +# if __name__ == "__main__": +# asyncio.run(main()) diff --git a/modules/test/old_test/testrem.py b/modules/test/old_test/testrem.py new file mode 100644 index 0000000..ffb9377 --- /dev/null +++ b/modules/test/old_test/testrem.py @@ -0,0 +1,98 @@ +import cv2, numpy as np, base64, requests +import time, os + +img_path = 'img/6.jpg' +img = cv2.imread(img_path) + +if img is None: + raise FileNotFoundError(f'이미지를 찾을 수 없습니다: {img_path}') + +_, buf = cv2.imencode('.png', img) +img_b64 = base64.b64encode(buf).decode() + +url = "http://192.168.0.150:35756/api/v1/run_plugin_gen_image" +# url = "http://59.26.209.89:47396//api/v1/run_plugin_gen_image" +payload = { + "name": "RemoveBG", + "image": f"data:image/png;base64,{img_b64}", + "scale": 1 +} + +start = time.perf_counter() +resp = requests.post(url, json=payload) +elapsed = time.perf_counter() - start + +print("STATUS", resp.status_code) +print("ELAPSED_SEC", elapsed) + +if resp.status_code == 200: + # 결과 저장 + os.makedirs("output", exist_ok=True) + out_path = "output/6_removed.png" + with open(out_path, "wb") as f: + f.write(resp.content) + + # 수신된 바이너리 PNG를 numpy 배열로 변환 + nparr = np.frombuffer(resp.content, np.uint8) + result_img = cv2.imdecode(nparr, cv2.IMREAD_UNCHANGED) + + # 결과 이미지가 있을 때 대상 영역을 찾아 자르고 흰색으로 합성 + if result_img is not None and result_img.ndim == 3: + # 1) 초기 마스크 계산 (알파 있으면 알파 > 200, 없으면 밝기 < 230) + if result_img.shape[2] == 4: + mask_init = (result_img[:, :, 3] > 200).astype(np.uint8) + rgba_img = result_img + else: + gray = cv2.cvtColor(result_img[:, :, :3], cv2.COLOR_BGR2GRAY) + mask_init = (gray < 230).astype(np.uint8) + alpha_channel = (mask_init * 255).astype(np.uint8) + rgba_img = np.dstack([result_img, alpha_channel]) + + # 2) 모폴로지로 잡티 제거 (erode 1 → dilate 2) + kernel = np.ones((3, 3), np.uint8) + mask = cv2.erode(mask_init, kernel, iterations=1) + mask = cv2.dilate(mask, kernel, iterations=2) + + # 3) 가장 큰 연결 요소만 선택 + num, labels, stats, _ = cv2.connectedComponentsWithStats(mask, connectivity=8) + if num > 1: + largest = 1 + np.argmax(stats[1:, cv2.CC_STAT_AREA]) + mask = (labels == largest).astype(np.uint8) + + ys, xs = np.where(mask > 0) + if len(xs) > 0 and len(ys) > 0: + top, left = ys.min(), xs.min() + bottom, right = ys.max(), xs.max() + + crop_rgba = rgba_img[top:bottom + 1, left:right + 1] + + # 객체 크기에 비례한 테두리 마진 (10%) + ch, cw = crop_rgba.shape[:2] + margin = int(max(ch, cw) * 0.1) + + crop_rgba = cv2.copyMakeBorder( + crop_rgba, margin, margin, margin, margin, + borderType=cv2.BORDER_CONSTANT, + value=[255, 255, 255, 0] + ) + + # RGBA -> 흰 배경 BGR + bgr_crop = crop_rgba[:, :, :3].astype(np.float32) + alpha_crop = crop_rgba[:, :, 3:4].astype(np.float32) / 255.0 + white_bg = np.full_like(bgr_crop, 255.0) + result_img = (bgr_crop * alpha_crop + white_bg * (1 - alpha_crop)).astype(np.uint8) + else: + # mask 가 거의 없으면 객체 없음으로 판단 + white_bg = np.full_like(result_img[:, :, :3], 255) + result_img = white_bg.astype(np.uint8) + + # 흰배경 결과를 파일로 저장 + white_path = "output/6_removed_white.png" + cv2.imwrite(white_path, result_img) + + # 화면에 표시 + cv2.imshow('Removed BG (White BG)', result_img) + cv2.waitKey(0) + cv2.destroyAllWindows() +else: + print("ERROR", resp.text) \ No newline at end of file diff --git a/modules/test/outputs_test/r_1.jpg b/modules/test/outputs_test/r_1.jpg new file mode 100644 index 0000000..d810fab Binary files /dev/null and b/modules/test/outputs_test/r_1.jpg differ diff --git a/modules/test/outputs_test/r_nobg_thumb_1.png b/modules/test/outputs_test/r_nobg_thumb_1.png new file mode 100644 index 0000000..7bc01c6 Binary files /dev/null and b/modules/test/outputs_test/r_nobg_thumb_1.png differ diff --git a/modules/test/outputs_test/t_translated_detail_img_2.webp b/modules/test/outputs_test/t_translated_detail_img_2.webp new file mode 100644 index 0000000..5894d5a Binary files /dev/null and b/modules/test/outputs_test/t_translated_detail_img_2.webp differ diff --git a/modules/test/perf_test.py b/modules/test/perf_test.py new file mode 100644 index 0000000..a93726f --- /dev/null +++ b/modules/test/perf_test.py @@ -0,0 +1,190 @@ +import os +import time +import json +import argparse +import requests + + +def submit_job(api, img_path, prefix="detail", group="g1", seq=1, toggles=None): + payload = { + "file_path": img_path, + "index": seq - 1, + "file_prefix": prefix, + "group_id": group, + "seq": seq, + } + if toggles: + payload["toggle_overrides"] = toggles + r = requests.post(f"{api}/v1/process-image", json=payload, timeout=30) + r.raise_for_status() + return r.json()["job_id"] + + +def submit_rembg(api, img_path, prefix="thumb", toggles=None): + payload = { + "file_path": img_path, + "file_prefix": prefix, + } + if toggles: + payload["toggle_overrides"] = toggles + r = requests.post(f"{api}/v1/remove-background", json=payload, timeout=30) + r.raise_for_status() + return r.json()["job_id"] + + +def wait_job(api, job_id, timeout=300): + end = time.time() + timeout + while time.time() < end: + r = requests.get(f"{api}/v1/jobs/{job_id}", timeout=15) + if r.status_code == 200: + data = r.json() + if data.get("status") in ("done", "error", "cancelled"): + return data + time.sleep(0.2) + raise TimeoutError("job wait timeout") + + +def main(): + parser = argparse.ArgumentParser() + parser.add_argument("--api", default="http://127.0.0.1:8009", help="API base URL") + parser.add_argument("--samples", nargs="*", default=None, help="sample image paths") + parser.add_argument("--provider-ocr", default=None, help="force OCR provider: auto|dml|cpu") + parser.add_argument("--provider-migan", default=None, help="force MIGAN provider: auto|dml|cpu (via env/.env)") + parser.add_argument("--accel-migan", default=None, help="use accel for MIGAN: 1|0") + parser.add_argument("--mode", default="both", choices=["translate", "rembg", "both"], help="test mode") + parser.add_argument("--prefix", default="detail", help="file_prefix for translate (detail|option|thumb)") + parser.add_argument("--rembg-prefix", default="thumb", help="file_prefix for rembg (thumb|detail)") + parser.add_argument("--outdir", default=None, help="copy result files to this directory") + args = parser.parse_args() + + api = args.api.rstrip("/") + + # 준비: 헬스체크 + try: + hr = requests.get(f"{api}/health", timeout=10) + hr.raise_for_status() + h = hr.json() + print("health:", h, flush=True) + if not h.get("ready"): + print("Worker not ready. Start API server: python main.py", flush=True) + return + except Exception as e: + print(f"Cannot reach API at {api}. Start server: python main.py | error={e}", flush=True) + return + + # 런타임 프로바이더 선택(옵션) + if args.provider_ocr: + rr = requests.post(f"{api}/v1/ocr/reinit", json={"provider": args.provider_ocr}, timeout=30) + print("reinit ocr:", rr.status_code, rr.text) + if args.accel_migan is not None or args.provider_migan: + # MIGAN accel 플래그 우선 + use_accel = None + if args.accel_migan is not None: + use_accel = (str(args.accel_migan) not in ("0", "false", "False")) + mr = requests.post(f"{api}/v1/migan/reset", json={"use_cuda": use_accel}, timeout=30) + print("reset migan:", mr.status_code, mr.text) + + # 샘플 이미지 로드 + root = os.path.abspath(os.path.join(os.path.dirname(__file__), "..")) + default_samples = [ + os.path.join(root, "test", "1.jpg"), + os.path.join(root, "test", "2.jpg"), + os.path.join(root, "test", "5.jpg"), + ] + samples = args.samples or default_samples + missing = [s for s in samples if not os.path.isfile(s)] + samples = [s for s in samples if os.path.isfile(s)] + if missing: + print("Missing sample files:", missing, flush=True) + if not samples: + print("No sample images found. Use --samples with absolute paths.", flush=True) + return + + # 제출 및 성능 수집 + total_start = time.time() + translate_ids, rembg_ids = [], [] + + if args.mode in ("translate", "both"): + print("\nSubmitting translate jobs...") + for i, img in enumerate(samples, start=1): + jid = submit_job(api, img, prefix=args.prefix, group="bench-t", seq=i) + print(f" translate submitted: {img} -> {jid}") + translate_ids.append((img, jid)) + + if args.mode in ("rembg", "both"): + print("\nSubmitting rembg jobs...") + for i, img in enumerate(samples, start=1): + jid = submit_rembg(api, img, prefix=args.rembg_prefix) + print(f" rembg submitted: {img} -> {jid}") + rembg_ids.append((img, jid)) + + outdir = args.outdir + if outdir: + os.makedirs(outdir, exist_ok=True) + + # 결과 수집: translate + translate_results = [] + if translate_ids: + print("\nWaiting translate results...") + for img, jid in translate_ids: + res = wait_job(api, jid, timeout=900) + translate_results.append(res) + rr = res.get("result") or {} + path = rr.get("path") + print(f" translate done: {img} -> status={rr.get('status')} path={path}") + if outdir and path and os.path.isfile(path): + import shutil + base = os.path.basename(path) + shutil.copy2(path, os.path.join(outdir, f"t_{base}")) + + # 결과 수집: rembg + rembg_results = [] + if rembg_ids: + print("\nWaiting rembg results...") + for img, jid in rembg_ids: + res = wait_job(api, jid, timeout=900) + rembg_results.append(res) + rr = res.get("result") or {} + path = rr.get("path") + print(f" rembg done: {img} -> status={rr.get('status')} path={path}") + if outdir and path and os.path.isfile(path): + import shutil + base = os.path.basename(path) + shutil.copy2(path, os.path.join(outdir, f"r_{base}")) + + total_end = time.time() + total_s = total_end - total_start + total_jobs = len(translate_ids) + len(rembg_ids) + if total_jobs: + print(f"\nBatch finished: {total_jobs} jobs in {total_s:.2f}s (avg {total_s/total_jobs:.2f}s/job)") + + # 타이밍 통계(translate) + if translate_results: + def collect(key): + vals = [] + for r in translate_results: + rr = r.get("result") or {} + timings = (rr.get("timings") or {}) + if key in timings: + try: + vals.append(float(timings[key])) + except Exception: + pass + return vals + + keys = ["total_ms", "download", "ocr", "translate", "mask", "inpaint", "render", "save"] + print("\nTiming stats (translate, ms):") + for k in keys: + vals = collect(k) + if not vals: + continue + avg = sum(vals) / len(vals) + p50 = sorted(vals)[len(vals)//2] + p90 = sorted(vals)[int(len(vals)*0.9)-1] if len(vals) >= 10 else max(vals) + print(f" {k:10s} avg={avg:8.1f} p50={p50:8.1f} p90={p90:8.1f} n={len(vals)}") + + +if __name__ == "__main__": + main() + + diff --git a/modules/text_rendering_module.py b/modules/text_rendering_module.py new file mode 100644 index 0000000..b4a71f7 --- /dev/null +++ b/modules/text_rendering_module.py @@ -0,0 +1,358 @@ +# -*- coding: utf-8 -*- +""" +텍스트 렌더링 모듈 - 인페인팅된 이미지에 번역된 텍스트를 자연스럽게 렌더링 (라이브러리화) +""" + +import cv2 +import numpy as np +from PIL import Image, ImageDraw, ImageFont +from typing import List, Dict, Any, Tuple, Optional +import os +import math +import gc +import logging + +class TextRenderingModule: + def __init__(self, logger, font_path: Optional[str] = None): + self.logger = logger + self.font_path = font_path or self._setup_default_fonts() + self.default_font_size = 20 + self.font_cache = {} + self.logger.log("텍스트 렌더링 모듈 초기화 완료", level=logging.INFO) + self.logger.log(f"기본 폰트: {self.font_path}", level=logging.INFO) + + def _setup_default_fonts(self): + possible_fonts = [ + "/usr/share/fonts/truetype/nanum/NanumGothic.ttf", + "/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf", + "/System/Library/Fonts/AppleGothic.ttf", + "C:/Windows/Fonts/malgun.ttf", + "C:/Windows/Fonts/gulim.ttc" + ] + self.available_fonts = [f for f in possible_fonts if os.path.exists(f)] + + return self.available_fonts + + + def get_font(self, size: int, font_path: Optional[str] = None) -> ImageFont.FreeTypeFont: + font_path = font_path or self.font_path + cache_key = f"{font_path}_{size}" + if cache_key not in self.font_cache: + try: + if font_path and os.path.exists(font_path): + font = ImageFont.truetype(font_path, size) + else: + font = ImageFont.load_default() + self.font_cache[cache_key] = font + except Exception as e: + print(f"폰트 로드 오류: {e}") + font = ImageFont.load_default() + self.font_cache[cache_key] = font + return self.font_cache[cache_key] + + def estimate_text_size(self, text: str, font_size: int, font_path: Optional[str] = None) -> Tuple[int, int]: + font = self.get_font(font_size, font_path) + try: + bbox = font.getbbox(text) + width = bbox[2] - bbox[0] + height = bbox[3] - bbox[1] + except AttributeError: + width, height = font.getsize(text) + return width, height + + def calculate_optimal_font_size(self, text: str, target_width: int, target_height: int, min_size: int = 8, max_size: int = 100, font_path: Optional[str] = None) -> int: + best_size = min_size + for size in range(min_size, max_size + 1): + width, height = self.estimate_text_size(text, size, font_path) + if width <= target_width and height <= target_height: + best_size = size + else: + break + return best_size + + def _estimate_background_color(self, image: np.ndarray, x1: int, y1: int, x2: int, y2: int) -> Tuple[int, int, int]: + margin = 5 + y1_exp = max(0, y1 - margin) + y2_exp = min(image.shape[0], y2 + margin) + x1_exp = max(0, x1 - margin) + x2_exp = min(image.shape[1], x2 + margin) + region = image[y1_exp:y2_exp, x1_exp:x2_exp] + if region.size == 0: + # ROI가 비었을 때는 흰색 배경 가정 + return (255, 255, 255) + + mean_color = np.mean(region, axis=(0, 1)) + if np.isnan(mean_color).any(): + return (255, 255, 255) + + return (int(mean_color[2]), int(mean_color[1]), int(mean_color[0])) + + def _get_contrasting_color(self, bg_color: Tuple[int, int, int]) -> Tuple[int, int, int]: + brightness = (bg_color[0] * 0.299 + bg_color[1] * 0.587 + bg_color[2] * 0.114) + if brightness > 128: + return (0, 0, 0) + else: + return (255, 255, 255) + + def render_text(self, image: np.ndarray, ocr_results: List[Dict], translated_texts: List[str], font_path: Optional[str] = None) -> np.ndarray: + result_image = image.copy() + for i, (ocr_result, translated_text) in enumerate(zip(ocr_results, translated_texts)): + polygon = ocr_result['polygon'] + polygon_array = np.array(polygon) + x_coords = polygon_array[:, 0] + y_coords = polygon_array[:, 1] + x_min, x_max = int(np.min(x_coords)), int(np.max(x_coords)) + y_min, y_max = int(np.min(y_coords)), int(np.max(y_coords)) + width = x_max - x_min + height = y_max - y_min + optimal_font_size = self.calculate_optimal_font_size(translated_text, width, height, font_path=font_path) + text_width, text_height = self.estimate_text_size(translated_text, optimal_font_size, font_path) + center_x = (x_min + x_max) // 2 + center_y = (y_min + y_max) // 2 + text_x = center_x - text_width // 2 + text_y = center_y - text_height // 2 + angle = 0 + if len(polygon_array) >= 2: + dx = polygon_array[1][0] - polygon_array[0][0] + dy = polygon_array[1][1] - polygon_array[0][1] + angle = math.degrees(math.atan2(dy, dx)) + bg_color = self._estimate_background_color(image, x_min, y_min, x_max, y_max) + text_color = self._get_contrasting_color(bg_color) + result_image = self.render_text_on_image( + result_image, translated_text, (text_x, text_y), + font_size=optimal_font_size, + font_path=font_path, + text_color=text_color, + background_color=None, + angle=angle + ) + return result_image + + def render_text_on_image(self, image: np.ndarray, text: str, position: Tuple[int, int], font_size: Optional[int] = None, font_path: Optional[str] = None, text_color: Tuple[int, int, int] = (0, 0, 0), background_color: Optional[Tuple[int, int, int]] = None, angle: float = 0) -> np.ndarray: + if font_size is None: + font_size = self.default_font_size + pil_image = Image.fromarray(cv2.cvtColor(image, cv2.COLOR_BGR2RGB)) + draw = ImageDraw.Draw(pil_image) + font = self.get_font(font_size, font_path) + text_width, text_height = self.estimate_text_size(text, font_size, font_path) + if background_color is not None: + bg_x1 = position[0] - 2 + bg_y1 = position[1] - 2 + bg_x2 = position[0] + text_width + 2 + bg_y2 = position[1] + text_height + 2 + draw.rectangle([bg_x1, bg_y1, bg_x2, bg_y2], fill=background_color) + if angle != 0: + text_image = Image.new('RGBA', (text_width + 10, text_height + 10), (255, 255, 255, 0)) + text_draw = ImageDraw.Draw(text_image) + text_draw.text((5, 5), text, font=font, fill=text_color + (255,)) + rotated_text = text_image.rotate(angle, expand=True) + pil_image.paste(rotated_text, position, rotated_text) + else: + draw.text(position, text, font=font, fill=text_color) + result_image = cv2.cvtColor(np.array(pil_image), cv2.COLOR_RGB2BGR) + return result_image + + def create_text_styles(self) -> Dict[str, Dict[str, Any]]: + """다양한 텍스트 스타일 정의""" + styles = { + 'default': { + 'color': (0, 0, 0), + 'bg_color': None, + 'outline': True, + 'outline_color': (255, 255, 255), + 'outline_width': 1 + }, + 'bold': { + 'color': (0, 0, 0), + 'bg_color': (255, 255, 255), + 'outline': True, + 'outline_color': (128, 128, 128), + 'outline_width': 2 + }, + 'highlight': { + 'color': (255, 255, 255), + 'bg_color': (255, 0, 0), + 'outline': False, + 'outline_color': None, + 'outline_width': 0 + }, + 'subtle': { + 'color': (128, 128, 128), + 'bg_color': None, + 'outline': True, + 'outline_color': (255, 255, 255), + 'outline_width': 1 + } + } + + return styles + + def render_with_style(self, image: np.ndarray, ocr_results: List[Dict], + translated_texts: List[str], style_name: str = 'default') -> np.ndarray: + """스타일을 적용한 텍스트 렌더링""" + styles = self.create_text_styles() + + if style_name not in styles: + print(f"알 수 없는 스타일: {style_name}") + style_name = 'default' + + style = styles[style_name] + + # 기본 렌더링 후 스타일 적용 + result = self.render_text(image, ocr_results, translated_texts) + + # 추가 스타일 처리는 여기서 구현 + # (예: 그림자, 글로우 효과 등) + + return result + + def adjust_text_for_space(self, text: str, max_width: int, max_height: int, + font_size: int) -> Tuple[str, int]: + """ + 공간에 맞게 텍스트 조정 + + Args: + text (str): 원본 텍스트 + max_width (int): 최대 너비 + max_height (int): 최대 높이 + font_size (int): 폰트 크기 + + Returns: + Tuple[str, int]: 조정된 텍스트와 폰트 크기 + """ + # 텍스트가 너무 길면 줄바꿈 또는 생략 + if len(text) > 20: + # 긴 텍스트는 줄바꿈 + words = text.split(' ') + if len(words) > 1: + mid = len(words) // 2 + text = ' '.join(words[:mid]) + '\n' + ' '.join(words[mid:]) + else: + # 단어가 하나면 생략 + text = text[:15] + '...' + + # 폰트 크기 조정 + adjusted_font_size = font_size + while adjusted_font_size > 8: + # 실제로는 텍스트 크기를 측정해서 비교 + estimated_width = len(text) * adjusted_font_size * 0.6 + if estimated_width <= max_width: + break + adjusted_font_size -= 2 + + return text, adjusted_font_size + + def _create_style_comparison(self, images: List[np.ndarray], style_names: List[str]): + """스타일 비교 이미지 생성""" + if not images: + return + + # 이미지 크기 조정 + target_width = 200 + target_height = int(images[0].shape[0] * target_width / images[0].shape[1]) + + resized_images = [] + for img in images: + resized = cv2.resize(img, (target_width, target_height)) + resized_images.append(resized) + + # 비교 이미지 생성 + num_images = len(resized_images) + comparison_width = target_width * num_images + comparison_height = target_height + 30 + + comparison = np.ones((comparison_height, comparison_width, 3), dtype=np.uint8) * 255 + + # 원본 이미지 + comparison[30:30+target_height, 0:target_width] = resized_images[0] + cv2.putText(comparison, "Original", (10, 20), + cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 0, 0), 1) + + # 스타일 이미지들 + for i, (img, style_name) in enumerate(zip(resized_images[1:], style_names)): + x_offset = target_width * (i + 1) + comparison[30:30+target_height, x_offset:x_offset+target_width] = img + cv2.putText(comparison, style_name, (x_offset + 10, 20), + cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 0, 0), 1) + + cv2.imwrite("test_output/text_style_comparison.jpg", comparison) + self.logger.log("스타일 비교 이미지 저장 완료", level=logging.INFO) + + +class TextRenderingModuleOptimized(TextRenderingModule): + """TextRenderingModule 메모리 최적화 버전. + - 오버레이 버퍼를 (small/medium/large) 3종만 캐시하여 재사용 + - 필요한 경우(더 큰 텍스트 영역)에는 1회성 버퍼를 생성 후 즉시 폐기 + - 기존 public API(render_text, render_with_style 등)은 그대로 유지 + """ + + def __init__(self, logger, font_path: Optional[str] = None): + super().__init__(logger, font_path) + # 범용 재사용 오버레이 캐시 (key: size label -> ndarray) + self._fixed_buffers: Dict[str, np.ndarray] = { + 'small': None, # 256x256 + 'medium': None, # 512x512 + 'large': None, # 1024x1024 + } + + # ──────────────────────────────────────────────── + # 내부: 오버레이 버퍼 선택/재사용 + # ──────────────────────────────────────────────── + def _get_reusable_overlay(self, w: int, h: int) -> Tuple[np.ndarray, bool]: + """주어진 크기를 수용할 수 있는 재사용 버퍼와 재사용 여부 반환""" + # 버퍼 캐싱을 제거하고 항상 새 C-contiguous 배열을 반환 + return np.zeros((h, w, 4), dtype=np.uint8, order="C"), False + + # ──────────────────────────────────────────────── + # override render_text + # ──────────────────────────────────────────────── + def render_text( + self, + image: np.ndarray, + ocr_results: List[Dict[str, Any]], + translated_texts: List[str], + font_path: Optional[str] = None, + style_name: Optional[str] = None, + debug: bool = False, + ) -> np.ndarray: + # 가독성을 위해 기본 구현을 그대로 사용 + if style_name: + return super().render_with_style( + image, ocr_results, translated_texts, style_name=style_name + ) + return super().render_text( + image, ocr_results, translated_texts, font_path=font_path + ) + + # ──────────────────────────────────────────────── + # alpha blend util (BGR target, RGBA src) + # ──────────────────────────────────────────────── + def _alpha_blend(self, target_bgr: np.ndarray, src_rgba: np.ndarray, top_left: Tuple[int, int]): + x0, y0 = top_left + h, w = src_rgba.shape[:2] + # 클리핑 + if x0 >= target_bgr.shape[1] or y0 >= target_bgr.shape[0]: + return + x1 = min(x0 + w, target_bgr.shape[1]) + y1 = min(y0 + h, target_bgr.shape[0]) + w = x1 - x0 + h = y1 - y0 + if w <= 0 or h <= 0: + return + + roi = target_bgr[y0:y1, x0:x1] + src = src_rgba[:h, :w] + + # 보장: contiguous & float32 계산 → uint8 캐스팅 + if not src.flags['C_CONTIGUOUS']: + src = np.ascontiguousarray(src) + + alpha = (src[:, :, 3].astype(np.float32) / 255.0)[..., None] # (h,w,1) + if np.all(alpha == 0): + return + + src_bgr = src[:, :, :3][:, :, ::-1].astype(np.float32) # RGBA ➜ BGR + roi_bgr = roi.astype(np.float32) + + blended = roi_bgr * (1.0 - alpha) + src_bgr * alpha + roi[:, :, :] = blended.astype(np.uint8) \ No newline at end of file diff --git a/modules/tray_app.py b/modules/tray_app.py new file mode 100644 index 0000000..d7e6525 --- /dev/null +++ b/modules/tray_app.py @@ -0,0 +1,204 @@ +import os +import sys +import threading +import time +from typing import Optional +import json + +import pystray +from PIL import Image, ImageDraw +import requests + +from loggerModule import Logger + + +class TrayController: + def __init__(self): + logs_root = os.path.join(os.environ.get("PROGRAMDATA", r"C:\\ProgramData"), "ImgWorker") + os.makedirs(logs_root, exist_ok=True) + self.logger = Logger(log_file=os.path.join(logs_root, "tray.log"), logger_name="img_worker_tray") + self.api = _TrayAPI(self.logger) + self.icon: Optional[pystray.Icon] = None + self._stop_event = threading.Event() + self._last_ready: bool = False + + def _create_image(self, color=(0, 120, 215)): + img = Image.new('RGB', (64, 64), color) + draw = ImageDraw.Draw(img) + draw.rectangle([8, 8, 56, 56], outline=(255, 255, 255), width=3) + draw.ellipse([22, 22, 42, 42], fill=(255, 255, 255)) + return img + + def _set_icon_color(self, state: str): + # state: 'ready' -> green, 'stopped' -> red, 'active' -> blink green/yellow + green = (0, 160, 0) + red = (180, 0, 0) + yellow = (200, 160, 0) + color = red + if state == 'ready': + color = green + elif state == 'active': + # 깜박임은 폴링 스레드에서 교차로 변경 + color = green if int(time.time()) % 2 == 0 else yellow + if self.icon: + self.icon.icon = self._create_image(color) + + def _update_title(self): + try: + st = self.api.worker_status() + ready = bool(st.get("ready", False)) + active = bool(st.get("active", False)) + avg_sec = st.get("avg_sec_per_image") + pid = st.get("pid") + self._last_ready = ready + status_label = 'ACTIVE' if active else ('READY' if ready else 'STOP') + perf = f" - avg. {avg_sec:.1f}(sec/장)" if isinstance(avg_sec, (int, float)) else '' + title = f"ImgWorker [{status_label}]{perf}" + if self.icon: + self.icon.title = title + # 메뉴를 상태에 맞게 재구성 + self.icon.menu = self._build_menu() + try: + self.icon.update_menu() + except Exception: + pass + self._set_icon_color('active' if active else ('ready' if ready else 'stopped')) + except Exception: + if self.icon: + self.icon.title = "ImgWorker (offline)" + + def _build_menu(self) -> pystray.Menu: + status_text = f"워커 상태: {'실행 중' if self._last_ready else '중지'}" + # 상태 라벨(비활성), 시작/중지 버튼은 상태에 따라 enable + return pystray.Menu( + pystray.MenuItem(status_text, lambda: None, enabled=False), + pystray.MenuItem("워커 시작", self._action_start, enabled=(not self._last_ready)), + pystray.MenuItem("워커 중지", self._action_stop, enabled=self._last_ready), + pystray.MenuItem("로그 열기", self._action_open_log), + pystray.Menu.SEPARATOR, + pystray.MenuItem("상태 새로고침", self._action_info), + pystray.MenuItem("서버 종료", self._action_exit), + ) + + def _action_start(self, icon, item): + self.logger.info("Tray: start worker") + self.api.worker_start() + time.sleep(0.3) + self._update_title() + + def _action_stop(self, icon, item): + self.logger.info("Tray: stop worker") + self.api.worker_stop() + time.sleep(0.3) + self._update_title() + + def _action_info(self, icon, item): + self.logger.info("Tray: info refresh") + self._update_title() + + def _action_exit(self, icon, item): + self.logger.info("Tray: exit clicked") + try: + self.api.shutdown_server() + except Exception: + pass + self._stop_event.set() + if self.icon: + self.icon.stop() + + def _action_open_log(self, icon, item): + try: + logs_root = os.path.join(os.environ.get("PROGRAMDATA", r"C:\\ProgramData"), "ImgWorker") + log_path = os.path.join(logs_root, "logs", "api_server.log") + # 기본 편집기로 열기 + if os.name == "nt": + os.startfile(log_path) # type: ignore[attr-defined] + else: + import subprocess + subprocess.Popen(["xdg-open", log_path]) + except Exception as e: + self.logger.error(f"로그 열기 실패: {e}") + + def run(self): + self.icon = pystray.Icon("imgworker_tray", self._create_image(), "ImgWorker", self._build_menu()) + + # 상태 폴링 쓰레드로 제목/메뉴 갱신 + def _poll(): + while not self._stop_event.is_set(): + try: + self._update_title() + except Exception: + pass + time.sleep(2.0) + + t = threading.Thread(target=_poll, daemon=True) + t.start() + # 최초 상태 반영 + self._update_title() + self.icon.run() + + +def main(): + TrayController().run() + + +if __name__ == "__main__": + main() + + +def _read_server_info() -> Optional[dict]: + try: + program_data = os.environ.get("PROGRAMDATA", r"C:\\ProgramData") + info_path = os.path.join(program_data, "ImgWorker", "server.json") + if os.path.isfile(info_path): + with open(info_path, "r", encoding="utf-8") as f: + return json.load(f) + except Exception: + return None + return None + + +class _TrayAPI: + def __init__(self, logger: Logger): + self.logger = logger + base = os.environ.get("IMGWK_API_BASE", "") + if not base: + info = _read_server_info() or {} + base = info.get("base") or f"http://{info.get('host','127.0.0.1')}:{info.get('port',8009)}" + if not base: + base = "http://127.0.0.1:8009" + self.base = base.rstrip("/") + + def worker_status(self) -> dict: + try: + r = requests.get(f"{self.base}/v1/worker/status", timeout=5) + r.raise_for_status() + return r.json() + except Exception as e: + return {"ready": False, "error": str(e)} + + def worker_start(self) -> dict: + try: + r = requests.post(f"{self.base}/v1/worker/start", timeout=10) + r.raise_for_status() + return r.json() + except Exception as e: + return {"ok": False, "error": str(e)} + + def worker_stop(self) -> dict: + try: + r = requests.post(f"{self.base}/v1/worker/stop", timeout=10) + r.raise_for_status() + return r.json() + except Exception as e: + return {"ok": False, "error": str(e)} + + def shutdown_server(self) -> dict: + try: + r = requests.post(f"{self.base}/v1/server/shutdown", timeout=5) + r.raise_for_status() + return r.json() + except Exception as e: + return {"ok": False, "error": str(e)} + + diff --git a/modules/user_data/ocr_provider.json b/modules/user_data/ocr_provider.json new file mode 100644 index 0000000..e6ac07c --- /dev/null +++ b/modules/user_data/ocr_provider.json @@ -0,0 +1 @@ +{"last_success_provider": "cpu"} \ No newline at end of file diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..bcb59d5 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,18 @@ +fastapi +uvicorn +pydantic +requests +pillow +pystray +onnx +onnxruntime-directml +psutil +python-dotenv +pyclipper +scikit-image +scipy +albumentations +shapely +lmdb +opencv-python +cx_freeze \ No newline at end of file diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..a526606 --- /dev/null +++ b/setup.py @@ -0,0 +1,207 @@ +from cx_Freeze import setup, Executable +from cx_Freeze.command.build_exe import build_exe as _build_exe +import os +import sys + +# 프로젝트 루트 +ROOT_DIR = os.path.abspath(os.path.dirname(__file__)) + +# 앱 메타 +APP_NAME = "ImgWorker" +APP_VERSION = "1.0.0" + +# 기본 포함 패키지 목록(동적 로딩되는 많은 모듈을 고려하여 넉넉히 포함) +INCLUDES = [ + # 표준 라이브러리에서 동적 사용하는 것들 + "asyncio", + "multiprocessing", + "logging", + "json", + "queue", + "ctypes", + "zipfile", + "urllib", + "socket", + "signal", + # 서드파티 + "fastapi", + "uvicorn", + "pydantic", + "dotenv", + "psutil", + "requests", + "numpy", + "PIL", + "cv2", + "onnx", + "onnxruntime", + # fastapi 런타임 의존 + "h11", + "anyio", + "starlette", + # 기타 사용 중인 것들 + "albumentations", + "imageio", + "scipy", + "networkx", + "pystray", + "PIL", # pystray 아이콘용 + "modules.tray_app", # 동적 import 대응을 위해 강제 포함 + 'pyclipper', + 'shapely', + 'skimage', # package import name (not scikit-image) + 'lmdb', +] + +EXCLUDES = [ + # 사용하지 않는 대형 모듈 제외 예시(필요 시 조정) + "tkinter", +] + +# 데이터 파일 매핑 +DATA_FILES = [] + +# ProgramData 기준 런타임 파일은 실행 시 생성되지만, 모델/폰트/onnx 등 리소스 포함 +MODULES_DIR = os.path.join(ROOT_DIR, "modules") + +# 모델/리소스 디렉토리들(필수 데이터만 포함; 테스트/샘플 이미지/소스 코드는 제외) +# RESOURCE_DIRS = [ +# os.path.join(MODULES_DIR, "fonts"), +# os.path.join(MODULES_DIR, "rembg_models"), +# os.path.join(MODULES_DIR, "migan_onnx"), +# # os.path.join(MODULES_DIR, "PP_Models"), +# os.path.join(MODULES_DIR, "user_data"), +# os.path.join(MODULES_DIR, "scripts"), +# ] + +# for d in RESOURCE_DIRS: +# if os.path.isdir(d): +# # cx_Freeze의 include_files에 (src, dst상대경로)로 추가 +# rel = os.path.relpath(d, ROOT_DIR) +# DATA_FILES.append((d, rel)) + +# ONNX OCR: 전체 모듈 포함(src/tools/ppocr 등 포함) +ONNX_MODULE_DIR = os.path.join(MODULES_DIR, "onnx_ocr_module") +if os.path.isdir(ONNX_MODULE_DIR): + # onnx_ocr_module 전체 트리를 lib/modules/onnx_ocr_module로 포함 + DATA_FILES.append((ONNX_MODULE_DIR, os.path.join("lib", "modules", "onnx_ocr_module"))) + + +class BuildWithBytecode(_build_exe): + def run(self): + # 0) 빌드 시작 전 기존 빌드 폴더 정리 + try: + import shutil + build_root = os.path.join(ROOT_DIR, "build") + if os.path.isdir(build_root): + shutil.rmtree(build_root, ignore_errors=True) + if os.path.isdir(self.build_exe): + shutil.rmtree(self.build_exe, ignore_errors=True) + except Exception: + pass + + # 1) 본 빌드 수행 + super().run() + # onnx_ocr_module 전체를 바이트코드 컴파일(호환성 위해 .py는 유지) + try: + import compileall + module_root = os.path.join(self.build_exe, "lib", "modules", "onnx_ocr_module") + if os.path.isdir(module_root): + compileall.compile_dir(module_root, force=True, optimize=2, quiet=1) + except Exception: + pass + + # 2) modules 아래 불필요 폴더/이미지 제거 (빌드 루트 및 lib/modules 모두) + try: + import shutil + exclude_dirs = [ + "test", "tests", "output", "outputs", "ocr_backend", "ocr_backends", + "nssm", "img", "client", "debug_images", "old_modules", "PP_Models", "user_data" + ] + bases = [ + os.path.join(self.build_exe, "modules"), + os.path.join(self.build_exe, "lib", "modules"), + ] + for base in bases: + if not os.path.isdir(base): + continue + # 폴더 제거 + for name in exclude_dirs: + p = os.path.join(base, name) + if os.path.exists(p): + try: + shutil.rmtree(p, ignore_errors=True) + except Exception: + pass + # 이미지 파일 제거(onnx_ocr_module은 제외) + for root, dirs, files in os.walk(base): + # onnx_ocr_module 경로는 건너뜀 (모델/데이터 보존) + if os.path.normpath(os.path.join(base, "onnx_ocr_module")) in os.path.normpath(root): + continue + for name in files: + ext = os.path.splitext(name)[1].lower() + if ext in (".jpg", ".jpeg", ".png", ".gif", ".webp", ".bmp"): + try: + os.remove(os.path.join(root, name)) + except Exception: + pass + except Exception: + pass + +# 추가 파일들: README, .env(있다면), 스크립트 +# EXTRA_FILES = [ +# (os.path.join(ROOT_DIR, "README.md"), "README.md"), +# (os.path.join(ROOT_DIR, "requirements.txt"), "requirements.txt"), +# ] +# for src, dst in list(EXTRA_FILES): +# if os.path.isfile(src): +# DATA_FILES.append((src, dst)) + +# PowerShell 스크립트 포함 +# 배포 편의를 위해 modules/scripts 경로 사용 +SCRIPTS_DIR = os.path.join(MODULES_DIR, "scripts") +if os.path.isdir(SCRIPTS_DIR): + for name in ("install_service.ps1", "uninstall_service.ps1"): + p = os.path.join(SCRIPTS_DIR, name) + if os.path.isfile(p): + DATA_FILES.append((p, os.path.join("modules", "scripts", name))) + +# 빌드 옵션 +build_exe_options = { + "packages": INCLUDES, + "excludes": EXCLUDES, + "include_files": DATA_FILES, + "include_msvcr": True, # MSVCRT 포함(윈도우 배포 편의) + # 패키지 압축 정책: 코드 패키지는 zip 포함, 대형 네이티브 패키지는 풀어서 포함 + "zip_include_packages": [ + "fastapi", "starlette", "pydantic", "anyio", "h11" + ], + "zip_exclude_packages": [ + "numpy", "PIL", "cv2", "onnx", "onnxruntime", "scipy" + ], +} + +# 실행 파일 설정 +executables = [ + Executable( + script=os.path.join(ROOT_DIR, "main.py"), + base='Win32GUI', + target_name=f"{APP_NAME}.exe", + icon=None, + ), + Executable( + script=os.path.join(ROOT_DIR, "modules", "tray_app.py"), + base='Win32GUI', + target_name=f"{APP_NAME}Tray.exe", + icon=None, + ) +] + +setup( + name=APP_NAME, + version=APP_VERSION, + description="Image Worker API", + options={"build_exe": build_exe_options}, + cmdclass={"build_exe": BuildWithBytecode}, + executables=executables, +) diff --git a/user_data/rembg_provider.json b/user_data/rembg_provider.json new file mode 100644 index 0000000..e6ac07c --- /dev/null +++ b/user_data/rembg_provider.json @@ -0,0 +1 @@ +{"last_success_provider": "cpu"} \ No newline at end of file